@loro-extended/change 0.2.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/LICENSE +21 -0
- package/README.md +565 -0
- package/dist/index.d.ts +339 -0
- package/dist/index.js +1491 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
- package/src/change.test.ts +2006 -0
- package/src/change.ts +105 -0
- package/src/conversion.test.ts +728 -0
- package/src/conversion.ts +220 -0
- package/src/draft-nodes/base.ts +34 -0
- package/src/draft-nodes/counter.ts +21 -0
- package/src/draft-nodes/doc.ts +81 -0
- package/src/draft-nodes/list-base.ts +326 -0
- package/src/draft-nodes/list.ts +18 -0
- package/src/draft-nodes/map.ts +156 -0
- package/src/draft-nodes/movable-list.ts +26 -0
- package/src/draft-nodes/record.ts +215 -0
- package/src/draft-nodes/text.ts +48 -0
- package/src/draft-nodes/tree.ts +31 -0
- package/src/draft-nodes/utils.ts +55 -0
- package/src/index.ts +33 -0
- package/src/json-patch.test.ts +697 -0
- package/src/json-patch.ts +391 -0
- package/src/overlay.ts +90 -0
- package/src/record.test.ts +188 -0
- package/src/schema.fixtures.ts +138 -0
- package/src/shape.ts +348 -0
- package/src/types.ts +15 -0
- package/src/utils/type-guards.ts +210 -0
- package/src/validation.ts +261 -0
package/src/change.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/** biome-ignore-all lint/suspicious/noExplicitAny: fix later */
|
|
2
|
+
|
|
3
|
+
import { LoroDoc } from "loro-crdt"
|
|
4
|
+
import { DraftDoc } from "./draft-nodes/doc.js"
|
|
5
|
+
import {
|
|
6
|
+
type JsonPatch,
|
|
7
|
+
JsonPatchApplicator,
|
|
8
|
+
type JsonPatchOperation,
|
|
9
|
+
normalizePath,
|
|
10
|
+
} from "./json-patch.js"
|
|
11
|
+
import { overlayEmptyState } from "./overlay.js"
|
|
12
|
+
import type { DocShape } from "./shape.js"
|
|
13
|
+
import type { Draft, InferPlainType } from "./types.js"
|
|
14
|
+
import { validateEmptyState } from "./validation.js"
|
|
15
|
+
|
|
16
|
+
// Core TypedDoc abstraction around LoroDoc
|
|
17
|
+
export class TypedDoc<Shape extends DocShape> {
|
|
18
|
+
constructor(
|
|
19
|
+
private shape: Shape,
|
|
20
|
+
private emptyState: InferPlainType<Shape>,
|
|
21
|
+
private doc: LoroDoc = new LoroDoc(),
|
|
22
|
+
) {
|
|
23
|
+
validateEmptyState(emptyState, shape)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get value(): InferPlainType<Shape> {
|
|
27
|
+
const crdtValue = this.doc.toJSON()
|
|
28
|
+
return overlayEmptyState(
|
|
29
|
+
this.shape,
|
|
30
|
+
crdtValue,
|
|
31
|
+
this.emptyState,
|
|
32
|
+
) as InferPlainType<Shape>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
change(fn: (draft: Draft<Shape>) => void): InferPlainType<Shape> {
|
|
36
|
+
// Reuse existing DocumentDraft system with empty state integration
|
|
37
|
+
const draft = new DraftDoc({
|
|
38
|
+
shape: this.shape,
|
|
39
|
+
emptyState: this.emptyState,
|
|
40
|
+
doc: this.doc,
|
|
41
|
+
})
|
|
42
|
+
fn(draft as unknown as Draft<Shape>)
|
|
43
|
+
draft.absorbPlainValues()
|
|
44
|
+
this.doc.commit()
|
|
45
|
+
return this.value
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Apply JSON Patch operations to the document
|
|
50
|
+
*
|
|
51
|
+
* @param patch - Array of JSON Patch operations (RFC 6902)
|
|
52
|
+
* @param pathPrefix - Optional path prefix for scoped operations
|
|
53
|
+
* @returns Updated document value
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const result = typedDoc.applyPatch([
|
|
58
|
+
* { op: 'add', path: '/users/0/name', value: 'Alice' },
|
|
59
|
+
* { op: 'replace', path: '/settings/theme', value: 'dark' }
|
|
60
|
+
* ])
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
applyPatch(
|
|
64
|
+
patch: JsonPatch,
|
|
65
|
+
pathPrefix?: (string | number)[],
|
|
66
|
+
): InferPlainType<Shape> {
|
|
67
|
+
return this.change(draft => {
|
|
68
|
+
const applicator = new JsonPatchApplicator(draft)
|
|
69
|
+
|
|
70
|
+
// Apply path prefix if provided
|
|
71
|
+
const prefixedPatch = pathPrefix
|
|
72
|
+
? patch.map((op: JsonPatchOperation) => ({
|
|
73
|
+
...op,
|
|
74
|
+
path: [...pathPrefix, ...normalizePath(op.path)],
|
|
75
|
+
}))
|
|
76
|
+
: patch
|
|
77
|
+
|
|
78
|
+
applicator.applyPatch(prefixedPatch)
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Expose underlying doc for advanced use cases
|
|
83
|
+
get loroDoc(): LoroDoc {
|
|
84
|
+
return this.doc
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Expose shape for internal use
|
|
88
|
+
get docShape(): Shape {
|
|
89
|
+
return this.shape
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Get raw CRDT value without overlay
|
|
93
|
+
get rawValue(): any {
|
|
94
|
+
return this.doc.toJSON()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Factory function for TypedLoroDoc
|
|
99
|
+
export function createTypedDoc<Shape extends DocShape>(
|
|
100
|
+
shape: Shape,
|
|
101
|
+
emptyState: InferPlainType<Shape>,
|
|
102
|
+
existingDoc?: LoroDoc,
|
|
103
|
+
): TypedDoc<Shape> {
|
|
104
|
+
return new TypedDoc<Shape>(shape, emptyState, existingDoc || new LoroDoc())
|
|
105
|
+
}
|