@cuboapp/crdt 1.0.5 → 1.0.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cuboapp/crdt",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "CRDT for CUBO",
5
5
  "main": "src/index.ts",
6
6
  "repository": "git@github.com:cuboapp/crdt.git",
@@ -11,12 +11,16 @@
11
11
  "access": "public"
12
12
  },
13
13
  "dependencies": {
14
- "@cuboapp/types": "^2.0.11",
15
- "@cuboapp/utils": "1.0.9",
16
- "@cuboapp/ws": "1.0.4",
14
+ "@cuboapp/types": "^2.0.12",
15
+ "@cuboapp/utils": "1.0.10",
16
+ "@cuboapp/ws": "1.0.5",
17
17
  "ws": "^8.19.0",
18
18
  "yjs": "^13.6.29"
19
19
  },
20
+ "peerDependencies": {
21
+ "vue": "^3.5.26",
22
+ "vue-router": "^4.6.3"
23
+ },
20
24
  "devDependencies": {
21
25
  "@types/node": "^24.10.1",
22
26
  "@types/ws": "^8.18.1",
@@ -1,106 +1,415 @@
1
- import { EventEmitter } from '@cuboapp/utils'
2
- import { createWsClient, WsClient } from '@cuboapp/ws'
1
+ import { keyBy, pick, uuid } from '@cuboapp/utils'
2
+ import { computed, reactive } from 'vue'
3
3
  import { applyUpdate, Doc } from 'yjs'
4
4
 
5
- export type CuboCrdtClientOptions = {
6
- host?: string
7
- port?: number
8
- protocol?: 'wss' | 'ws' | string
9
- authToken?: () => Promise<string>
10
- debug?: boolean
11
- }
5
+ import { CUBO_CRDT_EVENT } from '../constants'
6
+ import { CuboCrdtServerDocumentIncomingAction } from '../server'
7
+ import { CuboCrdtKey } from '../types'
12
8
 
13
- export class CuboCrdtClient {
14
- private ws: WsClient
9
+ import {
10
+ CuboCrdtClientDocOrigin,
11
+ CuboCrdtClientDocUpdateOptions,
12
+ CuboCrdtClientList,
13
+ CuboCrdtClientOptions,
14
+ CuboCrdtClientRow,
15
+ CuboCrdtClientStore,
16
+ CuboCrdtClientSubscribeEvent,
17
+ CuboCrdtClientUseOptions
18
+ } from './types'
15
19
 
16
- constructor(
17
- private opts: CuboCrdtClientOptions,
18
- private ee = new EventEmitter()
19
- ) {
20
- this.ws = createWsClient({
21
- host: this.opts.host,
22
- port: this.opts.port,
23
- protocol: this.opts.protocol,
24
- autoReconnect: true,
25
- pingInterval: 5000,
26
- authToken: this.opts.authToken ? () => this.opts.authToken!() : () => ''
20
+ export * from './types'
21
+
22
+ export class CuboCrdtClient<M> {
23
+ public store: CuboCrdtClientStore<M> = {}
24
+
25
+ private listeners: Map<string, Map<string, (ctx: CuboCrdtClientSubscribeEvent) => void>> = new Map()
26
+
27
+ constructor(private opts: CuboCrdtClientOptions<M>) {
28
+ opts.entities.forEach((e) => this.listeners.set(e, new Map()))
29
+ }
30
+
31
+ async start() {
32
+ this.ws.registerHandler(CUBO_CRDT_EVENT.EVENT, ({ message }) => {
33
+ let events: CuboCrdtClientSubscribeEvent[] = message.data as any
34
+ if (!Array.isArray(events)) {
35
+ events = [events as any]
36
+ }
37
+
38
+ // todo: perform events through queue in await mode
39
+ events.forEach((event) => {
40
+ const listeners = this.listeners.get(event.entity)
41
+
42
+ if (this.debug) {
43
+ console.log('[CRDT] incoming event', { listeners: listeners?.size, event })
44
+ }
45
+
46
+ if (event.subscribe_id) {
47
+ const listener = listeners?.get(event.subscribe_id)
48
+ if (!listener) {
49
+ console.warn('[CRDT] listener not found:', event.subscribe_id, event)
50
+ } else {
51
+ listener(event)
52
+ }
53
+ } else {
54
+ listeners?.forEach((cb) => cb(event))
55
+ }
56
+ })
27
57
  })
28
58
  }
29
59
 
30
- public async start() {
31
- await this.ws.connect()
60
+ async stop() {
61
+ this.ws.deleteHandler(CUBO_CRDT_EVENT.EVENT)
32
62
 
33
- this.ws.registerHandler('message', this.onMessage.bind(this))
63
+ this.listeners.clear()
34
64
  }
35
65
 
36
- public async stop() {
37
- try {
38
- await this.ws.disconnect()
39
- } catch {}
66
+ public useComputedList<K extends Extract<keyof M, string>>(entity: K) {
67
+ return computed(() => (this.store[entity]?.state.rows || []) as M[K][])
40
68
  }
41
69
 
42
- private onMessage({ message }: any) {
43
- if (message.event) {
44
- this.ee.emit(message.event, message)
45
- }
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
+ })
46
74
  }
47
75
 
48
- async subscribe({ entity, id, filters }: { entity: string; id?: number; filters?: any }, cb: any) {
49
- if (this.opts.debug) {
50
- console.log('subscribeEntity', { entity, id })
76
+ public useList<K extends Extract<keyof M, string>, T = CuboCrdtKey<K, M>>(
77
+ entity: K | string,
78
+ opts?: CuboCrdtClientUseOptions
79
+ ): CuboCrdtClientList<T> {
80
+ const storeKey = opts?.storeKey ?? `${entity}`
81
+ const filters = opts?.filters || {}
82
+
83
+ const subscribe_id = uuid()
84
+
85
+ // создаём хранилище
86
+ if (this.store[storeKey] === undefined) {
87
+ const state = reactive({
88
+ rows: [],
89
+ subscribed: false
90
+ })
91
+
92
+ this.store[storeKey] = {
93
+ state,
94
+ docs: new Map()
95
+ }
51
96
  }
52
97
 
53
- filters = filters || {}
54
- if (id) {
55
- filters.id = id
98
+ // подписка
99
+ const subscribe = (filters?: Record<string, any>) => {
100
+ if (this.store[storeKey]?.state.subscribed) {
101
+ return
102
+ }
103
+
104
+ // ставим сразу флаг, потому что не может быть ошибок (считаем так)
105
+ this.store[storeKey]!.state.subscribed = true
106
+
107
+ filters = filters !== undefined ? filters : opts?.filters
108
+
109
+ if (this.debug) {
110
+ console.log('[CRDT] subscribe', { entity, opts, filters })
111
+ }
112
+
113
+ // подписываемся на фронте
114
+ this.listeners.get(entity)?.set(subscribe_id, (ctx) => {
115
+ this.onIncomingUpdate(ctx, {
116
+ subscribe_id,
117
+ storeKey,
118
+ ...(pick(opts || {}, ['onBeforeCreate', 'onBeforeUpdate', 'onBeforeDelete', 'onAfterCreate', 'onAfterUpdate', 'onAfterDelete']) ||
119
+ {})
120
+ })
121
+ })
122
+
123
+ // подписываемся на бэке
124
+ this.ws.request({ method: CUBO_CRDT_EVENT.SUBSCRIBE, data: { subscribe_id, entity, filters } })
56
125
  }
57
126
 
58
- const event = `crdt:${entity}`
59
- // const event = id ? `crdt:${entity}:${id}` : `crdt:${entity}`
127
+ // отписка
128
+ const unsubscribe = (clear?: boolean) => {
129
+ if (this.debug) {
130
+ console.log('[CRDT] unsubscribe', { entity, opts, filters })
131
+ }
132
+
133
+ // отписываемся на бэке
134
+ this.ws.request({ method: CUBO_CRDT_EVENT.UNSUBSCRIBE, data: { subscribe_id } })
135
+
136
+ // отписываемся на фронте
137
+ this.listeners.get(entity)?.delete(subscribe_id)
60
138
 
61
- this.ee.addListener(event, cb)
62
- this.ws.request({ method: 'crdt:subscribe', data: { event, filters } })
139
+ // очищаем стор
140
+ if (clear !== false) {
141
+ this.store[storeKey] = undefined
142
+ }
143
+ }
144
+
145
+ // апгрейд
146
+ const upgrade = (filters?: Record<string, any>) => {
147
+ // подписываемся на бэке
148
+ this.upgrade(subscribe_id, filters)
149
+ }
150
+
151
+ // авто-подписка
152
+ if (opts?.autoSubscribe !== false) {
153
+ subscribe()
154
+ }
155
+
156
+ return {
157
+ subscribe_id,
158
+ storeKey,
159
+ subscribe,
160
+ upgrade,
161
+ unsubscribe,
162
+ docs: computed(() => this.store[storeKey]?.docs),
163
+ subscribed: () => computed(() => this.store[storeKey]?.state.subscribed || false),
164
+ rows: () => computed(() => this.store[storeKey]?.state.rows as T[]),
165
+ rowsById: () => computed(() => keyBy(this.store[storeKey]?.state.rows as T[], 'id' as keyof T))
166
+ }
63
167
  }
64
168
 
65
- async unsubscribe({ entity, id, filters }: { entity: string; id?: number; filters?: any }, cb: any) {
66
- if (this.opts.debug) {
67
- console.log('subscribeEntity', { entity, id })
169
+ public useRow<K extends Extract<keyof M, string>, T = CuboCrdtKey<K, M>>(
170
+ entity: K | string,
171
+ id: number,
172
+ opts?: CuboCrdtClientUseOptions
173
+ ): CuboCrdtClientRow<T> {
174
+ // console.log('store key', opts?.storeKey ?? `${entity}:${id}`)
175
+
176
+ const { subscribe_id, storeKey, docs, subscribe, upgrade, unsubscribe, subscribed } = this.useList(entity, {
177
+ ...opts,
178
+ filters: opts?.filters ?? { id },
179
+ storeKey: opts?.storeKey ?? `${entity}:${id}`
180
+ })
181
+
182
+ return {
183
+ subscribe_id,
184
+ storeKey,
185
+ doc: computed(() => docs.value?.get(id)),
186
+ subscribe,
187
+ upgrade,
188
+ unsubscribe,
189
+ subscribed,
190
+ row: () => {
191
+ return computed(() => {
192
+ return (this.store[storeKey]?.state.rows?.[0] ?? null) as T
193
+ })
194
+ }
68
195
  }
196
+ }
69
197
 
70
- const event = `crdt:${entity}`
71
- // const event = id ? `crdt:${entity}:${id}` : `crdt:${entity}`
198
+ public update<K extends string>(
199
+ entity: K,
200
+ entity_id: number,
201
+ dto: K extends Extract<keyof M, string> ? Partial<M[K]> : object,
202
+ opts?: CuboCrdtClientDocOrigin
203
+ ) {
204
+ const cardRow = this.store[`${entity}:${entity_id}`]?.state.rows?.[0]
205
+ const listRow = this.store[entity]?.state?.rows.find((r: any) => r.id === entity_id)
206
+ const cardDoc = this.store[`${entity}:${entity_id}`]?.docs.get(entity_id)
207
+ const listDoc = this.store[entity]?.docs.get(entity_id)
208
+
209
+ // обновляем документы
210
+ for (const doc of [cardDoc, listDoc]) {
211
+ if (doc) {
212
+ const map = doc.getMap()
213
+
214
+ const row = (cardRow || listRow) as any
215
+ const toUpdate = Object.fromEntries(Object.entries(dto).filter(([key, value]) => row[key] !== value))
216
+
217
+ if (Object.keys(toUpdate).length) {
218
+ const origin: CuboCrdtClientDocOrigin = {
219
+ store: opts?.store ?? true,
220
+ expose: opts?.expose ?? 'other',
221
+ keys: opts?.keys ?? Object.keys(toUpdate),
222
+ react: true // отправляем на бэк
223
+ }
224
+
225
+ doc.transact(() => {
226
+ Object.entries(toUpdate).forEach(([key, value]) => {
227
+ map.set(key, value)
228
+ })
229
+ }, origin)
230
+ }
231
+ }
232
+ }
233
+
234
+ // обновляем реактивку
235
+ for (const row of [cardRow, listRow]) {
236
+ if (row) {
237
+ const toUpdate = Object.fromEntries(Object.entries(dto).filter(([key, value]) => row[key as keyof typeof row] !== value))
238
+
239
+ if (Object.keys(toUpdate).length) {
240
+ Object.entries(toUpdate).forEach(([key, value]) => {
241
+ row[key as keyof typeof row] = value
242
+ })
243
+ }
244
+ }
245
+ }
246
+ }
72
247
 
73
- this.ee.removeListener(event, cb)
74
- this.ws.request({ method: 'crdt:unsubscribe', data: { event, filters } })
248
+ public upgrade(subscribe_id: string, filters?: Record<string, any>) {
249
+ this.ws.request({ method: CUBO_CRDT_EVENT.UPGRADE, data: { subscribe_id, filters } })
75
250
  }
76
251
 
77
- async openDocument(entity: string, id: number, doc: Doc): Promise<void> {
78
- const state: any = await this.ws.request(
79
- {
80
- method: 'crdt:open',
81
- data: { name: `${entity}:${id}` }
82
- },
83
- { wait: true }
84
- )
85
-
86
- this.ee.addListener(`crdt:update:${entity}:${id}`, (ctx) => {
87
- applyUpdate(doc, new Uint8Array(ctx.data))
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
+
263
+ if (opts?.onBeforeCreate) {
264
+ const result = await opts.onBeforeCreate(update, ctx, opts)
265
+ if (!result) {
266
+ return
267
+ }
268
+ }
269
+
270
+ applyUpdate(doc, update, { react: false })
271
+
272
+ // подписываемся на обновления документа
273
+ doc.on('update', (update, origin: CuboCrdtClientDocOrigin) => {
274
+ // react = false, если это апдейт с бэка
275
+ if (origin?.react !== false) {
276
+ const data: CuboCrdtServerDocumentIncomingAction = {
277
+ action: 'update',
278
+ entity: ctx.entity,
279
+ entity_id: ctx.entity_id,
280
+ data: Array.from(update),
281
+ origin: {
282
+ ...pick(origin || {}, ['store', 'keys', 'expose']),
283
+ subscribe_id: opts?.subscribe_id
284
+ }
285
+ }
286
+
287
+ this.ws.request({
288
+ method: CUBO_CRDT_EVENT.EVENT,
289
+ data
290
+ })
291
+ }
88
292
  })
89
293
 
90
- this.ws.request({ method: 'crdt:subscribe', data: { crdt: { entity, id } } })
294
+ // добавляем документ в хранилище документов
295
+ this.store[opts.storeKey]?.docs.set(ctx.entity_id, doc)
296
+
297
+ // добавляем документ в реактивное хранилище
298
+ const json: any = doc.getMap().toJSON()
299
+ const index = this.store[opts.storeKey]?.state.rows.findIndex((r: any) => r.id === ctx.entity_id)
300
+ if (index !== undefined && index >= 0) {
301
+ if (this.debug) {
302
+ console.warn('[CRDT] onDocumentCreate - row already exists: "' + ctx.entity_id + '"', { ctx, opts })
303
+ }
304
+
305
+ this.store[opts.storeKey]!.state.rows[index] = json
306
+ } else {
307
+ this.store[opts.storeKey]?.state.rows.push(json)
308
+ }
309
+
310
+ if (opts?.onAfterCreate) {
311
+ await opts.onAfterCreate(doc, update, ctx, opts)
312
+ }
313
+ }
314
+
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
+
324
+ if (opts?.onBeforeUpdate) {
325
+ const result = await opts.onBeforeUpdate(doc, update, ctx, opts)
326
+ if (!result) {
327
+ return
328
+ }
329
+ }
330
+
331
+ // обновляем yjs-ный документ
332
+ applyUpdate(doc, update, { react: false })
333
+
334
+ // обновляем документ в реактивном хранилище
335
+ const json: any = doc.getMap().toJSON()
336
+ const index = this.store[opts.storeKey]?.state.rows.findIndex((r: any) => r.id === ctx.entity_id)
337
+ if (index !== undefined && index >= 0) {
338
+ this.store[opts.storeKey]!.state.rows[index] = json
339
+ } else {
340
+ if (this.debug) {
341
+ console.warn('[CRDT] onDocumentUpdate - row not exists: "' + ctx.entity_id + '"', { ctx, opts })
342
+ }
343
+
344
+ this.store[opts.storeKey]!.state.rows.push(json)
345
+ }
91
346
 
92
- applyUpdate(doc, new Uint8Array(state))
347
+ if (opts?.onAfterUpdate) {
348
+ await opts.onAfterUpdate(doc, update, ctx, opts)
349
+ }
93
350
  }
94
351
 
95
- async closeDocument(entity: string, id: number) {
96
- await this.ws.request(
97
- {
98
- method: 'crdt:close',
99
- data: { name: `${entity}:${id}` }
100
- },
101
- { wait: true }
102
- )
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
+
359
+ if (opts?.onBeforeDelete) {
360
+ const result = await opts.onBeforeDelete(doc, ctx, opts)
361
+ if (!result) {
362
+ return
363
+ }
364
+ }
365
+
366
+ // console.log('onDocumentDelete', ctx, opts)
367
+
368
+ doc.destroy()
369
+ this.store[opts.storeKey]?.docs.delete(ctx.entity_id)
370
+
371
+ const index = this.store[opts.storeKey]?.state.rows.findIndex((r: any) => r.id === ctx.entity_id)
372
+ if (index !== undefined && index >= 0) {
373
+ this.store[opts.storeKey]?.state.rows.splice(index, 1)
374
+ } else {
375
+ if (this.debug) {
376
+ console.log('[CRDT] row not found', index, ctx, opts)
377
+ }
378
+ }
379
+
380
+ if (opts?.onAfterDelete) {
381
+ await opts.onAfterDelete(doc, ctx, opts)
382
+ }
383
+ }
384
+
385
+ private onIncomingUpdate(ctx: CuboCrdtClientSubscribeEvent, opts: CuboCrdtClientDocUpdateOptions) {
386
+ if (this.debug) {
387
+ console.log('[CRDT] call event listener', ctx, opts)
388
+ }
389
+
390
+ if (!this.store[opts.storeKey]) {
391
+ console.warn('[CRDT] on incoming update, store key not found: "' + opts.storeKey + '"')
392
+ }
393
+
394
+ switch (ctx.action) {
395
+ case 'create':
396
+ this.onDocumentCreate(ctx, opts)
397
+ break
398
+ case 'update':
399
+ case 'upsert':
400
+ this.onDocumentUpdate(ctx, opts)
401
+ break
402
+ case 'delete':
403
+ this.onDocumentDelete(ctx, opts)
404
+ break
405
+ }
406
+ }
407
+
408
+ private get ws() {
409
+ return this.opts.ws
410
+ }
103
411
 
104
- this.ee.removeAllListeners(`crdt:document:${entity}:${id}:update`)
412
+ private get debug() {
413
+ return this.opts.debug
105
414
  }
106
415
  }
@@ -0,0 +1,22 @@
1
+ import { CuboCrdtExposeStrategy } from '../../types'
2
+
3
+ import { CuboCrdtClientBaseOptions } from './utils'
4
+
5
+ export type CuboCrdtClientDocOrigin = {
6
+ // сохранять ли на бэке в дебаунсе
7
+ store?: boolean
8
+
9
+ // реагировать ли в onUpdate (на фронте)
10
+ react?: boolean
11
+
12
+ // обновлённые ключи
13
+ keys?: string[]
14
+
15
+ // куда раскатывать обновления - всем или всем кроме себя
16
+ expose?: CuboCrdtExposeStrategy
17
+ }
18
+
19
+ export type CuboCrdtClientDocUpdateOptions = { subscribe_id: string; storeKey: string } & Pick<
20
+ CuboCrdtClientBaseOptions,
21
+ 'onBeforeCreate' | 'onAfterCreate' | 'onBeforeUpdate' | 'onAfterUpdate' | 'onBeforeDelete' | 'onAfterDelete'
22
+ >
@@ -0,0 +1,53 @@
1
+ import { type WsClient } from '@cuboapp/ws'
2
+ import { ComputedRef } from 'vue'
3
+ import { type Doc } from 'yjs'
4
+
5
+ import { CuboCrdtAction } from '../../types'
6
+
7
+ import { CuboCrdtClientBaseOptions } from './utils'
8
+
9
+ export * from './document'
10
+ export * from './store'
11
+ export * from './utils'
12
+
13
+ export type CuboCrdtClientOptions<M> = {
14
+ ws: WsClient
15
+ entities: Extract<keyof M, string>[]
16
+ debug?: boolean
17
+ }
18
+
19
+ export type CuboCrdtClientUseOptions = CuboCrdtClientBaseOptions & {
20
+ storeKey?: string
21
+ filters?: Record<string, any>
22
+ }
23
+
24
+ export type CuboCrdtClientSubscribeEvent = {
25
+ action: CuboCrdtAction
26
+ entity: string
27
+ entity_id: number
28
+ subscribe_id?: string
29
+ data?: number[]
30
+ }
31
+
32
+ export type CuboCrdtClientList<T> = {
33
+ subscribe_id: string
34
+ storeKey: string
35
+ docs: ComputedRef<Map<number, Doc> | undefined>
36
+ subscribe: (filters?: any) => void
37
+ upgrade: (filters?: any) => void
38
+ unsubscribe: (clear?: boolean) => void
39
+ subscribed: () => ComputedRef<boolean>
40
+ rows: () => ComputedRef<T[]>
41
+ rowsById: () => ComputedRef<Record<string, T>>
42
+ }
43
+
44
+ export type CuboCrdtClientRow<T> = {
45
+ subscribe_id: string
46
+ storeKey: string
47
+ doc: ComputedRef<Doc | undefined>
48
+ subscribe: () => void
49
+ upgrade: (filters?: any) => void
50
+ unsubscribe: (clear?: boolean) => void
51
+ subscribed: () => ComputedRef<boolean>
52
+ row: () => ComputedRef<T>
53
+ }
@@ -0,0 +1,15 @@
1
+ import { Reactive } from 'vue'
2
+ import { Doc } from 'yjs'
3
+
4
+ import { CuboCrdtKey } from '../../types'
5
+
6
+ export type CuboCrdtClientStoreItem<T> = {
7
+ state: Reactive<{
8
+ rows: T[]
9
+ subscribed: boolean
10
+ }>
11
+ docs: Map<number, Doc>
12
+ }
13
+ export type CuboCrdtClientStore<M> = Partial<{
14
+ [K in string]: CuboCrdtClientStoreItem<CuboCrdtKey<K, M>>
15
+ }>
@@ -0,0 +1,35 @@
1
+ import { Doc } from 'yjs'
2
+ import { CuboCrdtClientSubscribeEvent } from '.'
3
+ import { CuboCrdtClientDocUpdateOptions } from './document'
4
+
5
+ export type CuboCrdtClientBaseOptions = {
6
+ autoSubscribe?: boolean
7
+
8
+ onBeforeCreate?: (
9
+ update: Uint8Array,
10
+ ctx: CuboCrdtClientSubscribeEvent,
11
+ opts: CuboCrdtClientDocUpdateOptions
12
+ ) => Promise<boolean> | boolean
13
+ onAfterCreate?: (
14
+ doc: Doc,
15
+ update: Uint8Array,
16
+ ctx: CuboCrdtClientSubscribeEvent,
17
+ opts: CuboCrdtClientDocUpdateOptions
18
+ ) => Promise<void> | void
19
+
20
+ onBeforeUpdate?: (
21
+ doc: Doc,
22
+ update: Uint8Array,
23
+ ctx: CuboCrdtClientSubscribeEvent,
24
+ opts: CuboCrdtClientDocUpdateOptions
25
+ ) => Promise<boolean> | boolean
26
+ onAfterUpdate?: (
27
+ doc: Doc,
28
+ update: Uint8Array,
29
+ ctx: CuboCrdtClientSubscribeEvent,
30
+ opts: CuboCrdtClientDocUpdateOptions
31
+ ) => Promise<void> | void
32
+
33
+ onBeforeDelete?: (doc: Doc, ctx: CuboCrdtClientSubscribeEvent, opts: CuboCrdtClientDocUpdateOptions) => Promise<boolean> | boolean
34
+ onAfterDelete?: (doc: Doc, ctx: CuboCrdtClientSubscribeEvent, opts: CuboCrdtClientDocUpdateOptions) => Promise<void> | void
35
+ }
@@ -0,0 +1,6 @@
1
+ export const CUBO_CRDT_EVENT = {
2
+ EVENT: 'crdt:event',
3
+ SUBSCRIBE: 'crdt:subscribe',
4
+ UPGRADE: 'crdt:upgrade',
5
+ UNSUBSCRIBE: 'crdt:unsubscribe'
6
+ }