@loro-extended/change 0.9.0 → 0.9.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.
@@ -1,3 +1,12 @@
1
+ import {
2
+ LoroCounter,
3
+ LoroList,
4
+ LoroMap,
5
+ LoroMovableList,
6
+ LoroText,
7
+ LoroTree,
8
+ type Value,
9
+ } from "loro-crdt"
1
10
  import type {
2
11
  ContainerShape,
3
12
  CounterContainerShape,
@@ -8,7 +17,7 @@ import type {
8
17
  TextContainerShape,
9
18
  TreeContainerShape,
10
19
  } from "../shape.js"
11
- import type { TypedRef, TypedRefParams } from "./base.js"
20
+ import { TypedRef, type TypedRefParams } from "./base.js"
12
21
  import { CounterRef } from "./counter.js"
13
22
  import { ListRef } from "./list.js"
14
23
  import { MapRef } from "./map.js"
@@ -22,6 +31,82 @@ import { RecordRef } from "./record.js"
22
31
  import { TextRef } from "./text.js"
23
32
  import { TreeRef } from "./tree.js"
24
33
 
34
+ /**
35
+ * Mapping from container shape types to their Loro constructor classes.
36
+ * Used when creating new containers via getOrCreateContainer().
37
+ */
38
+ export const containerConstructor = {
39
+ counter: LoroCounter,
40
+ list: LoroList,
41
+ map: LoroMap,
42
+ movableList: LoroMovableList,
43
+ record: LoroMap, // Records use LoroMap as their underlying container
44
+ text: LoroText,
45
+ tree: LoroTree,
46
+ } as const
47
+
48
+ /**
49
+ * Unwraps a TypedRef to its primitive value for readonly access.
50
+ * Counter refs return their numeric value, Text refs return their string.
51
+ * Other container types are returned as-is.
52
+ */
53
+ export function unwrapReadonlyPrimitive(
54
+ ref: TypedRef<any>,
55
+ shape: ContainerShape,
56
+ ): any {
57
+ if (shape._type === "counter") {
58
+ return (ref as any).value
59
+ }
60
+ if (shape._type === "text") {
61
+ return (ref as any).toString()
62
+ }
63
+ return ref
64
+ }
65
+
66
+ /**
67
+ * Absorbs cached plain values back into a LoroMap container.
68
+ * For TypedRef entries, recursively calls absorbPlainValues().
69
+ * For plain Value entries, sets them directly on the container.
70
+ */
71
+ export function absorbCachedPlainValues(
72
+ cache: Map<string, TypedRef<ContainerShape> | Value>,
73
+ getContainer: () => LoroMap,
74
+ ): void {
75
+ let container: LoroMap | undefined
76
+
77
+ for (const [key, ref] of cache.entries()) {
78
+ if (ref instanceof TypedRef) {
79
+ // Contains a TypedRef, not a plain Value: keep recursing
80
+ ref.absorbPlainValues()
81
+ } else {
82
+ // Plain value!
83
+ if (!container) container = getContainer()
84
+ container.set(key, ref)
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Serializes a TypedRef to JSON by iterating over its keys.
91
+ * For nested TypedRefs with toJSON(), calls their toJSON method.
92
+ * For plain values, includes them directly.
93
+ */
94
+ export function serializeRefToJSON(
95
+ ref: Record<string, any>,
96
+ keys: Iterable<string>,
97
+ ): Record<string, any> {
98
+ const result: Record<string, any> = {}
99
+ for (const key of keys) {
100
+ const value = ref[key]
101
+ if (value && typeof value === "object" && "toJSON" in value) {
102
+ result[key] = value.toJSON()
103
+ } else {
104
+ result[key] = value
105
+ }
106
+ }
107
+ return result
108
+ }
109
+
25
110
  // Generic catch-all overload
26
111
  export function createContainerTypedRef<T extends ContainerShape>(
27
112
  params: TypedRefParams<T>,
@@ -63,26 +148,26 @@ export function createContainerTypedRef(
63
148
  }
64
149
 
65
150
  export function assignPlainValueToTypedRef(
66
- node: TypedRef<any>,
151
+ ref: TypedRef<any>,
67
152
  value: any,
68
153
  ): boolean {
69
- const shapeType = (node as any).shape._type
154
+ const shapeType = (ref as any).shape._type
70
155
 
71
156
  if (shapeType === "map" || shapeType === "record") {
72
157
  for (const k in value) {
73
- ;(node as any)[k] = value[k]
158
+ ;(ref as any)[k] = value[k]
74
159
  }
75
160
  return true
76
161
  }
77
162
 
78
163
  if (shapeType === "list" || shapeType === "movableList") {
79
164
  if (Array.isArray(value)) {
80
- const listNode = node as any
81
- if (listNode.length > 0) {
82
- listNode.delete(0, listNode.length)
165
+ const listRef = ref as any
166
+ if (listRef.length > 0) {
167
+ listRef.delete(0, listRef.length)
83
168
  }
84
169
  for (const item of value) {
85
- listNode.push(item)
170
+ listRef.push(item)
86
171
  }
87
172
  return true
88
173
  }