@loro-extended/change 0.9.1 → 1.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 +179 -69
- package/dist/index.d.ts +361 -169
- package/dist/index.js +516 -235
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/change.test.ts +180 -175
- package/src/conversion.test.ts +19 -19
- package/src/conversion.ts +7 -7
- package/src/derive-placeholder.test.ts +14 -14
- package/src/derive-placeholder.ts +3 -3
- package/src/discriminated-union-assignability.test.ts +7 -7
- package/src/discriminated-union-tojson.test.ts +13 -24
- package/src/discriminated-union.test.ts +9 -8
- package/src/equality.test.ts +10 -2
- package/src/functional-helpers.test.ts +149 -0
- package/src/functional-helpers.ts +61 -0
- package/src/grand-unified-api.test.ts +423 -0
- package/src/index.ts +8 -6
- package/src/json-patch.test.ts +64 -56
- package/src/overlay-recursion.test.ts +23 -22
- package/src/overlay.ts +9 -9
- package/src/readonly.test.ts +27 -26
- package/src/shape.ts +103 -21
- package/src/typed-doc.ts +227 -58
- package/src/typed-refs/base.ts +23 -1
- package/src/typed-refs/counter.test.ts +44 -13
- package/src/typed-refs/counter.ts +40 -3
- package/src/typed-refs/doc.ts +12 -6
- package/src/typed-refs/json-compatibility.test.ts +37 -32
- package/src/typed-refs/list-base.ts +26 -22
- package/src/typed-refs/list.test.ts +4 -3
- package/src/typed-refs/movable-list.test.ts +3 -2
- package/src/typed-refs/movable-list.ts +4 -1
- package/src/typed-refs/proxy-handlers.ts +14 -1
- package/src/typed-refs/record.test.ts +107 -42
- package/src/typed-refs/record.ts +37 -19
- package/src/typed-refs/{map.ts → struct.ts} +31 -16
- package/src/typed-refs/text.ts +42 -1
- package/src/typed-refs/utils.ts +28 -6
- package/src/types.test.ts +34 -39
- package/src/types.ts +5 -40
- package/src/utils/type-guards.ts +11 -6
- package/src/validation.ts +10 -10
package/src/change.test.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { LoroDoc, LoroMap } from "loro-crdt"
|
|
2
2
|
import { describe, expect, it } from "vitest"
|
|
3
|
+
import { change } from "./functional-helpers.js"
|
|
3
4
|
import { Shape } from "./shape.js"
|
|
4
|
-
import { createTypedDoc
|
|
5
|
+
import { createTypedDoc } from "./typed-doc.js"
|
|
5
6
|
|
|
6
7
|
describe("CRDT Operations", () => {
|
|
7
8
|
describe("Text Operations", () => {
|
|
@@ -12,11 +13,11 @@ describe("CRDT Operations", () => {
|
|
|
12
13
|
|
|
13
14
|
const typedDoc = createTypedDoc(schema)
|
|
14
15
|
|
|
15
|
-
const result =
|
|
16
|
+
const result = change(typedDoc, draft => {
|
|
16
17
|
draft.title.insert(0, "Hello")
|
|
17
18
|
draft.title.insert(5, " World")
|
|
18
19
|
draft.title.delete(0, 5) // Delete "Hello"
|
|
19
|
-
})
|
|
20
|
+
}).toJSON()
|
|
20
21
|
|
|
21
22
|
expect(result.title).toBe(" World")
|
|
22
23
|
})
|
|
@@ -28,10 +29,10 @@ describe("CRDT Operations", () => {
|
|
|
28
29
|
|
|
29
30
|
const typedDoc = createTypedDoc(schema)
|
|
30
31
|
|
|
31
|
-
const result =
|
|
32
|
+
const result = change(typedDoc, draft => {
|
|
32
33
|
draft.content.insert(0, "Initial content")
|
|
33
34
|
draft.content.update("Replaced content")
|
|
34
|
-
})
|
|
35
|
+
}).toJSON()
|
|
35
36
|
|
|
36
37
|
expect(result.content).toBe("Replaced content")
|
|
37
38
|
})
|
|
@@ -43,11 +44,11 @@ describe("CRDT Operations", () => {
|
|
|
43
44
|
|
|
44
45
|
const typedDoc = createTypedDoc(schema)
|
|
45
46
|
|
|
46
|
-
const result =
|
|
47
|
+
const result = change(typedDoc, draft => {
|
|
47
48
|
draft.richText.insert(0, "Bold text")
|
|
48
49
|
draft.richText.mark({ start: 0, end: 4 }, "bold", true)
|
|
49
50
|
draft.richText.unmark({ start: 0, end: 2 }, "bold")
|
|
50
|
-
})
|
|
51
|
+
}).toJSON()
|
|
51
52
|
|
|
52
53
|
expect(result.richText).toBe("Bold text")
|
|
53
54
|
})
|
|
@@ -59,7 +60,7 @@ describe("CRDT Operations", () => {
|
|
|
59
60
|
|
|
60
61
|
const typedDoc = createTypedDoc(schema)
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
change(typedDoc, draft => {
|
|
63
64
|
draft.deltaText.insert(0, "Hello World")
|
|
64
65
|
const delta = draft.deltaText.toDelta()
|
|
65
66
|
expect(delta).toBeDefined()
|
|
@@ -68,7 +69,8 @@ describe("CRDT Operations", () => {
|
|
|
68
69
|
draft.deltaText.applyDelta([{ insert: "New " }])
|
|
69
70
|
})
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
// Use toJSON() to get plain values for comparison
|
|
73
|
+
const result = typedDoc.toJSON()
|
|
72
74
|
expect(result.deltaText).toContain("New")
|
|
73
75
|
})
|
|
74
76
|
|
|
@@ -79,7 +81,7 @@ describe("CRDT Operations", () => {
|
|
|
79
81
|
|
|
80
82
|
const typedDoc = createTypedDoc(schema)
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
change(typedDoc, draft => {
|
|
83
85
|
draft.measuredText.insert(0, "Hello")
|
|
84
86
|
expect(draft.measuredText.length).toBe(5)
|
|
85
87
|
|
|
@@ -97,11 +99,11 @@ describe("CRDT Operations", () => {
|
|
|
97
99
|
|
|
98
100
|
const typedDoc = createTypedDoc(schema)
|
|
99
101
|
|
|
100
|
-
const result =
|
|
102
|
+
const result = change(typedDoc, draft => {
|
|
101
103
|
draft.count.increment(5)
|
|
102
104
|
draft.count.decrement(2)
|
|
103
105
|
draft.count.increment(10)
|
|
104
|
-
})
|
|
106
|
+
}).toJSON()
|
|
105
107
|
|
|
106
108
|
expect(result.count).toBe(13) // 5 - 2 + 10 = 13
|
|
107
109
|
})
|
|
@@ -113,7 +115,7 @@ describe("CRDT Operations", () => {
|
|
|
113
115
|
|
|
114
116
|
const typedDoc = createTypedDoc(schema)
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
change(typedDoc, draft => {
|
|
117
119
|
draft.counter.increment(7)
|
|
118
120
|
expect(draft.counter.value).toBe(7)
|
|
119
121
|
|
|
@@ -129,10 +131,10 @@ describe("CRDT Operations", () => {
|
|
|
129
131
|
|
|
130
132
|
const typedDoc = createTypedDoc(schema)
|
|
131
133
|
|
|
132
|
-
const result =
|
|
134
|
+
const result = change(typedDoc, draft => {
|
|
133
135
|
draft.negativeCounter.increment(-5) // Negative increment
|
|
134
136
|
draft.negativeCounter.decrement(-3) // Negative decrement (adds 3)
|
|
135
|
-
})
|
|
137
|
+
}).toJSON()
|
|
136
138
|
|
|
137
139
|
expect(result.negativeCounter).toBe(-2) // -5 + 3 = -2
|
|
138
140
|
})
|
|
@@ -146,12 +148,12 @@ describe("CRDT Operations", () => {
|
|
|
146
148
|
|
|
147
149
|
const typedDoc = createTypedDoc(schema)
|
|
148
150
|
|
|
149
|
-
const result =
|
|
151
|
+
const result = change(typedDoc, draft => {
|
|
150
152
|
draft.items.push("first")
|
|
151
153
|
draft.items.insert(0, "zero")
|
|
152
154
|
draft.items.push("second")
|
|
153
155
|
draft.items.delete(1, 1) // Delete "first"
|
|
154
|
-
})
|
|
156
|
+
}).toJSON()
|
|
155
157
|
|
|
156
158
|
expect(result.items).toEqual(["zero", "second"])
|
|
157
159
|
})
|
|
@@ -163,11 +165,11 @@ describe("CRDT Operations", () => {
|
|
|
163
165
|
|
|
164
166
|
const typedDoc = createTypedDoc(schema)
|
|
165
167
|
|
|
166
|
-
const result =
|
|
168
|
+
const result = change(typedDoc, draft => {
|
|
167
169
|
draft.numbers.push(1)
|
|
168
170
|
draft.numbers.push(2)
|
|
169
171
|
draft.numbers.insert(1, 1.5)
|
|
170
|
-
})
|
|
172
|
+
}).toJSON()
|
|
171
173
|
|
|
172
174
|
expect(result.numbers).toEqual([1, 1.5, 2])
|
|
173
175
|
})
|
|
@@ -179,11 +181,11 @@ describe("CRDT Operations", () => {
|
|
|
179
181
|
|
|
180
182
|
const typedDoc = createTypedDoc(schema)
|
|
181
183
|
|
|
182
|
-
const result =
|
|
184
|
+
const result = change(typedDoc, draft => {
|
|
183
185
|
draft.flags.push(true)
|
|
184
186
|
draft.flags.push(false)
|
|
185
187
|
draft.flags.insert(1, true)
|
|
186
|
-
})
|
|
188
|
+
}).toJSON()
|
|
187
189
|
|
|
188
190
|
expect(result.flags).toEqual([true, true, false])
|
|
189
191
|
})
|
|
@@ -195,7 +197,7 @@ describe("CRDT Operations", () => {
|
|
|
195
197
|
|
|
196
198
|
const typedDoc = createTypedDoc(schema)
|
|
197
199
|
|
|
198
|
-
|
|
200
|
+
change(typedDoc, draft => {
|
|
199
201
|
draft.testList.push("a")
|
|
200
202
|
draft.testList.push("b")
|
|
201
203
|
|
|
@@ -213,13 +215,13 @@ describe("CRDT Operations", () => {
|
|
|
213
215
|
|
|
214
216
|
const typedDoc = createTypedDoc(schema)
|
|
215
217
|
|
|
216
|
-
|
|
218
|
+
change(typedDoc, draft => {
|
|
217
219
|
// Note: pushContainer and insertContainer expect actual container instances
|
|
218
220
|
// For testing purposes, we'll just verify the list exists
|
|
219
221
|
expect(draft.containerList.length).toBe(0)
|
|
220
222
|
})
|
|
221
223
|
|
|
222
|
-
const result = typedDoc.
|
|
224
|
+
const result = typedDoc.toJSON()
|
|
223
225
|
expect(result.containerList).toHaveLength(0) // No containers were actually added
|
|
224
226
|
})
|
|
225
227
|
|
|
@@ -231,18 +233,18 @@ describe("CRDT Operations", () => {
|
|
|
231
233
|
const typedDoc = createTypedDoc(schema)
|
|
232
234
|
|
|
233
235
|
// Add initial items
|
|
234
|
-
|
|
236
|
+
change(typedDoc, draft => {
|
|
235
237
|
draft.items.push("first")
|
|
236
238
|
draft.items.push("second")
|
|
237
239
|
draft.items.push("third")
|
|
238
240
|
})
|
|
239
241
|
|
|
240
242
|
// Test move operation: move first item to the end
|
|
241
|
-
const result =
|
|
243
|
+
const result = change(typedDoc, draft => {
|
|
242
244
|
const valueToMove = draft.items.get(0)
|
|
243
245
|
draft.items.delete(0, 1)
|
|
244
246
|
draft.items.insert(2, valueToMove)
|
|
245
|
-
})
|
|
247
|
+
}).toJSON()
|
|
246
248
|
|
|
247
249
|
expect(result.items).toEqual(["second", "third", "first"])
|
|
248
250
|
})
|
|
@@ -252,7 +254,7 @@ describe("CRDT Operations", () => {
|
|
|
252
254
|
it("should handle push, insert, delete, and move operations", () => {
|
|
253
255
|
const schema = Shape.doc({
|
|
254
256
|
tasks: Shape.movableList(
|
|
255
|
-
Shape.plain.
|
|
257
|
+
Shape.plain.struct({
|
|
256
258
|
id: Shape.plain.string(),
|
|
257
259
|
title: Shape.plain.string(),
|
|
258
260
|
}),
|
|
@@ -261,13 +263,13 @@ describe("CRDT Operations", () => {
|
|
|
261
263
|
|
|
262
264
|
const typedDoc = createTypedDoc(schema)
|
|
263
265
|
|
|
264
|
-
const result =
|
|
266
|
+
const result = change(typedDoc, draft => {
|
|
265
267
|
draft.tasks.push({ id: "1", title: "Task 1" })
|
|
266
268
|
draft.tasks.push({ id: "2", title: "Task 2" })
|
|
267
269
|
draft.tasks.push({ id: "3", title: "Task 3" })
|
|
268
270
|
draft.tasks.move(0, 2) // Move first task to position 2
|
|
269
271
|
draft.tasks.delete(1, 1) // Delete middle task
|
|
270
|
-
})
|
|
272
|
+
}).toJSON()
|
|
271
273
|
|
|
272
274
|
expect(result.tasks).toHaveLength(2)
|
|
273
275
|
expect(result.tasks[0]).toEqual({ id: "2", title: "Task 2" })
|
|
@@ -281,10 +283,10 @@ describe("CRDT Operations", () => {
|
|
|
281
283
|
|
|
282
284
|
const typedDoc = createTypedDoc(schema)
|
|
283
285
|
|
|
284
|
-
const result =
|
|
286
|
+
const result = change(typedDoc, draft => {
|
|
285
287
|
draft.editableList.push("original")
|
|
286
288
|
draft.editableList.set(0, "modified")
|
|
287
|
-
})
|
|
289
|
+
}).toJSON()
|
|
288
290
|
|
|
289
291
|
expect(result.editableList).toEqual(["modified"])
|
|
290
292
|
})
|
|
@@ -296,7 +298,7 @@ describe("CRDT Operations", () => {
|
|
|
296
298
|
|
|
297
299
|
const typedDoc = createTypedDoc(schema)
|
|
298
300
|
|
|
299
|
-
|
|
301
|
+
change(typedDoc, draft => {
|
|
300
302
|
draft.movableItems.push(10)
|
|
301
303
|
draft.movableItems.push(20)
|
|
302
304
|
|
|
@@ -310,7 +312,7 @@ describe("CRDT Operations", () => {
|
|
|
310
312
|
describe("Map Operations", () => {
|
|
311
313
|
it("should handle set, get, and delete operations", () => {
|
|
312
314
|
const schema = Shape.doc({
|
|
313
|
-
metadata: Shape.
|
|
315
|
+
metadata: Shape.struct({
|
|
314
316
|
title: Shape.plain.string(),
|
|
315
317
|
count: Shape.plain.number().placeholder(1),
|
|
316
318
|
enabled: Shape.plain.boolean(),
|
|
@@ -319,12 +321,12 @@ describe("CRDT Operations", () => {
|
|
|
319
321
|
|
|
320
322
|
const typedDoc = createTypedDoc(schema)
|
|
321
323
|
|
|
322
|
-
const result =
|
|
324
|
+
const result = change(typedDoc, draft => {
|
|
323
325
|
draft.metadata.set("title", "Test Title")
|
|
324
326
|
draft.metadata.set("count", 42)
|
|
325
327
|
draft.metadata.set("enabled", true)
|
|
326
328
|
draft.metadata.delete("count")
|
|
327
|
-
})
|
|
329
|
+
}).toJSON()
|
|
328
330
|
|
|
329
331
|
expect(result.metadata.title).toBe("Test Title")
|
|
330
332
|
expect(result.metadata.count).toBe(1) // Should fall back to empty state
|
|
@@ -333,7 +335,7 @@ describe("CRDT Operations", () => {
|
|
|
333
335
|
|
|
334
336
|
it("should handle array values in maps", () => {
|
|
335
337
|
const schema = Shape.doc({
|
|
336
|
-
config: Shape.
|
|
338
|
+
config: Shape.struct({
|
|
337
339
|
tags: Shape.plain.array(Shape.plain.string()),
|
|
338
340
|
numbers: Shape.plain.array(Shape.plain.number()),
|
|
339
341
|
}),
|
|
@@ -341,10 +343,10 @@ describe("CRDT Operations", () => {
|
|
|
341
343
|
|
|
342
344
|
const typedDoc = createTypedDoc(schema)
|
|
343
345
|
|
|
344
|
-
const result =
|
|
346
|
+
const result = change(typedDoc, draft => {
|
|
345
347
|
draft.config.set("tags", ["tag1", "tag2", "tag3"])
|
|
346
348
|
draft.config.set("numbers", [1, 2, 3])
|
|
347
|
-
})
|
|
349
|
+
}).toJSON()
|
|
348
350
|
|
|
349
351
|
expect(result.config.tags).toEqual(["tag1", "tag2", "tag3"])
|
|
350
352
|
expect(result.config.numbers).toEqual([1, 2, 3])
|
|
@@ -352,7 +354,7 @@ describe("CRDT Operations", () => {
|
|
|
352
354
|
|
|
353
355
|
it("should provide map utility methods", () => {
|
|
354
356
|
const schema = Shape.doc({
|
|
355
|
-
testMap: Shape.
|
|
357
|
+
testMap: Shape.struct({
|
|
356
358
|
key1: Shape.plain.string(),
|
|
357
359
|
key2: Shape.plain.number(),
|
|
358
360
|
}),
|
|
@@ -360,7 +362,7 @@ describe("CRDT Operations", () => {
|
|
|
360
362
|
|
|
361
363
|
const typedDoc = createTypedDoc(schema)
|
|
362
364
|
|
|
363
|
-
|
|
365
|
+
change(typedDoc, draft => {
|
|
364
366
|
draft.testMap.set("key1", "value1")
|
|
365
367
|
draft.testMap.set("key2", 123)
|
|
366
368
|
|
|
@@ -377,20 +379,20 @@ describe("CRDT Operations", () => {
|
|
|
377
379
|
|
|
378
380
|
it("should handle container insertion in maps", () => {
|
|
379
381
|
const schema = Shape.doc({
|
|
380
|
-
containerMap: Shape.
|
|
382
|
+
containerMap: Shape.struct({
|
|
381
383
|
textField: Shape.text(),
|
|
382
384
|
}),
|
|
383
385
|
})
|
|
384
386
|
|
|
385
387
|
const typedDoc = createTypedDoc(schema)
|
|
386
388
|
|
|
387
|
-
|
|
389
|
+
change(typedDoc, draft => {
|
|
388
390
|
// Note: setContainer expects actual container instances
|
|
389
391
|
// For testing purposes, we'll just verify the map exists
|
|
390
392
|
expect(draft.containerMap).toBeDefined()
|
|
391
393
|
})
|
|
392
394
|
|
|
393
|
-
const rawValue = typedDoc
|
|
395
|
+
const rawValue = typedDoc.$.rawValue
|
|
394
396
|
// Since no container was actually set, containerMap might be undefined
|
|
395
397
|
expect(rawValue.containerMap).toBeUndefined()
|
|
396
398
|
})
|
|
@@ -399,12 +401,12 @@ describe("CRDT Operations", () => {
|
|
|
399
401
|
describe("Tree Operations", () => {
|
|
400
402
|
it("should handle basic tree operations", () => {
|
|
401
403
|
const schema = Shape.doc({
|
|
402
|
-
tree: Shape.tree(Shape.
|
|
404
|
+
tree: Shape.tree(Shape.struct({ name: Shape.text() })),
|
|
403
405
|
})
|
|
404
406
|
|
|
405
407
|
const typedDoc = createTypedDoc(schema)
|
|
406
408
|
|
|
407
|
-
|
|
409
|
+
change(typedDoc, draft => {
|
|
408
410
|
const root = draft.tree.createNode()
|
|
409
411
|
expect(root).toBeDefined()
|
|
410
412
|
|
|
@@ -416,12 +418,12 @@ describe("CRDT Operations", () => {
|
|
|
416
418
|
|
|
417
419
|
it("should handle tree node movement and deletion", () => {
|
|
418
420
|
const schema = Shape.doc({
|
|
419
|
-
hierarchy: Shape.tree(Shape.
|
|
421
|
+
hierarchy: Shape.tree(Shape.struct({ name: Shape.text() })),
|
|
420
422
|
})
|
|
421
423
|
|
|
422
424
|
const typedDoc = createTypedDoc(schema)
|
|
423
425
|
|
|
424
|
-
|
|
426
|
+
change(typedDoc, draft => {
|
|
425
427
|
const parent1 = draft.hierarchy.createNode()
|
|
426
428
|
const parent2 = draft.hierarchy.createNode()
|
|
427
429
|
|
|
@@ -434,12 +436,12 @@ describe("CRDT Operations", () => {
|
|
|
434
436
|
|
|
435
437
|
it("should handle tree node lookup by ID", () => {
|
|
436
438
|
const schema = Shape.doc({
|
|
437
|
-
searchableTree: Shape.tree(Shape.
|
|
439
|
+
searchableTree: Shape.tree(Shape.struct({ name: Shape.text() })),
|
|
438
440
|
})
|
|
439
441
|
|
|
440
442
|
const typedDoc = createTypedDoc(schema)
|
|
441
443
|
|
|
442
|
-
|
|
444
|
+
change(typedDoc, draft => {
|
|
443
445
|
const node = draft.searchableTree.createNode()
|
|
444
446
|
|
|
445
447
|
// Note: getNodeByID might not be available in all versions
|
|
@@ -454,11 +456,11 @@ describe("Nested Operations", () => {
|
|
|
454
456
|
describe("Nested Maps", () => {
|
|
455
457
|
it("should handle deeply nested map structures", () => {
|
|
456
458
|
const schema = Shape.doc({
|
|
457
|
-
article: Shape.
|
|
459
|
+
article: Shape.struct({
|
|
458
460
|
title: Shape.text(),
|
|
459
|
-
metadata: Shape.
|
|
461
|
+
metadata: Shape.struct({
|
|
460
462
|
views: Shape.counter(),
|
|
461
|
-
author: Shape.
|
|
463
|
+
author: Shape.struct({
|
|
462
464
|
name: Shape.plain.string(),
|
|
463
465
|
email: Shape.plain.string(),
|
|
464
466
|
}),
|
|
@@ -468,12 +470,12 @@ describe("Nested Operations", () => {
|
|
|
468
470
|
|
|
469
471
|
const typedDoc = createTypedDoc(schema)
|
|
470
472
|
|
|
471
|
-
const result =
|
|
473
|
+
const result = change(typedDoc, draft => {
|
|
472
474
|
draft.article.title.insert(0, "Nested Article")
|
|
473
475
|
draft.article.metadata.views.increment(10)
|
|
474
476
|
draft.article.metadata.author.set("name", "John Doe")
|
|
475
477
|
draft.article.metadata.author.set("email", "john@example.com")
|
|
476
|
-
})
|
|
478
|
+
}).toJSON()
|
|
477
479
|
|
|
478
480
|
expect(result.article.title).toBe("Nested Article")
|
|
479
481
|
expect(result.article.metadata.views).toBe(10)
|
|
@@ -483,7 +485,7 @@ describe("Nested Operations", () => {
|
|
|
483
485
|
|
|
484
486
|
it("should handle maps with mixed Zod and Loro schemas", () => {
|
|
485
487
|
const schema = Shape.doc({
|
|
486
|
-
mixed: Shape.
|
|
488
|
+
mixed: Shape.struct({
|
|
487
489
|
plainString: Shape.plain.string(),
|
|
488
490
|
plainArray: Shape.plain.array(Shape.plain.number()),
|
|
489
491
|
loroText: Shape.text(),
|
|
@@ -493,12 +495,12 @@ describe("Nested Operations", () => {
|
|
|
493
495
|
|
|
494
496
|
const typedDoc = createTypedDoc(schema)
|
|
495
497
|
|
|
496
|
-
const result =
|
|
498
|
+
const result = change(typedDoc, draft => {
|
|
497
499
|
draft.mixed.set("plainString", "Hello")
|
|
498
500
|
draft.mixed.set("plainArray", [1, 2, 3])
|
|
499
501
|
draft.mixed.loroText.insert(0, "Loro Text")
|
|
500
502
|
draft.mixed.loroCounter.increment(5)
|
|
501
|
-
})
|
|
503
|
+
}).toJSON()
|
|
502
504
|
|
|
503
505
|
expect(result.mixed.plainString).toBe("Hello")
|
|
504
506
|
expect(result.mixed.plainArray).toEqual([1, 2, 3])
|
|
@@ -511,10 +513,10 @@ describe("Nested Operations", () => {
|
|
|
511
513
|
it("should handle lists of maps with nested structures", () => {
|
|
512
514
|
const schema = Shape.doc({
|
|
513
515
|
articles: Shape.list(
|
|
514
|
-
Shape.
|
|
516
|
+
Shape.struct({
|
|
515
517
|
title: Shape.text(),
|
|
516
518
|
tags: Shape.list(Shape.plain.string()),
|
|
517
|
-
metadata: Shape.
|
|
519
|
+
metadata: Shape.struct({
|
|
518
520
|
views: Shape.counter(),
|
|
519
521
|
published: Shape.plain.boolean(),
|
|
520
522
|
}),
|
|
@@ -524,7 +526,7 @@ describe("Nested Operations", () => {
|
|
|
524
526
|
|
|
525
527
|
const typedDoc = createTypedDoc(schema)
|
|
526
528
|
|
|
527
|
-
const result =
|
|
529
|
+
const result = change(typedDoc, draft => {
|
|
528
530
|
draft.articles.push({
|
|
529
531
|
title: "First Article",
|
|
530
532
|
tags: ["tech", "programming"],
|
|
@@ -542,7 +544,7 @@ describe("Nested Operations", () => {
|
|
|
542
544
|
published: false,
|
|
543
545
|
},
|
|
544
546
|
})
|
|
545
|
-
})
|
|
547
|
+
}).toJSON()
|
|
546
548
|
|
|
547
549
|
expect(result.articles).toHaveLength(2)
|
|
548
550
|
expect(result.articles[0].title).toBe("First Article")
|
|
@@ -554,9 +556,9 @@ describe("Nested Operations", () => {
|
|
|
554
556
|
|
|
555
557
|
it("should handle nested plain value maps", () => {
|
|
556
558
|
const schema = Shape.doc({
|
|
557
|
-
articles: Shape.
|
|
558
|
-
metadata: Shape.plain.
|
|
559
|
-
views: Shape.plain.
|
|
559
|
+
articles: Shape.struct({
|
|
560
|
+
metadata: Shape.plain.struct({
|
|
561
|
+
views: Shape.plain.struct({
|
|
560
562
|
page: Shape.plain.number(),
|
|
561
563
|
}),
|
|
562
564
|
}),
|
|
@@ -565,25 +567,25 @@ describe("Nested Operations", () => {
|
|
|
565
567
|
|
|
566
568
|
const typedDoc = createTypedDoc(schema)
|
|
567
569
|
|
|
568
|
-
const result1 =
|
|
570
|
+
const result1 = change(typedDoc, draft => {
|
|
569
571
|
// natural object access & assignment for Value nodes
|
|
570
572
|
draft.articles.metadata.views.page = 1
|
|
571
|
-
})
|
|
573
|
+
}).toJSON()
|
|
572
574
|
|
|
573
575
|
expect(result1).toEqual({
|
|
574
576
|
articles: { metadata: { views: { page: 1 } } },
|
|
575
577
|
})
|
|
576
578
|
|
|
577
|
-
const result2 =
|
|
579
|
+
const result2 = change(typedDoc, draft => {
|
|
578
580
|
// natural object access & assignment for Value nodes
|
|
579
581
|
draft.articles.metadata = { views: { page: 2 } }
|
|
580
|
-
})
|
|
582
|
+
}).toJSON()
|
|
581
583
|
|
|
582
584
|
expect(result2).toEqual({
|
|
583
585
|
articles: { metadata: { views: { page: 2 } } },
|
|
584
586
|
})
|
|
585
587
|
|
|
586
|
-
expect(typedDoc
|
|
588
|
+
expect(typedDoc.$.rawValue).toEqual({
|
|
587
589
|
articles: { metadata: { views: { page: 2 } } },
|
|
588
590
|
})
|
|
589
591
|
})
|
|
@@ -595,10 +597,10 @@ describe("Nested Operations", () => {
|
|
|
595
597
|
|
|
596
598
|
const typedDoc = createTypedDoc(schema)
|
|
597
599
|
|
|
598
|
-
const result =
|
|
600
|
+
const result = change(typedDoc, draft => {
|
|
599
601
|
draft.matrix.push([1, 2, 3])
|
|
600
602
|
draft.matrix.push([4, 5, 6])
|
|
601
|
-
})
|
|
603
|
+
}).toJSON()
|
|
602
604
|
|
|
603
605
|
const correctResult = {
|
|
604
606
|
matrix: [
|
|
@@ -608,14 +610,14 @@ describe("Nested Operations", () => {
|
|
|
608
610
|
}
|
|
609
611
|
|
|
610
612
|
expect(result).toEqual(correctResult)
|
|
611
|
-
expect(typedDoc
|
|
613
|
+
expect(typedDoc.$.rawValue).toEqual(correctResult)
|
|
612
614
|
})
|
|
613
615
|
})
|
|
614
616
|
|
|
615
617
|
describe("Maps with List Values", () => {
|
|
616
618
|
it("should handle maps containing lists", () => {
|
|
617
619
|
const schema = Shape.doc({
|
|
618
|
-
categories: Shape.
|
|
620
|
+
categories: Shape.struct({
|
|
619
621
|
tech: Shape.list(Shape.plain.string()),
|
|
620
622
|
design: Shape.list(Shape.plain.string()),
|
|
621
623
|
}),
|
|
@@ -623,11 +625,11 @@ describe("Nested Operations", () => {
|
|
|
623
625
|
|
|
624
626
|
const typedDoc = createTypedDoc(schema)
|
|
625
627
|
|
|
626
|
-
const result =
|
|
628
|
+
const result = change(typedDoc, draft => {
|
|
627
629
|
draft.categories.tech.push("JavaScript")
|
|
628
630
|
draft.categories.tech.push("TypeScript")
|
|
629
631
|
draft.categories.design.push("UI/UX")
|
|
630
|
-
})
|
|
632
|
+
}).toJSON()
|
|
631
633
|
|
|
632
634
|
expect(result.categories.tech).toEqual(["JavaScript", "TypeScript"])
|
|
633
635
|
expect(result.categories.design).toEqual(["UI/UX"])
|
|
@@ -662,10 +664,10 @@ describe("TypedLoroDoc", () => {
|
|
|
662
664
|
|
|
663
665
|
const typedDoc = createTypedDoc(schema)
|
|
664
666
|
|
|
665
|
-
const result =
|
|
667
|
+
const result = change(typedDoc, draft => {
|
|
666
668
|
draft.title.insert(0, "Hello World")
|
|
667
669
|
draft.count.increment(5)
|
|
668
|
-
})
|
|
670
|
+
}).toJSON()
|
|
669
671
|
|
|
670
672
|
expect(result.title).toBe("Hello World")
|
|
671
673
|
expect(result.count).toBe(5)
|
|
@@ -674,9 +676,9 @@ describe("TypedLoroDoc", () => {
|
|
|
674
676
|
|
|
675
677
|
it("should handle nested empty state structures", () => {
|
|
676
678
|
const schema = Shape.doc({
|
|
677
|
-
article: Shape.
|
|
679
|
+
article: Shape.struct({
|
|
678
680
|
title: Shape.text().placeholder("Default Title"),
|
|
679
|
-
metadata: Shape.
|
|
681
|
+
metadata: Shape.struct({
|
|
680
682
|
views: Shape.counter(),
|
|
681
683
|
tags: Shape.plain.array(Shape.plain.string()),
|
|
682
684
|
author: Shape.plain.string().placeholder("Anonymous"),
|
|
@@ -699,11 +701,11 @@ describe("TypedLoroDoc", () => {
|
|
|
699
701
|
|
|
700
702
|
expect(typedDoc.toJSON()).toEqual(expectedPlaceholder)
|
|
701
703
|
|
|
702
|
-
const result =
|
|
704
|
+
const result = change(typedDoc, draft => {
|
|
703
705
|
draft.article.title.insert(0, "New Title")
|
|
704
706
|
draft.article.metadata.views.increment(10)
|
|
705
707
|
draft.article.metadata.set("author", "John Doe")
|
|
706
|
-
})
|
|
708
|
+
}).toJSON()
|
|
707
709
|
|
|
708
710
|
expect(result.article.title).toBe("New Title")
|
|
709
711
|
expect(result.article.metadata.views).toBe(10)
|
|
@@ -713,7 +715,7 @@ describe("TypedLoroDoc", () => {
|
|
|
713
715
|
|
|
714
716
|
it("should handle empty state with optional fields", () => {
|
|
715
717
|
const schema = Shape.doc({
|
|
716
|
-
profile: Shape.
|
|
718
|
+
profile: Shape.struct({
|
|
717
719
|
name: Shape.plain.string().placeholder("Anonymous"),
|
|
718
720
|
email: Shape.plain
|
|
719
721
|
.union([Shape.plain.null(), Shape.plain.string()])
|
|
@@ -726,10 +728,10 @@ describe("TypedLoroDoc", () => {
|
|
|
726
728
|
|
|
727
729
|
const typedDoc = createTypedDoc(schema)
|
|
728
730
|
|
|
729
|
-
const result =
|
|
731
|
+
const result = change(typedDoc, draft => {
|
|
730
732
|
draft.profile.set("name", "John Doe")
|
|
731
733
|
draft.profile.set("email", "john@example.com")
|
|
732
|
-
})
|
|
734
|
+
}).toJSON()
|
|
733
735
|
|
|
734
736
|
expect(result.profile.name).toBe("John Doe")
|
|
735
737
|
expect(result.profile.email).toBe("john@example.com")
|
|
@@ -741,24 +743,25 @@ describe("TypedLoroDoc", () => {
|
|
|
741
743
|
it("should distinguish between raw CRDT and overlaid values", () => {
|
|
742
744
|
const schema = Shape.doc({
|
|
743
745
|
title: Shape.text(),
|
|
744
|
-
metadata: Shape.
|
|
746
|
+
metadata: Shape.struct({
|
|
745
747
|
optional: Shape.plain.string().placeholder("default-optional"),
|
|
746
748
|
}),
|
|
747
749
|
})
|
|
748
750
|
|
|
749
751
|
const typedDoc = createTypedDoc(schema)
|
|
750
752
|
|
|
751
|
-
|
|
753
|
+
change(typedDoc, draft => {
|
|
752
754
|
draft.title.insert(0, "Hello")
|
|
753
755
|
})
|
|
754
756
|
|
|
755
757
|
// Raw value should only contain what was actually set in CRDT
|
|
756
|
-
const rawValue = typedDoc
|
|
758
|
+
const rawValue = typedDoc.$.rawValue
|
|
757
759
|
expect(rawValue.title).toBe("Hello")
|
|
758
760
|
expect(rawValue.metadata).toBeUndefined()
|
|
759
761
|
|
|
760
762
|
// Overlaid value should include empty state defaults
|
|
761
|
-
|
|
763
|
+
// Use toJSON() to get plain values for comparison
|
|
764
|
+
const overlaidValue = typedDoc.toJSON()
|
|
762
765
|
expect(overlaidValue.title).toBe("Hello")
|
|
763
766
|
expect(overlaidValue.metadata.optional).toBe("default-optional")
|
|
764
767
|
})
|
|
@@ -791,7 +794,7 @@ describe("TypedLoroDoc", () => {
|
|
|
791
794
|
|
|
792
795
|
it("should handle null values in placeholder correctly", () => {
|
|
793
796
|
const schema = Shape.doc({
|
|
794
|
-
interjection: Shape.
|
|
797
|
+
interjection: Shape.struct({
|
|
795
798
|
currentPrediction: Shape.plain
|
|
796
799
|
.union([Shape.plain.null(), Shape.plain.string()])
|
|
797
800
|
.placeholder(null),
|
|
@@ -802,7 +805,7 @@ describe("TypedLoroDoc", () => {
|
|
|
802
805
|
|
|
803
806
|
// This should not throw "placeholder required"
|
|
804
807
|
expect(() => {
|
|
805
|
-
|
|
808
|
+
change(typedDoc, draft => {
|
|
806
809
|
// Accessing the property triggers getOrCreateNode
|
|
807
810
|
const current = draft.interjection.currentPrediction
|
|
808
811
|
expect(current).toBeNull()
|
|
@@ -827,22 +830,22 @@ describe("TypedLoroDoc", () => {
|
|
|
827
830
|
const typedDoc = createTypedDoc(schema)
|
|
828
831
|
|
|
829
832
|
// First change
|
|
830
|
-
let result =
|
|
833
|
+
let result = change(typedDoc, draft => {
|
|
831
834
|
draft.title.insert(0, "Hello")
|
|
832
835
|
draft.count.increment(5)
|
|
833
836
|
draft.items.push("first")
|
|
834
|
-
})
|
|
837
|
+
}).toJSON()
|
|
835
838
|
|
|
836
839
|
expect(result.title).toBe("Hello")
|
|
837
840
|
expect(result.count).toBe(5)
|
|
838
841
|
expect(result.items).toEqual(["first"])
|
|
839
842
|
|
|
840
843
|
// Second change - should build on previous state
|
|
841
|
-
result =
|
|
844
|
+
result = change(typedDoc, draft => {
|
|
842
845
|
draft.title.insert(5, " World")
|
|
843
846
|
draft.count.increment(3)
|
|
844
847
|
draft.items.push("second")
|
|
845
|
-
})
|
|
848
|
+
}).toJSON()
|
|
846
849
|
|
|
847
850
|
expect(result.title).toBe("Hello World")
|
|
848
851
|
expect(result.count).toBe(8) // 5 + 3
|
|
@@ -854,7 +857,7 @@ describe("TypedLoroDoc", () => {
|
|
|
854
857
|
it("should convert plain objects to map containers in lists", () => {
|
|
855
858
|
const schema = Shape.doc({
|
|
856
859
|
articles: Shape.list(
|
|
857
|
-
Shape.
|
|
860
|
+
Shape.struct({
|
|
858
861
|
title: Shape.text(),
|
|
859
862
|
tags: Shape.list(Shape.plain.string()),
|
|
860
863
|
}),
|
|
@@ -863,12 +866,12 @@ describe("TypedLoroDoc", () => {
|
|
|
863
866
|
|
|
864
867
|
const typedDoc = createTypedDoc(schema)
|
|
865
868
|
|
|
866
|
-
const result =
|
|
869
|
+
const result = change(typedDoc, draft => {
|
|
867
870
|
draft.articles.push({
|
|
868
871
|
title: "Hello World",
|
|
869
872
|
tags: ["hello", "world"],
|
|
870
873
|
})
|
|
871
|
-
})
|
|
874
|
+
}).toJSON()
|
|
872
875
|
|
|
873
876
|
expect(result.articles).toHaveLength(1)
|
|
874
877
|
expect(result.articles[0].title).toBe("Hello World")
|
|
@@ -878,7 +881,7 @@ describe("TypedLoroDoc", () => {
|
|
|
878
881
|
it("should handle nested conversion in movable lists", () => {
|
|
879
882
|
const schema = Shape.doc({
|
|
880
883
|
tasks: Shape.movableList(
|
|
881
|
-
Shape.
|
|
884
|
+
Shape.struct({
|
|
882
885
|
title: Shape.text(),
|
|
883
886
|
completed: Shape.plain.boolean(),
|
|
884
887
|
subtasks: Shape.list(Shape.plain.string()),
|
|
@@ -888,13 +891,13 @@ describe("TypedLoroDoc", () => {
|
|
|
888
891
|
|
|
889
892
|
const typedDoc = createTypedDoc(schema)
|
|
890
893
|
|
|
891
|
-
const result =
|
|
894
|
+
const result = change(typedDoc, draft => {
|
|
892
895
|
draft.tasks.push({
|
|
893
896
|
title: "Main Task",
|
|
894
897
|
completed: false,
|
|
895
898
|
subtasks: ["subtask1", "subtask2"],
|
|
896
899
|
})
|
|
897
|
-
})
|
|
900
|
+
}).toJSON()
|
|
898
901
|
|
|
899
902
|
expect(result.tasks).toHaveLength(1)
|
|
900
903
|
expect(result.tasks[0].title).toBe("Main Task")
|
|
@@ -905,9 +908,9 @@ describe("TypedLoroDoc", () => {
|
|
|
905
908
|
it("should handle deeply nested conversion", () => {
|
|
906
909
|
const schema = Shape.doc({
|
|
907
910
|
posts: Shape.list(
|
|
908
|
-
Shape.
|
|
911
|
+
Shape.struct({
|
|
909
912
|
title: Shape.text(),
|
|
910
|
-
metadata: Shape.
|
|
913
|
+
metadata: Shape.struct({
|
|
911
914
|
views: Shape.counter(),
|
|
912
915
|
tags: Shape.plain.array(Shape.plain.string()),
|
|
913
916
|
}),
|
|
@@ -917,7 +920,7 @@ describe("TypedLoroDoc", () => {
|
|
|
917
920
|
|
|
918
921
|
const typedDoc = createTypedDoc(schema)
|
|
919
922
|
|
|
920
|
-
const result =
|
|
923
|
+
const result = change(typedDoc, draft => {
|
|
921
924
|
draft.posts.push({
|
|
922
925
|
title: "Complex Post",
|
|
923
926
|
metadata: {
|
|
@@ -925,7 +928,7 @@ describe("TypedLoroDoc", () => {
|
|
|
925
928
|
tags: ["complex", "nested"],
|
|
926
929
|
},
|
|
927
930
|
})
|
|
928
|
-
})
|
|
931
|
+
}).toJSON()
|
|
929
932
|
|
|
930
933
|
expect(result.posts).toHaveLength(1)
|
|
931
934
|
expect(result.posts[0].title).toBe("Complex Post")
|
|
@@ -940,7 +943,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
940
943
|
it("should maintain type safety with complex schemas", () => {
|
|
941
944
|
const schema = Shape.doc({
|
|
942
945
|
title: Shape.text(),
|
|
943
|
-
metadata: Shape.
|
|
946
|
+
metadata: Shape.struct({
|
|
944
947
|
author: Shape.plain.string().placeholder("Anonymous"),
|
|
945
948
|
publishedAt: Shape.plain.string().placeholder("2025-01-01"),
|
|
946
949
|
}),
|
|
@@ -949,23 +952,24 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
949
952
|
const typedDoc = createTypedDoc(schema)
|
|
950
953
|
|
|
951
954
|
// Multiple changes
|
|
952
|
-
|
|
955
|
+
change(typedDoc, draft => {
|
|
953
956
|
draft.title.insert(0, "First Title")
|
|
954
957
|
draft.metadata.set("author", "John Doe")
|
|
955
958
|
})
|
|
956
959
|
|
|
957
|
-
|
|
960
|
+
// Use toJSON() to get plain values for comparison
|
|
961
|
+
let result = typedDoc.toJSON()
|
|
958
962
|
expect(result.title).toBe("First Title")
|
|
959
963
|
expect(result.metadata.author).toBe("John Doe")
|
|
960
964
|
expect(result.metadata.publishedAt).toBe("2025-01-01")
|
|
961
965
|
|
|
962
966
|
// More changes
|
|
963
|
-
|
|
967
|
+
change(typedDoc, draft => {
|
|
964
968
|
draft.title.update("Updated Title")
|
|
965
969
|
draft.metadata.set("publishedAt", "2025-12-01")
|
|
966
970
|
})
|
|
967
971
|
|
|
968
|
-
result = typedDoc.
|
|
972
|
+
result = typedDoc.toJSON()
|
|
969
973
|
expect(result.title).toBe("Updated Title")
|
|
970
974
|
expect(result.metadata.author).toBe("John Doe") // Preserved from previous change
|
|
971
975
|
expect(result.metadata.publishedAt).toBe("2025-12-01")
|
|
@@ -974,7 +978,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
974
978
|
it("should handle empty containers gracefully", () => {
|
|
975
979
|
const schema = Shape.doc({
|
|
976
980
|
todos: Shape.list(
|
|
977
|
-
Shape.
|
|
981
|
+
Shape.struct({
|
|
978
982
|
text: Shape.text(),
|
|
979
983
|
completed: Shape.plain.boolean(),
|
|
980
984
|
}),
|
|
@@ -984,12 +988,12 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
984
988
|
const typedDoc = createTypedDoc(schema)
|
|
985
989
|
|
|
986
990
|
// Add a todo item with minimal data
|
|
987
|
-
const result =
|
|
991
|
+
const result = change(typedDoc, draft => {
|
|
988
992
|
draft.todos.push({
|
|
989
993
|
text: "Test Todo",
|
|
990
994
|
completed: false,
|
|
991
995
|
})
|
|
992
|
-
})
|
|
996
|
+
}).toJSON()
|
|
993
997
|
|
|
994
998
|
expect(result.todos).toHaveLength(1)
|
|
995
999
|
expect(result.todos[0].text).toBe("Test Todo")
|
|
@@ -1006,13 +1010,13 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1006
1010
|
|
|
1007
1011
|
const typedDoc = createTypedDoc(schema)
|
|
1008
1012
|
|
|
1009
|
-
const result =
|
|
1013
|
+
const result = change(typedDoc, draft => {
|
|
1010
1014
|
// Add many items
|
|
1011
1015
|
for (let i = 0; i < 100; i++) {
|
|
1012
1016
|
draft.items.push(`item-${i}`)
|
|
1013
1017
|
draft.counter.increment(1)
|
|
1014
1018
|
}
|
|
1015
|
-
})
|
|
1019
|
+
}).toJSON()
|
|
1016
1020
|
|
|
1017
1021
|
expect(result.items).toHaveLength(100)
|
|
1018
1022
|
expect(result.counter).toBe(100)
|
|
@@ -1031,11 +1035,11 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1031
1035
|
|
|
1032
1036
|
const typedDoc = createTypedDoc(schema)
|
|
1033
1037
|
|
|
1034
|
-
const result =
|
|
1038
|
+
const result = change(typedDoc, draft => {
|
|
1035
1039
|
draft.text.insert(0, "")
|
|
1036
1040
|
draft.count.increment(0)
|
|
1037
1041
|
draft.items.push("")
|
|
1038
|
-
})
|
|
1042
|
+
}).toJSON()
|
|
1039
1043
|
|
|
1040
1044
|
expect(result.text).toBe("")
|
|
1041
1045
|
expect(result.count).toBe(0)
|
|
@@ -1050,11 +1054,11 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1050
1054
|
|
|
1051
1055
|
const typedDoc = createTypedDoc(schema)
|
|
1052
1056
|
|
|
1053
|
-
const result =
|
|
1057
|
+
const result = change(typedDoc, draft => {
|
|
1054
1058
|
draft.unicode.insert(0, "Hello 世界 🌍")
|
|
1055
1059
|
draft.emoji.push("🚀")
|
|
1056
1060
|
draft.emoji.push("⭐")
|
|
1057
|
-
})
|
|
1061
|
+
}).toJSON()
|
|
1058
1062
|
|
|
1059
1063
|
expect(result.unicode).toBe("Hello 世界 🌍")
|
|
1060
1064
|
expect(result.emoji).toEqual(["🚀", "⭐"])
|
|
@@ -1070,7 +1074,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1070
1074
|
|
|
1071
1075
|
const typedDoc = createTypedDoc(schema)
|
|
1072
1076
|
|
|
1073
|
-
|
|
1077
|
+
change(typedDoc, draft => {
|
|
1074
1078
|
draft.items.push("apple")
|
|
1075
1079
|
draft.items.push("banana")
|
|
1076
1080
|
draft.items.push("cherry")
|
|
@@ -1091,7 +1095,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1091
1095
|
|
|
1092
1096
|
const typedDoc = createTypedDoc(schema)
|
|
1093
1097
|
|
|
1094
|
-
|
|
1098
|
+
change(typedDoc, draft => {
|
|
1095
1099
|
draft.numbers.push(10)
|
|
1096
1100
|
draft.numbers.push(20)
|
|
1097
1101
|
draft.numbers.push(30)
|
|
@@ -1112,7 +1116,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1112
1116
|
|
|
1113
1117
|
const typedDoc = createTypedDoc(schema)
|
|
1114
1118
|
|
|
1115
|
-
|
|
1119
|
+
change(typedDoc, draft => {
|
|
1116
1120
|
draft.words.push("hello")
|
|
1117
1121
|
draft.words.push("world")
|
|
1118
1122
|
|
|
@@ -1139,7 +1143,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1139
1143
|
|
|
1140
1144
|
const typedDoc = createTypedDoc(schema)
|
|
1141
1145
|
|
|
1142
|
-
|
|
1146
|
+
change(typedDoc, draft => {
|
|
1143
1147
|
draft.numbers.push(1)
|
|
1144
1148
|
draft.numbers.push(2)
|
|
1145
1149
|
draft.numbers.push(3)
|
|
@@ -1162,7 +1166,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1162
1166
|
|
|
1163
1167
|
const typedDoc = createTypedDoc(schema)
|
|
1164
1168
|
|
|
1165
|
-
|
|
1169
|
+
change(typedDoc, draft => {
|
|
1166
1170
|
draft.items.push("a")
|
|
1167
1171
|
draft.items.push("b")
|
|
1168
1172
|
draft.items.push("c")
|
|
@@ -1188,7 +1192,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1188
1192
|
|
|
1189
1193
|
const typedDoc = createTypedDoc(schema)
|
|
1190
1194
|
|
|
1191
|
-
|
|
1195
|
+
change(typedDoc, draft => {
|
|
1192
1196
|
draft.numbers.push(1)
|
|
1193
1197
|
draft.numbers.push(3)
|
|
1194
1198
|
draft.numbers.push(5)
|
|
@@ -1214,7 +1218,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1214
1218
|
|
|
1215
1219
|
const typedDoc = createTypedDoc(schema)
|
|
1216
1220
|
|
|
1217
|
-
|
|
1221
|
+
change(typedDoc, draft => {
|
|
1218
1222
|
draft.numbers.push(2)
|
|
1219
1223
|
draft.numbers.push(4)
|
|
1220
1224
|
draft.numbers.push(6)
|
|
@@ -1236,7 +1240,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1236
1240
|
it("should work with lists of plain objects", () => {
|
|
1237
1241
|
const schema = Shape.doc({
|
|
1238
1242
|
todos: Shape.list(
|
|
1239
|
-
Shape.plain.
|
|
1243
|
+
Shape.plain.struct({
|
|
1240
1244
|
id: Shape.plain.string(),
|
|
1241
1245
|
text: Shape.plain.string(),
|
|
1242
1246
|
completed: Shape.plain.boolean(),
|
|
@@ -1246,7 +1250,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1246
1250
|
|
|
1247
1251
|
const typedDoc = createTypedDoc(schema)
|
|
1248
1252
|
|
|
1249
|
-
|
|
1253
|
+
change(typedDoc, draft => {
|
|
1250
1254
|
draft.todos.push({ id: "1", text: "Buy milk", completed: false })
|
|
1251
1255
|
draft.todos.push({ id: "2", text: "Walk dog", completed: true })
|
|
1252
1256
|
draft.todos.push({ id: "3", text: "Write code", completed: false })
|
|
@@ -1286,7 +1290,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1286
1290
|
it("should work with lists of maps (nested containers)", () => {
|
|
1287
1291
|
const schema = Shape.doc({
|
|
1288
1292
|
articles: Shape.list(
|
|
1289
|
-
Shape.
|
|
1293
|
+
Shape.struct({
|
|
1290
1294
|
title: Shape.text(),
|
|
1291
1295
|
published: Shape.plain.boolean(),
|
|
1292
1296
|
}),
|
|
@@ -1295,7 +1299,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1295
1299
|
|
|
1296
1300
|
const typedDoc = createTypedDoc(schema)
|
|
1297
1301
|
|
|
1298
|
-
|
|
1302
|
+
change(typedDoc, draft => {
|
|
1299
1303
|
draft.articles.push({
|
|
1300
1304
|
title: "First Article",
|
|
1301
1305
|
published: true,
|
|
@@ -1328,7 +1332,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1328
1332
|
it("should support all array methods on movable lists", () => {
|
|
1329
1333
|
const schema = Shape.doc({
|
|
1330
1334
|
tasks: Shape.movableList(
|
|
1331
|
-
Shape.plain.
|
|
1335
|
+
Shape.plain.struct({
|
|
1332
1336
|
id: Shape.plain.string(),
|
|
1333
1337
|
priority: Shape.plain.number(),
|
|
1334
1338
|
}),
|
|
@@ -1337,7 +1341,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1337
1341
|
|
|
1338
1342
|
const typedDoc = createTypedDoc(schema)
|
|
1339
1343
|
|
|
1340
|
-
|
|
1344
|
+
change(typedDoc, draft => {
|
|
1341
1345
|
draft.tasks.push({ id: "1", priority: 1 })
|
|
1342
1346
|
draft.tasks.push({ id: "2", priority: 3 })
|
|
1343
1347
|
draft.tasks.push({ id: "3", priority: 2 })
|
|
@@ -1381,7 +1385,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1381
1385
|
|
|
1382
1386
|
const typedDoc = createTypedDoc(schema)
|
|
1383
1387
|
|
|
1384
|
-
|
|
1388
|
+
change(typedDoc, draft => {
|
|
1385
1389
|
// Test all methods on empty list
|
|
1386
1390
|
expect(draft.items.find(_item => true)).toBeUndefined()
|
|
1387
1391
|
expect(draft.items.findIndex(_item => true)).toBe(-1)
|
|
@@ -1405,7 +1409,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1405
1409
|
|
|
1406
1410
|
const typedDoc = createTypedDoc(schema)
|
|
1407
1411
|
|
|
1408
|
-
|
|
1412
|
+
change(typedDoc, draft => {
|
|
1409
1413
|
draft.items.push(42)
|
|
1410
1414
|
|
|
1411
1415
|
// Test all methods on single item list
|
|
@@ -1434,7 +1438,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1434
1438
|
|
|
1435
1439
|
const typedDoc = createTypedDoc(schema)
|
|
1436
1440
|
|
|
1437
|
-
|
|
1441
|
+
change(typedDoc, draft => {
|
|
1438
1442
|
draft.items.push("a")
|
|
1439
1443
|
draft.items.push("b")
|
|
1440
1444
|
draft.items.push("c")
|
|
@@ -1480,7 +1484,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1480
1484
|
it("should allow mutation of items found via array methods", () => {
|
|
1481
1485
|
const schema = Shape.doc({
|
|
1482
1486
|
todos: Shape.list(
|
|
1483
|
-
Shape.plain.
|
|
1487
|
+
Shape.plain.struct({
|
|
1484
1488
|
id: Shape.plain.string(),
|
|
1485
1489
|
text: Shape.plain.string(),
|
|
1486
1490
|
completed: Shape.plain.boolean(),
|
|
@@ -1491,14 +1495,14 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1491
1495
|
const typedDoc = createTypedDoc(schema)
|
|
1492
1496
|
|
|
1493
1497
|
// Add initial todos
|
|
1494
|
-
|
|
1498
|
+
change(typedDoc, draft => {
|
|
1495
1499
|
draft.todos.push({ id: "1", text: "Buy milk", completed: false })
|
|
1496
1500
|
draft.todos.push({ id: "2", text: "Walk dog", completed: false })
|
|
1497
1501
|
draft.todos.push({ id: "3", text: "Write code", completed: true })
|
|
1498
1502
|
})
|
|
1499
1503
|
|
|
1500
1504
|
// Test the key developer expectation: find + mutate
|
|
1501
|
-
const result =
|
|
1505
|
+
const result = change(typedDoc, draft => {
|
|
1502
1506
|
// Find a todo and toggle its completion status
|
|
1503
1507
|
const todo = draft.todos.find(t => t.id === "2")
|
|
1504
1508
|
if (todo) {
|
|
@@ -1510,7 +1514,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1510
1514
|
if (codeTodo) {
|
|
1511
1515
|
codeTodo.text = "Write better code"
|
|
1512
1516
|
}
|
|
1513
|
-
})
|
|
1517
|
+
}).toJSON()
|
|
1514
1518
|
|
|
1515
1519
|
// Verify the mutations persisted to the document state
|
|
1516
1520
|
expect(result.todos[0]).toEqual({
|
|
@@ -1529,8 +1533,8 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1529
1533
|
completed: true,
|
|
1530
1534
|
}) // Text should be changed
|
|
1531
1535
|
|
|
1532
|
-
// Also verify via typedDoc.
|
|
1533
|
-
const finalState = typedDoc.
|
|
1536
|
+
// Also verify via typedDoc.toJSON()
|
|
1537
|
+
const finalState = typedDoc.toJSON()
|
|
1534
1538
|
expect(finalState.todos[1].completed).toBe(true)
|
|
1535
1539
|
expect(finalState.todos[2].text).toBe("Write better code")
|
|
1536
1540
|
})
|
|
@@ -1538,10 +1542,10 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1538
1542
|
it("should allow mutation of nested container items found via array methods", () => {
|
|
1539
1543
|
const schema = Shape.doc({
|
|
1540
1544
|
articles: Shape.list(
|
|
1541
|
-
Shape.
|
|
1545
|
+
Shape.struct({
|
|
1542
1546
|
title: Shape.text(),
|
|
1543
1547
|
viewCount: Shape.counter(),
|
|
1544
|
-
metadata: Shape.plain.
|
|
1548
|
+
metadata: Shape.plain.struct({
|
|
1545
1549
|
author: Shape.plain.string(),
|
|
1546
1550
|
published: Shape.plain.boolean(),
|
|
1547
1551
|
}),
|
|
@@ -1552,7 +1556,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1552
1556
|
const typedDoc = createTypedDoc(schema)
|
|
1553
1557
|
|
|
1554
1558
|
// Add initial articles
|
|
1555
|
-
|
|
1559
|
+
change(typedDoc, draft => {
|
|
1556
1560
|
draft.articles.push({
|
|
1557
1561
|
title: "First Article",
|
|
1558
1562
|
viewCount: 0,
|
|
@@ -1566,7 +1570,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1566
1570
|
})
|
|
1567
1571
|
|
|
1568
1572
|
// Test mutation of nested containers found via array methods
|
|
1569
|
-
const result =
|
|
1573
|
+
const result = change(typedDoc, draft => {
|
|
1570
1574
|
// Find article by author and modify its nested properties
|
|
1571
1575
|
const aliceArticle = draft.articles.find(
|
|
1572
1576
|
article => article.metadata.author === "Alice",
|
|
@@ -1590,7 +1594,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1590
1594
|
publishedArticle.title.update("Updated Second Article")
|
|
1591
1595
|
publishedArticle.viewCount.increment(3)
|
|
1592
1596
|
}
|
|
1593
|
-
})
|
|
1597
|
+
}).toJSON()
|
|
1594
1598
|
|
|
1595
1599
|
// Verify all mutations persisted correctly
|
|
1596
1600
|
expect(result.articles[0].title).toBe("📝 First Article")
|
|
@@ -1599,8 +1603,8 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1599
1603
|
expect(result.articles[1].title).toBe("Updated Second Article")
|
|
1600
1604
|
expect(result.articles[1].viewCount).toBe(8) // 5 + 3
|
|
1601
1605
|
|
|
1602
|
-
// Verify via typedDoc.
|
|
1603
|
-
const finalState = typedDoc.
|
|
1606
|
+
// Verify via typedDoc.toJSON() as well (use toJSON for plain values)
|
|
1607
|
+
const finalState = typedDoc.toJSON()
|
|
1604
1608
|
expect(finalState.articles[0].title).toBe("📝 First Article")
|
|
1605
1609
|
expect(finalState.articles[0].viewCount).toBe(10)
|
|
1606
1610
|
expect(finalState.articles[1].viewCount).toBe(8)
|
|
@@ -1609,7 +1613,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1609
1613
|
it("should support common developer patterns with array methods", () => {
|
|
1610
1614
|
const schema = Shape.doc({
|
|
1611
1615
|
users: Shape.list(
|
|
1612
|
-
Shape.plain.
|
|
1616
|
+
Shape.plain.struct({
|
|
1613
1617
|
id: Shape.plain.string(),
|
|
1614
1618
|
name: Shape.plain.string(),
|
|
1615
1619
|
active: Shape.plain.boolean(),
|
|
@@ -1621,7 +1625,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1621
1625
|
const typedDoc = createTypedDoc(schema)
|
|
1622
1626
|
|
|
1623
1627
|
// Add initial users
|
|
1624
|
-
|
|
1628
|
+
change(typedDoc, draft => {
|
|
1625
1629
|
draft.users.push({
|
|
1626
1630
|
id: "1",
|
|
1627
1631
|
name: "Alice",
|
|
@@ -1637,7 +1641,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1637
1641
|
})
|
|
1638
1642
|
})
|
|
1639
1643
|
|
|
1640
|
-
const result =
|
|
1644
|
+
const result = change(typedDoc, draft => {
|
|
1641
1645
|
// Pattern 1: Find and toggle boolean
|
|
1642
1646
|
const inactiveUser = draft.users.find(user => !user.active)
|
|
1643
1647
|
if (inactiveUser) {
|
|
@@ -1662,7 +1666,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1662
1666
|
if (firstUser) {
|
|
1663
1667
|
firstUser.name = `👑 ${firstUser.name}`
|
|
1664
1668
|
}
|
|
1665
|
-
})
|
|
1669
|
+
}).toJSON()
|
|
1666
1670
|
|
|
1667
1671
|
// Verify all patterns worked
|
|
1668
1672
|
expect(result.users[0].name).toBe("👑 Alice")
|
|
@@ -1673,7 +1677,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1673
1677
|
expect(result.users[2].score).toBe(180) // 120 + 50 VIP + 10 bonus
|
|
1674
1678
|
|
|
1675
1679
|
// Verify persistence
|
|
1676
|
-
const finalState = typedDoc.
|
|
1680
|
+
const finalState = typedDoc.toJSON()
|
|
1677
1681
|
expect(finalState.users.every(user => user.active)).toBe(true)
|
|
1678
1682
|
expect(finalState.users[2].name).toContain("VIP")
|
|
1679
1683
|
})
|
|
@@ -1681,7 +1685,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1681
1685
|
it("should handle edge cases in find-and-mutate patterns", () => {
|
|
1682
1686
|
const schema = Shape.doc({
|
|
1683
1687
|
items: Shape.list(
|
|
1684
|
-
Shape.plain.
|
|
1688
|
+
Shape.plain.struct({
|
|
1685
1689
|
id: Shape.plain.string(),
|
|
1686
1690
|
value: Shape.plain.number(),
|
|
1687
1691
|
}),
|
|
@@ -1690,7 +1694,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1690
1694
|
|
|
1691
1695
|
const typedDoc = createTypedDoc(schema)
|
|
1692
1696
|
|
|
1693
|
-
const result =
|
|
1697
|
+
const result = change(typedDoc, draft => {
|
|
1694
1698
|
// Add some items
|
|
1695
1699
|
draft.items.push({ id: "1", value: 10 })
|
|
1696
1700
|
draft.items.push({ id: "2", value: 20 })
|
|
@@ -1717,7 +1721,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1717
1721
|
item.value += 5
|
|
1718
1722
|
}
|
|
1719
1723
|
}
|
|
1720
|
-
})
|
|
1724
|
+
}).toJSON()
|
|
1721
1725
|
|
|
1722
1726
|
// Verify mutations worked correctly
|
|
1723
1727
|
expect(result.items).toHaveLength(2)
|
|
@@ -1737,7 +1741,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1737
1741
|
|
|
1738
1742
|
const typedDoc = createTypedDoc(schema)
|
|
1739
1743
|
|
|
1740
|
-
|
|
1744
|
+
change(typedDoc, draft => {
|
|
1741
1745
|
draft.items.push("a")
|
|
1742
1746
|
draft.items.push("b")
|
|
1743
1747
|
draft.items.push("c")
|
|
@@ -1757,7 +1761,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1757
1761
|
|
|
1758
1762
|
const typedDoc = createTypedDoc(schema)
|
|
1759
1763
|
|
|
1760
|
-
|
|
1764
|
+
change(typedDoc, draft => {
|
|
1761
1765
|
draft.items.push("a")
|
|
1762
1766
|
draft.items.push("b")
|
|
1763
1767
|
draft.items.push("c")
|
|
@@ -1785,7 +1789,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1785
1789
|
|
|
1786
1790
|
const typedDoc = createTypedDoc(schema)
|
|
1787
1791
|
|
|
1788
|
-
|
|
1792
|
+
change(typedDoc, draft => {
|
|
1789
1793
|
draft.items.push("a")
|
|
1790
1794
|
draft.items.push("b")
|
|
1791
1795
|
draft.items.push("c")
|
|
@@ -1804,7 +1808,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1804
1808
|
|
|
1805
1809
|
const typedDoc = createTypedDoc(schema)
|
|
1806
1810
|
|
|
1807
|
-
|
|
1811
|
+
change(typedDoc, draft => {
|
|
1808
1812
|
draft.items.push("a")
|
|
1809
1813
|
draft.items.push("b")
|
|
1810
1814
|
draft.items.push("c")
|
|
@@ -1822,7 +1826,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1822
1826
|
|
|
1823
1827
|
const typedDoc = createTypedDoc(schema)
|
|
1824
1828
|
|
|
1825
|
-
|
|
1829
|
+
change(typedDoc, draft => {
|
|
1826
1830
|
draft.items.push("a")
|
|
1827
1831
|
draft.items.push("b")
|
|
1828
1832
|
draft.items.push("c")
|
|
@@ -1848,7 +1852,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1848
1852
|
|
|
1849
1853
|
const typedDoc = createTypedDoc(schema)
|
|
1850
1854
|
|
|
1851
|
-
|
|
1855
|
+
change(typedDoc, draft => {
|
|
1852
1856
|
// slice() on empty list returns []
|
|
1853
1857
|
expect(draft.items.slice()).toEqual([])
|
|
1854
1858
|
expect(draft.items.slice(0, 10)).toEqual([])
|
|
@@ -1859,7 +1863,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1859
1863
|
it("should allow mutations to persist", () => {
|
|
1860
1864
|
const schema = Shape.doc({
|
|
1861
1865
|
items: Shape.list(
|
|
1862
|
-
Shape.plain.
|
|
1866
|
+
Shape.plain.struct({
|
|
1863
1867
|
id: Shape.plain.string(),
|
|
1864
1868
|
value: Shape.plain.number(),
|
|
1865
1869
|
}),
|
|
@@ -1868,7 +1872,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1868
1872
|
|
|
1869
1873
|
const typedDoc = createTypedDoc(schema)
|
|
1870
1874
|
|
|
1871
|
-
|
|
1875
|
+
change(typedDoc, draft => {
|
|
1872
1876
|
draft.items.push({ id: "1", value: 10 })
|
|
1873
1877
|
draft.items.push({ id: "2", value: 20 })
|
|
1874
1878
|
draft.items.push({ id: "3", value: 30 })
|
|
@@ -1876,12 +1880,12 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1876
1880
|
})
|
|
1877
1881
|
|
|
1878
1882
|
// Modify items from slice and verify changes persist
|
|
1879
|
-
const result =
|
|
1883
|
+
const result = change(typedDoc, draft => {
|
|
1880
1884
|
const middleItems = draft.items.slice(1, 3)
|
|
1881
1885
|
// Mutate the sliced items
|
|
1882
1886
|
middleItems[0].value = 200
|
|
1883
1887
|
middleItems[1].value = 300
|
|
1884
|
-
})
|
|
1888
|
+
}).toJSON()
|
|
1885
1889
|
|
|
1886
1890
|
// Verify mutations persisted to the original list
|
|
1887
1891
|
expect(result.items[0].value).toBe(10) // unchanged
|
|
@@ -1897,7 +1901,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1897
1901
|
|
|
1898
1902
|
const typedDoc = createTypedDoc(schema)
|
|
1899
1903
|
|
|
1900
|
-
|
|
1904
|
+
change(typedDoc, draft => {
|
|
1901
1905
|
draft.tasks.push("task1")
|
|
1902
1906
|
draft.tasks.push("task2")
|
|
1903
1907
|
draft.tasks.push("task3")
|
|
@@ -1915,7 +1919,7 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1915
1919
|
it("should work with nested container items", () => {
|
|
1916
1920
|
const schema = Shape.doc({
|
|
1917
1921
|
articles: Shape.list(
|
|
1918
|
-
Shape.
|
|
1922
|
+
Shape.struct({
|
|
1919
1923
|
title: Shape.text(),
|
|
1920
1924
|
views: Shape.counter(),
|
|
1921
1925
|
}),
|
|
@@ -1924,19 +1928,19 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1924
1928
|
|
|
1925
1929
|
const typedDoc = createTypedDoc(schema)
|
|
1926
1930
|
|
|
1927
|
-
|
|
1931
|
+
change(typedDoc, draft => {
|
|
1928
1932
|
draft.articles.push({ title: "Article 1", views: 10 })
|
|
1929
1933
|
draft.articles.push({ title: "Article 2", views: 20 })
|
|
1930
1934
|
draft.articles.push({ title: "Article 3", views: 30 })
|
|
1931
1935
|
})
|
|
1932
1936
|
|
|
1933
|
-
const result =
|
|
1937
|
+
const result = change(typedDoc, draft => {
|
|
1934
1938
|
const sliced = draft.articles.slice(0, 2)
|
|
1935
1939
|
// Mutate nested containers in sliced items
|
|
1936
1940
|
sliced[0].title.update("Updated Article 1")
|
|
1937
1941
|
sliced[0].views.increment(5)
|
|
1938
1942
|
sliced[1].views.increment(10)
|
|
1939
|
-
})
|
|
1943
|
+
}).toJSON()
|
|
1940
1944
|
|
|
1941
1945
|
// Verify mutations persisted
|
|
1942
1946
|
expect(result.articles[0].title).toBe("Updated Article 1")
|
|
@@ -1963,12 +1967,12 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1963
1967
|
*/
|
|
1964
1968
|
it("should call toJSON() without error when Record has entries with partial CRDT data", () => {
|
|
1965
1969
|
// Schema with a Record containing Maps (similar to user's tomState schema)
|
|
1966
|
-
const StudentStateSchema = Shape.
|
|
1970
|
+
const StudentStateSchema = Shape.struct({
|
|
1967
1971
|
peerId: Shape.plain.string(),
|
|
1968
1972
|
authorName: Shape.plain.string(),
|
|
1969
1973
|
authorColor: Shape.plain.string(),
|
|
1970
1974
|
history: Shape.list(
|
|
1971
|
-
Shape.
|
|
1975
|
+
Shape.struct({
|
|
1972
1976
|
timestamp: Shape.plain.number(),
|
|
1973
1977
|
value: Shape.plain.string(),
|
|
1974
1978
|
}),
|
|
@@ -1992,11 +1996,12 @@ describe("Edge Cases and Error Handling", () => {
|
|
|
1992
1996
|
// Note: authorColor is NOT set - this should fall back to placeholder default
|
|
1993
1997
|
|
|
1994
1998
|
// Wrap with TypedDoc
|
|
1995
|
-
const typedDoc =
|
|
1999
|
+
const typedDoc = createTypedDoc(DocSchema, loroDoc)
|
|
1996
2000
|
|
|
1997
2001
|
// This should not throw "placeholder required"
|
|
1998
2002
|
expect(() => {
|
|
1999
|
-
|
|
2003
|
+
// Use typedDoc.toJSON() to get plain values
|
|
2004
|
+
const json = typedDoc.toJSON()
|
|
2000
2005
|
// Verify the result has placeholder defaults for missing fields
|
|
2001
2006
|
expect(json.students["peer-123"].peerId).toBe("peer-123")
|
|
2002
2007
|
expect(json.students["peer-123"].authorName).toBe("Alice")
|