@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.
@@ -1,7 +1,99 @@
1
1
  import { CuboApiEntitiesMap } from '@cuboapp/types'
2
2
 
3
- import { CuboCrdt, CuboCrdtOptions } from '..'
3
+ import {
4
+ CuboCrdtClient,
5
+ CuboCrdtClientOptions,
6
+ CuboCrdtServer,
7
+ CuboCrdtServerDocumentOrigin,
8
+ CuboCrdtServerOptions,
9
+ CuboCrdtServerSubscribe
10
+ } from '..'
4
11
 
5
- export function createCrdt<T extends CuboApiEntitiesMap<T>, A = {}>(opts: CuboCrdtOptions<A>) {
6
- return new CuboCrdt<T, A>(opts)
12
+ export function createCrdtServer<M extends CuboApiEntitiesMap<M>, A = {}, E extends Extract<keyof M, string> = Extract<keyof M, string>>(
13
+ opts: CuboCrdtServerOptions<M, A, E>
14
+ ) {
15
+ return new CuboCrdtServer<M, A, E>(opts)
16
+ }
17
+
18
+ export function createCrdtClient<M extends CuboApiEntitiesMap<M>>(opts: CuboCrdtClientOptions<M>) {
19
+ return new CuboCrdtClient<M>(opts)
20
+ }
21
+
22
+ export function cleanObject<F>(object: F): any {
23
+ if (Array.isArray(object)) {
24
+ return object.map(cleanObject).filter(Boolean)
25
+ } else if (typeof object === 'object' && object !== null) {
26
+ const newObj: any = {}
27
+
28
+ for (const key in object) {
29
+ const value = cleanObject(object[key])
30
+
31
+ if (value !== undefined && value !== null && value !== '' && !(typeof value === 'object' && Object.keys(value).length === 0)) {
32
+ newObj[key] = value
33
+ }
34
+ }
35
+
36
+ return newObj
37
+ }
38
+
39
+ return object
40
+ }
41
+
42
+ export function checkRowIsSutable(
43
+ row: any,
44
+ filters?: object,
45
+ customKeys?: Record<string, (key: string, row: any, filters?: any) => boolean>
46
+ ) {
47
+ let sutable = true
48
+
49
+ if (Object.keys(filters || {}).length) {
50
+ Object.entries(filters || {}).forEach(([key, value]) => {
51
+ if (!['limit', 'with'].includes(key)) {
52
+ if (customKeys?.[key]) {
53
+ sutable = customKeys[key](key, row, filters)
54
+ } else {
55
+ const [formula, str] = `${value}`.split(':')
56
+
57
+ // console.log({ formula, str, value: row[key] })
58
+
59
+ if (!str) {
60
+ if (row[key as keyof typeof row] !== value) {
61
+ sutable = false
62
+ }
63
+ } else {
64
+ switch (formula) {
65
+ case 'in':
66
+ if (
67
+ !str
68
+ .split(',')
69
+ .map(Number)
70
+ .includes(row[key as keyof typeof row] as any)
71
+ ) {
72
+ sutable = false
73
+ }
74
+ break
75
+ default:
76
+ sutable = false
77
+ break
78
+ }
79
+ }
80
+ }
81
+ }
82
+ })
83
+ }
84
+
85
+ return sutable
86
+ }
87
+
88
+ export function checkSubscribeIsSutable(origin: CuboCrdtServerDocumentOrigin, subscribe?: CuboCrdtServerSubscribe) {
89
+ if (!subscribe) {
90
+ return false
91
+ }
92
+
93
+ const noStrategyResolved = !origin?.expose || origin?.expose === 'all'
94
+ const strategyOtherResolved = (origin?.expose === 'other' && origin.subscribe_id !== subscribe?.id) || false
95
+ const strategyClientResolved = origin?.expose === 'client' && subscribe?.client_id === origin.client_id
96
+ const strategySubscribeResolved = origin?.expose === 'subscribe' && subscribe?.id === origin.subscribe_id
97
+
98
+ return noStrategyResolved || strategyOtherResolved || strategyClientResolved || strategySubscribeResolved
7
99
  }
package/src/yjs/index.ts DELETED
@@ -1,118 +0,0 @@
1
- import { type WsServerSocket } from '@cuboapp/ws'
2
- import { Doc, encodeStateAsUpdate } from 'yjs'
3
-
4
- export type CuboCrdtDocumentHooks = {
5
- onBeforeDestroy?: () => Promise<void> | void
6
- onAfterDestroy?: () => Promise<void> | void
7
- }
8
-
9
- export type CuboCrdtDocumentOptions = CuboCrdtDocumentHooks & {
10
- name: string
11
- autoRemove?: boolean
12
- debug?: boolean
13
- initial?: any
14
-
15
- initialState?: () => Promise<any>
16
- onUpdate?: (ctx: { data: Uint8Array<ArrayBufferLike>; document: CuboCrdtDocument }) => void
17
-
18
- yjsOptions?: {
19
- guid?: string
20
- collectionid?: string
21
- gc?: boolean
22
- gcFilter?: () => true
23
- meta?: any
24
- }
25
- }
26
-
27
- export class CuboCrdtDocument {
28
- private ydoc: Doc
29
- public clients = new Set<WsServerSocket>()
30
-
31
- constructor(public opts: CuboCrdtDocumentOptions) {
32
- opts.autoRemove = opts?.autoRemove ?? true
33
- }
34
-
35
- get stat() {
36
- return {
37
- clients: this.clients.size
38
- }
39
- }
40
-
41
- get stateAsUpdate() {
42
- return encodeStateAsUpdate(this.ydoc)
43
- }
44
-
45
- async init() {
46
- if (this.opts.debug) {
47
- console.log('init doc', this.opts.name)
48
- }
49
-
50
- this.ydoc = new Doc({
51
- ...(this.opts?.yjsOptions || {}),
52
- autoLoad: false,
53
- shouldLoad: false
54
- })
55
-
56
- const state = this.opts?.initialState ? (await this.opts?.initialState()) || {} : {}
57
-
58
- // console.log('initial state', state)
59
-
60
- return new Promise<void>((resolve) => {
61
- this.ydoc.once('update', () => {
62
- this.ydoc.on('update', (data) => {
63
- // console.log('document updated', this.opts.name, this.getMap().toJSON())
64
-
65
- this.opts?.onUpdate?.({ data, document: this })
66
- })
67
-
68
- resolve()
69
- })
70
-
71
- const map = this.getMap()
72
-
73
- this.ydoc.transact(() => {
74
- if (state) {
75
- Object.entries(state).forEach(([key, value]) => {
76
- map.set(key, value)
77
- })
78
- }
79
- })
80
- })
81
- }
82
-
83
- getMap() {
84
- return this.ydoc.getMap()
85
- }
86
-
87
- async hasClient(client: WsServerSocket) {
88
- return this.clients.has(client)
89
- }
90
-
91
- async addClient(client: WsServerSocket) {
92
- this.clients.add(client)
93
- }
94
-
95
- async removeClient(client: WsServerSocket) {
96
- this.clients.delete(client)
97
- }
98
-
99
- async write(dto: object) {
100
- if (this.opts.debug) {
101
- console.log('CRDT write document', this.opts.name, dto)
102
- }
103
-
104
- return new Promise<void>(async (resolve) => {
105
- const map = this.getMap()
106
-
107
- this.ydoc.transact(() => {
108
- Object.entries(dto).forEach(([key, value]) => {
109
- map.set(key, value)
110
- })
111
-
112
- // Внимание! Резолв промиса не означает раскатку по всем подключениям,
113
- // только лишь запись в Yjs-документ
114
- resolve()
115
- })
116
- })
117
- }
118
- }