@cuboapp/crdt 1.0.14 → 1.0.16

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,4 +1,4 @@
1
- import { debounce, pick } from '@cuboapp/utils'
1
+ import { cloneDeep, debounce, pick } from '@cuboapp/utils'
2
2
  import { applyUpdate, Doc, encodeStateAsUpdate } from 'yjs'
3
3
  import { Awareness, encodeAwarenessUpdate } from 'y-protocols/awareness'
4
4
 
@@ -6,6 +6,7 @@ import { CuboCrdtServerDocumentOptions, CuboCrdtServerDocumentOrigin } from '../
6
6
 
7
7
  export class CuboCrdtServerDocument {
8
8
  private ydoc: Doc
9
+ private persistedState: object = {}
9
10
  public awareness: Awareness
10
11
  public awarenessBySubscribe = new Map<string, Set<number>>()
11
12
 
@@ -42,6 +43,8 @@ export class CuboCrdtServerDocument {
42
43
  })
43
44
  }
44
45
 
46
+ this.persistedState = cloneDeep(opts.initialState ?? {})
47
+
45
48
  // записываем исходное состояние
46
49
  this.write(opts.initialState ?? {})
47
50
 
@@ -91,11 +94,29 @@ export class CuboCrdtServerDocument {
91
94
  return this.getMap().toJSON()
92
95
  }
93
96
 
97
+ getPersistedState() {
98
+ return cloneDeep(this.persistedState)
99
+ }
100
+
101
+ setPersistedState(row: object) {
102
+ this.persistedState = cloneDeep(row)
103
+ }
104
+
94
105
  async write(dto: object, origin?: CuboCrdtServerDocumentOrigin) {
95
106
  const map = this.getMap()
96
107
 
108
+ // Пропускаем скалярные ключи с тем же значением: Y.Map.set всегда создаёт новый Item и
109
+ // тумбстонит старый (безостановочный рост документа + лишний update-эвент), даже если
110
+ // значение не изменилось. Сравнение по ссылке — дёшево и не трогает объекты (для них
111
+ // set выполняется как и раньше, без лишней сериализации на горячем пути).
112
+ const entries = Object.entries(dto).filter(([key, value]) => map.get(key) !== value)
113
+
114
+ if (!entries.length) {
115
+ return
116
+ }
117
+
97
118
  this.ydoc.transact(() => {
98
- Object.entries(dto).forEach(([key, value]) => {
119
+ entries.forEach(([key, value]) => {
99
120
  map.set(key, value)
100
121
  })
101
122
  }, origin)
@@ -110,8 +131,12 @@ export class CuboCrdtServerDocument {
110
131
  this.ydoc.destroy()
111
132
  }
112
133
 
113
- public store(body: object, origin: CuboCrdtServerDocumentOrigin) {
114
- this.opts?.onStore?.(body, origin)
134
+ public async store(body: object, origin: CuboCrdtServerDocumentOrigin) {
135
+ const previousRow = this.getPersistedState()
136
+ const row = this.getJson()
137
+
138
+ await this.opts?.onStore?.(body, origin, { previousRow, row })
139
+ this.setPersistedState(row)
115
140
  }
116
141
  public debounceStore = debounce(this.store.bind(this), 300)
117
142
  }