@loro-extended/change 0.9.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -93
- package/dist/index.d.ts +361 -169
- package/dist/index.js +516 -235
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/change.test.ts +180 -175
- package/src/conversion.test.ts +19 -19
- package/src/conversion.ts +7 -7
- package/src/derive-placeholder.test.ts +14 -14
- package/src/derive-placeholder.ts +3 -3
- package/src/discriminated-union-assignability.test.ts +7 -7
- package/src/discriminated-union-tojson.test.ts +13 -24
- package/src/discriminated-union.test.ts +9 -8
- package/src/equality.test.ts +10 -2
- package/src/functional-helpers.test.ts +149 -0
- package/src/functional-helpers.ts +61 -0
- package/src/grand-unified-api.test.ts +423 -0
- package/src/index.ts +8 -6
- package/src/json-patch.test.ts +64 -56
- package/src/overlay-recursion.test.ts +23 -22
- package/src/overlay.ts +9 -9
- package/src/readonly.test.ts +27 -26
- package/src/shape.ts +103 -21
- package/src/typed-doc.ts +227 -58
- package/src/typed-refs/base.ts +23 -1
- package/src/typed-refs/counter.test.ts +44 -13
- package/src/typed-refs/counter.ts +40 -3
- package/src/typed-refs/doc.ts +12 -6
- package/src/typed-refs/json-compatibility.test.ts +37 -32
- package/src/typed-refs/list-base.ts +26 -22
- package/src/typed-refs/list.test.ts +4 -3
- package/src/typed-refs/movable-list.test.ts +3 -2
- package/src/typed-refs/movable-list.ts +4 -1
- package/src/typed-refs/proxy-handlers.ts +14 -1
- package/src/typed-refs/record.test.ts +107 -42
- package/src/typed-refs/record.ts +37 -19
- package/src/typed-refs/{map.ts → struct.ts} +31 -16
- package/src/typed-refs/text.ts +42 -1
- package/src/typed-refs/utils.ts +28 -6
- package/src/types.test.ts +34 -39
- package/src/types.ts +5 -40
- package/src/utils/type-guards.ts +11 -6
- package/src/validation.ts +10 -10
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LoroList, LoroMovableList, Container, LoroMap, Value, LoroText,
|
|
1
|
+
import { LoroDoc, LoroCounter, LoroList, LoroMovableList, Container, LoroMap, Value, LoroText, LoroTree } from 'loro-crdt';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Infers the plain (JSON-serializable) type from any Shape.
|
|
@@ -9,7 +9,7 @@ import { LoroList, LoroMovableList, Container, LoroMap, Value, LoroText, LoroCou
|
|
|
9
9
|
* @example
|
|
10
10
|
* ```typescript
|
|
11
11
|
* const ChatSchema = Shape.doc({
|
|
12
|
-
* messages: Shape.list(Shape.
|
|
12
|
+
* messages: Shape.list(Shape.struct({
|
|
13
13
|
* id: Shape.plain.string(),
|
|
14
14
|
* content: Shape.text(),
|
|
15
15
|
* })),
|
|
@@ -19,9 +19,9 @@ import { LoroList, LoroMovableList, Container, LoroMap, Value, LoroText, LoroCou
|
|
|
19
19
|
* type ChatDoc = Infer<typeof ChatSchema>
|
|
20
20
|
* // Result: { messages: { id: string; content: string }[] }
|
|
21
21
|
*
|
|
22
|
-
* const PresenceSchema = Shape.plain.
|
|
22
|
+
* const PresenceSchema = Shape.plain.struct({
|
|
23
23
|
* name: Shape.plain.string(),
|
|
24
|
-
* cursor: Shape.plain.
|
|
24
|
+
* cursor: Shape.plain.struct({ x: Shape.plain.number(), y: Shape.plain.number() }),
|
|
25
25
|
* })
|
|
26
26
|
*
|
|
27
27
|
* // Extract the presence type
|
|
@@ -35,10 +35,6 @@ type Infer<T> = T extends Shape<infer P, any, any> ? P : never;
|
|
|
35
35
|
* This is the type used within change() callbacks for mutation.
|
|
36
36
|
*/
|
|
37
37
|
type InferMutableType<T> = T extends Shape<any, infer M, any> ? M : never;
|
|
38
|
-
/**
|
|
39
|
-
* @deprecated Use InferMutableType<T> instead
|
|
40
|
-
*/
|
|
41
|
-
type InferDraftType<T> = InferMutableType<T>;
|
|
42
38
|
/**
|
|
43
39
|
* Extracts the valid placeholder type from a shape.
|
|
44
40
|
*
|
|
@@ -47,53 +43,18 @@ type InferDraftType<T> = InferMutableType<T>;
|
|
|
47
43
|
*/
|
|
48
44
|
type InferPlaceholderType<T> = T extends Shape<any, any, infer P> ? P : never;
|
|
49
45
|
/**
|
|
50
|
-
* Mutable type for use within change() callbacks.
|
|
46
|
+
* Mutable type for use within change() callbacks and direct mutations on doc.value.
|
|
51
47
|
* This is the type-safe wrapper around CRDT containers that allows mutation.
|
|
52
48
|
*/
|
|
53
49
|
type Mutable<T extends DocShape<Record<string, ContainerShape>>> = InferMutableType<T>;
|
|
54
|
-
/**
|
|
55
|
-
* @deprecated Use Mutable<T> instead
|
|
56
|
-
*/
|
|
57
|
-
type Draft<T extends DocShape<Record<string, ContainerShape>>> = Mutable<T>;
|
|
58
|
-
/**
|
|
59
|
-
* Interface for objects that have a toJSON method.
|
|
60
|
-
* This is separate from the data type to avoid polluting Object.values().
|
|
61
|
-
*/
|
|
62
|
-
interface HasToJSON<T> {
|
|
63
|
-
toJSON(): T;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Deep readonly wrapper for plain objects (no index signature).
|
|
67
|
-
* Includes toJSON() method.
|
|
68
|
-
*/
|
|
69
|
-
type DeepReadonlyObject<T extends object> = {
|
|
70
|
-
readonly [P in keyof T]: DeepReadonly<T[P]>;
|
|
71
|
-
} & HasToJSON<T>;
|
|
72
|
-
/**
|
|
73
|
-
* Deep readonly wrapper for Record types (with string index signature).
|
|
74
|
-
* The toJSON() method is available but NOT part of the index signature,
|
|
75
|
-
* so Object.values() returns clean types.
|
|
76
|
-
*/
|
|
77
|
-
type DeepReadonlyRecord<T> = {
|
|
78
|
-
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
79
|
-
} & HasToJSON<Record<string, T[keyof T]>>;
|
|
80
|
-
/**
|
|
81
|
-
* Deep readonly wrapper that makes all properties readonly recursively
|
|
82
|
-
* and adds a toJSON() method for JSON serialization.
|
|
83
|
-
*
|
|
84
|
-
* For arrays: Returns ReadonlyArray with toJSON()
|
|
85
|
-
* For objects with string index signature (Records): toJSON() is available
|
|
86
|
-
* but doesn't pollute Object.values() type inference
|
|
87
|
-
* For plain objects: Returns readonly properties with toJSON()
|
|
88
|
-
* For primitives: Returns as-is
|
|
89
|
-
*/
|
|
90
|
-
type DeepReadonly<T> = T extends any[] ? ReadonlyArray<DeepReadonly<T[number]>> & HasToJSON<T> : T extends object ? string extends keyof T ? DeepReadonlyRecord<T> : DeepReadonlyObject<T> : T;
|
|
91
50
|
|
|
92
51
|
type TypedRefParams<Shape extends DocShape | ContainerShape> = {
|
|
93
52
|
shape: Shape;
|
|
94
53
|
placeholder?: Infer<Shape>;
|
|
95
54
|
getContainer: () => ShapeToContainer<Shape>;
|
|
96
55
|
readonly?: boolean;
|
|
56
|
+
autoCommit?: boolean;
|
|
57
|
+
getDoc?: () => LoroDoc;
|
|
97
58
|
};
|
|
98
59
|
declare abstract class TypedRef<Shape extends DocShape | ContainerShape> {
|
|
99
60
|
protected _params: TypedRefParams<Shape>;
|
|
@@ -103,20 +64,37 @@ declare abstract class TypedRef<Shape extends DocShape | ContainerShape> {
|
|
|
103
64
|
protected get shape(): Shape;
|
|
104
65
|
protected get placeholder(): Infer<Shape> | undefined;
|
|
105
66
|
protected get readonly(): boolean;
|
|
67
|
+
protected get autoCommit(): boolean;
|
|
68
|
+
protected get doc(): LoroDoc | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Commits changes if autoCommit is enabled.
|
|
71
|
+
* Call this after any mutation operation.
|
|
72
|
+
*/
|
|
73
|
+
protected commitIfAuto(): void;
|
|
106
74
|
/**
|
|
107
75
|
* Throws an error if this ref is in readonly mode.
|
|
108
76
|
* Call this at the start of any mutating method.
|
|
77
|
+
* @deprecated Mutations are always allowed now; this will be removed.
|
|
109
78
|
*/
|
|
110
79
|
protected assertMutable(): void;
|
|
111
80
|
protected get container(): ShapeToContainer<Shape>;
|
|
112
81
|
}
|
|
113
82
|
|
|
114
83
|
declare class CounterRef extends TypedRef<CounterContainerShape> {
|
|
84
|
+
private _materialized;
|
|
85
|
+
protected get container(): LoroCounter;
|
|
115
86
|
absorbPlainValues(): void;
|
|
116
|
-
increment(value
|
|
117
|
-
decrement(value
|
|
87
|
+
increment(value?: number): void;
|
|
88
|
+
decrement(value?: number): void;
|
|
89
|
+
/**
|
|
90
|
+
* Returns the counter value.
|
|
91
|
+
* If the counter hasn't been materialized (no operations performed),
|
|
92
|
+
* returns the placeholder value if available.
|
|
93
|
+
*/
|
|
118
94
|
get value(): number;
|
|
95
|
+
valueOf(): number;
|
|
119
96
|
toJSON(): number;
|
|
97
|
+
[Symbol.toPrimitive](hint: string): number | string;
|
|
120
98
|
}
|
|
121
99
|
|
|
122
100
|
declare abstract class ListRefBase<NestedShape extends ContainerOrValueShape, Item = NestedShape["_plain"], MutableItem = NestedShape["_mutable"]> extends TypedRef<any> {
|
|
@@ -158,26 +136,6 @@ declare class ListRef<NestedShape extends ContainerOrValueShape> extends ListRef
|
|
|
158
136
|
protected absorbValueAtIndex(index: number, value: any): void;
|
|
159
137
|
}
|
|
160
138
|
|
|
161
|
-
declare class MapRef<NestedShapes extends Record<string, ContainerOrValueShape>> extends TypedRef<any> {
|
|
162
|
-
private propertyCache;
|
|
163
|
-
constructor(params: TypedRefParams<MapContainerShape<NestedShapes>>);
|
|
164
|
-
protected get shape(): MapContainerShape<NestedShapes>;
|
|
165
|
-
protected get container(): LoroMap;
|
|
166
|
-
absorbPlainValues(): void;
|
|
167
|
-
getTypedRefParams<S extends ContainerShape>(key: string, shape: S): TypedRefParams<ContainerShape>;
|
|
168
|
-
getOrCreateRef<Shape extends ContainerShape | ValueShape>(key: string, shape: Shape): any;
|
|
169
|
-
private createLazyProperties;
|
|
170
|
-
toJSON(): any;
|
|
171
|
-
get(key: string): any;
|
|
172
|
-
set(key: string, value: Value): void;
|
|
173
|
-
setContainer<C extends Container>(key: string, container: C): C;
|
|
174
|
-
delete(key: string): void;
|
|
175
|
-
has(key: string): boolean;
|
|
176
|
-
keys(): string[];
|
|
177
|
-
values(): any[];
|
|
178
|
-
get size(): number;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
139
|
declare class MovableListRef<NestedShape extends ContainerOrValueShape, Item = NestedShape["_plain"]> extends ListRefBase<NestedShape> {
|
|
182
140
|
[index: number]: Infer<NestedShape>;
|
|
183
141
|
protected get container(): LoroMovableList;
|
|
@@ -193,6 +151,16 @@ declare class RecordRef<NestedShape extends ContainerOrValueShape> extends Typed
|
|
|
193
151
|
protected get container(): LoroMap;
|
|
194
152
|
absorbPlainValues(): void;
|
|
195
153
|
getTypedRefParams<S extends ContainerShape>(key: string, shape: S): TypedRefParams<ContainerShape>;
|
|
154
|
+
/**
|
|
155
|
+
* Gets an existing ref for a key, or returns undefined if the key doesn't exist.
|
|
156
|
+
* Used for reading operations where we want optional chaining to work.
|
|
157
|
+
*/
|
|
158
|
+
getRef(key: string): any;
|
|
159
|
+
/**
|
|
160
|
+
* Gets or creates a ref for a key.
|
|
161
|
+
* Always creates the container if it doesn't exist.
|
|
162
|
+
* This is the method used for write operations.
|
|
163
|
+
*/
|
|
196
164
|
getOrCreateRef(key: string): any;
|
|
197
165
|
get(key: string): InferMutableType<NestedShape>;
|
|
198
166
|
set(key: string, value: any): void;
|
|
@@ -202,15 +170,48 @@ declare class RecordRef<NestedShape extends ContainerOrValueShape> extends Typed
|
|
|
202
170
|
keys(): string[];
|
|
203
171
|
values(): any[];
|
|
204
172
|
get size(): number;
|
|
205
|
-
toJSON(): Record<string,
|
|
173
|
+
toJSON(): Record<string, Infer<NestedShape>>;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Typed ref for struct containers (objects with fixed keys).
|
|
178
|
+
* Uses LoroMap as the underlying container.
|
|
179
|
+
*/
|
|
180
|
+
declare class StructRef<NestedShapes extends Record<string, ContainerOrValueShape>> extends TypedRef<any> {
|
|
181
|
+
private propertyCache;
|
|
182
|
+
constructor(params: TypedRefParams<StructContainerShape<NestedShapes>>);
|
|
183
|
+
protected get shape(): StructContainerShape<NestedShapes>;
|
|
184
|
+
protected get container(): LoroMap;
|
|
185
|
+
absorbPlainValues(): void;
|
|
186
|
+
getTypedRefParams<S extends ContainerShape>(key: string, shape: S): TypedRefParams<ContainerShape>;
|
|
187
|
+
getOrCreateRef<Shape extends ContainerShape | ValueShape>(key: string, shape: Shape): any;
|
|
188
|
+
private createLazyProperties;
|
|
189
|
+
toJSON(): Infer<StructContainerShape<NestedShapes>>;
|
|
190
|
+
get(key: string): any;
|
|
191
|
+
set(key: string, value: Value): void;
|
|
192
|
+
setContainer<C extends Container>(key: string, container: C): C;
|
|
193
|
+
delete(key: string): void;
|
|
194
|
+
has(key: string): boolean;
|
|
195
|
+
keys(): string[];
|
|
196
|
+
values(): any[];
|
|
197
|
+
get size(): number;
|
|
206
198
|
}
|
|
207
199
|
|
|
208
200
|
declare class TextRef extends TypedRef<TextContainerShape> {
|
|
201
|
+
private _materialized;
|
|
202
|
+
protected get container(): LoroText;
|
|
209
203
|
absorbPlainValues(): void;
|
|
210
204
|
insert(index: number, content: string): void;
|
|
211
205
|
delete(index: number, len: number): void;
|
|
206
|
+
/**
|
|
207
|
+
* Returns the text content.
|
|
208
|
+
* If the text hasn't been materialized (no operations performed),
|
|
209
|
+
* returns the placeholder value if available.
|
|
210
|
+
*/
|
|
212
211
|
toString(): string;
|
|
212
|
+
valueOf(): string;
|
|
213
213
|
toJSON(): string;
|
|
214
|
+
[Symbol.toPrimitive](_hint: string): string;
|
|
214
215
|
update(text: string): void;
|
|
215
216
|
mark(range: {
|
|
216
217
|
start: number;
|
|
@@ -256,21 +257,30 @@ interface MovableListContainerShape<NestedShape extends ContainerOrValueShape =
|
|
|
256
257
|
readonly _type: "movableList";
|
|
257
258
|
readonly shape: NestedShape;
|
|
258
259
|
}
|
|
259
|
-
|
|
260
|
+
/**
|
|
261
|
+
* @deprecated Use StructContainerShape instead. MapContainerShape is an alias for backward compatibility.
|
|
262
|
+
*/
|
|
263
|
+
type MapContainerShape<NestedShapes extends Record<string, ContainerOrValueShape> = Record<string, ContainerOrValueShape>> = StructContainerShape<NestedShapes>;
|
|
264
|
+
/**
|
|
265
|
+
* Container shape for objects with fixed keys (structs).
|
|
266
|
+
* This is the preferred way to define fixed-key objects.
|
|
267
|
+
* Uses LoroMap as the underlying container.
|
|
268
|
+
*/
|
|
269
|
+
interface StructContainerShape<NestedShapes extends Record<string, ContainerOrValueShape> = Record<string, ContainerOrValueShape>> extends Shape<{
|
|
260
270
|
[K in keyof NestedShapes]: NestedShapes[K]["_plain"];
|
|
261
|
-
},
|
|
271
|
+
}, StructRef<NestedShapes> & {
|
|
262
272
|
[K in keyof NestedShapes]: NestedShapes[K]["_mutable"];
|
|
263
273
|
}, {
|
|
264
274
|
[K in keyof NestedShapes]: NestedShapes[K]["_placeholder"];
|
|
265
275
|
}> {
|
|
266
|
-
readonly _type: "
|
|
276
|
+
readonly _type: "struct";
|
|
267
277
|
readonly shapes: NestedShapes;
|
|
268
278
|
}
|
|
269
279
|
interface RecordContainerShape<NestedShape extends ContainerOrValueShape = ContainerOrValueShape> extends Shape<Record<string, NestedShape["_plain"]>, RecordRef<NestedShape>, Record<string, never>> {
|
|
270
280
|
readonly _type: "record";
|
|
271
281
|
readonly shape: NestedShape;
|
|
272
282
|
}
|
|
273
|
-
type ContainerShape = CounterContainerShape | ListContainerShape |
|
|
283
|
+
type ContainerShape = CounterContainerShape | ListContainerShape | MovableListContainerShape | RecordContainerShape | StructContainerShape | TextContainerShape | TreeContainerShape;
|
|
274
284
|
type ContainerType = ContainerShape["_type"];
|
|
275
285
|
interface StringValueShape<T extends string = string> extends Shape<T, T, T> {
|
|
276
286
|
readonly _type: "value";
|
|
@@ -297,7 +307,16 @@ interface Uint8ArrayValueShape extends Shape<Uint8Array, Uint8Array, Uint8Array>
|
|
|
297
307
|
readonly _type: "value";
|
|
298
308
|
readonly valueType: "uint8array";
|
|
299
309
|
}
|
|
300
|
-
|
|
310
|
+
/**
|
|
311
|
+
* @deprecated Use StructValueShape instead. ObjectValueShape is an alias for backward compatibility.
|
|
312
|
+
*/
|
|
313
|
+
type ObjectValueShape<T extends Record<string, ValueShape> = Record<string, ValueShape>> = StructValueShape<T>;
|
|
314
|
+
/**
|
|
315
|
+
* Value shape for objects with fixed keys (structs).
|
|
316
|
+
* This is the preferred way to define fixed-key plain value objects.
|
|
317
|
+
* Identical structure to ObjectValueShape but with valueType: "struct".
|
|
318
|
+
*/
|
|
319
|
+
interface StructValueShape<T extends Record<string, ValueShape> = Record<string, ValueShape>> extends Shape<{
|
|
301
320
|
[K in keyof T]: T[K]["_plain"];
|
|
302
321
|
}, {
|
|
303
322
|
[K in keyof T]: T[K]["_mutable"];
|
|
@@ -305,7 +324,7 @@ interface ObjectValueShape<T extends Record<string, ValueShape> = Record<string,
|
|
|
305
324
|
[K in keyof T]: T[K]["_placeholder"];
|
|
306
325
|
}> {
|
|
307
326
|
readonly _type: "value";
|
|
308
|
-
readonly valueType: "
|
|
327
|
+
readonly valueType: "struct";
|
|
309
328
|
readonly shape: T;
|
|
310
329
|
}
|
|
311
330
|
interface RecordValueShape<T extends ValueShape = ValueShape> extends Shape<Record<string, T["_plain"]>, Record<string, T["_mutable"]>, Record<string, never>> {
|
|
@@ -336,13 +355,13 @@ interface UnionValueShape<T extends ValueShape[] = ValueShape[]> extends Shape<T
|
|
|
336
355
|
* @typeParam K - The discriminant key (e.g., "type")
|
|
337
356
|
* @typeParam T - A record mapping discriminant values to their object shapes
|
|
338
357
|
*/
|
|
339
|
-
interface DiscriminatedUnionValueShape<K extends string = string, T extends Record<string,
|
|
358
|
+
interface DiscriminatedUnionValueShape<K extends string = string, T extends Record<string, StructValueShape> = Record<string, StructValueShape>, Plain = T[keyof T]["_plain"], Mutable = T[keyof T]["_mutable"], Placeholder = T[keyof T]["_placeholder"]> extends Shape<Plain, Mutable, Placeholder> {
|
|
340
359
|
readonly _type: "value";
|
|
341
360
|
readonly valueType: "discriminatedUnion";
|
|
342
361
|
readonly discriminantKey: K;
|
|
343
362
|
readonly variants: T;
|
|
344
363
|
}
|
|
345
|
-
type ValueShape = StringValueShape | NumberValueShape | BooleanValueShape | NullValueShape | UndefinedValueShape | Uint8ArrayValueShape |
|
|
364
|
+
type ValueShape = StringValueShape | NumberValueShape | BooleanValueShape | NullValueShape | UndefinedValueShape | Uint8ArrayValueShape | StructValueShape | RecordValueShape | ArrayValueShape | UnionValueShape | DiscriminatedUnionValueShape;
|
|
346
365
|
type ContainerOrValueShape = ContainerShape | ValueShape;
|
|
347
366
|
interface Shape<Plain, Mutable, Placeholder = Plain> {
|
|
348
367
|
readonly _type: string;
|
|
@@ -361,11 +380,29 @@ declare const Shape: {
|
|
|
361
380
|
doc: <T extends Record<string, ContainerShape>>(shape: T) => DocShape<T>;
|
|
362
381
|
counter: () => WithPlaceholder<CounterContainerShape>;
|
|
363
382
|
list: <T extends ContainerOrValueShape>(shape: T) => ListContainerShape<T>;
|
|
364
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Creates a struct container shape for objects with fixed keys.
|
|
385
|
+
* This is the preferred way to define fixed-key objects.
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* ```typescript
|
|
389
|
+
* const UserSchema = Shape.doc({
|
|
390
|
+
* user: Shape.struct({
|
|
391
|
+
* name: Shape.text(),
|
|
392
|
+
* age: Shape.counter(),
|
|
393
|
+
* }),
|
|
394
|
+
* })
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
struct: <T extends Record<string, ContainerOrValueShape>>(shape: T) => StructContainerShape<T>;
|
|
398
|
+
/**
|
|
399
|
+
* @deprecated Use `Shape.struct` instead. `Shape.struct` will be removed in a future version.
|
|
400
|
+
*/
|
|
401
|
+
map: <T extends Record<string, ContainerOrValueShape>>(shape: T) => StructContainerShape<T>;
|
|
365
402
|
record: <T extends ContainerOrValueShape>(shape: T) => RecordContainerShape<T>;
|
|
366
403
|
movableList: <T extends ContainerOrValueShape>(shape: T) => MovableListContainerShape<T>;
|
|
367
404
|
text: () => WithPlaceholder<TextContainerShape>;
|
|
368
|
-
tree: <T extends MapContainerShape>(shape: T) => TreeContainerShape;
|
|
405
|
+
tree: <T extends MapContainerShape | StructContainerShape>(shape: T) => TreeContainerShape;
|
|
369
406
|
plain: {
|
|
370
407
|
string: <T extends string = string>(...options: T[]) => WithPlaceholder<StringValueShape<T>>;
|
|
371
408
|
number: () => WithPlaceholder<NumberValueShape>;
|
|
@@ -373,7 +410,23 @@ declare const Shape: {
|
|
|
373
410
|
null: () => NullValueShape;
|
|
374
411
|
undefined: () => UndefinedValueShape;
|
|
375
412
|
uint8Array: () => Uint8ArrayValueShape;
|
|
376
|
-
|
|
413
|
+
/**
|
|
414
|
+
* Creates a struct value shape for plain objects with fixed keys.
|
|
415
|
+
* This is the preferred way to define fixed-key plain value objects.
|
|
416
|
+
*
|
|
417
|
+
* @example
|
|
418
|
+
* ```typescript
|
|
419
|
+
* const PointSchema = Shape.plain.struct({
|
|
420
|
+
* x: Shape.plain.number(),
|
|
421
|
+
* y: Shape.plain.number(),
|
|
422
|
+
* })
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
struct: <T extends Record<string, ValueShape>>(shape: T) => StructValueShape<T>;
|
|
426
|
+
/**
|
|
427
|
+
* @deprecated Use `Shape.plain.struct` instead. `Shape.plain.struct` will be removed in a future version.
|
|
428
|
+
*/
|
|
429
|
+
object: <T extends Record<string, ValueShape>>(shape: T) => StructValueShape<T>;
|
|
377
430
|
record: <T extends ValueShape>(shape: T) => RecordValueShape<T>;
|
|
378
431
|
array: <T extends ValueShape>(shape: T) => ArrayValueShape<T>;
|
|
379
432
|
union: <T extends ValueShape[]>(shapes: T) => WithPlaceholder<UnionValueShape<T>>;
|
|
@@ -382,15 +435,15 @@ declare const Shape: {
|
|
|
382
435
|
*
|
|
383
436
|
* @example
|
|
384
437
|
* ```typescript
|
|
385
|
-
* const ClientPresenceShape = Shape.plain.
|
|
438
|
+
* const ClientPresenceShape = Shape.plain.struct({
|
|
386
439
|
* type: Shape.plain.string("client"),
|
|
387
440
|
* name: Shape.plain.string(),
|
|
388
|
-
* input: Shape.plain.
|
|
441
|
+
* input: Shape.plain.struct({ force: Shape.plain.number(), angle: Shape.plain.number() }),
|
|
389
442
|
* })
|
|
390
443
|
*
|
|
391
|
-
* const ServerPresenceShape = Shape.plain.
|
|
444
|
+
* const ServerPresenceShape = Shape.plain.struct({
|
|
392
445
|
* type: Shape.plain.string("server"),
|
|
393
|
-
* cars: Shape.plain.record(Shape.plain.
|
|
446
|
+
* cars: Shape.plain.record(Shape.plain.struct({ x: Shape.plain.number(), y: Shape.plain.number() })),
|
|
394
447
|
* tick: Shape.plain.number(),
|
|
395
448
|
* })
|
|
396
449
|
*
|
|
@@ -403,10 +456,10 @@ declare const Shape: {
|
|
|
403
456
|
* @param discriminantKey - The key used to discriminate between variants (e.g., "type")
|
|
404
457
|
* @param variants - A record mapping discriminant values to their object shapes
|
|
405
458
|
*/
|
|
406
|
-
discriminatedUnion: <K extends string, T extends Record<string,
|
|
459
|
+
discriminatedUnion: <K extends string, T extends Record<string, StructValueShape>>(discriminantKey: K, variants: T) => WithPlaceholder<DiscriminatedUnionValueShape<K, T>>;
|
|
407
460
|
};
|
|
408
461
|
};
|
|
409
|
-
type ShapeToContainer<T extends DocShape | ContainerShape> = T extends TextContainerShape ? LoroText : T extends CounterContainerShape ? LoroCounter : T extends ListContainerShape ? LoroList : T extends MovableListContainerShape ? LoroMovableList : T extends
|
|
462
|
+
type ShapeToContainer<T extends DocShape | ContainerShape> = T extends TextContainerShape ? LoroText : T extends CounterContainerShape ? LoroCounter : T extends ListContainerShape ? LoroList : T extends MovableListContainerShape ? LoroMovableList : T extends StructContainerShape | RecordContainerShape ? LoroMap : T extends TreeContainerShape ? LoroTree : never;
|
|
410
463
|
|
|
411
464
|
/**
|
|
412
465
|
* Derives the placeholder state from a schema by composing placeholder values.
|
|
@@ -423,6 +476,227 @@ declare function derivePlaceholder<T extends DocShape>(schema: T): InferPlacehol
|
|
|
423
476
|
*/
|
|
424
477
|
declare function deriveShapePlaceholder(shape: ContainerOrValueShape): unknown;
|
|
425
478
|
|
|
479
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: JSON Patch values can be any type */
|
|
480
|
+
|
|
481
|
+
type JsonPatchAddOperation = {
|
|
482
|
+
op: "add";
|
|
483
|
+
path: string | (string | number)[];
|
|
484
|
+
value: any;
|
|
485
|
+
};
|
|
486
|
+
type JsonPatchRemoveOperation = {
|
|
487
|
+
op: "remove";
|
|
488
|
+
path: string | (string | number)[];
|
|
489
|
+
};
|
|
490
|
+
type JsonPatchReplaceOperation = {
|
|
491
|
+
op: "replace";
|
|
492
|
+
path: string | (string | number)[];
|
|
493
|
+
value: any;
|
|
494
|
+
};
|
|
495
|
+
type JsonPatchMoveOperation = {
|
|
496
|
+
op: "move";
|
|
497
|
+
path: string | (string | number)[];
|
|
498
|
+
from: string | (string | number)[];
|
|
499
|
+
};
|
|
500
|
+
type JsonPatchCopyOperation = {
|
|
501
|
+
op: "copy";
|
|
502
|
+
path: string | (string | number)[];
|
|
503
|
+
from: string | (string | number)[];
|
|
504
|
+
};
|
|
505
|
+
type JsonPatchTestOperation = {
|
|
506
|
+
op: "test";
|
|
507
|
+
path: string | (string | number)[];
|
|
508
|
+
value: any;
|
|
509
|
+
};
|
|
510
|
+
type JsonPatchOperation = JsonPatchAddOperation | JsonPatchRemoveOperation | JsonPatchReplaceOperation | JsonPatchMoveOperation | JsonPatchCopyOperation | JsonPatchTestOperation;
|
|
511
|
+
type JsonPatch = JsonPatchOperation[];
|
|
512
|
+
|
|
513
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: fix later */
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Meta-operations namespace for TypedDoc.
|
|
517
|
+
* Access via doc.$ to perform batch operations, serialization, etc.
|
|
518
|
+
*/
|
|
519
|
+
declare class TypedDocMeta<Shape extends DocShape> {
|
|
520
|
+
private internal;
|
|
521
|
+
constructor(internal: TypedDocInternal<Shape>);
|
|
522
|
+
/**
|
|
523
|
+
* The primary method of mutating typed documents.
|
|
524
|
+
* Batches multiple mutations into a single transaction.
|
|
525
|
+
* All changes commit together at the end.
|
|
526
|
+
*
|
|
527
|
+
* Use this for:
|
|
528
|
+
* - Find-and-mutate operations (required due to JS limitations)
|
|
529
|
+
* - Performance (fewer commits)
|
|
530
|
+
* - Atomic undo (all changes = one undo step)
|
|
531
|
+
*
|
|
532
|
+
* Returns the doc for chaining.
|
|
533
|
+
*/
|
|
534
|
+
change(fn: (draft: Mutable<Shape>) => void): TypedDoc<Shape>;
|
|
535
|
+
/**
|
|
536
|
+
* Returns the full plain JavaScript object representation of the document.
|
|
537
|
+
* This is an expensive O(N) operation that serializes the entire document.
|
|
538
|
+
*/
|
|
539
|
+
toJSON(): Infer<Shape>;
|
|
540
|
+
/**
|
|
541
|
+
* Apply JSON Patch operations to the document
|
|
542
|
+
*
|
|
543
|
+
* @param patch - Array of JSON Patch operations (RFC 6902)
|
|
544
|
+
* @param pathPrefix - Optional path prefix for scoped operations
|
|
545
|
+
* @returns Updated document value
|
|
546
|
+
*/
|
|
547
|
+
applyPatch(patch: JsonPatch, pathPrefix?: (string | number)[]): TypedDoc<Shape>;
|
|
548
|
+
/**
|
|
549
|
+
* Access the underlying LoroDoc for advanced operations.
|
|
550
|
+
*/
|
|
551
|
+
get loroDoc(): LoroDoc;
|
|
552
|
+
/**
|
|
553
|
+
* Access the document schema shape.
|
|
554
|
+
*/
|
|
555
|
+
get docShape(): Shape;
|
|
556
|
+
/**
|
|
557
|
+
* Get raw CRDT value without placeholder overlay.
|
|
558
|
+
*/
|
|
559
|
+
get rawValue(): any;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Internal TypedDoc implementation (not directly exposed to users).
|
|
563
|
+
* Users interact with the proxied version that provides direct schema access.
|
|
564
|
+
*/
|
|
565
|
+
declare class TypedDocInternal<Shape extends DocShape> {
|
|
566
|
+
private shape;
|
|
567
|
+
private placeholder;
|
|
568
|
+
private doc;
|
|
569
|
+
private _valueRef;
|
|
570
|
+
proxy: TypedDoc<Shape> | null;
|
|
571
|
+
constructor(shape: Shape, doc?: LoroDoc);
|
|
572
|
+
get value(): Mutable<Shape>;
|
|
573
|
+
toJSON(): Infer<Shape>;
|
|
574
|
+
change(fn: (draft: Mutable<Shape>) => void): void;
|
|
575
|
+
applyPatch(patch: JsonPatch, pathPrefix?: (string | number)[]): void;
|
|
576
|
+
get loroDoc(): LoroDoc;
|
|
577
|
+
get docShape(): Shape;
|
|
578
|
+
get rawValue(): any;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* The proxied TypedDoc type that provides direct schema access.
|
|
582
|
+
* Schema properties are accessed directly on the doc object.
|
|
583
|
+
* Meta-operations are available via the $ namespace.
|
|
584
|
+
*
|
|
585
|
+
* @example
|
|
586
|
+
* ```typescript
|
|
587
|
+
* const doc = createTypedDoc(schema);
|
|
588
|
+
*
|
|
589
|
+
* // Direct schema access
|
|
590
|
+
* doc.count.increment(5);
|
|
591
|
+
* doc.title.insert(0, "Hello");
|
|
592
|
+
*
|
|
593
|
+
* // Serialize to JSON (works on doc and all refs)
|
|
594
|
+
* const snapshot = doc.toJSON();
|
|
595
|
+
* const users = doc.users.toJSON();
|
|
596
|
+
*
|
|
597
|
+
* // Meta-operations via $ (escape hatch)
|
|
598
|
+
* doc.$.change(draft => { ... });
|
|
599
|
+
* doc.$.loroDoc;
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
type TypedDoc<Shape extends DocShape> = Mutable<Shape> & {
|
|
603
|
+
/**
|
|
604
|
+
* Meta-operations namespace.
|
|
605
|
+
* Use for change(), loroDoc, etc.
|
|
606
|
+
*/
|
|
607
|
+
$: TypedDocMeta<Shape>;
|
|
608
|
+
/**
|
|
609
|
+
* Returns the full plain JavaScript object representation of the document.
|
|
610
|
+
* This is an O(N) operation that serializes the entire document.
|
|
611
|
+
*
|
|
612
|
+
* @example
|
|
613
|
+
* ```typescript
|
|
614
|
+
* const snapshot = doc.toJSON();
|
|
615
|
+
* console.log(snapshot.count); // number
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
618
|
+
toJSON(): Infer<Shape>;
|
|
619
|
+
};
|
|
620
|
+
/**
|
|
621
|
+
* Creates a new TypedDoc with the given schema.
|
|
622
|
+
* Returns a proxied document where schema properties are accessed directly.
|
|
623
|
+
*
|
|
624
|
+
* @param shape - The document schema (with optional .placeholder() values)
|
|
625
|
+
* @param existingDoc - Optional existing LoroDoc to wrap
|
|
626
|
+
* @returns A proxied TypedDoc with direct schema access and $ namespace
|
|
627
|
+
*
|
|
628
|
+
* @example
|
|
629
|
+
* ```typescript
|
|
630
|
+
* const schema = Shape.doc({
|
|
631
|
+
* title: Shape.text(),
|
|
632
|
+
* count: Shape.counter(),
|
|
633
|
+
* });
|
|
634
|
+
*
|
|
635
|
+
* const doc = createTypedDoc(schema);
|
|
636
|
+
*
|
|
637
|
+
* // Direct mutations (auto-commit)
|
|
638
|
+
* doc.count.increment(5);
|
|
639
|
+
* doc.title.insert(0, "Hello");
|
|
640
|
+
*
|
|
641
|
+
* // Batched mutations are committed together via `change`
|
|
642
|
+
* doc.$.change(draft => {
|
|
643
|
+
* draft.count.increment(10);
|
|
644
|
+
* draft.title.update("World");
|
|
645
|
+
* });
|
|
646
|
+
*
|
|
647
|
+
* // Get plain JSON
|
|
648
|
+
* const snapshot = doc.toJSON();
|
|
649
|
+
* ```
|
|
650
|
+
*/
|
|
651
|
+
declare function createTypedDoc<Shape extends DocShape>(shape: Shape, existingDoc?: LoroDoc): TypedDoc<Shape>;
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* The primary method of mutating typed documents.
|
|
655
|
+
* Batches multiple mutations into a single transaction.
|
|
656
|
+
* All changes commit together at the end.
|
|
657
|
+
*
|
|
658
|
+
* Use this for:
|
|
659
|
+
* - Find-and-mutate operations (required due to JS limitations)
|
|
660
|
+
* - Performance (fewer commits)
|
|
661
|
+
* - Atomic undo (all changes = one undo step)
|
|
662
|
+
*
|
|
663
|
+
* Returns the doc for chaining.
|
|
664
|
+
*
|
|
665
|
+
* @param doc - The TypedDoc to mutate
|
|
666
|
+
* @param fn - Function that performs mutations on the draft
|
|
667
|
+
* @returns The same TypedDoc for chaining
|
|
668
|
+
*
|
|
669
|
+
* @example
|
|
670
|
+
* ```typescript
|
|
671
|
+
* import { change } from "@loro-extended/change"
|
|
672
|
+
*
|
|
673
|
+
* // Chainable API
|
|
674
|
+
* change(doc, draft => {
|
|
675
|
+
* draft.count.increment(10)
|
|
676
|
+
* draft.title.update("Hello")
|
|
677
|
+
* })
|
|
678
|
+
* .count.increment(5) // Optional: continue mutating
|
|
679
|
+
* .toJSON() // Optional: get last item snapshot when needed
|
|
680
|
+
* ```
|
|
681
|
+
*/
|
|
682
|
+
declare function change<Shape extends DocShape>(doc: TypedDoc<Shape>, fn: (draft: Mutable<Shape>) => void): TypedDoc<Shape>;
|
|
683
|
+
/**
|
|
684
|
+
* Access the underlying LoroDoc for advanced operations.
|
|
685
|
+
*
|
|
686
|
+
* @param doc - The TypedDoc to unwrap
|
|
687
|
+
* @returns The underlying LoroDoc instance
|
|
688
|
+
*
|
|
689
|
+
* @example
|
|
690
|
+
* ```typescript
|
|
691
|
+
* import { getLoroDoc } from "@loro-extended/change"
|
|
692
|
+
*
|
|
693
|
+
* const loroDoc = getLoroDoc(doc)
|
|
694
|
+
* const version = loroDoc.version()
|
|
695
|
+
* loroDoc.subscribe(() => console.log("changed"))
|
|
696
|
+
* ```
|
|
697
|
+
*/
|
|
698
|
+
declare function getLoroDoc<Shape extends DocShape>(doc: TypedDoc<Shape>): LoroDoc;
|
|
699
|
+
|
|
426
700
|
/**
|
|
427
701
|
* Overlays CRDT state with placeholder defaults
|
|
428
702
|
*/
|
|
@@ -491,88 +765,6 @@ interface PresenceInterface {
|
|
|
491
765
|
subscribe: (cb: (values: ObjectValue) => void) => () => void;
|
|
492
766
|
}
|
|
493
767
|
|
|
494
|
-
/** biome-ignore-all lint/suspicious/noExplicitAny: JSON Patch values can be any type */
|
|
495
|
-
|
|
496
|
-
type JsonPatchAddOperation = {
|
|
497
|
-
op: "add";
|
|
498
|
-
path: string | (string | number)[];
|
|
499
|
-
value: any;
|
|
500
|
-
};
|
|
501
|
-
type JsonPatchRemoveOperation = {
|
|
502
|
-
op: "remove";
|
|
503
|
-
path: string | (string | number)[];
|
|
504
|
-
};
|
|
505
|
-
type JsonPatchReplaceOperation = {
|
|
506
|
-
op: "replace";
|
|
507
|
-
path: string | (string | number)[];
|
|
508
|
-
value: any;
|
|
509
|
-
};
|
|
510
|
-
type JsonPatchMoveOperation = {
|
|
511
|
-
op: "move";
|
|
512
|
-
path: string | (string | number)[];
|
|
513
|
-
from: string | (string | number)[];
|
|
514
|
-
};
|
|
515
|
-
type JsonPatchCopyOperation = {
|
|
516
|
-
op: "copy";
|
|
517
|
-
path: string | (string | number)[];
|
|
518
|
-
from: string | (string | number)[];
|
|
519
|
-
};
|
|
520
|
-
type JsonPatchTestOperation = {
|
|
521
|
-
op: "test";
|
|
522
|
-
path: string | (string | number)[];
|
|
523
|
-
value: any;
|
|
524
|
-
};
|
|
525
|
-
type JsonPatchOperation = JsonPatchAddOperation | JsonPatchRemoveOperation | JsonPatchReplaceOperation | JsonPatchMoveOperation | JsonPatchCopyOperation | JsonPatchTestOperation;
|
|
526
|
-
type JsonPatch = JsonPatchOperation[];
|
|
527
|
-
|
|
528
|
-
/** biome-ignore-all lint/suspicious/noExplicitAny: fix later */
|
|
529
|
-
|
|
530
|
-
declare class TypedDoc<Shape extends DocShape> {
|
|
531
|
-
private shape;
|
|
532
|
-
private placeholder;
|
|
533
|
-
private doc;
|
|
534
|
-
/**
|
|
535
|
-
* Creates a new TypedDoc with the given schema.
|
|
536
|
-
* Placeholder state is automatically derived from the schema's placeholder values.
|
|
537
|
-
*
|
|
538
|
-
* @param shape - The document schema (with optional .placeholder() values)
|
|
539
|
-
* @param doc - Optional existing LoroDoc to wrap
|
|
540
|
-
*/
|
|
541
|
-
constructor(shape: Shape, doc?: LoroDoc);
|
|
542
|
-
/**
|
|
543
|
-
* Returns a read-only, live view of the document.
|
|
544
|
-
* Accessing properties on this object will read directly from the underlying CRDT.
|
|
545
|
-
* This is efficient (O(1) per access) and always up-to-date.
|
|
546
|
-
*/
|
|
547
|
-
get value(): DeepReadonly<Infer<Shape>>;
|
|
548
|
-
/**
|
|
549
|
-
* Returns the full plain JavaScript object representation of the document.
|
|
550
|
-
* This is an expensive O(N) operation that serializes the entire document.
|
|
551
|
-
*/
|
|
552
|
-
toJSON(): Infer<Shape>;
|
|
553
|
-
change(fn: (draft: Draft<Shape>) => void): Infer<Shape>;
|
|
554
|
-
/**
|
|
555
|
-
* Apply JSON Patch operations to the document
|
|
556
|
-
*
|
|
557
|
-
* @param patch - Array of JSON Patch operations (RFC 6902)
|
|
558
|
-
* @param pathPrefix - Optional path prefix for scoped operations
|
|
559
|
-
* @returns Updated document value
|
|
560
|
-
*
|
|
561
|
-
* @example
|
|
562
|
-
* ```typescript
|
|
563
|
-
* const result = typedDoc.applyPatch([
|
|
564
|
-
* { op: 'add', path: '/users/0/name', value: 'Alice' },
|
|
565
|
-
* { op: 'replace', path: '/settings/theme', value: 'dark' }
|
|
566
|
-
* ])
|
|
567
|
-
* ```
|
|
568
|
-
*/
|
|
569
|
-
applyPatch(patch: JsonPatch, pathPrefix?: (string | number)[]): Infer<Shape>;
|
|
570
|
-
get loroDoc(): LoroDoc;
|
|
571
|
-
get docShape(): Shape;
|
|
572
|
-
get rawValue(): any;
|
|
573
|
-
}
|
|
574
|
-
declare function createTypedDoc<Shape extends DocShape>(shape: Shape, existingDoc?: LoroDoc): TypedDoc<Shape>;
|
|
575
|
-
|
|
576
768
|
/**
|
|
577
769
|
* A strongly-typed wrapper around a PresenceInterface.
|
|
578
770
|
* Provides type-safe access to presence data with automatic placeholder merging.
|
|
@@ -624,4 +816,4 @@ declare class TypedPresence<S extends ContainerShape | ValueShape> {
|
|
|
624
816
|
*/
|
|
625
817
|
declare function validatePlaceholder<T extends DocShape>(placeholder: unknown, schema: T): Infer<T>;
|
|
626
818
|
|
|
627
|
-
export { type ArrayValueShape, type ContainerOrValueShape, type ContainerShape, type CounterContainerShape, type
|
|
819
|
+
export { type ArrayValueShape, type ContainerOrValueShape, type ContainerShape, type CounterContainerShape, type DiscriminatedUnionValueShape, type DocShape, type Infer, type InferMutableType, type InferPlaceholderType, type ListContainerShape, type MapContainerShape, type MovableListContainerShape, type Mutable, type ObjectValue, type ObjectValueShape, type PresenceInterface, type RecordContainerShape, type RecordValueShape, type ContainerType as RootContainerType, Shape, type StructContainerShape, type StructValueShape, type TextContainerShape, type TreeContainerShape, type TypedDoc, TypedPresence, type UnionValueShape, type ValueShape, type WithPlaceholder, change, createPlaceholderProxy, createTypedDoc, derivePlaceholder, deriveShapePlaceholder, getLoroDoc, mergeValue, overlayPlaceholder, validatePlaceholder };
|