@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,8 +1,17 @@
1
1
  import { CuboCrdtAction, CuboCrdtExposeStrategy } from '../../types'
2
2
 
3
+ export type CuboCrdtServerDocumentStoreContext = {
4
+ previousRow: object
5
+ row: object
6
+ }
7
+
3
8
  export type CuboCrdtServerDocumentOptions = {
4
9
  name: string
5
- onStore?: (body: object, origin: CuboCrdtServerDocumentOrigin) => void
10
+ onStore?: (
11
+ body: object,
12
+ origin: CuboCrdtServerDocumentOrigin,
13
+ context: CuboCrdtServerDocumentStoreContext
14
+ ) => void | Promise<void>
6
15
  // onCreate?: (data: Uint8Array, origin?: CuboCrdtServerDocumentOrigin) => void
7
16
  onUpdate?: (data: Uint8Array, origin?: CuboCrdtServerDocumentOrigin) => void
8
17
  onAwarenessUpdate?: (data: Uint8Array, origin?: CuboCrdtServerDocumentOrigin) => void
@@ -1,4 +1,5 @@
1
1
  import { WsServer, WsServerSocket } from '@cuboapp/ws'
2
+ import { CuboCrdtListTotals, CuboCrdtMutation } from '../../types'
2
3
  import { CuboCrdtServerDocument } from '../document'
3
4
  import { CuboCrdtServerDocumentOrigin } from './document'
4
5
  import { CuboCrdtServerSubscribe } from './subscribe'
@@ -6,6 +7,11 @@ import { CuboCrdtServerSubscribe } from './subscribe'
6
7
  export * from './document'
7
8
  export * from './subscribe'
8
9
 
10
+ export type CuboCrdtServerFetchResult<T> = {
11
+ rows: T[]
12
+ totals?: CuboCrdtListTotals
13
+ }
14
+
9
15
  export type CuboCrdtServerOptions<M, A, E extends Extract<keyof M, string>> = {
10
16
  ws: WsServer
11
17
  entities: E[]
@@ -19,8 +25,19 @@ export type CuboCrdtServerOptions<M, A, E extends Extract<keyof M, string>> = {
19
25
  maxSize?: number
20
26
  debounce?: number
21
27
  }
22
- fetchRows?: (client: WsServerSocket, subscribe: CuboCrdtServerSubscribe) => Promise<M[E][]>
28
+ pagination?: {
29
+ debounce?: number
30
+ }
31
+ fetchRows?: (
32
+ client: WsServerSocket,
33
+ subscribe: CuboCrdtServerSubscribe
34
+ ) => Promise<M[E][] | CuboCrdtServerFetchResult<M[E]>>
23
35
  checkRowIsSutable?: (baseState: boolean, ctx: { entity: E; subscribe: CuboCrdtServerSubscribe; row: any }) => boolean
36
+ shouldRefreshSubscribe?: (ctx: {
37
+ entity: E
38
+ subscribe: CuboCrdtServerSubscribe
39
+ mutation: CuboCrdtMutation<M[E]>
40
+ }) => boolean
24
41
  storeRow?: <K extends E>(
25
42
  entity: K,
26
43
  entity_id: number,
@@ -29,6 +46,8 @@ export type CuboCrdtServerOptions<M, A, E extends Extract<keyof M, string>> = {
29
46
  document: CuboCrdtServerDocument
30
47
  client: CuboCrdtSocketClient<A>
31
48
  origin: CuboCrdtServerDocumentOrigin
49
+ previousRow: M[K]
50
+ row: M[K]
32
51
  }
33
52
  ) => Promise<void> | void
34
53
  }
@@ -1,3 +1,5 @@
1
+ import type { CuboCrdtListTotals } from '../../types'
2
+
1
3
  export type CuboCrdtServerUnsubscribeDto = {
2
4
  subscribe_id: string
3
5
  }
@@ -7,10 +9,21 @@ export type CuboCrdtServerSubscribeDto = {
7
9
  filters: {
8
10
  [K in 'id' | string]: any
9
11
  }
10
- awareness: boolean
12
+ awareness?: boolean
13
+ paginated?: boolean
11
14
  }
12
15
 
13
16
  export type CuboCrdtServerSubscribe = {
14
17
  id: string
15
18
  client_id: string
16
- } & Pick<CuboCrdtServerSubscribeDto, 'entity' | 'filters' | 'awareness'>
19
+ row_ids: number[]
20
+ totals: CuboCrdtListTotals
21
+ revision: number
22
+ } & Pick<CuboCrdtServerSubscribeDto, 'entity' | 'filters' | 'awareness' | 'paginated'>
23
+
24
+ export type CuboCrdtSubscribeRefreshState = {
25
+ dirty: boolean
26
+ forceSync: boolean
27
+ timer?: ReturnType<typeof setTimeout>
28
+ running?: Promise<void>
29
+ }
@@ -1,5 +1,28 @@
1
1
  export type CuboCrdtKey<K, M> = K extends Extract<keyof M, string> ? M[K] : any
2
2
 
3
- export type CuboCrdtAction = 'create' | 'upsert' | 'update' | 'delete' | 'awareness'
3
+ export type CuboCrdtAction = 'create' | 'upsert' | 'update' | 'delete' | 'awareness' | 'sync'
4
4
 
5
5
  export type CuboCrdtExposeStrategy = 'all' | 'other' | 'subscribe' | 'client'
6
+
7
+ export type CuboCrdtListTotals = Record<string, number>
8
+
9
+ export type CuboCrdtSubscriptionStatus =
10
+ | 'idle'
11
+ | 'loading'
12
+ | 'ready'
13
+ | 'error'
14
+
15
+ export type CuboCrdtListSyncData = {
16
+ ids: number[]
17
+ totals: CuboCrdtListTotals
18
+ revision: number
19
+ }
20
+
21
+ export type CuboCrdtMutationAction = 'create' | 'update' | 'delete'
22
+
23
+ export type CuboCrdtMutation<T = any> = {
24
+ action: CuboCrdtMutationAction
25
+ row: T
26
+ previousRow?: T
27
+ changedKeys?: string[]
28
+ }
@@ -51,6 +51,18 @@ export function checkRowIsSutable(
51
51
  if (!['limit', 'with'].includes(key)) {
52
52
  if (customKeys?.[key]) {
53
53
  sutable = customKeys[key](key, row, filters)
54
+ } else if (row && typeof row === 'object' && !(key in row)) {
55
+ // Виртуальный фильтр: поля с таким именем у строки нет, значит на бэкенде он
56
+ // разворачивается в условие запроса (join/exists/диапазон) и по одной строке
57
+ // не проверяется в принципе — например membership пользователя в компании.
58
+ //
59
+ // Раньше такой ключ молча не совпадал, и строка считалась выпавшей из подписки.
60
+ // Для update это фатально: рассылка подменяет action на delete (см. server),
61
+ // поэтому подписчик получал удаление вместо обновления — строка исчезала из
62
+ // списка при любой правке, а фоновые обновления постепенно вычищали его целиком.
63
+ //
64
+ // Пропускаем такой ключ. Если фильтр всё же должен влиять на live-раскатку,
65
+ // его семантику описывают в options.checkRowIsSutable или в customKeys.
54
66
  } else {
55
67
  const [formula, str] = `${value}`.split(':')
56
68
 
@@ -100,3 +112,15 @@ export function checkSubscribeIsSutable(origin: CuboCrdtServerDocumentOrigin, su
100
112
 
101
113
  return noStrategyResolved || strategyOtherResolved || strategyClientResolved || strategySubscribeResolved
102
114
  }
115
+
116
+ export function areCrdtListIdsEqual(left: number[], right: number[]) {
117
+ return left.length === right.length && left.every((id, index) => id === right[index])
118
+ }
119
+
120
+ export function areCrdtListTotalsEqual(
121
+ left: Record<string, number>,
122
+ right: Record<string, number>
123
+ ) {
124
+ const keys = new Set([...Object.keys(left), ...Object.keys(right)])
125
+ return Array.from(keys).every((key) => left[key] === right[key])
126
+ }