@loro-extended/change 0.6.0 → 0.8.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 +26 -31
- package/dist/index.d.ts +115 -53
- package/dist/index.js +743 -393
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/change.test.ts +97 -403
- package/src/derive-placeholder.test.ts +245 -0
- package/src/derive-placeholder.ts +132 -0
- package/src/discriminated-union.test.ts +74 -1
- package/src/draft-nodes/base.ts +9 -4
- package/src/draft-nodes/counter.md +31 -0
- package/src/draft-nodes/counter.test.ts +53 -0
- package/src/draft-nodes/doc.ts +27 -6
- package/src/draft-nodes/list-base.ts +24 -3
- package/src/draft-nodes/list.test.ts +43 -0
- package/src/draft-nodes/list.ts +3 -0
- package/src/draft-nodes/map.ts +57 -16
- package/src/draft-nodes/movable-list.test.ts +27 -0
- package/src/draft-nodes/movable-list.ts +5 -0
- package/src/draft-nodes/proxy-handlers.ts +87 -0
- package/src/{record.test.ts → draft-nodes/record.test.ts} +98 -18
- package/src/draft-nodes/record.ts +42 -80
- package/src/draft-nodes/utils.ts +46 -5
- package/src/equality.test.ts +19 -0
- package/src/index.ts +14 -7
- package/src/json-patch.test.ts +33 -167
- package/src/overlay.ts +46 -39
- package/src/readonly.test.ts +92 -0
- package/src/shape.ts +131 -73
- package/src/{change.ts → typed-doc.ts} +50 -28
- package/src/types.test.ts +188 -0
- package/src/types.ts +37 -5
- package/src/utils/type-guards.ts +1 -0
- package/src/validation.ts +45 -12
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ import { TypedDoc, Shape } from "@loro-extended/change";
|
|
|
32
32
|
|
|
33
33
|
// Define your document schema
|
|
34
34
|
const schema = Shape.doc({
|
|
35
|
-
title: Shape.text(),
|
|
35
|
+
title: Shape.text().placeholder("My Todo List"),
|
|
36
36
|
todos: Shape.list(
|
|
37
37
|
Shape.plain.object({
|
|
38
38
|
id: Shape.plain.string(),
|
|
@@ -42,14 +42,8 @@ const schema = Shape.doc({
|
|
|
42
42
|
),
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
// Define empty state (default values)
|
|
46
|
-
const emptyState = {
|
|
47
|
-
title: "My Todo List",
|
|
48
|
-
todos: [],
|
|
49
|
-
};
|
|
50
|
-
|
|
51
45
|
// Create a typed document
|
|
52
|
-
const doc = new TypedDoc(schema
|
|
46
|
+
const doc = new TypedDoc(schema);
|
|
53
47
|
|
|
54
48
|
// Make changes with natural syntax
|
|
55
49
|
const result = doc.change((draft) => {
|
|
@@ -109,19 +103,26 @@ const blogSchema = Shape.doc({
|
|
|
109
103
|
Empty state provides default values that are merged when CRDT containers are empty, keeping the whole document typesafe:
|
|
110
104
|
|
|
111
105
|
```typescript
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
106
|
+
// Use .placeholder() to set default values
|
|
107
|
+
const blogSchemaWithDefaults = Shape.doc({
|
|
108
|
+
title: Shape.text().placeholder("Untitled Document"),
|
|
109
|
+
viewCount: Shape.counter(), // defaults to 0
|
|
110
|
+
tags: Shape.list(Shape.plain.string()), // defaults to []
|
|
111
|
+
metadata: Shape.map({
|
|
112
|
+
author: Shape.plain.string().placeholder("Anonymous"),
|
|
113
|
+
publishedAt: Shape.plain.string(), // defaults to ""
|
|
114
|
+
featured: Shape.plain.boolean(), // defaults to false
|
|
115
|
+
}),
|
|
116
|
+
sections: Shape.movableList(
|
|
117
|
+
Shape.map({
|
|
118
|
+
heading: Shape.text(),
|
|
119
|
+
content: Shape.text(),
|
|
120
|
+
order: Shape.plain.number(),
|
|
121
|
+
})
|
|
122
|
+
),
|
|
123
|
+
});
|
|
123
124
|
|
|
124
|
-
const doc = new TypedDoc(
|
|
125
|
+
const doc = new TypedDoc(blogSchemaWithDefaults);
|
|
125
126
|
|
|
126
127
|
// Initially returns empty state
|
|
127
128
|
console.log(doc.value);
|
|
@@ -351,13 +352,13 @@ doc.change((draft) => {
|
|
|
351
352
|
|
|
352
353
|
### Core Functions
|
|
353
354
|
|
|
354
|
-
#### `new TypedDoc<T>(schema,
|
|
355
|
+
#### `new TypedDoc<T>(schema, existingDoc?)`
|
|
355
356
|
|
|
356
357
|
Creates a new typed Loro document.
|
|
357
358
|
|
|
358
359
|
```typescript
|
|
359
|
-
const doc = new TypedDoc(schema
|
|
360
|
-
const docFromExisting = new TypedDoc(schema,
|
|
360
|
+
const doc = new TypedDoc(schema);
|
|
361
|
+
const docFromExisting = new TypedDoc(schema, existingLoroDoc);
|
|
361
362
|
```
|
|
362
363
|
|
|
363
364
|
#### `doc.change(mutator)`
|
|
@@ -571,14 +572,8 @@ const todoSchema = Shape.doc({
|
|
|
571
572
|
),
|
|
572
573
|
});
|
|
573
574
|
|
|
574
|
-
// Define empty state that matches your interface
|
|
575
|
-
const emptyState: TodoDoc = {
|
|
576
|
-
title: "My Todos",
|
|
577
|
-
todos: [],
|
|
578
|
-
};
|
|
579
|
-
|
|
580
575
|
// TypeScript will ensure the schema produces the correct type
|
|
581
|
-
const doc = new TypedDoc(todoSchema
|
|
576
|
+
const doc = new TypedDoc(todoSchema);
|
|
582
577
|
|
|
583
578
|
// The result will be properly typed as TodoDoc
|
|
584
579
|
const result: TodoDoc = doc.change((draft) => {
|
|
@@ -610,7 +605,7 @@ import { LoroDoc } from "loro-crdt";
|
|
|
610
605
|
|
|
611
606
|
// Wrap existing LoroDoc
|
|
612
607
|
const existingDoc = new LoroDoc();
|
|
613
|
-
const typedDoc = new TypedDoc(schema,
|
|
608
|
+
const typedDoc = new TypedDoc(schema, existingDoc);
|
|
614
609
|
|
|
615
610
|
// Access underlying LoroDoc
|
|
616
611
|
const loroDoc = typedDoc.loroDoc;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,53 @@
|
|
|
1
1
|
import { LoroList, LoroMovableList, Container, LoroMap, Value, LoroText, LoroCounter, LoroTree, LoroDoc } from 'loro-crdt';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Infers the plain (JSON-serializable) type from any Shape.
|
|
5
|
+
*
|
|
6
|
+
* This is the recommended way to extract types from shapes.
|
|
7
|
+
* Works with DocShape, ContainerShape, and ValueShape.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const ChatSchema = Shape.doc({
|
|
12
|
+
* messages: Shape.list(Shape.map({
|
|
13
|
+
* id: Shape.plain.string(),
|
|
14
|
+
* content: Shape.text(),
|
|
15
|
+
* })),
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* // Extract the document type
|
|
19
|
+
* type ChatDoc = Infer<typeof ChatSchema>
|
|
20
|
+
* // Result: { messages: { id: string; content: string }[] }
|
|
21
|
+
*
|
|
22
|
+
* const PresenceSchema = Shape.plain.object({
|
|
23
|
+
* name: Shape.plain.string(),
|
|
24
|
+
* cursor: Shape.plain.object({ x: Shape.plain.number(), y: Shape.plain.number() }),
|
|
25
|
+
* })
|
|
26
|
+
*
|
|
27
|
+
* // Extract the presence type
|
|
28
|
+
* type Presence = Infer<typeof PresenceSchema>
|
|
29
|
+
* // Result: { name: string; cursor: { x: number; y: number } }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
type Infer<T> = T extends Shape<infer P, any, any> ? P : never;
|
|
4
33
|
type InferDraftType<T> = T extends Shape<any, infer D, any> ? D : never;
|
|
5
34
|
/**
|
|
6
|
-
* Extracts the valid
|
|
35
|
+
* Extracts the valid placeholder type from a shape.
|
|
7
36
|
*
|
|
8
37
|
* For dynamic containers (list, record, etc.), this will be constrained to
|
|
9
38
|
* empty values ([] or {}) to prevent users from expecting per-entry merging.
|
|
10
39
|
*/
|
|
11
|
-
type
|
|
40
|
+
type InferPlaceholderType<T> = T extends Shape<any, any, infer P> ? P : never;
|
|
12
41
|
type Draft<T extends DocShape<Record<string, ContainerShape>>> = InferDraftType<T>;
|
|
42
|
+
type DeepReadonly<T> = {
|
|
43
|
+
readonly [P in keyof T]: DeepReadonly<T[P]>;
|
|
44
|
+
};
|
|
13
45
|
|
|
14
46
|
type DraftNodeParams<Shape extends DocShape | ContainerShape> = {
|
|
15
47
|
shape: Shape;
|
|
16
|
-
|
|
48
|
+
placeholder?: Infer<Shape>;
|
|
17
49
|
getContainer: () => ShapeToContainer<Shape>;
|
|
50
|
+
readonly?: boolean;
|
|
18
51
|
};
|
|
19
52
|
declare abstract class DraftNode<Shape extends DocShape | ContainerShape> {
|
|
20
53
|
protected _params: DraftNodeParams<Shape>;
|
|
@@ -22,7 +55,8 @@ declare abstract class DraftNode<Shape extends DocShape | ContainerShape> {
|
|
|
22
55
|
constructor(_params: DraftNodeParams<Shape>);
|
|
23
56
|
abstract absorbPlainValues(): void;
|
|
24
57
|
protected get shape(): Shape;
|
|
25
|
-
protected get
|
|
58
|
+
protected get placeholder(): Infer<Shape> | undefined;
|
|
59
|
+
protected get readonly(): boolean;
|
|
26
60
|
protected get container(): ShapeToContainer<Shape>;
|
|
27
61
|
}
|
|
28
62
|
|
|
@@ -43,7 +77,7 @@ declare abstract class ListDraftNodeBase<NestedShape extends ContainerOrValueSha
|
|
|
43
77
|
protected pushWithConversion(item: Item): void;
|
|
44
78
|
getDraftNodeParams(index: number, shape: ContainerShape): DraftNodeParams<ContainerShape>;
|
|
45
79
|
protected getPredicateItem(index: number): Item;
|
|
46
|
-
protected getDraftItem(index: number):
|
|
80
|
+
protected getDraftItem(index: number): any;
|
|
47
81
|
find(predicate: (item: Item, index: number) => boolean): DraftItem | undefined;
|
|
48
82
|
findIndex(predicate: (item: Item, index: number) => boolean): number;
|
|
49
83
|
map<ReturnType>(callback: (item: Item, index: number) => ReturnType): ReturnType[];
|
|
@@ -64,6 +98,7 @@ declare abstract class ListDraftNodeBase<NestedShape extends ContainerOrValueSha
|
|
|
64
98
|
}
|
|
65
99
|
|
|
66
100
|
declare class ListDraftNode<NestedShape extends ContainerOrValueShape> extends ListDraftNodeBase<NestedShape> {
|
|
101
|
+
[index: number]: Infer<NestedShape>;
|
|
67
102
|
protected get container(): LoroList;
|
|
68
103
|
protected absorbValueAtIndex(index: number, value: any): void;
|
|
69
104
|
}
|
|
@@ -75,7 +110,7 @@ declare class MapDraftNode<NestedShapes extends Record<string, ContainerOrValueS
|
|
|
75
110
|
protected get container(): LoroMap;
|
|
76
111
|
absorbPlainValues(): void;
|
|
77
112
|
getDraftNodeParams<S extends ContainerShape>(key: string, shape: S): DraftNodeParams<ContainerShape>;
|
|
78
|
-
getOrCreateNode<Shape extends ContainerShape | ValueShape>(key: string, shape: Shape):
|
|
113
|
+
getOrCreateNode<Shape extends ContainerShape | ValueShape>(key: string, shape: Shape): any;
|
|
79
114
|
private createLazyProperties;
|
|
80
115
|
get(key: string): any;
|
|
81
116
|
set(key: string, value: Value): void;
|
|
@@ -88,6 +123,7 @@ declare class MapDraftNode<NestedShapes extends Record<string, ContainerOrValueS
|
|
|
88
123
|
}
|
|
89
124
|
|
|
90
125
|
declare class MovableListDraftNode<NestedShape extends ContainerOrValueShape, Item = NestedShape["_plain"]> extends ListDraftNodeBase<NestedShape> {
|
|
126
|
+
[index: number]: Infer<NestedShape>;
|
|
91
127
|
protected get container(): LoroMovableList;
|
|
92
128
|
protected absorbValueAtIndex(index: number, value: any): void;
|
|
93
129
|
move(from: number, to: number): void;
|
|
@@ -95,13 +131,13 @@ declare class MovableListDraftNode<NestedShape extends ContainerOrValueShape, It
|
|
|
95
131
|
}
|
|
96
132
|
|
|
97
133
|
declare class RecordDraftNode<NestedShape extends ContainerOrValueShape> extends DraftNode<any> {
|
|
134
|
+
[key: string]: Infer<NestedShape> | any;
|
|
98
135
|
private nodeCache;
|
|
99
|
-
constructor(params: DraftNodeParams<RecordContainerShape<NestedShape>>);
|
|
100
136
|
protected get shape(): RecordContainerShape<NestedShape>;
|
|
101
137
|
protected get container(): LoroMap;
|
|
102
138
|
absorbPlainValues(): void;
|
|
103
139
|
getDraftNodeParams<S extends ContainerShape>(key: string, shape: S): DraftNodeParams<ContainerShape>;
|
|
104
|
-
getOrCreateNode(key: string):
|
|
140
|
+
getOrCreateNode(key: string): any;
|
|
105
141
|
get(key: string): InferDraftType<NestedShape>;
|
|
106
142
|
set(key: string, value: any): void;
|
|
107
143
|
setContainer<C extends Container>(key: string, container: C): C;
|
|
@@ -131,12 +167,15 @@ declare class TextDraftNode extends DraftNode<TextContainerShape> {
|
|
|
131
167
|
get length(): number;
|
|
132
168
|
}
|
|
133
169
|
|
|
170
|
+
type WithPlaceholder<S extends Shape<any, any, any>> = S & {
|
|
171
|
+
placeholder(value: S["_placeholder"]): S;
|
|
172
|
+
};
|
|
134
173
|
interface DocShape<NestedShapes extends Record<string, ContainerShape> = Record<string, ContainerShape>> extends Shape<{
|
|
135
174
|
[K in keyof NestedShapes]: NestedShapes[K]["_plain"];
|
|
136
175
|
}, {
|
|
137
176
|
[K in keyof NestedShapes]: NestedShapes[K]["_draft"];
|
|
138
177
|
}, {
|
|
139
|
-
[K in keyof NestedShapes]: NestedShapes[K]["
|
|
178
|
+
[K in keyof NestedShapes]: NestedShapes[K]["_placeholder"];
|
|
140
179
|
}> {
|
|
141
180
|
readonly _type: "doc";
|
|
142
181
|
readonly shapes: NestedShapes;
|
|
@@ -164,7 +203,7 @@ interface MapContainerShape<NestedShapes extends Record<string, ContainerOrValue
|
|
|
164
203
|
}, MapDraftNode<NestedShapes> & {
|
|
165
204
|
[K in keyof NestedShapes]: NestedShapes[K]["_draft"];
|
|
166
205
|
}, {
|
|
167
|
-
[K in keyof NestedShapes]: NestedShapes[K]["
|
|
206
|
+
[K in keyof NestedShapes]: NestedShapes[K]["_placeholder"];
|
|
168
207
|
}> {
|
|
169
208
|
readonly _type: "map";
|
|
170
209
|
readonly shapes: NestedShapes;
|
|
@@ -205,7 +244,7 @@ interface ObjectValueShape<T extends Record<string, ValueShape> = Record<string,
|
|
|
205
244
|
}, {
|
|
206
245
|
[K in keyof T]: T[K]["_draft"];
|
|
207
246
|
}, {
|
|
208
|
-
[K in keyof T]: T[K]["
|
|
247
|
+
[K in keyof T]: T[K]["_placeholder"];
|
|
209
248
|
}> {
|
|
210
249
|
readonly _type: "value";
|
|
211
250
|
readonly valueType: "object";
|
|
@@ -221,7 +260,7 @@ interface ArrayValueShape<T extends ValueShape = ValueShape> extends Shape<T["_p
|
|
|
221
260
|
readonly valueType: "array";
|
|
222
261
|
readonly shape: T;
|
|
223
262
|
}
|
|
224
|
-
interface UnionValueShape<T extends ValueShape[] = ValueShape[]> extends Shape<T[number]["_plain"], T[number]["_draft"], T[number]["
|
|
263
|
+
interface UnionValueShape<T extends ValueShape[] = ValueShape[]> extends Shape<T[number]["_plain"], T[number]["_draft"], T[number]["_placeholder"]> {
|
|
225
264
|
readonly _type: "value";
|
|
226
265
|
readonly valueType: "union";
|
|
227
266
|
readonly shapes: T;
|
|
@@ -239,7 +278,7 @@ interface UnionValueShape<T extends ValueShape[] = ValueShape[]> extends Shape<T
|
|
|
239
278
|
* @typeParam K - The discriminant key (e.g., "type")
|
|
240
279
|
* @typeParam T - A record mapping discriminant values to their object shapes
|
|
241
280
|
*/
|
|
242
|
-
interface DiscriminatedUnionValueShape<K extends string = string, T extends Record<string, ObjectValueShape> = Record<string, ObjectValueShape>> extends Shape<T[keyof T]["_plain"], T[keyof T]["_draft"], T[keyof T]["
|
|
281
|
+
interface DiscriminatedUnionValueShape<K extends string = string, T extends Record<string, ObjectValueShape> = Record<string, ObjectValueShape>> extends Shape<T[keyof T]["_plain"], T[keyof T]["_draft"], T[keyof T]["_placeholder"]> {
|
|
243
282
|
readonly _type: "value";
|
|
244
283
|
readonly valueType: "discriminatedUnion";
|
|
245
284
|
readonly discriminantKey: K;
|
|
@@ -247,11 +286,11 @@ interface DiscriminatedUnionValueShape<K extends string = string, T extends Reco
|
|
|
247
286
|
}
|
|
248
287
|
type ValueShape = StringValueShape | NumberValueShape | BooleanValueShape | NullValueShape | UndefinedValueShape | Uint8ArrayValueShape | ObjectValueShape | RecordValueShape | ArrayValueShape | UnionValueShape | DiscriminatedUnionValueShape;
|
|
249
288
|
type ContainerOrValueShape = ContainerShape | ValueShape;
|
|
250
|
-
interface Shape<Plain, Draft,
|
|
289
|
+
interface Shape<Plain, Draft, Placeholder = Plain> {
|
|
251
290
|
readonly _type: string;
|
|
252
291
|
readonly _plain: Plain;
|
|
253
292
|
readonly _draft: Draft;
|
|
254
|
-
readonly
|
|
293
|
+
readonly _placeholder: Placeholder;
|
|
255
294
|
}
|
|
256
295
|
/**
|
|
257
296
|
* The LoroShape factory object
|
|
@@ -262,24 +301,24 @@ interface Shape<Plain, Draft, EmptyState = Plain> {
|
|
|
262
301
|
*/
|
|
263
302
|
declare const Shape: {
|
|
264
303
|
doc: <T extends Record<string, ContainerShape>>(shape: T) => DocShape<T>;
|
|
265
|
-
counter: () => CounterContainerShape
|
|
304
|
+
counter: () => WithPlaceholder<CounterContainerShape>;
|
|
266
305
|
list: <T extends ContainerOrValueShape>(shape: T) => ListContainerShape<T>;
|
|
267
306
|
map: <T extends Record<string, ContainerOrValueShape>>(shape: T) => MapContainerShape<T>;
|
|
268
307
|
record: <T extends ContainerOrValueShape>(shape: T) => RecordContainerShape<T>;
|
|
269
308
|
movableList: <T extends ContainerOrValueShape>(shape: T) => MovableListContainerShape<T>;
|
|
270
|
-
text: () => TextContainerShape
|
|
309
|
+
text: () => WithPlaceholder<TextContainerShape>;
|
|
271
310
|
tree: <T extends MapContainerShape>(shape: T) => TreeContainerShape;
|
|
272
311
|
plain: {
|
|
273
|
-
string: <T extends string = string>(...options: T[]) => StringValueShape<T
|
|
274
|
-
number: () => NumberValueShape
|
|
275
|
-
boolean: () => BooleanValueShape
|
|
312
|
+
string: <T extends string = string>(...options: T[]) => WithPlaceholder<StringValueShape<T>>;
|
|
313
|
+
number: () => WithPlaceholder<NumberValueShape>;
|
|
314
|
+
boolean: () => WithPlaceholder<BooleanValueShape>;
|
|
276
315
|
null: () => NullValueShape;
|
|
277
316
|
undefined: () => UndefinedValueShape;
|
|
278
317
|
uint8Array: () => Uint8ArrayValueShape;
|
|
279
318
|
object: <T extends Record<string, ValueShape>>(shape: T) => ObjectValueShape<T>;
|
|
280
319
|
record: <T extends ValueShape>(shape: T) => RecordValueShape<T>;
|
|
281
320
|
array: <T extends ValueShape>(shape: T) => ArrayValueShape<T>;
|
|
282
|
-
union: <T extends ValueShape[]>(shapes: T) => UnionValueShape<T
|
|
321
|
+
union: <T extends ValueShape[]>(shapes: T) => WithPlaceholder<UnionValueShape<T>>;
|
|
283
322
|
/**
|
|
284
323
|
* Creates a discriminated union shape for type-safe tagged unions.
|
|
285
324
|
*
|
|
@@ -306,11 +345,41 @@ declare const Shape: {
|
|
|
306
345
|
* @param discriminantKey - The key used to discriminate between variants (e.g., "type")
|
|
307
346
|
* @param variants - A record mapping discriminant values to their object shapes
|
|
308
347
|
*/
|
|
309
|
-
discriminatedUnion: <K extends string, T extends Record<string, ObjectValueShape>>(discriminantKey: K, variants: T) => DiscriminatedUnionValueShape<K, T
|
|
348
|
+
discriminatedUnion: <K extends string, T extends Record<string, ObjectValueShape>>(discriminantKey: K, variants: T) => WithPlaceholder<DiscriminatedUnionValueShape<K, T>>;
|
|
310
349
|
};
|
|
311
350
|
};
|
|
312
351
|
type ShapeToContainer<T extends DocShape | ContainerShape> = T extends TextContainerShape ? LoroText : T extends CounterContainerShape ? LoroCounter : T extends ListContainerShape ? LoroList : T extends MovableListContainerShape ? LoroMovableList : T extends MapContainerShape | RecordContainerShape ? LoroMap : T extends TreeContainerShape ? LoroTree : never;
|
|
313
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Derives the placeholder state from a schema by composing placeholder values.
|
|
355
|
+
*
|
|
356
|
+
* For leaf nodes (text, counter, values): uses _placeholder directly
|
|
357
|
+
* For containers (map, list, record): recurses into nested shapes
|
|
358
|
+
*/
|
|
359
|
+
declare function derivePlaceholder<T extends DocShape>(schema: T): InferPlaceholderType<T>;
|
|
360
|
+
/**
|
|
361
|
+
* Derives placeholder for a single shape.
|
|
362
|
+
*
|
|
363
|
+
* Leaf nodes: return _placeholder directly
|
|
364
|
+
* Containers: recurse into nested shapes (ignore _placeholder on containers)
|
|
365
|
+
*/
|
|
366
|
+
declare function deriveShapePlaceholder(shape: ContainerOrValueShape): unknown;
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Overlays CRDT state with placeholder defaults
|
|
370
|
+
*/
|
|
371
|
+
declare function overlayPlaceholder<Shape extends DocShape>(shape: Shape, crdtValue: {
|
|
372
|
+
[key: string]: Value;
|
|
373
|
+
}, placeholderValue: {
|
|
374
|
+
[key: string]: Value;
|
|
375
|
+
}): {
|
|
376
|
+
[key: string]: Value;
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* Merges individual CRDT values with placeholder defaults
|
|
380
|
+
*/
|
|
381
|
+
declare function mergeValue<Shape extends ContainerShape | ValueShape>(shape: Shape, crdtValue: Value, placeholderValue: Value): Value;
|
|
382
|
+
|
|
314
383
|
/** biome-ignore-all lint/suspicious/noExplicitAny: JSON Patch values can be any type */
|
|
315
384
|
|
|
316
385
|
type JsonPatchAddOperation = {
|
|
@@ -349,20 +418,28 @@ type JsonPatch = JsonPatchOperation[];
|
|
|
349
418
|
|
|
350
419
|
declare class TypedDoc<Shape extends DocShape> {
|
|
351
420
|
private shape;
|
|
352
|
-
private
|
|
421
|
+
private placeholder;
|
|
353
422
|
private doc;
|
|
354
423
|
/**
|
|
355
|
-
* Creates a new TypedDoc with the given schema
|
|
424
|
+
* Creates a new TypedDoc with the given schema.
|
|
425
|
+
* Placeholder state is automatically derived from the schema's placeholder values.
|
|
356
426
|
*
|
|
357
|
-
* @param shape - The document schema
|
|
358
|
-
* @param emptyState - Default values for the document. For dynamic containers
|
|
359
|
-
* (list, record, etc.), only empty values ([] or {}) are allowed. Use
|
|
360
|
-
* `.change()` to add initial data after construction.
|
|
427
|
+
* @param shape - The document schema (with optional .placeholder() values)
|
|
361
428
|
* @param doc - Optional existing LoroDoc to wrap
|
|
362
429
|
*/
|
|
363
|
-
constructor(shape: Shape,
|
|
364
|
-
|
|
365
|
-
|
|
430
|
+
constructor(shape: Shape, doc?: LoroDoc);
|
|
431
|
+
/**
|
|
432
|
+
* Returns a read-only, live view of the document.
|
|
433
|
+
* Accessing properties on this object will read directly from the underlying CRDT.
|
|
434
|
+
* This is efficient (O(1) per access) and always up-to-date.
|
|
435
|
+
*/
|
|
436
|
+
get value(): DeepReadonly<Infer<Shape>>;
|
|
437
|
+
/**
|
|
438
|
+
* Returns the full plain JavaScript object representation of the document.
|
|
439
|
+
* This is an expensive O(N) operation that serializes the entire document.
|
|
440
|
+
*/
|
|
441
|
+
toJSON(): Infer<Shape>;
|
|
442
|
+
change(fn: (draft: Draft<Shape>) => void): Infer<Shape>;
|
|
366
443
|
/**
|
|
367
444
|
* Apply JSON Patch operations to the document
|
|
368
445
|
*
|
|
@@ -378,32 +455,17 @@ declare class TypedDoc<Shape extends DocShape> {
|
|
|
378
455
|
* ])
|
|
379
456
|
* ```
|
|
380
457
|
*/
|
|
381
|
-
applyPatch(patch: JsonPatch, pathPrefix?: (string | number)[]):
|
|
458
|
+
applyPatch(patch: JsonPatch, pathPrefix?: (string | number)[]): Infer<Shape>;
|
|
382
459
|
get loroDoc(): LoroDoc;
|
|
383
460
|
get docShape(): Shape;
|
|
384
461
|
get rawValue(): any;
|
|
385
462
|
}
|
|
386
|
-
declare function createTypedDoc<Shape extends DocShape>(shape: Shape,
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Overlays CRDT state with empty state defaults
|
|
390
|
-
*/
|
|
391
|
-
declare function overlayEmptyState<Shape extends DocShape>(shape: Shape, crdtValue: {
|
|
392
|
-
[key: string]: Value;
|
|
393
|
-
}, emptyValue: {
|
|
394
|
-
[key: string]: Value;
|
|
395
|
-
}): {
|
|
396
|
-
[key: string]: Value;
|
|
397
|
-
};
|
|
398
|
-
/**
|
|
399
|
-
* Merges individual CRDT values with empty state defaults
|
|
400
|
-
*/
|
|
401
|
-
declare function mergeValue<Shape extends ContainerShape | ValueShape>(shape: Shape, crdtValue: Value, emptyValue: Value): Value;
|
|
463
|
+
declare function createTypedDoc<Shape extends DocShape>(shape: Shape, existingDoc?: LoroDoc): TypedDoc<Shape>;
|
|
402
464
|
|
|
403
465
|
/**
|
|
404
|
-
* Validates
|
|
405
|
-
* Combines the functionality of
|
|
466
|
+
* Validates placeholder against schema structure without using Zod
|
|
467
|
+
* Combines the functionality of createPlaceholderValidator and createValueValidator
|
|
406
468
|
*/
|
|
407
|
-
declare function
|
|
469
|
+
declare function validatePlaceholder<T extends DocShape>(placeholder: unknown, schema: T): Infer<T>;
|
|
408
470
|
|
|
409
|
-
export { type ArrayValueShape, type ContainerOrValueShape, type ContainerShape, type CounterContainerShape, type DiscriminatedUnionValueShape, type DocShape, type Draft, type
|
|
471
|
+
export { type ArrayValueShape, type ContainerOrValueShape, type ContainerShape, type CounterContainerShape, type DeepReadonly, type DiscriminatedUnionValueShape, type DocShape, type Draft, type Infer, type InferDraftType, type InferPlaceholderType, type ListContainerShape, type MapContainerShape, type MovableListContainerShape, type ObjectValueShape, type RecordContainerShape, type RecordValueShape, type ContainerType as RootContainerType, Shape, type TextContainerShape, type TreeContainerShape, TypedDoc, type UnionValueShape, type ValueShape, type WithPlaceholder, createTypedDoc, derivePlaceholder, deriveShapePlaceholder, mergeValue, overlayPlaceholder, validatePlaceholder };
|