@loro-extended/change 0.9.0 → 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/dist/index.js CHANGED
@@ -143,12 +143,21 @@ function mergeValue(shape, crdtValue, placeholderValue) {
143
143
  }
144
144
  switch (shape._type) {
145
145
  case "text":
146
- return crdtValue ?? placeholderValue ?? "";
146
+ return crdtValue !== void 0 ? crdtValue : placeholderValue ?? "";
147
147
  case "counter":
148
- return crdtValue ?? placeholderValue ?? 0;
148
+ return crdtValue !== void 0 ? crdtValue : placeholderValue ?? 0;
149
149
  case "list":
150
- case "movableList":
151
- return crdtValue ?? placeholderValue ?? [];
150
+ case "movableList": {
151
+ if (crdtValue === void 0) {
152
+ return placeholderValue ?? [];
153
+ }
154
+ const crdtArray = crdtValue;
155
+ const itemShape = shape.shape;
156
+ const itemPlaceholder = deriveShapePlaceholder(itemShape);
157
+ return crdtArray.map(
158
+ (item) => mergeValue(itemShape, item, itemPlaceholder)
159
+ );
160
+ }
152
161
  case "map": {
153
162
  if (!isObjectValue(crdtValue) && crdtValue !== void 0) {
154
163
  throw new Error("map crdt must be object");
@@ -171,14 +180,31 @@ function mergeValue(shape, crdtValue, placeholderValue) {
171
180
  return result;
172
181
  }
173
182
  case "tree":
174
- return crdtValue ?? placeholderValue ?? [];
183
+ return crdtValue !== void 0 ? crdtValue : placeholderValue ?? [];
184
+ case "record": {
185
+ if (!isObjectValue(crdtValue) && crdtValue !== void 0) {
186
+ throw new Error("record crdt must be object");
187
+ }
188
+ const crdtRecordValue = crdtValue ?? {};
189
+ const result = {};
190
+ for (const key of Object.keys(crdtRecordValue)) {
191
+ const nestedCrdtValue = crdtRecordValue[key];
192
+ const nestedPlaceholderValue = deriveShapePlaceholder(shape.shape);
193
+ result[key] = mergeValue(
194
+ shape.shape,
195
+ nestedCrdtValue,
196
+ nestedPlaceholderValue
197
+ );
198
+ }
199
+ return result;
200
+ }
175
201
  default:
176
202
  if (shape._type === "value" && shape.valueType === "object") {
177
203
  const crdtObj = crdtValue ?? {};
178
204
  const placeholderObj = placeholderValue ?? {};
179
205
  const result = { ...placeholderObj };
180
206
  if (typeof crdtObj !== "object" || crdtObj === null) {
181
- return crdtValue ?? placeholderValue;
207
+ return crdtValue !== void 0 ? crdtValue : placeholderValue;
182
208
  }
183
209
  for (const [key, propShape] of Object.entries(shape.shape)) {
184
210
  const propCrdt = crdtObj[key];
@@ -194,7 +220,7 @@ function mergeValue(shape, crdtValue, placeholderValue) {
194
220
  placeholderValue
195
221
  );
196
222
  }
197
- return crdtValue ?? placeholderValue;
223
+ return crdtValue !== void 0 ? crdtValue : placeholderValue;
198
224
  }
199
225
  }
200
226
  function mergeDiscriminatedUnion(shape, crdtValue, placeholderValue) {
@@ -206,7 +232,7 @@ function mergeDiscriminatedUnion(shape, crdtValue, placeholderValue) {
206
232
  }
207
233
  const variantShape = shape.variants[discriminantValue];
208
234
  if (!variantShape) {
209
- return crdtValue ?? placeholderValue;
235
+ return crdtValue !== void 0 ? crdtValue : placeholderValue;
210
236
  }
211
237
  const placeholderDiscriminant = placeholderObj[shape.discriminantKey];
212
238
  const effectivePlaceholderValue = placeholderDiscriminant === discriminantValue ? placeholderValue : void 0;
@@ -678,6 +704,15 @@ var TypedRef = class {
678
704
  get readonly() {
679
705
  return !!this._params.readonly;
680
706
  }
707
+ /**
708
+ * Throws an error if this ref is in readonly mode.
709
+ * Call this at the start of any mutating method.
710
+ */
711
+ assertMutable() {
712
+ if (this.readonly) {
713
+ throw new Error("Cannot modify readonly ref");
714
+ }
715
+ }
681
716
  get container() {
682
717
  if (!this._cachedContainer) {
683
718
  const container = this._params.getContainer();
@@ -688,16 +723,26 @@ var TypedRef = class {
688
723
  }
689
724
  };
690
725
 
726
+ // src/typed-refs/utils.ts
727
+ import {
728
+ LoroCounter as LoroCounter2,
729
+ LoroList as LoroList2,
730
+ LoroMap as LoroMap2,
731
+ LoroMovableList as LoroMovableList2,
732
+ LoroText as LoroText2,
733
+ LoroTree
734
+ } from "loro-crdt";
735
+
691
736
  // src/typed-refs/counter.ts
692
737
  var CounterRef = class extends TypedRef {
693
738
  absorbPlainValues() {
694
739
  }
695
740
  increment(value) {
696
- if (this.readonly) throw new Error("Cannot modify readonly ref");
741
+ this.assertMutable();
697
742
  this.container.increment(value);
698
743
  }
699
744
  decrement(value) {
700
- if (this.readonly) throw new Error("Cannot modify readonly ref");
745
+ this.assertMutable();
701
746
  this.container.decrement(value);
702
747
  }
703
748
  get value() {
@@ -732,7 +777,7 @@ function convertListInput(value, shape) {
732
777
  }
733
778
  const list = new LoroList();
734
779
  for (const item of value) {
735
- const convertedItem = convertInputToNode(item, shape.shape);
780
+ const convertedItem = convertInputToRef(item, shape.shape);
736
781
  if (isContainer(convertedItem)) {
737
782
  list.pushContainer(convertedItem);
738
783
  } else {
@@ -747,7 +792,7 @@ function convertMovableListInput(value, shape) {
747
792
  }
748
793
  const list = new LoroMovableList();
749
794
  for (const item of value) {
750
- const convertedItem = convertInputToNode(item, shape.shape);
795
+ const convertedItem = convertInputToRef(item, shape.shape);
751
796
  if (isContainer(convertedItem)) {
752
797
  list.pushContainer(convertedItem);
753
798
  } else {
@@ -764,7 +809,7 @@ function convertMapInput(value, shape) {
764
809
  for (const [k, v] of Object.entries(value)) {
765
810
  const nestedSchema = shape.shapes[k];
766
811
  if (nestedSchema) {
767
- const convertedValue = convertInputToNode(v, nestedSchema);
812
+ const convertedValue = convertInputToRef(v, nestedSchema);
768
813
  if (isContainer(convertedValue)) {
769
814
  map.setContainer(k, convertedValue);
770
815
  } else {
@@ -782,7 +827,7 @@ function convertRecordInput(value, shape) {
782
827
  }
783
828
  const map = new LoroMap();
784
829
  for (const [k, v] of Object.entries(value)) {
785
- const convertedValue = convertInputToNode(v, shape.shape);
830
+ const convertedValue = convertInputToRef(v, shape.shape);
786
831
  if (isContainer(convertedValue)) {
787
832
  map.setContainer(k, convertedValue);
788
833
  } else {
@@ -791,7 +836,7 @@ function convertRecordInput(value, shape) {
791
836
  }
792
837
  return map;
793
838
  }
794
- function convertInputToNode(value, shape) {
839
+ function convertInputToRef(value, shape) {
795
840
  switch (shape._type) {
796
841
  case "text": {
797
842
  if (typeof value !== "string") {
@@ -868,7 +913,7 @@ var ListRefBase = class extends TypedRef {
868
913
  this.itemCache.clear();
869
914
  }
870
915
  insertWithConversion(index, item) {
871
- const convertedItem = convertInputToNode(item, this.shape.shape);
916
+ const convertedItem = convertInputToRef(item, this.shape.shape);
872
917
  if (isContainer(convertedItem)) {
873
918
  this.container.insertContainer(index, convertedItem);
874
919
  } else {
@@ -876,7 +921,7 @@ var ListRefBase = class extends TypedRef {
876
921
  }
877
922
  }
878
923
  pushWithConversion(item) {
879
- const convertedItem = convertInputToNode(item, this.shape.shape);
924
+ const convertedItem = convertInputToRef(item, this.shape.shape);
880
925
  if (isContainer(convertedItem)) {
881
926
  this.container.pushContainer(convertedItem);
882
927
  } else {
@@ -949,13 +994,10 @@ var ListRefBase = class extends TypedRef {
949
994
  );
950
995
  this.itemCache.set(index, cachedItem);
951
996
  if (this.readonly) {
952
- const shape = this.shape.shape;
953
- if (shape._type === "counter") {
954
- return cachedItem.value;
955
- }
956
- if (shape._type === "text") {
957
- return cachedItem.toString();
958
- }
997
+ return unwrapReadonlyPrimitive(
998
+ cachedItem,
999
+ this.shape.shape
1000
+ );
959
1001
  }
960
1002
  return cachedItem;
961
1003
  }
@@ -1033,25 +1075,25 @@ var ListRefBase = class extends TypedRef {
1033
1075
  return result;
1034
1076
  }
1035
1077
  insert(index, item) {
1036
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1078
+ this.assertMutable();
1037
1079
  this.updateCacheForInsert(index);
1038
1080
  this.insertWithConversion(index, item);
1039
1081
  }
1040
1082
  delete(index, len) {
1041
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1083
+ this.assertMutable();
1042
1084
  this.updateCacheForDelete(index, len);
1043
1085
  this.container.delete(index, len);
1044
1086
  }
1045
1087
  push(item) {
1046
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1088
+ this.assertMutable();
1047
1089
  this.pushWithConversion(item);
1048
1090
  }
1049
1091
  pushContainer(container) {
1050
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1092
+ this.assertMutable();
1051
1093
  return this.container.pushContainer(container);
1052
1094
  }
1053
1095
  insertContainer(index, container) {
1054
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1096
+ this.assertMutable();
1055
1097
  return this.container.insertContainer(index, container);
1056
1098
  }
1057
1099
  get(index) {
@@ -1065,6 +1107,16 @@ var ListRefBase = class extends TypedRef {
1065
1107
  return result;
1066
1108
  }
1067
1109
  toJSON() {
1110
+ if (this.readonly) {
1111
+ const nativeJson = this.container.toJSON();
1112
+ if (isContainerShape(this.shape.shape) || isValueShape(this.shape.shape) && this.shape.shape.valueType === "object") {
1113
+ const itemPlaceholder = deriveShapePlaceholder(this.shape.shape);
1114
+ return nativeJson.map(
1115
+ (item) => mergeValue(this.shape.shape, item, itemPlaceholder)
1116
+ );
1117
+ }
1118
+ return nativeJson ?? [];
1119
+ }
1068
1120
  return this.toArray();
1069
1121
  }
1070
1122
  [Symbol.iterator]() {
@@ -1122,23 +1174,6 @@ var ListRef = class extends ListRefBase {
1122
1174
  };
1123
1175
 
1124
1176
  // src/typed-refs/map.ts
1125
- import {
1126
- LoroCounter as LoroCounter2,
1127
- LoroList as LoroList2,
1128
- LoroMap as LoroMap2,
1129
- LoroMovableList as LoroMovableList2,
1130
- LoroText as LoroText2,
1131
- LoroTree
1132
- } from "loro-crdt";
1133
- var containerConstructor = {
1134
- counter: LoroCounter2,
1135
- list: LoroList2,
1136
- map: LoroMap2,
1137
- movableList: LoroMovableList2,
1138
- record: LoroMap2,
1139
- text: LoroText2,
1140
- tree: LoroTree
1141
- };
1142
1177
  var MapRef = class extends TypedRef {
1143
1178
  propertyCache = /* @__PURE__ */ new Map();
1144
1179
  constructor(params) {
@@ -1152,13 +1187,7 @@ var MapRef = class extends TypedRef {
1152
1187
  return super.container;
1153
1188
  }
1154
1189
  absorbPlainValues() {
1155
- for (const [key, node] of this.propertyCache.entries()) {
1156
- if (node instanceof TypedRef) {
1157
- node.absorbPlainValues();
1158
- continue;
1159
- }
1160
- this.container.set(key, node);
1161
- }
1190
+ absorbCachedPlainValues(this.propertyCache, () => this.container);
1162
1191
  }
1163
1192
  getTypedRefParams(key, shape) {
1164
1193
  const placeholder = this.placeholder?.[key];
@@ -1170,57 +1199,52 @@ var MapRef = class extends TypedRef {
1170
1199
  readonly: this.readonly
1171
1200
  };
1172
1201
  }
1173
- getOrCreateNode(key, shape) {
1174
- let node = this.propertyCache.get(key);
1175
- if (!node) {
1202
+ getOrCreateRef(key, shape) {
1203
+ let ref = this.propertyCache.get(key);
1204
+ if (!ref) {
1176
1205
  if (isContainerShape(shape)) {
1177
- node = createContainerTypedRef(this.getTypedRefParams(key, shape));
1178
- this.propertyCache.set(key, node);
1206
+ ref = createContainerTypedRef(this.getTypedRefParams(key, shape));
1207
+ this.propertyCache.set(key, ref);
1179
1208
  } else {
1180
1209
  const containerValue = this.container.get(key);
1181
1210
  if (containerValue !== void 0) {
1182
- node = containerValue;
1211
+ ref = containerValue;
1183
1212
  } else {
1184
1213
  const placeholder = this.placeholder?.[key];
1185
1214
  if (placeholder === void 0) {
1186
1215
  throw new Error("placeholder required");
1187
1216
  }
1188
- node = placeholder;
1217
+ ref = placeholder;
1189
1218
  }
1190
1219
  if (!this.readonly) {
1191
- this.propertyCache.set(key, node);
1220
+ this.propertyCache.set(key, ref);
1192
1221
  }
1193
1222
  }
1194
- if (node === void 0) throw new Error("no container made");
1223
+ if (ref === void 0) throw new Error("no container made");
1195
1224
  }
1196
1225
  if (this.readonly && isContainerShape(shape)) {
1197
1226
  const existing = this.container.get(key);
1198
1227
  if (existing === void 0) {
1199
1228
  return this.placeholder?.[key];
1200
1229
  }
1201
- if (shape._type === "counter") {
1202
- return node.value;
1203
- }
1204
- if (shape._type === "text") {
1205
- return node.toString();
1206
- }
1230
+ return unwrapReadonlyPrimitive(ref, shape);
1207
1231
  }
1208
- return node;
1232
+ return ref;
1209
1233
  }
1210
1234
  createLazyProperties() {
1211
1235
  for (const key in this.shape.shapes) {
1212
1236
  const shape = this.shape.shapes[key];
1213
1237
  Object.defineProperty(this, key, {
1214
- get: () => this.getOrCreateNode(key, shape),
1238
+ get: () => this.getOrCreateRef(key, shape),
1215
1239
  set: (value) => {
1216
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1240
+ this.assertMutable();
1217
1241
  if (isValueShape(shape)) {
1218
1242
  this.container.set(key, value);
1219
1243
  this.propertyCache.set(key, value);
1220
1244
  } else {
1221
1245
  if (value && typeof value === "object") {
1222
- const node = this.getOrCreateNode(key, shape);
1223
- if (assignPlainValueToTypedRef(node, value)) {
1246
+ const ref = this.getOrCreateRef(key, shape);
1247
+ if (assignPlainValueToTypedRef(ref, value)) {
1224
1248
  return;
1225
1249
  }
1226
1250
  }
@@ -1234,31 +1258,26 @@ var MapRef = class extends TypedRef {
1234
1258
  }
1235
1259
  }
1236
1260
  toJSON() {
1237
- const result = {};
1238
- for (const key in this.shape.shapes) {
1239
- const value = this[key];
1240
- if (value && typeof value === "object" && "toJSON" in value) {
1241
- result[key] = value.toJSON();
1242
- } else {
1243
- result[key] = value;
1244
- }
1261
+ if (this.readonly) {
1262
+ const nativeJson = this.container.toJSON();
1263
+ return mergeValue(this.shape, nativeJson, this.placeholder);
1245
1264
  }
1246
- return result;
1265
+ return serializeRefToJSON(this, Object.keys(this.shape.shapes));
1247
1266
  }
1248
- // TOOD(duane): return correct type here
1267
+ // TODO(duane): return correct type here
1249
1268
  get(key) {
1250
1269
  return this.container.get(key);
1251
1270
  }
1252
1271
  set(key, value) {
1253
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1272
+ this.assertMutable();
1254
1273
  this.container.set(key, value);
1255
1274
  }
1256
1275
  setContainer(key, container) {
1257
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1276
+ this.assertMutable();
1258
1277
  return this.container.setContainer(key, container);
1259
1278
  }
1260
1279
  delete(key) {
1261
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1280
+ this.assertMutable();
1262
1281
  this.container.delete(key);
1263
1282
  }
1264
1283
  has(key) {
@@ -1284,11 +1303,11 @@ var MovableListRef = class extends ListRefBase {
1284
1303
  this.container.set(index, value);
1285
1304
  }
1286
1305
  move(from, to) {
1287
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1306
+ this.assertMutable();
1288
1307
  this.container.move(from, to);
1289
1308
  }
1290
1309
  set(index, item) {
1291
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1310
+ this.assertMutable();
1292
1311
  return this.container.set(index, item);
1293
1312
  }
1294
1313
  };
@@ -1374,25 +1393,8 @@ var movableListProxyHandler = {
1374
1393
  };
1375
1394
 
1376
1395
  // src/typed-refs/record.ts
1377
- import {
1378
- LoroCounter as LoroCounter3,
1379
- LoroList as LoroList3,
1380
- LoroMap as LoroMap3,
1381
- LoroMovableList as LoroMovableList3,
1382
- LoroText as LoroText3,
1383
- LoroTree as LoroTree2
1384
- } from "loro-crdt";
1385
- var containerConstructor2 = {
1386
- counter: LoroCounter3,
1387
- list: LoroList3,
1388
- map: LoroMap3,
1389
- movableList: LoroMovableList3,
1390
- record: LoroMap3,
1391
- text: LoroText3,
1392
- tree: LoroTree2
1393
- };
1394
1396
  var RecordRef = class extends TypedRef {
1395
- nodeCache = /* @__PURE__ */ new Map();
1397
+ refCache = /* @__PURE__ */ new Map();
1396
1398
  get shape() {
1397
1399
  return super.shape;
1398
1400
  }
@@ -1400,20 +1402,14 @@ var RecordRef = class extends TypedRef {
1400
1402
  return super.container;
1401
1403
  }
1402
1404
  absorbPlainValues() {
1403
- for (const [key, node] of this.nodeCache.entries()) {
1404
- if (node instanceof TypedRef) {
1405
- node.absorbPlainValues();
1406
- continue;
1407
- }
1408
- this.container.set(key, node);
1409
- }
1405
+ absorbCachedPlainValues(this.refCache, () => this.container);
1410
1406
  }
1411
1407
  getTypedRefParams(key, shape) {
1412
1408
  let placeholder = this.placeholder?.[key];
1413
1409
  if (placeholder === void 0) {
1414
1410
  placeholder = deriveShapePlaceholder(shape);
1415
1411
  }
1416
- const LoroContainer = containerConstructor2[shape._type];
1412
+ const LoroContainer = containerConstructor[shape._type];
1417
1413
  return {
1418
1414
  shape,
1419
1415
  placeholder,
@@ -1421,61 +1417,58 @@ var RecordRef = class extends TypedRef {
1421
1417
  readonly: this.readonly
1422
1418
  };
1423
1419
  }
1424
- getOrCreateNode(key) {
1420
+ getOrCreateRef(key) {
1425
1421
  if (this.readonly && isContainerShape(this.shape.shape)) {
1426
1422
  const existing = this.container.get(key);
1427
1423
  if (existing === void 0) {
1428
1424
  return void 0;
1429
1425
  }
1430
1426
  }
1431
- let node = this.nodeCache.get(key);
1432
- if (!node) {
1427
+ let ref = this.refCache.get(key);
1428
+ if (!ref) {
1433
1429
  const shape = this.shape.shape;
1434
1430
  if (isContainerShape(shape)) {
1435
- node = createContainerTypedRef(
1431
+ ref = createContainerTypedRef(
1436
1432
  this.getTypedRefParams(key, shape)
1437
1433
  );
1438
- this.nodeCache.set(key, node);
1434
+ this.refCache.set(key, ref);
1439
1435
  } else {
1440
1436
  const containerValue = this.container.get(key);
1441
1437
  if (containerValue !== void 0) {
1442
- node = containerValue;
1438
+ ref = containerValue;
1443
1439
  } else {
1444
1440
  const placeholder = this.placeholder?.[key];
1445
1441
  if (placeholder === void 0) {
1446
- node = shape._plain;
1442
+ ref = shape._plain;
1447
1443
  } else {
1448
- node = placeholder;
1444
+ ref = placeholder;
1449
1445
  }
1450
1446
  }
1451
- if (node !== void 0 && !this.readonly) {
1452
- this.nodeCache.set(key, node);
1447
+ if (ref !== void 0 && !this.readonly) {
1448
+ this.refCache.set(key, ref);
1453
1449
  }
1454
1450
  }
1455
1451
  }
1456
1452
  if (this.readonly && isContainerShape(this.shape.shape)) {
1457
- const shape = this.shape.shape;
1458
- if (shape._type === "counter") {
1459
- return node.value;
1460
- }
1461
- if (shape._type === "text") {
1462
- return node.toString();
1463
- }
1453
+ return unwrapReadonlyPrimitive(
1454
+ ref,
1455
+ this.shape.shape
1456
+ );
1464
1457
  }
1465
- return node;
1458
+ return ref;
1466
1459
  }
1467
1460
  get(key) {
1468
- return this.getOrCreateNode(key);
1461
+ return this.getOrCreateRef(key);
1469
1462
  }
1470
1463
  set(key, value) {
1471
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1464
+ this.assertMutable();
1472
1465
  if (isValueShape(this.shape.shape)) {
1473
1466
  this.container.set(key, value);
1474
- this.nodeCache.set(key, value);
1467
+ this.refCache.set(key, value);
1475
1468
  } else {
1476
1469
  if (value && typeof value === "object") {
1477
- const node = this.getOrCreateNode(key);
1478
- if (assignPlainValueToTypedRef(node, value)) {
1470
+ const ref = this.getOrCreateRef(key);
1471
+ if (assignPlainValueToTypedRef(ref, value)) {
1479
1472
  return;
1480
1473
  }
1481
1474
  }
@@ -1485,13 +1478,13 @@ var RecordRef = class extends TypedRef {
1485
1478
  }
1486
1479
  }
1487
1480
  setContainer(key, container) {
1488
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1481
+ this.assertMutable();
1489
1482
  return this.container.setContainer(key, container);
1490
1483
  }
1491
1484
  delete(key) {
1492
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1485
+ this.assertMutable();
1493
1486
  this.container.delete(key);
1494
- this.nodeCache.delete(key);
1487
+ this.refCache.delete(key);
1495
1488
  }
1496
1489
  has(key) {
1497
1490
  return this.container.get(key) !== void 0;
@@ -1506,16 +1499,20 @@ var RecordRef = class extends TypedRef {
1506
1499
  return this.container.size;
1507
1500
  }
1508
1501
  toJSON() {
1509
- const result = {};
1510
- for (const key of this.keys()) {
1511
- const value = this.get(key);
1512
- if (value && typeof value === "object" && "toJSON" in value) {
1513
- result[key] = value.toJSON();
1514
- } else {
1515
- result[key] = value;
1502
+ if (this.readonly) {
1503
+ const nativeJson = this.container.toJSON();
1504
+ const result = {};
1505
+ for (const key of Object.keys(nativeJson)) {
1506
+ const nestedPlaceholderValue = deriveShapePlaceholder(this.shape.shape);
1507
+ result[key] = mergeValue(
1508
+ this.shape.shape,
1509
+ nativeJson[key],
1510
+ nestedPlaceholderValue
1511
+ );
1516
1512
  }
1513
+ return result;
1517
1514
  }
1518
- return result;
1515
+ return serializeRefToJSON(this, this.keys());
1519
1516
  }
1520
1517
  };
1521
1518
 
@@ -1525,11 +1522,11 @@ var TextRef = class extends TypedRef {
1525
1522
  }
1526
1523
  // Text methods
1527
1524
  insert(index, content) {
1528
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1525
+ this.assertMutable();
1529
1526
  this.container.insert(index, content);
1530
1527
  }
1531
1528
  delete(index, len) {
1532
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1529
+ this.assertMutable();
1533
1530
  this.container.delete(index, len);
1534
1531
  }
1535
1532
  toString() {
@@ -1539,22 +1536,22 @@ var TextRef = class extends TypedRef {
1539
1536
  return this.toString();
1540
1537
  }
1541
1538
  update(text) {
1542
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1539
+ this.assertMutable();
1543
1540
  this.container.update(text);
1544
1541
  }
1545
1542
  mark(range, key, value) {
1546
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1543
+ this.assertMutable();
1547
1544
  this.container.mark(range, key, value);
1548
1545
  }
1549
1546
  unmark(range, key) {
1550
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1547
+ this.assertMutable();
1551
1548
  this.container.unmark(range, key);
1552
1549
  }
1553
1550
  toDelta() {
1554
1551
  return this.container.toDelta();
1555
1552
  }
1556
1553
  applyDelta(delta) {
1557
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1554
+ this.assertMutable();
1558
1555
  this.container.applyDelta(delta);
1559
1556
  }
1560
1557
  get length() {
@@ -1567,15 +1564,15 @@ var TreeRef = class extends TypedRef {
1567
1564
  absorbPlainValues() {
1568
1565
  }
1569
1566
  createNode(parent, index) {
1570
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1567
+ this.assertMutable();
1571
1568
  return this.container.createNode(parent, index);
1572
1569
  }
1573
1570
  move(target, parent, index) {
1574
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1571
+ this.assertMutable();
1575
1572
  this.container.move(target, parent, index);
1576
1573
  }
1577
1574
  delete(target) {
1578
- if (this.readonly) throw new Error("Cannot modify readonly ref");
1575
+ this.assertMutable();
1579
1576
  this.container.delete(target);
1580
1577
  }
1581
1578
  has(target) {
@@ -1587,6 +1584,48 @@ var TreeRef = class extends TypedRef {
1587
1584
  };
1588
1585
 
1589
1586
  // src/typed-refs/utils.ts
1587
+ var containerConstructor = {
1588
+ counter: LoroCounter2,
1589
+ list: LoroList2,
1590
+ map: LoroMap2,
1591
+ movableList: LoroMovableList2,
1592
+ record: LoroMap2,
1593
+ // Records use LoroMap as their underlying container
1594
+ text: LoroText2,
1595
+ tree: LoroTree
1596
+ };
1597
+ function unwrapReadonlyPrimitive(ref, shape) {
1598
+ if (shape._type === "counter") {
1599
+ return ref.value;
1600
+ }
1601
+ if (shape._type === "text") {
1602
+ return ref.toString();
1603
+ }
1604
+ return ref;
1605
+ }
1606
+ function absorbCachedPlainValues(cache, getContainer) {
1607
+ let container;
1608
+ for (const [key, ref] of cache.entries()) {
1609
+ if (ref instanceof TypedRef) {
1610
+ ref.absorbPlainValues();
1611
+ } else {
1612
+ if (!container) container = getContainer();
1613
+ container.set(key, ref);
1614
+ }
1615
+ }
1616
+ }
1617
+ function serializeRefToJSON(ref, keys) {
1618
+ const result = {};
1619
+ for (const key of keys) {
1620
+ const value = ref[key];
1621
+ if (value && typeof value === "object" && "toJSON" in value) {
1622
+ result[key] = value.toJSON();
1623
+ } else {
1624
+ result[key] = value;
1625
+ }
1626
+ }
1627
+ return result;
1628
+ }
1590
1629
  function createContainerTypedRef(params) {
1591
1630
  switch (params.shape._type) {
1592
1631
  case "counter":
@@ -1618,23 +1657,23 @@ function createContainerTypedRef(params) {
1618
1657
  );
1619
1658
  }
1620
1659
  }
1621
- function assignPlainValueToTypedRef(node, value) {
1622
- const shapeType = node.shape._type;
1660
+ function assignPlainValueToTypedRef(ref, value) {
1661
+ const shapeType = ref.shape._type;
1623
1662
  if (shapeType === "map" || shapeType === "record") {
1624
1663
  for (const k in value) {
1625
1664
  ;
1626
- node[k] = value[k];
1665
+ ref[k] = value[k];
1627
1666
  }
1628
1667
  return true;
1629
1668
  }
1630
1669
  if (shapeType === "list" || shapeType === "movableList") {
1631
1670
  if (Array.isArray(value)) {
1632
- const listNode = node;
1633
- if (listNode.length > 0) {
1634
- listNode.delete(0, listNode.length);
1671
+ const listRef = ref;
1672
+ if (listRef.length > 0) {
1673
+ listRef.delete(0, listRef.length);
1635
1674
  }
1636
1675
  for (const item of value) {
1637
- listNode.push(item);
1676
+ listRef.push(item);
1638
1677
  }
1639
1678
  return true;
1640
1679
  }
@@ -1684,20 +1723,15 @@ var DocRef = class extends TypedRef {
1684
1723
  return this.requiredPlaceholder[key];
1685
1724
  }
1686
1725
  }
1687
- let node = this.propertyCache.get(key);
1688
- if (!node) {
1689
- node = createContainerTypedRef(this.getTypedRefParams(key, shape));
1690
- this.propertyCache.set(key, node);
1726
+ let ref = this.propertyCache.get(key);
1727
+ if (!ref) {
1728
+ ref = createContainerTypedRef(this.getTypedRefParams(key, shape));
1729
+ this.propertyCache.set(key, ref);
1691
1730
  }
1692
1731
  if (this.readonly) {
1693
- if (shape._type === "counter") {
1694
- return node.value;
1695
- }
1696
- if (shape._type === "text") {
1697
- return node.toString();
1698
- }
1732
+ return unwrapReadonlyPrimitive(ref, shape);
1699
1733
  }
1700
- return node;
1734
+ return ref;
1701
1735
  }
1702
1736
  createLazyProperties() {
1703
1737
  for (const key in this.shape.shapes) {
@@ -1709,20 +1743,14 @@ var DocRef = class extends TypedRef {
1709
1743
  }
1710
1744
  }
1711
1745
  toJSON() {
1712
- const result = {};
1713
- for (const key in this.shape.shapes) {
1714
- const value = this[key];
1715
- if (value && typeof value === "object" && "toJSON" in value) {
1716
- result[key] = value.toJSON();
1717
- } else {
1718
- result[key] = value;
1719
- }
1720
- }
1721
- return result;
1746
+ return serializeRefToJSON(
1747
+ this,
1748
+ Object.keys(this.shape.shapes)
1749
+ );
1722
1750
  }
1723
1751
  absorbPlainValues() {
1724
- for (const [, node] of this.propertyCache.entries()) {
1725
- node.absorbPlainValues();
1752
+ for (const [, ref] of this.propertyCache.entries()) {
1753
+ ref.absorbPlainValues();
1726
1754
  }
1727
1755
  }
1728
1756
  };