@loro-extended/change 1.0.1 → 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 +165 -46
- package/dist/index.d.ts +206 -101
- package/dist/index.js +363 -109
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/any-shape.test.ts +164 -0
- package/src/change.test.ts +255 -2
- package/src/derive-placeholder.ts +8 -0
- package/src/index.ts +15 -2
- package/src/overlay.ts +10 -0
- package/src/path-builder.ts +131 -0
- package/src/path-compiler.ts +64 -0
- package/src/path-evaluator.ts +76 -0
- package/src/path-selector.test.ts +322 -0
- package/src/path-selector.ts +131 -0
- package/src/readonly.test.ts +5 -4
- package/src/shape.ts +256 -40
- package/src/typed-refs/base.ts +6 -0
- package/src/typed-refs/counter.test.ts +2 -1
- package/src/typed-refs/doc.ts +13 -2
- package/src/typed-refs/json-compatibility.test.ts +27 -0
- package/src/typed-refs/list-base.ts +1 -1
- package/src/typed-refs/list.test.ts +1 -1
- package/src/typed-refs/list.ts +5 -2
- package/src/typed-refs/movable-list.test.ts +1 -1
- package/src/typed-refs/movable-list.ts +2 -2
- package/src/typed-refs/record.ts +11 -2
- package/src/typed-refs/struct.ts +9 -0
- package/src/typed-refs/tree.ts +6 -0
- package/src/typed-refs/utils.ts +13 -0
- package/src/validation.ts +9 -0
- package/src/presence-interface.ts +0 -52
- package/src/typed-presence.ts +0 -96
package/dist/index.js
CHANGED
|
@@ -8,6 +8,9 @@ function derivePlaceholder(schema) {
|
|
|
8
8
|
}
|
|
9
9
|
function deriveShapePlaceholder(shape) {
|
|
10
10
|
switch (shape._type) {
|
|
11
|
+
// Any container - no placeholder (undefined)
|
|
12
|
+
case "any":
|
|
13
|
+
return void 0;
|
|
11
14
|
// Leaf containers - use _placeholder directly
|
|
12
15
|
case "text":
|
|
13
16
|
return shape._placeholder;
|
|
@@ -36,6 +39,9 @@ function deriveShapePlaceholder(shape) {
|
|
|
36
39
|
}
|
|
37
40
|
function deriveValueShapePlaceholder(shape) {
|
|
38
41
|
switch (shape.valueType) {
|
|
42
|
+
// Any value - no placeholder (undefined)
|
|
43
|
+
case "any":
|
|
44
|
+
return void 0;
|
|
39
45
|
// Leaf values - use _placeholder directly
|
|
40
46
|
case "string":
|
|
41
47
|
return shape._placeholder;
|
|
@@ -146,6 +152,12 @@ function overlayPlaceholder(shape, crdtValue, placeholderValue) {
|
|
|
146
152
|
return result;
|
|
147
153
|
}
|
|
148
154
|
function mergeValue(shape, crdtValue, placeholderValue) {
|
|
155
|
+
if (shape._type === "any") {
|
|
156
|
+
return crdtValue;
|
|
157
|
+
}
|
|
158
|
+
if (shape._type === "value" && shape.valueType === "any") {
|
|
159
|
+
return crdtValue;
|
|
160
|
+
}
|
|
149
161
|
if (crdtValue === void 0 && placeholderValue === void 0) {
|
|
150
162
|
throw new Error("either crdt or placeholder value must be defined");
|
|
151
163
|
}
|
|
@@ -247,6 +259,154 @@ function mergeDiscriminatedUnion(shape, crdtValue, placeholderValue) {
|
|
|
247
259
|
return mergeValue(variantShape, crdtValue, effectivePlaceholderValue);
|
|
248
260
|
}
|
|
249
261
|
|
|
262
|
+
// src/path-builder.ts
|
|
263
|
+
function createPathSelector(segments) {
|
|
264
|
+
return {
|
|
265
|
+
__resultType: void 0,
|
|
266
|
+
__segments: segments
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function createPathNode(shape, segments) {
|
|
270
|
+
const selector = createPathSelector(segments);
|
|
271
|
+
if (shape._type === "text" || shape._type === "counter") {
|
|
272
|
+
return selector;
|
|
273
|
+
}
|
|
274
|
+
if (shape._type === "value") {
|
|
275
|
+
return selector;
|
|
276
|
+
}
|
|
277
|
+
if (shape._type === "list" || shape._type === "movableList") {
|
|
278
|
+
return Object.assign(selector, {
|
|
279
|
+
get $each() {
|
|
280
|
+
return createPathNode(shape.shape, [...segments, { type: "each" }]);
|
|
281
|
+
},
|
|
282
|
+
$at(index) {
|
|
283
|
+
return createPathNode(shape.shape, [
|
|
284
|
+
...segments,
|
|
285
|
+
{ type: "index", index }
|
|
286
|
+
]);
|
|
287
|
+
},
|
|
288
|
+
get $first() {
|
|
289
|
+
return createPathNode(shape.shape, [
|
|
290
|
+
...segments,
|
|
291
|
+
{ type: "index", index: 0 }
|
|
292
|
+
]);
|
|
293
|
+
},
|
|
294
|
+
get $last() {
|
|
295
|
+
return createPathNode(shape.shape, [
|
|
296
|
+
...segments,
|
|
297
|
+
{ type: "index", index: -1 }
|
|
298
|
+
]);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (shape._type === "struct") {
|
|
303
|
+
const props = {};
|
|
304
|
+
for (const key in shape.shapes) {
|
|
305
|
+
Object.defineProperty(props, key, {
|
|
306
|
+
get() {
|
|
307
|
+
return createPathNode(shape.shapes[key], [
|
|
308
|
+
...segments,
|
|
309
|
+
{ type: "property", key }
|
|
310
|
+
]);
|
|
311
|
+
},
|
|
312
|
+
enumerable: true
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
return Object.assign(selector, props);
|
|
316
|
+
}
|
|
317
|
+
if (shape._type === "record") {
|
|
318
|
+
return Object.assign(selector, {
|
|
319
|
+
get $each() {
|
|
320
|
+
return createPathNode(shape.shape, [...segments, { type: "each" }]);
|
|
321
|
+
},
|
|
322
|
+
$key(key) {
|
|
323
|
+
return createPathNode(shape.shape, [...segments, { type: "key", key }]);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
return selector;
|
|
328
|
+
}
|
|
329
|
+
function createPathBuilder(docShape) {
|
|
330
|
+
const builder = {};
|
|
331
|
+
for (const key in docShape.shapes) {
|
|
332
|
+
Object.defineProperty(builder, key, {
|
|
333
|
+
get() {
|
|
334
|
+
return createPathNode(docShape.shapes[key], [{ type: "property", key }]);
|
|
335
|
+
},
|
|
336
|
+
enumerable: true
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return builder;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/path-compiler.ts
|
|
343
|
+
function compileToJsonPath(segments) {
|
|
344
|
+
let path = "$";
|
|
345
|
+
for (const segment of segments) {
|
|
346
|
+
switch (segment.type) {
|
|
347
|
+
case "property":
|
|
348
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(segment.key)) {
|
|
349
|
+
path += `.${segment.key}`;
|
|
350
|
+
} else {
|
|
351
|
+
path += `["${escapeJsonPathKey(segment.key)}"]`;
|
|
352
|
+
}
|
|
353
|
+
break;
|
|
354
|
+
case "each":
|
|
355
|
+
path += "[*]";
|
|
356
|
+
break;
|
|
357
|
+
case "index":
|
|
358
|
+
path += `[${segment.index}]`;
|
|
359
|
+
break;
|
|
360
|
+
case "key":
|
|
361
|
+
path += `["${escapeJsonPathKey(segment.key)}"]`;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return path;
|
|
366
|
+
}
|
|
367
|
+
function escapeJsonPathKey(key) {
|
|
368
|
+
return key.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
369
|
+
}
|
|
370
|
+
function hasWildcard(segments) {
|
|
371
|
+
return segments.some((s) => s.type === "each");
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/path-evaluator.ts
|
|
375
|
+
function evaluatePath(doc, selector) {
|
|
376
|
+
const json = doc.$.toJSON();
|
|
377
|
+
return evaluatePathOnValue(json, selector.__segments);
|
|
378
|
+
}
|
|
379
|
+
function evaluatePathOnValue(value, segments) {
|
|
380
|
+
if (segments.length === 0) {
|
|
381
|
+
return value;
|
|
382
|
+
}
|
|
383
|
+
const [segment, ...rest] = segments;
|
|
384
|
+
switch (segment.type) {
|
|
385
|
+
case "property":
|
|
386
|
+
case "key":
|
|
387
|
+
if (value == null) return void 0;
|
|
388
|
+
if (typeof value !== "object") return void 0;
|
|
389
|
+
return evaluatePathOnValue(
|
|
390
|
+
value[segment.key],
|
|
391
|
+
rest
|
|
392
|
+
);
|
|
393
|
+
case "index": {
|
|
394
|
+
if (!Array.isArray(value)) return void 0;
|
|
395
|
+
const index = segment.index < 0 ? value.length + segment.index : segment.index;
|
|
396
|
+
if (index < 0 || index >= value.length) return void 0;
|
|
397
|
+
return evaluatePathOnValue(value[index], rest);
|
|
398
|
+
}
|
|
399
|
+
case "each":
|
|
400
|
+
if (Array.isArray(value)) {
|
|
401
|
+
return value.map((item) => evaluatePathOnValue(item, rest));
|
|
402
|
+
}
|
|
403
|
+
if (typeof value === "object" && value !== null) {
|
|
404
|
+
return Object.values(value).map((item) => evaluatePathOnValue(item, rest));
|
|
405
|
+
}
|
|
406
|
+
return [];
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
250
410
|
// src/placeholder-proxy.ts
|
|
251
411
|
function createPlaceholderProxy(target) {
|
|
252
412
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -270,6 +430,29 @@ function createPlaceholderProxy(target) {
|
|
|
270
430
|
}
|
|
271
431
|
|
|
272
432
|
// src/shape.ts
|
|
433
|
+
function makeNullable(shape) {
|
|
434
|
+
const nullShape = {
|
|
435
|
+
_type: "value",
|
|
436
|
+
valueType: "null",
|
|
437
|
+
_plain: null,
|
|
438
|
+
_mutable: null,
|
|
439
|
+
_placeholder: null
|
|
440
|
+
};
|
|
441
|
+
const base = {
|
|
442
|
+
_type: "value",
|
|
443
|
+
valueType: "union",
|
|
444
|
+
shapes: [nullShape, shape],
|
|
445
|
+
_plain: null,
|
|
446
|
+
_mutable: null,
|
|
447
|
+
_placeholder: null
|
|
448
|
+
// Default placeholder is null
|
|
449
|
+
};
|
|
450
|
+
return Object.assign(base, {
|
|
451
|
+
placeholder(value) {
|
|
452
|
+
return { ...base, _placeholder: value };
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
}
|
|
273
456
|
var Shape = {
|
|
274
457
|
doc: (shape) => ({
|
|
275
458
|
_type: "doc",
|
|
@@ -278,6 +461,27 @@ var Shape = {
|
|
|
278
461
|
_mutable: {},
|
|
279
462
|
_placeholder: {}
|
|
280
463
|
}),
|
|
464
|
+
/**
|
|
465
|
+
* Creates an "any" container shape - an escape hatch for untyped containers.
|
|
466
|
+
* Use this when integrating with external libraries that manage their own document structure.
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* ```typescript
|
|
470
|
+
* // loro-prosemirror manages its own structure
|
|
471
|
+
* const ProseMirrorDocShape = Shape.doc({
|
|
472
|
+
* doc: Shape.any(), // opt out of typing for this container
|
|
473
|
+
* })
|
|
474
|
+
*
|
|
475
|
+
* const handle = repo.get(docId, ProseMirrorDocShape, CursorPresenceShape)
|
|
476
|
+
* // handle.doc.doc is typed as `unknown` - you're on your own
|
|
477
|
+
* ```
|
|
478
|
+
*/
|
|
479
|
+
any: () => ({
|
|
480
|
+
_type: "any",
|
|
481
|
+
_plain: void 0,
|
|
482
|
+
_mutable: void 0,
|
|
483
|
+
_placeholder: void 0
|
|
484
|
+
}),
|
|
281
485
|
// CRDTs are represented by Loro Containers--they converge on state using Loro's
|
|
282
486
|
// various CRDT algorithms
|
|
283
487
|
counter: () => {
|
|
@@ -382,6 +586,9 @@ var Shape = {
|
|
|
382
586
|
return Object.assign(base, {
|
|
383
587
|
placeholder(value) {
|
|
384
588
|
return { ...base, _placeholder: value };
|
|
589
|
+
},
|
|
590
|
+
nullable() {
|
|
591
|
+
return makeNullable(base);
|
|
385
592
|
}
|
|
386
593
|
});
|
|
387
594
|
},
|
|
@@ -396,6 +603,9 @@ var Shape = {
|
|
|
396
603
|
return Object.assign(base, {
|
|
397
604
|
placeholder(value) {
|
|
398
605
|
return { ...base, _placeholder: value };
|
|
606
|
+
},
|
|
607
|
+
nullable() {
|
|
608
|
+
return makeNullable(base);
|
|
399
609
|
}
|
|
400
610
|
});
|
|
401
611
|
},
|
|
@@ -410,6 +620,9 @@ var Shape = {
|
|
|
410
620
|
return Object.assign(base, {
|
|
411
621
|
placeholder(value) {
|
|
412
622
|
return { ...base, _placeholder: value };
|
|
623
|
+
},
|
|
624
|
+
nullable() {
|
|
625
|
+
return makeNullable(base);
|
|
413
626
|
}
|
|
414
627
|
});
|
|
415
628
|
},
|
|
@@ -427,12 +640,63 @@ var Shape = {
|
|
|
427
640
|
_mutable: void 0,
|
|
428
641
|
_placeholder: void 0
|
|
429
642
|
}),
|
|
430
|
-
uint8Array: () =>
|
|
643
|
+
uint8Array: () => {
|
|
644
|
+
const base = {
|
|
645
|
+
_type: "value",
|
|
646
|
+
valueType: "uint8array",
|
|
647
|
+
_plain: new Uint8Array(),
|
|
648
|
+
_mutable: new Uint8Array(),
|
|
649
|
+
_placeholder: new Uint8Array()
|
|
650
|
+
};
|
|
651
|
+
return Object.assign(base, {
|
|
652
|
+
nullable() {
|
|
653
|
+
return makeNullable(base);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
},
|
|
657
|
+
/**
|
|
658
|
+
* Alias for `uint8Array()` - creates a shape for binary data.
|
|
659
|
+
* Use this for better discoverability when working with binary data like cursor positions.
|
|
660
|
+
*
|
|
661
|
+
* @example
|
|
662
|
+
* ```typescript
|
|
663
|
+
* const CursorPresenceShape = Shape.plain.struct({
|
|
664
|
+
* anchor: Shape.plain.bytes().nullable(),
|
|
665
|
+
* focus: Shape.plain.bytes().nullable(),
|
|
666
|
+
* })
|
|
667
|
+
* ```
|
|
668
|
+
*/
|
|
669
|
+
bytes: () => {
|
|
670
|
+
const base = {
|
|
671
|
+
_type: "value",
|
|
672
|
+
valueType: "uint8array",
|
|
673
|
+
_plain: new Uint8Array(),
|
|
674
|
+
_mutable: new Uint8Array(),
|
|
675
|
+
_placeholder: new Uint8Array()
|
|
676
|
+
};
|
|
677
|
+
return Object.assign(base, {
|
|
678
|
+
nullable() {
|
|
679
|
+
return makeNullable(base);
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
},
|
|
683
|
+
/**
|
|
684
|
+
* Creates an "any" value shape - an escape hatch for untyped values.
|
|
685
|
+
* Use this when you need to accept any valid Loro value type.
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* ```typescript
|
|
689
|
+
* const FlexiblePresenceShape = Shape.plain.struct({
|
|
690
|
+
* metadata: Shape.plain.any(), // accept any value type
|
|
691
|
+
* })
|
|
692
|
+
* ```
|
|
693
|
+
*/
|
|
694
|
+
any: () => ({
|
|
431
695
|
_type: "value",
|
|
432
|
-
valueType: "
|
|
433
|
-
_plain:
|
|
434
|
-
_mutable:
|
|
435
|
-
_placeholder:
|
|
696
|
+
valueType: "any",
|
|
697
|
+
_plain: void 0,
|
|
698
|
+
_mutable: void 0,
|
|
699
|
+
_placeholder: void 0
|
|
436
700
|
}),
|
|
437
701
|
/**
|
|
438
702
|
* Creates a struct value shape for plain objects with fixed keys.
|
|
@@ -446,41 +710,69 @@ var Shape = {
|
|
|
446
710
|
* })
|
|
447
711
|
* ```
|
|
448
712
|
*/
|
|
449
|
-
struct: (shape) =>
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
713
|
+
struct: (shape) => {
|
|
714
|
+
const base = {
|
|
715
|
+
_type: "value",
|
|
716
|
+
valueType: "struct",
|
|
717
|
+
shape,
|
|
718
|
+
_plain: {},
|
|
719
|
+
_mutable: {},
|
|
720
|
+
_placeholder: {}
|
|
721
|
+
};
|
|
722
|
+
return Object.assign(base, {
|
|
723
|
+
nullable() {
|
|
724
|
+
return makeNullable(base);
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
},
|
|
457
728
|
/**
|
|
458
729
|
* @deprecated Use `Shape.plain.struct` instead. `Shape.plain.struct` will be removed in a future version.
|
|
459
730
|
*/
|
|
460
|
-
object: (shape) =>
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
731
|
+
object: (shape) => {
|
|
732
|
+
const base = {
|
|
733
|
+
_type: "value",
|
|
734
|
+
valueType: "struct",
|
|
735
|
+
shape,
|
|
736
|
+
_plain: {},
|
|
737
|
+
_mutable: {},
|
|
738
|
+
_placeholder: {}
|
|
739
|
+
};
|
|
740
|
+
return Object.assign(base, {
|
|
741
|
+
nullable() {
|
|
742
|
+
return makeNullable(base);
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
},
|
|
746
|
+
record: (shape) => {
|
|
747
|
+
const base = {
|
|
748
|
+
_type: "value",
|
|
749
|
+
valueType: "record",
|
|
750
|
+
shape,
|
|
751
|
+
_plain: {},
|
|
752
|
+
_mutable: {},
|
|
753
|
+
_placeholder: {}
|
|
754
|
+
};
|
|
755
|
+
return Object.assign(base, {
|
|
756
|
+
nullable() {
|
|
757
|
+
return makeNullable(base);
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
},
|
|
761
|
+
array: (shape) => {
|
|
762
|
+
const base = {
|
|
763
|
+
_type: "value",
|
|
764
|
+
valueType: "array",
|
|
765
|
+
shape,
|
|
766
|
+
_plain: [],
|
|
767
|
+
_mutable: [],
|
|
768
|
+
_placeholder: []
|
|
769
|
+
};
|
|
770
|
+
return Object.assign(base, {
|
|
771
|
+
nullable() {
|
|
772
|
+
return makeNullable(base);
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
},
|
|
484
776
|
// Special value type that helps make things like `string | null` representable
|
|
485
777
|
// TODO(duane): should this be a more general type for containers too?
|
|
486
778
|
union: (shapes) => {
|
|
@@ -1408,6 +1700,11 @@ var RecordRef = class extends TypedRef {
|
|
|
1408
1700
|
if (placeholder === void 0) {
|
|
1409
1701
|
placeholder = deriveShapePlaceholder(shape);
|
|
1410
1702
|
}
|
|
1703
|
+
if (!hasContainerConstructor(shape._type)) {
|
|
1704
|
+
throw new Error(
|
|
1705
|
+
`Cannot create typed ref for shape type "${shape._type}". Use Shape.any() only at the document root level.`
|
|
1706
|
+
);
|
|
1707
|
+
}
|
|
1411
1708
|
const LoroContainer = containerConstructor[shape._type];
|
|
1412
1709
|
return {
|
|
1413
1710
|
shape,
|
|
@@ -1550,6 +1847,11 @@ var StructRef = class extends TypedRef {
|
|
|
1550
1847
|
}
|
|
1551
1848
|
getTypedRefParams(key, shape) {
|
|
1552
1849
|
const placeholder = this.placeholder?.[key];
|
|
1850
|
+
if (!hasContainerConstructor(shape._type)) {
|
|
1851
|
+
throw new Error(
|
|
1852
|
+
`Cannot create typed ref for shape type "${shape._type}". Use Shape.any() only at the document root level.`
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1553
1855
|
const LoroContainer = containerConstructor[shape._type];
|
|
1554
1856
|
return {
|
|
1555
1857
|
shape,
|
|
@@ -1746,6 +2048,9 @@ var TextRef = class extends TypedRef {
|
|
|
1746
2048
|
var TreeRef = class extends TypedRef {
|
|
1747
2049
|
absorbPlainValues() {
|
|
1748
2050
|
}
|
|
2051
|
+
toJSON() {
|
|
2052
|
+
return this.container.toJSON();
|
|
2053
|
+
}
|
|
1749
2054
|
createNode(parent, index) {
|
|
1750
2055
|
this.assertMutable();
|
|
1751
2056
|
return this.container.createNode(parent, index);
|
|
@@ -1778,6 +2083,9 @@ var containerConstructor = {
|
|
|
1778
2083
|
text: LoroText2,
|
|
1779
2084
|
tree: LoroTree
|
|
1780
2085
|
};
|
|
2086
|
+
function hasContainerConstructor(type) {
|
|
2087
|
+
return type in containerConstructor;
|
|
2088
|
+
}
|
|
1781
2089
|
function unwrapReadonlyPrimitive(ref, shape) {
|
|
1782
2090
|
if (shape._type === "counter") {
|
|
1783
2091
|
return ref.value;
|
|
@@ -1917,7 +2225,13 @@ var DocRef = class extends TypedRef {
|
|
|
1917
2225
|
this.createLazyProperties();
|
|
1918
2226
|
}
|
|
1919
2227
|
getTypedRefParams(key, shape) {
|
|
1920
|
-
|
|
2228
|
+
if (shape._type === "any") {
|
|
2229
|
+
throw new Error(
|
|
2230
|
+
`Cannot get typed ref params for "any" shape type. The "any" shape is an escape hatch for untyped containers and should be accessed directly via loroDoc.`
|
|
2231
|
+
);
|
|
2232
|
+
}
|
|
2233
|
+
const getterName = containerGetter[shape._type];
|
|
2234
|
+
const getter = this._doc[getterName].bind(this._doc);
|
|
1921
2235
|
return {
|
|
1922
2236
|
shape,
|
|
1923
2237
|
placeholder: this.requiredPlaceholder[key],
|
|
@@ -1972,6 +2286,9 @@ function validateValue(value, schema, path = "") {
|
|
|
1972
2286
|
throw new Error(`Invalid schema at path ${path}: missing _type`);
|
|
1973
2287
|
}
|
|
1974
2288
|
const currentPath = path || "root";
|
|
2289
|
+
if (schema._type === "any") {
|
|
2290
|
+
return value;
|
|
2291
|
+
}
|
|
1975
2292
|
if (schema._type === "text") {
|
|
1976
2293
|
if (typeof value !== "string") {
|
|
1977
2294
|
throw new Error(
|
|
@@ -2039,6 +2356,9 @@ function validateValue(value, schema, path = "") {
|
|
|
2039
2356
|
if (schema._type === "value") {
|
|
2040
2357
|
const valueSchema = schema;
|
|
2041
2358
|
switch (valueSchema.valueType) {
|
|
2359
|
+
// AnyValueShape - no validation, accept anything
|
|
2360
|
+
case "any":
|
|
2361
|
+
return value;
|
|
2042
2362
|
case "string": {
|
|
2043
2363
|
if (typeof value !== "string") {
|
|
2044
2364
|
throw new Error(
|
|
@@ -2352,85 +2672,19 @@ function createTypedDoc(shape, existingDoc) {
|
|
|
2352
2672
|
internal.proxy = proxy;
|
|
2353
2673
|
return proxy;
|
|
2354
2674
|
}
|
|
2355
|
-
|
|
2356
|
-
// src/typed-presence.ts
|
|
2357
|
-
var TypedPresence = class {
|
|
2358
|
-
constructor(shape, presence) {
|
|
2359
|
-
this.shape = shape;
|
|
2360
|
-
this.presence = presence;
|
|
2361
|
-
this.placeholder = deriveShapePlaceholder(shape);
|
|
2362
|
-
}
|
|
2363
|
-
placeholder;
|
|
2364
|
-
/**
|
|
2365
|
-
* Get the current peer's presence state with placeholder values merged in.
|
|
2366
|
-
*/
|
|
2367
|
-
get self() {
|
|
2368
|
-
return mergeValue(
|
|
2369
|
-
this.shape,
|
|
2370
|
-
this.presence.self,
|
|
2371
|
-
this.placeholder
|
|
2372
|
-
);
|
|
2373
|
-
}
|
|
2374
|
-
/**
|
|
2375
|
-
* Get other peers' presence states with placeholder values merged in.
|
|
2376
|
-
* Does NOT include self. Use this for iterating over remote peers.
|
|
2377
|
-
*/
|
|
2378
|
-
get peers() {
|
|
2379
|
-
const result = /* @__PURE__ */ new Map();
|
|
2380
|
-
for (const [peerId, value] of this.presence.peers) {
|
|
2381
|
-
result.set(
|
|
2382
|
-
peerId,
|
|
2383
|
-
mergeValue(this.shape, value, this.placeholder)
|
|
2384
|
-
);
|
|
2385
|
-
}
|
|
2386
|
-
return result;
|
|
2387
|
-
}
|
|
2388
|
-
/**
|
|
2389
|
-
* Get all peers' presence states with placeholder values merged in.
|
|
2390
|
-
* @deprecated Use `peers` and `self` separately. This property is synthesized
|
|
2391
|
-
* from `peers` and `self` for backward compatibility.
|
|
2392
|
-
*/
|
|
2393
|
-
get all() {
|
|
2394
|
-
const result = {};
|
|
2395
|
-
const all = this.presence.all;
|
|
2396
|
-
for (const peerId of Object.keys(all)) {
|
|
2397
|
-
result[peerId] = mergeValue(
|
|
2398
|
-
this.shape,
|
|
2399
|
-
all[peerId],
|
|
2400
|
-
this.placeholder
|
|
2401
|
-
);
|
|
2402
|
-
}
|
|
2403
|
-
return result;
|
|
2404
|
-
}
|
|
2405
|
-
/**
|
|
2406
|
-
* Set presence values for the current peer.
|
|
2407
|
-
*/
|
|
2408
|
-
set(value) {
|
|
2409
|
-
this.presence.set(value);
|
|
2410
|
-
}
|
|
2411
|
-
/**
|
|
2412
|
-
* Subscribe to presence changes.
|
|
2413
|
-
* The callback is called immediately with the current state, then on each change.
|
|
2414
|
-
*
|
|
2415
|
-
* @param cb Callback that receives the typed presence state
|
|
2416
|
-
* @returns Unsubscribe function
|
|
2417
|
-
*/
|
|
2418
|
-
subscribe(cb) {
|
|
2419
|
-
cb({ self: this.self, peers: this.peers, all: this.all });
|
|
2420
|
-
return this.presence.subscribe(() => {
|
|
2421
|
-
cb({ self: this.self, peers: this.peers, all: this.all });
|
|
2422
|
-
});
|
|
2423
|
-
}
|
|
2424
|
-
};
|
|
2425
2675
|
export {
|
|
2426
2676
|
Shape,
|
|
2427
|
-
TypedPresence,
|
|
2428
2677
|
change,
|
|
2678
|
+
compileToJsonPath,
|
|
2679
|
+
createPathBuilder,
|
|
2429
2680
|
createPlaceholderProxy,
|
|
2430
2681
|
createTypedDoc,
|
|
2431
2682
|
derivePlaceholder,
|
|
2432
2683
|
deriveShapePlaceholder,
|
|
2684
|
+
evaluatePath,
|
|
2685
|
+
evaluatePathOnValue,
|
|
2433
2686
|
getLoroDoc,
|
|
2687
|
+
hasWildcard,
|
|
2434
2688
|
mergeValue,
|
|
2435
2689
|
overlayPlaceholder,
|
|
2436
2690
|
validatePlaceholder
|