@cocreate/crdt 1.3.14 → 1.3.18

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,230 +0,0 @@
1
- import CoCreateCrdt from "./core.js"
2
- import * as Y from 'yjs'
3
- import crud from '@cocreate/crud-client';
4
-
5
-
6
- class CoCreateCRDTClass extends CoCreateCrdt
7
- {
8
- constructor(org, doc) {
9
- super(org, doc)
10
- }
11
-
12
- /*
13
- crdt.init({
14
- collection: "module",
15
- document_id: "",
16
- name: "",
17
- element: dom_object,
18
- metadata: "xxxx"
19
- })
20
- */
21
- init(info) {
22
- try {
23
- // this.__validateKeysJson(info, ['collection', 'document_id', 'name']);
24
-
25
- const id = this.__getYDocId(info['collection'], info['document_id'], info['name'])
26
-
27
- if (!id) return;
28
- this.createDoc(id)
29
-
30
- } catch(e) {
31
- console.log('Invalid param', e);
32
- }
33
- }
34
-
35
- /*
36
- crdt.replaceText({
37
- collection: "module",
38
- document_id: "",
39
- name: "",
40
- value: "",
41
- updateCrud: true | false,
42
- element: dom_object,
43
- metadata: "xxxx"
44
- })
45
- */
46
-
47
- replaceText(info){
48
- if (!info) return;
49
-
50
- let id = this.generateID(config.organization_Id, info['collection'], info['document_id'], info['name'])
51
- let info1 = this.parseType(id)
52
-
53
- if (!id) return;
54
-
55
- if (info.updateCrud != false) info.updateCrud = true;
56
-
57
- if (this.getType(id) ) {
58
- let oldData = this.getType(id).toString();
59
- let textValue = info.value.toString();
60
- if (oldData && oldData.length > 0) {
61
- this.deleteText(info['collection'], info['document_id'], info['name'], 0, Math.max(oldData.length, textValue.length));
62
- }
63
- this.insertText(info['collection'], info['document_id'], info['name'], 0, textValue);
64
- }
65
- if (info.crud != false) {
66
- crud.updateDocument({
67
- collection: info.collection,
68
- document_id: info.document_id,
69
- data: {[info.name]: info.value},
70
- element: info.element,
71
- metadata:info.metadata,
72
- namespace: info.namespace,
73
- room: info.room,
74
- broadcast: info.broadcast,
75
- upsert: info.upsert,
76
- broadcast_sender: info.broadcast_sender
77
- })
78
- }
79
- }
80
-
81
- /*
82
- crdt.insertText({
83
- collection: 'module_activities',
84
- document_id: '5e4802ce3ed96d38e71fc7e5',
85
- name: 'name',
86
- value: 'T',
87
- position: '8',
88
- attributes: {bold: true}
89
- })
90
- */
91
- insertText(info) {
92
- try {
93
- // this.__validateKeysJson(info,['collection','document_id','name','value','position']);
94
- let id = this.generateID(config.organization_Id, info['collection'], info['document_id'], info['name'])
95
- let info1 = this.parseType(id)
96
-
97
- if (id) {
98
- this.getType(id).insert(info['position'], info['value'], info['attribute']);
99
- let wholestring = this.getType(id).toString();
100
-
101
- console.log(wholestring)
102
-
103
- if (info.crud != false) {
104
- crud.updateDocument({
105
- collection: info.collection,
106
- document_id: info.document_id,
107
- data: {
108
- [info.name]: wholestring
109
- },
110
- metadata: 'yjs-change'
111
- })
112
- }
113
- }
114
- }
115
- catch (e) {
116
- console.error(e);
117
- }
118
- }
119
-
120
-
121
- /*
122
- crdt.deleteText({
123
- collection: 'module_activities',
124
- document_id: '5e4802ce3ed96d38e71fc7e5',
125
- name: 'name',
126
- position: '8',
127
- length: 2,
128
- })
129
- */
130
-
131
- deleteText(info) {
132
- try{
133
- // this.__validateKeysJson(info,['collection','document_id','name', 'position','length']);
134
- let id = this.generateID(config.organization_Id, info['collection'], info['document_id'], info['name'])
135
- let info1 = this.parseType(id)
136
- if (id) {
137
- this.docs[info1.id].doc.getText(id).delete(info['position'], info['length']);
138
- let wholestring = this.getType(id).toString();
139
-
140
- console.log(wholestring)
141
-
142
- if (info.crud != false) {
143
- crud.updateDocument({
144
- collection: info.collection,
145
- document_id: info.document_id,
146
- data: {
147
- [info.name]: wholestring
148
- },
149
- metadata: 'yjs-change'
150
- })
151
- }
152
- }
153
- }
154
- catch (e) {
155
- console.error(e);
156
- }
157
- }
158
-
159
-
160
- /*
161
- crdt.getText({
162
- collection: 'module_activities',
163
- document_id: '5e4802ce3ed96d38e71fc7e5',
164
- name: 'name'
165
- })
166
- */
167
-
168
- getText(info) {
169
- try{
170
- // this.__validateKeysJson(info,['collection','document_id','name']);
171
- let id = this.generateID(config.organization_Id, info['collection'], info['document_id'], info['name'])
172
- let info1 = this.parseType(id)
173
- if (id) {
174
- return this.docs[info1.id].doc.getText(id).toString();
175
- } else {
176
- return "--";
177
- }
178
- }
179
- catch (e) {
180
- console.error(e);
181
- return "";
182
- }
183
- }
184
-
185
-
186
- /*
187
- crdt.getPosition(function(data))
188
- crdt.getPosition(function(data){console.log(" EScuchando ahora ",data)})
189
- */
190
- getPosition(callback){
191
- if(typeof miFuncion === 'function')
192
- this.changeListenAwereness(callback);
193
- else
194
- console.error('Callback should be a function')
195
- }
196
-
197
- __getYDocId(collection, document_id, name) {
198
- if (!crud.checkAttrValue(collection) ||
199
- !crud.checkAttrValue(document_id) ||
200
- !crud.checkAttrValue(name))
201
- {
202
- return null;
203
- }
204
- return this.generateID(config.organization_Id, collection, document_id, name);
205
- }
206
-
207
- __validateKeysJson(json,rules){
208
- let keys_json = Object.keys(json);
209
- keys_json.forEach(key=>{
210
- const index = rules.indexOf(key);
211
- if(index != -1)
212
- rules.splice(index, 1);
213
- });
214
- if( rules.length )
215
- throw "Requires the following "+ rules.toString();
216
- }
217
- }
218
-
219
- let CoCreateCrdt = null;
220
- if (!window.CoCreateCrdt) {
221
- const crdtDoc = new Y.Doc();
222
- CoCreateCrdt = new CoCreateCRDTClass(config.organization_Id, crdtDoc);
223
- window.Y = Y;
224
- window.CoCreateCrdt = CoCreateCrdt;
225
- } else {
226
- CoCreateCrdt = window.CoCreateCrdt;
227
- }
228
-
229
- export default CoCreateCrdt;
230
-
@@ -1,367 +0,0 @@
1
- /*
2
- Unlike stated in the LICENSE file, it is not necessary to include the copyright notice and permission notice when you copy code from this file.
3
- */
4
-
5
- /**
6
- * @module provider/websocket
7
- */
8
-
9
- /* eslint-env browser */
10
-
11
- import * as Y from 'yjs' // eslint-disable-line
12
- import * as bc from 'lib0/broadcastchannel.js'
13
- import * as time from 'lib0/time.js'
14
- import * as encoding from 'lib0/encoding.js'
15
- import * as decoding from 'lib0/decoding.js'
16
- import * as syncProtocol from 'y-protocols/sync.js'
17
- import * as authProtocol from 'y-protocols/auth.js'
18
- import * as awarenessProtocol from 'y-protocols/awareness.js'
19
- import * as mutex from 'lib0/mutex.js'
20
- import { Observable } from 'lib0/observable.js'
21
- import * as math from 'lib0/math.js'
22
- import * as url from 'lib0/url.js'
23
-
24
- const messageSync = 0
25
- const messageQueryAwareness = 3
26
- const messageAwareness = 1
27
- const messageAuth = 2
28
-
29
- const reconnectTimeoutBase = 1200
30
- const maxReconnectTimeout = 2500
31
- // @todo - this should depend on awareness.outdatedTime
32
- const messageReconnectTimeout = 30000
33
-
34
- /**
35
- * @param {WebsocketProvider} provider
36
- * @param {string} reason
37
- */
38
- const permissionDeniedHandler = (provider, reason) => console.warn(`Permission denied to access ${provider.url}.\n${reason}`)
39
-
40
- /**
41
- * @param {WebsocketProvider} provider
42
- * @param {Uint8Array} buf
43
- * @param {boolean} emitSynced
44
- * @return {encoding.Encoder}
45
- */
46
- const readMessage = (provider, buf, emitSynced) => {
47
- const decoder = decoding.createDecoder(buf)
48
- const encoder = encoding.createEncoder()
49
- const messageType = decoding.readVarUint(decoder)
50
- switch (messageType) {
51
- case messageSync: {
52
- encoding.writeVarUint(encoder, messageSync)
53
- const syncMessageType = syncProtocol.readSyncMessage(decoder, encoder, provider.doc, provider)
54
- if (emitSynced && syncMessageType === syncProtocol.messageYjsSyncStep2 && !provider.synced) {
55
- provider.synced = true
56
- }
57
- break
58
- }
59
- case messageQueryAwareness:
60
- encoding.writeVarUint(encoder, messageAwareness)
61
- encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(provider.awareness, Array.from(provider.awareness.getStates().keys())))
62
- break
63
- case messageAwareness:
64
- awarenessProtocol.applyAwarenessUpdate(provider.awareness, decoding.readVarUint8Array(decoder), provider)
65
- break
66
- case messageAuth:
67
- authProtocol.readAuthMessage(decoder, provider.doc, permissionDeniedHandler)
68
- break
69
- default:
70
- console.error('Unable to compute message')
71
- return encoder
72
- }
73
- return encoder
74
- }
75
-
76
- /**
77
- * @param {WebsocketProvider} provider
78
- */
79
- const setupWS = provider => {
80
- if (provider.shouldConnect && provider.ws === null) {
81
- const websocket = new provider._WS(provider.url)
82
- websocket.binaryType = 'arraybuffer'
83
- provider.ws = websocket
84
- provider.wsconnecting = true
85
- provider.wsconnected = false
86
- provider.synced = false
87
-
88
- websocket.onmessage = event => {
89
- provider.wsLastMessageReceived = time.getUnixTime()
90
- const encoder = readMessage(provider, new Uint8Array(event.data), true)
91
- if (encoding.length(encoder) > 1) {
92
- websocket.send(encoding.toUint8Array(encoder))
93
- }
94
- }
95
- websocket.onclose = () => {
96
- provider.ws = null
97
- provider.wsconnecting = false
98
- if (provider.wsconnected) {
99
- provider.wsconnected = false
100
- provider.synced = false
101
- // update awareness (all users except local left)
102
- awarenessProtocol.removeAwarenessStates(provider.awareness, Array.from(provider.awareness.getStates().keys()).filter(client => client !== provider.doc.clientID), provider)
103
- provider.emit('status', [{
104
- status: 'disconnected'
105
- }])
106
- } else {
107
- provider.wsUnsuccessfulReconnects++
108
- }
109
- // Start with no reconnect timeout and increase timeout by
110
- // log10(wsUnsuccessfulReconnects).
111
- // The idea is to increase reconnect timeout slowly and have no reconnect
112
- // timeout at the beginning (log(1) = 0)
113
- setTimeout(setupWS, math.min(math.log10(provider.wsUnsuccessfulReconnects + 1) * reconnectTimeoutBase, maxReconnectTimeout), provider)
114
- }
115
- websocket.onopen = () => {
116
- provider.wsLastMessageReceived = time.getUnixTime()
117
- provider.wsconnecting = false
118
- provider.wsconnected = true
119
- provider.wsUnsuccessfulReconnects = 0
120
- provider.emit('status', [{
121
- status: 'connected'
122
- }])
123
- // always send sync step 1 when connected
124
- const encoder = encoding.createEncoder()
125
- encoding.writeVarUint(encoder, messageSync)
126
- syncProtocol.writeSyncStep1(encoder, provider.doc)
127
- websocket.send(encoding.toUint8Array(encoder))
128
- // broadcast local awareness state
129
- if (provider.awareness.getLocalState() !== null) {
130
- const encoderAwarenessState = encoding.createEncoder()
131
- encoding.writeVarUint(encoderAwarenessState, messageAwareness)
132
- encoding.writeVarUint8Array(encoderAwarenessState, awarenessProtocol.encodeAwarenessUpdate(provider.awareness, [provider.doc.clientID]))
133
- websocket.send(encoding.toUint8Array(encoderAwarenessState))
134
- }
135
- }
136
-
137
- provider.emit('status', [{
138
- status: 'connecting'
139
- }])
140
- }
141
- }
142
-
143
- /**
144
- * @param {WebsocketProvider} provider
145
- * @param {ArrayBuffer} buf
146
- */
147
- const broadcastMessage = (provider, buf) => {
148
- if (provider.wsconnected) {
149
- // @ts-ignore We know that wsconnected = true
150
- provider.ws.send(buf)
151
- }
152
- if (provider.bcconnected) {
153
- provider.mux(() => {
154
- bc.publish(provider.bcChannel, buf)
155
- })
156
- }
157
- }
158
-
159
- /**
160
- * Websocket Provider for Yjs. Creates a websocket connection to sync the shared document.
161
- * The document name is attached to the provided url. I.e. the following example
162
- * creates a websocket connection to http://localhost:1234/my-document-name
163
- *
164
- * @example
165
- * import * as Y from 'yjs'
166
- * import { WebsocketProvider } from 'y-websocket'
167
- * const doc = new Y.Doc()
168
- * const provider = new WebsocketProvider('http://localhost:1234', 'my-document-name', doc)
169
- *
170
- * @extends {Observable<string>}
171
- */
172
- export class WebsocketProvider extends Observable {
173
- /**
174
- * @param {string} serverUrl
175
- * @param {string} roomname
176
- * @param {Y.Doc} doc
177
- * @param {object} [opts]
178
- * @param {boolean} [opts.connect]
179
- * @param {awarenessProtocol.Awareness} [opts.awareness]
180
- * @param {Object<string,string>} [opts.params]
181
- * @param {typeof WebSocket} [opts.WebSocketPolyfill] Optionall provide a WebSocket polyfill
182
- * @param {number} [opts.resyncInterval] Request server state every `resyncInterval` milliseconds
183
- */
184
- constructor (serverUrl, roomname, doc, { connect = true, awareness = new awarenessProtocol.Awareness(doc), params = {}, WebSocketPolyfill = WebSocket, resyncInterval = -1 } = {}) {
185
- super()
186
- // ensure that url is always ends with /
187
- while (serverUrl[serverUrl.length - 1] === '/') {
188
- serverUrl = serverUrl.slice(0, serverUrl.length - 1)
189
- }
190
- const encodedParams = url.encodeQueryParams(params)
191
- this.bcChannel = serverUrl + '/' + roomname
192
- this.url = serverUrl + '/' + roomname + (encodedParams.length === 0 ? '' : '?' + encodedParams)
193
- this.roomname = roomname
194
- this.doc = doc
195
- this._WS = WebSocketPolyfill
196
- this.awareness = awareness
197
- this.wsconnected = false
198
- this.wsconnecting = false
199
- this.bcconnected = false
200
- this.wsUnsuccessfulReconnects = 0
201
- this.mux = mutex.createMutex()
202
- /**
203
- * @type {boolean}
204
- */
205
- this._synced = false
206
- /**
207
- * @type {WebSocket?}
208
- */
209
- this.ws = null
210
- this.wsLastMessageReceived = 0
211
- /**
212
- * Whether to connect to other peers or not
213
- * @type {boolean}
214
- */
215
- this.shouldConnect = connect
216
-
217
- /**
218
- * @type {NodeJS.Timeout | number}
219
- */
220
- this._resyncInterval = 0
221
- if (resyncInterval > 0) {
222
- this._resyncInterval = setInterval(() => {
223
- if (this.ws) {
224
- // resend sync step 1
225
- const encoder = encoding.createEncoder()
226
- encoding.writeVarUint(encoder, messageSync)
227
- syncProtocol.writeSyncStep1(encoder, doc)
228
- this.ws.send(encoding.toUint8Array(encoder))
229
- }
230
- }, resyncInterval)
231
- }
232
-
233
- /**
234
- * @param {ArrayBuffer} data
235
- */
236
- this._bcSubscriber = data => {
237
- this.mux(() => {
238
- const encoder = readMessage(this, new Uint8Array(data), false)
239
- if (encoding.length(encoder) > 1) {
240
- bc.publish(this.bcChannel, encoding.toUint8Array(encoder))
241
- }
242
- })
243
- }
244
- /**
245
- * Listens to Yjs updates and sends them to remote peers (ws and broadcastchannel)
246
- * @param {Uint8Array} update
247
- * @param {any} origin
248
- */
249
- this._updateHandler = (update, origin) => {
250
- if (origin !== this || origin === null) {
251
- const encoder = encoding.createEncoder()
252
- encoding.writeVarUint(encoder, messageSync)
253
- syncProtocol.writeUpdate(encoder, update)
254
- broadcastMessage(this, encoding.toUint8Array(encoder))
255
- }
256
- }
257
- this.doc.on('update', this._updateHandler)
258
- /**
259
- * @param {any} changed
260
- * @param {any} origin
261
- */
262
- this._awarenessUpdateHandler = ({ added, updated, removed }, origin) => {
263
- const changedClients = added.concat(updated).concat(removed)
264
- const encoder = encoding.createEncoder()
265
- encoding.writeVarUint(encoder, messageAwareness)
266
- encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(awareness, changedClients))
267
- broadcastMessage(this, encoding.toUint8Array(encoder))
268
- }
269
- window.addEventListener('beforeunload', () => {
270
- awarenessProtocol.removeAwarenessStates(this.awareness, [doc.clientID], 'window unload')
271
- })
272
- awareness.on('update', this._awarenessUpdateHandler)
273
- this._checkInterval = setInterval(() => {
274
- if (this.wsconnected && messageReconnectTimeout < time.getUnixTime() - this.wsLastMessageReceived) {
275
- // no message received in a long time - not even your own awareness
276
- // updates (which are updated every 15 seconds)
277
- /** @type {WebSocket} */ (this.ws).close()
278
- }
279
- }, messageReconnectTimeout / 10)
280
- if (connect) {
281
- this.connect()
282
- }
283
- }
284
-
285
- /**
286
- * @type {boolean}
287
- */
288
- get synced () {
289
- return this._synced
290
- }
291
-
292
- set synced (state) {
293
- if (this._synced !== state) {
294
- this._synced = state
295
- this.emit('synced', [state])
296
- this.emit('sync', [state])
297
- }
298
- }
299
-
300
- destroy () {
301
- if (this._resyncInterval !== 0) {
302
- clearInterval(/** @type {NodeJS.Timeout} */ (this._resyncInterval))
303
- }
304
- clearInterval(this._checkInterval)
305
- this.disconnect()
306
- this.awareness.off('update', this._awarenessUpdateHandler)
307
- this.doc.off('update', this._updateHandler)
308
- super.destroy()
309
- }
310
-
311
- connectBc () {
312
- if (!this.bcconnected) {
313
- bc.subscribe(this.bcChannel, this._bcSubscriber)
314
- this.bcconnected = true
315
- }
316
- // send sync step1 to bc
317
- this.mux(() => {
318
- // write sync step 1
319
- const encoderSync = encoding.createEncoder()
320
- encoding.writeVarUint(encoderSync, messageSync)
321
- syncProtocol.writeSyncStep1(encoderSync, this.doc)
322
- bc.publish(this.bcChannel, encoding.toUint8Array(encoderSync))
323
- // broadcast local state
324
- const encoderState = encoding.createEncoder()
325
- encoding.writeVarUint(encoderState, messageSync)
326
- syncProtocol.writeSyncStep2(encoderState, this.doc)
327
- bc.publish(this.bcChannel, encoding.toUint8Array(encoderState))
328
- // write queryAwareness
329
- const encoderAwarenessQuery = encoding.createEncoder()
330
- encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness)
331
- bc.publish(this.bcChannel, encoding.toUint8Array(encoderAwarenessQuery))
332
- // broadcast local awareness state
333
- const encoderAwarenessState = encoding.createEncoder()
334
- encoding.writeVarUint(encoderAwarenessState, messageAwareness)
335
- encoding.writeVarUint8Array(encoderAwarenessState, awarenessProtocol.encodeAwarenessUpdate(this.awareness, [this.doc.clientID]))
336
- bc.publish(this.bcChannel, encoding.toUint8Array(encoderAwarenessState))
337
- })
338
- }
339
-
340
- disconnectBc () {
341
- // broadcast message with local awareness state set to null (indicating disconnect)
342
- const encoder = encoding.createEncoder()
343
- encoding.writeVarUint(encoder, messageAwareness)
344
- encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this.awareness, [this.doc.clientID], new Map()))
345
- broadcastMessage(this, encoding.toUint8Array(encoder))
346
- if (this.bcconnected) {
347
- bc.unsubscribe(this.bcChannel, this._bcSubscriber)
348
- this.bcconnected = false
349
- }
350
- }
351
-
352
- disconnect () {
353
- this.shouldConnect = false
354
- this.disconnectBc()
355
- if (this.ws !== null) {
356
- this.ws.close()
357
- }
358
- }
359
-
360
- connect () {
361
- this.shouldConnect = true
362
- if (!this.wsconnected && this.ws === null) {
363
- setupWS(this)
364
- this.connectBc()
365
- }
366
- }
367
- }