@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.
- package/README.md +289 -150
- package/dist/index.d.ts +1012 -310
- package/dist/index.js +1334 -568
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/change.test.ts +51 -52
- package/src/functional-helpers.test.ts +316 -4
- package/src/functional-helpers.ts +96 -6
- package/src/grand-unified-api.test.ts +35 -29
- package/src/index.ts +27 -1
- package/src/json-patch.test.ts +46 -27
- package/src/loro.test.ts +449 -0
- package/src/loro.ts +273 -0
- package/src/overlay-recursion.test.ts +1 -1
- package/src/overlay.ts +62 -3
- package/src/path-evaluator.ts +1 -1
- package/src/path-selector.test.ts +94 -1
- package/src/shape.ts +107 -14
- package/src/typed-doc.ts +100 -98
- package/src/typed-refs/base.ts +126 -46
- package/src/typed-refs/counter-ref-internals.ts +62 -0
- package/src/typed-refs/{counter.test.ts → counter-ref.test.ts} +5 -4
- package/src/typed-refs/counter-ref.ts +45 -0
- package/src/typed-refs/{doc.ts → doc-ref-internals.ts} +33 -56
- package/src/typed-refs/doc-ref.ts +47 -0
- package/src/typed-refs/encapsulation.test.ts +226 -0
- package/src/typed-refs/list-ref-base-internals.ts +280 -0
- package/src/typed-refs/list-ref-base.ts +518 -0
- package/src/typed-refs/list-ref-internals.ts +21 -0
- package/src/typed-refs/list-ref-value-updates.test.ts +213 -0
- package/src/typed-refs/{list.ts → list-ref.ts} +10 -11
- package/src/typed-refs/movable-list-ref-internals.ts +38 -0
- package/src/typed-refs/movable-list-ref.ts +31 -0
- package/src/typed-refs/proxy-handlers.ts +13 -4
- package/src/typed-refs/record-ref-internals.ts +216 -0
- package/src/typed-refs/record-ref-value-updates.test.ts +214 -0
- package/src/typed-refs/{record.test.ts → record-ref.test.ts} +21 -16
- package/src/typed-refs/record-ref.ts +80 -0
- package/src/typed-refs/struct-ref-internals.ts +195 -0
- package/src/typed-refs/struct-ref.test.ts +202 -0
- package/src/typed-refs/struct-ref.ts +257 -0
- package/src/typed-refs/text-ref-internals.ts +100 -0
- package/src/typed-refs/text-ref.ts +72 -0
- package/src/typed-refs/tree-node-ref-internals.ts +111 -0
- package/src/typed-refs/tree-node-ref.test.ts +234 -0
- package/src/typed-refs/tree-node-ref.ts +200 -0
- package/src/typed-refs/tree-node.test.ts +384 -0
- package/src/typed-refs/tree-ref-internals.ts +110 -0
- package/src/typed-refs/tree-ref.ts +194 -0
- package/src/typed-refs/utils.ts +38 -17
- package/src/types.ts +36 -1
- package/src/utils/type-guards.ts +1 -0
- package/src/typed-refs/counter.ts +0 -64
- package/src/typed-refs/list-base.ts +0 -424
- package/src/typed-refs/movable-list.ts +0 -34
- package/src/typed-refs/record.ts +0 -220
- package/src/typed-refs/struct.ts +0 -206
- package/src/typed-refs/text.ts +0 -97
- package/src/typed-refs/tree.ts +0 -40
- /package/src/typed-refs/{list.test.ts → list-ref.test.ts} +0 -0
- /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
|
|
116
|
+
return doc.change(fn);
|
|
105
117
|
}
|
|
106
|
-
function getLoroDoc(
|
|
107
|
-
return doc
|
|
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
|
-
|
|
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
|
|
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
|
|
1041
|
-
|
|
1042
|
-
|
|
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
|
-
|
|
1045
|
-
|
|
1046
|
-
return this.
|
|
1120
|
+
/** Get the shape for this ref */
|
|
1121
|
+
getShape() {
|
|
1122
|
+
return this.params.shape;
|
|
1047
1123
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1124
|
+
/** Get the placeholder value */
|
|
1125
|
+
getPlaceholder() {
|
|
1126
|
+
return this.params.placeholder;
|
|
1050
1127
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1128
|
+
/** Check if autoCommit is enabled */
|
|
1129
|
+
getAutoCommit() {
|
|
1130
|
+
return !!this.params.autoCommit;
|
|
1053
1131
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1132
|
+
/** Check if in batched mutation mode */
|
|
1133
|
+
getBatchedMutation() {
|
|
1134
|
+
return !!this.params.batchedMutation;
|
|
1056
1135
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1136
|
+
/** Get the LoroDoc */
|
|
1137
|
+
getDoc() {
|
|
1138
|
+
return this.params.getDoc();
|
|
1059
1139
|
}
|
|
1060
|
-
/**
|
|
1061
|
-
|
|
1062
|
-
|
|
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
|
-
*
|
|
1071
|
-
*
|
|
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
|
-
|
|
1075
|
-
|
|
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
|
|
1101
|
-
|
|
1102
|
-
|
|
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.
|
|
1110
|
-
this.
|
|
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.
|
|
1116
|
-
this.
|
|
1117
|
-
this.container.decrement(value);
|
|
1194
|
+
this.materialized = true;
|
|
1195
|
+
this.getContainer().decrement(value);
|
|
1118
1196
|
this.commitIfAuto();
|
|
1119
1197
|
}
|
|
1120
|
-
/**
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
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
|
-
|
|
1131
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1337
|
-
|
|
1338
|
-
getDoc: this.
|
|
1415
|
+
autoCommit: this.getAutoCommit(),
|
|
1416
|
+
batchedMutation: this.getBatchedMutation(),
|
|
1417
|
+
getDoc: () => this.getDoc()
|
|
1339
1418
|
};
|
|
1340
1419
|
}
|
|
1341
|
-
|
|
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(
|
|
1425
|
+
if (cachedItem && isValueShape(shape.shape)) {
|
|
1345
1426
|
return cachedItem;
|
|
1346
1427
|
}
|
|
1347
|
-
const containerItem =
|
|
1428
|
+
const containerItem = container.get(index);
|
|
1348
1429
|
if (containerItem === void 0) {
|
|
1349
1430
|
return void 0;
|
|
1350
1431
|
}
|
|
1351
|
-
if (isValueShape(
|
|
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
|
-
|
|
1447
|
+
/** Get mutable item for return values (returns ref or cached value) */
|
|
1367
1448
|
getMutableItem(index) {
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
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(
|
|
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
|
-
|
|
1464
|
+
cachedItem2 = JSON.parse(JSON.stringify(containerItem));
|
|
1379
1465
|
} else {
|
|
1380
|
-
|
|
1381
|
-
}
|
|
1382
|
-
if (!this.readonly) {
|
|
1383
|
-
this.itemCache.set(index, cachedItem);
|
|
1466
|
+
cachedItem2 = containerItem;
|
|
1384
1467
|
}
|
|
1385
|
-
|
|
1386
|
-
|
|
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,
|
|
1474
|
+
this.getTypedRefParams(index, shape.shape)
|
|
1389
1475
|
);
|
|
1390
1476
|
this.itemCache.set(index, cachedItem);
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
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.
|
|
1474
|
-
this.
|
|
1475
|
-
this.
|
|
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.
|
|
1480
|
-
this.
|
|
1481
|
-
|
|
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.
|
|
1486
|
-
this.
|
|
1487
|
-
this.commitIfAuto();
|
|
1665
|
+
this[INTERNAL_SYMBOL].pushWithConversion(item);
|
|
1666
|
+
this[INTERNAL_SYMBOL].commitIfAuto();
|
|
1488
1667
|
}
|
|
1489
1668
|
pushContainer(container) {
|
|
1490
|
-
this.
|
|
1491
|
-
const result =
|
|
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.
|
|
1497
|
-
const result =
|
|
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
|
|
1513
|
-
|
|
1514
|
-
|
|
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(
|
|
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 {
|
|
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
|
-
|
|
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
|
|
1566
|
-
|
|
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.
|
|
1571
|
-
|
|
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/
|
|
1576
|
-
var
|
|
1577
|
-
|
|
1578
|
-
return
|
|
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.
|
|
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.
|
|
1585
|
-
|
|
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.
|
|
1590
|
-
|
|
1757
|
+
const container = this.getContainer();
|
|
1758
|
+
container.set(index, item);
|
|
1591
1759
|
this.commitIfAuto();
|
|
1592
|
-
|
|
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
|
|
1866
|
+
// src/typed-refs/record-ref-internals.ts
|
|
1867
|
+
var RecordRefInternals = class extends BaseRefInternals {
|
|
1688
1868
|
refCache = /* @__PURE__ */ new Map();
|
|
1689
|
-
|
|
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.
|
|
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: () =>
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
getDoc: this.
|
|
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
|
-
|
|
1724
|
-
|
|
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
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
)
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
const
|
|
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
|
-
|
|
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.
|
|
1751
|
-
if (placeholder
|
|
1752
|
-
|
|
1931
|
+
const placeholder = this.getPlaceholder()?.[key];
|
|
1932
|
+
if (placeholder !== void 0) {
|
|
1933
|
+
ref2 = placeholder;
|
|
1753
1934
|
} else {
|
|
1754
|
-
|
|
1935
|
+
ref2 = shape._plain;
|
|
1755
1936
|
}
|
|
1756
1937
|
}
|
|
1757
|
-
|
|
1758
|
-
this.refCache.set(key, ref);
|
|
1759
|
-
}
|
|
1938
|
+
this.refCache.set(key, ref2);
|
|
1760
1939
|
}
|
|
1940
|
+
return ref2;
|
|
1761
1941
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
this.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
|
-
|
|
1771
|
-
return this.getRef(key);
|
|
1772
|
-
}
|
|
1951
|
+
/** Set a value at a key */
|
|
1773
1952
|
set(key, value) {
|
|
1774
|
-
this.
|
|
1775
|
-
|
|
1776
|
-
|
|
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
|
-
|
|
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.
|
|
1798
|
-
|
|
1973
|
+
const container = this.getContainer();
|
|
1974
|
+
container.delete(key);
|
|
1799
1975
|
this.refCache.delete(key);
|
|
1800
1976
|
this.commitIfAuto();
|
|
1801
1977
|
}
|
|
1802
|
-
|
|
1803
|
-
|
|
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
|
-
|
|
1809
|
-
|
|
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
|
-
|
|
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
|
|
2055
|
+
// src/typed-refs/struct-ref-internals.ts
|
|
2056
|
+
var StructRefInternals = class extends BaseRefInternals {
|
|
1834
2057
|
propertyCache = /* @__PURE__ */ new Map();
|
|
1835
|
-
|
|
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.
|
|
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: () =>
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
getDoc: this.
|
|
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
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
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
|
-
|
|
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.
|
|
2104
|
+
const placeholder = this.getPlaceholder()?.[key];
|
|
1877
2105
|
if (placeholder === void 0) {
|
|
1878
2106
|
throw new Error("placeholder required");
|
|
1879
2107
|
}
|
|
1880
|
-
|
|
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
|
-
|
|
2112
|
+
return ref2;
|
|
1887
2113
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
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
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
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.
|
|
2198
|
+
Object.keys(this.structShape.shapes)
|
|
1933
2199
|
);
|
|
1934
2200
|
}
|
|
1935
|
-
//
|
|
2201
|
+
// Deprecated methods - kept for backward compatibility
|
|
2202
|
+
// @deprecated Use property access instead: obj.key
|
|
1936
2203
|
get(key) {
|
|
1937
|
-
|
|
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.
|
|
1941
|
-
|
|
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.
|
|
1946
|
-
const result =
|
|
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.
|
|
1952
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2233
|
+
const container = this[INTERNAL_SYMBOL].getContainer();
|
|
2234
|
+
return container.keys();
|
|
1960
2235
|
}
|
|
2236
|
+
// @deprecated Use Object.values(obj) instead
|
|
1961
2237
|
values() {
|
|
1962
|
-
|
|
2238
|
+
const container = this[INTERNAL_SYMBOL].getContainer();
|
|
2239
|
+
return container.values();
|
|
1963
2240
|
}
|
|
2241
|
+
// @deprecated Not standard for objects
|
|
1964
2242
|
get size() {
|
|
1965
|
-
|
|
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
|
|
1971
|
-
|
|
1972
|
-
|
|
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.
|
|
1981
|
-
this.
|
|
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.
|
|
1987
|
-
this.
|
|
1988
|
-
this.container.delete(index, len);
|
|
2321
|
+
this.materialized = true;
|
|
2322
|
+
this.getContainer().delete(index, len);
|
|
1989
2323
|
this.commitIfAuto();
|
|
1990
2324
|
}
|
|
1991
|
-
/**
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
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
|
-
|
|
2002
|
-
|
|
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
|
-
|
|
2016
|
-
|
|
2017
|
-
this.
|
|
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
|
-
|
|
2022
|
-
|
|
2023
|
-
this.
|
|
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
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
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
|
-
|
|
2034
|
-
|
|
2449
|
+
dataRef;
|
|
2450
|
+
/** Get the underlying LoroTreeNode */
|
|
2451
|
+
getNode() {
|
|
2452
|
+
return this.params.node;
|
|
2035
2453
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
this.
|
|
2039
|
-
|
|
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
|
-
|
|
2043
|
-
|
|
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
|
-
|
|
2719
|
+
[INTERNAL_SYMBOL];
|
|
2720
|
+
constructor(params) {
|
|
2721
|
+
super();
|
|
2722
|
+
this[INTERNAL_SYMBOL] = new TreeRefInternals(params);
|
|
2723
|
+
this[INTERNAL_SYMBOL].setTreeRef(this);
|
|
2050
2724
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2725
|
+
/**
|
|
2726
|
+
* Get the data shape for tree nodes.
|
|
2727
|
+
*/
|
|
2728
|
+
get dataShape() {
|
|
2729
|
+
return this[INTERNAL_SYMBOL].getDataShape();
|
|
2053
2730
|
}
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
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
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
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.
|
|
2064
|
-
this.container.delete(target);
|
|
2747
|
+
this[INTERNAL_SYMBOL].delete(target);
|
|
2065
2748
|
}
|
|
2066
|
-
|
|
2067
|
-
|
|
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
|
-
|
|
2070
|
-
|
|
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
|
|
2090
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
2211
|
-
_doc;
|
|
2990
|
+
var DocRefInternals = class extends BaseRefInternals {
|
|
2212
2991
|
propertyCache = /* @__PURE__ */ new Map();
|
|
2992
|
+
doc;
|
|
2213
2993
|
requiredPlaceholder;
|
|
2214
|
-
constructor(
|
|
2994
|
+
constructor(params) {
|
|
2215
2995
|
super({
|
|
2216
|
-
...
|
|
2996
|
+
...params,
|
|
2217
2997
|
getContainer: () => {
|
|
2218
2998
|
throw new Error("can't get container on DocRef");
|
|
2219
2999
|
},
|
|
2220
|
-
getDoc: () =>
|
|
3000
|
+
getDoc: () => params.doc
|
|
2221
3001
|
});
|
|
2222
|
-
|
|
2223
|
-
this.
|
|
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.
|
|
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
|
-
|
|
2240
|
-
|
|
2241
|
-
getDoc: () => this.
|
|
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
|
-
|
|
2263
|
-
|
|
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,
|
|
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(
|
|
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
|
-
|
|
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.
|
|
2584
|
-
this.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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 === "
|
|
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
|
|
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:
|
|
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
|