@loro-extended/change 4.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -149
- package/dist/index.d.ts +962 -335
- package/dist/index.js +1040 -598
- 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 +25 -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/path-evaluator.ts +1 -1
- package/src/path-selector.test.ts +94 -1
- package/src/shape.ts +47 -15
- package/src/typed-doc.ts +99 -98
- package/src/typed-refs/base.ts +126 -35
- 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 -38
- 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-base.ts → list-ref-base.ts} +255 -160
- package/src/typed-refs/list-ref-internals.ts +21 -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.ts → record-ref-internals.ts} +78 -79
- 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-value-updates.test.ts → struct-ref.test.ts} +5 -3
- 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.ts → tree-node-ref.ts} +58 -94
- 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 +21 -23
- package/src/typed-refs/counter.ts +0 -62
- package/src/typed-refs/movable-list.ts +0 -32
- package/src/typed-refs/struct.ts +0 -201
- package/src/typed-refs/text.ts +0 -91
- package/src/typed-refs/tree.ts +0 -268
- /package/src/typed-refs/{list-value-updates.test.ts → list-ref-value-updates.test.ts} +0 -0
- /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/src/typed-refs/{record-value-updates.test.ts → record-ref-value-updates.test.ts} +0 -0
- /package/src/typed-refs/{tree-node-value-updates.test.ts → tree-node-ref.test.ts} +0 -0
- /package/src/typed-refs/{tree.test.ts → tree-node.test.ts} +0 -0
package/src/typed-refs/utils.ts
CHANGED
|
@@ -17,19 +17,19 @@ import type {
|
|
|
17
17
|
TextContainerShape,
|
|
18
18
|
TreeContainerShape,
|
|
19
19
|
} from "../shape.js"
|
|
20
|
-
import type
|
|
21
|
-
import { CounterRef } from "./counter.js"
|
|
22
|
-
import { ListRef } from "./list.js"
|
|
23
|
-
import { MovableListRef } from "./movable-list.js"
|
|
20
|
+
import { INTERNAL_SYMBOL, type TypedRef, type TypedRefParams } from "./base.js"
|
|
21
|
+
import { CounterRef } from "./counter-ref.js"
|
|
22
|
+
import { ListRef } from "./list-ref.js"
|
|
23
|
+
import { MovableListRef } from "./movable-list-ref.js"
|
|
24
24
|
import {
|
|
25
25
|
listProxyHandler,
|
|
26
26
|
movableListProxyHandler,
|
|
27
27
|
recordProxyHandler,
|
|
28
28
|
} from "./proxy-handlers.js"
|
|
29
|
-
import { RecordRef } from "./record.js"
|
|
30
|
-
import {
|
|
31
|
-
import { TextRef } from "./text.js"
|
|
32
|
-
import { TreeRef } from "./tree.js"
|
|
29
|
+
import { RecordRef } from "./record-ref.js"
|
|
30
|
+
import { createStructRef } from "./struct-ref.js"
|
|
31
|
+
import { TextRef } from "./text-ref.js"
|
|
32
|
+
import { TreeRef } from "./tree-ref.js"
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Mapping from container shape types to their Loro constructor classes.
|
|
@@ -77,22 +77,17 @@ export function unwrapReadonlyPrimitive(
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
* Type guard to check if a value has
|
|
80
|
+
* Type guard to check if a value has internal methods via INTERNAL_SYMBOL.
|
|
81
81
|
*/
|
|
82
|
-
function
|
|
82
|
+
function hasInternalSymbol(
|
|
83
83
|
value: unknown,
|
|
84
|
-
): value is { absorbPlainValues(): void } {
|
|
85
|
-
return
|
|
86
|
-
value !== null &&
|
|
87
|
-
typeof value === "object" &&
|
|
88
|
-
"absorbPlainValues" in value &&
|
|
89
|
-
typeof (value as any).absorbPlainValues === "function"
|
|
90
|
-
)
|
|
84
|
+
): value is { [INTERNAL_SYMBOL]: { absorbPlainValues(): void } } {
|
|
85
|
+
return value !== null && typeof value === "object" && INTERNAL_SYMBOL in value
|
|
91
86
|
}
|
|
92
87
|
|
|
93
88
|
/**
|
|
94
89
|
* Absorbs cached plain values back into a LoroMap container.
|
|
95
|
-
* For TypedRef entries (or any object with
|
|
90
|
+
* For TypedRef entries (or any object with INTERNAL_SYMBOL), recursively calls absorbPlainValues().
|
|
96
91
|
* For plain Value entries, sets them directly on the container.
|
|
97
92
|
*/
|
|
98
93
|
export function absorbCachedPlainValues(
|
|
@@ -102,9 +97,9 @@ export function absorbCachedPlainValues(
|
|
|
102
97
|
let container: LoroMap | undefined
|
|
103
98
|
|
|
104
99
|
for (const [key, ref] of cache.entries()) {
|
|
105
|
-
if (
|
|
100
|
+
if (hasInternalSymbol(ref)) {
|
|
106
101
|
// Contains a TypedRef or TreeRef, not a plain Value: keep recursing
|
|
107
|
-
ref.absorbPlainValues()
|
|
102
|
+
ref[INTERNAL_SYMBOL].absorbPlainValues()
|
|
108
103
|
} else {
|
|
109
104
|
// Plain value!
|
|
110
105
|
if (!container) container = getContainer()
|
|
@@ -152,7 +147,9 @@ export function createContainerTypedRef(
|
|
|
152
147
|
listProxyHandler,
|
|
153
148
|
)
|
|
154
149
|
case "struct":
|
|
155
|
-
return
|
|
150
|
+
return createStructRef(
|
|
151
|
+
params as TypedRefParams<StructContainerShape>,
|
|
152
|
+
) as unknown as TypedRef<ContainerShape>
|
|
156
153
|
case "movableList":
|
|
157
154
|
return new Proxy(
|
|
158
155
|
new MovableListRef(params as TypedRefParams<MovableListContainerShape>),
|
|
@@ -171,7 +168,6 @@ export function createContainerTypedRef(
|
|
|
171
168
|
shape: treeShape,
|
|
172
169
|
placeholder: params.placeholder as never[],
|
|
173
170
|
getContainer: params.getContainer as () => LoroTree,
|
|
174
|
-
readonly: params.readonly,
|
|
175
171
|
autoCommit: params.autoCommit,
|
|
176
172
|
getDoc: params.getDoc,
|
|
177
173
|
})
|
|
@@ -187,7 +183,9 @@ export function assignPlainValueToTypedRef(
|
|
|
187
183
|
ref: TypedRef<any>,
|
|
188
184
|
value: any,
|
|
189
185
|
): boolean {
|
|
190
|
-
|
|
186
|
+
// Access shape via INTERNAL_SYMBOL or fallback to direct property access for StructRef proxy
|
|
187
|
+
const shape = ref[INTERNAL_SYMBOL]?.getShape?.() ?? (ref as any).shape
|
|
188
|
+
const shapeType = shape?._type
|
|
191
189
|
|
|
192
190
|
if (shapeType === "struct" || shapeType === "record") {
|
|
193
191
|
for (const k in value) {
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import type { LoroCounter } from "loro-crdt"
|
|
2
|
-
import type { CounterContainerShape } from "../shape.js"
|
|
3
|
-
import { TypedRef } from "./base.js"
|
|
4
|
-
|
|
5
|
-
// Counter typed ref
|
|
6
|
-
export class CounterRef extends TypedRef<CounterContainerShape> {
|
|
7
|
-
// Track if we've materialized the container (made any changes)
|
|
8
|
-
private _materialized = false
|
|
9
|
-
|
|
10
|
-
protected get container(): LoroCounter {
|
|
11
|
-
return super.container as LoroCounter
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
absorbPlainValues() {
|
|
15
|
-
// no plain values contained within
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
increment(value: number = 1): void {
|
|
19
|
-
this._materialized = true
|
|
20
|
-
this.container.increment(value)
|
|
21
|
-
this.commitIfAuto()
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
decrement(value: number = 1): void {
|
|
25
|
-
this._materialized = true
|
|
26
|
-
this.container.decrement(value)
|
|
27
|
-
this.commitIfAuto()
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Returns the counter value.
|
|
32
|
-
* If the counter hasn't been materialized (no operations performed),
|
|
33
|
-
* returns the placeholder value if available.
|
|
34
|
-
*/
|
|
35
|
-
get value(): number {
|
|
36
|
-
// Check if the container has any value (non-zero means it was modified)
|
|
37
|
-
const containerValue = this.container.value
|
|
38
|
-
if (containerValue !== 0 || this._materialized) {
|
|
39
|
-
return containerValue
|
|
40
|
-
}
|
|
41
|
-
// Return placeholder if available and container is at default state
|
|
42
|
-
if (this.placeholder !== undefined) {
|
|
43
|
-
return this.placeholder as number
|
|
44
|
-
}
|
|
45
|
-
return containerValue
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
valueOf(): number {
|
|
49
|
-
return this.value
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
toJSON(): number {
|
|
53
|
-
return this.value
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
[Symbol.toPrimitive](hint: string): number | string {
|
|
57
|
-
if (hint === "string") {
|
|
58
|
-
return String(this.value)
|
|
59
|
-
}
|
|
60
|
-
return this.value
|
|
61
|
-
}
|
|
62
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { Container, LoroMovableList } from "loro-crdt"
|
|
2
|
-
import type { ContainerOrValueShape } from "../shape.js"
|
|
3
|
-
import type { InferMutableType } from "../types.js"
|
|
4
|
-
import { ListRefBase } from "./list-base.js"
|
|
5
|
-
|
|
6
|
-
// Movable list typed ref
|
|
7
|
-
export class MovableListRef<
|
|
8
|
-
NestedShape extends ContainerOrValueShape,
|
|
9
|
-
Item = NestedShape["_plain"],
|
|
10
|
-
> extends ListRefBase<NestedShape> {
|
|
11
|
-
[index: number]: InferMutableType<NestedShape> | undefined
|
|
12
|
-
|
|
13
|
-
protected get container(): LoroMovableList {
|
|
14
|
-
return super.container as LoroMovableList
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
protected absorbValueAtIndex(index: number, value: any): void {
|
|
18
|
-
// LoroMovableList has set method
|
|
19
|
-
this.container.set(index, value)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
move(from: number, to: number): void {
|
|
23
|
-
this.container.move(from, to)
|
|
24
|
-
this.commitIfAuto()
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
set(index: number, item: Exclude<Item, Container>) {
|
|
28
|
-
const result = this.container.set(index, item)
|
|
29
|
-
this.commitIfAuto()
|
|
30
|
-
return result
|
|
31
|
-
}
|
|
32
|
-
}
|
package/src/typed-refs/struct.ts
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import type { Container, LoroMap, Value } from "loro-crdt"
|
|
2
|
-
import type {
|
|
3
|
-
ContainerOrValueShape,
|
|
4
|
-
ContainerShape,
|
|
5
|
-
StructContainerShape,
|
|
6
|
-
ValueShape,
|
|
7
|
-
} from "../shape.js"
|
|
8
|
-
import type { Infer } from "../types.js"
|
|
9
|
-
import { isValueShape } from "../utils/type-guards.js"
|
|
10
|
-
import { TypedRef, type TypedRefParams } from "./base.js"
|
|
11
|
-
import {
|
|
12
|
-
absorbCachedPlainValues,
|
|
13
|
-
assignPlainValueToTypedRef,
|
|
14
|
-
containerConstructor,
|
|
15
|
-
createContainerTypedRef,
|
|
16
|
-
hasContainerConstructor,
|
|
17
|
-
serializeRefToJSON,
|
|
18
|
-
} from "./utils.js"
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Typed ref for struct containers (objects with fixed keys).
|
|
22
|
-
* Uses LoroMap as the underlying container.
|
|
23
|
-
*/
|
|
24
|
-
export class StructRef<
|
|
25
|
-
NestedShapes extends Record<string, ContainerOrValueShape>,
|
|
26
|
-
> extends TypedRef<any> {
|
|
27
|
-
private propertyCache = new Map<string, TypedRef<ContainerShape> | Value>()
|
|
28
|
-
|
|
29
|
-
constructor(params: TypedRefParams<StructContainerShape<NestedShapes>>) {
|
|
30
|
-
super(params)
|
|
31
|
-
this.createLazyProperties()
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
protected get shape(): StructContainerShape<NestedShapes> {
|
|
35
|
-
return super.shape as StructContainerShape<NestedShapes>
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
protected get container(): LoroMap {
|
|
39
|
-
return super.container as LoroMap
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
absorbPlainValues() {
|
|
43
|
-
absorbCachedPlainValues(this.propertyCache, () => this.container)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
getTypedRefParams<S extends ContainerShape>(
|
|
47
|
-
key: string,
|
|
48
|
-
shape: S,
|
|
49
|
-
): TypedRefParams<ContainerShape> {
|
|
50
|
-
const placeholder = (this.placeholder as any)?.[key]
|
|
51
|
-
|
|
52
|
-
// AnyContainerShape is an escape hatch - it doesn't have a constructor
|
|
53
|
-
if (!hasContainerConstructor(shape._type)) {
|
|
54
|
-
throw new Error(
|
|
55
|
-
`Cannot create typed ref for shape type "${shape._type}". ` +
|
|
56
|
-
`Use Shape.any() only at the document root level.`,
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const LoroContainer = containerConstructor[shape._type]
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
shape,
|
|
64
|
-
placeholder,
|
|
65
|
-
getContainer: () =>
|
|
66
|
-
this.container.getOrCreateContainer(key, new (LoroContainer as any)()),
|
|
67
|
-
autoCommit: this._params.autoCommit,
|
|
68
|
-
batchedMutation: this.batchedMutation,
|
|
69
|
-
getDoc: this._params.getDoc,
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
getOrCreateRef<Shape extends ContainerShape | ValueShape>(
|
|
74
|
-
key: string,
|
|
75
|
-
shape: Shape,
|
|
76
|
-
): any {
|
|
77
|
-
if (isValueShape(shape)) {
|
|
78
|
-
// When NOT in batchedMutation mode (direct access outside of change()), ALWAYS read fresh
|
|
79
|
-
// from container (NEVER cache). This ensures we always get the latest value
|
|
80
|
-
// from the CRDT, even when modified by a different ref instance (e.g., drafts from change())
|
|
81
|
-
//
|
|
82
|
-
// When in batchedMutation mode (inside change()), we cache value shapes so that
|
|
83
|
-
// mutations to nested objects persist back to the CRDT via absorbPlainValues()
|
|
84
|
-
if (!this.batchedMutation) {
|
|
85
|
-
const containerValue = this.container.get(key)
|
|
86
|
-
if (containerValue !== undefined) {
|
|
87
|
-
return containerValue
|
|
88
|
-
}
|
|
89
|
-
// Only fall back to placeholder if the container doesn't have the value
|
|
90
|
-
const placeholder = (this.placeholder as any)?.[key]
|
|
91
|
-
if (placeholder === undefined) {
|
|
92
|
-
throw new Error("placeholder required")
|
|
93
|
-
}
|
|
94
|
-
return placeholder
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// In batched mode (within change()), we cache value shapes so that
|
|
98
|
-
// mutations to nested objects persist back to the CRDT via absorbPlainValues()
|
|
99
|
-
let ref = this.propertyCache.get(key)
|
|
100
|
-
if (!ref) {
|
|
101
|
-
const containerValue = this.container.get(key)
|
|
102
|
-
if (containerValue !== undefined) {
|
|
103
|
-
// For objects, create a deep copy so mutations can be tracked
|
|
104
|
-
if (typeof containerValue === "object" && containerValue !== null) {
|
|
105
|
-
ref = JSON.parse(JSON.stringify(containerValue))
|
|
106
|
-
} else {
|
|
107
|
-
ref = containerValue as Value
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
// Only fall back to placeholder if the container doesn't have the value
|
|
111
|
-
const placeholder = (this.placeholder as any)?.[key]
|
|
112
|
-
if (placeholder === undefined) {
|
|
113
|
-
throw new Error("placeholder required")
|
|
114
|
-
}
|
|
115
|
-
ref = placeholder as Value
|
|
116
|
-
}
|
|
117
|
-
this.propertyCache.set(key, ref)
|
|
118
|
-
}
|
|
119
|
-
return ref
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Container shapes: safe to cache (handles)
|
|
123
|
-
let ref = this.propertyCache.get(key)
|
|
124
|
-
if (!ref) {
|
|
125
|
-
ref = createContainerTypedRef(this.getTypedRefParams(key, shape))
|
|
126
|
-
this.propertyCache.set(key, ref)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return ref as Shape extends ContainerShape ? TypedRef<Shape> : Value
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
private createLazyProperties(): void {
|
|
133
|
-
for (const key in this.shape.shapes) {
|
|
134
|
-
const shape = this.shape.shapes[key]
|
|
135
|
-
Object.defineProperty(this, key, {
|
|
136
|
-
get: () => this.getOrCreateRef(key, shape),
|
|
137
|
-
set: value => {
|
|
138
|
-
if (isValueShape(shape)) {
|
|
139
|
-
this.container.set(key, value)
|
|
140
|
-
this.propertyCache.set(key, value)
|
|
141
|
-
} else {
|
|
142
|
-
// For container shapes, try to assign the plain value
|
|
143
|
-
const ref = this.getOrCreateRef(key, shape)
|
|
144
|
-
if (assignPlainValueToTypedRef(ref as TypedRef<any>, value)) {
|
|
145
|
-
return
|
|
146
|
-
}
|
|
147
|
-
throw new Error(
|
|
148
|
-
"Cannot set container directly, modify the typed ref instead",
|
|
149
|
-
)
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
enumerable: true,
|
|
153
|
-
})
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
toJSON(): Infer<StructContainerShape<NestedShapes>> {
|
|
158
|
-
return serializeRefToJSON(
|
|
159
|
-
this as any,
|
|
160
|
-
Object.keys(this.shape.shapes),
|
|
161
|
-
) as Infer<StructContainerShape<NestedShapes>>
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// TODO(duane): return correct type here
|
|
165
|
-
get(key: string): any {
|
|
166
|
-
return this.container.get(key)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
set(key: string, value: Value): void {
|
|
170
|
-
this.container.set(key, value)
|
|
171
|
-
this.commitIfAuto()
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
setContainer<C extends Container>(key: string, container: C): C {
|
|
175
|
-
const result = this.container.setContainer(key, container)
|
|
176
|
-
this.commitIfAuto()
|
|
177
|
-
return result
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
delete(key: string): void {
|
|
181
|
-
this.container.delete(key)
|
|
182
|
-
this.commitIfAuto()
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
has(key: string): boolean {
|
|
186
|
-
// LoroMap doesn't have a has method, so we check if get returns undefined
|
|
187
|
-
return this.container.get(key) !== undefined
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
keys(): string[] {
|
|
191
|
-
return this.container.keys()
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
values(): any[] {
|
|
195
|
-
return this.container.values()
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
get size(): number {
|
|
199
|
-
return this.container.size
|
|
200
|
-
}
|
|
201
|
-
}
|
package/src/typed-refs/text.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import type { LoroText } from "loro-crdt"
|
|
2
|
-
import type { TextContainerShape } from "../shape.js"
|
|
3
|
-
import { TypedRef } from "./base.js"
|
|
4
|
-
|
|
5
|
-
// Text typed ref
|
|
6
|
-
export class TextRef extends TypedRef<TextContainerShape> {
|
|
7
|
-
// Track if we've materialized the container (made any changes)
|
|
8
|
-
private _materialized = false
|
|
9
|
-
|
|
10
|
-
protected get container(): LoroText {
|
|
11
|
-
return super.container as LoroText
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
absorbPlainValues() {
|
|
15
|
-
// no plain values contained within
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Text methods
|
|
19
|
-
insert(index: number, content: string): void {
|
|
20
|
-
this._materialized = true
|
|
21
|
-
this.container.insert(index, content)
|
|
22
|
-
this.commitIfAuto()
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
delete(index: number, len: number): void {
|
|
26
|
-
this._materialized = true
|
|
27
|
-
this.container.delete(index, len)
|
|
28
|
-
this.commitIfAuto()
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Returns the text content.
|
|
33
|
-
* If the text hasn't been materialized (no operations performed),
|
|
34
|
-
* returns the placeholder value if available.
|
|
35
|
-
*/
|
|
36
|
-
toString(): string {
|
|
37
|
-
const containerValue = this.container.toString()
|
|
38
|
-
if (containerValue !== "" || this._materialized) {
|
|
39
|
-
return containerValue
|
|
40
|
-
}
|
|
41
|
-
// Return placeholder if available and container is at default state
|
|
42
|
-
if (this.placeholder !== undefined) {
|
|
43
|
-
return this.placeholder as string
|
|
44
|
-
}
|
|
45
|
-
return containerValue
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
valueOf(): string {
|
|
49
|
-
return this.toString()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
toJSON(): string {
|
|
53
|
-
return this.toString()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
[Symbol.toPrimitive](_hint: string): string {
|
|
57
|
-
return this.toString()
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
update(text: string): void {
|
|
61
|
-
this._materialized = true
|
|
62
|
-
this.container.update(text)
|
|
63
|
-
this.commitIfAuto()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
mark(range: { start: number; end: number }, key: string, value: any): void {
|
|
67
|
-
this._materialized = true
|
|
68
|
-
this.container.mark(range, key, value)
|
|
69
|
-
this.commitIfAuto()
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
unmark(range: { start: number; end: number }, key: string): void {
|
|
73
|
-
this._materialized = true
|
|
74
|
-
this.container.unmark(range, key)
|
|
75
|
-
this.commitIfAuto()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
toDelta(): any[] {
|
|
79
|
-
return this.container.toDelta()
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
applyDelta(delta: any[]): void {
|
|
83
|
-
this._materialized = true
|
|
84
|
-
this.container.applyDelta(delta)
|
|
85
|
-
this.commitIfAuto()
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
get length(): number {
|
|
89
|
-
return this.container.length
|
|
90
|
-
}
|
|
91
|
-
}
|