@loro-extended/change 0.8.1 → 0.9.1
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 +78 -0
- package/dist/index.d.ts +199 -43
- package/dist/index.js +642 -429
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/change.test.ts +277 -1
- package/src/conversion.test.ts +72 -72
- package/src/conversion.ts +5 -5
- package/src/discriminated-union-assignability.test.ts +45 -0
- package/src/discriminated-union-tojson.test.ts +128 -0
- package/src/index.ts +7 -0
- package/src/overlay-recursion.test.ts +325 -0
- package/src/overlay.ts +45 -8
- package/src/placeholder-proxy.test.ts +52 -0
- package/src/placeholder-proxy.ts +37 -0
- package/src/presence-interface.ts +52 -0
- package/src/shape.ts +44 -50
- package/src/typed-doc.ts +4 -4
- package/src/typed-presence.ts +96 -0
- package/src/{draft-nodes → typed-refs}/base.ts +14 -4
- package/src/{draft-nodes → typed-refs}/counter.test.ts +1 -1
- package/src/{draft-nodes → typed-refs}/counter.ts +9 -3
- package/src/{draft-nodes → typed-refs}/doc.ts +32 -25
- package/src/typed-refs/json-compatibility.test.ts +255 -0
- package/src/{draft-nodes → typed-refs}/list-base.ts +115 -42
- package/src/{draft-nodes → typed-refs}/list.test.ts +1 -1
- package/src/{draft-nodes → typed-refs}/list.ts +4 -4
- package/src/{draft-nodes → typed-refs}/map.ts +50 -66
- package/src/{draft-nodes → typed-refs}/movable-list.test.ts +1 -1
- package/src/{draft-nodes → typed-refs}/movable-list.ts +6 -6
- package/src/{draft-nodes → typed-refs}/proxy-handlers.ts +25 -26
- package/src/{draft-nodes → typed-refs}/record.test.ts +78 -9
- package/src/typed-refs/record.ts +193 -0
- package/src/{draft-nodes → typed-refs}/text.ts +13 -3
- package/src/{draft-nodes → typed-refs}/tree.ts +6 -3
- package/src/typed-refs/utils.ts +177 -0
- package/src/types.test.ts +97 -2
- package/src/types.ts +62 -5
- package/src/draft-nodes/counter.md +0 -31
- package/src/draft-nodes/record.ts +0 -177
- package/src/draft-nodes/utils.ts +0 -96
package/src/conversion.test.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
type LoroText,
|
|
10
10
|
} from "loro-crdt"
|
|
11
11
|
import { describe, expect, it } from "vitest"
|
|
12
|
-
import {
|
|
12
|
+
import { convertInputToRef } from "./conversion.js"
|
|
13
13
|
import { Shape } from "./shape.js"
|
|
14
14
|
import {
|
|
15
15
|
isContainer,
|
|
@@ -24,7 +24,7 @@ describe("Conversion Functions", () => {
|
|
|
24
24
|
describe("convertInputToNode - Text Conversion", () => {
|
|
25
25
|
it("should convert string to LoroText", () => {
|
|
26
26
|
const shape = Shape.text()
|
|
27
|
-
const result =
|
|
27
|
+
const result = convertInputToRef("Hello World", shape)
|
|
28
28
|
|
|
29
29
|
expect(isContainer(result)).toBe(true)
|
|
30
30
|
expect(isLoroText(result as any)).toBe(true)
|
|
@@ -35,7 +35,7 @@ describe("Conversion Functions", () => {
|
|
|
35
35
|
|
|
36
36
|
it("should handle empty string", () => {
|
|
37
37
|
const shape = Shape.text()
|
|
38
|
-
const result =
|
|
38
|
+
const result = convertInputToRef("", shape)
|
|
39
39
|
|
|
40
40
|
expect(isLoroText(result as any)).toBe(true)
|
|
41
41
|
const text = result as LoroText
|
|
@@ -53,7 +53,7 @@ describe("Conversion Functions", () => {
|
|
|
53
53
|
]
|
|
54
54
|
|
|
55
55
|
for (const testString of testStrings) {
|
|
56
|
-
const result =
|
|
56
|
+
const result = convertInputToRef(testString, shape)
|
|
57
57
|
expect(isLoroText(result as any)).toBe(true)
|
|
58
58
|
const text = result as LoroText
|
|
59
59
|
expect(text.toString()).toBe(testString)
|
|
@@ -63,19 +63,19 @@ describe("Conversion Functions", () => {
|
|
|
63
63
|
it("should throw error for non-string input", () => {
|
|
64
64
|
const shape = Shape.text()
|
|
65
65
|
|
|
66
|
-
expect(() =>
|
|
66
|
+
expect(() => convertInputToRef(123 as any, shape)).toThrow(
|
|
67
67
|
"string expected",
|
|
68
68
|
)
|
|
69
|
-
expect(() =>
|
|
69
|
+
expect(() => convertInputToRef(null as any, shape)).toThrow(
|
|
70
70
|
"string expected",
|
|
71
71
|
)
|
|
72
|
-
expect(() =>
|
|
72
|
+
expect(() => convertInputToRef([] as any, shape)).toThrow(
|
|
73
73
|
"string expected",
|
|
74
74
|
)
|
|
75
|
-
expect(() =>
|
|
75
|
+
expect(() => convertInputToRef({} as any, shape)).toThrow(
|
|
76
76
|
"string expected",
|
|
77
77
|
)
|
|
78
|
-
expect(() =>
|
|
78
|
+
expect(() => convertInputToRef(true as any, shape)).toThrow(
|
|
79
79
|
"string expected",
|
|
80
80
|
)
|
|
81
81
|
})
|
|
@@ -84,7 +84,7 @@ describe("Conversion Functions", () => {
|
|
|
84
84
|
describe("convertInputToNode - Counter Conversion", () => {
|
|
85
85
|
it("should convert number to LoroCounter", () => {
|
|
86
86
|
const shape = Shape.counter()
|
|
87
|
-
const result =
|
|
87
|
+
const result = convertInputToRef(42, shape)
|
|
88
88
|
|
|
89
89
|
expect(isContainer(result)).toBe(true)
|
|
90
90
|
expect(isLoroCounter(result as any)).toBe(true)
|
|
@@ -95,7 +95,7 @@ describe("Conversion Functions", () => {
|
|
|
95
95
|
|
|
96
96
|
it("should handle zero value", () => {
|
|
97
97
|
const shape = Shape.counter()
|
|
98
|
-
const result =
|
|
98
|
+
const result = convertInputToRef(0, shape)
|
|
99
99
|
|
|
100
100
|
expect(isLoroCounter(result as any)).toBe(true)
|
|
101
101
|
const counter = result as LoroCounter
|
|
@@ -104,7 +104,7 @@ describe("Conversion Functions", () => {
|
|
|
104
104
|
|
|
105
105
|
it("should handle negative numbers", () => {
|
|
106
106
|
const shape = Shape.counter()
|
|
107
|
-
const result =
|
|
107
|
+
const result = convertInputToRef(-15, shape)
|
|
108
108
|
|
|
109
109
|
expect(isLoroCounter(result as any)).toBe(true)
|
|
110
110
|
const counter = result as LoroCounter
|
|
@@ -113,7 +113,7 @@ describe("Conversion Functions", () => {
|
|
|
113
113
|
|
|
114
114
|
it("should handle floating point numbers", () => {
|
|
115
115
|
const shape = Shape.counter()
|
|
116
|
-
const result =
|
|
116
|
+
const result = convertInputToRef(3.14, shape)
|
|
117
117
|
|
|
118
118
|
expect(isLoroCounter(result as any)).toBe(true)
|
|
119
119
|
const counter = result as LoroCounter
|
|
@@ -123,19 +123,19 @@ describe("Conversion Functions", () => {
|
|
|
123
123
|
it("should throw error for non-number input", () => {
|
|
124
124
|
const shape = Shape.counter()
|
|
125
125
|
|
|
126
|
-
expect(() =>
|
|
126
|
+
expect(() => convertInputToRef("123" as any, shape)).toThrow(
|
|
127
127
|
"number expected",
|
|
128
128
|
)
|
|
129
|
-
expect(() =>
|
|
129
|
+
expect(() => convertInputToRef(null as any, shape)).toThrow(
|
|
130
130
|
"number expected",
|
|
131
131
|
)
|
|
132
|
-
expect(() =>
|
|
132
|
+
expect(() => convertInputToRef([] as any, shape)).toThrow(
|
|
133
133
|
"number expected",
|
|
134
134
|
)
|
|
135
|
-
expect(() =>
|
|
135
|
+
expect(() => convertInputToRef({} as any, shape)).toThrow(
|
|
136
136
|
"number expected",
|
|
137
137
|
)
|
|
138
|
-
expect(() =>
|
|
138
|
+
expect(() => convertInputToRef(true as any, shape)).toThrow(
|
|
139
139
|
"number expected",
|
|
140
140
|
)
|
|
141
141
|
})
|
|
@@ -144,7 +144,7 @@ describe("Conversion Functions", () => {
|
|
|
144
144
|
describe("convertInputToNode - List Conversion", () => {
|
|
145
145
|
it("should convert array to LoroList with value items", () => {
|
|
146
146
|
const shape = Shape.list(Shape.plain.string())
|
|
147
|
-
const result =
|
|
147
|
+
const result = convertInputToRef(["hello", "world"], shape)
|
|
148
148
|
|
|
149
149
|
expect(isContainer(result)).toBe(true)
|
|
150
150
|
expect(isLoroList(result as any)).toBe(true)
|
|
@@ -157,7 +157,7 @@ describe("Conversion Functions", () => {
|
|
|
157
157
|
|
|
158
158
|
it("should convert array to LoroList with container items", () => {
|
|
159
159
|
const shape = Shape.list(Shape.text())
|
|
160
|
-
const result =
|
|
160
|
+
const result = convertInputToRef(["first", "second"], shape)
|
|
161
161
|
|
|
162
162
|
expect(isLoroList(result as any)).toBe(true)
|
|
163
163
|
const list = result as LoroList
|
|
@@ -172,7 +172,7 @@ describe("Conversion Functions", () => {
|
|
|
172
172
|
|
|
173
173
|
it("should handle empty array", () => {
|
|
174
174
|
const shape = Shape.list(Shape.plain.string())
|
|
175
|
-
const result =
|
|
175
|
+
const result = convertInputToRef([], shape)
|
|
176
176
|
|
|
177
177
|
expect(isLoroList(result as any)).toBe(true)
|
|
178
178
|
const list = result as LoroList
|
|
@@ -181,7 +181,7 @@ describe("Conversion Functions", () => {
|
|
|
181
181
|
|
|
182
182
|
it("should handle mixed value types in list", () => {
|
|
183
183
|
const shape = Shape.list(Shape.plain.number())
|
|
184
|
-
const result =
|
|
184
|
+
const result = convertInputToRef([1, 2.5, -3, 0], shape)
|
|
185
185
|
|
|
186
186
|
expect(isLoroList(result as any)).toBe(true)
|
|
187
187
|
const list = result as LoroList
|
|
@@ -195,7 +195,7 @@ describe("Conversion Functions", () => {
|
|
|
195
195
|
it("should return plain array for value shape", () => {
|
|
196
196
|
const shape = Shape.plain.array(Shape.plain.string())
|
|
197
197
|
const input = ["hello", "world"]
|
|
198
|
-
const result =
|
|
198
|
+
const result = convertInputToRef(input, shape)
|
|
199
199
|
|
|
200
200
|
expect(isContainer(result)).toBe(false)
|
|
201
201
|
expect(Array.isArray(result)).toBe(true)
|
|
@@ -204,7 +204,7 @@ describe("Conversion Functions", () => {
|
|
|
204
204
|
|
|
205
205
|
it("should handle nested container conversion", () => {
|
|
206
206
|
const shape = Shape.list(Shape.counter())
|
|
207
|
-
const result =
|
|
207
|
+
const result = convertInputToRef([5, 10, 15], shape)
|
|
208
208
|
|
|
209
209
|
expect(isLoroList(result as any)).toBe(true)
|
|
210
210
|
const list = result as LoroList
|
|
@@ -217,16 +217,16 @@ describe("Conversion Functions", () => {
|
|
|
217
217
|
it("should throw error for non-array input", () => {
|
|
218
218
|
const shape = Shape.list(Shape.plain.string())
|
|
219
219
|
|
|
220
|
-
expect(() =>
|
|
220
|
+
expect(() => convertInputToRef("not array" as any, shape)).toThrow(
|
|
221
221
|
"array expected",
|
|
222
222
|
)
|
|
223
|
-
expect(() =>
|
|
223
|
+
expect(() => convertInputToRef(123 as any, shape)).toThrow(
|
|
224
224
|
"array expected",
|
|
225
225
|
)
|
|
226
|
-
expect(() =>
|
|
226
|
+
expect(() => convertInputToRef({} as any, shape)).toThrow(
|
|
227
227
|
"array expected",
|
|
228
228
|
)
|
|
229
|
-
expect(() =>
|
|
229
|
+
expect(() => convertInputToRef(null as any, shape)).toThrow(
|
|
230
230
|
"array expected",
|
|
231
231
|
)
|
|
232
232
|
})
|
|
@@ -235,7 +235,7 @@ describe("Conversion Functions", () => {
|
|
|
235
235
|
describe("convertInputToNode - MovableList Conversion", () => {
|
|
236
236
|
it("should convert array to LoroMovableList with value items", () => {
|
|
237
237
|
const shape = Shape.movableList(Shape.plain.string())
|
|
238
|
-
const result =
|
|
238
|
+
const result = convertInputToRef(["first", "second", "third"], shape)
|
|
239
239
|
|
|
240
240
|
expect(isContainer(result)).toBe(true)
|
|
241
241
|
expect(isLoroMovableList(result as any)).toBe(true)
|
|
@@ -249,7 +249,7 @@ describe("Conversion Functions", () => {
|
|
|
249
249
|
|
|
250
250
|
it("should convert array to LoroMovableList with container items", () => {
|
|
251
251
|
const shape = Shape.movableList(Shape.counter())
|
|
252
|
-
const result =
|
|
252
|
+
const result = convertInputToRef([1, 5, 10], shape)
|
|
253
253
|
|
|
254
254
|
expect(isLoroMovableList(result as any)).toBe(true)
|
|
255
255
|
const list = result as LoroMovableList
|
|
@@ -261,7 +261,7 @@ describe("Conversion Functions", () => {
|
|
|
261
261
|
|
|
262
262
|
it("should handle empty movable list", () => {
|
|
263
263
|
const shape = Shape.movableList(Shape.plain.boolean())
|
|
264
|
-
const result =
|
|
264
|
+
const result = convertInputToRef([], shape)
|
|
265
265
|
|
|
266
266
|
expect(isLoroMovableList(result as any)).toBe(true)
|
|
267
267
|
const list = result as LoroMovableList
|
|
@@ -271,7 +271,7 @@ describe("Conversion Functions", () => {
|
|
|
271
271
|
it("should return plain array for value shape", () => {
|
|
272
272
|
const shape = Shape.plain.array(Shape.plain.number())
|
|
273
273
|
const input = [1, 2, 3]
|
|
274
|
-
const result =
|
|
274
|
+
const result = convertInputToRef(input, shape)
|
|
275
275
|
|
|
276
276
|
expect(isContainer(result)).toBe(false)
|
|
277
277
|
expect(Array.isArray(result)).toBe(true)
|
|
@@ -281,16 +281,16 @@ describe("Conversion Functions", () => {
|
|
|
281
281
|
it("should throw error for non-array input", () => {
|
|
282
282
|
const shape = Shape.movableList(Shape.plain.string())
|
|
283
283
|
|
|
284
|
-
expect(() =>
|
|
284
|
+
expect(() => convertInputToRef("not array" as any, shape)).toThrow(
|
|
285
285
|
"array expected",
|
|
286
286
|
)
|
|
287
|
-
expect(() =>
|
|
287
|
+
expect(() => convertInputToRef(123 as any, shape)).toThrow(
|
|
288
288
|
"array expected",
|
|
289
289
|
)
|
|
290
|
-
expect(() =>
|
|
290
|
+
expect(() => convertInputToRef({} as any, shape)).toThrow(
|
|
291
291
|
"array expected",
|
|
292
292
|
)
|
|
293
|
-
expect(() =>
|
|
293
|
+
expect(() => convertInputToRef(null as any, shape)).toThrow(
|
|
294
294
|
"array expected",
|
|
295
295
|
)
|
|
296
296
|
})
|
|
@@ -310,7 +310,7 @@ describe("Conversion Functions", () => {
|
|
|
310
310
|
active: true,
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
const result =
|
|
313
|
+
const result = convertInputToRef(input, shape)
|
|
314
314
|
|
|
315
315
|
expect(isContainer(result)).toBe(true)
|
|
316
316
|
expect(isLoroMap(result as any)).toBe(true)
|
|
@@ -332,7 +332,7 @@ describe("Conversion Functions", () => {
|
|
|
332
332
|
count: 42,
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
const result =
|
|
335
|
+
const result = convertInputToRef(input, shape)
|
|
336
336
|
|
|
337
337
|
expect(isLoroMap(result as any)).toBe(true)
|
|
338
338
|
const map = result as LoroMap
|
|
@@ -344,7 +344,7 @@ describe("Conversion Functions", () => {
|
|
|
344
344
|
|
|
345
345
|
it("should handle empty object", () => {
|
|
346
346
|
const shape = Shape.map({})
|
|
347
|
-
const result =
|
|
347
|
+
const result = convertInputToRef({}, shape)
|
|
348
348
|
|
|
349
349
|
expect(isLoroMap(result as any)).toBe(true)
|
|
350
350
|
const map = result as LoroMap
|
|
@@ -361,7 +361,7 @@ describe("Conversion Functions", () => {
|
|
|
361
361
|
extraProp: "should be ignored", // This should be set as-is
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
-
const result =
|
|
364
|
+
const result = convertInputToRef(input, shape)
|
|
365
365
|
|
|
366
366
|
expect(isLoroMap(result as any)).toBe(true)
|
|
367
367
|
const map = result as LoroMap
|
|
@@ -389,7 +389,7 @@ describe("Conversion Functions", () => {
|
|
|
389
389
|
},
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
const result =
|
|
392
|
+
const result = convertInputToRef(input, shape)
|
|
393
393
|
|
|
394
394
|
expect(isLoroMap(result as any)).toBe(true)
|
|
395
395
|
const map = result as LoroMap
|
|
@@ -404,7 +404,7 @@ describe("Conversion Functions", () => {
|
|
|
404
404
|
})
|
|
405
405
|
|
|
406
406
|
const input = { name: "John", age: 30 }
|
|
407
|
-
const result =
|
|
407
|
+
const result = convertInputToRef(input, shape)
|
|
408
408
|
|
|
409
409
|
expect(isContainer(result)).toBe(false)
|
|
410
410
|
expect(typeof result).toBe("object")
|
|
@@ -416,16 +416,16 @@ describe("Conversion Functions", () => {
|
|
|
416
416
|
name: Shape.plain.string(),
|
|
417
417
|
})
|
|
418
418
|
|
|
419
|
-
expect(() =>
|
|
419
|
+
expect(() => convertInputToRef("not object" as any, shape)).toThrow(
|
|
420
420
|
"object expected",
|
|
421
421
|
)
|
|
422
|
-
expect(() =>
|
|
422
|
+
expect(() => convertInputToRef(123 as any, shape)).toThrow(
|
|
423
423
|
"object expected",
|
|
424
424
|
)
|
|
425
|
-
expect(() =>
|
|
425
|
+
expect(() => convertInputToRef([] as any, shape)).toThrow(
|
|
426
426
|
"object expected",
|
|
427
427
|
)
|
|
428
|
-
expect(() =>
|
|
428
|
+
expect(() => convertInputToRef(null as any, shape)).toThrow(
|
|
429
429
|
"object expected",
|
|
430
430
|
)
|
|
431
431
|
})
|
|
@@ -438,10 +438,10 @@ describe("Conversion Functions", () => {
|
|
|
438
438
|
const booleanShape = Shape.plain.boolean()
|
|
439
439
|
const nullShape = Shape.plain.null()
|
|
440
440
|
|
|
441
|
-
expect(
|
|
442
|
-
expect(
|
|
443
|
-
expect(
|
|
444
|
-
expect(
|
|
441
|
+
expect(convertInputToRef("hello", stringShape)).toBe("hello")
|
|
442
|
+
expect(convertInputToRef(42, numberShape)).toBe(42)
|
|
443
|
+
expect(convertInputToRef(true, booleanShape)).toBe(true)
|
|
444
|
+
expect(convertInputToRef(null, nullShape)).toBe(null)
|
|
445
445
|
})
|
|
446
446
|
|
|
447
447
|
it("should handle complex value shapes", () => {
|
|
@@ -454,15 +454,15 @@ describe("Conversion Functions", () => {
|
|
|
454
454
|
const arrayInput = ["a", "b", "c"]
|
|
455
455
|
const objectInput = { name: "test", count: 5 }
|
|
456
456
|
|
|
457
|
-
expect(
|
|
458
|
-
expect(
|
|
457
|
+
expect(convertInputToRef(arrayInput, arrayShape)).toEqual(arrayInput)
|
|
458
|
+
expect(convertInputToRef(objectInput, objectShape)).toEqual(objectInput)
|
|
459
459
|
})
|
|
460
460
|
|
|
461
461
|
it("should handle Uint8Array values", () => {
|
|
462
462
|
const shape = Shape.plain.uint8Array()
|
|
463
463
|
const input = new Uint8Array([1, 2, 3, 4])
|
|
464
464
|
|
|
465
|
-
const result =
|
|
465
|
+
const result = convertInputToRef(input, shape)
|
|
466
466
|
expect(result).toBe(input)
|
|
467
467
|
expect(result instanceof Uint8Array).toBe(true)
|
|
468
468
|
})
|
|
@@ -472,7 +472,7 @@ describe("Conversion Functions", () => {
|
|
|
472
472
|
it("should throw error for tree type (unimplemented)", () => {
|
|
473
473
|
const shape = Shape.tree(Shape.map({}))
|
|
474
474
|
|
|
475
|
-
expect(() =>
|
|
475
|
+
expect(() => convertInputToRef({}, shape)).toThrow(
|
|
476
476
|
"tree type unimplemented",
|
|
477
477
|
)
|
|
478
478
|
})
|
|
@@ -480,7 +480,7 @@ describe("Conversion Functions", () => {
|
|
|
480
480
|
it("should throw error for invalid value shape", () => {
|
|
481
481
|
const invalidShape = { _type: "value", valueType: "invalid" } as any
|
|
482
482
|
|
|
483
|
-
expect(() =>
|
|
483
|
+
expect(() => convertInputToRef("test", invalidShape)).toThrow(
|
|
484
484
|
"value expected",
|
|
485
485
|
)
|
|
486
486
|
})
|
|
@@ -515,7 +515,7 @@ describe("Conversion Functions", () => {
|
|
|
515
515
|
},
|
|
516
516
|
]
|
|
517
517
|
|
|
518
|
-
const result =
|
|
518
|
+
const result = convertInputToRef(input, shape)
|
|
519
519
|
|
|
520
520
|
expect(isLoroList(result as any)).toBe(true)
|
|
521
521
|
const list = result as LoroList
|
|
@@ -545,7 +545,7 @@ describe("Conversion Functions", () => {
|
|
|
545
545
|
},
|
|
546
546
|
}
|
|
547
547
|
|
|
548
|
-
const result =
|
|
548
|
+
const result = convertInputToRef(input, shape)
|
|
549
549
|
|
|
550
550
|
expect(isLoroMap(result as any)).toBe(true)
|
|
551
551
|
const map = result as LoroMap
|
|
@@ -564,7 +564,7 @@ describe("Conversion Functions", () => {
|
|
|
564
564
|
[7, 8, 9],
|
|
565
565
|
]
|
|
566
566
|
|
|
567
|
-
const result =
|
|
567
|
+
const result = convertInputToRef(input, shape)
|
|
568
568
|
|
|
569
569
|
expect(isLoroList(result as any)).toBe(true)
|
|
570
570
|
const outerList = result as LoroList
|
|
@@ -585,7 +585,7 @@ describe("Conversion Functions", () => {
|
|
|
585
585
|
{ id: "2", title: "Task 2", completed: true },
|
|
586
586
|
]
|
|
587
587
|
|
|
588
|
-
const result =
|
|
588
|
+
const result = convertInputToRef(input, shape)
|
|
589
589
|
|
|
590
590
|
expect(isLoroMovableList(result as any)).toBe(true)
|
|
591
591
|
const list = result as LoroMovableList
|
|
@@ -598,8 +598,8 @@ describe("Conversion Functions", () => {
|
|
|
598
598
|
const nullShape = Shape.plain.null()
|
|
599
599
|
const undefinedShape = Shape.plain.undefined()
|
|
600
600
|
|
|
601
|
-
expect(
|
|
602
|
-
expect(
|
|
601
|
+
expect(convertInputToRef(null, nullShape)).toBe(null)
|
|
602
|
+
expect(convertInputToRef(undefined, undefinedShape)).toBe(undefined)
|
|
603
603
|
})
|
|
604
604
|
|
|
605
605
|
it("should handle empty containers", () => {
|
|
@@ -607,9 +607,9 @@ describe("Conversion Functions", () => {
|
|
|
607
607
|
const emptyMapShape = Shape.map({})
|
|
608
608
|
const emptyMovableListShape = Shape.movableList(Shape.plain.number())
|
|
609
609
|
|
|
610
|
-
const emptyList =
|
|
611
|
-
const emptyMap =
|
|
612
|
-
const emptyMovableList =
|
|
610
|
+
const emptyList = convertInputToRef([], emptyListShape)
|
|
611
|
+
const emptyMap = convertInputToRef({}, emptyMapShape)
|
|
612
|
+
const emptyMovableList = convertInputToRef([], emptyMovableListShape)
|
|
613
613
|
|
|
614
614
|
expect(isLoroList(emptyList as any)).toBe(true)
|
|
615
615
|
expect((emptyList as LoroList).length).toBe(0)
|
|
@@ -624,7 +624,7 @@ describe("Conversion Functions", () => {
|
|
|
624
624
|
it("should handle very large numbers", () => {
|
|
625
625
|
const shape = Shape.counter()
|
|
626
626
|
const largeNumber = Number.MAX_SAFE_INTEGER
|
|
627
|
-
const result =
|
|
627
|
+
const result = convertInputToRef(largeNumber, shape)
|
|
628
628
|
|
|
629
629
|
expect(isLoroCounter(result as any)).toBe(true)
|
|
630
630
|
expect((result as LoroCounter).value).toBe(largeNumber)
|
|
@@ -633,7 +633,7 @@ describe("Conversion Functions", () => {
|
|
|
633
633
|
it("should handle very long strings", () => {
|
|
634
634
|
const shape = Shape.text()
|
|
635
635
|
const longString = "a".repeat(10000)
|
|
636
|
-
const result =
|
|
636
|
+
const result = convertInputToRef(longString, shape)
|
|
637
637
|
|
|
638
638
|
expect(isLoroText(result as any)).toBe(true)
|
|
639
639
|
expect((result as LoroText).toString()).toBe(longString)
|
|
@@ -643,7 +643,7 @@ describe("Conversion Functions", () => {
|
|
|
643
643
|
it("should handle arrays with many items", () => {
|
|
644
644
|
const shape = Shape.list(Shape.plain.number())
|
|
645
645
|
const largeArray = Array.from({ length: 1000 }, (_, i) => i)
|
|
646
|
-
const result =
|
|
646
|
+
const result = convertInputToRef(largeArray, shape)
|
|
647
647
|
|
|
648
648
|
expect(isLoroList(result as any)).toBe(true)
|
|
649
649
|
const list = result as LoroList
|
|
@@ -663,7 +663,7 @@ describe("Conversion Functions", () => {
|
|
|
663
663
|
}
|
|
664
664
|
|
|
665
665
|
const shape = Shape.map(shapes)
|
|
666
|
-
const result =
|
|
666
|
+
const result = convertInputToRef(input, shape)
|
|
667
667
|
|
|
668
668
|
expect(isLoroMap(result as any)).toBe(true)
|
|
669
669
|
const map = result as LoroMap
|
|
@@ -676,7 +676,7 @@ describe("Conversion Functions", () => {
|
|
|
676
676
|
describe("convertInputToNode - Type Safety", () => {
|
|
677
677
|
it("should maintain referential integrity for containers", () => {
|
|
678
678
|
const shape = Shape.list(Shape.text())
|
|
679
|
-
const result =
|
|
679
|
+
const result = convertInputToRef(["test"], shape)
|
|
680
680
|
|
|
681
681
|
expect(isLoroList(result as any)).toBe(true)
|
|
682
682
|
const list = result as LoroList
|
|
@@ -688,8 +688,8 @@ describe("Conversion Functions", () => {
|
|
|
688
688
|
|
|
689
689
|
it("should create independent container instances", () => {
|
|
690
690
|
const shape = Shape.counter()
|
|
691
|
-
const result1 =
|
|
692
|
-
const result2 =
|
|
691
|
+
const result1 = convertInputToRef(5, shape)
|
|
692
|
+
const result2 = convertInputToRef(5, shape)
|
|
693
693
|
|
|
694
694
|
expect(isLoroCounter(result1 as any)).toBe(true)
|
|
695
695
|
expect(isLoroCounter(result2 as any)).toBe(true)
|
|
@@ -718,7 +718,7 @@ describe("Conversion Functions", () => {
|
|
|
718
718
|
children: ["child1", "child2"],
|
|
719
719
|
}
|
|
720
720
|
|
|
721
|
-
const result =
|
|
721
|
+
const result = convertInputToRef(input, shape)
|
|
722
722
|
|
|
723
723
|
expect(isLoroMap(result as any)).toBe(true)
|
|
724
724
|
const map = result as LoroMap
|
package/src/conversion.ts
CHANGED
|
@@ -59,7 +59,7 @@ function convertListInput(
|
|
|
59
59
|
const list = new LoroList()
|
|
60
60
|
|
|
61
61
|
for (const item of value) {
|
|
62
|
-
const convertedItem =
|
|
62
|
+
const convertedItem = convertInputToRef(item, shape.shape)
|
|
63
63
|
if (isContainer(convertedItem)) {
|
|
64
64
|
list.pushContainer(convertedItem)
|
|
65
65
|
} else {
|
|
@@ -85,7 +85,7 @@ function convertMovableListInput(
|
|
|
85
85
|
const list = new LoroMovableList()
|
|
86
86
|
|
|
87
87
|
for (const item of value) {
|
|
88
|
-
const convertedItem =
|
|
88
|
+
const convertedItem = convertInputToRef(item, shape.shape)
|
|
89
89
|
if (isContainer(convertedItem)) {
|
|
90
90
|
list.pushContainer(convertedItem)
|
|
91
91
|
} else {
|
|
@@ -111,7 +111,7 @@ function convertMapInput(
|
|
|
111
111
|
for (const [k, v] of Object.entries(value)) {
|
|
112
112
|
const nestedSchema = shape.shapes[k]
|
|
113
113
|
if (nestedSchema) {
|
|
114
|
-
const convertedValue =
|
|
114
|
+
const convertedValue = convertInputToRef(v, nestedSchema)
|
|
115
115
|
if (isContainer(convertedValue)) {
|
|
116
116
|
map.setContainer(k, convertedValue)
|
|
117
117
|
} else {
|
|
@@ -138,7 +138,7 @@ function convertRecordInput(
|
|
|
138
138
|
|
|
139
139
|
const map = new LoroMap()
|
|
140
140
|
for (const [k, v] of Object.entries(value)) {
|
|
141
|
-
const convertedValue =
|
|
141
|
+
const convertedValue = convertInputToRef(v, shape.shape)
|
|
142
142
|
if (isContainer(convertedValue)) {
|
|
143
143
|
map.setContainer(k, convertedValue)
|
|
144
144
|
} else {
|
|
@@ -153,7 +153,7 @@ function convertRecordInput(
|
|
|
153
153
|
* Main conversion function that transforms input values to appropriate CRDT containers
|
|
154
154
|
* based on schema definitions
|
|
155
155
|
*/
|
|
156
|
-
export function
|
|
156
|
+
export function convertInputToRef<Shape extends ContainerOrValueShape>(
|
|
157
157
|
value: Value,
|
|
158
158
|
shape: Shape,
|
|
159
159
|
): Container | Value {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expectTypeOf, it } from "vitest"
|
|
2
|
+
import { Shape } from "./shape.js"
|
|
3
|
+
|
|
4
|
+
describe("Discriminated Union Placeholder Issue", () => {
|
|
5
|
+
it("should allow discriminated union with placeholder in a map", () => {
|
|
6
|
+
const PauseReasonSchema = Shape.plain.string()
|
|
7
|
+
const ActiveModeSchema = Shape.plain.string()
|
|
8
|
+
|
|
9
|
+
const SessionPhaseSchema = Shape.plain
|
|
10
|
+
.discriminatedUnion("phase", {
|
|
11
|
+
"not-started": Shape.plain.object({
|
|
12
|
+
phase: Shape.plain.string("not-started"),
|
|
13
|
+
}),
|
|
14
|
+
|
|
15
|
+
lobby: Shape.plain.object({
|
|
16
|
+
phase: Shape.plain.string("lobby"),
|
|
17
|
+
}),
|
|
18
|
+
"lobby-paused": Shape.plain.object({
|
|
19
|
+
phase: Shape.plain.string("lobby-paused"),
|
|
20
|
+
reason: PauseReasonSchema,
|
|
21
|
+
}),
|
|
22
|
+
|
|
23
|
+
active: Shape.plain.object({
|
|
24
|
+
phase: Shape.plain.string("active"),
|
|
25
|
+
mode: ActiveModeSchema,
|
|
26
|
+
}),
|
|
27
|
+
"active-paused": Shape.plain.object({
|
|
28
|
+
phase: Shape.plain.string("active-paused"),
|
|
29
|
+
mode: ActiveModeSchema,
|
|
30
|
+
reason: PauseReasonSchema,
|
|
31
|
+
}),
|
|
32
|
+
|
|
33
|
+
ended: Shape.plain.object({
|
|
34
|
+
phase: Shape.plain.string("ended"),
|
|
35
|
+
}),
|
|
36
|
+
})
|
|
37
|
+
.placeholder({ phase: "not-started" })
|
|
38
|
+
|
|
39
|
+
const PhaseTransitionSchema = Shape.map({
|
|
40
|
+
phase: SessionPhaseSchema,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
expectTypeOf(PhaseTransitionSchema).not.toBeNever()
|
|
44
|
+
})
|
|
45
|
+
})
|