@loro-extended/change 4.0.0 → 5.1.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.
Files changed (56) hide show
  1. package/README.md +173 -149
  2. package/dist/index.d.ts +962 -335
  3. package/dist/index.js +1040 -598
  4. package/dist/index.js.map +1 -1
  5. package/package.json +4 -4
  6. package/src/change.test.ts +51 -52
  7. package/src/functional-helpers.test.ts +316 -4
  8. package/src/functional-helpers.ts +96 -6
  9. package/src/grand-unified-api.test.ts +35 -29
  10. package/src/index.ts +25 -1
  11. package/src/json-patch.test.ts +46 -27
  12. package/src/loro.test.ts +449 -0
  13. package/src/loro.ts +273 -0
  14. package/src/overlay-recursion.test.ts +1 -1
  15. package/src/path-evaluator.ts +1 -1
  16. package/src/path-selector.test.ts +94 -1
  17. package/src/shape.ts +47 -15
  18. package/src/typed-doc.ts +99 -98
  19. package/src/typed-refs/base.ts +126 -35
  20. package/src/typed-refs/counter-ref-internals.ts +62 -0
  21. package/src/typed-refs/{counter.test.ts → counter-ref.test.ts} +5 -4
  22. package/src/typed-refs/counter-ref.ts +45 -0
  23. package/src/typed-refs/{doc.ts → doc-ref-internals.ts} +33 -38
  24. package/src/typed-refs/doc-ref.ts +47 -0
  25. package/src/typed-refs/encapsulation.test.ts +226 -0
  26. package/src/typed-refs/list-ref-base-internals.ts +280 -0
  27. package/src/typed-refs/{list-base.ts → list-ref-base.ts} +255 -160
  28. package/src/typed-refs/list-ref-internals.ts +21 -0
  29. package/src/typed-refs/{list.ts → list-ref.ts} +10 -11
  30. package/src/typed-refs/movable-list-ref-internals.ts +38 -0
  31. package/src/typed-refs/movable-list-ref.ts +31 -0
  32. package/src/typed-refs/proxy-handlers.ts +13 -4
  33. package/src/typed-refs/{record.ts → record-ref-internals.ts} +78 -79
  34. package/src/typed-refs/{record.test.ts → record-ref.test.ts} +21 -16
  35. package/src/typed-refs/record-ref.ts +80 -0
  36. package/src/typed-refs/struct-ref-internals.ts +195 -0
  37. package/src/typed-refs/{struct-value-updates.test.ts → struct-ref.test.ts} +5 -3
  38. package/src/typed-refs/struct-ref.ts +257 -0
  39. package/src/typed-refs/text-ref-internals.ts +100 -0
  40. package/src/typed-refs/text-ref.ts +72 -0
  41. package/src/typed-refs/tree-node-ref-internals.ts +111 -0
  42. package/src/typed-refs/{tree-node.ts → tree-node-ref.ts} +58 -94
  43. package/src/typed-refs/tree-ref-internals.ts +110 -0
  44. package/src/typed-refs/tree-ref.ts +194 -0
  45. package/src/typed-refs/utils.ts +21 -23
  46. package/src/typed-refs/counter.ts +0 -62
  47. package/src/typed-refs/movable-list.ts +0 -32
  48. package/src/typed-refs/struct.ts +0 -201
  49. package/src/typed-refs/text.ts +0 -91
  50. package/src/typed-refs/tree.ts +0 -268
  51. /package/src/typed-refs/{list-value-updates.test.ts → list-ref-value-updates.test.ts} +0 -0
  52. /package/src/typed-refs/{list.test.ts → list-ref.test.ts} +0 -0
  53. /package/src/typed-refs/{movable-list.test.ts → movable-list-ref.test.ts} +0 -0
  54. /package/src/typed-refs/{record-value-updates.test.ts → record-ref-value-updates.test.ts} +0 -0
  55. /package/src/typed-refs/{tree-node-value-updates.test.ts → tree-node-ref.test.ts} +0 -0
  56. /package/src/typed-refs/{tree.test.ts → tree-node.test.ts} +0 -0
package/dist/index.js CHANGED
@@ -99,12 +99,27 @@ function deriveValueShapePlaceholder(shape) {
99
99
  }
100
100
  }
101
101
 
102
+ // src/loro.ts
103
+ var LORO_SYMBOL = /* @__PURE__ */ Symbol.for("loro-extended:loro");
104
+ function loro(refOrDoc) {
105
+ const loroNamespace = refOrDoc[LORO_SYMBOL];
106
+ if (!loroNamespace) {
107
+ throw new Error(
108
+ "Invalid argument: expected TypedRef, TreeRef, or TypedDoc with loro() support"
109
+ );
110
+ }
111
+ return loroNamespace;
112
+ }
113
+
102
114
  // src/functional-helpers.ts
103
115
  function change(doc, fn) {
104
- return doc.$.change(fn);
116
+ return doc.change(fn);
105
117
  }
106
- function getLoroDoc(doc) {
107
- return doc.$.loroDoc;
118
+ function getLoroDoc(docOrRef) {
119
+ return loro(docOrRef).doc;
120
+ }
121
+ function getLoroContainer(ref) {
122
+ return loro(ref).container;
108
123
  }
109
124
 
110
125
  // src/utils/type-guards.ts
@@ -396,7 +411,7 @@ function hasWildcard(segments) {
396
411
 
397
412
  // src/path-evaluator.ts
398
413
  function evaluatePath(doc, selector) {
399
- const json = doc.$.toJSON();
414
+ const json = doc.toJSON();
400
415
  return evaluatePathOnValue(json, selector.__segments);
401
416
  }
402
417
  function evaluatePathOnValue(value, segments) {
@@ -600,7 +615,7 @@ var Shape = {
600
615
  * states: Shape.tree(StateNodeDataShape),
601
616
  * })
602
617
  *
603
- * doc.$.change(draft => {
618
+ * doc.change(draft => {
604
619
  * const root = draft.states.createNode({ name: "idle", facts: {} })
605
620
  * const child = root.createNode({ name: "running", facts: {} })
606
621
  * child.data.name = "active"
@@ -1082,42 +1097,76 @@ var JsonPatchApplicator = class {
1082
1097
  };
1083
1098
 
1084
1099
  // src/typed-refs/base.ts
1085
- var TypedRef = class {
1086
- constructor(_params) {
1087
- this._params = _params;
1100
+ var INTERNAL_SYMBOL = /* @__PURE__ */ Symbol.for("loro-extended:internal");
1101
+ var BaseRefInternals = class {
1102
+ constructor(params) {
1103
+ this.params = params;
1104
+ }
1105
+ cachedContainer;
1106
+ loroNamespace;
1107
+ /** Get the underlying Loro container (cached) */
1108
+ getContainer() {
1109
+ if (!this.cachedContainer) {
1110
+ this.cachedContainer = this.params.getContainer();
1111
+ }
1112
+ return this.cachedContainer;
1088
1113
  }
1089
- _cachedContainer;
1090
- get shape() {
1091
- return this._params.shape;
1114
+ /** Commit changes if autoCommit is enabled */
1115
+ commitIfAuto() {
1116
+ if (this.params.autoCommit) {
1117
+ this.params.getDoc().commit();
1118
+ }
1092
1119
  }
1093
- get placeholder() {
1094
- return this._params.placeholder;
1120
+ /** Get the shape for this ref */
1121
+ getShape() {
1122
+ return this.params.shape;
1095
1123
  }
1096
- get autoCommit() {
1097
- return !!this._params.autoCommit;
1124
+ /** Get the placeholder value */
1125
+ getPlaceholder() {
1126
+ return this.params.placeholder;
1098
1127
  }
1099
- get batchedMutation() {
1100
- return !!this._params.batchedMutation;
1128
+ /** Check if autoCommit is enabled */
1129
+ getAutoCommit() {
1130
+ return !!this.params.autoCommit;
1101
1131
  }
1102
- get doc() {
1103
- return this._params.getDoc?.();
1132
+ /** Check if in batched mutation mode */
1133
+ getBatchedMutation() {
1134
+ return !!this.params.batchedMutation;
1104
1135
  }
1105
- /**
1106
- * Commits changes if autoCommit is enabled.
1107
- * Call this after any mutation operation.
1108
- */
1109
- commitIfAuto() {
1110
- if (this.autoCommit && this.doc) {
1111
- this.doc.commit();
1112
- }
1136
+ /** Get the LoroDoc */
1137
+ getDoc() {
1138
+ return this.params.getDoc();
1113
1139
  }
1114
- get container() {
1115
- if (!this._cachedContainer) {
1116
- const container = this._params.getContainer();
1117
- this._cachedContainer = container;
1118
- return container;
1140
+ /** Get the loro namespace (cached) */
1141
+ getLoroNamespace() {
1142
+ if (!this.loroNamespace) {
1143
+ this.loroNamespace = this.createLoroNamespace();
1119
1144
  }
1120
- return this._cachedContainer;
1145
+ return this.loroNamespace;
1146
+ }
1147
+ /** Create the loro() namespace object - subclasses override for specific types */
1148
+ createLoroNamespace() {
1149
+ const self = this;
1150
+ return {
1151
+ get doc() {
1152
+ return self.params.getDoc();
1153
+ },
1154
+ get container() {
1155
+ return self.getContainer();
1156
+ },
1157
+ subscribe(callback) {
1158
+ return self.getContainer().subscribe(callback);
1159
+ }
1160
+ };
1161
+ }
1162
+ };
1163
+ var TypedRef = class {
1164
+ /**
1165
+ * Access the loro() namespace via the well-known symbol.
1166
+ * This is used by the loro() function to access CRDT internals.
1167
+ */
1168
+ get [LORO_SYMBOL]() {
1169
+ return this[INTERNAL_SYMBOL].getLoroNamespace();
1121
1170
  }
1122
1171
  };
1123
1172
 
@@ -1131,40 +1180,73 @@ import {
1131
1180
  LoroTree
1132
1181
  } from "loro-crdt";
1133
1182
 
1134
- // src/typed-refs/counter.ts
1135
- var CounterRef = class extends TypedRef {
1136
- // Track if we've materialized the container (made any changes)
1137
- _materialized = false;
1138
- get container() {
1139
- return super.container;
1140
- }
1141
- absorbPlainValues() {
1142
- }
1183
+ // src/typed-refs/counter-ref-internals.ts
1184
+ var CounterRefInternals = class extends BaseRefInternals {
1185
+ materialized = false;
1186
+ /** Increment the counter value */
1143
1187
  increment(value = 1) {
1144
- this._materialized = true;
1145
- this.container.increment(value);
1188
+ this.materialized = true;
1189
+ this.getContainer().increment(value);
1146
1190
  this.commitIfAuto();
1147
1191
  }
1192
+ /** Decrement the counter value */
1148
1193
  decrement(value = 1) {
1149
- this._materialized = true;
1150
- this.container.decrement(value);
1194
+ this.materialized = true;
1195
+ this.getContainer().decrement(value);
1151
1196
  this.commitIfAuto();
1152
1197
  }
1153
- /**
1154
- * Returns the counter value.
1155
- * If the counter hasn't been materialized (no operations performed),
1156
- * returns the placeholder value if available.
1157
- */
1158
- get value() {
1159
- const containerValue = this.container.value;
1160
- if (containerValue !== 0 || this._materialized) {
1198
+ /** Get the current counter value */
1199
+ getValue() {
1200
+ const container = this.getContainer();
1201
+ const containerValue = container.value;
1202
+ if (containerValue !== 0 || this.materialized) {
1161
1203
  return containerValue;
1162
1204
  }
1163
- if (this.placeholder !== void 0) {
1164
- return this.placeholder;
1205
+ const placeholder = this.getPlaceholder();
1206
+ if (placeholder !== void 0) {
1207
+ return placeholder;
1165
1208
  }
1166
1209
  return containerValue;
1167
1210
  }
1211
+ /** No plain values in counter */
1212
+ absorbPlainValues() {
1213
+ }
1214
+ /** Create the loro namespace for counter */
1215
+ createLoroNamespace() {
1216
+ const self = this;
1217
+ return {
1218
+ get doc() {
1219
+ return self.getDoc();
1220
+ },
1221
+ get container() {
1222
+ return self.getContainer();
1223
+ },
1224
+ subscribe(callback) {
1225
+ return self.getContainer().subscribe(callback);
1226
+ }
1227
+ };
1228
+ }
1229
+ };
1230
+
1231
+ // src/typed-refs/counter-ref.ts
1232
+ var CounterRef = class extends TypedRef {
1233
+ [INTERNAL_SYMBOL];
1234
+ constructor(params) {
1235
+ super();
1236
+ this[INTERNAL_SYMBOL] = new CounterRefInternals(params);
1237
+ }
1238
+ /** Increment the counter by the given value (default 1) */
1239
+ increment(value) {
1240
+ this[INTERNAL_SYMBOL].increment(value);
1241
+ }
1242
+ /** Decrement the counter by the given value (default 1) */
1243
+ decrement(value) {
1244
+ this[INTERNAL_SYMBOL].decrement(value);
1245
+ }
1246
+ /** Get the current counter value */
1247
+ get value() {
1248
+ return this[INTERNAL_SYMBOL].getValue();
1249
+ }
1168
1250
  valueOf() {
1169
1251
  return this.value;
1170
1252
  }
@@ -1313,75 +1395,41 @@ function convertInputToRef(value, shape) {
1313
1395
  }
1314
1396
  }
1315
1397
 
1316
- // src/typed-refs/list-base.ts
1317
- var ListRefBase = class extends TypedRef {
1318
- // Cache for items returned by array methods to track mutations
1398
+ // src/typed-refs/list-ref-base.ts
1399
+ var ListRefBaseInternals = class extends BaseRefInternals {
1319
1400
  itemCache = /* @__PURE__ */ new Map();
1320
- get container() {
1321
- return super.container;
1322
- }
1323
- get shape() {
1324
- return super.shape;
1325
- }
1326
- absorbPlainValues() {
1327
- for (const [index, cachedItem] of this.itemCache.entries()) {
1328
- if (cachedItem) {
1329
- if (isValueShape(this.shape.shape)) {
1330
- this.absorbValueAtIndex(index, cachedItem);
1331
- } else {
1332
- if (cachedItem && typeof cachedItem === "object" && "absorbPlainValues" in cachedItem) {
1333
- ;
1334
- cachedItem.absorbPlainValues();
1335
- }
1336
- }
1337
- }
1338
- }
1339
- this.itemCache.clear();
1340
- }
1341
- insertWithConversion(index, item) {
1342
- const convertedItem = convertInputToRef(item, this.shape.shape);
1343
- if (isContainer(convertedItem)) {
1344
- this.container.insertContainer(index, convertedItem);
1345
- } else {
1346
- this.container.insert(index, convertedItem);
1347
- }
1348
- }
1349
- pushWithConversion(item) {
1350
- const convertedItem = convertInputToRef(item, this.shape.shape);
1351
- if (isContainer(convertedItem)) {
1352
- this.container.pushContainer(convertedItem);
1353
- } else {
1354
- this.container.push(convertedItem);
1355
- }
1356
- }
1401
+ /** Get typed ref params for creating child refs at an index */
1357
1402
  getTypedRefParams(index, shape) {
1358
1403
  return {
1359
1404
  shape,
1360
1405
  placeholder: void 0,
1361
1406
  // List items don't have placeholder
1362
1407
  getContainer: () => {
1363
- const containerItem = this.container.get(index);
1408
+ const container = this.getContainer();
1409
+ const containerItem = container.get(index);
1364
1410
  if (!containerItem || !isContainer(containerItem)) {
1365
1411
  throw new Error(`No container found at index ${index}`);
1366
1412
  }
1367
1413
  return containerItem;
1368
1414
  },
1369
- autoCommit: this._params.autoCommit,
1370
- batchedMutation: this.batchedMutation,
1371
- getDoc: this._params.getDoc
1415
+ autoCommit: this.getAutoCommit(),
1416
+ batchedMutation: this.getBatchedMutation(),
1417
+ getDoc: () => this.getDoc()
1372
1418
  };
1373
1419
  }
1374
- // Get item for predicate functions - always returns plain Item for filtering logic
1420
+ /** Get item for predicate functions (returns plain value) */
1375
1421
  getPredicateItem(index) {
1422
+ const shape = this.getShape();
1423
+ const container = this.getContainer();
1376
1424
  const cachedItem = this.itemCache.get(index);
1377
- if (cachedItem && isValueShape(this.shape.shape)) {
1425
+ if (cachedItem && isValueShape(shape.shape)) {
1378
1426
  return cachedItem;
1379
1427
  }
1380
- const containerItem = this.container.get(index);
1428
+ const containerItem = container.get(index);
1381
1429
  if (containerItem === void 0) {
1382
1430
  return void 0;
1383
1431
  }
1384
- if (isValueShape(this.shape.shape)) {
1432
+ if (isValueShape(shape.shape)) {
1385
1433
  return containerItem;
1386
1434
  } else {
1387
1435
  if (isContainer(containerItem)) {
@@ -1396,14 +1444,16 @@ var ListRefBase = class extends TypedRef {
1396
1444
  return containerItem;
1397
1445
  }
1398
1446
  }
1399
- // Get item for return values - returns MutableItem that can be mutated
1447
+ /** Get mutable item for return values (returns ref or cached value) */
1400
1448
  getMutableItem(index) {
1401
- const containerItem = this.container.get(index);
1449
+ const shape = this.getShape();
1450
+ const container = this.getContainer();
1451
+ const containerItem = container.get(index);
1402
1452
  if (containerItem === void 0) {
1403
1453
  return void 0;
1404
1454
  }
1405
- if (isValueShape(this.shape.shape)) {
1406
- if (!this.batchedMutation) {
1455
+ if (isValueShape(shape.shape)) {
1456
+ if (!this.getBatchedMutation()) {
1407
1457
  return containerItem;
1408
1458
  }
1409
1459
  let cachedItem2 = this.itemCache.get(index);
@@ -1421,26 +1471,127 @@ var ListRefBase = class extends TypedRef {
1421
1471
  let cachedItem = this.itemCache.get(index);
1422
1472
  if (!cachedItem) {
1423
1473
  cachedItem = createContainerTypedRef(
1424
- this.getTypedRefParams(index, this.shape.shape)
1474
+ this.getTypedRefParams(index, shape.shape)
1425
1475
  );
1426
1476
  this.itemCache.set(index, cachedItem);
1427
1477
  }
1428
1478
  return cachedItem;
1429
1479
  }
1480
+ /** Insert with automatic conversion */
1481
+ insertWithConversion(index, item) {
1482
+ const shape = this.getShape();
1483
+ const container = this.getContainer();
1484
+ const convertedItem = convertInputToRef(item, shape.shape);
1485
+ if (isContainer(convertedItem)) {
1486
+ container.insertContainer(index, convertedItem);
1487
+ } else {
1488
+ container.insert(index, convertedItem);
1489
+ }
1490
+ }
1491
+ /** Push with automatic conversion */
1492
+ pushWithConversion(item) {
1493
+ const shape = this.getShape();
1494
+ const container = this.getContainer();
1495
+ const convertedItem = convertInputToRef(item, shape.shape);
1496
+ if (isContainer(convertedItem)) {
1497
+ container.pushContainer(convertedItem);
1498
+ } else {
1499
+ container.push(convertedItem);
1500
+ }
1501
+ }
1502
+ /** Absorb value at specific index (for value shapes) - subclasses override */
1503
+ absorbValueAtIndex(_index, _value) {
1504
+ throw new Error("absorbValueAtIndex must be implemented by subclass");
1505
+ }
1506
+ /** Update cache indices after a delete operation */
1507
+ updateCacheForDelete(deleteIndex, deleteLen) {
1508
+ const newCache = /* @__PURE__ */ new Map();
1509
+ for (const [cachedIndex, cachedItem] of this.itemCache.entries()) {
1510
+ if (cachedIndex < deleteIndex) {
1511
+ newCache.set(cachedIndex, cachedItem);
1512
+ } else if (cachedIndex >= deleteIndex + deleteLen) {
1513
+ newCache.set(cachedIndex - deleteLen, cachedItem);
1514
+ }
1515
+ }
1516
+ this.itemCache = newCache;
1517
+ }
1518
+ /** Update cache indices after an insert operation */
1519
+ updateCacheForInsert(insertIndex) {
1520
+ const newCache = /* @__PURE__ */ new Map();
1521
+ for (const [cachedIndex, cachedItem] of this.itemCache.entries()) {
1522
+ if (cachedIndex < insertIndex) {
1523
+ newCache.set(cachedIndex, cachedItem);
1524
+ } else {
1525
+ newCache.set(cachedIndex + 1, cachedItem);
1526
+ }
1527
+ }
1528
+ this.itemCache = newCache;
1529
+ }
1530
+ /** Absorb mutated plain values back into Loro containers */
1531
+ absorbPlainValues() {
1532
+ const shape = this.getShape();
1533
+ for (const [index, cachedItem] of this.itemCache.entries()) {
1534
+ if (cachedItem) {
1535
+ if (isValueShape(shape.shape)) {
1536
+ this.absorbValueAtIndex(index, cachedItem);
1537
+ } else {
1538
+ if (cachedItem && typeof cachedItem === "object" && INTERNAL_SYMBOL in cachedItem) {
1539
+ ;
1540
+ cachedItem[INTERNAL_SYMBOL].absorbPlainValues();
1541
+ }
1542
+ }
1543
+ }
1544
+ }
1545
+ this.itemCache.clear();
1546
+ }
1547
+ /** Create the loro namespace for list */
1548
+ createLoroNamespace() {
1549
+ const self = this;
1550
+ return {
1551
+ get doc() {
1552
+ return self.getDoc();
1553
+ },
1554
+ get container() {
1555
+ return self.getContainer();
1556
+ },
1557
+ subscribe(callback) {
1558
+ return self.getContainer().subscribe(
1559
+ callback
1560
+ );
1561
+ },
1562
+ pushContainer(container) {
1563
+ const result = self.getContainer().pushContainer(container);
1564
+ self.commitIfAuto();
1565
+ return result;
1566
+ },
1567
+ insertContainer(index, container) {
1568
+ const result = self.getContainer().insertContainer(index, container);
1569
+ self.commitIfAuto();
1570
+ return result;
1571
+ }
1572
+ };
1573
+ }
1574
+ };
1575
+ var ListRefBase = class extends TypedRef {
1576
+ [INTERNAL_SYMBOL];
1577
+ constructor(params) {
1578
+ super();
1579
+ this[INTERNAL_SYMBOL] = this.createInternals(params);
1580
+ }
1430
1581
  // Array-like methods for better developer experience
1431
1582
  // DUAL INTERFACE: Predicates get Item (plain data), return values are MutableItem (mutable)
1432
1583
  find(predicate) {
1433
1584
  for (let i = 0; i < this.length; i++) {
1434
- const predicateItem = this.getPredicateItem(i);
1585
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1435
1586
  if (predicate(predicateItem, i)) {
1436
- return this.getMutableItem(i);
1587
+ return this[INTERNAL_SYMBOL].getMutableItem(i);
1437
1588
  }
1438
1589
  }
1439
1590
  return void 0;
1440
1591
  }
1441
1592
  findIndex(predicate) {
1442
1593
  for (let i = 0; i < this.length; i++) {
1443
- const predicateItem = this.getPredicateItem(i);
1594
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1444
1595
  if (predicate(predicateItem, i)) {
1445
1596
  return i;
1446
1597
  }
@@ -1450,7 +1601,7 @@ var ListRefBase = class extends TypedRef {
1450
1601
  map(callback) {
1451
1602
  const result = [];
1452
1603
  for (let i = 0; i < this.length; i++) {
1453
- const predicateItem = this.getPredicateItem(i);
1604
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1454
1605
  result.push(callback(predicateItem, i));
1455
1606
  }
1456
1607
  return result;
@@ -1458,22 +1609,22 @@ var ListRefBase = class extends TypedRef {
1458
1609
  filter(predicate) {
1459
1610
  const result = [];
1460
1611
  for (let i = 0; i < this.length; i++) {
1461
- const predicateItem = this.getPredicateItem(i);
1612
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1462
1613
  if (predicate(predicateItem, i)) {
1463
- result.push(this.getMutableItem(i));
1614
+ result.push(this[INTERNAL_SYMBOL].getMutableItem(i));
1464
1615
  }
1465
1616
  }
1466
1617
  return result;
1467
1618
  }
1468
1619
  forEach(callback) {
1469
1620
  for (let i = 0; i < this.length; i++) {
1470
- const predicateItem = this.getPredicateItem(i);
1621
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1471
1622
  callback(predicateItem, i);
1472
1623
  }
1473
1624
  }
1474
1625
  some(predicate) {
1475
1626
  for (let i = 0; i < this.length; i++) {
1476
- const predicateItem = this.getPredicateItem(i);
1627
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1477
1628
  if (predicate(predicateItem, i)) {
1478
1629
  return true;
1479
1630
  }
@@ -1482,7 +1633,7 @@ var ListRefBase = class extends TypedRef {
1482
1633
  }
1483
1634
  every(predicate) {
1484
1635
  for (let i = 0; i < this.length; i++) {
1485
- const predicateItem = this.getPredicateItem(i);
1636
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1486
1637
  if (!predicate(predicateItem, i)) {
1487
1638
  return false;
1488
1639
  }
@@ -1495,50 +1646,55 @@ var ListRefBase = class extends TypedRef {
1495
1646
  const endIndex = end === void 0 ? len : end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
1496
1647
  const result = [];
1497
1648
  for (let i = startIndex; i < endIndex; i++) {
1498
- result.push(this.getMutableItem(i));
1649
+ result.push(this[INTERNAL_SYMBOL].getMutableItem(i));
1499
1650
  }
1500
1651
  return result;
1501
1652
  }
1502
1653
  insert(index, item) {
1503
- this.updateCacheForInsert(index);
1504
- this.insertWithConversion(index, item);
1505
- this.commitIfAuto();
1654
+ this[INTERNAL_SYMBOL].updateCacheForInsert(index);
1655
+ this[INTERNAL_SYMBOL].insertWithConversion(index, item);
1656
+ this[INTERNAL_SYMBOL].commitIfAuto();
1506
1657
  }
1507
1658
  delete(index, len) {
1508
- this.updateCacheForDelete(index, len);
1509
- this.container.delete(index, len);
1510
- this.commitIfAuto();
1659
+ this[INTERNAL_SYMBOL].updateCacheForDelete(index, len);
1660
+ const container = this[INTERNAL_SYMBOL].getContainer();
1661
+ container.delete(index, len);
1662
+ this[INTERNAL_SYMBOL].commitIfAuto();
1511
1663
  }
1512
1664
  push(item) {
1513
- this.pushWithConversion(item);
1514
- this.commitIfAuto();
1665
+ this[INTERNAL_SYMBOL].pushWithConversion(item);
1666
+ this[INTERNAL_SYMBOL].commitIfAuto();
1515
1667
  }
1516
1668
  pushContainer(container) {
1517
- const result = this.container.pushContainer(container);
1518
- this.commitIfAuto();
1669
+ const loroContainer = this[INTERNAL_SYMBOL].getContainer();
1670
+ const result = loroContainer.pushContainer(container);
1671
+ this[INTERNAL_SYMBOL].commitIfAuto();
1519
1672
  return result;
1520
1673
  }
1521
1674
  insertContainer(index, container) {
1522
- const result = this.container.insertContainer(index, container);
1523
- this.commitIfAuto();
1675
+ const loroContainer = this[INTERNAL_SYMBOL].getContainer();
1676
+ const result = loroContainer.insertContainer(index, container);
1677
+ this[INTERNAL_SYMBOL].commitIfAuto();
1524
1678
  return result;
1525
1679
  }
1526
1680
  get(index) {
1527
- return this.getMutableItem(index);
1681
+ return this[INTERNAL_SYMBOL].getMutableItem(index);
1528
1682
  }
1529
1683
  toArray() {
1530
1684
  const result = [];
1531
1685
  for (let i = 0; i < this.length; i++) {
1532
- result.push(this.getPredicateItem(i));
1686
+ result.push(this[INTERNAL_SYMBOL].getPredicateItem(i));
1533
1687
  }
1534
1688
  return result;
1535
1689
  }
1536
1690
  toJSON() {
1537
- const nativeJson = this.container.toJSON();
1538
- if (isContainerShape(this.shape.shape) || isValueShape(this.shape.shape) && this.shape.shape.valueType === "struct") {
1539
- const itemPlaceholder = deriveShapePlaceholder(this.shape.shape);
1691
+ const shape = this[INTERNAL_SYMBOL].getShape();
1692
+ const container = this[INTERNAL_SYMBOL].getContainer();
1693
+ const nativeJson = container.toJSON();
1694
+ if (isContainerShape(shape.shape) || isValueShape(shape.shape) && shape.shape.valueType === "struct") {
1695
+ const itemPlaceholder = deriveShapePlaceholder(shape.shape);
1540
1696
  return nativeJson.map(
1541
- (item) => mergeValue(this.shape.shape, item, itemPlaceholder)
1697
+ (item) => mergeValue(shape.shape, item, itemPlaceholder)
1542
1698
  );
1543
1699
  }
1544
1700
  return nativeJson ?? [];
@@ -1548,7 +1704,10 @@ var ListRefBase = class extends TypedRef {
1548
1704
  return {
1549
1705
  next: () => {
1550
1706
  if (index < this.length) {
1551
- return { value: this.getMutableItem(index++), done: false };
1707
+ return {
1708
+ value: this[INTERNAL_SYMBOL].getMutableItem(index++),
1709
+ done: false
1710
+ };
1552
1711
  }
1553
1712
  return { value: void 0, done: true };
1554
1713
  },
@@ -1558,61 +1717,59 @@ var ListRefBase = class extends TypedRef {
1558
1717
  };
1559
1718
  }
1560
1719
  get length() {
1561
- return this.container.length;
1562
- }
1563
- // Update cache indices when items are deleted
1564
- updateCacheForDelete(deleteIndex, deleteLen) {
1565
- const newCache = /* @__PURE__ */ new Map();
1566
- for (const [cachedIndex, cachedItem] of this.itemCache.entries()) {
1567
- if (cachedIndex < deleteIndex) {
1568
- newCache.set(cachedIndex, cachedItem);
1569
- } else if (cachedIndex >= deleteIndex + deleteLen) {
1570
- newCache.set(cachedIndex - deleteLen, cachedItem);
1571
- }
1572
- }
1573
- this.itemCache = newCache;
1574
- }
1575
- // Update cache indices when items are inserted
1576
- updateCacheForInsert(insertIndex) {
1577
- const newCache = /* @__PURE__ */ new Map();
1578
- for (const [cachedIndex, cachedItem] of this.itemCache.entries()) {
1579
- if (cachedIndex < insertIndex) {
1580
- newCache.set(cachedIndex, cachedItem);
1581
- } else {
1582
- newCache.set(cachedIndex + 1, cachedItem);
1583
- }
1584
- }
1585
- this.itemCache = newCache;
1720
+ const container = this[INTERNAL_SYMBOL].getContainer();
1721
+ return container.length;
1586
1722
  }
1587
1723
  };
1588
1724
 
1589
- // src/typed-refs/list.ts
1590
- var ListRef = class extends ListRefBase {
1591
- get container() {
1592
- return super.container;
1593
- }
1725
+ // src/typed-refs/list-ref-internals.ts
1726
+ var ListRefInternals = class extends ListRefBaseInternals {
1727
+ /** Absorb value at specific index for LoroList */
1594
1728
  absorbValueAtIndex(index, value) {
1595
- this.container.delete(index, 1);
1596
- this.container.insert(index, value);
1729
+ const container = this.getContainer();
1730
+ container.delete(index, 1);
1731
+ container.insert(index, value);
1597
1732
  }
1598
1733
  };
1599
1734
 
1600
- // src/typed-refs/movable-list.ts
1601
- var MovableListRef = class extends ListRefBase {
1602
- get container() {
1603
- return super.container;
1735
+ // src/typed-refs/list-ref.ts
1736
+ var ListRef = class extends ListRefBase {
1737
+ createInternals(params) {
1738
+ return new ListRefInternals(params);
1604
1739
  }
1740
+ };
1741
+
1742
+ // src/typed-refs/movable-list-ref-internals.ts
1743
+ var MovableListRefInternals = class extends ListRefBaseInternals {
1744
+ /** Absorb value at specific index for LoroMovableList */
1605
1745
  absorbValueAtIndex(index, value) {
1606
- this.container.set(index, value);
1746
+ const container = this.getContainer();
1747
+ container.set(index, value);
1607
1748
  }
1749
+ /** Move an item from one index to another */
1608
1750
  move(from, to) {
1609
- this.container.move(from, to);
1751
+ const container = this.getContainer();
1752
+ container.move(from, to);
1610
1753
  this.commitIfAuto();
1611
1754
  }
1755
+ /** Set an item at a specific index */
1612
1756
  set(index, item) {
1613
- const result = this.container.set(index, item);
1757
+ const container = this.getContainer();
1758
+ container.set(index, item);
1614
1759
  this.commitIfAuto();
1615
- return result;
1760
+ }
1761
+ };
1762
+
1763
+ // src/typed-refs/movable-list-ref.ts
1764
+ var MovableListRef = class extends ListRefBase {
1765
+ createInternals(params) {
1766
+ return new MovableListRefInternals(params);
1767
+ }
1768
+ move(from, to) {
1769
+ this[INTERNAL_SYMBOL].move(from, to);
1770
+ }
1771
+ set(index, item) {
1772
+ this[INTERNAL_SYMBOL].set(index, item);
1616
1773
  }
1617
1774
  };
1618
1775
 
@@ -1620,7 +1777,7 @@ var MovableListRef = class extends ListRefBase {
1620
1777
  var recordProxyHandler = {
1621
1778
  get: (target, prop) => {
1622
1779
  if (typeof prop === "string" && !(prop in target)) {
1623
- return target.getRef(prop);
1780
+ return target[INTERNAL_SYMBOL].getRef(prop);
1624
1781
  }
1625
1782
  return Reflect.get(target, prop);
1626
1783
  },
@@ -1706,20 +1863,12 @@ var movableListProxyHandler = {
1706
1863
  }
1707
1864
  };
1708
1865
 
1709
- // src/typed-refs/record.ts
1710
- var RecordRef = class extends TypedRef {
1866
+ // src/typed-refs/record-ref-internals.ts
1867
+ var RecordRefInternals = class extends BaseRefInternals {
1711
1868
  refCache = /* @__PURE__ */ new Map();
1712
- get shape() {
1713
- return super.shape;
1714
- }
1715
- get container() {
1716
- return super.container;
1717
- }
1718
- absorbPlainValues() {
1719
- absorbCachedPlainValues(this.refCache, () => this.container);
1720
- }
1869
+ /** Get typed ref params for creating child refs at a key */
1721
1870
  getTypedRefParams(key, shape) {
1722
- let placeholder = this.placeholder?.[key];
1871
+ let placeholder = this.getPlaceholder()?.[key];
1723
1872
  if (placeholder === void 0) {
1724
1873
  placeholder = deriveShapePlaceholder(shape);
1725
1874
  }
@@ -1729,42 +1878,41 @@ var RecordRef = class extends TypedRef {
1729
1878
  );
1730
1879
  }
1731
1880
  const LoroContainer = containerConstructor[shape._type];
1881
+ const container = this.getContainer();
1732
1882
  return {
1733
1883
  shape,
1734
1884
  placeholder,
1735
- getContainer: () => this.container.getOrCreateContainer(key, new LoroContainer()),
1736
- autoCommit: this._params.autoCommit,
1737
- batchedMutation: this.batchedMutation,
1738
- getDoc: this._params.getDoc
1885
+ getContainer: () => container.getOrCreateContainer(key, new LoroContainer()),
1886
+ autoCommit: this.getAutoCommit(),
1887
+ batchedMutation: this.getBatchedMutation(),
1888
+ getDoc: () => this.getDoc()
1739
1889
  };
1740
1890
  }
1741
- /**
1742
- * Gets an existing ref for a key, or returns undefined if the key doesn't exist.
1743
- * Used for reading operations where we want optional chaining to work.
1744
- */
1891
+ /** Get a ref for a key without creating (returns undefined for non-existent container keys) */
1745
1892
  getRef(key) {
1746
- if (isContainerShape(this.shape.shape)) {
1747
- const existing = this.container.get(key);
1893
+ const recordShape = this.getShape();
1894
+ const shape = recordShape.shape;
1895
+ const container = this.getContainer();
1896
+ if (isContainerShape(shape)) {
1897
+ const existing = container.get(key);
1748
1898
  if (existing === void 0) {
1749
1899
  return void 0;
1750
1900
  }
1751
1901
  }
1752
1902
  return this.getOrCreateRef(key);
1753
1903
  }
1754
- /**
1755
- * Gets or creates a ref for a key.
1756
- * Always creates the container if it doesn't exist.
1757
- * This is the method used for write operations.
1758
- */
1904
+ /** Get or create a ref for a key (always creates for container shapes) */
1759
1905
  getOrCreateRef(key) {
1760
- const shape = this.shape.shape;
1906
+ const recordShape = this.getShape();
1907
+ const shape = recordShape.shape;
1908
+ const container = this.getContainer();
1761
1909
  if (isValueShape(shape)) {
1762
- if (!this.batchedMutation) {
1763
- const containerValue = this.container.get(key);
1910
+ if (!this.getBatchedMutation()) {
1911
+ const containerValue = container.get(key);
1764
1912
  if (containerValue !== void 0) {
1765
1913
  return containerValue;
1766
1914
  }
1767
- const placeholder = this.placeholder?.[key];
1915
+ const placeholder = this.getPlaceholder()?.[key];
1768
1916
  if (placeholder !== void 0) {
1769
1917
  return placeholder;
1770
1918
  }
@@ -1772,7 +1920,7 @@ var RecordRef = class extends TypedRef {
1772
1920
  }
1773
1921
  let ref2 = this.refCache.get(key);
1774
1922
  if (!ref2) {
1775
- const containerValue = this.container.get(key);
1923
+ const containerValue = container.get(key);
1776
1924
  if (containerValue !== void 0) {
1777
1925
  if (typeof containerValue === "object" && containerValue !== null) {
1778
1926
  ref2 = JSON.parse(JSON.stringify(containerValue));
@@ -1780,7 +1928,7 @@ var RecordRef = class extends TypedRef {
1780
1928
  ref2 = containerValue;
1781
1929
  }
1782
1930
  } else {
1783
- const placeholder = this.placeholder?.[key];
1931
+ const placeholder = this.getPlaceholder()?.[key];
1784
1932
  if (placeholder !== void 0) {
1785
1933
  ref2 = placeholder;
1786
1934
  } else {
@@ -1800,12 +1948,13 @@ var RecordRef = class extends TypedRef {
1800
1948
  }
1801
1949
  return ref;
1802
1950
  }
1803
- get(key) {
1804
- return this.getRef(key);
1805
- }
1951
+ /** Set a value at a key */
1806
1952
  set(key, value) {
1807
- if (isValueShape(this.shape.shape)) {
1808
- this.container.set(key, value);
1953
+ const recordShape = this.getShape();
1954
+ const shape = recordShape.shape;
1955
+ const container = this.getContainer();
1956
+ if (isValueShape(shape)) {
1957
+ container.set(key, value);
1809
1958
  this.refCache.set(key, value);
1810
1959
  this.commitIfAuto();
1811
1960
  } else {
@@ -1819,74 +1968,124 @@ var RecordRef = class extends TypedRef {
1819
1968
  );
1820
1969
  }
1821
1970
  }
1822
- setContainer(key, container) {
1823
- const result = this.container.setContainer(key, container);
1824
- this.commitIfAuto();
1825
- return result;
1826
- }
1971
+ /** Delete a key */
1827
1972
  delete(key) {
1828
- this.container.delete(key);
1973
+ const container = this.getContainer();
1974
+ container.delete(key);
1829
1975
  this.refCache.delete(key);
1830
1976
  this.commitIfAuto();
1831
1977
  }
1978
+ /** Absorb mutated plain values back into Loro containers */
1979
+ absorbPlainValues() {
1980
+ absorbCachedPlainValues(this.refCache, () => this.getContainer());
1981
+ }
1982
+ /** Create the loro namespace for record */
1983
+ createLoroNamespace() {
1984
+ const self = this;
1985
+ return {
1986
+ get doc() {
1987
+ return self.getDoc();
1988
+ },
1989
+ get container() {
1990
+ return self.getContainer();
1991
+ },
1992
+ subscribe(callback) {
1993
+ return self.getContainer().subscribe(callback);
1994
+ },
1995
+ setContainer(key, container) {
1996
+ const result = self.getContainer().setContainer(
1997
+ key,
1998
+ container
1999
+ );
2000
+ self.commitIfAuto();
2001
+ return result;
2002
+ }
2003
+ };
2004
+ }
2005
+ };
2006
+
2007
+ // src/typed-refs/record-ref.ts
2008
+ var RecordRef = class extends TypedRef {
2009
+ [INTERNAL_SYMBOL];
2010
+ constructor(params) {
2011
+ super();
2012
+ this[INTERNAL_SYMBOL] = new RecordRefInternals(params);
2013
+ }
2014
+ /** Set a value at a key */
2015
+ set(key, value) {
2016
+ this[INTERNAL_SYMBOL].set(key, value);
2017
+ }
2018
+ /** Delete a key */
2019
+ delete(key) {
2020
+ this[INTERNAL_SYMBOL].delete(key);
2021
+ }
2022
+ get(key) {
2023
+ if (this[INTERNAL_SYMBOL].getBatchedMutation()) {
2024
+ return this[INTERNAL_SYMBOL].getOrCreateRef(key);
2025
+ }
2026
+ return this[INTERNAL_SYMBOL].getRef(key);
2027
+ }
2028
+ setContainer(key, container) {
2029
+ const loroContainer = this[INTERNAL_SYMBOL].getContainer();
2030
+ const result = loroContainer.setContainer(key, container);
2031
+ this[INTERNAL_SYMBOL].commitIfAuto();
2032
+ return result;
2033
+ }
1832
2034
  has(key) {
1833
- return this.container.get(key) !== void 0;
2035
+ const container = this[INTERNAL_SYMBOL].getContainer();
2036
+ return container.get(key) !== void 0;
1834
2037
  }
1835
2038
  keys() {
1836
- return this.container.keys();
2039
+ const container = this[INTERNAL_SYMBOL].getContainer();
2040
+ return container.keys();
1837
2041
  }
1838
2042
  values() {
1839
- return this.container.values();
2043
+ const container = this[INTERNAL_SYMBOL].getContainer();
2044
+ return container.values();
1840
2045
  }
1841
2046
  get size() {
1842
- return this.container.size;
2047
+ const container = this[INTERNAL_SYMBOL].getContainer();
2048
+ return container.size;
1843
2049
  }
1844
2050
  toJSON() {
1845
2051
  return serializeRefToJSON(this, this.keys());
1846
2052
  }
1847
2053
  };
1848
2054
 
1849
- // src/typed-refs/struct.ts
1850
- var StructRef = class extends TypedRef {
2055
+ // src/typed-refs/struct-ref-internals.ts
2056
+ var StructRefInternals = class extends BaseRefInternals {
1851
2057
  propertyCache = /* @__PURE__ */ new Map();
1852
- constructor(params) {
1853
- super(params);
1854
- this.createLazyProperties();
1855
- }
1856
- get shape() {
1857
- return super.shape;
1858
- }
1859
- get container() {
1860
- return super.container;
1861
- }
1862
- absorbPlainValues() {
1863
- absorbCachedPlainValues(this.propertyCache, () => this.container);
1864
- }
2058
+ /** Get typed ref params for creating child refs at a key */
1865
2059
  getTypedRefParams(key, shape) {
1866
- const placeholder = this.placeholder?.[key];
2060
+ const placeholder = this.getPlaceholder()?.[key];
1867
2061
  if (!hasContainerConstructor(shape._type)) {
1868
2062
  throw new Error(
1869
2063
  `Cannot create typed ref for shape type "${shape._type}". Use Shape.any() only at the document root level.`
1870
2064
  );
1871
2065
  }
1872
2066
  const LoroContainer = containerConstructor[shape._type];
2067
+ const container = this.getContainer();
1873
2068
  return {
1874
2069
  shape,
1875
2070
  placeholder,
1876
- getContainer: () => this.container.getOrCreateContainer(key, new LoroContainer()),
1877
- autoCommit: this._params.autoCommit,
1878
- batchedMutation: this.batchedMutation,
1879
- getDoc: this._params.getDoc
2071
+ getContainer: () => container.getOrCreateContainer(key, new LoroContainer()),
2072
+ autoCommit: this.getAutoCommit(),
2073
+ batchedMutation: this.getBatchedMutation(),
2074
+ getDoc: () => this.getDoc()
1880
2075
  };
1881
2076
  }
2077
+ /** Get or create a ref for a key */
1882
2078
  getOrCreateRef(key, shape) {
1883
- if (isValueShape(shape)) {
1884
- if (!this.batchedMutation) {
1885
- const containerValue = this.container.get(key);
2079
+ const structShape = this.getShape();
2080
+ const actualShape = shape || structShape.shapes[key];
2081
+ const container = this.getContainer();
2082
+ if (isValueShape(actualShape)) {
2083
+ if (!this.getBatchedMutation()) {
2084
+ const containerValue = container.get(key);
1886
2085
  if (containerValue !== void 0) {
1887
2086
  return containerValue;
1888
2087
  }
1889
- const placeholder = this.placeholder?.[key];
2088
+ const placeholder = this.getPlaceholder()?.[key];
1890
2089
  if (placeholder === void 0) {
1891
2090
  throw new Error("placeholder required");
1892
2091
  }
@@ -1894,7 +2093,7 @@ var StructRef = class extends TypedRef {
1894
2093
  }
1895
2094
  let ref2 = this.propertyCache.get(key);
1896
2095
  if (!ref2) {
1897
- const containerValue = this.container.get(key);
2096
+ const containerValue = container.get(key);
1898
2097
  if (containerValue !== void 0) {
1899
2098
  if (typeof containerValue === "object" && containerValue !== null) {
1900
2099
  ref2 = JSON.parse(JSON.stringify(containerValue));
@@ -1902,7 +2101,7 @@ var StructRef = class extends TypedRef {
1902
2101
  ref2 = containerValue;
1903
2102
  }
1904
2103
  } else {
1905
- const placeholder = this.placeholder?.[key];
2104
+ const placeholder = this.getPlaceholder()?.[key];
1906
2105
  if (placeholder === void 0) {
1907
2106
  throw new Error("placeholder required");
1908
2107
  }
@@ -1914,196 +2113,424 @@ var StructRef = class extends TypedRef {
1914
2113
  }
1915
2114
  let ref = this.propertyCache.get(key);
1916
2115
  if (!ref) {
1917
- ref = createContainerTypedRef(this.getTypedRefParams(key, shape));
2116
+ ref = createContainerTypedRef(
2117
+ this.getTypedRefParams(key, actualShape)
2118
+ );
1918
2119
  this.propertyCache.set(key, ref);
1919
2120
  }
1920
2121
  return ref;
1921
2122
  }
1922
- createLazyProperties() {
1923
- for (const key in this.shape.shapes) {
1924
- const shape = this.shape.shapes[key];
1925
- Object.defineProperty(this, key, {
1926
- get: () => this.getOrCreateRef(key, shape),
1927
- set: (value) => {
1928
- if (isValueShape(shape)) {
1929
- this.container.set(key, value);
1930
- this.propertyCache.set(key, value);
1931
- } else {
1932
- const ref = this.getOrCreateRef(key, shape);
1933
- if (assignPlainValueToTypedRef(ref, value)) {
1934
- return;
1935
- }
1936
- throw new Error(
1937
- "Cannot set container directly, modify the typed ref instead"
1938
- );
1939
- }
1940
- },
1941
- enumerable: true
1942
- });
2123
+ /** Set a property value */
2124
+ setPropertyValue(key, value) {
2125
+ const structShape = this.getShape();
2126
+ const shape = structShape.shapes[key];
2127
+ const container = this.getContainer();
2128
+ if (!shape) {
2129
+ throw new Error(`Unknown property: ${key}`);
2130
+ }
2131
+ if (isValueShape(shape)) {
2132
+ container.set(key, value);
2133
+ this.propertyCache.set(key, value);
2134
+ this.commitIfAuto();
2135
+ } else {
2136
+ const ref = this.getOrCreateRef(key, shape);
2137
+ if (assignPlainValueToTypedRef(ref, value)) {
2138
+ this.commitIfAuto();
2139
+ return;
2140
+ }
2141
+ throw new Error(
2142
+ "Cannot set container directly, modify the typed ref instead"
2143
+ );
1943
2144
  }
1944
2145
  }
1945
- toJSON() {
1946
- return serializeRefToJSON(
1947
- this,
1948
- Object.keys(this.shape.shapes)
2146
+ /** Delete a property */
2147
+ deleteProperty(key) {
2148
+ const container = this.getContainer();
2149
+ container.delete(key);
2150
+ this.propertyCache.delete(key);
2151
+ this.commitIfAuto();
2152
+ }
2153
+ /** Absorb mutated plain values back into Loro containers */
2154
+ absorbPlainValues() {
2155
+ absorbCachedPlainValues(
2156
+ this.propertyCache,
2157
+ () => this.getContainer()
1949
2158
  );
1950
2159
  }
1951
- // TODO(duane): return correct type here
2160
+ /** Create the loro namespace for struct */
2161
+ createLoroNamespace() {
2162
+ const self = this;
2163
+ return {
2164
+ get doc() {
2165
+ return self.getDoc();
2166
+ },
2167
+ get container() {
2168
+ return self.getContainer();
2169
+ },
2170
+ subscribe(callback) {
2171
+ return self.getContainer().subscribe(callback);
2172
+ },
2173
+ setContainer(key, container) {
2174
+ const result = self.getContainer().setContainer(
2175
+ key,
2176
+ container
2177
+ );
2178
+ self.commitIfAuto();
2179
+ return result;
2180
+ }
2181
+ };
2182
+ }
2183
+ };
2184
+
2185
+ // src/typed-refs/struct-ref.ts
2186
+ var StructRefImpl = class extends TypedRef {
2187
+ [INTERNAL_SYMBOL];
2188
+ constructor(params) {
2189
+ super();
2190
+ this[INTERNAL_SYMBOL] = new StructRefInternals(params);
2191
+ }
2192
+ get structShape() {
2193
+ return this[INTERNAL_SYMBOL].getShape();
2194
+ }
2195
+ toJSON() {
2196
+ return serializeRefToJSON(
2197
+ this,
2198
+ Object.keys(this.structShape.shapes)
2199
+ );
2200
+ }
2201
+ // Deprecated methods - kept for backward compatibility
2202
+ // @deprecated Use property access instead: obj.key
1952
2203
  get(key) {
1953
- return this.container.get(key);
2204
+ const container = this[INTERNAL_SYMBOL].getContainer();
2205
+ return container.get(key);
1954
2206
  }
2207
+ // @deprecated Use property assignment instead: obj.key = value
1955
2208
  set(key, value) {
1956
- this.container.set(key, value);
1957
- this.commitIfAuto();
2209
+ const container = this[INTERNAL_SYMBOL].getContainer();
2210
+ container.set(key, value);
2211
+ this[INTERNAL_SYMBOL].commitIfAuto();
1958
2212
  }
2213
+ // @deprecated Use loro(struct).setContainer() instead
1959
2214
  setContainer(key, container) {
1960
- const result = this.container.setContainer(key, container);
1961
- this.commitIfAuto();
2215
+ const loroContainer = this[INTERNAL_SYMBOL].getContainer();
2216
+ const result = loroContainer.setContainer(key, container);
2217
+ this[INTERNAL_SYMBOL].commitIfAuto();
1962
2218
  return result;
1963
2219
  }
2220
+ // @deprecated Use delete obj.key instead
1964
2221
  delete(key) {
1965
- this.container.delete(key);
1966
- this.commitIfAuto();
2222
+ const container = this[INTERNAL_SYMBOL].getContainer();
2223
+ container.delete(key);
2224
+ this[INTERNAL_SYMBOL].commitIfAuto();
1967
2225
  }
2226
+ // @deprecated Use 'key' in obj instead
1968
2227
  has(key) {
1969
- return this.container.get(key) !== void 0;
2228
+ const container = this[INTERNAL_SYMBOL].getContainer();
2229
+ return container.get(key) !== void 0;
1970
2230
  }
2231
+ // @deprecated Use Object.keys(obj) instead
1971
2232
  keys() {
1972
- return this.container.keys();
2233
+ const container = this[INTERNAL_SYMBOL].getContainer();
2234
+ return container.keys();
1973
2235
  }
2236
+ // @deprecated Use Object.values(obj) instead
1974
2237
  values() {
1975
- return this.container.values();
2238
+ const container = this[INTERNAL_SYMBOL].getContainer();
2239
+ return container.values();
1976
2240
  }
2241
+ // @deprecated Not standard for objects
1977
2242
  get size() {
1978
- return this.container.size;
2243
+ const container = this[INTERNAL_SYMBOL].getContainer();
2244
+ return container.size;
1979
2245
  }
1980
2246
  };
2247
+ function createStructRef(params) {
2248
+ const impl = new StructRefImpl(params);
2249
+ const proxy = new Proxy(impl, {
2250
+ get(target, prop, receiver) {
2251
+ if (prop === LORO_SYMBOL) {
2252
+ return target[INTERNAL_SYMBOL].getLoroNamespace();
2253
+ }
2254
+ if (prop === INTERNAL_SYMBOL) {
2255
+ return target[INTERNAL_SYMBOL];
2256
+ }
2257
+ if (prop === "toJSON") {
2258
+ return () => serializeRefToJSON(receiver, Object.keys(target.structShape.shapes));
2259
+ }
2260
+ if (prop === "shape") {
2261
+ return target.structShape;
2262
+ }
2263
+ if (typeof prop === "string" && prop in target.structShape.shapes) {
2264
+ const shape = target.structShape.shapes[prop];
2265
+ return target[INTERNAL_SYMBOL].getOrCreateRef(prop, shape);
2266
+ }
2267
+ return void 0;
2268
+ },
2269
+ set(target, prop, value) {
2270
+ if (typeof prop === "string" && prop in target.structShape.shapes) {
2271
+ target[INTERNAL_SYMBOL].setPropertyValue(prop, value);
2272
+ return true;
2273
+ }
2274
+ return false;
2275
+ },
2276
+ has(target, prop) {
2277
+ if (prop === LORO_SYMBOL || prop === INTERNAL_SYMBOL || prop === "toJSON" || prop === "shape") {
2278
+ return true;
2279
+ }
2280
+ if (typeof prop === "string") {
2281
+ return prop in target.structShape.shapes;
2282
+ }
2283
+ return false;
2284
+ },
2285
+ deleteProperty(target, prop) {
2286
+ if (typeof prop === "string" && prop in target.structShape.shapes) {
2287
+ target[INTERNAL_SYMBOL].deleteProperty(prop);
2288
+ return true;
2289
+ }
2290
+ return false;
2291
+ },
2292
+ ownKeys(target) {
2293
+ return Object.keys(target.structShape.shapes);
2294
+ },
2295
+ getOwnPropertyDescriptor(target, prop) {
2296
+ if (typeof prop === "string" && prop in target.structShape.shapes) {
2297
+ const shape = target.structShape.shapes[prop];
2298
+ return {
2299
+ configurable: true,
2300
+ enumerable: true,
2301
+ value: target[INTERNAL_SYMBOL].getOrCreateRef(prop, shape)
2302
+ };
2303
+ }
2304
+ return void 0;
2305
+ }
2306
+ });
2307
+ return proxy;
2308
+ }
1981
2309
 
1982
- // src/typed-refs/text.ts
1983
- var TextRef = class extends TypedRef {
1984
- // Track if we've materialized the container (made any changes)
1985
- _materialized = false;
1986
- get container() {
1987
- return super.container;
1988
- }
1989
- absorbPlainValues() {
1990
- }
1991
- // Text methods
2310
+ // src/typed-refs/text-ref-internals.ts
2311
+ var TextRefInternals = class extends BaseRefInternals {
2312
+ materialized = false;
2313
+ /** Insert text at the given index */
1992
2314
  insert(index, content) {
1993
- this._materialized = true;
1994
- this.container.insert(index, content);
2315
+ this.materialized = true;
2316
+ this.getContainer().insert(index, content);
1995
2317
  this.commitIfAuto();
1996
2318
  }
2319
+ /** Delete text at the given index */
1997
2320
  delete(index, len) {
1998
- this._materialized = true;
1999
- this.container.delete(index, len);
2321
+ this.materialized = true;
2322
+ this.getContainer().delete(index, len);
2000
2323
  this.commitIfAuto();
2001
2324
  }
2002
- /**
2003
- * Returns the text content.
2004
- * If the text hasn't been materialized (no operations performed),
2005
- * returns the placeholder value if available.
2006
- */
2007
- toString() {
2008
- const containerValue = this.container.toString();
2009
- if (containerValue !== "" || this._materialized) {
2325
+ /** Update the entire text content */
2326
+ update(text) {
2327
+ this.materialized = true;
2328
+ this.getContainer().update(text);
2329
+ this.commitIfAuto();
2330
+ }
2331
+ /** Mark a range of text with a key-value pair */
2332
+ mark(range, key, value) {
2333
+ this.materialized = true;
2334
+ this.getContainer().mark(range, key, value);
2335
+ this.commitIfAuto();
2336
+ }
2337
+ /** Remove a mark from a range of text */
2338
+ unmark(range, key) {
2339
+ this.materialized = true;
2340
+ this.getContainer().unmark(range, key);
2341
+ this.commitIfAuto();
2342
+ }
2343
+ /** Apply a delta to the text */
2344
+ applyDelta(delta) {
2345
+ this.materialized = true;
2346
+ this.getContainer().applyDelta(delta);
2347
+ this.commitIfAuto();
2348
+ }
2349
+ /** Get the text as a string */
2350
+ getStringValue() {
2351
+ const container = this.getContainer();
2352
+ const containerValue = container.toString();
2353
+ if (containerValue !== "" || this.materialized) {
2010
2354
  return containerValue;
2011
2355
  }
2012
- if (this.placeholder !== void 0) {
2013
- return this.placeholder;
2356
+ const placeholder = this.getPlaceholder();
2357
+ if (placeholder !== void 0) {
2358
+ return placeholder;
2014
2359
  }
2015
2360
  return containerValue;
2016
2361
  }
2017
- valueOf() {
2018
- return this.toString();
2362
+ /** Get the text as a delta */
2363
+ toDelta() {
2364
+ return this.getContainer().toDelta();
2019
2365
  }
2020
- toJSON() {
2021
- return this.toString();
2366
+ /** Get the length of the text */
2367
+ getLength() {
2368
+ return this.getContainer().length;
2022
2369
  }
2023
- [Symbol.toPrimitive](_hint) {
2024
- return this.toString();
2370
+ /** No plain values in text */
2371
+ absorbPlainValues() {
2372
+ }
2373
+ /** Create the loro namespace for text */
2374
+ createLoroNamespace() {
2375
+ const self = this;
2376
+ return {
2377
+ get doc() {
2378
+ return self.getDoc();
2379
+ },
2380
+ get container() {
2381
+ return self.getContainer();
2382
+ },
2383
+ subscribe(callback) {
2384
+ return self.getContainer().subscribe(callback);
2385
+ }
2386
+ };
2387
+ }
2388
+ };
2389
+
2390
+ // src/typed-refs/text-ref.ts
2391
+ var TextRef = class extends TypedRef {
2392
+ [INTERNAL_SYMBOL];
2393
+ constructor(params) {
2394
+ super();
2395
+ this[INTERNAL_SYMBOL] = new TextRefInternals(params);
2396
+ }
2397
+ /** Insert text at the given index */
2398
+ insert(index, content) {
2399
+ this[INTERNAL_SYMBOL].insert(index, content);
2400
+ }
2401
+ /** Delete text at the given index */
2402
+ delete(index, len) {
2403
+ this[INTERNAL_SYMBOL].delete(index, len);
2025
2404
  }
2405
+ /** Update the entire text content */
2026
2406
  update(text) {
2027
- this._materialized = true;
2028
- this.container.update(text);
2029
- this.commitIfAuto();
2407
+ this[INTERNAL_SYMBOL].update(text);
2030
2408
  }
2409
+ /** Mark a range of text with a key-value pair */
2031
2410
  mark(range, key, value) {
2032
- this._materialized = true;
2033
- this.container.mark(range, key, value);
2034
- this.commitIfAuto();
2411
+ this[INTERNAL_SYMBOL].mark(range, key, value);
2035
2412
  }
2413
+ /** Remove a mark from a range of text */
2036
2414
  unmark(range, key) {
2037
- this._materialized = true;
2038
- this.container.unmark(range, key);
2039
- this.commitIfAuto();
2040
- }
2041
- toDelta() {
2042
- return this.container.toDelta();
2415
+ this[INTERNAL_SYMBOL].unmark(range, key);
2043
2416
  }
2417
+ /** Apply a delta to the text */
2044
2418
  applyDelta(delta) {
2045
- this._materialized = true;
2046
- this.container.applyDelta(delta);
2047
- this.commitIfAuto();
2419
+ this[INTERNAL_SYMBOL].applyDelta(delta);
2420
+ }
2421
+ /** Get the text as a string */
2422
+ toString() {
2423
+ return this[INTERNAL_SYMBOL].getStringValue();
2424
+ }
2425
+ valueOf() {
2426
+ return this.toString();
2427
+ }
2428
+ toJSON() {
2429
+ return this.toString();
2048
2430
  }
2431
+ [Symbol.toPrimitive](_hint) {
2432
+ return this.toString();
2433
+ }
2434
+ /** Get the text as a delta */
2435
+ toDelta() {
2436
+ return this[INTERNAL_SYMBOL].toDelta();
2437
+ }
2438
+ /** Get the length of the text */
2049
2439
  get length() {
2050
- return this.container.length;
2440
+ return this[INTERNAL_SYMBOL].getLength();
2051
2441
  }
2052
2442
  };
2053
2443
 
2054
- // src/typed-refs/tree-node.ts
2055
- var TreeNodeRef = class {
2056
- _node;
2057
- _dataShape;
2058
- _treeRef;
2059
- _dataRef;
2060
- _autoCommit;
2061
- _batchedMutation;
2062
- _getDoc;
2444
+ // src/typed-refs/tree-node-ref-internals.ts
2445
+ var TreeNodeRefInternals = class {
2063
2446
  constructor(params) {
2064
- this._node = params.node;
2065
- this._dataShape = params.dataShape;
2066
- this._treeRef = params.treeRef;
2067
- this._autoCommit = params.autoCommit ?? false;
2068
- this._batchedMutation = params.batchedMutation ?? false;
2069
- this._getDoc = params.getDoc;
2447
+ this.params = params;
2070
2448
  }
2071
- /**
2072
- * The unique TreeID of this node.
2073
- */
2074
- get id() {
2075
- return this._node.id;
2449
+ dataRef;
2450
+ /** Get the underlying LoroTreeNode */
2451
+ getNode() {
2452
+ return this.params.node;
2076
2453
  }
2077
- /**
2078
- * Typed access to the node's metadata.
2079
- * This is a StructRef wrapping the node's LoroMap data container.
2080
- */
2081
- get data() {
2082
- if (!this._dataRef) {
2083
- const dataContainer = this._node.data;
2454
+ /** Get the data shape for this node */
2455
+ getDataShape() {
2456
+ return this.params.dataShape;
2457
+ }
2458
+ /** Get the parent TreeRef */
2459
+ getTreeRef() {
2460
+ return this.params.treeRef;
2461
+ }
2462
+ /** Check if autoCommit is enabled */
2463
+ getAutoCommit() {
2464
+ return this.params.autoCommit ?? false;
2465
+ }
2466
+ /** Check if in batched mutation mode */
2467
+ getBatchedMutation() {
2468
+ return this.params.batchedMutation ?? false;
2469
+ }
2470
+ /** Get the LoroDoc */
2471
+ getDoc() {
2472
+ return this.params.getDoc();
2473
+ }
2474
+ /** Commit changes if autoCommit is enabled */
2475
+ commitIfAuto() {
2476
+ if (this.params.autoCommit) {
2477
+ this.params.getDoc().commit();
2478
+ }
2479
+ }
2480
+ /** Get or create the data StructRef */
2481
+ getOrCreateDataRef() {
2482
+ if (!this.dataRef) {
2483
+ const node = this.getNode();
2484
+ const dataShape = this.getDataShape();
2485
+ const dataContainer = node.data;
2084
2486
  if (!dataContainer) {
2085
- throw new Error(`Node ${this.id} has no data container`);
2487
+ throw new Error(`Node ${node.id} has no data container`);
2086
2488
  }
2087
- const placeholder = deriveShapePlaceholder(
2088
- this._dataShape
2089
- );
2090
- const params = {
2489
+ const placeholder = deriveShapePlaceholder(dataShape);
2490
+ const refParams = {
2091
2491
  shape: {
2092
2492
  _type: "struct",
2093
- shapes: this._dataShape.shapes,
2493
+ shapes: dataShape.shapes,
2094
2494
  _plain: {},
2095
2495
  _mutable: {},
2096
2496
  _placeholder: {}
2097
2497
  },
2098
2498
  placeholder,
2099
2499
  getContainer: () => dataContainer,
2100
- autoCommit: this._autoCommit,
2101
- batchedMutation: this._batchedMutation,
2102
- getDoc: this._getDoc
2500
+ autoCommit: this.getAutoCommit(),
2501
+ batchedMutation: this.getBatchedMutation(),
2502
+ getDoc: this.params.getDoc
2103
2503
  };
2104
- this._dataRef = new StructRef(params);
2504
+ this.dataRef = createStructRef(refParams);
2505
+ }
2506
+ return this.dataRef;
2507
+ }
2508
+ /** Absorb mutated plain values back into Loro containers */
2509
+ absorbPlainValues() {
2510
+ if (this.dataRef) {
2511
+ this.dataRef[INTERNAL_SYMBOL].absorbPlainValues();
2105
2512
  }
2106
- return this._dataRef;
2513
+ }
2514
+ };
2515
+
2516
+ // src/typed-refs/tree-node-ref.ts
2517
+ var TreeNodeRef = class {
2518
+ [INTERNAL_SYMBOL];
2519
+ constructor(params) {
2520
+ this[INTERNAL_SYMBOL] = new TreeNodeRefInternals(params);
2521
+ }
2522
+ /**
2523
+ * The unique TreeID of this node.
2524
+ */
2525
+ get id() {
2526
+ return this[INTERNAL_SYMBOL].getNode().id;
2527
+ }
2528
+ /**
2529
+ * Typed access to the node's metadata.
2530
+ * This is a StructRef wrapping the node's LoroMap data container.
2531
+ */
2532
+ get data() {
2533
+ return this[INTERNAL_SYMBOL].getOrCreateDataRef();
2107
2534
  }
2108
2535
  /**
2109
2536
  * Create a child node under this node.
@@ -2113,33 +2540,37 @@ var TreeNodeRef = class {
2113
2540
  * @returns The created child TreeNodeRef
2114
2541
  */
2115
2542
  createNode(initialData, index) {
2116
- const loroNode = this._node.createNode(index);
2117
- const nodeRef = this._treeRef.getOrCreateNodeRef(loroNode);
2543
+ const node = this[INTERNAL_SYMBOL].getNode();
2544
+ const treeRef = this[INTERNAL_SYMBOL].getTreeRef();
2545
+ const loroNode = node.createNode(index);
2546
+ const nodeRef = treeRef.getOrCreateNodeRef(loroNode);
2118
2547
  if (initialData) {
2119
2548
  for (const [key, value] of Object.entries(initialData)) {
2120
2549
  ;
2121
2550
  nodeRef.data[key] = value;
2122
2551
  }
2123
2552
  }
2124
- this.commitIfAuto();
2553
+ this[INTERNAL_SYMBOL].commitIfAuto();
2125
2554
  return nodeRef;
2126
2555
  }
2127
2556
  /**
2128
2557
  * Get the parent node, if any.
2129
2558
  */
2130
2559
  parent() {
2131
- const parentNode = this._node.parent?.();
2560
+ const node = this[INTERNAL_SYMBOL].getNode();
2561
+ const treeRef = this[INTERNAL_SYMBOL].getTreeRef();
2562
+ const parentNode = node.parent?.();
2132
2563
  if (!parentNode) return void 0;
2133
- return this._treeRef.getOrCreateNodeRef(parentNode);
2564
+ return treeRef.getOrCreateNodeRef(parentNode);
2134
2565
  }
2135
2566
  /**
2136
2567
  * Get all child nodes in order.
2137
2568
  */
2138
2569
  children() {
2139
- const childNodes = this._node.children?.() || [];
2140
- return childNodes.map(
2141
- (node) => this._treeRef.getOrCreateNodeRef(node)
2142
- );
2570
+ const node = this[INTERNAL_SYMBOL].getNode();
2571
+ const treeRef = this[INTERNAL_SYMBOL].getTreeRef();
2572
+ const childNodes = node.children?.() || [];
2573
+ return childNodes.map((n) => treeRef.getOrCreateNodeRef(n));
2143
2574
  }
2144
2575
  /**
2145
2576
  * Move this node to a new parent.
@@ -2148,49 +2579,49 @@ var TreeNodeRef = class {
2148
2579
  * @param index - Optional position among siblings
2149
2580
  */
2150
2581
  move(newParent, index) {
2151
- const parentNode = newParent?._node;
2152
- this._node.move?.(parentNode, index);
2153
- this.commitIfAuto();
2582
+ const node = this[INTERNAL_SYMBOL].getNode();
2583
+ const parentNode = newParent ? newParent[INTERNAL_SYMBOL].getNode() : void 0;
2584
+ node.move?.(parentNode, index);
2585
+ this[INTERNAL_SYMBOL].commitIfAuto();
2154
2586
  }
2155
2587
  /**
2156
2588
  * Move this node to be after the given sibling.
2157
2589
  */
2158
2590
  moveAfter(sibling) {
2159
- this._node.moveAfter(sibling._node);
2160
- this.commitIfAuto();
2591
+ const node = this[INTERNAL_SYMBOL].getNode();
2592
+ const siblingNode = sibling[INTERNAL_SYMBOL].getNode();
2593
+ node.moveAfter(siblingNode);
2594
+ this[INTERNAL_SYMBOL].commitIfAuto();
2161
2595
  }
2162
2596
  /**
2163
2597
  * Move this node to be before the given sibling.
2164
2598
  */
2165
2599
  moveBefore(sibling) {
2166
- this._node.moveBefore(sibling._node);
2167
- this.commitIfAuto();
2600
+ const node = this[INTERNAL_SYMBOL].getNode();
2601
+ const siblingNode = sibling[INTERNAL_SYMBOL].getNode();
2602
+ node.moveBefore(siblingNode);
2603
+ this[INTERNAL_SYMBOL].commitIfAuto();
2168
2604
  }
2169
2605
  /**
2170
2606
  * Get the index of this node among its siblings.
2171
2607
  */
2172
2608
  index() {
2173
- return this._node.index();
2609
+ const node = this[INTERNAL_SYMBOL].getNode();
2610
+ return node.index();
2174
2611
  }
2175
2612
  /**
2176
2613
  * Get the fractional index string for ordering.
2177
2614
  */
2178
2615
  fractionalIndex() {
2179
- return this._node.fractionalIndex();
2616
+ const node = this[INTERNAL_SYMBOL].getNode();
2617
+ return node.fractionalIndex();
2180
2618
  }
2181
2619
  /**
2182
2620
  * Check if this node has been deleted.
2183
2621
  */
2184
2622
  isDeleted() {
2185
- return this._node.isDeleted();
2186
- }
2187
- /**
2188
- * Absorb plain values from the data StructRef.
2189
- */
2190
- absorbPlainValues() {
2191
- if (this._dataRef) {
2192
- this._dataRef.absorbPlainValues();
2193
- }
2623
+ const node = this[INTERNAL_SYMBOL].getNode();
2624
+ return node.isDeleted();
2194
2625
  }
2195
2626
  /**
2196
2627
  * Serialize this node and its descendants to JSON.
@@ -2206,78 +2637,123 @@ var TreeNodeRef = class {
2206
2637
  children: children.map((child) => child.toJSON())
2207
2638
  };
2208
2639
  }
2209
- commitIfAuto() {
2210
- if (this._autoCommit && this._getDoc) {
2211
- this._getDoc().commit();
2212
- }
2213
- }
2214
2640
  };
2215
2641
 
2216
- // src/typed-refs/tree.ts
2217
- var TreeRef = class {
2642
+ // src/typed-refs/tree-ref-internals.ts
2643
+ var TreeRefInternals = class extends BaseRefInternals {
2218
2644
  nodeCache = /* @__PURE__ */ new Map();
2219
- _cachedContainer;
2220
- _params;
2221
- constructor(params) {
2222
- this._params = params;
2645
+ treeRef = null;
2646
+ /** Set the parent TreeRef (needed for creating node refs) */
2647
+ setTreeRef(treeRef) {
2648
+ this.treeRef = treeRef;
2223
2649
  }
2224
- get shape() {
2225
- return this._params.shape;
2650
+ /** Get the data shape for tree nodes */
2651
+ getDataShape() {
2652
+ const shape = this.getShape();
2653
+ return shape.shape;
2226
2654
  }
2227
- get container() {
2228
- if (!this._cachedContainer) {
2229
- this._cachedContainer = this._params.getContainer();
2655
+ /** Get or create a node ref for a LoroTreeNode */
2656
+ getOrCreateNodeRef(node) {
2657
+ const id = node.id;
2658
+ if (!this.treeRef) {
2659
+ throw new Error("treeRef required");
2230
2660
  }
2231
- return this._cachedContainer;
2661
+ let nodeRef = this.nodeCache.get(id);
2662
+ if (!nodeRef) {
2663
+ nodeRef = new TreeNodeRef({
2664
+ node,
2665
+ dataShape: this.getDataShape(),
2666
+ treeRef: this.treeRef,
2667
+ autoCommit: this.getAutoCommit(),
2668
+ batchedMutation: this.getBatchedMutation(),
2669
+ getDoc: () => this.getDoc()
2670
+ });
2671
+ this.nodeCache.set(id, nodeRef);
2672
+ }
2673
+ return nodeRef;
2232
2674
  }
2233
- get dataShape() {
2234
- return this.shape.shape;
2675
+ /** Get a node by its ID */
2676
+ getNodeByID(id) {
2677
+ const cached = this.nodeCache.get(id);
2678
+ if (cached) return cached;
2679
+ const container = this.getContainer();
2680
+ if (!container.has(id)) return void 0;
2681
+ const nodes = container.nodes();
2682
+ const node = nodes.find((n) => n.id === id);
2683
+ if (!node) return void 0;
2684
+ return this.getOrCreateNodeRef(node);
2685
+ }
2686
+ /** Delete a node from the tree */
2687
+ delete(target) {
2688
+ const id = typeof target === "string" ? target : target.id;
2689
+ const container = this.getContainer();
2690
+ container.delete(id);
2691
+ this.nodeCache.delete(id);
2692
+ this.commitIfAuto();
2693
+ }
2694
+ /** Absorb mutated plain values back into Loro containers */
2695
+ absorbPlainValues() {
2696
+ for (const nodeRef of this.nodeCache.values()) {
2697
+ nodeRef[INTERNAL_SYMBOL].absorbPlainValues();
2698
+ }
2699
+ }
2700
+ /** Create the loro namespace for tree */
2701
+ createLoroNamespace() {
2702
+ const self = this;
2703
+ return {
2704
+ get doc() {
2705
+ return self.getDoc();
2706
+ },
2707
+ get container() {
2708
+ return self.getContainer();
2709
+ },
2710
+ subscribe(callback) {
2711
+ return self.getContainer().subscribe(callback);
2712
+ }
2713
+ };
2235
2714
  }
2236
- get autoCommit() {
2237
- return !!this._params.autoCommit;
2715
+ };
2716
+
2717
+ // src/typed-refs/tree-ref.ts
2718
+ var TreeRef = class extends TypedRef {
2719
+ [INTERNAL_SYMBOL];
2720
+ constructor(params) {
2721
+ super();
2722
+ this[INTERNAL_SYMBOL] = new TreeRefInternals(params);
2723
+ this[INTERNAL_SYMBOL].setTreeRef(this);
2238
2724
  }
2239
- get batchedMutation() {
2240
- return !!this._params.batchedMutation;
2725
+ /**
2726
+ * Get the data shape for tree nodes.
2727
+ */
2728
+ get dataShape() {
2729
+ return this[INTERNAL_SYMBOL].getDataShape();
2241
2730
  }
2242
- get doc() {
2243
- return this._params.getDoc?.();
2731
+ /**
2732
+ * Get or create a node ref for a LoroTreeNode.
2733
+ */
2734
+ getOrCreateNodeRef(node) {
2735
+ return this[INTERNAL_SYMBOL].getOrCreateNodeRef(node);
2244
2736
  }
2245
2737
  /**
2246
- * Commits changes if autoCommit is enabled.
2738
+ * Get a node by its ID.
2247
2739
  */
2248
- commitIfAuto() {
2249
- if (this.autoCommit && this.doc) {
2250
- this.doc.commit();
2251
- }
2740
+ getNodeByID(id) {
2741
+ return this[INTERNAL_SYMBOL].getNodeByID(id);
2252
2742
  }
2253
2743
  /**
2254
- * Absorb plain values from all cached nodes.
2255
- * Called before committing changes to ensure all pending values are written.
2744
+ * Delete a node from the tree.
2256
2745
  */
2257
- absorbPlainValues() {
2258
- for (const nodeRef of this.nodeCache.values()) {
2259
- nodeRef.absorbPlainValues();
2260
- }
2746
+ delete(target) {
2747
+ this[INTERNAL_SYMBOL].delete(target);
2261
2748
  }
2262
2749
  /**
2263
- * Get or create a TreeNodeRef for the given LoroTreeNode.
2264
- * Uses caching to ensure the same TreeNodeRef is returned for the same node.
2750
+ * Serialize the tree to a nested JSON structure.
2751
+ * Each node includes its data and children recursively.
2265
2752
  */
2266
- getOrCreateNodeRef(node) {
2267
- const id = node.id;
2268
- let nodeRef = this.nodeCache.get(id);
2269
- if (!nodeRef) {
2270
- nodeRef = new TreeNodeRef({
2271
- node,
2272
- dataShape: this.dataShape,
2273
- treeRef: this,
2274
- autoCommit: this.autoCommit,
2275
- batchedMutation: this.batchedMutation,
2276
- getDoc: this._params.getDoc
2277
- });
2278
- this.nodeCache.set(id, nodeRef);
2279
- }
2280
- return nodeRef;
2753
+ toJSON() {
2754
+ const container = this[INTERNAL_SYMBOL].getContainer();
2755
+ const nativeJson = container.toJSON();
2756
+ return this.transformNativeJson(nativeJson);
2281
2757
  }
2282
2758
  /**
2283
2759
  * Create a new root node with optional initial data.
@@ -2286,7 +2762,8 @@ var TreeRef = class {
2286
2762
  * @returns The created TreeNodeRef
2287
2763
  */
2288
2764
  createNode(initialData) {
2289
- const loroNode = this.container.createNode();
2765
+ const container = this[INTERNAL_SYMBOL].getContainer();
2766
+ const loroNode = container.createNode();
2290
2767
  const nodeRef = this.getOrCreateNodeRef(loroNode);
2291
2768
  if (initialData) {
2292
2769
  for (const [key, value] of Object.entries(initialData)) {
@@ -2294,7 +2771,7 @@ var TreeRef = class {
2294
2771
  nodeRef.data[key] = value;
2295
2772
  }
2296
2773
  }
2297
- this.commitIfAuto();
2774
+ this[INTERNAL_SYMBOL].commitIfAuto();
2298
2775
  return nodeRef;
2299
2776
  }
2300
2777
  /**
@@ -2302,47 +2779,23 @@ var TreeRef = class {
2302
2779
  * Returns nodes in their fractional index order.
2303
2780
  */
2304
2781
  roots() {
2305
- return this.container.roots().map((node) => this.getOrCreateNodeRef(node));
2782
+ const container = this[INTERNAL_SYMBOL].getContainer();
2783
+ return container.roots().map((node) => this.getOrCreateNodeRef(node));
2306
2784
  }
2307
2785
  /**
2308
2786
  * Get all nodes in the tree (unordered).
2309
2787
  * Includes all nodes, not just roots.
2310
2788
  */
2311
2789
  nodes() {
2312
- return this.container.nodes().map((node) => this.getOrCreateNodeRef(node));
2313
- }
2314
- /**
2315
- * Get a node by its TreeID.
2316
- *
2317
- * @param id - The TreeID of the node to find
2318
- * @returns The TreeNodeRef if found, undefined otherwise
2319
- */
2320
- getNodeByID(id) {
2321
- const cached = this.nodeCache.get(id);
2322
- if (cached) return cached;
2323
- if (!this.container.has(id)) return void 0;
2324
- const nodes = this.container.nodes();
2325
- const node = nodes.find((n) => n.id === id);
2326
- if (!node) return void 0;
2327
- return this.getOrCreateNodeRef(node);
2790
+ const container = this[INTERNAL_SYMBOL].getContainer();
2791
+ return container.nodes().map((node) => this.getOrCreateNodeRef(node));
2328
2792
  }
2329
2793
  /**
2330
2794
  * Check if a node with the given ID exists in the tree.
2331
2795
  */
2332
2796
  has(id) {
2333
- return this.container.has(id);
2334
- }
2335
- /**
2336
- * Delete a node and all its descendants.
2337
- * Also removes the node from the cache.
2338
- *
2339
- * @param target - The TreeID or TreeNodeRef to delete
2340
- */
2341
- delete(target) {
2342
- const id = typeof target === "string" ? target : target.id;
2343
- this.container.delete(id);
2344
- this.nodeCache.delete(id);
2345
- this.commitIfAuto();
2797
+ const container = this[INTERNAL_SYMBOL].getContainer();
2798
+ return container.has(id);
2346
2799
  }
2347
2800
  /**
2348
2801
  * Enable fractional index generation for ordering.
@@ -2350,15 +2803,8 @@ var TreeRef = class {
2350
2803
  * @param jitter - Optional jitter value to avoid conflicts (0 = no jitter)
2351
2804
  */
2352
2805
  enableFractionalIndex(jitter = 0) {
2353
- this.container.enableFractionalIndex(jitter);
2354
- }
2355
- /**
2356
- * Serialize the tree to a nested JSON structure.
2357
- * Each node includes its data and children recursively.
2358
- */
2359
- toJSON() {
2360
- const nativeJson = this.container.toJSON();
2361
- return this.transformNativeJson(nativeJson);
2806
+ const container = this[INTERNAL_SYMBOL].getContainer();
2807
+ container.enableFractionalIndex(jitter);
2362
2808
  }
2363
2809
  /**
2364
2810
  * Transform Loro's native JSON format to our typed format.
@@ -2393,7 +2839,8 @@ var TreeRef = class {
2393
2839
  }
2394
2840
  }
2395
2841
  };
2396
- const nativeJson = this.container.toJSON();
2842
+ const container = this[INTERNAL_SYMBOL].getContainer();
2843
+ const nativeJson = container.toJSON();
2397
2844
  flattenNodes(nativeJson);
2398
2845
  return result;
2399
2846
  }
@@ -2414,14 +2861,14 @@ var containerConstructor = {
2414
2861
  function hasContainerConstructor(type) {
2415
2862
  return type in containerConstructor;
2416
2863
  }
2417
- function hasAbsorbPlainValues(value) {
2418
- return value !== null && typeof value === "object" && "absorbPlainValues" in value && typeof value.absorbPlainValues === "function";
2864
+ function hasInternalSymbol(value) {
2865
+ return value !== null && typeof value === "object" && INTERNAL_SYMBOL in value;
2419
2866
  }
2420
2867
  function absorbCachedPlainValues(cache, getContainer) {
2421
2868
  let container;
2422
2869
  for (const [key, ref] of cache.entries()) {
2423
- if (hasAbsorbPlainValues(ref)) {
2424
- ref.absorbPlainValues();
2870
+ if (hasInternalSymbol(ref)) {
2871
+ ref[INTERNAL_SYMBOL].absorbPlainValues();
2425
2872
  } else {
2426
2873
  if (!container) container = getContainer();
2427
2874
  container.set(key, ref);
@@ -2450,7 +2897,9 @@ function createContainerTypedRef(params) {
2450
2897
  listProxyHandler
2451
2898
  );
2452
2899
  case "struct":
2453
- return new StructRef(params);
2900
+ return createStructRef(
2901
+ params
2902
+ );
2454
2903
  case "movableList":
2455
2904
  return new Proxy(
2456
2905
  new MovableListRef(params),
@@ -2469,7 +2918,6 @@ function createContainerTypedRef(params) {
2469
2918
  shape: treeShape,
2470
2919
  placeholder: params.placeholder,
2471
2920
  getContainer: params.getContainer,
2472
- readonly: params.readonly,
2473
2921
  autoCommit: params.autoCommit,
2474
2922
  getDoc: params.getDoc
2475
2923
  });
@@ -2481,7 +2929,8 @@ function createContainerTypedRef(params) {
2481
2929
  }
2482
2930
  }
2483
2931
  function assignPlainValueToTypedRef(ref, value) {
2484
- const shapeType = ref.shape._type;
2932
+ const shape = ref[INTERNAL_SYMBOL]?.getShape?.() ?? ref.shape;
2933
+ const shapeType = shape?._type;
2485
2934
  if (shapeType === "struct" || shapeType === "record") {
2486
2935
  for (const k in value) {
2487
2936
  ;
@@ -2527,7 +2976,7 @@ function assignPlainValueToTypedRef(ref, value) {
2527
2976
  return false;
2528
2977
  }
2529
2978
 
2530
- // src/typed-refs/doc.ts
2979
+ // src/typed-refs/doc-ref-internals.ts
2531
2980
  var containerGetter = {
2532
2981
  counter: "getCounter",
2533
2982
  list: "getList",
@@ -2538,23 +2987,22 @@ var containerGetter = {
2538
2987
  text: "getText",
2539
2988
  tree: "getTree"
2540
2989
  };
2541
- var DocRef = class extends TypedRef {
2542
- _doc;
2990
+ var DocRefInternals = class extends BaseRefInternals {
2543
2991
  propertyCache = /* @__PURE__ */ new Map();
2992
+ doc;
2544
2993
  requiredPlaceholder;
2545
- constructor(_params) {
2994
+ constructor(params) {
2546
2995
  super({
2547
- ..._params,
2996
+ ...params,
2548
2997
  getContainer: () => {
2549
2998
  throw new Error("can't get container on DocRef");
2550
2999
  },
2551
- getDoc: () => this._doc
3000
+ getDoc: () => params.doc
2552
3001
  });
2553
- if (!_params.placeholder) throw new Error("placeholder required");
2554
- this._doc = _params.doc;
2555
- this.requiredPlaceholder = _params.placeholder;
2556
- this.createLazyProperties();
3002
+ this.doc = params.doc;
3003
+ this.requiredPlaceholder = params.placeholder;
2557
3004
  }
3005
+ /** Get typed ref params for creating child refs at a key */
2558
3006
  getTypedRefParams(key, shape) {
2559
3007
  if (shape._type === "any") {
2560
3008
  throw new Error(
@@ -2562,16 +3010,17 @@ var DocRef = class extends TypedRef {
2562
3010
  );
2563
3011
  }
2564
3012
  const getterName = containerGetter[shape._type];
2565
- const getter = this._doc[getterName].bind(this._doc);
3013
+ const getter = this.doc[getterName].bind(this.doc);
2566
3014
  return {
2567
3015
  shape,
2568
3016
  placeholder: this.requiredPlaceholder[key],
2569
3017
  getContainer: () => getter(key),
2570
- autoCommit: this._params.autoCommit,
2571
- batchedMutation: this.batchedMutation,
2572
- getDoc: () => this._doc
3018
+ autoCommit: this.getAutoCommit(),
3019
+ batchedMutation: this.getBatchedMutation(),
3020
+ getDoc: () => this.doc
2573
3021
  };
2574
3022
  }
3023
+ /** Get or create a typed ref for a key */
2575
3024
  getOrCreateTypedRef(key, shape) {
2576
3025
  let ref = this.propertyCache.get(key);
2577
3026
  if (!ref) {
@@ -2580,26 +3029,40 @@ var DocRef = class extends TypedRef {
2580
3029
  }
2581
3030
  return ref;
2582
3031
  }
3032
+ /** Absorb mutated plain values back into Loro containers */
3033
+ absorbPlainValues() {
3034
+ for (const [, ref] of this.propertyCache.entries()) {
3035
+ ref[INTERNAL_SYMBOL].absorbPlainValues();
3036
+ }
3037
+ }
3038
+ };
3039
+
3040
+ // src/typed-refs/doc-ref.ts
3041
+ var DocRef = class extends TypedRef {
3042
+ [INTERNAL_SYMBOL];
3043
+ constructor(params) {
3044
+ super();
3045
+ if (!params.placeholder) throw new Error("placeholder required");
3046
+ this[INTERNAL_SYMBOL] = new DocRefInternals(params);
3047
+ this.createLazyProperties();
3048
+ }
2583
3049
  createLazyProperties() {
2584
- for (const key in this.shape.shapes) {
2585
- const shape = this.shape.shapes[key];
3050
+ const shape = this[INTERNAL_SYMBOL].getShape();
3051
+ for (const key in shape.shapes) {
3052
+ const containerShape = shape.shapes[key];
2586
3053
  Object.defineProperty(this, key, {
2587
- get: () => this.getOrCreateTypedRef(key, shape),
3054
+ get: () => this[INTERNAL_SYMBOL].getOrCreateTypedRef(key, containerShape),
2588
3055
  enumerable: true
2589
3056
  });
2590
3057
  }
2591
3058
  }
2592
3059
  toJSON() {
3060
+ const shape = this[INTERNAL_SYMBOL].getShape();
2593
3061
  return serializeRefToJSON(
2594
3062
  this,
2595
- Object.keys(this.shape.shapes)
3063
+ Object.keys(shape.shapes)
2596
3064
  );
2597
3065
  }
2598
- absorbPlainValues() {
2599
- for (const [, ref] of this.propertyCache.entries()) {
2600
- ref.absorbPlainValues();
2601
- }
2602
- }
2603
3066
  };
2604
3067
 
2605
3068
  // src/validation.ts
@@ -2831,68 +3294,11 @@ function validatePlaceholder(placeholder, schema) {
2831
3294
  }
2832
3295
 
2833
3296
  // src/typed-doc.ts
2834
- var TypedDocMeta = class {
2835
- constructor(internal) {
2836
- this.internal = internal;
2837
- }
2838
- /**
2839
- * The primary method of mutating typed documents.
2840
- * Batches multiple mutations into a single transaction.
2841
- * All changes commit together at the end.
2842
- *
2843
- * Use this for:
2844
- * - Find-and-mutate operations (required due to JS limitations)
2845
- * - Performance (fewer commits)
2846
- * - Atomic undo (all changes = one undo step)
2847
- *
2848
- * Returns the doc for chaining.
2849
- */
2850
- change(fn) {
2851
- this.internal.change(fn);
2852
- return this.internal.proxy;
2853
- }
2854
- /**
2855
- * Returns the full plain JavaScript object representation of the document.
2856
- * This is an expensive O(N) operation that serializes the entire document.
2857
- */
2858
- toJSON() {
2859
- return this.internal.toJSON();
2860
- }
2861
- /**
2862
- * Apply JSON Patch operations to the document
2863
- *
2864
- * @param patch - Array of JSON Patch operations (RFC 6902)
2865
- * @param pathPrefix - Optional path prefix for scoped operations
2866
- * @returns Updated document value
2867
- */
2868
- applyPatch(patch, pathPrefix) {
2869
- this.internal.applyPatch(patch, pathPrefix);
2870
- return this.internal.proxy;
2871
- }
2872
- /**
2873
- * Access the underlying LoroDoc for advanced operations.
2874
- */
2875
- get loroDoc() {
2876
- return this.internal.loroDoc;
2877
- }
2878
- /**
2879
- * Access the document schema shape.
2880
- */
2881
- get docShape() {
2882
- return this.internal.docShape;
2883
- }
2884
- /**
2885
- * Get raw CRDT value without placeholder overlay.
2886
- */
2887
- get rawValue() {
2888
- return this.internal.rawValue;
2889
- }
2890
- };
2891
3297
  var TypedDocInternal = class {
2892
3298
  shape;
2893
3299
  placeholder;
2894
3300
  doc;
2895
- _valueRef = null;
3301
+ valueRef = null;
2896
3302
  // Reference to the proxy for returning from change()
2897
3303
  proxy = null;
2898
3304
  constructor(shape, doc = new LoroDoc()) {
@@ -2902,15 +3308,15 @@ var TypedDocInternal = class {
2902
3308
  validatePlaceholder(this.placeholder, this.shape);
2903
3309
  }
2904
3310
  get value() {
2905
- if (!this._valueRef) {
2906
- this._valueRef = new DocRef({
3311
+ if (!this.valueRef) {
3312
+ this.valueRef = new DocRef({
2907
3313
  shape: this.shape,
2908
3314
  placeholder: this.placeholder,
2909
3315
  doc: this.doc,
2910
3316
  autoCommit: true
2911
3317
  });
2912
3318
  }
2913
- return this._valueRef;
3319
+ return this.valueRef;
2914
3320
  }
2915
3321
  toJSON() {
2916
3322
  const crdtValue = this.doc.toJSON();
@@ -2930,9 +3336,9 @@ var TypedDocInternal = class {
2930
3336
  // Enable value shape caching for find-and-mutate patterns
2931
3337
  });
2932
3338
  fn(draft);
2933
- draft.absorbPlainValues();
3339
+ draft[INTERNAL_SYMBOL].absorbPlainValues();
2934
3340
  this.doc.commit();
2935
- this._valueRef = null;
3341
+ this.valueRef = null;
2936
3342
  }
2937
3343
  applyPatch(patch, pathPrefix) {
2938
3344
  this.change((draft) => {
@@ -2956,11 +3362,37 @@ var TypedDocInternal = class {
2956
3362
  };
2957
3363
  function createTypedDoc(shape, existingDoc) {
2958
3364
  const internal = new TypedDocInternal(shape, existingDoc || new LoroDoc());
2959
- const meta = new TypedDocMeta(internal);
3365
+ const loroNamespace = {
3366
+ get doc() {
3367
+ return internal.loroDoc;
3368
+ },
3369
+ get container() {
3370
+ return internal.loroDoc;
3371
+ },
3372
+ subscribe(callback) {
3373
+ return internal.loroDoc.subscribe(callback);
3374
+ },
3375
+ applyPatch(patch, pathPrefix) {
3376
+ internal.applyPatch(patch, pathPrefix);
3377
+ },
3378
+ get docShape() {
3379
+ return internal.docShape;
3380
+ },
3381
+ get rawValue() {
3382
+ return internal.rawValue;
3383
+ }
3384
+ };
3385
+ const changeFunction = (fn) => {
3386
+ internal.change(fn);
3387
+ return proxy;
3388
+ };
2960
3389
  const proxy = new Proxy(internal.value, {
2961
3390
  get(target, prop, receiver) {
2962
- if (prop === "$") {
2963
- return meta;
3391
+ if (prop === LORO_SYMBOL) {
3392
+ return loroNamespace;
3393
+ }
3394
+ if (prop === "change") {
3395
+ return changeFunction;
2964
3396
  }
2965
3397
  if (prop === "toJSON") {
2966
3398
  return () => internal.toJSON();
@@ -2968,26 +3400,33 @@ function createTypedDoc(shape, existingDoc) {
2968
3400
  return Reflect.get(target, prop, receiver);
2969
3401
  },
2970
3402
  set(target, prop, value, receiver) {
2971
- if (prop === "$") {
3403
+ if (prop === LORO_SYMBOL || prop === "change") {
2972
3404
  return false;
2973
3405
  }
2974
3406
  return Reflect.set(target, prop, value, receiver);
2975
3407
  },
2976
3408
  // Support 'in' operator
2977
3409
  has(target, prop) {
2978
- if (prop === "$") return true;
3410
+ if (prop === LORO_SYMBOL || prop === "change") return true;
2979
3411
  return Reflect.has(target, prop);
2980
3412
  },
2981
- // Support Object.keys() - don't include $ in enumeration
3413
+ // Support Object.keys() - don't include change or LORO_SYMBOL in enumeration
2982
3414
  ownKeys(target) {
2983
3415
  return Reflect.ownKeys(target);
2984
3416
  },
2985
3417
  getOwnPropertyDescriptor(target, prop) {
2986
- if (prop === "$") {
3418
+ if (prop === "change") {
3419
+ return {
3420
+ configurable: true,
3421
+ enumerable: false,
3422
+ value: changeFunction
3423
+ };
3424
+ }
3425
+ if (prop === LORO_SYMBOL) {
2987
3426
  return {
2988
3427
  configurable: true,
2989
3428
  enumerable: false,
2990
- value: meta
3429
+ value: loroNamespace
2991
3430
  };
2992
3431
  }
2993
3432
  return Reflect.getOwnPropertyDescriptor(target, prop);
@@ -2997,6 +3436,7 @@ function createTypedDoc(shape, existingDoc) {
2997
3436
  return proxy;
2998
3437
  }
2999
3438
  export {
3439
+ LORO_SYMBOL,
3000
3440
  Shape,
3001
3441
  change,
3002
3442
  compileToJsonPath,
@@ -3007,8 +3447,10 @@ export {
3007
3447
  deriveShapePlaceholder,
3008
3448
  evaluatePath,
3009
3449
  evaluatePathOnValue,
3450
+ getLoroContainer,
3010
3451
  getLoroDoc,
3011
3452
  hasWildcard,
3453
+ loro,
3012
3454
  mergeValue,
3013
3455
  overlayPlaceholder,
3014
3456
  validatePlaceholder