@loro-extended/change 3.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +289 -150
  2. package/dist/index.d.ts +1012 -310
  3. package/dist/index.js +1334 -568
  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 +27 -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/overlay.ts +62 -3
  16. package/src/path-evaluator.ts +1 -1
  17. package/src/path-selector.test.ts +94 -1
  18. package/src/shape.ts +107 -14
  19. package/src/typed-doc.ts +100 -98
  20. package/src/typed-refs/base.ts +126 -46
  21. package/src/typed-refs/counter-ref-internals.ts +62 -0
  22. package/src/typed-refs/{counter.test.ts → counter-ref.test.ts} +5 -4
  23. package/src/typed-refs/counter-ref.ts +45 -0
  24. package/src/typed-refs/{doc.ts → doc-ref-internals.ts} +33 -56
  25. package/src/typed-refs/doc-ref.ts +47 -0
  26. package/src/typed-refs/encapsulation.test.ts +226 -0
  27. package/src/typed-refs/list-ref-base-internals.ts +280 -0
  28. package/src/typed-refs/list-ref-base.ts +518 -0
  29. package/src/typed-refs/list-ref-internals.ts +21 -0
  30. package/src/typed-refs/list-ref-value-updates.test.ts +213 -0
  31. package/src/typed-refs/{list.ts → list-ref.ts} +10 -11
  32. package/src/typed-refs/movable-list-ref-internals.ts +38 -0
  33. package/src/typed-refs/movable-list-ref.ts +31 -0
  34. package/src/typed-refs/proxy-handlers.ts +13 -4
  35. package/src/typed-refs/record-ref-internals.ts +216 -0
  36. package/src/typed-refs/record-ref-value-updates.test.ts +214 -0
  37. package/src/typed-refs/{record.test.ts → record-ref.test.ts} +21 -16
  38. package/src/typed-refs/record-ref.ts +80 -0
  39. package/src/typed-refs/struct-ref-internals.ts +195 -0
  40. package/src/typed-refs/struct-ref.test.ts +202 -0
  41. package/src/typed-refs/struct-ref.ts +257 -0
  42. package/src/typed-refs/text-ref-internals.ts +100 -0
  43. package/src/typed-refs/text-ref.ts +72 -0
  44. package/src/typed-refs/tree-node-ref-internals.ts +111 -0
  45. package/src/typed-refs/tree-node-ref.test.ts +234 -0
  46. package/src/typed-refs/tree-node-ref.ts +200 -0
  47. package/src/typed-refs/tree-node.test.ts +384 -0
  48. package/src/typed-refs/tree-ref-internals.ts +110 -0
  49. package/src/typed-refs/tree-ref.ts +194 -0
  50. package/src/typed-refs/utils.ts +38 -17
  51. package/src/types.ts +36 -1
  52. package/src/utils/type-guards.ts +1 -0
  53. package/src/typed-refs/counter.ts +0 -64
  54. package/src/typed-refs/list-base.ts +0 -424
  55. package/src/typed-refs/movable-list.ts +0 -34
  56. package/src/typed-refs/record.ts +0 -220
  57. package/src/typed-refs/struct.ts +0 -206
  58. package/src/typed-refs/text.ts +0 -97
  59. package/src/typed-refs/tree.ts +0 -40
  60. /package/src/typed-refs/{list.test.ts → list-ref.test.ts} +0 -0
  61. /package/src/typed-refs/{movable-list.test.ts → movable-list-ref.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
@@ -124,7 +139,8 @@ function isValueShape(schema) {
124
139
  "record",
125
140
  "array",
126
141
  "union",
127
- "discriminatedUnion"
142
+ "discriminatedUnion",
143
+ "any"
128
144
  ].includes(schema.valueType);
129
145
  }
130
146
  function isObjectValue(value) {
@@ -199,8 +215,13 @@ function mergeValue(shape, crdtValue, placeholderValue) {
199
215
  }
200
216
  return result;
201
217
  }
202
- case "tree":
203
- return crdtValue !== void 0 ? crdtValue : placeholderValue ?? [];
218
+ case "tree": {
219
+ if (crdtValue === void 0) {
220
+ return placeholderValue ?? [];
221
+ }
222
+ const treeShape = shape;
223
+ return transformTreeNodes(crdtValue, treeShape.shape);
224
+ }
204
225
  case "record": {
205
226
  if (!isObjectValue(crdtValue) && crdtValue !== void 0) {
206
227
  throw new Error("record crdt must be object");
@@ -258,6 +279,23 @@ function mergeDiscriminatedUnion(shape, crdtValue, placeholderValue) {
258
279
  const effectivePlaceholderValue = placeholderDiscriminant === discriminantValue ? placeholderValue : void 0;
259
280
  return mergeValue(variantShape, crdtValue, effectivePlaceholderValue);
260
281
  }
282
+ function transformTreeNodes(nodes, dataShape) {
283
+ const dataPlaceholder = deriveShapePlaceholder(dataShape);
284
+ return nodes.map((node) => transformTreeNode(node, dataShape, dataPlaceholder));
285
+ }
286
+ function transformTreeNode(node, dataShape, dataPlaceholder) {
287
+ const mergedData = mergeValue(dataShape, node.meta, dataPlaceholder);
288
+ return {
289
+ id: node.id,
290
+ parent: node.parent,
291
+ index: node.index,
292
+ fractionalIndex: node.fractional_index,
293
+ data: mergedData,
294
+ children: node.children.map(
295
+ (child) => transformTreeNode(child, dataShape, dataPlaceholder)
296
+ )
297
+ };
298
+ }
261
299
 
262
300
  // src/path-builder.ts
263
301
  function createPathSelector(segments) {
@@ -373,7 +411,7 @@ function hasWildcard(segments) {
373
411
 
374
412
  // src/path-evaluator.ts
375
413
  function evaluatePath(doc, selector) {
376
- const json = doc.$.toJSON();
414
+ const json = doc.toJSON();
377
415
  return evaluatePathOnValue(json, selector.__segments);
378
416
  }
379
417
  function evaluatePathOnValue(value, segments) {
@@ -562,10 +600,32 @@ var Shape = {
562
600
  }
563
601
  });
564
602
  },
603
+ /**
604
+ * Creates a tree container shape for hierarchical data structures.
605
+ * Each node in the tree has typed metadata defined by the data shape.
606
+ *
607
+ * @example
608
+ * ```typescript
609
+ * const StateNodeDataShape = Shape.struct({
610
+ * name: Shape.text(),
611
+ * facts: Shape.record(Shape.plain.any()),
612
+ * })
613
+ *
614
+ * const Schema = Shape.doc({
615
+ * states: Shape.tree(StateNodeDataShape),
616
+ * })
617
+ *
618
+ * doc.change(draft => {
619
+ * const root = draft.states.createNode({ name: "idle", facts: {} })
620
+ * const child = root.createNode({ name: "running", facts: {} })
621
+ * child.data.name = "active"
622
+ * })
623
+ * ```
624
+ */
565
625
  tree: (shape) => ({
566
626
  _type: "tree",
567
627
  shape,
568
- _plain: {},
628
+ _plain: [],
569
629
  _mutable: {},
570
630
  _placeholder: []
571
631
  }),
@@ -1037,52 +1097,76 @@ var JsonPatchApplicator = class {
1037
1097
  };
1038
1098
 
1039
1099
  // src/typed-refs/base.ts
1040
- var TypedRef = class {
1041
- constructor(_params) {
1042
- 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;
1113
+ }
1114
+ /** Commit changes if autoCommit is enabled */
1115
+ commitIfAuto() {
1116
+ if (this.params.autoCommit) {
1117
+ this.params.getDoc().commit();
1118
+ }
1043
1119
  }
1044
- _cachedContainer;
1045
- get shape() {
1046
- return this._params.shape;
1120
+ /** Get the shape for this ref */
1121
+ getShape() {
1122
+ return this.params.shape;
1047
1123
  }
1048
- get placeholder() {
1049
- return this._params.placeholder;
1124
+ /** Get the placeholder value */
1125
+ getPlaceholder() {
1126
+ return this.params.placeholder;
1050
1127
  }
1051
- get readonly() {
1052
- return !!this._params.readonly;
1128
+ /** Check if autoCommit is enabled */
1129
+ getAutoCommit() {
1130
+ return !!this.params.autoCommit;
1053
1131
  }
1054
- get autoCommit() {
1055
- return !!this._params.autoCommit;
1132
+ /** Check if in batched mutation mode */
1133
+ getBatchedMutation() {
1134
+ return !!this.params.batchedMutation;
1056
1135
  }
1057
- get doc() {
1058
- return this._params.getDoc?.();
1136
+ /** Get the LoroDoc */
1137
+ getDoc() {
1138
+ return this.params.getDoc();
1059
1139
  }
1060
- /**
1061
- * Commits changes if autoCommit is enabled.
1062
- * Call this after any mutation operation.
1063
- */
1064
- commitIfAuto() {
1065
- if (this.autoCommit && this.doc) {
1066
- this.doc.commit();
1140
+ /** Get the loro namespace (cached) */
1141
+ getLoroNamespace() {
1142
+ if (!this.loroNamespace) {
1143
+ this.loroNamespace = this.createLoroNamespace();
1067
1144
  }
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
+ };
1068
1161
  }
1162
+ };
1163
+ var TypedRef = class {
1069
1164
  /**
1070
- * Throws an error if this ref is in readonly mode.
1071
- * Call this at the start of any mutating method.
1072
- * @deprecated Mutations are always allowed now; this will be removed.
1165
+ * Access the loro() namespace via the well-known symbol.
1166
+ * This is used by the loro() function to access CRDT internals.
1073
1167
  */
1074
- assertMutable() {
1075
- if (this.readonly) {
1076
- throw new Error("Cannot modify readonly ref");
1077
- }
1078
- }
1079
- get container() {
1080
- if (!this._cachedContainer) {
1081
- const container = this._params.getContainer();
1082
- this._cachedContainer = container;
1083
- return container;
1084
- }
1085
- return this._cachedContainer;
1168
+ get [LORO_SYMBOL]() {
1169
+ return this[INTERNAL_SYMBOL].getLoroNamespace();
1086
1170
  }
1087
1171
  };
1088
1172
 
@@ -1096,42 +1180,73 @@ import {
1096
1180
  LoroTree
1097
1181
  } from "loro-crdt";
1098
1182
 
1099
- // src/typed-refs/counter.ts
1100
- var CounterRef = class extends TypedRef {
1101
- // Track if we've materialized the container (made any changes)
1102
- _materialized = false;
1103
- get container() {
1104
- return super.container;
1105
- }
1106
- absorbPlainValues() {
1107
- }
1183
+ // src/typed-refs/counter-ref-internals.ts
1184
+ var CounterRefInternals = class extends BaseRefInternals {
1185
+ materialized = false;
1186
+ /** Increment the counter value */
1108
1187
  increment(value = 1) {
1109
- this.assertMutable();
1110
- this._materialized = true;
1111
- this.container.increment(value);
1188
+ this.materialized = true;
1189
+ this.getContainer().increment(value);
1112
1190
  this.commitIfAuto();
1113
1191
  }
1192
+ /** Decrement the counter value */
1114
1193
  decrement(value = 1) {
1115
- this.assertMutable();
1116
- this._materialized = true;
1117
- this.container.decrement(value);
1194
+ this.materialized = true;
1195
+ this.getContainer().decrement(value);
1118
1196
  this.commitIfAuto();
1119
1197
  }
1120
- /**
1121
- * Returns the counter value.
1122
- * If the counter hasn't been materialized (no operations performed),
1123
- * returns the placeholder value if available.
1124
- */
1125
- get value() {
1126
- const containerValue = this.container.value;
1127
- 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) {
1128
1203
  return containerValue;
1129
1204
  }
1130
- if (this.placeholder !== void 0) {
1131
- return this.placeholder;
1205
+ const placeholder = this.getPlaceholder();
1206
+ if (placeholder !== void 0) {
1207
+ return placeholder;
1132
1208
  }
1133
1209
  return containerValue;
1134
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
+ }
1135
1250
  valueOf() {
1136
1251
  return this.value;
1137
1252
  }
@@ -1280,75 +1395,41 @@ function convertInputToRef(value, shape) {
1280
1395
  }
1281
1396
  }
1282
1397
 
1283
- // src/typed-refs/list-base.ts
1284
- var ListRefBase = class extends TypedRef {
1285
- // Cache for items returned by array methods to track mutations
1398
+ // src/typed-refs/list-ref-base.ts
1399
+ var ListRefBaseInternals = class extends BaseRefInternals {
1286
1400
  itemCache = /* @__PURE__ */ new Map();
1287
- get container() {
1288
- return super.container;
1289
- }
1290
- get shape() {
1291
- return super.shape;
1292
- }
1293
- absorbPlainValues() {
1294
- for (const [index, cachedItem] of this.itemCache.entries()) {
1295
- if (cachedItem) {
1296
- if (isValueShape(this.shape.shape)) {
1297
- this.absorbValueAtIndex(index, cachedItem);
1298
- } else {
1299
- if (cachedItem && typeof cachedItem === "object" && "absorbPlainValues" in cachedItem) {
1300
- ;
1301
- cachedItem.absorbPlainValues();
1302
- }
1303
- }
1304
- }
1305
- }
1306
- this.itemCache.clear();
1307
- }
1308
- insertWithConversion(index, item) {
1309
- const convertedItem = convertInputToRef(item, this.shape.shape);
1310
- if (isContainer(convertedItem)) {
1311
- this.container.insertContainer(index, convertedItem);
1312
- } else {
1313
- this.container.insert(index, convertedItem);
1314
- }
1315
- }
1316
- pushWithConversion(item) {
1317
- const convertedItem = convertInputToRef(item, this.shape.shape);
1318
- if (isContainer(convertedItem)) {
1319
- this.container.pushContainer(convertedItem);
1320
- } else {
1321
- this.container.push(convertedItem);
1322
- }
1323
- }
1401
+ /** Get typed ref params for creating child refs at an index */
1324
1402
  getTypedRefParams(index, shape) {
1325
1403
  return {
1326
1404
  shape,
1327
1405
  placeholder: void 0,
1328
1406
  // List items don't have placeholder
1329
1407
  getContainer: () => {
1330
- const containerItem = this.container.get(index);
1408
+ const container = this.getContainer();
1409
+ const containerItem = container.get(index);
1331
1410
  if (!containerItem || !isContainer(containerItem)) {
1332
1411
  throw new Error(`No container found at index ${index}`);
1333
1412
  }
1334
1413
  return containerItem;
1335
1414
  },
1336
- readonly: this.readonly,
1337
- autoCommit: this._params.autoCommit,
1338
- getDoc: this._params.getDoc
1415
+ autoCommit: this.getAutoCommit(),
1416
+ batchedMutation: this.getBatchedMutation(),
1417
+ getDoc: () => this.getDoc()
1339
1418
  };
1340
1419
  }
1341
- // Get item for predicate functions - always returns plain Item for filtering logic
1420
+ /** Get item for predicate functions (returns plain value) */
1342
1421
  getPredicateItem(index) {
1422
+ const shape = this.getShape();
1423
+ const container = this.getContainer();
1343
1424
  const cachedItem = this.itemCache.get(index);
1344
- if (cachedItem && isValueShape(this.shape.shape)) {
1425
+ if (cachedItem && isValueShape(shape.shape)) {
1345
1426
  return cachedItem;
1346
1427
  }
1347
- const containerItem = this.container.get(index);
1428
+ const containerItem = container.get(index);
1348
1429
  if (containerItem === void 0) {
1349
1430
  return void 0;
1350
1431
  }
1351
- if (isValueShape(this.shape.shape)) {
1432
+ if (isValueShape(shape.shape)) {
1352
1433
  return containerItem;
1353
1434
  } else {
1354
1435
  if (isContainer(containerItem)) {
@@ -1363,54 +1444,154 @@ var ListRefBase = class extends TypedRef {
1363
1444
  return containerItem;
1364
1445
  }
1365
1446
  }
1366
- // Get item for return values - returns MutableItem that can be mutated
1447
+ /** Get mutable item for return values (returns ref or cached value) */
1367
1448
  getMutableItem(index) {
1368
- let cachedItem = this.itemCache.get(index);
1369
- if (cachedItem) {
1370
- return cachedItem;
1371
- }
1372
- const containerItem = this.container.get(index);
1449
+ const shape = this.getShape();
1450
+ const container = this.getContainer();
1451
+ const containerItem = container.get(index);
1373
1452
  if (containerItem === void 0) {
1374
1453
  return void 0;
1375
1454
  }
1376
- if (isValueShape(this.shape.shape)) {
1455
+ if (isValueShape(shape.shape)) {
1456
+ if (!this.getBatchedMutation()) {
1457
+ return containerItem;
1458
+ }
1459
+ let cachedItem2 = this.itemCache.get(index);
1460
+ if (cachedItem2) {
1461
+ return cachedItem2;
1462
+ }
1377
1463
  if (typeof containerItem === "object" && containerItem !== null) {
1378
- cachedItem = JSON.parse(JSON.stringify(containerItem));
1464
+ cachedItem2 = JSON.parse(JSON.stringify(containerItem));
1379
1465
  } else {
1380
- cachedItem = containerItem;
1381
- }
1382
- if (!this.readonly) {
1383
- this.itemCache.set(index, cachedItem);
1466
+ cachedItem2 = containerItem;
1384
1467
  }
1385
- return cachedItem;
1386
- } else {
1468
+ this.itemCache.set(index, cachedItem2);
1469
+ return cachedItem2;
1470
+ }
1471
+ let cachedItem = this.itemCache.get(index);
1472
+ if (!cachedItem) {
1387
1473
  cachedItem = createContainerTypedRef(
1388
- this.getTypedRefParams(index, this.shape.shape)
1474
+ this.getTypedRefParams(index, shape.shape)
1389
1475
  );
1390
1476
  this.itemCache.set(index, cachedItem);
1391
- if (this.readonly) {
1392
- return unwrapReadonlyPrimitive(
1393
- cachedItem,
1394
- this.shape.shape
1395
- );
1477
+ }
1478
+ return cachedItem;
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
+ }
1396
1543
  }
1397
- return cachedItem;
1398
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);
1399
1580
  }
1400
1581
  // Array-like methods for better developer experience
1401
1582
  // DUAL INTERFACE: Predicates get Item (plain data), return values are MutableItem (mutable)
1402
1583
  find(predicate) {
1403
1584
  for (let i = 0; i < this.length; i++) {
1404
- const predicateItem = this.getPredicateItem(i);
1585
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1405
1586
  if (predicate(predicateItem, i)) {
1406
- return this.getMutableItem(i);
1587
+ return this[INTERNAL_SYMBOL].getMutableItem(i);
1407
1588
  }
1408
1589
  }
1409
1590
  return void 0;
1410
1591
  }
1411
1592
  findIndex(predicate) {
1412
1593
  for (let i = 0; i < this.length; i++) {
1413
- const predicateItem = this.getPredicateItem(i);
1594
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1414
1595
  if (predicate(predicateItem, i)) {
1415
1596
  return i;
1416
1597
  }
@@ -1420,7 +1601,7 @@ var ListRefBase = class extends TypedRef {
1420
1601
  map(callback) {
1421
1602
  const result = [];
1422
1603
  for (let i = 0; i < this.length; i++) {
1423
- const predicateItem = this.getPredicateItem(i);
1604
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1424
1605
  result.push(callback(predicateItem, i));
1425
1606
  }
1426
1607
  return result;
@@ -1428,22 +1609,22 @@ var ListRefBase = class extends TypedRef {
1428
1609
  filter(predicate) {
1429
1610
  const result = [];
1430
1611
  for (let i = 0; i < this.length; i++) {
1431
- const predicateItem = this.getPredicateItem(i);
1612
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1432
1613
  if (predicate(predicateItem, i)) {
1433
- result.push(this.getMutableItem(i));
1614
+ result.push(this[INTERNAL_SYMBOL].getMutableItem(i));
1434
1615
  }
1435
1616
  }
1436
1617
  return result;
1437
1618
  }
1438
1619
  forEach(callback) {
1439
1620
  for (let i = 0; i < this.length; i++) {
1440
- const predicateItem = this.getPredicateItem(i);
1621
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1441
1622
  callback(predicateItem, i);
1442
1623
  }
1443
1624
  }
1444
1625
  some(predicate) {
1445
1626
  for (let i = 0; i < this.length; i++) {
1446
- const predicateItem = this.getPredicateItem(i);
1627
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1447
1628
  if (predicate(predicateItem, i)) {
1448
1629
  return true;
1449
1630
  }
@@ -1452,7 +1633,7 @@ var ListRefBase = class extends TypedRef {
1452
1633
  }
1453
1634
  every(predicate) {
1454
1635
  for (let i = 0; i < this.length; i++) {
1455
- const predicateItem = this.getPredicateItem(i);
1636
+ const predicateItem = this[INTERNAL_SYMBOL].getPredicateItem(i);
1456
1637
  if (!predicate(predicateItem, i)) {
1457
1638
  return false;
1458
1639
  }
@@ -1465,55 +1646,55 @@ var ListRefBase = class extends TypedRef {
1465
1646
  const endIndex = end === void 0 ? len : end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
1466
1647
  const result = [];
1467
1648
  for (let i = startIndex; i < endIndex; i++) {
1468
- result.push(this.getMutableItem(i));
1649
+ result.push(this[INTERNAL_SYMBOL].getMutableItem(i));
1469
1650
  }
1470
1651
  return result;
1471
1652
  }
1472
1653
  insert(index, item) {
1473
- this.assertMutable();
1474
- this.updateCacheForInsert(index);
1475
- this.insertWithConversion(index, item);
1476
- this.commitIfAuto();
1654
+ this[INTERNAL_SYMBOL].updateCacheForInsert(index);
1655
+ this[INTERNAL_SYMBOL].insertWithConversion(index, item);
1656
+ this[INTERNAL_SYMBOL].commitIfAuto();
1477
1657
  }
1478
1658
  delete(index, len) {
1479
- this.assertMutable();
1480
- this.updateCacheForDelete(index, len);
1481
- this.container.delete(index, len);
1482
- 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();
1483
1663
  }
1484
1664
  push(item) {
1485
- this.assertMutable();
1486
- this.pushWithConversion(item);
1487
- this.commitIfAuto();
1665
+ this[INTERNAL_SYMBOL].pushWithConversion(item);
1666
+ this[INTERNAL_SYMBOL].commitIfAuto();
1488
1667
  }
1489
1668
  pushContainer(container) {
1490
- this.assertMutable();
1491
- const result = this.container.pushContainer(container);
1492
- this.commitIfAuto();
1669
+ const loroContainer = this[INTERNAL_SYMBOL].getContainer();
1670
+ const result = loroContainer.pushContainer(container);
1671
+ this[INTERNAL_SYMBOL].commitIfAuto();
1493
1672
  return result;
1494
1673
  }
1495
1674
  insertContainer(index, container) {
1496
- this.assertMutable();
1497
- const result = this.container.insertContainer(index, container);
1498
- this.commitIfAuto();
1675
+ const loroContainer = this[INTERNAL_SYMBOL].getContainer();
1676
+ const result = loroContainer.insertContainer(index, container);
1677
+ this[INTERNAL_SYMBOL].commitIfAuto();
1499
1678
  return result;
1500
1679
  }
1501
1680
  get(index) {
1502
- return this.getMutableItem(index);
1681
+ return this[INTERNAL_SYMBOL].getMutableItem(index);
1503
1682
  }
1504
1683
  toArray() {
1505
1684
  const result = [];
1506
1685
  for (let i = 0; i < this.length; i++) {
1507
- result.push(this.getPredicateItem(i));
1686
+ result.push(this[INTERNAL_SYMBOL].getPredicateItem(i));
1508
1687
  }
1509
1688
  return result;
1510
1689
  }
1511
1690
  toJSON() {
1512
- const nativeJson = this.container.toJSON();
1513
- if (isContainerShape(this.shape.shape) || isValueShape(this.shape.shape) && this.shape.shape.valueType === "struct") {
1514
- 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);
1515
1696
  return nativeJson.map(
1516
- (item) => mergeValue(this.shape.shape, item, itemPlaceholder)
1697
+ (item) => mergeValue(shape.shape, item, itemPlaceholder)
1517
1698
  );
1518
1699
  }
1519
1700
  return nativeJson ?? [];
@@ -1523,7 +1704,10 @@ var ListRefBase = class extends TypedRef {
1523
1704
  return {
1524
1705
  next: () => {
1525
1706
  if (index < this.length) {
1526
- return { value: this.getMutableItem(index++), done: false };
1707
+ return {
1708
+ value: this[INTERNAL_SYMBOL].getMutableItem(index++),
1709
+ done: false
1710
+ };
1527
1711
  }
1528
1712
  return { value: void 0, done: true };
1529
1713
  },
@@ -1533,63 +1717,59 @@ var ListRefBase = class extends TypedRef {
1533
1717
  };
1534
1718
  }
1535
1719
  get length() {
1536
- return this.container.length;
1537
- }
1538
- // Update cache indices when items are deleted
1539
- updateCacheForDelete(deleteIndex, deleteLen) {
1540
- const newCache = /* @__PURE__ */ new Map();
1541
- for (const [cachedIndex, cachedItem] of this.itemCache.entries()) {
1542
- if (cachedIndex < deleteIndex) {
1543
- newCache.set(cachedIndex, cachedItem);
1544
- } else if (cachedIndex >= deleteIndex + deleteLen) {
1545
- newCache.set(cachedIndex - deleteLen, cachedItem);
1546
- }
1547
- }
1548
- this.itemCache = newCache;
1549
- }
1550
- // Update cache indices when items are inserted
1551
- updateCacheForInsert(insertIndex) {
1552
- const newCache = /* @__PURE__ */ new Map();
1553
- for (const [cachedIndex, cachedItem] of this.itemCache.entries()) {
1554
- if (cachedIndex < insertIndex) {
1555
- newCache.set(cachedIndex, cachedItem);
1556
- } else {
1557
- newCache.set(cachedIndex + 1, cachedItem);
1558
- }
1559
- }
1560
- this.itemCache = newCache;
1720
+ const container = this[INTERNAL_SYMBOL].getContainer();
1721
+ return container.length;
1561
1722
  }
1562
1723
  };
1563
1724
 
1564
- // src/typed-refs/list.ts
1565
- var ListRef = class extends ListRefBase {
1566
- get container() {
1567
- return super.container;
1568
- }
1725
+ // src/typed-refs/list-ref-internals.ts
1726
+ var ListRefInternals = class extends ListRefBaseInternals {
1727
+ /** Absorb value at specific index for LoroList */
1569
1728
  absorbValueAtIndex(index, value) {
1570
- this.container.delete(index, 1);
1571
- this.container.insert(index, value);
1729
+ const container = this.getContainer();
1730
+ container.delete(index, 1);
1731
+ container.insert(index, value);
1572
1732
  }
1573
1733
  };
1574
1734
 
1575
- // src/typed-refs/movable-list.ts
1576
- var MovableListRef = class extends ListRefBase {
1577
- get container() {
1578
- return super.container;
1735
+ // src/typed-refs/list-ref.ts
1736
+ var ListRef = class extends ListRefBase {
1737
+ createInternals(params) {
1738
+ return new ListRefInternals(params);
1579
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 */
1580
1745
  absorbValueAtIndex(index, value) {
1581
- this.container.set(index, value);
1746
+ const container = this.getContainer();
1747
+ container.set(index, value);
1582
1748
  }
1749
+ /** Move an item from one index to another */
1583
1750
  move(from, to) {
1584
- this.assertMutable();
1585
- this.container.move(from, to);
1751
+ const container = this.getContainer();
1752
+ container.move(from, to);
1586
1753
  this.commitIfAuto();
1587
1754
  }
1755
+ /** Set an item at a specific index */
1588
1756
  set(index, item) {
1589
- this.assertMutable();
1590
- const result = this.container.set(index, item);
1757
+ const container = this.getContainer();
1758
+ container.set(index, item);
1591
1759
  this.commitIfAuto();
1592
- 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);
1593
1773
  }
1594
1774
  };
1595
1775
 
@@ -1597,7 +1777,7 @@ var MovableListRef = class extends ListRefBase {
1597
1777
  var recordProxyHandler = {
1598
1778
  get: (target, prop) => {
1599
1779
  if (typeof prop === "string" && !(prop in target)) {
1600
- return target.getRef(prop);
1780
+ return target[INTERNAL_SYMBOL].getRef(prop);
1601
1781
  }
1602
1782
  return Reflect.get(target, prop);
1603
1783
  },
@@ -1683,20 +1863,12 @@ var movableListProxyHandler = {
1683
1863
  }
1684
1864
  };
1685
1865
 
1686
- // src/typed-refs/record.ts
1687
- var RecordRef = class extends TypedRef {
1866
+ // src/typed-refs/record-ref-internals.ts
1867
+ var RecordRefInternals = class extends BaseRefInternals {
1688
1868
  refCache = /* @__PURE__ */ new Map();
1689
- get shape() {
1690
- return super.shape;
1691
- }
1692
- get container() {
1693
- return super.container;
1694
- }
1695
- absorbPlainValues() {
1696
- absorbCachedPlainValues(this.refCache, () => this.container);
1697
- }
1869
+ /** Get typed ref params for creating child refs at a key */
1698
1870
  getTypedRefParams(key, shape) {
1699
- let placeholder = this.placeholder?.[key];
1871
+ let placeholder = this.getPlaceholder()?.[key];
1700
1872
  if (placeholder === void 0) {
1701
1873
  placeholder = deriveShapePlaceholder(shape);
1702
1874
  }
@@ -1706,74 +1878,83 @@ var RecordRef = class extends TypedRef {
1706
1878
  );
1707
1879
  }
1708
1880
  const LoroContainer = containerConstructor[shape._type];
1881
+ const container = this.getContainer();
1709
1882
  return {
1710
1883
  shape,
1711
1884
  placeholder,
1712
- getContainer: () => this.container.getOrCreateContainer(key, new LoroContainer()),
1713
- readonly: this.readonly,
1714
- autoCommit: this._params.autoCommit,
1715
- getDoc: this._params.getDoc
1885
+ getContainer: () => container.getOrCreateContainer(key, new LoroContainer()),
1886
+ autoCommit: this.getAutoCommit(),
1887
+ batchedMutation: this.getBatchedMutation(),
1888
+ getDoc: () => this.getDoc()
1716
1889
  };
1717
1890
  }
1718
- /**
1719
- * Gets an existing ref for a key, or returns undefined if the key doesn't exist.
1720
- * Used for reading operations where we want optional chaining to work.
1721
- */
1891
+ /** Get a ref for a key without creating (returns undefined for non-existent container keys) */
1722
1892
  getRef(key) {
1723
- if (isContainerShape(this.shape.shape)) {
1724
- 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);
1725
1898
  if (existing === void 0) {
1726
1899
  return void 0;
1727
1900
  }
1728
1901
  }
1729
1902
  return this.getOrCreateRef(key);
1730
1903
  }
1731
- /**
1732
- * Gets or creates a ref for a key.
1733
- * Always creates the container if it doesn't exist.
1734
- * This is the method used for write operations.
1735
- */
1904
+ /** Get or create a ref for a key (always creates for container shapes) */
1736
1905
  getOrCreateRef(key) {
1737
- let ref = this.refCache.get(key);
1738
- if (!ref) {
1739
- const shape = this.shape.shape;
1740
- if (isContainerShape(shape)) {
1741
- ref = createContainerTypedRef(
1742
- this.getTypedRefParams(key, shape)
1743
- );
1744
- this.refCache.set(key, ref);
1745
- } else {
1746
- const containerValue = this.container.get(key);
1906
+ const recordShape = this.getShape();
1907
+ const shape = recordShape.shape;
1908
+ const container = this.getContainer();
1909
+ if (isValueShape(shape)) {
1910
+ if (!this.getBatchedMutation()) {
1911
+ const containerValue = container.get(key);
1912
+ if (containerValue !== void 0) {
1913
+ return containerValue;
1914
+ }
1915
+ const placeholder = this.getPlaceholder()?.[key];
1916
+ if (placeholder !== void 0) {
1917
+ return placeholder;
1918
+ }
1919
+ return shape._plain;
1920
+ }
1921
+ let ref2 = this.refCache.get(key);
1922
+ if (!ref2) {
1923
+ const containerValue = container.get(key);
1747
1924
  if (containerValue !== void 0) {
1748
- ref = containerValue;
1925
+ if (typeof containerValue === "object" && containerValue !== null) {
1926
+ ref2 = JSON.parse(JSON.stringify(containerValue));
1927
+ } else {
1928
+ ref2 = containerValue;
1929
+ }
1749
1930
  } else {
1750
- const placeholder = this.placeholder?.[key];
1751
- if (placeholder === void 0) {
1752
- ref = shape._plain;
1931
+ const placeholder = this.getPlaceholder()?.[key];
1932
+ if (placeholder !== void 0) {
1933
+ ref2 = placeholder;
1753
1934
  } else {
1754
- ref = placeholder;
1935
+ ref2 = shape._plain;
1755
1936
  }
1756
1937
  }
1757
- if (ref !== void 0 && !this.readonly) {
1758
- this.refCache.set(key, ref);
1759
- }
1938
+ this.refCache.set(key, ref2);
1760
1939
  }
1940
+ return ref2;
1761
1941
  }
1762
- if (this.readonly && isContainerShape(this.shape.shape)) {
1763
- return unwrapReadonlyPrimitive(
1764
- ref,
1765
- this.shape.shape
1942
+ let ref = this.refCache.get(key);
1943
+ if (!ref) {
1944
+ ref = createContainerTypedRef(
1945
+ this.getTypedRefParams(key, shape)
1766
1946
  );
1947
+ this.refCache.set(key, ref);
1767
1948
  }
1768
1949
  return ref;
1769
1950
  }
1770
- get(key) {
1771
- return this.getRef(key);
1772
- }
1951
+ /** Set a value at a key */
1773
1952
  set(key, value) {
1774
- this.assertMutable();
1775
- if (isValueShape(this.shape.shape)) {
1776
- 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);
1777
1958
  this.refCache.set(key, value);
1778
1959
  this.commitIfAuto();
1779
1960
  } else {
@@ -1787,222 +1968,460 @@ var RecordRef = class extends TypedRef {
1787
1968
  );
1788
1969
  }
1789
1970
  }
1790
- setContainer(key, container) {
1791
- this.assertMutable();
1792
- const result = this.container.setContainer(key, container);
1793
- this.commitIfAuto();
1794
- return result;
1795
- }
1971
+ /** Delete a key */
1796
1972
  delete(key) {
1797
- this.assertMutable();
1798
- this.container.delete(key);
1973
+ const container = this.getContainer();
1974
+ container.delete(key);
1799
1975
  this.refCache.delete(key);
1800
1976
  this.commitIfAuto();
1801
1977
  }
1802
- has(key) {
1803
- return this.container.get(key) !== void 0;
1804
- }
1805
- keys() {
1806
- return this.container.keys();
1978
+ /** Absorb mutated plain values back into Loro containers */
1979
+ absorbPlainValues() {
1980
+ absorbCachedPlainValues(this.refCache, () => this.getContainer());
1807
1981
  }
1808
- values() {
1809
- return this.container.values();
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
+ }
2034
+ has(key) {
2035
+ const container = this[INTERNAL_SYMBOL].getContainer();
2036
+ return container.get(key) !== void 0;
2037
+ }
2038
+ keys() {
2039
+ const container = this[INTERNAL_SYMBOL].getContainer();
2040
+ return container.keys();
2041
+ }
2042
+ values() {
2043
+ const container = this[INTERNAL_SYMBOL].getContainer();
2044
+ return container.values();
1810
2045
  }
1811
2046
  get size() {
1812
- return this.container.size;
2047
+ const container = this[INTERNAL_SYMBOL].getContainer();
2048
+ return container.size;
1813
2049
  }
1814
2050
  toJSON() {
1815
- if (this.readonly) {
1816
- const nativeJson = this.container.toJSON();
1817
- const result = {};
1818
- for (const key of Object.keys(nativeJson)) {
1819
- const nestedPlaceholderValue = deriveShapePlaceholder(this.shape.shape);
1820
- result[key] = mergeValue(
1821
- this.shape.shape,
1822
- nativeJson[key],
1823
- nestedPlaceholderValue
1824
- );
1825
- }
1826
- return result;
1827
- }
1828
2051
  return serializeRefToJSON(this, this.keys());
1829
2052
  }
1830
2053
  };
1831
2054
 
1832
- // src/typed-refs/struct.ts
1833
- var StructRef = class extends TypedRef {
2055
+ // src/typed-refs/struct-ref-internals.ts
2056
+ var StructRefInternals = class extends BaseRefInternals {
1834
2057
  propertyCache = /* @__PURE__ */ new Map();
1835
- constructor(params) {
1836
- super(params);
1837
- this.createLazyProperties();
1838
- }
1839
- get shape() {
1840
- return super.shape;
1841
- }
1842
- get container() {
1843
- return super.container;
1844
- }
1845
- absorbPlainValues() {
1846
- absorbCachedPlainValues(this.propertyCache, () => this.container);
1847
- }
2058
+ /** Get typed ref params for creating child refs at a key */
1848
2059
  getTypedRefParams(key, shape) {
1849
- const placeholder = this.placeholder?.[key];
2060
+ const placeholder = this.getPlaceholder()?.[key];
1850
2061
  if (!hasContainerConstructor(shape._type)) {
1851
2062
  throw new Error(
1852
2063
  `Cannot create typed ref for shape type "${shape._type}". Use Shape.any() only at the document root level.`
1853
2064
  );
1854
2065
  }
1855
2066
  const LoroContainer = containerConstructor[shape._type];
2067
+ const container = this.getContainer();
1856
2068
  return {
1857
2069
  shape,
1858
2070
  placeholder,
1859
- getContainer: () => this.container.getOrCreateContainer(key, new LoroContainer()),
1860
- readonly: this.readonly,
1861
- autoCommit: this._params.autoCommit,
1862
- getDoc: this._params.getDoc
2071
+ getContainer: () => container.getOrCreateContainer(key, new LoroContainer()),
2072
+ autoCommit: this.getAutoCommit(),
2073
+ batchedMutation: this.getBatchedMutation(),
2074
+ getDoc: () => this.getDoc()
1863
2075
  };
1864
2076
  }
2077
+ /** Get or create a ref for a key */
1865
2078
  getOrCreateRef(key, shape) {
1866
- let ref = this.propertyCache.get(key);
1867
- if (!ref) {
1868
- if (isContainerShape(shape)) {
1869
- ref = createContainerTypedRef(this.getTypedRefParams(key, shape));
1870
- this.propertyCache.set(key, ref);
1871
- } else {
1872
- 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);
2085
+ if (containerValue !== void 0) {
2086
+ return containerValue;
2087
+ }
2088
+ const placeholder = this.getPlaceholder()?.[key];
2089
+ if (placeholder === void 0) {
2090
+ throw new Error("placeholder required");
2091
+ }
2092
+ return placeholder;
2093
+ }
2094
+ let ref2 = this.propertyCache.get(key);
2095
+ if (!ref2) {
2096
+ const containerValue = container.get(key);
1873
2097
  if (containerValue !== void 0) {
1874
- ref = containerValue;
2098
+ if (typeof containerValue === "object" && containerValue !== null) {
2099
+ ref2 = JSON.parse(JSON.stringify(containerValue));
2100
+ } else {
2101
+ ref2 = containerValue;
2102
+ }
1875
2103
  } else {
1876
- const placeholder = this.placeholder?.[key];
2104
+ const placeholder = this.getPlaceholder()?.[key];
1877
2105
  if (placeholder === void 0) {
1878
2106
  throw new Error("placeholder required");
1879
2107
  }
1880
- ref = placeholder;
1881
- }
1882
- if (!this.readonly) {
1883
- this.propertyCache.set(key, ref);
2108
+ ref2 = placeholder;
1884
2109
  }
2110
+ this.propertyCache.set(key, ref2);
1885
2111
  }
1886
- if (ref === void 0) throw new Error("no container made");
2112
+ return ref2;
1887
2113
  }
1888
- if (this.readonly && isContainerShape(shape)) {
1889
- const existing = this.container.get(key);
1890
- if (existing === void 0) {
1891
- return this.placeholder?.[key];
1892
- }
1893
- return unwrapReadonlyPrimitive(ref, shape);
2114
+ let ref = this.propertyCache.get(key);
2115
+ if (!ref) {
2116
+ ref = createContainerTypedRef(
2117
+ this.getTypedRefParams(key, actualShape)
2118
+ );
2119
+ this.propertyCache.set(key, ref);
1894
2120
  }
1895
2121
  return ref;
1896
2122
  }
1897
- createLazyProperties() {
1898
- for (const key in this.shape.shapes) {
1899
- const shape = this.shape.shapes[key];
1900
- Object.defineProperty(this, key, {
1901
- get: () => this.getOrCreateRef(key, shape),
1902
- set: (value) => {
1903
- this.assertMutable();
1904
- if (isValueShape(shape)) {
1905
- this.container.set(key, value);
1906
- this.propertyCache.set(key, value);
1907
- } else {
1908
- const ref = this.getOrCreateRef(key, shape);
1909
- if (assignPlainValueToTypedRef(ref, value)) {
1910
- return;
1911
- }
1912
- throw new Error(
1913
- "Cannot set container directly, modify the typed ref instead"
1914
- );
1915
- }
1916
- },
1917
- enumerable: true
1918
- });
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
+ );
1919
2144
  }
1920
2145
  }
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()
2158
+ );
2159
+ }
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
+ }
1921
2195
  toJSON() {
1922
- if (this.readonly) {
1923
- const nativeJson = this.container.toJSON();
1924
- return mergeValue(
1925
- this.shape,
1926
- nativeJson,
1927
- this.placeholder
1928
- );
1929
- }
1930
2196
  return serializeRefToJSON(
1931
2197
  this,
1932
- Object.keys(this.shape.shapes)
2198
+ Object.keys(this.structShape.shapes)
1933
2199
  );
1934
2200
  }
1935
- // TODO(duane): return correct type here
2201
+ // Deprecated methods - kept for backward compatibility
2202
+ // @deprecated Use property access instead: obj.key
1936
2203
  get(key) {
1937
- return this.container.get(key);
2204
+ const container = this[INTERNAL_SYMBOL].getContainer();
2205
+ return container.get(key);
1938
2206
  }
2207
+ // @deprecated Use property assignment instead: obj.key = value
1939
2208
  set(key, value) {
1940
- this.assertMutable();
1941
- this.container.set(key, value);
1942
- this.commitIfAuto();
2209
+ const container = this[INTERNAL_SYMBOL].getContainer();
2210
+ container.set(key, value);
2211
+ this[INTERNAL_SYMBOL].commitIfAuto();
1943
2212
  }
2213
+ // @deprecated Use loro(struct).setContainer() instead
1944
2214
  setContainer(key, container) {
1945
- this.assertMutable();
1946
- const result = this.container.setContainer(key, container);
1947
- this.commitIfAuto();
2215
+ const loroContainer = this[INTERNAL_SYMBOL].getContainer();
2216
+ const result = loroContainer.setContainer(key, container);
2217
+ this[INTERNAL_SYMBOL].commitIfAuto();
1948
2218
  return result;
1949
2219
  }
2220
+ // @deprecated Use delete obj.key instead
1950
2221
  delete(key) {
1951
- this.assertMutable();
1952
- this.container.delete(key);
1953
- this.commitIfAuto();
2222
+ const container = this[INTERNAL_SYMBOL].getContainer();
2223
+ container.delete(key);
2224
+ this[INTERNAL_SYMBOL].commitIfAuto();
1954
2225
  }
2226
+ // @deprecated Use 'key' in obj instead
1955
2227
  has(key) {
1956
- return this.container.get(key) !== void 0;
2228
+ const container = this[INTERNAL_SYMBOL].getContainer();
2229
+ return container.get(key) !== void 0;
1957
2230
  }
2231
+ // @deprecated Use Object.keys(obj) instead
1958
2232
  keys() {
1959
- return this.container.keys();
2233
+ const container = this[INTERNAL_SYMBOL].getContainer();
2234
+ return container.keys();
1960
2235
  }
2236
+ // @deprecated Use Object.values(obj) instead
1961
2237
  values() {
1962
- return this.container.values();
2238
+ const container = this[INTERNAL_SYMBOL].getContainer();
2239
+ return container.values();
1963
2240
  }
2241
+ // @deprecated Not standard for objects
1964
2242
  get size() {
1965
- return this.container.size;
2243
+ const container = this[INTERNAL_SYMBOL].getContainer();
2244
+ return container.size;
1966
2245
  }
1967
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
+ }
1968
2309
 
1969
- // src/typed-refs/text.ts
1970
- var TextRef = class extends TypedRef {
1971
- // Track if we've materialized the container (made any changes)
1972
- _materialized = false;
1973
- get container() {
1974
- return super.container;
1975
- }
1976
- absorbPlainValues() {
1977
- }
1978
- // 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 */
1979
2314
  insert(index, content) {
1980
- this.assertMutable();
1981
- this._materialized = true;
1982
- this.container.insert(index, content);
2315
+ this.materialized = true;
2316
+ this.getContainer().insert(index, content);
1983
2317
  this.commitIfAuto();
1984
2318
  }
2319
+ /** Delete text at the given index */
1985
2320
  delete(index, len) {
1986
- this.assertMutable();
1987
- this._materialized = true;
1988
- this.container.delete(index, len);
2321
+ this.materialized = true;
2322
+ this.getContainer().delete(index, len);
1989
2323
  this.commitIfAuto();
1990
2324
  }
1991
- /**
1992
- * Returns the text content.
1993
- * If the text hasn't been materialized (no operations performed),
1994
- * returns the placeholder value if available.
1995
- */
1996
- toString() {
1997
- const containerValue = this.container.toString();
1998
- 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) {
1999
2354
  return containerValue;
2000
2355
  }
2001
- if (this.placeholder !== void 0) {
2002
- return this.placeholder;
2356
+ const placeholder = this.getPlaceholder();
2357
+ if (placeholder !== void 0) {
2358
+ return placeholder;
2003
2359
  }
2004
2360
  return containerValue;
2005
2361
  }
2362
+ /** Get the text as a delta */
2363
+ toDelta() {
2364
+ return this.getContainer().toDelta();
2365
+ }
2366
+ /** Get the length of the text */
2367
+ getLength() {
2368
+ return this.getContainer().length;
2369
+ }
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);
2404
+ }
2405
+ /** Update the entire text content */
2406
+ update(text) {
2407
+ this[INTERNAL_SYMBOL].update(text);
2408
+ }
2409
+ /** Mark a range of text with a key-value pair */
2410
+ mark(range, key, value) {
2411
+ this[INTERNAL_SYMBOL].mark(range, key, value);
2412
+ }
2413
+ /** Remove a mark from a range of text */
2414
+ unmark(range, key) {
2415
+ this[INTERNAL_SYMBOL].unmark(range, key);
2416
+ }
2417
+ /** Apply a delta to the text */
2418
+ applyDelta(delta) {
2419
+ this[INTERNAL_SYMBOL].applyDelta(delta);
2420
+ }
2421
+ /** Get the text as a string */
2422
+ toString() {
2423
+ return this[INTERNAL_SYMBOL].getStringValue();
2424
+ }
2006
2425
  valueOf() {
2007
2426
  return this.toString();
2008
2427
  }
@@ -2012,62 +2431,418 @@ var TextRef = class extends TypedRef {
2012
2431
  [Symbol.toPrimitive](_hint) {
2013
2432
  return this.toString();
2014
2433
  }
2015
- update(text) {
2016
- this.assertMutable();
2017
- this._materialized = true;
2018
- this.container.update(text);
2019
- this.commitIfAuto();
2434
+ /** Get the text as a delta */
2435
+ toDelta() {
2436
+ return this[INTERNAL_SYMBOL].toDelta();
2020
2437
  }
2021
- mark(range, key, value) {
2022
- this.assertMutable();
2023
- this._materialized = true;
2024
- this.container.mark(range, key, value);
2025
- this.commitIfAuto();
2438
+ /** Get the length of the text */
2439
+ get length() {
2440
+ return this[INTERNAL_SYMBOL].getLength();
2026
2441
  }
2027
- unmark(range, key) {
2028
- this.assertMutable();
2029
- this._materialized = true;
2030
- this.container.unmark(range, key);
2031
- this.commitIfAuto();
2442
+ };
2443
+
2444
+ // src/typed-refs/tree-node-ref-internals.ts
2445
+ var TreeNodeRefInternals = class {
2446
+ constructor(params) {
2447
+ this.params = params;
2032
2448
  }
2033
- toDelta() {
2034
- return this.container.toDelta();
2449
+ dataRef;
2450
+ /** Get the underlying LoroTreeNode */
2451
+ getNode() {
2452
+ return this.params.node;
2035
2453
  }
2036
- applyDelta(delta) {
2037
- this.assertMutable();
2038
- this._materialized = true;
2039
- this.container.applyDelta(delta);
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;
2486
+ if (!dataContainer) {
2487
+ throw new Error(`Node ${node.id} has no data container`);
2488
+ }
2489
+ const placeholder = deriveShapePlaceholder(dataShape);
2490
+ const refParams = {
2491
+ shape: {
2492
+ _type: "struct",
2493
+ shapes: dataShape.shapes,
2494
+ _plain: {},
2495
+ _mutable: {},
2496
+ _placeholder: {}
2497
+ },
2498
+ placeholder,
2499
+ getContainer: () => dataContainer,
2500
+ autoCommit: this.getAutoCommit(),
2501
+ batchedMutation: this.getBatchedMutation(),
2502
+ getDoc: this.params.getDoc
2503
+ };
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();
2512
+ }
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();
2534
+ }
2535
+ /**
2536
+ * Create a child node under this node.
2537
+ *
2538
+ * @param initialData - Optional partial data to initialize the child with
2539
+ * @param index - Optional position among siblings
2540
+ * @returns The created child TreeNodeRef
2541
+ */
2542
+ createNode(initialData, index) {
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);
2547
+ if (initialData) {
2548
+ for (const [key, value] of Object.entries(initialData)) {
2549
+ ;
2550
+ nodeRef.data[key] = value;
2551
+ }
2552
+ }
2553
+ this[INTERNAL_SYMBOL].commitIfAuto();
2554
+ return nodeRef;
2555
+ }
2556
+ /**
2557
+ * Get the parent node, if any.
2558
+ */
2559
+ parent() {
2560
+ const node = this[INTERNAL_SYMBOL].getNode();
2561
+ const treeRef = this[INTERNAL_SYMBOL].getTreeRef();
2562
+ const parentNode = node.parent?.();
2563
+ if (!parentNode) return void 0;
2564
+ return treeRef.getOrCreateNodeRef(parentNode);
2565
+ }
2566
+ /**
2567
+ * Get all child nodes in order.
2568
+ */
2569
+ children() {
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));
2574
+ }
2575
+ /**
2576
+ * Move this node to a new parent.
2577
+ *
2578
+ * @param newParent - The new parent node (undefined for root)
2579
+ * @param index - Optional position among siblings
2580
+ */
2581
+ move(newParent, index) {
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();
2586
+ }
2587
+ /**
2588
+ * Move this node to be after the given sibling.
2589
+ */
2590
+ moveAfter(sibling) {
2591
+ const node = this[INTERNAL_SYMBOL].getNode();
2592
+ const siblingNode = sibling[INTERNAL_SYMBOL].getNode();
2593
+ node.moveAfter(siblingNode);
2594
+ this[INTERNAL_SYMBOL].commitIfAuto();
2595
+ }
2596
+ /**
2597
+ * Move this node to be before the given sibling.
2598
+ */
2599
+ moveBefore(sibling) {
2600
+ const node = this[INTERNAL_SYMBOL].getNode();
2601
+ const siblingNode = sibling[INTERNAL_SYMBOL].getNode();
2602
+ node.moveBefore(siblingNode);
2603
+ this[INTERNAL_SYMBOL].commitIfAuto();
2604
+ }
2605
+ /**
2606
+ * Get the index of this node among its siblings.
2607
+ */
2608
+ index() {
2609
+ const node = this[INTERNAL_SYMBOL].getNode();
2610
+ return node.index();
2611
+ }
2612
+ /**
2613
+ * Get the fractional index string for ordering.
2614
+ */
2615
+ fractionalIndex() {
2616
+ const node = this[INTERNAL_SYMBOL].getNode();
2617
+ return node.fractionalIndex();
2618
+ }
2619
+ /**
2620
+ * Check if this node has been deleted.
2621
+ */
2622
+ isDeleted() {
2623
+ const node = this[INTERNAL_SYMBOL].getNode();
2624
+ return node.isDeleted();
2625
+ }
2626
+ /**
2627
+ * Serialize this node and its descendants to JSON.
2628
+ */
2629
+ toJSON() {
2630
+ const children = this.children();
2631
+ return {
2632
+ id: this.id,
2633
+ parent: this.parent()?.id ?? null,
2634
+ index: this.index() ?? 0,
2635
+ fractionalIndex: this.fractionalIndex() ?? "",
2636
+ data: this.data.toJSON(),
2637
+ children: children.map((child) => child.toJSON())
2638
+ };
2639
+ }
2640
+ };
2641
+
2642
+ // src/typed-refs/tree-ref-internals.ts
2643
+ var TreeRefInternals = class extends BaseRefInternals {
2644
+ nodeCache = /* @__PURE__ */ new Map();
2645
+ treeRef = null;
2646
+ /** Set the parent TreeRef (needed for creating node refs) */
2647
+ setTreeRef(treeRef) {
2648
+ this.treeRef = treeRef;
2649
+ }
2650
+ /** Get the data shape for tree nodes */
2651
+ getDataShape() {
2652
+ const shape = this.getShape();
2653
+ return shape.shape;
2654
+ }
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");
2660
+ }
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;
2674
+ }
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);
2040
2692
  this.commitIfAuto();
2041
2693
  }
2042
- get length() {
2043
- return this.container.length;
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
+ };
2044
2714
  }
2045
2715
  };
2046
2716
 
2047
- // src/typed-refs/tree.ts
2717
+ // src/typed-refs/tree-ref.ts
2048
2718
  var TreeRef = class extends TypedRef {
2049
- absorbPlainValues() {
2719
+ [INTERNAL_SYMBOL];
2720
+ constructor(params) {
2721
+ super();
2722
+ this[INTERNAL_SYMBOL] = new TreeRefInternals(params);
2723
+ this[INTERNAL_SYMBOL].setTreeRef(this);
2050
2724
  }
2051
- toJSON() {
2052
- return this.container.toJSON();
2725
+ /**
2726
+ * Get the data shape for tree nodes.
2727
+ */
2728
+ get dataShape() {
2729
+ return this[INTERNAL_SYMBOL].getDataShape();
2053
2730
  }
2054
- createNode(parent, index) {
2055
- this.assertMutable();
2056
- return this.container.createNode(parent, index);
2731
+ /**
2732
+ * Get or create a node ref for a LoroTreeNode.
2733
+ */
2734
+ getOrCreateNodeRef(node) {
2735
+ return this[INTERNAL_SYMBOL].getOrCreateNodeRef(node);
2057
2736
  }
2058
- move(target, parent, index) {
2059
- this.assertMutable();
2060
- this.container.move(target, parent, index);
2737
+ /**
2738
+ * Get a node by its ID.
2739
+ */
2740
+ getNodeByID(id) {
2741
+ return this[INTERNAL_SYMBOL].getNodeByID(id);
2061
2742
  }
2743
+ /**
2744
+ * Delete a node from the tree.
2745
+ */
2062
2746
  delete(target) {
2063
- this.assertMutable();
2064
- this.container.delete(target);
2747
+ this[INTERNAL_SYMBOL].delete(target);
2065
2748
  }
2066
- has(target) {
2067
- return this.container.has(target);
2749
+ /**
2750
+ * Serialize the tree to a nested JSON structure.
2751
+ * Each node includes its data and children recursively.
2752
+ */
2753
+ toJSON() {
2754
+ const container = this[INTERNAL_SYMBOL].getContainer();
2755
+ const nativeJson = container.toJSON();
2756
+ return this.transformNativeJson(nativeJson);
2068
2757
  }
2069
- getNodeByID(id) {
2070
- return this.container.getNodeByID ? this.container.getNodeByID(id) : void 0;
2758
+ /**
2759
+ * Create a new root node with optional initial data.
2760
+ *
2761
+ * @param initialData - Optional partial data to initialize the node with
2762
+ * @returns The created TreeNodeRef
2763
+ */
2764
+ createNode(initialData) {
2765
+ const container = this[INTERNAL_SYMBOL].getContainer();
2766
+ const loroNode = container.createNode();
2767
+ const nodeRef = this.getOrCreateNodeRef(loroNode);
2768
+ if (initialData) {
2769
+ for (const [key, value] of Object.entries(initialData)) {
2770
+ ;
2771
+ nodeRef.data[key] = value;
2772
+ }
2773
+ }
2774
+ this[INTERNAL_SYMBOL].commitIfAuto();
2775
+ return nodeRef;
2776
+ }
2777
+ /**
2778
+ * Get all root nodes (nodes without parents).
2779
+ * Returns nodes in their fractional index order.
2780
+ */
2781
+ roots() {
2782
+ const container = this[INTERNAL_SYMBOL].getContainer();
2783
+ return container.roots().map((node) => this.getOrCreateNodeRef(node));
2784
+ }
2785
+ /**
2786
+ * Get all nodes in the tree (unordered).
2787
+ * Includes all nodes, not just roots.
2788
+ */
2789
+ nodes() {
2790
+ const container = this[INTERNAL_SYMBOL].getContainer();
2791
+ return container.nodes().map((node) => this.getOrCreateNodeRef(node));
2792
+ }
2793
+ /**
2794
+ * Check if a node with the given ID exists in the tree.
2795
+ */
2796
+ has(id) {
2797
+ const container = this[INTERNAL_SYMBOL].getContainer();
2798
+ return container.has(id);
2799
+ }
2800
+ /**
2801
+ * Enable fractional index generation for ordering.
2802
+ *
2803
+ * @param jitter - Optional jitter value to avoid conflicts (0 = no jitter)
2804
+ */
2805
+ enableFractionalIndex(jitter = 0) {
2806
+ const container = this[INTERNAL_SYMBOL].getContainer();
2807
+ container.enableFractionalIndex(jitter);
2808
+ }
2809
+ /**
2810
+ * Transform Loro's native JSON format to our typed format.
2811
+ */
2812
+ transformNativeJson(nodes) {
2813
+ return nodes.map((node) => ({
2814
+ id: node.id,
2815
+ parent: node.parent,
2816
+ index: node.index,
2817
+ fractionalIndex: node.fractional_index,
2818
+ data: node.meta,
2819
+ children: this.transformNativeJson(node.children || [])
2820
+ }));
2821
+ }
2822
+ /**
2823
+ * Get a flat array representation of all nodes.
2824
+ * Flattens the nested tree structure into a single array.
2825
+ */
2826
+ toArray() {
2827
+ const result = [];
2828
+ const flattenNodes = (nodes) => {
2829
+ for (const node of nodes) {
2830
+ result.push({
2831
+ id: node.id,
2832
+ parent: node.parent,
2833
+ index: node.index,
2834
+ fractionalIndex: node.fractional_index,
2835
+ data: node.meta
2836
+ });
2837
+ if (node.children && node.children.length > 0) {
2838
+ flattenNodes(node.children);
2839
+ }
2840
+ }
2841
+ };
2842
+ const container = this[INTERNAL_SYMBOL].getContainer();
2843
+ const nativeJson = container.toJSON();
2844
+ flattenNodes(nativeJson);
2845
+ return result;
2071
2846
  }
2072
2847
  };
2073
2848
 
@@ -2086,20 +2861,14 @@ var containerConstructor = {
2086
2861
  function hasContainerConstructor(type) {
2087
2862
  return type in containerConstructor;
2088
2863
  }
2089
- function unwrapReadonlyPrimitive(ref, shape) {
2090
- if (shape._type === "counter") {
2091
- return ref.value;
2092
- }
2093
- if (shape._type === "text") {
2094
- return ref.toString();
2095
- }
2096
- return ref;
2864
+ function hasInternalSymbol(value) {
2865
+ return value !== null && typeof value === "object" && INTERNAL_SYMBOL in value;
2097
2866
  }
2098
2867
  function absorbCachedPlainValues(cache, getContainer) {
2099
2868
  let container;
2100
2869
  for (const [key, ref] of cache.entries()) {
2101
- if (ref instanceof TypedRef) {
2102
- ref.absorbPlainValues();
2870
+ if (hasInternalSymbol(ref)) {
2871
+ ref[INTERNAL_SYMBOL].absorbPlainValues();
2103
2872
  } else {
2104
2873
  if (!container) container = getContainer();
2105
2874
  container.set(key, ref);
@@ -2128,7 +2897,9 @@ function createContainerTypedRef(params) {
2128
2897
  listProxyHandler
2129
2898
  );
2130
2899
  case "struct":
2131
- return new StructRef(params);
2900
+ return createStructRef(
2901
+ params
2902
+ );
2132
2903
  case "movableList":
2133
2904
  return new Proxy(
2134
2905
  new MovableListRef(params),
@@ -2141,8 +2912,16 @@ function createContainerTypedRef(params) {
2141
2912
  );
2142
2913
  case "text":
2143
2914
  return new TextRef(params);
2144
- case "tree":
2145
- return new TreeRef(params);
2915
+ case "tree": {
2916
+ const treeShape = params.shape;
2917
+ return new TreeRef({
2918
+ shape: treeShape,
2919
+ placeholder: params.placeholder,
2920
+ getContainer: params.getContainer,
2921
+ autoCommit: params.autoCommit,
2922
+ getDoc: params.getDoc
2923
+ });
2924
+ }
2146
2925
  default:
2147
2926
  throw new Error(
2148
2927
  `Unknown container type: ${params.shape._type}`
@@ -2150,7 +2929,8 @@ function createContainerTypedRef(params) {
2150
2929
  }
2151
2930
  }
2152
2931
  function assignPlainValueToTypedRef(ref, value) {
2153
- const shapeType = ref.shape._type;
2932
+ const shape = ref[INTERNAL_SYMBOL]?.getShape?.() ?? ref.shape;
2933
+ const shapeType = shape?._type;
2154
2934
  if (shapeType === "struct" || shapeType === "record") {
2155
2935
  for (const k in value) {
2156
2936
  ;
@@ -2196,7 +2976,7 @@ function assignPlainValueToTypedRef(ref, value) {
2196
2976
  return false;
2197
2977
  }
2198
2978
 
2199
- // src/typed-refs/doc.ts
2979
+ // src/typed-refs/doc-ref-internals.ts
2200
2980
  var containerGetter = {
2201
2981
  counter: "getCounter",
2202
2982
  list: "getList",
@@ -2207,23 +2987,22 @@ var containerGetter = {
2207
2987
  text: "getText",
2208
2988
  tree: "getTree"
2209
2989
  };
2210
- var DocRef = class extends TypedRef {
2211
- _doc;
2990
+ var DocRefInternals = class extends BaseRefInternals {
2212
2991
  propertyCache = /* @__PURE__ */ new Map();
2992
+ doc;
2213
2993
  requiredPlaceholder;
2214
- constructor(_params) {
2994
+ constructor(params) {
2215
2995
  super({
2216
- ..._params,
2996
+ ...params,
2217
2997
  getContainer: () => {
2218
2998
  throw new Error("can't get container on DocRef");
2219
2999
  },
2220
- getDoc: () => this._doc
3000
+ getDoc: () => params.doc
2221
3001
  });
2222
- if (!_params.placeholder) throw new Error("placeholder required");
2223
- this._doc = _params.doc;
2224
- this.requiredPlaceholder = _params.placeholder;
2225
- this.createLazyProperties();
3002
+ this.doc = params.doc;
3003
+ this.requiredPlaceholder = params.placeholder;
2226
3004
  }
3005
+ /** Get typed ref params for creating child refs at a key */
2227
3006
  getTypedRefParams(key, shape) {
2228
3007
  if (shape._type === "any") {
2229
3008
  throw new Error(
@@ -2231,53 +3010,59 @@ var DocRef = class extends TypedRef {
2231
3010
  );
2232
3011
  }
2233
3012
  const getterName = containerGetter[shape._type];
2234
- const getter = this._doc[getterName].bind(this._doc);
3013
+ const getter = this.doc[getterName].bind(this.doc);
2235
3014
  return {
2236
3015
  shape,
2237
3016
  placeholder: this.requiredPlaceholder[key],
2238
3017
  getContainer: () => getter(key),
2239
- readonly: this.readonly,
2240
- autoCommit: this._params.autoCommit,
2241
- getDoc: () => this._doc
3018
+ autoCommit: this.getAutoCommit(),
3019
+ batchedMutation: this.getBatchedMutation(),
3020
+ getDoc: () => this.doc
2242
3021
  };
2243
3022
  }
3023
+ /** Get or create a typed ref for a key */
2244
3024
  getOrCreateTypedRef(key, shape) {
2245
- if (this.readonly && (shape._type === "counter" || shape._type === "text")) {
2246
- const shallow = this._doc.getShallowValue();
2247
- if (!shallow[key]) {
2248
- return this.requiredPlaceholder[key];
2249
- }
2250
- }
2251
3025
  let ref = this.propertyCache.get(key);
2252
3026
  if (!ref) {
2253
3027
  ref = createContainerTypedRef(this.getTypedRefParams(key, shape));
2254
3028
  this.propertyCache.set(key, ref);
2255
3029
  }
2256
- if (this.readonly) {
2257
- return unwrapReadonlyPrimitive(ref, shape);
2258
- }
2259
3030
  return ref;
2260
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
+ }
2261
3049
  createLazyProperties() {
2262
- for (const key in this.shape.shapes) {
2263
- 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];
2264
3053
  Object.defineProperty(this, key, {
2265
- get: () => this.getOrCreateTypedRef(key, shape),
3054
+ get: () => this[INTERNAL_SYMBOL].getOrCreateTypedRef(key, containerShape),
2266
3055
  enumerable: true
2267
3056
  });
2268
3057
  }
2269
3058
  }
2270
3059
  toJSON() {
3060
+ const shape = this[INTERNAL_SYMBOL].getShape();
2271
3061
  return serializeRefToJSON(
2272
3062
  this,
2273
- Object.keys(this.shape.shapes)
3063
+ Object.keys(shape.shapes)
2274
3064
  );
2275
3065
  }
2276
- absorbPlainValues() {
2277
- for (const [, ref] of this.propertyCache.entries()) {
2278
- ref.absorbPlainValues();
2279
- }
2280
- }
2281
3066
  };
2282
3067
 
2283
3068
  // src/validation.ts
@@ -2509,68 +3294,11 @@ function validatePlaceholder(placeholder, schema) {
2509
3294
  }
2510
3295
 
2511
3296
  // src/typed-doc.ts
2512
- var TypedDocMeta = class {
2513
- constructor(internal) {
2514
- this.internal = internal;
2515
- }
2516
- /**
2517
- * The primary method of mutating typed documents.
2518
- * Batches multiple mutations into a single transaction.
2519
- * All changes commit together at the end.
2520
- *
2521
- * Use this for:
2522
- * - Find-and-mutate operations (required due to JS limitations)
2523
- * - Performance (fewer commits)
2524
- * - Atomic undo (all changes = one undo step)
2525
- *
2526
- * Returns the doc for chaining.
2527
- */
2528
- change(fn) {
2529
- this.internal.change(fn);
2530
- return this.internal.proxy;
2531
- }
2532
- /**
2533
- * Returns the full plain JavaScript object representation of the document.
2534
- * This is an expensive O(N) operation that serializes the entire document.
2535
- */
2536
- toJSON() {
2537
- return this.internal.toJSON();
2538
- }
2539
- /**
2540
- * Apply JSON Patch operations to the document
2541
- *
2542
- * @param patch - Array of JSON Patch operations (RFC 6902)
2543
- * @param pathPrefix - Optional path prefix for scoped operations
2544
- * @returns Updated document value
2545
- */
2546
- applyPatch(patch, pathPrefix) {
2547
- this.internal.applyPatch(patch, pathPrefix);
2548
- return this.internal.proxy;
2549
- }
2550
- /**
2551
- * Access the underlying LoroDoc for advanced operations.
2552
- */
2553
- get loroDoc() {
2554
- return this.internal.loroDoc;
2555
- }
2556
- /**
2557
- * Access the document schema shape.
2558
- */
2559
- get docShape() {
2560
- return this.internal.docShape;
2561
- }
2562
- /**
2563
- * Get raw CRDT value without placeholder overlay.
2564
- */
2565
- get rawValue() {
2566
- return this.internal.rawValue;
2567
- }
2568
- };
2569
3297
  var TypedDocInternal = class {
2570
3298
  shape;
2571
3299
  placeholder;
2572
3300
  doc;
2573
- _valueRef = null;
3301
+ valueRef = null;
2574
3302
  // Reference to the proxy for returning from change()
2575
3303
  proxy = null;
2576
3304
  constructor(shape, doc = new LoroDoc()) {
@@ -2580,15 +3308,15 @@ var TypedDocInternal = class {
2580
3308
  validatePlaceholder(this.placeholder, this.shape);
2581
3309
  }
2582
3310
  get value() {
2583
- if (!this._valueRef) {
2584
- this._valueRef = new DocRef({
3311
+ if (!this.valueRef) {
3312
+ this.valueRef = new DocRef({
2585
3313
  shape: this.shape,
2586
3314
  placeholder: this.placeholder,
2587
3315
  doc: this.doc,
2588
3316
  autoCommit: true
2589
3317
  });
2590
3318
  }
2591
- return this._valueRef;
3319
+ return this.valueRef;
2592
3320
  }
2593
3321
  toJSON() {
2594
3322
  const crdtValue = this.doc.toJSON();
@@ -2603,12 +3331,14 @@ var TypedDocInternal = class {
2603
3331
  shape: this.shape,
2604
3332
  placeholder: this.placeholder,
2605
3333
  doc: this.doc,
2606
- autoCommit: false
3334
+ autoCommit: false,
3335
+ batchedMutation: true
3336
+ // Enable value shape caching for find-and-mutate patterns
2607
3337
  });
2608
3338
  fn(draft);
2609
- draft.absorbPlainValues();
3339
+ draft[INTERNAL_SYMBOL].absorbPlainValues();
2610
3340
  this.doc.commit();
2611
- this._valueRef = null;
3341
+ this.valueRef = null;
2612
3342
  }
2613
3343
  applyPatch(patch, pathPrefix) {
2614
3344
  this.change((draft) => {
@@ -2632,11 +3362,37 @@ var TypedDocInternal = class {
2632
3362
  };
2633
3363
  function createTypedDoc(shape, existingDoc) {
2634
3364
  const internal = new TypedDocInternal(shape, existingDoc || new LoroDoc());
2635
- 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
+ };
2636
3389
  const proxy = new Proxy(internal.value, {
2637
3390
  get(target, prop, receiver) {
2638
- if (prop === "$") {
2639
- return meta;
3391
+ if (prop === LORO_SYMBOL) {
3392
+ return loroNamespace;
3393
+ }
3394
+ if (prop === "change") {
3395
+ return changeFunction;
2640
3396
  }
2641
3397
  if (prop === "toJSON") {
2642
3398
  return () => internal.toJSON();
@@ -2644,26 +3400,33 @@ function createTypedDoc(shape, existingDoc) {
2644
3400
  return Reflect.get(target, prop, receiver);
2645
3401
  },
2646
3402
  set(target, prop, value, receiver) {
2647
- if (prop === "$") {
3403
+ if (prop === LORO_SYMBOL || prop === "change") {
2648
3404
  return false;
2649
3405
  }
2650
3406
  return Reflect.set(target, prop, value, receiver);
2651
3407
  },
2652
3408
  // Support 'in' operator
2653
3409
  has(target, prop) {
2654
- if (prop === "$") return true;
3410
+ if (prop === LORO_SYMBOL || prop === "change") return true;
2655
3411
  return Reflect.has(target, prop);
2656
3412
  },
2657
- // Support Object.keys() - don't include $ in enumeration
3413
+ // Support Object.keys() - don't include change or LORO_SYMBOL in enumeration
2658
3414
  ownKeys(target) {
2659
3415
  return Reflect.ownKeys(target);
2660
3416
  },
2661
3417
  getOwnPropertyDescriptor(target, prop) {
2662
- if (prop === "$") {
3418
+ if (prop === "change") {
3419
+ return {
3420
+ configurable: true,
3421
+ enumerable: false,
3422
+ value: changeFunction
3423
+ };
3424
+ }
3425
+ if (prop === LORO_SYMBOL) {
2663
3426
  return {
2664
3427
  configurable: true,
2665
3428
  enumerable: false,
2666
- value: meta
3429
+ value: loroNamespace
2667
3430
  };
2668
3431
  }
2669
3432
  return Reflect.getOwnPropertyDescriptor(target, prop);
@@ -2673,6 +3436,7 @@ function createTypedDoc(shape, existingDoc) {
2673
3436
  return proxy;
2674
3437
  }
2675
3438
  export {
3439
+ LORO_SYMBOL,
2676
3440
  Shape,
2677
3441
  change,
2678
3442
  compileToJsonPath,
@@ -2683,8 +3447,10 @@ export {
2683
3447
  deriveShapePlaceholder,
2684
3448
  evaluatePath,
2685
3449
  evaluatePathOnValue,
3450
+ getLoroContainer,
2686
3451
  getLoroDoc,
2687
3452
  hasWildcard,
3453
+ loro,
2688
3454
  mergeValue,
2689
3455
  overlayPlaceholder,
2690
3456
  validatePlaceholder