@cuboapp/crdt 1.0.6 → 1.0.8
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/package.json +1 -1
- package/src/client/index.ts +57 -26
- package/src/client/types/index.ts +4 -2
- package/src/constants/index.ts +1 -0
- package/src/server/document/index.ts +14 -21
- package/src/server/index.ts +297 -164
- package/src/server/types/document.ts +3 -1
- package/src/server/types/index.ts +8 -5
- package/src/types/index.ts +1 -1
- package/src/utils/index.ts +54 -24
- package/src/client/old.ts +0 -249
- package/src/server/old/document/index.ts +0 -107
- package/src/server/old/index.ts +0 -49
package/package.json
CHANGED
package/src/client/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { applyUpdate, Doc } from 'yjs'
|
|
|
4
4
|
|
|
5
5
|
import { CUBO_CRDT_EVENT } from '../constants'
|
|
6
6
|
import { CuboCrdtServerDocumentIncomingAction } from '../server'
|
|
7
|
-
import {
|
|
7
|
+
import { CuboCrdtKey } from '../types'
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
CuboCrdtClientDocOrigin,
|
|
@@ -67,6 +67,12 @@ export class CuboCrdtClient<M> {
|
|
|
67
67
|
return computed(() => (this.store[entity]?.state.rows || []) as M[K][])
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
public useComputedRow<K extends Extract<keyof M, string>>(entity: K, entity_id: number) {
|
|
71
|
+
return computed(() => {
|
|
72
|
+
return ((this.store[entity]?.state.rows || []) as M[K][]).find((row: any) => row.id === entity_id)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
70
76
|
public useList<K extends Extract<keyof M, string>, T = CuboCrdtKey<K, M>>(
|
|
71
77
|
entity: K | string,
|
|
72
78
|
opts?: CuboCrdtClientUseOptions
|
|
@@ -105,14 +111,14 @@ export class CuboCrdtClient<M> {
|
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
// подписываемся на фронте
|
|
108
|
-
this.listeners.get(entity)?.set(subscribe_id, (ctx) =>
|
|
114
|
+
this.listeners.get(entity)?.set(subscribe_id, (ctx) => {
|
|
109
115
|
this.onIncomingUpdate(ctx, {
|
|
110
116
|
subscribe_id,
|
|
111
117
|
storeKey,
|
|
112
118
|
...(pick(opts || {}, ['onBeforeCreate', 'onBeforeUpdate', 'onBeforeDelete', 'onAfterCreate', 'onAfterUpdate', 'onAfterDelete']) ||
|
|
113
119
|
{})
|
|
114
120
|
})
|
|
115
|
-
)
|
|
121
|
+
})
|
|
116
122
|
|
|
117
123
|
// подписываемся на бэке
|
|
118
124
|
this.ws.request({ method: CUBO_CRDT_EVENT.SUBSCRIBE, data: { subscribe_id, entity, filters } })
|
|
@@ -136,6 +142,12 @@ export class CuboCrdtClient<M> {
|
|
|
136
142
|
}
|
|
137
143
|
}
|
|
138
144
|
|
|
145
|
+
// апгрейд
|
|
146
|
+
const upgrade = (filters?: Record<string, any>) => {
|
|
147
|
+
// подписываемся на бэке
|
|
148
|
+
this.upgrade(subscribe_id, filters)
|
|
149
|
+
}
|
|
150
|
+
|
|
139
151
|
// авто-подписка
|
|
140
152
|
if (opts?.autoSubscribe !== false) {
|
|
141
153
|
subscribe()
|
|
@@ -145,6 +157,7 @@ export class CuboCrdtClient<M> {
|
|
|
145
157
|
subscribe_id,
|
|
146
158
|
storeKey,
|
|
147
159
|
subscribe,
|
|
160
|
+
upgrade,
|
|
148
161
|
unsubscribe,
|
|
149
162
|
docs: computed(() => this.store[storeKey]?.docs),
|
|
150
163
|
subscribed: () => computed(() => this.store[storeKey]?.state.subscribed || false),
|
|
@@ -160,7 +173,7 @@ export class CuboCrdtClient<M> {
|
|
|
160
173
|
): CuboCrdtClientRow<T> {
|
|
161
174
|
// console.log('store key', opts?.storeKey ?? `${entity}:${id}`)
|
|
162
175
|
|
|
163
|
-
const { subscribe_id, storeKey, docs, subscribe, unsubscribe, subscribed } = this.useList(entity, {
|
|
176
|
+
const { subscribe_id, storeKey, docs, subscribe, upgrade, unsubscribe, subscribed } = this.useList(entity, {
|
|
164
177
|
...opts,
|
|
165
178
|
filters: opts?.filters ?? { id },
|
|
166
179
|
storeKey: opts?.storeKey ?? `${entity}:${id}`
|
|
@@ -171,6 +184,7 @@ export class CuboCrdtClient<M> {
|
|
|
171
184
|
storeKey,
|
|
172
185
|
doc: computed(() => docs.value?.get(id)),
|
|
173
186
|
subscribe,
|
|
187
|
+
upgrade,
|
|
174
188
|
unsubscribe,
|
|
175
189
|
subscribed,
|
|
176
190
|
row: () => {
|
|
@@ -231,7 +245,21 @@ export class CuboCrdtClient<M> {
|
|
|
231
245
|
}
|
|
232
246
|
}
|
|
233
247
|
|
|
234
|
-
|
|
248
|
+
public upgrade(subscribe_id: string, filters?: Record<string, any>) {
|
|
249
|
+
this.ws.request({ method: CUBO_CRDT_EVENT.UPGRADE, data: { subscribe_id, filters } })
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private async onDocumentCreate(ctx: CuboCrdtClientSubscribeEvent, opts: CuboCrdtClientDocUpdateOptions) {
|
|
253
|
+
let doc = this.store[opts.storeKey]?.docs.get(ctx.entity_id)
|
|
254
|
+
if (doc) {
|
|
255
|
+
console.warn('[CRDT] onDocumentCreate: doc already exists', { ctx, opts })
|
|
256
|
+
return
|
|
257
|
+
} else {
|
|
258
|
+
doc = new Doc()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const update = new Uint8Array(ctx.data as any)
|
|
262
|
+
|
|
235
263
|
if (opts?.onBeforeCreate) {
|
|
236
264
|
const result = await opts.onBeforeCreate(update, ctx, opts)
|
|
237
265
|
if (!result) {
|
|
@@ -239,7 +267,6 @@ export class CuboCrdtClient<M> {
|
|
|
239
267
|
}
|
|
240
268
|
}
|
|
241
269
|
|
|
242
|
-
const doc = new Doc()
|
|
243
270
|
applyUpdate(doc, update, { react: false })
|
|
244
271
|
|
|
245
272
|
// подписываемся на обновления документа
|
|
@@ -285,7 +312,15 @@ export class CuboCrdtClient<M> {
|
|
|
285
312
|
}
|
|
286
313
|
}
|
|
287
314
|
|
|
288
|
-
private async onDocumentUpdate(
|
|
315
|
+
private async onDocumentUpdate(ctx: CuboCrdtClientSubscribeEvent, opts: CuboCrdtClientDocUpdateOptions) {
|
|
316
|
+
const doc = this.store[opts.storeKey]?.docs.get(ctx.entity_id)
|
|
317
|
+
if (!doc) {
|
|
318
|
+
console.warn('[CRDT] onDocumentUpdate: doc not exists', { ctx, opts })
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const update = new Uint8Array(ctx.data as any)
|
|
323
|
+
|
|
289
324
|
if (opts?.onBeforeUpdate) {
|
|
290
325
|
const result = await opts.onBeforeUpdate(doc, update, ctx, opts)
|
|
291
326
|
if (!result) {
|
|
@@ -314,7 +349,13 @@ export class CuboCrdtClient<M> {
|
|
|
314
349
|
}
|
|
315
350
|
}
|
|
316
351
|
|
|
317
|
-
private async onDocumentDelete(
|
|
352
|
+
private async onDocumentDelete(ctx: CuboCrdtClientSubscribeEvent, opts: CuboCrdtClientDocUpdateOptions) {
|
|
353
|
+
const doc = this.store[opts.storeKey]?.docs.get(ctx.entity_id)
|
|
354
|
+
if (!doc) {
|
|
355
|
+
console.warn('[CRDT] onDocumentDelete: doc not exists', { ctx, opts })
|
|
356
|
+
return
|
|
357
|
+
}
|
|
358
|
+
|
|
318
359
|
if (opts?.onBeforeDelete) {
|
|
319
360
|
const result = await opts.onBeforeDelete(doc, ctx, opts)
|
|
320
361
|
if (!result) {
|
|
@@ -331,7 +372,9 @@ export class CuboCrdtClient<M> {
|
|
|
331
372
|
if (index !== undefined && index >= 0) {
|
|
332
373
|
this.store[opts.storeKey]?.state.rows.splice(index, 1)
|
|
333
374
|
} else {
|
|
334
|
-
|
|
375
|
+
if (this.debug) {
|
|
376
|
+
console.log('[CRDT] row not found', index, ctx, opts)
|
|
377
|
+
}
|
|
335
378
|
}
|
|
336
379
|
|
|
337
380
|
if (opts?.onAfterDelete) {
|
|
@@ -348,28 +391,16 @@ export class CuboCrdtClient<M> {
|
|
|
348
391
|
console.warn('[CRDT] on incoming update, store key not found: "' + opts.storeKey + '"')
|
|
349
392
|
}
|
|
350
393
|
|
|
351
|
-
|
|
352
|
-
const action: CuboCrdtAction = !doc ? 'create' : ctx.action === 'delete' ? 'delete' : 'update'
|
|
353
|
-
|
|
354
|
-
// console.log('action', action)
|
|
355
|
-
|
|
356
|
-
switch (action) {
|
|
394
|
+
switch (ctx.action) {
|
|
357
395
|
case 'create':
|
|
358
|
-
|
|
359
|
-
const update = new Uint8Array(ctx.data as any)
|
|
360
|
-
this.onDocumentCreate(update, ctx, opts)
|
|
361
|
-
}
|
|
396
|
+
this.onDocumentCreate(ctx, opts)
|
|
362
397
|
break
|
|
363
398
|
case 'update':
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
this.onDocumentUpdate(doc, update, ctx, opts)
|
|
367
|
-
}
|
|
399
|
+
case 'upsert':
|
|
400
|
+
this.onDocumentUpdate(ctx, opts)
|
|
368
401
|
break
|
|
369
402
|
case 'delete':
|
|
370
|
-
|
|
371
|
-
this.onDocumentDelete(doc, ctx, opts)
|
|
372
|
-
}
|
|
403
|
+
this.onDocumentDelete(ctx, opts)
|
|
373
404
|
break
|
|
374
405
|
}
|
|
375
406
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { WsClient } from '@cuboapp/ws'
|
|
1
|
+
import { type WsClient } from '@cuboapp/ws'
|
|
2
2
|
import { ComputedRef } from 'vue'
|
|
3
|
-
import { Doc } from 'yjs'
|
|
3
|
+
import { type Doc } from 'yjs'
|
|
4
4
|
|
|
5
5
|
import { CuboCrdtAction } from '../../types'
|
|
6
6
|
|
|
@@ -34,6 +34,7 @@ export type CuboCrdtClientList<T> = {
|
|
|
34
34
|
storeKey: string
|
|
35
35
|
docs: ComputedRef<Map<number, Doc> | undefined>
|
|
36
36
|
subscribe: (filters?: any) => void
|
|
37
|
+
upgrade: (filters?: any) => void
|
|
37
38
|
unsubscribe: (clear?: boolean) => void
|
|
38
39
|
subscribed: () => ComputedRef<boolean>
|
|
39
40
|
rows: () => ComputedRef<T[]>
|
|
@@ -45,6 +46,7 @@ export type CuboCrdtClientRow<T> = {
|
|
|
45
46
|
storeKey: string
|
|
46
47
|
doc: ComputedRef<Doc | undefined>
|
|
47
48
|
subscribe: () => void
|
|
49
|
+
upgrade: (filters?: any) => void
|
|
48
50
|
unsubscribe: (clear?: boolean) => void
|
|
49
51
|
subscribed: () => ComputedRef<boolean>
|
|
50
52
|
row: () => ComputedRef<T>
|
package/src/constants/index.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { debounce, pick } from '@cuboapp/utils'
|
|
2
2
|
import { applyUpdate, Doc, encodeStateAsUpdate } from 'yjs'
|
|
3
3
|
|
|
4
|
-
import { CuboCrdtExposeStrategy } from '../../types'
|
|
5
|
-
|
|
6
4
|
import { CuboCrdtServerDocumentOptions, CuboCrdtServerDocumentOrigin } from '../types'
|
|
7
5
|
|
|
8
6
|
export class CuboCrdtServerDocument {
|
|
9
7
|
private ydoc: Doc
|
|
10
8
|
|
|
11
|
-
private firstUpdate =
|
|
9
|
+
// private firstUpdate = false
|
|
12
10
|
|
|
13
11
|
constructor(public opts: CuboCrdtServerDocumentOptions) {
|
|
14
12
|
this.ydoc = new Doc({
|
|
@@ -16,14 +14,20 @@ export class CuboCrdtServerDocument {
|
|
|
16
14
|
autoLoad: false
|
|
17
15
|
})
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.firstUpdate = true
|
|
22
|
-
this.opts?.onCreate?.(data, origin)
|
|
23
|
-
} else {
|
|
24
|
-
this.opts?.onUpdate?.(data, origin)
|
|
25
|
-
}
|
|
17
|
+
// записываем исходное состояние
|
|
18
|
+
this.write(opts.initialState ?? {})
|
|
26
19
|
|
|
20
|
+
// подписываемся на апдейты
|
|
21
|
+
this.ydoc.on('update', (data, origin: CuboCrdtServerDocumentOrigin) => {
|
|
22
|
+
// if (!this.firstUpdate) {
|
|
23
|
+
// console.log('first update', this.name)
|
|
24
|
+
// this.firstUpdate = true
|
|
25
|
+
// // this.opts?.onCreate?.(data, origin)
|
|
26
|
+
// } else {
|
|
27
|
+
this.opts?.onUpdate?.(data, origin)
|
|
28
|
+
// }
|
|
29
|
+
|
|
30
|
+
// если нужно сохранять стейт документа - вызываем store по дебаунсу
|
|
27
31
|
if (origin?.store) {
|
|
28
32
|
const row = this.getJson()
|
|
29
33
|
const body = pick(row, origin.keys ?? Object.keys(row))
|
|
@@ -73,17 +77,6 @@ export class CuboCrdtServerDocument {
|
|
|
73
77
|
this.ydoc.destroy()
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
private _inited = false
|
|
77
|
-
public init(state?: object, expose: CuboCrdtExposeStrategy = 'all') {
|
|
78
|
-
if (this._inited) {
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
this._inited = true
|
|
83
|
-
|
|
84
|
-
this.write(state ?? {}, { expose })
|
|
85
|
-
}
|
|
86
|
-
|
|
87
80
|
public store(body: object, origin: CuboCrdtServerDocumentOrigin) {
|
|
88
81
|
this.opts?.onStore?.(body, origin)
|
|
89
82
|
}
|