@loro-extended/change 5.2.0 → 5.4.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 +46 -9
- package/dist/index.d.ts +195 -13
- package/dist/index.js +402 -22
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/conversion.ts +40 -4
- package/src/functional-helpers.test.ts +125 -0
- package/src/functional-helpers.ts +121 -7
- package/src/index.ts +14 -10
- package/src/loro.ts +2 -1
- package/src/nested-container-materialization.test.ts +336 -0
- package/src/replay-diff.test.ts +389 -0
- package/src/replay-diff.ts +229 -0
- package/src/shallow-fork.test.ts +302 -0
- package/src/shape.ts +21 -0
- package/src/typed-doc-ownkeys.test.ts +116 -0
- package/src/typed-doc.ts +10 -4
- package/src/typed-refs/base.ts +25 -4
- package/src/typed-refs/counter-ref-internals.ts +7 -2
- package/src/typed-refs/doc-ref-ownkeys.test.ts +78 -0
- package/src/typed-refs/list-ref-base-internals.ts +2 -1
- package/src/typed-refs/list-ref-base.ts +2 -1
- package/src/typed-refs/record-ref-internals.ts +104 -2
- package/src/typed-refs/record-ref.test.ts +522 -1
- package/src/typed-refs/record-ref.ts +72 -3
- package/src/typed-refs/struct-ref-internals.ts +28 -3
- package/src/typed-refs/text-ref-internals.ts +2 -2
- package/src/typed-refs/tree-node-ref-internals.ts +14 -2
- package/src/typed-refs/tree-ref-internals.ts +2 -1
- package/src/typed-refs/utils.ts +65 -8
package/dist/index.js
CHANGED
|
@@ -99,6 +99,11 @@ function deriveValueShapePlaceholder(shape) {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
// src/functional-helpers.ts
|
|
103
|
+
import {
|
|
104
|
+
LoroDoc as LoroDoc2
|
|
105
|
+
} from "loro-crdt";
|
|
106
|
+
|
|
102
107
|
// src/loro.ts
|
|
103
108
|
var LORO_SYMBOL = /* @__PURE__ */ Symbol.for("loro-extended:loro");
|
|
104
109
|
function loro(refOrDoc) {
|
|
@@ -112,7 +117,9 @@ function loro(refOrDoc) {
|
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
// src/typed-doc.ts
|
|
115
|
-
import {
|
|
120
|
+
import {
|
|
121
|
+
LoroDoc
|
|
122
|
+
} from "loro-crdt";
|
|
116
123
|
|
|
117
124
|
// src/json-patch.ts
|
|
118
125
|
function normalizePath(path) {
|
|
@@ -495,6 +502,7 @@ var BaseRefInternals = class {
|
|
|
495
502
|
}
|
|
496
503
|
cachedContainer;
|
|
497
504
|
loroNamespace;
|
|
505
|
+
_suppressAutoCommit = false;
|
|
498
506
|
/** Get the underlying Loro container (cached) */
|
|
499
507
|
getContainer() {
|
|
500
508
|
if (!this.cachedContainer) {
|
|
@@ -502,12 +510,23 @@ var BaseRefInternals = class {
|
|
|
502
510
|
}
|
|
503
511
|
return this.cachedContainer;
|
|
504
512
|
}
|
|
505
|
-
/** Commit changes if autoCommit is enabled */
|
|
513
|
+
/** Commit changes if autoCommit is enabled and not suppressed */
|
|
506
514
|
commitIfAuto() {
|
|
507
|
-
if (this.params.autoCommit) {
|
|
515
|
+
if (this.params.autoCommit && !this._suppressAutoCommit) {
|
|
508
516
|
this.params.getDoc().commit();
|
|
509
517
|
}
|
|
510
518
|
}
|
|
519
|
+
/**
|
|
520
|
+
* Temporarily suppress auto-commit during batch operations.
|
|
521
|
+
* Used by assignPlainValueToTypedRef() to batch multiple property assignments.
|
|
522
|
+
*/
|
|
523
|
+
setSuppressAutoCommit(suppress) {
|
|
524
|
+
this._suppressAutoCommit = suppress;
|
|
525
|
+
}
|
|
526
|
+
/** Check if auto-commit is currently suppressed */
|
|
527
|
+
isSuppressAutoCommit() {
|
|
528
|
+
return this._suppressAutoCommit;
|
|
529
|
+
}
|
|
511
530
|
/** Get the shape for this ref */
|
|
512
531
|
getShape() {
|
|
513
532
|
return this.params.shape;
|
|
@@ -552,6 +571,10 @@ var BaseRefInternals = class {
|
|
|
552
571
|
}
|
|
553
572
|
return this.loroNamespace;
|
|
554
573
|
}
|
|
574
|
+
/** Force materialization of the container and its nested containers */
|
|
575
|
+
materialize() {
|
|
576
|
+
this.getContainer();
|
|
577
|
+
}
|
|
555
578
|
/** Create the loro() namespace object - subclasses override for specific types */
|
|
556
579
|
createLoroNamespace() {
|
|
557
580
|
const self = this;
|
|
@@ -722,17 +745,38 @@ function convertStructInput(value, shape) {
|
|
|
722
745
|
return value;
|
|
723
746
|
}
|
|
724
747
|
const map = new LoroMap();
|
|
725
|
-
for (const
|
|
748
|
+
for (const k of Object.keys(shape.shapes)) {
|
|
726
749
|
const nestedSchema = shape.shapes[k];
|
|
727
|
-
|
|
750
|
+
const v = value[k];
|
|
751
|
+
if (v !== void 0) {
|
|
728
752
|
const convertedValue = convertInputToRef(v, nestedSchema);
|
|
729
753
|
if (isContainer(convertedValue)) {
|
|
730
754
|
map.setContainer(k, convertedValue);
|
|
731
755
|
} else {
|
|
732
756
|
map.set(k, convertedValue);
|
|
733
757
|
}
|
|
734
|
-
} else {
|
|
735
|
-
|
|
758
|
+
} else if (isContainerShape(nestedSchema)) {
|
|
759
|
+
let emptyValue;
|
|
760
|
+
if (nestedSchema._type === "struct" || nestedSchema._type === "record") {
|
|
761
|
+
emptyValue = {};
|
|
762
|
+
} else if (nestedSchema._type === "list" || nestedSchema._type === "movableList") {
|
|
763
|
+
emptyValue = [];
|
|
764
|
+
} else if (nestedSchema._type === "text") {
|
|
765
|
+
emptyValue = "";
|
|
766
|
+
} else if (nestedSchema._type === "counter") {
|
|
767
|
+
emptyValue = 0;
|
|
768
|
+
}
|
|
769
|
+
if (emptyValue !== void 0) {
|
|
770
|
+
const convertedValue = convertInputToRef(emptyValue, nestedSchema);
|
|
771
|
+
if (isContainer(convertedValue)) {
|
|
772
|
+
map.setContainer(k, convertedValue);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
for (const [k, v] of Object.entries(value)) {
|
|
778
|
+
if (!shape.shapes[k]) {
|
|
779
|
+
map.set(k, v);
|
|
736
780
|
}
|
|
737
781
|
}
|
|
738
782
|
return map;
|
|
@@ -1368,7 +1412,6 @@ var RecordRefInternals = class extends BaseRefInternals {
|
|
|
1368
1412
|
} else {
|
|
1369
1413
|
const ref = this.getOrCreateRef(key);
|
|
1370
1414
|
if (assignPlainValueToTypedRef(ref, value)) {
|
|
1371
|
-
this.commitIfAuto();
|
|
1372
1415
|
return;
|
|
1373
1416
|
}
|
|
1374
1417
|
throw new Error(
|
|
@@ -1383,6 +1426,80 @@ var RecordRefInternals = class extends BaseRefInternals {
|
|
|
1383
1426
|
this.refCache.delete(key);
|
|
1384
1427
|
this.commitIfAuto();
|
|
1385
1428
|
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Replace entire contents with new values.
|
|
1431
|
+
* Keys not in `values` are removed.
|
|
1432
|
+
*/
|
|
1433
|
+
replace(values) {
|
|
1434
|
+
const container = this.getContainer();
|
|
1435
|
+
const currentKeys = new Set(container.keys());
|
|
1436
|
+
const newKeys = new Set(Object.keys(values));
|
|
1437
|
+
const wasSuppressed = this.isSuppressAutoCommit();
|
|
1438
|
+
if (!wasSuppressed) {
|
|
1439
|
+
this.setSuppressAutoCommit(true);
|
|
1440
|
+
}
|
|
1441
|
+
try {
|
|
1442
|
+
for (const key of currentKeys) {
|
|
1443
|
+
if (!newKeys.has(key)) {
|
|
1444
|
+
container.delete(key);
|
|
1445
|
+
this.refCache.delete(key);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
for (const key of newKeys) {
|
|
1449
|
+
this.set(key, values[key]);
|
|
1450
|
+
}
|
|
1451
|
+
} finally {
|
|
1452
|
+
if (!wasSuppressed) {
|
|
1453
|
+
this.setSuppressAutoCommit(false);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
this.commitIfAuto();
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Merge values into record.
|
|
1460
|
+
* Existing keys not in `values` are kept.
|
|
1461
|
+
*/
|
|
1462
|
+
merge(values) {
|
|
1463
|
+
const wasSuppressed = this.isSuppressAutoCommit();
|
|
1464
|
+
if (!wasSuppressed) {
|
|
1465
|
+
this.setSuppressAutoCommit(true);
|
|
1466
|
+
}
|
|
1467
|
+
try {
|
|
1468
|
+
for (const key of Object.keys(values)) {
|
|
1469
|
+
this.set(key, values[key]);
|
|
1470
|
+
}
|
|
1471
|
+
} finally {
|
|
1472
|
+
if (!wasSuppressed) {
|
|
1473
|
+
this.setSuppressAutoCommit(false);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
this.commitIfAuto();
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Remove all entries from the record.
|
|
1480
|
+
*/
|
|
1481
|
+
clear() {
|
|
1482
|
+
const container = this.getContainer();
|
|
1483
|
+
const keys = container.keys();
|
|
1484
|
+
if (keys.length === 0) {
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1487
|
+
const wasSuppressed = this.isSuppressAutoCommit();
|
|
1488
|
+
if (!wasSuppressed) {
|
|
1489
|
+
this.setSuppressAutoCommit(true);
|
|
1490
|
+
}
|
|
1491
|
+
try {
|
|
1492
|
+
for (const key of keys) {
|
|
1493
|
+
container.delete(key);
|
|
1494
|
+
this.refCache.delete(key);
|
|
1495
|
+
}
|
|
1496
|
+
} finally {
|
|
1497
|
+
if (!wasSuppressed) {
|
|
1498
|
+
this.setSuppressAutoCommit(false);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
this.commitIfAuto();
|
|
1502
|
+
}
|
|
1386
1503
|
/** Absorb mutated plain values back into Loro containers */
|
|
1387
1504
|
absorbPlainValues() {
|
|
1388
1505
|
absorbCachedPlainValues(this.refCache, () => this.getContainer());
|
|
@@ -1447,14 +1564,77 @@ var RecordRef = class extends TypedRef {
|
|
|
1447
1564
|
const container = this[INTERNAL_SYMBOL].getContainer();
|
|
1448
1565
|
return container.keys();
|
|
1449
1566
|
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Returns an array of all values in the record.
|
|
1569
|
+
* For container-valued records, returns properly typed refs.
|
|
1570
|
+
*/
|
|
1450
1571
|
values() {
|
|
1451
|
-
|
|
1452
|
-
|
|
1572
|
+
return this.keys().map(
|
|
1573
|
+
(key) => this.get(key)
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
* Returns an array of [key, value] pairs.
|
|
1578
|
+
* For container-valued records, values are properly typed refs.
|
|
1579
|
+
*/
|
|
1580
|
+
entries() {
|
|
1581
|
+
return this.keys().map((key) => [
|
|
1582
|
+
key,
|
|
1583
|
+
this.get(key)
|
|
1584
|
+
]);
|
|
1453
1585
|
}
|
|
1454
1586
|
get size() {
|
|
1455
1587
|
const container = this[INTERNAL_SYMBOL].getContainer();
|
|
1456
1588
|
return container.size;
|
|
1457
1589
|
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Replace entire contents with new values.
|
|
1592
|
+
* Keys not in `values` are removed.
|
|
1593
|
+
*
|
|
1594
|
+
* @example
|
|
1595
|
+
* ```typescript
|
|
1596
|
+
* doc.change(draft => {
|
|
1597
|
+
* draft.players.replace({
|
|
1598
|
+
* alice: { score: 100 },
|
|
1599
|
+
* bob: { score: 50 }
|
|
1600
|
+
* })
|
|
1601
|
+
* })
|
|
1602
|
+
* ```
|
|
1603
|
+
*/
|
|
1604
|
+
replace(values) {
|
|
1605
|
+
this[INTERNAL_SYMBOL].replace(values);
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Merge values into record.
|
|
1609
|
+
* Existing keys not in `values` are kept.
|
|
1610
|
+
*
|
|
1611
|
+
* @example
|
|
1612
|
+
* ```typescript
|
|
1613
|
+
* doc.change(draft => {
|
|
1614
|
+
* // Adds charlie, updates alice, keeps bob unchanged
|
|
1615
|
+
* draft.players.merge({
|
|
1616
|
+
* alice: { score: 150 },
|
|
1617
|
+
* charlie: { score: 25 }
|
|
1618
|
+
* })
|
|
1619
|
+
* })
|
|
1620
|
+
* ```
|
|
1621
|
+
*/
|
|
1622
|
+
merge(values) {
|
|
1623
|
+
this[INTERNAL_SYMBOL].merge(values);
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Remove all entries from the record.
|
|
1627
|
+
*
|
|
1628
|
+
* @example
|
|
1629
|
+
* ```typescript
|
|
1630
|
+
* doc.change(draft => {
|
|
1631
|
+
* draft.players.clear()
|
|
1632
|
+
* })
|
|
1633
|
+
* ```
|
|
1634
|
+
*/
|
|
1635
|
+
clear() {
|
|
1636
|
+
this[INTERNAL_SYMBOL].clear();
|
|
1637
|
+
}
|
|
1458
1638
|
toJSON() {
|
|
1459
1639
|
return serializeRefToJSON(this, this.keys());
|
|
1460
1640
|
}
|
|
@@ -1543,7 +1723,6 @@ var StructRefInternals = class extends BaseRefInternals {
|
|
|
1543
1723
|
} else {
|
|
1544
1724
|
const ref = this.getOrCreateRef(key, shape);
|
|
1545
1725
|
if (assignPlainValueToTypedRef(ref, value)) {
|
|
1546
|
-
this.commitIfAuto();
|
|
1547
1726
|
return;
|
|
1548
1727
|
}
|
|
1549
1728
|
throw new Error(
|
|
@@ -1565,6 +1744,18 @@ var StructRefInternals = class extends BaseRefInternals {
|
|
|
1565
1744
|
() => this.getContainer()
|
|
1566
1745
|
);
|
|
1567
1746
|
}
|
|
1747
|
+
/** Force materialization of the container and its nested containers */
|
|
1748
|
+
materialize() {
|
|
1749
|
+
this.getContainer();
|
|
1750
|
+
const structShape = this.getShape();
|
|
1751
|
+
for (const key in structShape.shapes) {
|
|
1752
|
+
const shape = structShape.shapes[key];
|
|
1753
|
+
if (!isValueShape(shape)) {
|
|
1754
|
+
const ref = this.getOrCreateRef(key, shape);
|
|
1755
|
+
ref[INTERNAL_SYMBOL].materialize();
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1568
1759
|
/** Create the loro namespace for struct */
|
|
1569
1760
|
createLoroNamespace() {
|
|
1570
1761
|
const self = this;
|
|
@@ -1920,6 +2111,11 @@ var TreeNodeRefInternals = class {
|
|
|
1920
2111
|
this.dataRef[INTERNAL_SYMBOL].absorbPlainValues();
|
|
1921
2112
|
}
|
|
1922
2113
|
}
|
|
2114
|
+
/** Force materialization of the container and its nested containers */
|
|
2115
|
+
materialize() {
|
|
2116
|
+
const dataRef = this.getOrCreateDataRef();
|
|
2117
|
+
dataRef[INTERNAL_SYMBOL].materialize();
|
|
2118
|
+
}
|
|
1923
2119
|
/** Get the loro namespace (cached) */
|
|
1924
2120
|
getLoroNamespace() {
|
|
1925
2121
|
if (!this.loroNamespace) {
|
|
@@ -2371,24 +2567,54 @@ function createContainerTypedRef(params) {
|
|
|
2371
2567
|
);
|
|
2372
2568
|
}
|
|
2373
2569
|
}
|
|
2374
|
-
function assignPlainValueToTypedRef(ref, value) {
|
|
2375
|
-
const
|
|
2570
|
+
function assignPlainValueToTypedRef(ref, value, skipCommit = false) {
|
|
2571
|
+
const internals = ref[INTERNAL_SYMBOL];
|
|
2572
|
+
if (internals) {
|
|
2573
|
+
internals.materialize();
|
|
2574
|
+
}
|
|
2575
|
+
const shape = internals?.getShape?.() ?? ref.shape;
|
|
2376
2576
|
const shapeType = shape?._type;
|
|
2377
2577
|
if (shapeType === "struct" || shapeType === "record") {
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2578
|
+
const wasSuppressed = internals?.isSuppressAutoCommit?.() ?? false;
|
|
2579
|
+
if (internals && !wasSuppressed) {
|
|
2580
|
+
internals.setSuppressAutoCommit(true);
|
|
2581
|
+
}
|
|
2582
|
+
try {
|
|
2583
|
+
for (const k in value) {
|
|
2584
|
+
;
|
|
2585
|
+
ref[k] = value[k];
|
|
2586
|
+
}
|
|
2587
|
+
} finally {
|
|
2588
|
+
if (internals && !wasSuppressed) {
|
|
2589
|
+
internals.setSuppressAutoCommit(false);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
if (!skipCommit && internals?.getAutoCommit?.()) {
|
|
2593
|
+
internals.getDoc().commit();
|
|
2381
2594
|
}
|
|
2382
2595
|
return true;
|
|
2383
2596
|
}
|
|
2384
2597
|
if (shapeType === "list" || shapeType === "movableList") {
|
|
2385
2598
|
if (Array.isArray(value)) {
|
|
2386
2599
|
const listRef = ref;
|
|
2387
|
-
|
|
2388
|
-
|
|
2600
|
+
const wasSuppressed = internals?.isSuppressAutoCommit?.() ?? false;
|
|
2601
|
+
if (internals && !wasSuppressed) {
|
|
2602
|
+
internals.setSuppressAutoCommit(true);
|
|
2603
|
+
}
|
|
2604
|
+
try {
|
|
2605
|
+
if (listRef.length > 0) {
|
|
2606
|
+
listRef.delete(0, listRef.length);
|
|
2607
|
+
}
|
|
2608
|
+
for (const item of value) {
|
|
2609
|
+
listRef.push(item);
|
|
2610
|
+
}
|
|
2611
|
+
} finally {
|
|
2612
|
+
if (internals && !wasSuppressed) {
|
|
2613
|
+
internals.setSuppressAutoCommit(false);
|
|
2614
|
+
}
|
|
2389
2615
|
}
|
|
2390
|
-
|
|
2391
|
-
|
|
2616
|
+
if (!skipCommit && internals?.getAutoCommit?.()) {
|
|
2617
|
+
internals.getDoc().commit();
|
|
2392
2618
|
}
|
|
2393
2619
|
return true;
|
|
2394
2620
|
}
|
|
@@ -2861,9 +3087,10 @@ function createTypedDoc(shape, existingDoc) {
|
|
|
2861
3087
|
return true;
|
|
2862
3088
|
return Reflect.has(target, prop);
|
|
2863
3089
|
},
|
|
2864
|
-
// Support Object.keys() -
|
|
3090
|
+
// Support Object.keys() - filter out Symbol properties to allow proxies to be used
|
|
3091
|
+
// in place of plain objects. This prevents React's "Object keys must be strings" error.
|
|
2865
3092
|
ownKeys(target) {
|
|
2866
|
-
return Reflect.ownKeys(target);
|
|
3093
|
+
return Reflect.ownKeys(target).filter((key) => typeof key === "string");
|
|
2867
3094
|
},
|
|
2868
3095
|
getOwnPropertyDescriptor(target, prop) {
|
|
2869
3096
|
if (prop === "change") {
|
|
@@ -2927,12 +3154,34 @@ function getLoroDoc(docOrRef) {
|
|
|
2927
3154
|
function getLoroContainer(ref) {
|
|
2928
3155
|
return loro(ref).container;
|
|
2929
3156
|
}
|
|
3157
|
+
function fork(doc, options) {
|
|
3158
|
+
const loroDoc = loro(doc).doc;
|
|
3159
|
+
const forkedLoroDoc = loroDoc.fork();
|
|
3160
|
+
const shape = loro(doc).docShape;
|
|
3161
|
+
if (options?.preservePeerId) {
|
|
3162
|
+
forkedLoroDoc.setPeerId(loroDoc.peerId);
|
|
3163
|
+
}
|
|
3164
|
+
return createTypedDoc(shape, forkedLoroDoc);
|
|
3165
|
+
}
|
|
2930
3166
|
function forkAt(doc, frontiers) {
|
|
2931
3167
|
const loroDoc = loro(doc).doc;
|
|
2932
3168
|
const forkedLoroDoc = loroDoc.forkAt(frontiers);
|
|
2933
3169
|
const shape = loro(doc).docShape;
|
|
2934
3170
|
return createTypedDoc(shape, forkedLoroDoc);
|
|
2935
3171
|
}
|
|
3172
|
+
function shallowForkAt(doc, frontiers, options) {
|
|
3173
|
+
const loroDoc = loro(doc).doc;
|
|
3174
|
+
const shape = loro(doc).docShape;
|
|
3175
|
+
const shallowBytes = loroDoc.export({
|
|
3176
|
+
mode: "shallow-snapshot",
|
|
3177
|
+
frontiers
|
|
3178
|
+
});
|
|
3179
|
+
const shallowLoroDoc = LoroDoc2.fromSnapshot(shallowBytes);
|
|
3180
|
+
if (options?.preservePeerId) {
|
|
3181
|
+
shallowLoroDoc.setPeerId(loroDoc.peerId);
|
|
3182
|
+
}
|
|
3183
|
+
return createTypedDoc(shape, shallowLoroDoc);
|
|
3184
|
+
}
|
|
2936
3185
|
|
|
2937
3186
|
// src/path-builder.ts
|
|
2938
3187
|
function createPathSelector(segments) {
|
|
@@ -3104,6 +3353,134 @@ function createPlaceholderProxy(target) {
|
|
|
3104
3353
|
});
|
|
3105
3354
|
}
|
|
3106
3355
|
|
|
3356
|
+
// src/replay-diff.ts
|
|
3357
|
+
function replayDiff(doc, diff) {
|
|
3358
|
+
const containerMap = /* @__PURE__ */ new Map();
|
|
3359
|
+
for (const [containerId, containerDiff] of diff) {
|
|
3360
|
+
let container = containerMap.get(containerId);
|
|
3361
|
+
if (!container) {
|
|
3362
|
+
container = doc.getContainerById(containerId);
|
|
3363
|
+
}
|
|
3364
|
+
if (!container) {
|
|
3365
|
+
continue;
|
|
3366
|
+
}
|
|
3367
|
+
switch (containerDiff.type) {
|
|
3368
|
+
case "text":
|
|
3369
|
+
replayTextDiff(container, containerDiff);
|
|
3370
|
+
break;
|
|
3371
|
+
case "list":
|
|
3372
|
+
replayListDiff(
|
|
3373
|
+
container,
|
|
3374
|
+
containerDiff,
|
|
3375
|
+
containerMap
|
|
3376
|
+
);
|
|
3377
|
+
break;
|
|
3378
|
+
case "map":
|
|
3379
|
+
replayMapDiff(container, containerDiff, containerMap);
|
|
3380
|
+
break;
|
|
3381
|
+
case "tree":
|
|
3382
|
+
replayTreeDiff(container, containerDiff);
|
|
3383
|
+
break;
|
|
3384
|
+
case "counter":
|
|
3385
|
+
replayCounterDiff(container, containerDiff);
|
|
3386
|
+
break;
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
function replayTextDiff(text, diff) {
|
|
3391
|
+
text.applyDelta(diff.diff);
|
|
3392
|
+
}
|
|
3393
|
+
function replayListDiff(list, diff, containerMap) {
|
|
3394
|
+
let index = 0;
|
|
3395
|
+
for (const delta of diff.diff) {
|
|
3396
|
+
if (delta.retain !== void 0) {
|
|
3397
|
+
index += delta.retain;
|
|
3398
|
+
} else if (delta.delete !== void 0) {
|
|
3399
|
+
list.delete(index, delta.delete);
|
|
3400
|
+
} else if (delta.insert !== void 0) {
|
|
3401
|
+
const values = delta.insert;
|
|
3402
|
+
for (let i = 0; i < values.length; i++) {
|
|
3403
|
+
const value = values[i];
|
|
3404
|
+
if (isContainer2(value)) {
|
|
3405
|
+
const newContainer = createContainerOfSameType(value);
|
|
3406
|
+
const insertedContainer = list.insertContainer(
|
|
3407
|
+
index + i,
|
|
3408
|
+
newContainer
|
|
3409
|
+
);
|
|
3410
|
+
containerMap.set(value.id, insertedContainer);
|
|
3411
|
+
} else {
|
|
3412
|
+
;
|
|
3413
|
+
list.insert(
|
|
3414
|
+
index + i,
|
|
3415
|
+
value
|
|
3416
|
+
);
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
index += values.length;
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
function replayMapDiff(map, diff, containerMap) {
|
|
3424
|
+
for (const [key, value] of Object.entries(diff.updated)) {
|
|
3425
|
+
if (value === void 0) {
|
|
3426
|
+
map.delete(key);
|
|
3427
|
+
} else if (isContainer2(value)) {
|
|
3428
|
+
const newContainer = createContainerOfSameType(value);
|
|
3429
|
+
const insertedContainer = map.setContainer(key, newContainer);
|
|
3430
|
+
containerMap.set(value.id, insertedContainer);
|
|
3431
|
+
} else {
|
|
3432
|
+
map.set(key, value);
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
function replayTreeDiff(tree, diff) {
|
|
3437
|
+
for (const item of diff.diff) {
|
|
3438
|
+
replayTreeDiffItem(tree, item);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
function replayTreeDiffItem(tree, item) {
|
|
3442
|
+
switch (item.action) {
|
|
3443
|
+
case "create":
|
|
3444
|
+
tree.createNode(item.parent, item.index);
|
|
3445
|
+
break;
|
|
3446
|
+
case "delete":
|
|
3447
|
+
tree.delete(item.target);
|
|
3448
|
+
break;
|
|
3449
|
+
case "move":
|
|
3450
|
+
tree.move(item.target, item.parent, item.index);
|
|
3451
|
+
break;
|
|
3452
|
+
}
|
|
3453
|
+
}
|
|
3454
|
+
function replayCounterDiff(counter, diff) {
|
|
3455
|
+
if (diff.increment > 0) {
|
|
3456
|
+
counter.increment(diff.increment);
|
|
3457
|
+
} else if (diff.increment < 0) {
|
|
3458
|
+
counter.decrement(-diff.increment);
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
function isContainer2(value) {
|
|
3462
|
+
return value !== null && typeof value === "object" && "kind" in value && typeof value.kind === "function";
|
|
3463
|
+
}
|
|
3464
|
+
function createContainerOfSameType(container) {
|
|
3465
|
+
const kind = container.kind();
|
|
3466
|
+
switch (kind) {
|
|
3467
|
+
case "List":
|
|
3468
|
+
return new container.constructor();
|
|
3469
|
+
case "Map":
|
|
3470
|
+
return new container.constructor();
|
|
3471
|
+
case "Text":
|
|
3472
|
+
return new container.constructor();
|
|
3473
|
+
case "Tree":
|
|
3474
|
+
return new container.constructor();
|
|
3475
|
+
case "Counter":
|
|
3476
|
+
return new container.constructor();
|
|
3477
|
+
case "MovableList":
|
|
3478
|
+
return new container.constructor();
|
|
3479
|
+
default:
|
|
3480
|
+
throw new Error(`Unknown container kind: ${kind}`);
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3107
3484
|
// src/shape.ts
|
|
3108
3485
|
function makeNullable(shape) {
|
|
3109
3486
|
const nullShape = {
|
|
@@ -3543,6 +3920,7 @@ export {
|
|
|
3543
3920
|
deriveShapePlaceholder,
|
|
3544
3921
|
evaluatePath,
|
|
3545
3922
|
evaluatePathOnValue,
|
|
3923
|
+
fork,
|
|
3546
3924
|
forkAt,
|
|
3547
3925
|
getLoroContainer,
|
|
3548
3926
|
getLoroDoc,
|
|
@@ -3550,6 +3928,8 @@ export {
|
|
|
3550
3928
|
loro,
|
|
3551
3929
|
mergeValue,
|
|
3552
3930
|
overlayPlaceholder,
|
|
3931
|
+
replayDiff,
|
|
3932
|
+
shallowForkAt,
|
|
3553
3933
|
validatePlaceholder
|
|
3554
3934
|
};
|
|
3555
3935
|
//# sourceMappingURL=index.js.map
|