@hocuspocus/provider 2.15.2 → 3.0.4-rc.0
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/dist/hocuspocus-provider.cjs +931 -1532
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +931 -1527
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/extension-database/src/Database.d.ts +1 -1
- package/dist/packages/extension-logger/src/Logger.d.ts +1 -1
- package/dist/packages/extension-redis/src/Redis.d.ts +4 -3
- package/dist/packages/extension-sqlite/src/SQLite.d.ts +2 -1
- package/dist/packages/extension-throttle/src/index.d.ts +2 -2
- package/dist/packages/extension-webhook/src/index.d.ts +3 -3
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +12 -45
- package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +5 -9
- package/dist/packages/provider/src/IncomingMessage.d.ts +3 -3
- package/dist/packages/provider/src/MessageReceiver.d.ts +2 -4
- package/dist/packages/provider/src/MessageSender.d.ts +2 -2
- package/dist/packages/provider/src/OutgoingMessage.d.ts +2 -2
- package/dist/packages/provider/src/OutgoingMessages/AuthenticationMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/AwarenessMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/CloseMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/QueryAwarenessMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/StatelessMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/SyncStepOneMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/SyncStepTwoMessage.d.ts +2 -1
- package/dist/packages/provider/src/OutgoingMessages/UpdateMessage.d.ts +2 -1
- package/dist/packages/provider/src/index.d.ts +0 -2
- package/dist/packages/provider/src/types.d.ts +12 -12
- package/dist/packages/server/src/ClientConnection.d.ts +19 -10
- package/dist/packages/server/src/Connection.d.ts +7 -23
- package/dist/packages/server/src/DirectConnection.d.ts +2 -2
- package/dist/packages/server/src/Document.d.ts +3 -7
- package/dist/packages/server/src/Hocuspocus.d.ts +7 -36
- package/dist/packages/server/src/IncomingMessage.d.ts +3 -3
- package/dist/packages/server/src/MessageReceiver.d.ts +4 -6
- package/dist/packages/server/src/OutgoingMessage.d.ts +4 -3
- package/dist/packages/server/src/Server.d.ts +23 -3
- package/dist/packages/server/src/index.d.ts +1 -1
- package/dist/packages/server/src/types.d.ts +15 -29
- package/dist/packages/server/src/util/getParameters.d.ts +1 -1
- package/dist/packages/transformer/src/Prosemirror.d.ts +1 -1
- package/dist/packages/transformer/src/Tiptap.d.ts +3 -3
- package/dist/packages/transformer/src/types.d.ts +1 -1
- package/dist/tests/utils/newHocuspocus.d.ts +2 -2
- package/dist/tests/utils/newHocuspocusProvider.d.ts +2 -2
- package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +4 -3
- package/dist/tests/utils/retryableAssertion.d.ts +1 -1
- package/package.json +2 -2
- package/src/EventEmitter.ts +3 -1
- package/src/HocuspocusProvider.ts +74 -200
- package/src/HocuspocusProviderWebsocket.ts +24 -75
- package/src/IncomingMessage.ts +5 -3
- package/src/MessageReceiver.ts +20 -19
- package/src/MessageSender.ts +3 -2
- package/src/OutgoingMessage.ts +3 -2
- package/src/OutgoingMessages/AuthenticationMessage.ts +2 -1
- package/src/OutgoingMessages/AwarenessMessage.ts +2 -1
- package/src/OutgoingMessages/CloseMessage.ts +2 -1
- package/src/OutgoingMessages/QueryAwarenessMessage.ts +2 -1
- package/src/OutgoingMessages/StatelessMessage.ts +2 -1
- package/src/OutgoingMessages/SyncStepOneMessage.ts +2 -1
- package/src/OutgoingMessages/SyncStepTwoMessage.ts +2 -1
- package/src/OutgoingMessages/UpdateMessage.ts +2 -1
- package/src/index.ts +0 -2
- package/src/types.ts +12 -12
- package/dist/packages/provider/src/TiptapCollabProvider.d.ts +0 -161
- package/dist/packages/provider/src/TiptapCollabProviderWebsocket.d.ts +0 -19
- package/dist/packages/server/src/Debugger.d.ts +0 -14
- package/dist/tests/server/getMessageLogs.d.ts +0 -1
- package/dist/tests/server/requiresAuthentication.d.ts +0 -1
- package/src/TiptapCollabProvider.ts +0 -505
- package/src/TiptapCollabProviderWebsocket.ts +0 -38
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var common = require('@hocuspocus/common');
|
|
6
4
|
var Y = require('yjs');
|
|
7
5
|
var attempt = require('@lifeomic/attempt');
|
|
8
6
|
|
|
9
|
-
function
|
|
10
|
-
if (e && e.__esModule) return e;
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
11
8
|
var n = Object.create(null);
|
|
12
9
|
if (e) {
|
|
13
10
|
Object.keys(e).forEach(function (k) {
|
|
@@ -20,51 +17,51 @@ function _interopNamespace(e) {
|
|
|
20
17
|
}
|
|
21
18
|
});
|
|
22
19
|
}
|
|
23
|
-
n
|
|
20
|
+
n.default = e;
|
|
24
21
|
return Object.freeze(n);
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
var Y__namespace = /*#__PURE__*/
|
|
24
|
+
var Y__namespace = /*#__PURE__*/_interopNamespaceDefault(Y);
|
|
28
25
|
|
|
29
26
|
/**
|
|
30
|
-
*
|
|
27
|
+
* Common Math expressions.
|
|
31
28
|
*
|
|
32
|
-
* @module
|
|
29
|
+
* @module math
|
|
33
30
|
*/
|
|
34
31
|
|
|
32
|
+
const floor = Math.floor;
|
|
33
|
+
|
|
35
34
|
/**
|
|
36
|
-
* Creates a new Map instance.
|
|
37
|
-
*
|
|
38
35
|
* @function
|
|
39
|
-
* @
|
|
40
|
-
*
|
|
36
|
+
* @param {number} a
|
|
37
|
+
* @param {number} b
|
|
38
|
+
* @return {number} The smaller element of a and b
|
|
39
|
+
*/
|
|
40
|
+
const min = (a, b) => a < b ? a : b;
|
|
41
|
+
|
|
42
|
+
/**
|
|
41
43
|
* @function
|
|
44
|
+
* @param {number} a
|
|
45
|
+
* @param {number} b
|
|
46
|
+
* @return {number} The bigger element of a and b
|
|
42
47
|
*/
|
|
43
|
-
const
|
|
48
|
+
const max = (a, b) => a > b ? a : b;
|
|
49
|
+
|
|
50
|
+
/* eslint-env browser */
|
|
51
|
+
|
|
52
|
+
const BIT7 = 64;
|
|
53
|
+
const BIT8 = 128;
|
|
54
|
+
const BITS6 = 63;
|
|
55
|
+
const BITS7 = 127;
|
|
44
56
|
|
|
45
57
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* ```js
|
|
49
|
-
* const listeners = map.setIfUndefined(events, 'eventName', set.create)
|
|
50
|
-
* listeners.add(listener)
|
|
51
|
-
* ```
|
|
58
|
+
* Utility helpers for working with numbers.
|
|
52
59
|
*
|
|
53
|
-
* @
|
|
54
|
-
* @template V,K
|
|
55
|
-
* @template {Map<K,V>} MAP
|
|
56
|
-
* @param {MAP} map
|
|
57
|
-
* @param {K} key
|
|
58
|
-
* @param {function():V} createT
|
|
59
|
-
* @return {V}
|
|
60
|
+
* @module number
|
|
60
61
|
*/
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
map.set(key, set = createT());
|
|
65
|
-
}
|
|
66
|
-
return set
|
|
67
|
-
};
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
|
|
68
65
|
|
|
69
66
|
/**
|
|
70
67
|
* Utility module to work with sets.
|
|
@@ -72,7 +69,7 @@ const setIfUndefined = (map, key, createT) => {
|
|
|
72
69
|
* @module set
|
|
73
70
|
*/
|
|
74
71
|
|
|
75
|
-
const create$
|
|
72
|
+
const create$2 = () => new Set();
|
|
76
73
|
|
|
77
74
|
/**
|
|
78
75
|
* Utility module to work with Arrays.
|
|
@@ -80,6 +77,7 @@ const create$1 = () => new Set();
|
|
|
80
77
|
* @module array
|
|
81
78
|
*/
|
|
82
79
|
|
|
80
|
+
|
|
83
81
|
/**
|
|
84
82
|
* Transforms something array-like to an actual Array.
|
|
85
83
|
*
|
|
@@ -166,387 +164,228 @@ if (utf8TextDecoder && utf8TextDecoder.decode(new Uint8Array()).length === 1) {
|
|
|
166
164
|
}
|
|
167
165
|
|
|
168
166
|
/**
|
|
169
|
-
*
|
|
167
|
+
* Efficient schema-less binary encoding with support for variable length encoding.
|
|
170
168
|
*
|
|
171
|
-
*
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
169
|
+
* Use [lib0/encoding] with [lib0/decoding]. Every encoding function has a corresponding decoding function.
|
|
170
|
+
*
|
|
171
|
+
* Encodes numbers in little-endian order (least to most significant byte order)
|
|
172
|
+
* and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
|
|
173
|
+
* which is also used in Protocol Buffers.
|
|
174
|
+
*
|
|
175
|
+
* ```js
|
|
176
|
+
* // encoding step
|
|
177
|
+
* const encoder = encoding.createEncoder()
|
|
178
|
+
* encoding.writeVarUint(encoder, 256)
|
|
179
|
+
* encoding.writeVarString(encoder, 'Hello world!')
|
|
180
|
+
* const buf = encoding.toUint8Array(encoder)
|
|
181
|
+
* ```
|
|
182
|
+
*
|
|
183
|
+
* ```js
|
|
184
|
+
* // decoding step
|
|
185
|
+
* const decoder = decoding.createDecoder(buf)
|
|
186
|
+
* decoding.readVarUint(decoder) // => 256
|
|
187
|
+
* decoding.readVarString(decoder) // => 'Hello world!'
|
|
188
|
+
* decoding.hasContent(decoder) // => false - all data is read
|
|
189
|
+
* ```
|
|
190
|
+
*
|
|
191
|
+
* @module encoding
|
|
178
192
|
*/
|
|
179
|
-
/* c8 ignore next */
|
|
180
|
-
const undefinedToNull = v => v === undefined ? null : v;
|
|
181
193
|
|
|
182
|
-
/* eslint-env browser */
|
|
183
194
|
|
|
184
195
|
/**
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
* Uses LocalStorage in the browser and falls back to in-memory storage.
|
|
188
|
-
*
|
|
189
|
-
* @module storage
|
|
196
|
+
* A BinaryEncoder handles the encoding to an Uint8Array.
|
|
190
197
|
*/
|
|
191
|
-
|
|
192
|
-
/* c8 ignore start */
|
|
193
|
-
class VarStoragePolyfill {
|
|
198
|
+
class Encoder {
|
|
194
199
|
constructor () {
|
|
195
|
-
this.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
*/
|
|
202
|
-
setItem (key, newValue) {
|
|
203
|
-
this.map.set(key, newValue);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* @param {string} key
|
|
208
|
-
*/
|
|
209
|
-
getItem (key) {
|
|
210
|
-
return this.map.get(key)
|
|
200
|
+
this.cpos = 0;
|
|
201
|
+
this.cbuf = new Uint8Array(100);
|
|
202
|
+
/**
|
|
203
|
+
* @type {Array<Uint8Array>}
|
|
204
|
+
*/
|
|
205
|
+
this.bufs = [];
|
|
211
206
|
}
|
|
212
207
|
}
|
|
213
|
-
/* c8 ignore stop */
|
|
214
208
|
|
|
215
209
|
/**
|
|
216
|
-
* @
|
|
210
|
+
* @function
|
|
211
|
+
* @return {Encoder}
|
|
217
212
|
*/
|
|
218
|
-
|
|
219
|
-
let usePolyfill = true;
|
|
220
|
-
|
|
221
|
-
/* c8 ignore start */
|
|
222
|
-
try {
|
|
223
|
-
// if the same-origin rule is violated, accessing localStorage might thrown an error
|
|
224
|
-
if (typeof localStorage !== 'undefined' && localStorage) {
|
|
225
|
-
_localStorage = localStorage;
|
|
226
|
-
usePolyfill = false;
|
|
227
|
-
}
|
|
228
|
-
} catch (e) { }
|
|
229
|
-
/* c8 ignore stop */
|
|
213
|
+
const createEncoder = () => new Encoder();
|
|
230
214
|
|
|
231
215
|
/**
|
|
232
|
-
*
|
|
216
|
+
* The current length of the encoded data.
|
|
217
|
+
*
|
|
218
|
+
* @function
|
|
219
|
+
* @param {Encoder} encoder
|
|
220
|
+
* @return {number}
|
|
233
221
|
*/
|
|
234
|
-
|
|
235
|
-
|
|
222
|
+
const length$1 = encoder => {
|
|
223
|
+
let len = encoder.cpos;
|
|
224
|
+
for (let i = 0; i < encoder.bufs.length; i++) {
|
|
225
|
+
len += encoder.bufs[i].length;
|
|
226
|
+
}
|
|
227
|
+
return len
|
|
228
|
+
};
|
|
236
229
|
|
|
237
230
|
/**
|
|
238
|
-
*
|
|
231
|
+
* Transform to Uint8Array.
|
|
239
232
|
*
|
|
240
|
-
* @param {function({ key: string, newValue: string, oldValue: string }): void} eventHandler
|
|
241
233
|
* @function
|
|
234
|
+
* @param {Encoder} encoder
|
|
235
|
+
* @return {Uint8Array} The created ArrayBuffer.
|
|
242
236
|
*/
|
|
243
|
-
|
|
244
|
-
const
|
|
237
|
+
const toUint8Array = encoder => {
|
|
238
|
+
const uint8arr = new Uint8Array(length$1(encoder));
|
|
239
|
+
let curPos = 0;
|
|
240
|
+
for (let i = 0; i < encoder.bufs.length; i++) {
|
|
241
|
+
const d = encoder.bufs[i];
|
|
242
|
+
uint8arr.set(d, curPos);
|
|
243
|
+
curPos += d.length;
|
|
244
|
+
}
|
|
245
|
+
uint8arr.set(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos), curPos);
|
|
246
|
+
return uint8arr
|
|
247
|
+
};
|
|
245
248
|
|
|
246
249
|
/**
|
|
247
|
-
*
|
|
250
|
+
* Write one byte to the encoder.
|
|
248
251
|
*
|
|
249
|
-
* @param {function({ key: string, newValue: string, oldValue: string }): void} eventHandler
|
|
250
252
|
* @function
|
|
253
|
+
* @param {Encoder} encoder
|
|
254
|
+
* @param {number} num The byte that is to be encoded.
|
|
251
255
|
*/
|
|
252
|
-
|
|
253
|
-
const
|
|
256
|
+
const write = (encoder, num) => {
|
|
257
|
+
const bufferLen = encoder.cbuf.length;
|
|
258
|
+
if (encoder.cpos === bufferLen) {
|
|
259
|
+
encoder.bufs.push(encoder.cbuf);
|
|
260
|
+
encoder.cbuf = new Uint8Array(bufferLen * 2);
|
|
261
|
+
encoder.cpos = 0;
|
|
262
|
+
}
|
|
263
|
+
encoder.cbuf[encoder.cpos++] = num;
|
|
264
|
+
};
|
|
254
265
|
|
|
255
266
|
/**
|
|
256
|
-
*
|
|
267
|
+
* Write a variable length unsigned integer. Max encodable integer is 2^53.
|
|
257
268
|
*
|
|
258
|
-
* @
|
|
269
|
+
* @function
|
|
270
|
+
* @param {Encoder} encoder
|
|
271
|
+
* @param {number} num The number that is to be encoded.
|
|
259
272
|
*/
|
|
273
|
+
const writeVarUint = (encoder, num) => {
|
|
274
|
+
while (num > BITS7) {
|
|
275
|
+
write(encoder, BIT8 | (BITS7 & num));
|
|
276
|
+
num = floor(num / 128); // shift >>> 7
|
|
277
|
+
}
|
|
278
|
+
write(encoder, BITS7 & num);
|
|
279
|
+
};
|
|
260
280
|
|
|
261
281
|
/**
|
|
262
|
-
*
|
|
282
|
+
* A cache to store strings temporarily
|
|
263
283
|
*/
|
|
264
|
-
const
|
|
284
|
+
const _strBuffer = new Uint8Array(30000);
|
|
285
|
+
const _maxStrBSize = _strBuffer.length / 3;
|
|
265
286
|
|
|
266
287
|
/**
|
|
267
|
-
*
|
|
288
|
+
* Write a variable length string.
|
|
268
289
|
*
|
|
269
|
-
* @
|
|
270
|
-
* @param {
|
|
271
|
-
* @param {
|
|
272
|
-
* @return {Array<R>}
|
|
290
|
+
* @function
|
|
291
|
+
* @param {Encoder} encoder
|
|
292
|
+
* @param {String} str The string that is to be encoded.
|
|
273
293
|
*/
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
294
|
+
const _writeVarStringNative = (encoder, str) => {
|
|
295
|
+
if (str.length < _maxStrBSize) {
|
|
296
|
+
// We can encode the string into the existing buffer
|
|
297
|
+
/* c8 ignore next */
|
|
298
|
+
const written = utf8TextEncoder.encodeInto(str, _strBuffer).written || 0;
|
|
299
|
+
writeVarUint(encoder, written);
|
|
300
|
+
for (let i = 0; i < written; i++) {
|
|
301
|
+
write(encoder, _strBuffer[i]);
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
writeVarUint8Array(encoder, encodeUtf8(str));
|
|
278
305
|
}
|
|
279
|
-
return results
|
|
280
306
|
};
|
|
281
307
|
|
|
282
308
|
/**
|
|
283
|
-
*
|
|
284
|
-
* @return {number}
|
|
285
|
-
*/
|
|
286
|
-
const length$1 = obj => keys(obj).length;
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Calls `Object.prototype.hasOwnProperty`.
|
|
309
|
+
* Write a variable length string.
|
|
290
310
|
*
|
|
291
|
-
* @
|
|
292
|
-
* @param {
|
|
293
|
-
* @
|
|
311
|
+
* @function
|
|
312
|
+
* @param {Encoder} encoder
|
|
313
|
+
* @param {String} str The string that is to be encoded.
|
|
294
314
|
*/
|
|
295
|
-
const
|
|
315
|
+
const _writeVarStringPolyfill = (encoder, str) => {
|
|
316
|
+
const encodedString = unescape(encodeURIComponent(str));
|
|
317
|
+
const len = encodedString.length;
|
|
318
|
+
writeVarUint(encoder, len);
|
|
319
|
+
for (let i = 0; i < len; i++) {
|
|
320
|
+
write(encoder, /** @type {number} */ (encodedString.codePointAt(i)));
|
|
321
|
+
}
|
|
322
|
+
};
|
|
296
323
|
|
|
297
324
|
/**
|
|
298
|
-
*
|
|
325
|
+
* Write a variable length string.
|
|
299
326
|
*
|
|
300
|
-
* @
|
|
327
|
+
* @function
|
|
328
|
+
* @param {Encoder} encoder
|
|
329
|
+
* @param {String} str The string that is to be encoded.
|
|
301
330
|
*/
|
|
331
|
+
/* c8 ignore next */
|
|
332
|
+
const writeVarString = (utf8TextEncoder && /** @type {any} */ (utf8TextEncoder).encodeInto) ? _writeVarStringNative : _writeVarStringPolyfill;
|
|
302
333
|
|
|
303
334
|
/**
|
|
304
|
-
*
|
|
335
|
+
* Append fixed-length Uint8Array to the encoder.
|
|
305
336
|
*
|
|
306
|
-
* @
|
|
307
|
-
* @param {
|
|
308
|
-
* @
|
|
309
|
-
*/
|
|
310
|
-
const equalityStrict = (a, b) => a === b;
|
|
311
|
-
|
|
312
|
-
/* c8 ignore start */
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* @param {any} a
|
|
316
|
-
* @param {any} b
|
|
317
|
-
* @return {boolean}
|
|
337
|
+
* @function
|
|
338
|
+
* @param {Encoder} encoder
|
|
339
|
+
* @param {Uint8Array} uint8Array
|
|
318
340
|
*/
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (a.byteLength !== b.byteLength) {
|
|
336
|
-
return false
|
|
337
|
-
}
|
|
338
|
-
for (let i = 0; i < a.length; i++) {
|
|
339
|
-
if (a[i] !== b[i]) {
|
|
340
|
-
return false
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
break
|
|
344
|
-
}
|
|
345
|
-
case Set: {
|
|
346
|
-
if (a.size !== b.size) {
|
|
347
|
-
return false
|
|
348
|
-
}
|
|
349
|
-
for (const value of a) {
|
|
350
|
-
if (!b.has(value)) {
|
|
351
|
-
return false
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
break
|
|
355
|
-
}
|
|
356
|
-
case Map: {
|
|
357
|
-
if (a.size !== b.size) {
|
|
358
|
-
return false
|
|
359
|
-
}
|
|
360
|
-
for (const key of a.keys()) {
|
|
361
|
-
if (!b.has(key) || !equalityDeep(a.get(key), b.get(key))) {
|
|
362
|
-
return false
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
break
|
|
366
|
-
}
|
|
367
|
-
case Object:
|
|
368
|
-
if (length$1(a) !== length$1(b)) {
|
|
369
|
-
return false
|
|
370
|
-
}
|
|
371
|
-
for (const key in a) {
|
|
372
|
-
if (!hasProperty(a, key) || !equalityDeep(a[key], b[key])) {
|
|
373
|
-
return false
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
break
|
|
377
|
-
case Array:
|
|
378
|
-
if (a.length !== b.length) {
|
|
379
|
-
return false
|
|
380
|
-
}
|
|
381
|
-
for (let i = 0; i < a.length; i++) {
|
|
382
|
-
if (!equalityDeep(a[i], b[i])) {
|
|
383
|
-
return false
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
break
|
|
387
|
-
default:
|
|
388
|
-
return false
|
|
341
|
+
const writeUint8Array = (encoder, uint8Array) => {
|
|
342
|
+
const bufferLen = encoder.cbuf.length;
|
|
343
|
+
const cpos = encoder.cpos;
|
|
344
|
+
const leftCopyLen = min(bufferLen - cpos, uint8Array.length);
|
|
345
|
+
const rightCopyLen = uint8Array.length - leftCopyLen;
|
|
346
|
+
encoder.cbuf.set(uint8Array.subarray(0, leftCopyLen), cpos);
|
|
347
|
+
encoder.cpos += leftCopyLen;
|
|
348
|
+
if (rightCopyLen > 0) {
|
|
349
|
+
// Still something to write, write right half..
|
|
350
|
+
// Append new buffer
|
|
351
|
+
encoder.bufs.push(encoder.cbuf);
|
|
352
|
+
// must have at least size of remaining buffer
|
|
353
|
+
encoder.cbuf = new Uint8Array(max(bufferLen * 2, rightCopyLen));
|
|
354
|
+
// copy array
|
|
355
|
+
encoder.cbuf.set(uint8Array.subarray(leftCopyLen));
|
|
356
|
+
encoder.cpos = rightCopyLen;
|
|
389
357
|
}
|
|
390
|
-
return true
|
|
391
358
|
};
|
|
392
359
|
|
|
393
360
|
/**
|
|
394
|
-
*
|
|
395
|
-
* @template {V} OPTS
|
|
396
|
-
*
|
|
397
|
-
* @param {V} value
|
|
398
|
-
* @param {Array<OPTS>} options
|
|
399
|
-
*/
|
|
400
|
-
// @ts-ignore
|
|
401
|
-
const isOneOf = (value, options) => options.includes(value);
|
|
402
|
-
|
|
403
|
-
/**
|
|
404
|
-
* Isomorphic module to work access the environment (query params, env variables).
|
|
361
|
+
* Append an Uint8Array to Encoder.
|
|
405
362
|
*
|
|
406
|
-
* @
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
/* c8 ignore next 2 */
|
|
410
|
-
// @ts-ignore
|
|
411
|
-
const isNode = typeof process !== 'undefined' && process.release && /node|io\.js/.test(process.release.name) && Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]';
|
|
412
|
-
|
|
413
|
-
/* c8 ignore next */
|
|
414
|
-
const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && !isNode;
|
|
415
|
-
/* c8 ignore next 3 */
|
|
416
|
-
typeof navigator !== 'undefined'
|
|
417
|
-
? /Mac/.test(navigator.platform)
|
|
418
|
-
: false;
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* @type {Map<string,string>}
|
|
363
|
+
* @function
|
|
364
|
+
* @param {Encoder} encoder
|
|
365
|
+
* @param {Uint8Array} uint8Array
|
|
422
366
|
*/
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const computeParams = () => {
|
|
427
|
-
if (params === undefined) {
|
|
428
|
-
if (isNode) {
|
|
429
|
-
params = create$2();
|
|
430
|
-
const pargs = process.argv;
|
|
431
|
-
let currParamName = null;
|
|
432
|
-
for (let i = 0; i < pargs.length; i++) {
|
|
433
|
-
const parg = pargs[i];
|
|
434
|
-
if (parg[0] === '-') {
|
|
435
|
-
if (currParamName !== null) {
|
|
436
|
-
params.set(currParamName, '');
|
|
437
|
-
}
|
|
438
|
-
currParamName = parg;
|
|
439
|
-
} else {
|
|
440
|
-
if (currParamName !== null) {
|
|
441
|
-
params.set(currParamName, parg);
|
|
442
|
-
currParamName = null;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
if (currParamName !== null) {
|
|
447
|
-
params.set(currParamName, '');
|
|
448
|
-
}
|
|
449
|
-
// in ReactNative for example this would not be true (unless connected to the Remote Debugger)
|
|
450
|
-
} else if (typeof location === 'object') {
|
|
451
|
-
params = create$2(); // eslint-disable-next-line no-undef
|
|
452
|
-
(location.search || '?').slice(1).split('&').forEach((kv) => {
|
|
453
|
-
if (kv.length !== 0) {
|
|
454
|
-
const [key, value] = kv.split('=');
|
|
455
|
-
params.set(`--${fromCamelCase(key, '-')}`, value);
|
|
456
|
-
params.set(`-${fromCamelCase(key, '-')}`, value);
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
} else {
|
|
460
|
-
params = create$2();
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
return params
|
|
367
|
+
const writeVarUint8Array = (encoder, uint8Array) => {
|
|
368
|
+
writeVarUint(encoder, uint8Array.byteLength);
|
|
369
|
+
writeUint8Array(encoder, uint8Array);
|
|
464
370
|
};
|
|
465
|
-
/* c8 ignore stop */
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* @param {string} name
|
|
469
|
-
* @return {boolean}
|
|
470
|
-
*/
|
|
471
|
-
/* c8 ignore next */
|
|
472
|
-
const hasParam = (name) => computeParams().has(name);
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* @param {string} name
|
|
476
|
-
* @return {string|null}
|
|
477
|
-
*/
|
|
478
|
-
/* c8 ignore next 4 */
|
|
479
|
-
const getVariable = (name) =>
|
|
480
|
-
isNode
|
|
481
|
-
? undefinedToNull(process.env[name.toUpperCase()])
|
|
482
|
-
: undefinedToNull(varStorage.getItem(name));
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* @param {string} name
|
|
486
|
-
* @return {boolean}
|
|
487
|
-
*/
|
|
488
|
-
/* c8 ignore next 2 */
|
|
489
|
-
const hasConf = (name) =>
|
|
490
|
-
hasParam('--' + name) || getVariable(name) !== null;
|
|
491
|
-
|
|
492
|
-
/* c8 ignore next */
|
|
493
|
-
hasConf('production');
|
|
494
|
-
|
|
495
|
-
/* c8 ignore next 2 */
|
|
496
|
-
const forceColor = isNode &&
|
|
497
|
-
isOneOf(process.env.FORCE_COLOR, ['true', '1', '2']);
|
|
498
|
-
|
|
499
|
-
/* c8 ignore start */
|
|
500
|
-
!hasParam('no-colors') &&
|
|
501
|
-
(!isNode || process.stdout.isTTY || forceColor) && (
|
|
502
|
-
!isNode || hasParam('color') || forceColor ||
|
|
503
|
-
getVariable('COLORTERM') !== null ||
|
|
504
|
-
(getVariable('TERM') || '').includes('color')
|
|
505
|
-
);
|
|
506
|
-
/* c8 ignore stop */
|
|
507
371
|
|
|
508
372
|
/**
|
|
509
|
-
*
|
|
373
|
+
* Error helpers.
|
|
510
374
|
*
|
|
511
|
-
* @module
|
|
512
|
-
*/
|
|
513
|
-
|
|
514
|
-
const floor = Math.floor;
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* @function
|
|
518
|
-
* @param {number} a
|
|
519
|
-
* @param {number} b
|
|
520
|
-
* @return {number} The smaller element of a and b
|
|
521
|
-
*/
|
|
522
|
-
const min = (a, b) => a < b ? a : b;
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* @function
|
|
526
|
-
* @param {number} a
|
|
527
|
-
* @param {number} b
|
|
528
|
-
* @return {number} The bigger element of a and b
|
|
375
|
+
* @module error
|
|
529
376
|
*/
|
|
530
|
-
const max = (a, b) => a > b ? a : b;
|
|
531
|
-
|
|
532
|
-
/* eslint-env browser */
|
|
533
|
-
const BIT7 = 64;
|
|
534
|
-
const BIT8 = 128;
|
|
535
|
-
const BITS6 = 63;
|
|
536
|
-
const BITS7 = 127;
|
|
537
377
|
|
|
538
378
|
/**
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
* @module number
|
|
379
|
+
* @param {string} s
|
|
380
|
+
* @return {Error}
|
|
542
381
|
*/
|
|
543
|
-
|
|
544
|
-
const
|
|
382
|
+
/* c8 ignore next */
|
|
383
|
+
const create$1 = s => new Error(s);
|
|
545
384
|
|
|
546
385
|
/**
|
|
547
|
-
* Efficient schema-less binary
|
|
386
|
+
* Efficient schema-less binary decoding with support for variable length encoding.
|
|
548
387
|
*
|
|
549
|
-
* Use [lib0/
|
|
388
|
+
* Use [lib0/decoding] with [lib0/encoding]. Every encoding function has a corresponding decoding function.
|
|
550
389
|
*
|
|
551
390
|
* Encodes numbers in little-endian order (least to most significant byte order)
|
|
552
391
|
* and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
|
|
@@ -568,232 +407,15 @@ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
|
|
|
568
407
|
* decoding.hasContent(decoder) // => false - all data is read
|
|
569
408
|
* ```
|
|
570
409
|
*
|
|
571
|
-
* @module
|
|
410
|
+
* @module decoding
|
|
572
411
|
*/
|
|
573
412
|
|
|
413
|
+
|
|
414
|
+
const errorUnexpectedEndOfArray = create$1('Unexpected end of array');
|
|
415
|
+
const errorIntegerOutOfRange = create$1('Integer out of Range');
|
|
416
|
+
|
|
574
417
|
/**
|
|
575
|
-
* A
|
|
576
|
-
*/
|
|
577
|
-
class Encoder {
|
|
578
|
-
constructor () {
|
|
579
|
-
this.cpos = 0;
|
|
580
|
-
this.cbuf = new Uint8Array(100);
|
|
581
|
-
/**
|
|
582
|
-
* @type {Array<Uint8Array>}
|
|
583
|
-
*/
|
|
584
|
-
this.bufs = [];
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* @function
|
|
590
|
-
* @return {Encoder}
|
|
591
|
-
*/
|
|
592
|
-
const createEncoder = () => new Encoder();
|
|
593
|
-
|
|
594
|
-
/**
|
|
595
|
-
* The current length of the encoded data.
|
|
596
|
-
*
|
|
597
|
-
* @function
|
|
598
|
-
* @param {Encoder} encoder
|
|
599
|
-
* @return {number}
|
|
600
|
-
*/
|
|
601
|
-
const length = encoder => {
|
|
602
|
-
let len = encoder.cpos;
|
|
603
|
-
for (let i = 0; i < encoder.bufs.length; i++) {
|
|
604
|
-
len += encoder.bufs[i].length;
|
|
605
|
-
}
|
|
606
|
-
return len
|
|
607
|
-
};
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* Transform to Uint8Array.
|
|
611
|
-
*
|
|
612
|
-
* @function
|
|
613
|
-
* @param {Encoder} encoder
|
|
614
|
-
* @return {Uint8Array} The created ArrayBuffer.
|
|
615
|
-
*/
|
|
616
|
-
const toUint8Array = encoder => {
|
|
617
|
-
const uint8arr = new Uint8Array(length(encoder));
|
|
618
|
-
let curPos = 0;
|
|
619
|
-
for (let i = 0; i < encoder.bufs.length; i++) {
|
|
620
|
-
const d = encoder.bufs[i];
|
|
621
|
-
uint8arr.set(d, curPos);
|
|
622
|
-
curPos += d.length;
|
|
623
|
-
}
|
|
624
|
-
uint8arr.set(new Uint8Array(encoder.cbuf.buffer, 0, encoder.cpos), curPos);
|
|
625
|
-
return uint8arr
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
/**
|
|
629
|
-
* Write one byte to the encoder.
|
|
630
|
-
*
|
|
631
|
-
* @function
|
|
632
|
-
* @param {Encoder} encoder
|
|
633
|
-
* @param {number} num The byte that is to be encoded.
|
|
634
|
-
*/
|
|
635
|
-
const write = (encoder, num) => {
|
|
636
|
-
const bufferLen = encoder.cbuf.length;
|
|
637
|
-
if (encoder.cpos === bufferLen) {
|
|
638
|
-
encoder.bufs.push(encoder.cbuf);
|
|
639
|
-
encoder.cbuf = new Uint8Array(bufferLen * 2);
|
|
640
|
-
encoder.cpos = 0;
|
|
641
|
-
}
|
|
642
|
-
encoder.cbuf[encoder.cpos++] = num;
|
|
643
|
-
};
|
|
644
|
-
|
|
645
|
-
/**
|
|
646
|
-
* Write a variable length unsigned integer. Max encodable integer is 2^53.
|
|
647
|
-
*
|
|
648
|
-
* @function
|
|
649
|
-
* @param {Encoder} encoder
|
|
650
|
-
* @param {number} num The number that is to be encoded.
|
|
651
|
-
*/
|
|
652
|
-
const writeVarUint = (encoder, num) => {
|
|
653
|
-
while (num > BITS7) {
|
|
654
|
-
write(encoder, BIT8 | (BITS7 & num));
|
|
655
|
-
num = floor(num / 128); // shift >>> 7
|
|
656
|
-
}
|
|
657
|
-
write(encoder, BITS7 & num);
|
|
658
|
-
};
|
|
659
|
-
|
|
660
|
-
/**
|
|
661
|
-
* A cache to store strings temporarily
|
|
662
|
-
*/
|
|
663
|
-
const _strBuffer = new Uint8Array(30000);
|
|
664
|
-
const _maxStrBSize = _strBuffer.length / 3;
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* Write a variable length string.
|
|
668
|
-
*
|
|
669
|
-
* @function
|
|
670
|
-
* @param {Encoder} encoder
|
|
671
|
-
* @param {String} str The string that is to be encoded.
|
|
672
|
-
*/
|
|
673
|
-
const _writeVarStringNative = (encoder, str) => {
|
|
674
|
-
if (str.length < _maxStrBSize) {
|
|
675
|
-
// We can encode the string into the existing buffer
|
|
676
|
-
/* c8 ignore next */
|
|
677
|
-
const written = utf8TextEncoder.encodeInto(str, _strBuffer).written || 0;
|
|
678
|
-
writeVarUint(encoder, written);
|
|
679
|
-
for (let i = 0; i < written; i++) {
|
|
680
|
-
write(encoder, _strBuffer[i]);
|
|
681
|
-
}
|
|
682
|
-
} else {
|
|
683
|
-
writeVarUint8Array(encoder, encodeUtf8(str));
|
|
684
|
-
}
|
|
685
|
-
};
|
|
686
|
-
|
|
687
|
-
/**
|
|
688
|
-
* Write a variable length string.
|
|
689
|
-
*
|
|
690
|
-
* @function
|
|
691
|
-
* @param {Encoder} encoder
|
|
692
|
-
* @param {String} str The string that is to be encoded.
|
|
693
|
-
*/
|
|
694
|
-
const _writeVarStringPolyfill = (encoder, str) => {
|
|
695
|
-
const encodedString = unescape(encodeURIComponent(str));
|
|
696
|
-
const len = encodedString.length;
|
|
697
|
-
writeVarUint(encoder, len);
|
|
698
|
-
for (let i = 0; i < len; i++) {
|
|
699
|
-
write(encoder, /** @type {number} */ (encodedString.codePointAt(i)));
|
|
700
|
-
}
|
|
701
|
-
};
|
|
702
|
-
|
|
703
|
-
/**
|
|
704
|
-
* Write a variable length string.
|
|
705
|
-
*
|
|
706
|
-
* @function
|
|
707
|
-
* @param {Encoder} encoder
|
|
708
|
-
* @param {String} str The string that is to be encoded.
|
|
709
|
-
*/
|
|
710
|
-
/* c8 ignore next */
|
|
711
|
-
const writeVarString = (utf8TextEncoder && /** @type {any} */ (utf8TextEncoder).encodeInto) ? _writeVarStringNative : _writeVarStringPolyfill;
|
|
712
|
-
|
|
713
|
-
/**
|
|
714
|
-
* Append fixed-length Uint8Array to the encoder.
|
|
715
|
-
*
|
|
716
|
-
* @function
|
|
717
|
-
* @param {Encoder} encoder
|
|
718
|
-
* @param {Uint8Array} uint8Array
|
|
719
|
-
*/
|
|
720
|
-
const writeUint8Array = (encoder, uint8Array) => {
|
|
721
|
-
const bufferLen = encoder.cbuf.length;
|
|
722
|
-
const cpos = encoder.cpos;
|
|
723
|
-
const leftCopyLen = min(bufferLen - cpos, uint8Array.length);
|
|
724
|
-
const rightCopyLen = uint8Array.length - leftCopyLen;
|
|
725
|
-
encoder.cbuf.set(uint8Array.subarray(0, leftCopyLen), cpos);
|
|
726
|
-
encoder.cpos += leftCopyLen;
|
|
727
|
-
if (rightCopyLen > 0) {
|
|
728
|
-
// Still something to write, write right half..
|
|
729
|
-
// Append new buffer
|
|
730
|
-
encoder.bufs.push(encoder.cbuf);
|
|
731
|
-
// must have at least size of remaining buffer
|
|
732
|
-
encoder.cbuf = new Uint8Array(max(bufferLen * 2, rightCopyLen));
|
|
733
|
-
// copy array
|
|
734
|
-
encoder.cbuf.set(uint8Array.subarray(leftCopyLen));
|
|
735
|
-
encoder.cpos = rightCopyLen;
|
|
736
|
-
}
|
|
737
|
-
};
|
|
738
|
-
|
|
739
|
-
/**
|
|
740
|
-
* Append an Uint8Array to Encoder.
|
|
741
|
-
*
|
|
742
|
-
* @function
|
|
743
|
-
* @param {Encoder} encoder
|
|
744
|
-
* @param {Uint8Array} uint8Array
|
|
745
|
-
*/
|
|
746
|
-
const writeVarUint8Array = (encoder, uint8Array) => {
|
|
747
|
-
writeVarUint(encoder, uint8Array.byteLength);
|
|
748
|
-
writeUint8Array(encoder, uint8Array);
|
|
749
|
-
};
|
|
750
|
-
|
|
751
|
-
/**
|
|
752
|
-
* Error helpers.
|
|
753
|
-
*
|
|
754
|
-
* @module error
|
|
755
|
-
*/
|
|
756
|
-
|
|
757
|
-
/**
|
|
758
|
-
* @param {string} s
|
|
759
|
-
* @return {Error}
|
|
760
|
-
*/
|
|
761
|
-
/* c8 ignore next */
|
|
762
|
-
const create = s => new Error(s);
|
|
763
|
-
|
|
764
|
-
/**
|
|
765
|
-
* Efficient schema-less binary decoding with support for variable length encoding.
|
|
766
|
-
*
|
|
767
|
-
* Use [lib0/decoding] with [lib0/encoding]. Every encoding function has a corresponding decoding function.
|
|
768
|
-
*
|
|
769
|
-
* Encodes numbers in little-endian order (least to most significant byte order)
|
|
770
|
-
* and is compatible with Golang's binary encoding (https://golang.org/pkg/encoding/binary/)
|
|
771
|
-
* which is also used in Protocol Buffers.
|
|
772
|
-
*
|
|
773
|
-
* ```js
|
|
774
|
-
* // encoding step
|
|
775
|
-
* const encoder = encoding.createEncoder()
|
|
776
|
-
* encoding.writeVarUint(encoder, 256)
|
|
777
|
-
* encoding.writeVarString(encoder, 'Hello world!')
|
|
778
|
-
* const buf = encoding.toUint8Array(encoder)
|
|
779
|
-
* ```
|
|
780
|
-
*
|
|
781
|
-
* ```js
|
|
782
|
-
* // decoding step
|
|
783
|
-
* const decoder = decoding.createDecoder(buf)
|
|
784
|
-
* decoding.readVarUint(decoder) // => 256
|
|
785
|
-
* decoding.readVarString(decoder) // => 'Hello world!'
|
|
786
|
-
* decoding.hasContent(decoder) // => false - all data is read
|
|
787
|
-
* ```
|
|
788
|
-
*
|
|
789
|
-
* @module decoding
|
|
790
|
-
*/
|
|
791
|
-
|
|
792
|
-
const errorUnexpectedEndOfArray = create('Unexpected end of array');
|
|
793
|
-
const errorIntegerOutOfRange = create('Integer out of Range');
|
|
794
|
-
|
|
795
|
-
/**
|
|
796
|
-
* A Decoder handles the decoding of an Uint8Array.
|
|
418
|
+
* A Decoder handles the decoding of an Uint8Array.
|
|
797
419
|
*/
|
|
798
420
|
class Decoder {
|
|
799
421
|
/**
|
|
@@ -1003,255 +625,66 @@ const peekVarString = decoder => {
|
|
|
1003
625
|
};
|
|
1004
626
|
|
|
1005
627
|
/**
|
|
1006
|
-
* Utility
|
|
628
|
+
* Utility module to work with time.
|
|
1007
629
|
*
|
|
1008
|
-
* @module
|
|
630
|
+
* @module time
|
|
1009
631
|
*/
|
|
1010
632
|
|
|
1011
|
-
/**
|
|
1012
|
-
* @param {number} len
|
|
1013
|
-
*/
|
|
1014
|
-
const createUint8ArrayFromLen = len => new Uint8Array(len);
|
|
1015
633
|
|
|
1016
634
|
/**
|
|
1017
|
-
*
|
|
635
|
+
* Return current unix time.
|
|
1018
636
|
*
|
|
1019
|
-
* @
|
|
1020
|
-
* @param {number} byteOffset
|
|
1021
|
-
* @param {number} length
|
|
637
|
+
* @return {number}
|
|
1022
638
|
*/
|
|
1023
|
-
const
|
|
639
|
+
const getUnixTime = Date.now;
|
|
1024
640
|
|
|
1025
641
|
/**
|
|
1026
|
-
*
|
|
642
|
+
* Utility module to work with key-value stores.
|
|
1027
643
|
*
|
|
1028
|
-
* @
|
|
644
|
+
* @module map
|
|
1029
645
|
*/
|
|
1030
|
-
const createUint8ArrayFromArrayBuffer = buffer => new Uint8Array(buffer);
|
|
1031
646
|
|
|
1032
|
-
/* c8 ignore start */
|
|
1033
647
|
/**
|
|
1034
|
-
*
|
|
1035
|
-
*
|
|
648
|
+
* Creates a new Map instance.
|
|
649
|
+
*
|
|
650
|
+
* @function
|
|
651
|
+
* @return {Map<any, any>}
|
|
652
|
+
*
|
|
653
|
+
* @function
|
|
1036
654
|
*/
|
|
1037
|
-
const
|
|
1038
|
-
let s = '';
|
|
1039
|
-
for (let i = 0; i < bytes.byteLength; i++) {
|
|
1040
|
-
s += fromCharCode(bytes[i]);
|
|
1041
|
-
}
|
|
1042
|
-
// eslint-disable-next-line no-undef
|
|
1043
|
-
return btoa(s)
|
|
1044
|
-
};
|
|
1045
|
-
/* c8 ignore stop */
|
|
655
|
+
const create = () => new Map();
|
|
1046
656
|
|
|
1047
657
|
/**
|
|
1048
|
-
*
|
|
1049
|
-
* @return {string}
|
|
1050
|
-
*/
|
|
1051
|
-
const toBase64Node = bytes => Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('base64');
|
|
1052
|
-
|
|
1053
|
-
/* c8 ignore start */
|
|
1054
|
-
/**
|
|
1055
|
-
* @param {string} s
|
|
1056
|
-
* @return {Uint8Array}
|
|
1057
|
-
*/
|
|
1058
|
-
const fromBase64Browser = s => {
|
|
1059
|
-
// eslint-disable-next-line no-undef
|
|
1060
|
-
const a = atob(s);
|
|
1061
|
-
const bytes = createUint8ArrayFromLen(a.length);
|
|
1062
|
-
for (let i = 0; i < a.length; i++) {
|
|
1063
|
-
bytes[i] = a.charCodeAt(i);
|
|
1064
|
-
}
|
|
1065
|
-
return bytes
|
|
1066
|
-
};
|
|
1067
|
-
/* c8 ignore stop */
|
|
1068
|
-
|
|
1069
|
-
/**
|
|
1070
|
-
* @param {string} s
|
|
1071
|
-
*/
|
|
1072
|
-
const fromBase64Node = s => {
|
|
1073
|
-
const buf = Buffer.from(s, 'base64');
|
|
1074
|
-
return createUint8ArrayViewFromArrayBuffer(buf.buffer, buf.byteOffset, buf.byteLength)
|
|
1075
|
-
};
|
|
1076
|
-
|
|
1077
|
-
/* c8 ignore next */
|
|
1078
|
-
const toBase64 = isBrowser ? toBase64Browser : toBase64Node;
|
|
1079
|
-
|
|
1080
|
-
/* c8 ignore next */
|
|
1081
|
-
const fromBase64 = isBrowser ? fromBase64Browser : fromBase64Node;
|
|
1082
|
-
|
|
1083
|
-
/* eslint-env browser */
|
|
1084
|
-
|
|
1085
|
-
/**
|
|
1086
|
-
* @typedef {Object} Channel
|
|
1087
|
-
* @property {Set<function(any, any):any>} Channel.subs
|
|
1088
|
-
* @property {any} Channel.bc
|
|
1089
|
-
*/
|
|
1090
|
-
|
|
1091
|
-
/**
|
|
1092
|
-
* @type {Map<string, Channel>}
|
|
1093
|
-
*/
|
|
1094
|
-
const channels = new Map();
|
|
1095
|
-
|
|
1096
|
-
/* c8 ignore start */
|
|
1097
|
-
class LocalStoragePolyfill {
|
|
1098
|
-
/**
|
|
1099
|
-
* @param {string} room
|
|
1100
|
-
*/
|
|
1101
|
-
constructor (room) {
|
|
1102
|
-
this.room = room;
|
|
1103
|
-
/**
|
|
1104
|
-
* @type {null|function({data:ArrayBuffer}):void}
|
|
1105
|
-
*/
|
|
1106
|
-
this.onmessage = null;
|
|
1107
|
-
/**
|
|
1108
|
-
* @param {any} e
|
|
1109
|
-
*/
|
|
1110
|
-
this._onChange = e => e.key === room && this.onmessage !== null && this.onmessage({ data: fromBase64(e.newValue || '') });
|
|
1111
|
-
onChange(this._onChange);
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
/**
|
|
1115
|
-
* @param {ArrayBuffer} buf
|
|
1116
|
-
*/
|
|
1117
|
-
postMessage (buf) {
|
|
1118
|
-
varStorage.setItem(this.room, toBase64(createUint8ArrayFromArrayBuffer(buf)));
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
close () {
|
|
1122
|
-
offChange(this._onChange);
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
/* c8 ignore stop */
|
|
1126
|
-
|
|
1127
|
-
// Use BroadcastChannel or Polyfill
|
|
1128
|
-
/* c8 ignore next */
|
|
1129
|
-
const BC = typeof BroadcastChannel === 'undefined' ? LocalStoragePolyfill : BroadcastChannel;
|
|
1130
|
-
|
|
1131
|
-
/**
|
|
1132
|
-
* @param {string} room
|
|
1133
|
-
* @return {Channel}
|
|
1134
|
-
*/
|
|
1135
|
-
const getChannel = room =>
|
|
1136
|
-
setIfUndefined(channels, room, () => {
|
|
1137
|
-
const subs = create$1();
|
|
1138
|
-
const bc = new BC(room);
|
|
1139
|
-
/**
|
|
1140
|
-
* @param {{data:ArrayBuffer}} e
|
|
1141
|
-
*/
|
|
1142
|
-
/* c8 ignore next */
|
|
1143
|
-
bc.onmessage = e => subs.forEach(sub => sub(e.data, 'broadcastchannel'));
|
|
1144
|
-
return {
|
|
1145
|
-
bc, subs
|
|
1146
|
-
}
|
|
1147
|
-
});
|
|
1148
|
-
|
|
1149
|
-
/**
|
|
1150
|
-
* Subscribe to global `publish` events.
|
|
1151
|
-
*
|
|
1152
|
-
* @function
|
|
1153
|
-
* @param {string} room
|
|
1154
|
-
* @param {function(any, any):any} f
|
|
1155
|
-
*/
|
|
1156
|
-
const subscribe = (room, f) => {
|
|
1157
|
-
getChannel(room).subs.add(f);
|
|
1158
|
-
return f
|
|
1159
|
-
};
|
|
1160
|
-
|
|
1161
|
-
/**
|
|
1162
|
-
* Unsubscribe from `publish` global events.
|
|
1163
|
-
*
|
|
1164
|
-
* @function
|
|
1165
|
-
* @param {string} room
|
|
1166
|
-
* @param {function(any, any):any} f
|
|
1167
|
-
*/
|
|
1168
|
-
const unsubscribe = (room, f) => {
|
|
1169
|
-
const channel = getChannel(room);
|
|
1170
|
-
const unsubscribed = channel.subs.delete(f);
|
|
1171
|
-
if (unsubscribed && channel.subs.size === 0) {
|
|
1172
|
-
channel.bc.close();
|
|
1173
|
-
channels.delete(room);
|
|
1174
|
-
}
|
|
1175
|
-
return unsubscribed
|
|
1176
|
-
};
|
|
1177
|
-
|
|
1178
|
-
/**
|
|
1179
|
-
* Publish data to all subscribers (including subscribers on this tab)
|
|
1180
|
-
*
|
|
1181
|
-
* @function
|
|
1182
|
-
* @param {string} room
|
|
1183
|
-
* @param {any} data
|
|
1184
|
-
* @param {any} [origin]
|
|
1185
|
-
*/
|
|
1186
|
-
const publish = (room, data, origin = null) => {
|
|
1187
|
-
const c = getChannel(room);
|
|
1188
|
-
c.bc.postMessage(data);
|
|
1189
|
-
c.subs.forEach(sub => sub(data, origin));
|
|
1190
|
-
};
|
|
1191
|
-
|
|
1192
|
-
/**
|
|
1193
|
-
* Mutual exclude for JavaScript.
|
|
1194
|
-
*
|
|
1195
|
-
* @module mutex
|
|
1196
|
-
*/
|
|
1197
|
-
|
|
1198
|
-
/**
|
|
1199
|
-
* @callback mutex
|
|
1200
|
-
* @param {function():void} cb Only executed when this mutex is not in the current stack
|
|
1201
|
-
* @param {function():void} [elseCb] Executed when this mutex is in the current stack
|
|
1202
|
-
*/
|
|
1203
|
-
|
|
1204
|
-
/**
|
|
1205
|
-
* Creates a mutual exclude function with the following property:
|
|
658
|
+
* Get map property. Create T if property is undefined and set T on map.
|
|
1206
659
|
*
|
|
1207
660
|
* ```js
|
|
1208
|
-
* const
|
|
1209
|
-
*
|
|
1210
|
-
* // This function is immediately executed
|
|
1211
|
-
* mutex(() => {
|
|
1212
|
-
* // This function is not executed, as the mutex is already active.
|
|
1213
|
-
* })
|
|
1214
|
-
* })
|
|
661
|
+
* const listeners = map.setIfUndefined(events, 'eventName', set.create)
|
|
662
|
+
* listeners.add(listener)
|
|
1215
663
|
* ```
|
|
1216
664
|
*
|
|
1217
|
-
* @
|
|
1218
|
-
* @
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
}
|
|
1230
|
-
} else if (g !== undefined) {
|
|
1231
|
-
g();
|
|
1232
|
-
}
|
|
665
|
+
* @function
|
|
666
|
+
* @template {Map<any, any>} MAP
|
|
667
|
+
* @template {MAP extends Map<any,infer V> ? function():V : unknown} CF
|
|
668
|
+
* @param {MAP} map
|
|
669
|
+
* @param {MAP extends Map<infer K,any> ? K : unknown} key
|
|
670
|
+
* @param {CF} createT
|
|
671
|
+
* @return {ReturnType<CF>}
|
|
672
|
+
*/
|
|
673
|
+
const setIfUndefined = (map, key, createT) => {
|
|
674
|
+
let set = map.get(key);
|
|
675
|
+
if (set === undefined) {
|
|
676
|
+
map.set(key, set = createT());
|
|
1233
677
|
}
|
|
678
|
+
return set
|
|
1234
679
|
};
|
|
1235
680
|
|
|
1236
|
-
/**
|
|
1237
|
-
* Utility module to work with time.
|
|
1238
|
-
*
|
|
1239
|
-
* @module time
|
|
1240
|
-
*/
|
|
1241
|
-
|
|
1242
|
-
/**
|
|
1243
|
-
* Return current unix time.
|
|
1244
|
-
*
|
|
1245
|
-
* @return {number}
|
|
1246
|
-
*/
|
|
1247
|
-
const getUnixTime = Date.now;
|
|
1248
|
-
|
|
1249
681
|
/**
|
|
1250
682
|
* Observable class prototype.
|
|
1251
683
|
*
|
|
1252
684
|
* @module observable
|
|
1253
685
|
*/
|
|
1254
686
|
|
|
687
|
+
|
|
1255
688
|
/* c8 ignore start */
|
|
1256
689
|
/**
|
|
1257
690
|
* Handles named events.
|
|
@@ -1265,7 +698,7 @@ class Observable {
|
|
|
1265
698
|
* Some desc.
|
|
1266
699
|
* @type {Map<N, any>}
|
|
1267
700
|
*/
|
|
1268
|
-
this._observers = create
|
|
701
|
+
this._observers = create();
|
|
1269
702
|
}
|
|
1270
703
|
|
|
1271
704
|
/**
|
|
@@ -1273,7 +706,7 @@ class Observable {
|
|
|
1273
706
|
* @param {function} f
|
|
1274
707
|
*/
|
|
1275
708
|
on (name, f) {
|
|
1276
|
-
setIfUndefined(this._observers, name, create$
|
|
709
|
+
setIfUndefined(this._observers, name, create$2).add(f);
|
|
1277
710
|
}
|
|
1278
711
|
|
|
1279
712
|
/**
|
|
@@ -1316,105 +749,257 @@ class Observable {
|
|
|
1316
749
|
*/
|
|
1317
750
|
emit (name, args) {
|
|
1318
751
|
// copy all listeners to an array first to make sure that no event is emitted to listeners that are subscribed while the event handler is called.
|
|
1319
|
-
return from((this._observers.get(name) || create
|
|
752
|
+
return from((this._observers.get(name) || create()).values()).forEach(f => f(...args))
|
|
1320
753
|
}
|
|
1321
754
|
|
|
1322
755
|
destroy () {
|
|
1323
|
-
this._observers = create
|
|
756
|
+
this._observers = create();
|
|
1324
757
|
}
|
|
1325
758
|
}
|
|
1326
759
|
/* c8 ignore end */
|
|
1327
760
|
|
|
1328
761
|
/**
|
|
1329
|
-
*
|
|
762
|
+
* Utility functions for working with EcmaScript objects.
|
|
763
|
+
*
|
|
764
|
+
* @module object
|
|
1330
765
|
*/
|
|
1331
766
|
|
|
1332
|
-
const outdatedTimeout = 30000;
|
|
1333
767
|
|
|
1334
768
|
/**
|
|
1335
|
-
* @
|
|
1336
|
-
* @property {number} MetaClientState.clock
|
|
1337
|
-
* @property {number} MetaClientState.lastUpdated unix timestamp
|
|
769
|
+
* @param {Object<string,any>} obj
|
|
1338
770
|
*/
|
|
771
|
+
const keys = Object.keys;
|
|
1339
772
|
|
|
1340
773
|
/**
|
|
1341
|
-
*
|
|
1342
|
-
* (cursor, username, status, ..). Each client can update its own local state and listen to state changes of
|
|
1343
|
-
* remote clients. Every client may set a state of a remote peer to `null` to mark the client as offline.
|
|
1344
|
-
*
|
|
1345
|
-
* Each client is identified by a unique client id (something we borrow from `doc.clientID`). A client can override
|
|
1346
|
-
* its own state by propagating a message with an increasing timestamp (`clock`). If such a message is received, it is
|
|
1347
|
-
* applied if the known state of that client is older than the new state (`clock < newClock`). If a client thinks that
|
|
1348
|
-
* a remote client is offline, it may propagate a message with
|
|
1349
|
-
* `{ clock: currentClientClock, state: null, client: remoteClient }`. If such a
|
|
1350
|
-
* message is received, and the known clock of that client equals the received clock, it will override the state with `null`.
|
|
1351
|
-
*
|
|
1352
|
-
* Before a client disconnects, it should propagate a `null` state with an updated clock.
|
|
1353
|
-
*
|
|
1354
|
-
* Awareness states must be updated every 30 seconds. Otherwise the Awareness instance will delete the client state.
|
|
774
|
+
* @todo implement mapToArray & map
|
|
1355
775
|
*
|
|
1356
|
-
* @
|
|
776
|
+
* @template R
|
|
777
|
+
* @param {Object<string,any>} obj
|
|
778
|
+
* @param {function(any,string):R} f
|
|
779
|
+
* @return {Array<R>}
|
|
1357
780
|
*/
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
constructor (doc) {
|
|
1363
|
-
super();
|
|
1364
|
-
this.doc = doc;
|
|
1365
|
-
/**
|
|
1366
|
-
* @type {number}
|
|
1367
|
-
*/
|
|
1368
|
-
this.clientID = doc.clientID;
|
|
1369
|
-
/**
|
|
1370
|
-
* Maps from client id to client state
|
|
1371
|
-
* @type {Map<number, Object<string, any>>}
|
|
1372
|
-
*/
|
|
1373
|
-
this.states = new Map();
|
|
1374
|
-
/**
|
|
1375
|
-
* @type {Map<number, MetaClientState>}
|
|
1376
|
-
*/
|
|
1377
|
-
this.meta = new Map();
|
|
1378
|
-
this._checkInterval = /** @type {any} */ (setInterval(() => {
|
|
1379
|
-
const now = getUnixTime();
|
|
1380
|
-
if (this.getLocalState() !== null && (outdatedTimeout / 2 <= now - /** @type {{lastUpdated:number}} */ (this.meta.get(this.clientID)).lastUpdated)) {
|
|
1381
|
-
// renew local clock
|
|
1382
|
-
this.setLocalState(this.getLocalState());
|
|
1383
|
-
}
|
|
1384
|
-
/**
|
|
1385
|
-
* @type {Array<number>}
|
|
1386
|
-
*/
|
|
1387
|
-
const remove = [];
|
|
1388
|
-
this.meta.forEach((meta, clientid) => {
|
|
1389
|
-
if (clientid !== this.clientID && outdatedTimeout <= now - meta.lastUpdated && this.states.has(clientid)) {
|
|
1390
|
-
remove.push(clientid);
|
|
1391
|
-
}
|
|
1392
|
-
});
|
|
1393
|
-
if (remove.length > 0) {
|
|
1394
|
-
removeAwarenessStates(this, remove, 'timeout');
|
|
1395
|
-
}
|
|
1396
|
-
}, floor(outdatedTimeout / 10)));
|
|
1397
|
-
doc.on('destroy', () => {
|
|
1398
|
-
this.destroy();
|
|
1399
|
-
});
|
|
1400
|
-
this.setLocalState({});
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
destroy () {
|
|
1404
|
-
this.emit('destroy', [this]);
|
|
1405
|
-
this.setLocalState(null);
|
|
1406
|
-
super.destroy();
|
|
1407
|
-
clearInterval(this._checkInterval);
|
|
781
|
+
const map = (obj, f) => {
|
|
782
|
+
const results = [];
|
|
783
|
+
for (const key in obj) {
|
|
784
|
+
results.push(f(obj[key], key));
|
|
1408
785
|
}
|
|
786
|
+
return results
|
|
787
|
+
};
|
|
1409
788
|
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
789
|
+
/**
|
|
790
|
+
* @deprecated use object.size instead
|
|
791
|
+
* @param {Object<string,any>} obj
|
|
792
|
+
* @return {number}
|
|
793
|
+
*/
|
|
794
|
+
const length = obj => keys(obj).length;
|
|
1416
795
|
|
|
1417
|
-
|
|
796
|
+
/**
|
|
797
|
+
* Calls `Object.prototype.hasOwnProperty`.
|
|
798
|
+
*
|
|
799
|
+
* @param {any} obj
|
|
800
|
+
* @param {string|symbol} key
|
|
801
|
+
* @return {boolean}
|
|
802
|
+
*/
|
|
803
|
+
const hasProperty = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Common functions and function call helpers.
|
|
807
|
+
*
|
|
808
|
+
* @module function
|
|
809
|
+
*/
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* @template T
|
|
814
|
+
*
|
|
815
|
+
* @param {T} a
|
|
816
|
+
* @param {T} b
|
|
817
|
+
* @return {boolean}
|
|
818
|
+
*/
|
|
819
|
+
const equalityStrict = (a, b) => a === b;
|
|
820
|
+
|
|
821
|
+
/* c8 ignore start */
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* @param {any} a
|
|
825
|
+
* @param {any} b
|
|
826
|
+
* @return {boolean}
|
|
827
|
+
*/
|
|
828
|
+
const equalityDeep = (a, b) => {
|
|
829
|
+
if (a == null || b == null) {
|
|
830
|
+
return equalityStrict(a, b)
|
|
831
|
+
}
|
|
832
|
+
if (a.constructor !== b.constructor) {
|
|
833
|
+
return false
|
|
834
|
+
}
|
|
835
|
+
if (a === b) {
|
|
836
|
+
return true
|
|
837
|
+
}
|
|
838
|
+
switch (a.constructor) {
|
|
839
|
+
case ArrayBuffer:
|
|
840
|
+
a = new Uint8Array(a);
|
|
841
|
+
b = new Uint8Array(b);
|
|
842
|
+
// eslint-disable-next-line no-fallthrough
|
|
843
|
+
case Uint8Array: {
|
|
844
|
+
if (a.byteLength !== b.byteLength) {
|
|
845
|
+
return false
|
|
846
|
+
}
|
|
847
|
+
for (let i = 0; i < a.length; i++) {
|
|
848
|
+
if (a[i] !== b[i]) {
|
|
849
|
+
return false
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
break
|
|
853
|
+
}
|
|
854
|
+
case Set: {
|
|
855
|
+
if (a.size !== b.size) {
|
|
856
|
+
return false
|
|
857
|
+
}
|
|
858
|
+
for (const value of a) {
|
|
859
|
+
if (!b.has(value)) {
|
|
860
|
+
return false
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
break
|
|
864
|
+
}
|
|
865
|
+
case Map: {
|
|
866
|
+
if (a.size !== b.size) {
|
|
867
|
+
return false
|
|
868
|
+
}
|
|
869
|
+
for (const key of a.keys()) {
|
|
870
|
+
if (!b.has(key) || !equalityDeep(a.get(key), b.get(key))) {
|
|
871
|
+
return false
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
break
|
|
875
|
+
}
|
|
876
|
+
case Object:
|
|
877
|
+
if (length(a) !== length(b)) {
|
|
878
|
+
return false
|
|
879
|
+
}
|
|
880
|
+
for (const key in a) {
|
|
881
|
+
if (!hasProperty(a, key) || !equalityDeep(a[key], b[key])) {
|
|
882
|
+
return false
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
break
|
|
886
|
+
case Array:
|
|
887
|
+
if (a.length !== b.length) {
|
|
888
|
+
return false
|
|
889
|
+
}
|
|
890
|
+
for (let i = 0; i < a.length; i++) {
|
|
891
|
+
if (!equalityDeep(a[i], b[i])) {
|
|
892
|
+
return false
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
break
|
|
896
|
+
default:
|
|
897
|
+
return false
|
|
898
|
+
}
|
|
899
|
+
return true
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* @template V
|
|
904
|
+
* @template {V} OPTS
|
|
905
|
+
*
|
|
906
|
+
* @param {V} value
|
|
907
|
+
* @param {Array<OPTS>} options
|
|
908
|
+
*/
|
|
909
|
+
// @ts-ignore
|
|
910
|
+
const isOneOf = (value, options) => options.includes(value);
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* @module awareness-protocol
|
|
914
|
+
*/
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
const outdatedTimeout = 30000;
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* @typedef {Object} MetaClientState
|
|
921
|
+
* @property {number} MetaClientState.clock
|
|
922
|
+
* @property {number} MetaClientState.lastUpdated unix timestamp
|
|
923
|
+
*/
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* The Awareness class implements a simple shared state protocol that can be used for non-persistent data like awareness information
|
|
927
|
+
* (cursor, username, status, ..). Each client can update its own local state and listen to state changes of
|
|
928
|
+
* remote clients. Every client may set a state of a remote peer to `null` to mark the client as offline.
|
|
929
|
+
*
|
|
930
|
+
* Each client is identified by a unique client id (something we borrow from `doc.clientID`). A client can override
|
|
931
|
+
* its own state by propagating a message with an increasing timestamp (`clock`). If such a message is received, it is
|
|
932
|
+
* applied if the known state of that client is older than the new state (`clock < newClock`). If a client thinks that
|
|
933
|
+
* a remote client is offline, it may propagate a message with
|
|
934
|
+
* `{ clock: currentClientClock, state: null, client: remoteClient }`. If such a
|
|
935
|
+
* message is received, and the known clock of that client equals the received clock, it will override the state with `null`.
|
|
936
|
+
*
|
|
937
|
+
* Before a client disconnects, it should propagate a `null` state with an updated clock.
|
|
938
|
+
*
|
|
939
|
+
* Awareness states must be updated every 30 seconds. Otherwise the Awareness instance will delete the client state.
|
|
940
|
+
*
|
|
941
|
+
* @extends {Observable<string>}
|
|
942
|
+
*/
|
|
943
|
+
class Awareness extends Observable {
|
|
944
|
+
/**
|
|
945
|
+
* @param {Y.Doc} doc
|
|
946
|
+
*/
|
|
947
|
+
constructor (doc) {
|
|
948
|
+
super();
|
|
949
|
+
this.doc = doc;
|
|
950
|
+
/**
|
|
951
|
+
* @type {number}
|
|
952
|
+
*/
|
|
953
|
+
this.clientID = doc.clientID;
|
|
954
|
+
/**
|
|
955
|
+
* Maps from client id to client state
|
|
956
|
+
* @type {Map<number, Object<string, any>>}
|
|
957
|
+
*/
|
|
958
|
+
this.states = new Map();
|
|
959
|
+
/**
|
|
960
|
+
* @type {Map<number, MetaClientState>}
|
|
961
|
+
*/
|
|
962
|
+
this.meta = new Map();
|
|
963
|
+
this._checkInterval = /** @type {any} */ (setInterval(() => {
|
|
964
|
+
const now = getUnixTime();
|
|
965
|
+
if (this.getLocalState() !== null && (outdatedTimeout / 2 <= now - /** @type {{lastUpdated:number}} */ (this.meta.get(this.clientID)).lastUpdated)) {
|
|
966
|
+
// renew local clock
|
|
967
|
+
this.setLocalState(this.getLocalState());
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* @type {Array<number>}
|
|
971
|
+
*/
|
|
972
|
+
const remove = [];
|
|
973
|
+
this.meta.forEach((meta, clientid) => {
|
|
974
|
+
if (clientid !== this.clientID && outdatedTimeout <= now - meta.lastUpdated && this.states.has(clientid)) {
|
|
975
|
+
remove.push(clientid);
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
if (remove.length > 0) {
|
|
979
|
+
removeAwarenessStates(this, remove, 'timeout');
|
|
980
|
+
}
|
|
981
|
+
}, floor(outdatedTimeout / 10)));
|
|
982
|
+
doc.on('destroy', () => {
|
|
983
|
+
this.destroy();
|
|
984
|
+
});
|
|
985
|
+
this.setLocalState({});
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
destroy () {
|
|
989
|
+
this.emit('destroy', [this]);
|
|
990
|
+
this.setLocalState(null);
|
|
991
|
+
super.destroy();
|
|
992
|
+
clearInterval(this._checkInterval);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* @return {Object<string,any>|null}
|
|
997
|
+
*/
|
|
998
|
+
getLocalState () {
|
|
999
|
+
return this.states.get(this.clientID) || null
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1418
1003
|
* @param {Object<string,any>|null} state
|
|
1419
1004
|
*/
|
|
1420
1005
|
setLocalState (state) {
|
|
@@ -1588,8 +1173,10 @@ const applyAwarenessUpdate = (awareness, update, origin) => {
|
|
|
1588
1173
|
|
|
1589
1174
|
class EventEmitter {
|
|
1590
1175
|
constructor() {
|
|
1176
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
1591
1177
|
this.callbacks = {};
|
|
1592
1178
|
}
|
|
1179
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
1593
1180
|
on(event, fn) {
|
|
1594
1181
|
if (!this.callbacks[event]) {
|
|
1595
1182
|
this.callbacks[event] = [];
|
|
@@ -1604,6 +1191,7 @@ class EventEmitter {
|
|
|
1604
1191
|
}
|
|
1605
1192
|
return this;
|
|
1606
1193
|
}
|
|
1194
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
1607
1195
|
off(event, fn) {
|
|
1608
1196
|
const callbacks = this.callbacks[event];
|
|
1609
1197
|
if (callbacks) {
|
|
@@ -1627,6 +1215,7 @@ class EventEmitter {
|
|
|
1627
1215
|
* @module url
|
|
1628
1216
|
*/
|
|
1629
1217
|
|
|
1218
|
+
|
|
1630
1219
|
/**
|
|
1631
1220
|
* @param {Object<string,string>} params
|
|
1632
1221
|
* @return {string}
|
|
@@ -1679,7 +1268,32 @@ class IncomingMessage {
|
|
|
1679
1268
|
return writeVarUint8Array(this.encoder, data);
|
|
1680
1269
|
}
|
|
1681
1270
|
length() {
|
|
1682
|
-
return length(this.encoder);
|
|
1271
|
+
return length$1(this.encoder);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
class OutgoingMessage {
|
|
1276
|
+
constructor() {
|
|
1277
|
+
this.encoder = createEncoder();
|
|
1278
|
+
}
|
|
1279
|
+
get(args) {
|
|
1280
|
+
return args.encoder;
|
|
1281
|
+
}
|
|
1282
|
+
toUint8Array() {
|
|
1283
|
+
return toUint8Array(this.encoder);
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
class CloseMessage extends OutgoingMessage {
|
|
1288
|
+
constructor() {
|
|
1289
|
+
super(...arguments);
|
|
1290
|
+
this.type = exports.MessageType.CLOSE;
|
|
1291
|
+
this.description = 'Ask the server to close the connection';
|
|
1292
|
+
}
|
|
1293
|
+
get(args) {
|
|
1294
|
+
writeVarString(this.encoder, args.documentName);
|
|
1295
|
+
writeVarUint(this.encoder, this.type);
|
|
1296
|
+
return this.encoder;
|
|
1683
1297
|
}
|
|
1684
1298
|
}
|
|
1685
1299
|
|
|
@@ -1724,7 +1338,6 @@ class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
1724
1338
|
onDestroy: () => null,
|
|
1725
1339
|
onAwarenessUpdate: () => null,
|
|
1726
1340
|
onAwarenessChange: () => null,
|
|
1727
|
-
quiet: false,
|
|
1728
1341
|
providerMap: new Map(),
|
|
1729
1342
|
};
|
|
1730
1343
|
this.webSocket = null;
|
|
@@ -1734,7 +1347,6 @@ class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
1734
1347
|
this.lastMessageReceived = 0;
|
|
1735
1348
|
this.identifier = 0;
|
|
1736
1349
|
this.intervals = {
|
|
1737
|
-
forceSync: null,
|
|
1738
1350
|
connectionChecker: null,
|
|
1739
1351
|
};
|
|
1740
1352
|
this.connectionAttempt = null;
|
|
@@ -1769,27 +1381,26 @@ class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
1769
1381
|
this.connect();
|
|
1770
1382
|
}
|
|
1771
1383
|
async onOpen(event) {
|
|
1384
|
+
this.cancelWebsocketRetry = undefined;
|
|
1772
1385
|
this.receivedOnOpenPayload = event;
|
|
1773
1386
|
}
|
|
1774
1387
|
async onStatus(data) {
|
|
1775
1388
|
this.receivedOnStatusPayload = data;
|
|
1776
1389
|
}
|
|
1777
1390
|
attach(provider) {
|
|
1778
|
-
let connectPromise;
|
|
1779
1391
|
this.configuration.providerMap.set(provider.configuration.name, provider);
|
|
1780
1392
|
if (this.status === exports.WebSocketStatus.Disconnected && this.shouldConnect) {
|
|
1781
|
-
|
|
1393
|
+
this.connect();
|
|
1782
1394
|
}
|
|
1783
1395
|
if (this.receivedOnOpenPayload) {
|
|
1784
1396
|
provider.onOpen(this.receivedOnOpenPayload);
|
|
1785
1397
|
}
|
|
1786
|
-
if (this.receivedOnStatusPayload) {
|
|
1787
|
-
provider.onStatus(this.receivedOnStatusPayload);
|
|
1788
|
-
}
|
|
1789
|
-
return connectPromise;
|
|
1790
1398
|
}
|
|
1791
1399
|
detach(provider) {
|
|
1792
|
-
this.configuration.providerMap.
|
|
1400
|
+
if (this.configuration.providerMap.has(provider.configuration.name)) {
|
|
1401
|
+
provider.send(CloseMessage, { documentName: provider.configuration.name });
|
|
1402
|
+
this.configuration.providerMap.delete(provider.configuration.name);
|
|
1403
|
+
}
|
|
1793
1404
|
}
|
|
1794
1405
|
setConfiguration(configuration = {}) {
|
|
1795
1406
|
this.configuration = { ...this.configuration, ...configuration };
|
|
@@ -1840,6 +1451,7 @@ class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
1840
1451
|
this.cancelWebsocketRetry = cancelFunc;
|
|
1841
1452
|
return retryPromise;
|
|
1842
1453
|
}
|
|
1454
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
1843
1455
|
attachWebSocketListeners(ws, reject) {
|
|
1844
1456
|
const { identifier } = ws;
|
|
1845
1457
|
const onMessageHandler = (payload) => this.emit('message', payload);
|
|
@@ -1958,7 +1570,7 @@ class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
1958
1570
|
this.messageQueue = [];
|
|
1959
1571
|
}
|
|
1960
1572
|
}
|
|
1961
|
-
// Ensure that the URL
|
|
1573
|
+
// Ensure that the URL never ends with /
|
|
1962
1574
|
get serverUrl() {
|
|
1963
1575
|
while (this.configuration.url[this.configuration.url.length - 1] === '/') {
|
|
1964
1576
|
return this.configuration.url.slice(0, this.configuration.url.length - 1);
|
|
@@ -1978,8 +1590,8 @@ class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
1978
1590
|
this.webSocket.close();
|
|
1979
1591
|
this.messageQueue = [];
|
|
1980
1592
|
}
|
|
1981
|
-
catch {
|
|
1982
|
-
|
|
1593
|
+
catch (e) {
|
|
1594
|
+
console.error(e);
|
|
1983
1595
|
}
|
|
1984
1596
|
}
|
|
1985
1597
|
send(message) {
|
|
@@ -1994,56 +1606,23 @@ class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
1994
1606
|
onClose({ event }) {
|
|
1995
1607
|
this.closeTries = 0;
|
|
1996
1608
|
this.cleanupWebSocket();
|
|
1997
|
-
if (this.status === exports.WebSocketStatus.Connected) {
|
|
1998
|
-
this.status = exports.WebSocketStatus.Disconnected;
|
|
1999
|
-
this.emit('status', { status: exports.WebSocketStatus.Disconnected });
|
|
2000
|
-
this.emit('disconnect', { event });
|
|
2001
|
-
}
|
|
2002
|
-
if (event.code === common.Unauthorized.code) {
|
|
2003
|
-
if (event.reason === common.Unauthorized.reason) {
|
|
2004
|
-
console.warn('[HocuspocusProvider] An authentication token is required, but you didn’t send one. Try adding a `token` to your HocuspocusProvider configuration. Won’t try again.');
|
|
2005
|
-
}
|
|
2006
|
-
else {
|
|
2007
|
-
console.warn(`[HocuspocusProvider] Connection closed with status Unauthorized: ${event.reason}`);
|
|
2008
|
-
}
|
|
2009
|
-
this.shouldConnect = false;
|
|
2010
|
-
}
|
|
2011
|
-
if (event.code === common.Forbidden.code) {
|
|
2012
|
-
if (!this.configuration.quiet) {
|
|
2013
|
-
console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.');
|
|
2014
|
-
return; // TODO REMOVE ME
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
if (event.code === common.MessageTooBig.code) {
|
|
2018
|
-
console.warn(`[HocuspocusProvider] Connection closed with status MessageTooBig: ${event.reason}`);
|
|
2019
|
-
this.shouldConnect = false;
|
|
2020
|
-
}
|
|
2021
1609
|
if (this.connectionAttempt) {
|
|
2022
1610
|
// That connection attempt failed.
|
|
2023
1611
|
this.rejectConnectionAttempt();
|
|
2024
1612
|
}
|
|
2025
|
-
else if (this.shouldConnect) {
|
|
2026
|
-
// The connection was closed by the server. Let’s just try again.
|
|
2027
|
-
this.connect();
|
|
2028
|
-
}
|
|
2029
|
-
// If we’ll reconnect, we’re done for now.
|
|
2030
|
-
if (this.shouldConnect) {
|
|
2031
|
-
return;
|
|
2032
|
-
}
|
|
2033
|
-
// The status is set correctly already.
|
|
2034
|
-
if (this.status === exports.WebSocketStatus.Disconnected) {
|
|
2035
|
-
return;
|
|
2036
|
-
}
|
|
2037
1613
|
// Let’s update the connection status.
|
|
2038
1614
|
this.status = exports.WebSocketStatus.Disconnected;
|
|
2039
1615
|
this.emit('status', { status: exports.WebSocketStatus.Disconnected });
|
|
2040
1616
|
this.emit('disconnect', { event });
|
|
1617
|
+
// trigger connect if no retry is running and we want to have a connection
|
|
1618
|
+
if (!this.cancelWebsocketRetry && this.shouldConnect) {
|
|
1619
|
+
setTimeout(() => {
|
|
1620
|
+
this.connect();
|
|
1621
|
+
}, this.configuration.delay);
|
|
1622
|
+
}
|
|
2041
1623
|
}
|
|
2042
1624
|
destroy() {
|
|
2043
1625
|
this.emit('destroy');
|
|
2044
|
-
if (this.intervals.forceSync) {
|
|
2045
|
-
clearInterval(this.intervals.forceSync);
|
|
2046
|
-
}
|
|
2047
1626
|
clearInterval(this.intervals.connectionChecker);
|
|
2048
1627
|
// If there is still a connection attempt outstanding then we should stop
|
|
2049
1628
|
// it before calling disconnect, otherwise it will be rejected in the onClose
|
|
@@ -2059,6 +1638,7 @@ class HocuspocusProviderWebsocket extends EventEmitter {
|
|
|
2059
1638
|
* @module sync-protocol
|
|
2060
1639
|
*/
|
|
2061
1640
|
|
|
1641
|
+
|
|
2062
1642
|
/**
|
|
2063
1643
|
* @typedef {Map<number, number>} StateMap
|
|
2064
1644
|
*/
|
|
@@ -2182,27 +1762,10 @@ const readSyncMessage = (decoder, encoder, doc, transactionOrigin) => {
|
|
|
2182
1762
|
return messageType
|
|
2183
1763
|
};
|
|
2184
1764
|
|
|
2185
|
-
class OutgoingMessage {
|
|
2186
|
-
constructor() {
|
|
2187
|
-
this.encoder = createEncoder();
|
|
2188
|
-
}
|
|
2189
|
-
get(args) {
|
|
2190
|
-
return args.encoder;
|
|
2191
|
-
}
|
|
2192
|
-
toUint8Array() {
|
|
2193
|
-
return toUint8Array(this.encoder);
|
|
2194
|
-
}
|
|
2195
|
-
}
|
|
2196
|
-
|
|
2197
1765
|
class MessageReceiver {
|
|
2198
1766
|
constructor(message) {
|
|
2199
|
-
this.broadcasted = false;
|
|
2200
1767
|
this.message = message;
|
|
2201
1768
|
}
|
|
2202
|
-
setBroadcasted(value) {
|
|
2203
|
-
this.broadcasted = value;
|
|
2204
|
-
return this;
|
|
2205
|
-
}
|
|
2206
1769
|
apply(provider, emitSynced) {
|
|
2207
1770
|
const { message } = this;
|
|
2208
1771
|
const type = message.readVarUint();
|
|
@@ -2226,21 +1789,26 @@ class MessageReceiver {
|
|
|
2226
1789
|
case exports.MessageType.SyncStatus:
|
|
2227
1790
|
this.applySyncStatusMessage(provider, readVarInt(message.decoder) === 1);
|
|
2228
1791
|
break;
|
|
1792
|
+
case exports.MessageType.CLOSE:
|
|
1793
|
+
// eslint-disable-next-line no-case-declarations
|
|
1794
|
+
const event = {
|
|
1795
|
+
code: 1000,
|
|
1796
|
+
reason: readVarString(message.decoder),
|
|
1797
|
+
// @ts-ignore
|
|
1798
|
+
target: provider.configuration.websocketProvider.webSocket,
|
|
1799
|
+
type: 'close',
|
|
1800
|
+
};
|
|
1801
|
+
provider.onClose();
|
|
1802
|
+
provider.configuration.onClose({ event });
|
|
1803
|
+
provider.forwardClose(event);
|
|
1804
|
+
break;
|
|
2229
1805
|
default:
|
|
2230
1806
|
throw new Error(`Can’t apply message of unknown type: ${type}`);
|
|
2231
1807
|
}
|
|
2232
1808
|
// Reply
|
|
2233
1809
|
if (message.length() > emptyMessageLength + 1) { // length of documentName (considered in emptyMessageLength plus length of yjs sync type, set in applySyncMessage)
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
// @ts-ignore
|
|
2237
|
-
provider.broadcast(OutgoingMessage, { encoder: message.encoder });
|
|
2238
|
-
}
|
|
2239
|
-
else {
|
|
2240
|
-
// TODO: Some weird TypeScript error
|
|
2241
|
-
// @ts-ignore
|
|
2242
|
-
provider.send(OutgoingMessage, { encoder: message.encoder });
|
|
2243
|
-
}
|
|
1810
|
+
// @ts-ignore
|
|
1811
|
+
provider.send(OutgoingMessage, { encoder: message.encoder });
|
|
2244
1812
|
}
|
|
2245
1813
|
}
|
|
2246
1814
|
applySyncMessage(provider, emitSynced) {
|
|
@@ -2277,6 +1845,366 @@ class MessageReceiver {
|
|
|
2277
1845
|
}
|
|
2278
1846
|
}
|
|
2279
1847
|
|
|
1848
|
+
/**
|
|
1849
|
+
* Often used conditions.
|
|
1850
|
+
*
|
|
1851
|
+
* @module conditions
|
|
1852
|
+
*/
|
|
1853
|
+
|
|
1854
|
+
/**
|
|
1855
|
+
* @template T
|
|
1856
|
+
* @param {T|null|undefined} v
|
|
1857
|
+
* @return {T|null}
|
|
1858
|
+
*/
|
|
1859
|
+
/* c8 ignore next */
|
|
1860
|
+
const undefinedToNull = v => v === undefined ? null : v;
|
|
1861
|
+
|
|
1862
|
+
/* eslint-env browser */
|
|
1863
|
+
|
|
1864
|
+
/**
|
|
1865
|
+
* Isomorphic variable storage.
|
|
1866
|
+
*
|
|
1867
|
+
* Uses LocalStorage in the browser and falls back to in-memory storage.
|
|
1868
|
+
*
|
|
1869
|
+
* @module storage
|
|
1870
|
+
*/
|
|
1871
|
+
|
|
1872
|
+
/* c8 ignore start */
|
|
1873
|
+
class VarStoragePolyfill {
|
|
1874
|
+
constructor () {
|
|
1875
|
+
this.map = new Map();
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
/**
|
|
1879
|
+
* @param {string} key
|
|
1880
|
+
* @param {any} newValue
|
|
1881
|
+
*/
|
|
1882
|
+
setItem (key, newValue) {
|
|
1883
|
+
this.map.set(key, newValue);
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
/**
|
|
1887
|
+
* @param {string} key
|
|
1888
|
+
*/
|
|
1889
|
+
getItem (key) {
|
|
1890
|
+
return this.map.get(key)
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
/* c8 ignore stop */
|
|
1894
|
+
|
|
1895
|
+
/**
|
|
1896
|
+
* @type {any}
|
|
1897
|
+
*/
|
|
1898
|
+
let _localStorage = new VarStoragePolyfill();
|
|
1899
|
+
let usePolyfill = true;
|
|
1900
|
+
|
|
1901
|
+
/* c8 ignore start */
|
|
1902
|
+
try {
|
|
1903
|
+
// if the same-origin rule is violated, accessing localStorage might thrown an error
|
|
1904
|
+
if (typeof localStorage !== 'undefined' && localStorage) {
|
|
1905
|
+
_localStorage = localStorage;
|
|
1906
|
+
usePolyfill = false;
|
|
1907
|
+
}
|
|
1908
|
+
} catch (e) { }
|
|
1909
|
+
/* c8 ignore stop */
|
|
1910
|
+
|
|
1911
|
+
/**
|
|
1912
|
+
* This is basically localStorage in browser, or a polyfill in nodejs
|
|
1913
|
+
*/
|
|
1914
|
+
/* c8 ignore next */
|
|
1915
|
+
const varStorage = _localStorage;
|
|
1916
|
+
|
|
1917
|
+
/**
|
|
1918
|
+
* A polyfill for `addEventListener('storage', event => {..})` that does nothing if the polyfill is being used.
|
|
1919
|
+
*
|
|
1920
|
+
* @param {function({ key: string, newValue: string, oldValue: string }): void} eventHandler
|
|
1921
|
+
* @function
|
|
1922
|
+
*/
|
|
1923
|
+
/* c8 ignore next */
|
|
1924
|
+
const onChange = eventHandler => usePolyfill || addEventListener('storage', /** @type {any} */ (eventHandler));
|
|
1925
|
+
|
|
1926
|
+
/**
|
|
1927
|
+
* A polyfill for `removeEventListener('storage', event => {..})` that does nothing if the polyfill is being used.
|
|
1928
|
+
*
|
|
1929
|
+
* @param {function({ key: string, newValue: string, oldValue: string }): void} eventHandler
|
|
1930
|
+
* @function
|
|
1931
|
+
*/
|
|
1932
|
+
/* c8 ignore next */
|
|
1933
|
+
const offChange = eventHandler => usePolyfill || removeEventListener('storage', /** @type {any} */ (eventHandler));
|
|
1934
|
+
|
|
1935
|
+
/**
|
|
1936
|
+
* Isomorphic module to work access the environment (query params, env variables).
|
|
1937
|
+
*
|
|
1938
|
+
* @module environment
|
|
1939
|
+
*/
|
|
1940
|
+
|
|
1941
|
+
|
|
1942
|
+
/* c8 ignore next 2 */
|
|
1943
|
+
// @ts-ignore
|
|
1944
|
+
const isNode = typeof process !== 'undefined' && process.release && /node|io\.js/.test(process.release.name) && Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]';
|
|
1945
|
+
|
|
1946
|
+
/* c8 ignore next */
|
|
1947
|
+
const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && !isNode;
|
|
1948
|
+
|
|
1949
|
+
/**
|
|
1950
|
+
* @type {Map<string,string>}
|
|
1951
|
+
*/
|
|
1952
|
+
let params;
|
|
1953
|
+
|
|
1954
|
+
/* c8 ignore start */
|
|
1955
|
+
const computeParams = () => {
|
|
1956
|
+
if (params === undefined) {
|
|
1957
|
+
if (isNode) {
|
|
1958
|
+
params = create();
|
|
1959
|
+
const pargs = process.argv;
|
|
1960
|
+
let currParamName = null;
|
|
1961
|
+
for (let i = 0; i < pargs.length; i++) {
|
|
1962
|
+
const parg = pargs[i];
|
|
1963
|
+
if (parg[0] === '-') {
|
|
1964
|
+
if (currParamName !== null) {
|
|
1965
|
+
params.set(currParamName, '');
|
|
1966
|
+
}
|
|
1967
|
+
currParamName = parg;
|
|
1968
|
+
} else {
|
|
1969
|
+
if (currParamName !== null) {
|
|
1970
|
+
params.set(currParamName, parg);
|
|
1971
|
+
currParamName = null;
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
if (currParamName !== null) {
|
|
1976
|
+
params.set(currParamName, '');
|
|
1977
|
+
}
|
|
1978
|
+
// in ReactNative for example this would not be true (unless connected to the Remote Debugger)
|
|
1979
|
+
} else if (typeof location === 'object') {
|
|
1980
|
+
params = create(); // eslint-disable-next-line no-undef
|
|
1981
|
+
(location.search || '?').slice(1).split('&').forEach((kv) => {
|
|
1982
|
+
if (kv.length !== 0) {
|
|
1983
|
+
const [key, value] = kv.split('=');
|
|
1984
|
+
params.set(`--${fromCamelCase(key, '-')}`, value);
|
|
1985
|
+
params.set(`-${fromCamelCase(key, '-')}`, value);
|
|
1986
|
+
}
|
|
1987
|
+
});
|
|
1988
|
+
} else {
|
|
1989
|
+
params = create();
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
return params
|
|
1993
|
+
};
|
|
1994
|
+
/* c8 ignore stop */
|
|
1995
|
+
|
|
1996
|
+
/**
|
|
1997
|
+
* @param {string} name
|
|
1998
|
+
* @return {boolean}
|
|
1999
|
+
*/
|
|
2000
|
+
/* c8 ignore next */
|
|
2001
|
+
const hasParam = (name) => computeParams().has(name);
|
|
2002
|
+
|
|
2003
|
+
/**
|
|
2004
|
+
* @param {string} name
|
|
2005
|
+
* @return {string|null}
|
|
2006
|
+
*/
|
|
2007
|
+
/* c8 ignore next 4 */
|
|
2008
|
+
const getVariable = (name) =>
|
|
2009
|
+
isNode
|
|
2010
|
+
? undefinedToNull(process.env[name.toUpperCase().replaceAll('-', '_')])
|
|
2011
|
+
: undefinedToNull(varStorage.getItem(name));
|
|
2012
|
+
|
|
2013
|
+
/**
|
|
2014
|
+
* @param {string} name
|
|
2015
|
+
* @return {boolean}
|
|
2016
|
+
*/
|
|
2017
|
+
/* c8 ignore next 2 */
|
|
2018
|
+
const hasConf = (name) =>
|
|
2019
|
+
hasParam('--' + name) || getVariable(name) !== null;
|
|
2020
|
+
|
|
2021
|
+
/* c8 ignore next */
|
|
2022
|
+
hasConf('production');
|
|
2023
|
+
|
|
2024
|
+
/* c8 ignore next 2 */
|
|
2025
|
+
const forceColor = isNode &&
|
|
2026
|
+
isOneOf(process.env.FORCE_COLOR, ['true', '1', '2']);
|
|
2027
|
+
|
|
2028
|
+
/* c8 ignore start */
|
|
2029
|
+
/**
|
|
2030
|
+
* Color is enabled by default if the terminal supports it.
|
|
2031
|
+
*
|
|
2032
|
+
* Explicitly enable color using `--color` parameter
|
|
2033
|
+
* Disable color using `--no-color` parameter or using `NO_COLOR=1` environment variable.
|
|
2034
|
+
* `FORCE_COLOR=1` enables color and takes precedence over all.
|
|
2035
|
+
*/
|
|
2036
|
+
forceColor || (
|
|
2037
|
+
!hasParam('--no-colors') && // @todo deprecate --no-colors
|
|
2038
|
+
!hasConf('no-color') &&
|
|
2039
|
+
(!isNode || process.stdout.isTTY) && (
|
|
2040
|
+
!isNode ||
|
|
2041
|
+
hasParam('--color') ||
|
|
2042
|
+
getVariable('COLORTERM') !== null ||
|
|
2043
|
+
(getVariable('TERM') || '').includes('color')
|
|
2044
|
+
)
|
|
2045
|
+
);
|
|
2046
|
+
/* c8 ignore stop */
|
|
2047
|
+
|
|
2048
|
+
/**
|
|
2049
|
+
* Utility functions to work with buffers (Uint8Array).
|
|
2050
|
+
*
|
|
2051
|
+
* @module buffer
|
|
2052
|
+
*/
|
|
2053
|
+
|
|
2054
|
+
|
|
2055
|
+
/**
|
|
2056
|
+
* @param {number} len
|
|
2057
|
+
*/
|
|
2058
|
+
const createUint8ArrayFromLen = len => new Uint8Array(len);
|
|
2059
|
+
|
|
2060
|
+
/**
|
|
2061
|
+
* Create Uint8Array with initial content from buffer
|
|
2062
|
+
*
|
|
2063
|
+
* @param {ArrayBuffer} buffer
|
|
2064
|
+
* @param {number} byteOffset
|
|
2065
|
+
* @param {number} length
|
|
2066
|
+
*/
|
|
2067
|
+
const createUint8ArrayViewFromArrayBuffer = (buffer, byteOffset, length) => new Uint8Array(buffer, byteOffset, length);
|
|
2068
|
+
|
|
2069
|
+
/**
|
|
2070
|
+
* Create Uint8Array with initial content from buffer
|
|
2071
|
+
*
|
|
2072
|
+
* @param {ArrayBuffer} buffer
|
|
2073
|
+
*/
|
|
2074
|
+
const createUint8ArrayFromArrayBuffer = buffer => new Uint8Array(buffer);
|
|
2075
|
+
|
|
2076
|
+
/* c8 ignore start */
|
|
2077
|
+
/**
|
|
2078
|
+
* @param {Uint8Array} bytes
|
|
2079
|
+
* @return {string}
|
|
2080
|
+
*/
|
|
2081
|
+
const toBase64Browser = bytes => {
|
|
2082
|
+
let s = '';
|
|
2083
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
2084
|
+
s += fromCharCode(bytes[i]);
|
|
2085
|
+
}
|
|
2086
|
+
// eslint-disable-next-line no-undef
|
|
2087
|
+
return btoa(s)
|
|
2088
|
+
};
|
|
2089
|
+
/* c8 ignore stop */
|
|
2090
|
+
|
|
2091
|
+
/**
|
|
2092
|
+
* @param {Uint8Array} bytes
|
|
2093
|
+
* @return {string}
|
|
2094
|
+
*/
|
|
2095
|
+
const toBase64Node = bytes => Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('base64');
|
|
2096
|
+
|
|
2097
|
+
/* c8 ignore start */
|
|
2098
|
+
/**
|
|
2099
|
+
* @param {string} s
|
|
2100
|
+
* @return {Uint8Array}
|
|
2101
|
+
*/
|
|
2102
|
+
const fromBase64Browser = s => {
|
|
2103
|
+
// eslint-disable-next-line no-undef
|
|
2104
|
+
const a = atob(s);
|
|
2105
|
+
const bytes = createUint8ArrayFromLen(a.length);
|
|
2106
|
+
for (let i = 0; i < a.length; i++) {
|
|
2107
|
+
bytes[i] = a.charCodeAt(i);
|
|
2108
|
+
}
|
|
2109
|
+
return bytes
|
|
2110
|
+
};
|
|
2111
|
+
/* c8 ignore stop */
|
|
2112
|
+
|
|
2113
|
+
/**
|
|
2114
|
+
* @param {string} s
|
|
2115
|
+
*/
|
|
2116
|
+
const fromBase64Node = s => {
|
|
2117
|
+
const buf = Buffer.from(s, 'base64');
|
|
2118
|
+
return createUint8ArrayViewFromArrayBuffer(buf.buffer, buf.byteOffset, buf.byteLength)
|
|
2119
|
+
};
|
|
2120
|
+
|
|
2121
|
+
/* c8 ignore next */
|
|
2122
|
+
const toBase64 = isBrowser ? toBase64Browser : toBase64Node;
|
|
2123
|
+
|
|
2124
|
+
/* c8 ignore next */
|
|
2125
|
+
const fromBase64 = isBrowser ? fromBase64Browser : fromBase64Node;
|
|
2126
|
+
|
|
2127
|
+
/* eslint-env browser */
|
|
2128
|
+
|
|
2129
|
+
|
|
2130
|
+
/**
|
|
2131
|
+
* @typedef {Object} Channel
|
|
2132
|
+
* @property {Set<function(any, any):any>} Channel.subs
|
|
2133
|
+
* @property {any} Channel.bc
|
|
2134
|
+
*/
|
|
2135
|
+
|
|
2136
|
+
/**
|
|
2137
|
+
* @type {Map<string, Channel>}
|
|
2138
|
+
*/
|
|
2139
|
+
const channels = new Map();
|
|
2140
|
+
|
|
2141
|
+
/* c8 ignore start */
|
|
2142
|
+
class LocalStoragePolyfill {
|
|
2143
|
+
/**
|
|
2144
|
+
* @param {string} room
|
|
2145
|
+
*/
|
|
2146
|
+
constructor (room) {
|
|
2147
|
+
this.room = room;
|
|
2148
|
+
/**
|
|
2149
|
+
* @type {null|function({data:ArrayBuffer}):void}
|
|
2150
|
+
*/
|
|
2151
|
+
this.onmessage = null;
|
|
2152
|
+
/**
|
|
2153
|
+
* @param {any} e
|
|
2154
|
+
*/
|
|
2155
|
+
this._onChange = e => e.key === room && this.onmessage !== null && this.onmessage({ data: fromBase64(e.newValue || '') });
|
|
2156
|
+
onChange(this._onChange);
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
/**
|
|
2160
|
+
* @param {ArrayBuffer} buf
|
|
2161
|
+
*/
|
|
2162
|
+
postMessage (buf) {
|
|
2163
|
+
varStorage.setItem(this.room, toBase64(createUint8ArrayFromArrayBuffer(buf)));
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
close () {
|
|
2167
|
+
offChange(this._onChange);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
/* c8 ignore stop */
|
|
2171
|
+
|
|
2172
|
+
// Use BroadcastChannel or Polyfill
|
|
2173
|
+
/* c8 ignore next */
|
|
2174
|
+
const BC = typeof BroadcastChannel === 'undefined' ? LocalStoragePolyfill : BroadcastChannel;
|
|
2175
|
+
|
|
2176
|
+
/**
|
|
2177
|
+
* @param {string} room
|
|
2178
|
+
* @return {Channel}
|
|
2179
|
+
*/
|
|
2180
|
+
const getChannel = room =>
|
|
2181
|
+
setIfUndefined(channels, room, () => {
|
|
2182
|
+
const subs = create$2();
|
|
2183
|
+
const bc = new BC(room);
|
|
2184
|
+
/**
|
|
2185
|
+
* @param {{data:ArrayBuffer}} e
|
|
2186
|
+
*/
|
|
2187
|
+
/* c8 ignore next */
|
|
2188
|
+
bc.onmessage = e => subs.forEach(sub => sub(e.data, 'broadcastchannel'));
|
|
2189
|
+
return {
|
|
2190
|
+
bc, subs
|
|
2191
|
+
}
|
|
2192
|
+
});
|
|
2193
|
+
|
|
2194
|
+
/**
|
|
2195
|
+
* Publish data to all subscribers (including subscribers on this tab)
|
|
2196
|
+
*
|
|
2197
|
+
* @function
|
|
2198
|
+
* @param {string} room
|
|
2199
|
+
* @param {any} data
|
|
2200
|
+
* @param {any} [origin]
|
|
2201
|
+
*/
|
|
2202
|
+
const publish = (room, data, origin = null) => {
|
|
2203
|
+
const c = getChannel(room);
|
|
2204
|
+
c.bc.postMessage(data);
|
|
2205
|
+
c.subs.forEach(sub => sub(data, origin));
|
|
2206
|
+
};
|
|
2207
|
+
|
|
2280
2208
|
class MessageSender {
|
|
2281
2209
|
constructor(Message, args = {}) {
|
|
2282
2210
|
this.message = new Message();
|
|
@@ -2337,32 +2265,6 @@ class AwarenessMessage extends OutgoingMessage {
|
|
|
2337
2265
|
}
|
|
2338
2266
|
}
|
|
2339
2267
|
|
|
2340
|
-
class CloseMessage extends OutgoingMessage {
|
|
2341
|
-
constructor() {
|
|
2342
|
-
super(...arguments);
|
|
2343
|
-
this.type = exports.MessageType.CLOSE;
|
|
2344
|
-
this.description = 'Ask the server to close the connection';
|
|
2345
|
-
}
|
|
2346
|
-
get(args) {
|
|
2347
|
-
writeVarString(this.encoder, args.documentName);
|
|
2348
|
-
writeVarUint(this.encoder, this.type);
|
|
2349
|
-
return this.encoder;
|
|
2350
|
-
}
|
|
2351
|
-
}
|
|
2352
|
-
|
|
2353
|
-
class QueryAwarenessMessage extends OutgoingMessage {
|
|
2354
|
-
constructor() {
|
|
2355
|
-
super(...arguments);
|
|
2356
|
-
this.type = exports.MessageType.QueryAwareness;
|
|
2357
|
-
this.description = 'Queries awareness states';
|
|
2358
|
-
}
|
|
2359
|
-
get(args) {
|
|
2360
|
-
writeVarString(this.encoder, args.documentName);
|
|
2361
|
-
writeVarUint(this.encoder, this.type);
|
|
2362
|
-
return this.encoder;
|
|
2363
|
-
}
|
|
2364
|
-
}
|
|
2365
|
-
|
|
2366
2268
|
class StatelessMessage extends OutgoingMessage {
|
|
2367
2269
|
constructor() {
|
|
2368
2270
|
super(...arguments);
|
|
@@ -2395,23 +2297,6 @@ class SyncStepOneMessage extends OutgoingMessage {
|
|
|
2395
2297
|
}
|
|
2396
2298
|
}
|
|
2397
2299
|
|
|
2398
|
-
class SyncStepTwoMessage extends OutgoingMessage {
|
|
2399
|
-
constructor() {
|
|
2400
|
-
super(...arguments);
|
|
2401
|
-
this.type = exports.MessageType.Sync;
|
|
2402
|
-
this.description = 'Second sync step';
|
|
2403
|
-
}
|
|
2404
|
-
get(args) {
|
|
2405
|
-
if (typeof args.document === 'undefined') {
|
|
2406
|
-
throw new Error('The sync step two message requires document as an argument');
|
|
2407
|
-
}
|
|
2408
|
-
writeVarString(this.encoder, args.documentName);
|
|
2409
|
-
writeVarUint(this.encoder, this.type);
|
|
2410
|
-
writeSyncStep2(this.encoder, args.document);
|
|
2411
|
-
return this.encoder;
|
|
2412
|
-
}
|
|
2413
|
-
}
|
|
2414
|
-
|
|
2415
2300
|
class UpdateMessage extends OutgoingMessage {
|
|
2416
2301
|
constructor() {
|
|
2417
2302
|
super(...arguments);
|
|
@@ -2443,8 +2328,6 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2443
2328
|
// @ts-ignore
|
|
2444
2329
|
awareness: undefined,
|
|
2445
2330
|
token: null,
|
|
2446
|
-
parameters: {},
|
|
2447
|
-
broadcast: true,
|
|
2448
2331
|
forceSyncInterval: false,
|
|
2449
2332
|
onAuthenticated: () => null,
|
|
2450
2333
|
onAuthenticationFailed: () => null,
|
|
@@ -2452,7 +2335,6 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2452
2335
|
onConnect: () => null,
|
|
2453
2336
|
onMessage: () => null,
|
|
2454
2337
|
onOutgoingMessage: () => null,
|
|
2455
|
-
onStatus: () => null,
|
|
2456
2338
|
onSynced: () => null,
|
|
2457
2339
|
onDisconnect: () => null,
|
|
2458
2340
|
onClose: () => null,
|
|
@@ -2460,30 +2342,24 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2460
2342
|
onAwarenessUpdate: () => null,
|
|
2461
2343
|
onAwarenessChange: () => null,
|
|
2462
2344
|
onStateless: () => null,
|
|
2463
|
-
quiet: false,
|
|
2464
|
-
connect: true,
|
|
2465
|
-
preserveConnection: true,
|
|
2466
2345
|
};
|
|
2467
|
-
this.subscribedToBroadcastChannel = false;
|
|
2468
2346
|
this.isSynced = false;
|
|
2469
2347
|
this.unsyncedChanges = 0;
|
|
2470
|
-
this.status = exports.WebSocketStatus.Disconnected;
|
|
2471
2348
|
this.isAuthenticated = false;
|
|
2472
2349
|
this.authorizedScope = undefined;
|
|
2473
|
-
|
|
2350
|
+
// @internal
|
|
2351
|
+
this.manageSocket = false;
|
|
2352
|
+
this.isAttached = false;
|
|
2474
2353
|
this.intervals = {
|
|
2475
2354
|
forceSync: null,
|
|
2476
2355
|
};
|
|
2477
|
-
this.isConnected = true;
|
|
2478
2356
|
this.boundDocumentUpdateHandler = this.documentUpdateHandler.bind(this);
|
|
2479
2357
|
this.boundAwarenessUpdateHandler = this.awarenessUpdateHandler.bind(this);
|
|
2480
|
-
this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
|
|
2481
2358
|
this.boundPageHide = this.pageHide.bind(this);
|
|
2482
2359
|
this.boundOnOpen = this.onOpen.bind(this);
|
|
2483
2360
|
this.boundOnClose = this.onClose.bind(this);
|
|
2484
|
-
this.boundOnStatus = this.onStatus.bind(this);
|
|
2485
2361
|
this.forwardConnect = (e) => this.emit('connect', e);
|
|
2486
|
-
|
|
2362
|
+
// forwardOpen = (e: any) => this.emit('open', e)
|
|
2487
2363
|
this.forwardClose = (e) => this.emit('close', e);
|
|
2488
2364
|
this.forwardDisconnect = (e) => this.emit('disconnect', e);
|
|
2489
2365
|
this.forwardDestroy = (e) => this.emit('destroy', e);
|
|
@@ -2500,18 +2376,6 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2500
2376
|
this.on('stateless', this.configuration.onStateless);
|
|
2501
2377
|
this.on('authenticated', this.configuration.onAuthenticated);
|
|
2502
2378
|
this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
|
|
2503
|
-
this.configuration.websocketProvider.on('connect', this.configuration.onConnect);
|
|
2504
|
-
this.configuration.websocketProvider.on('connect', this.forwardConnect);
|
|
2505
|
-
this.configuration.websocketProvider.on('open', this.boundOnOpen);
|
|
2506
|
-
this.configuration.websocketProvider.on('open', this.forwardOpen);
|
|
2507
|
-
this.configuration.websocketProvider.on('close', this.boundOnClose);
|
|
2508
|
-
this.configuration.websocketProvider.on('close', this.configuration.onClose);
|
|
2509
|
-
this.configuration.websocketProvider.on('close', this.forwardClose);
|
|
2510
|
-
this.configuration.websocketProvider.on('status', this.boundOnStatus);
|
|
2511
|
-
this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect);
|
|
2512
|
-
this.configuration.websocketProvider.on('disconnect', this.forwardDisconnect);
|
|
2513
|
-
this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy);
|
|
2514
|
-
this.configuration.websocketProvider.on('destroy', this.forwardDestroy);
|
|
2515
2379
|
(_a = this.awareness) === null || _a === void 0 ? void 0 : _a.on('update', () => {
|
|
2516
2380
|
this.emit('awarenessUpdate', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
|
|
2517
2381
|
});
|
|
@@ -2525,20 +2389,16 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2525
2389
|
&& typeof this.configuration.forceSyncInterval === 'number') {
|
|
2526
2390
|
this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
|
|
2527
2391
|
}
|
|
2528
|
-
this.
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
this.status = status;
|
|
2532
|
-
this.configuration.onStatus({ status });
|
|
2533
|
-
this.emit('status', { status });
|
|
2392
|
+
if (this.manageSocket) {
|
|
2393
|
+
this.attach();
|
|
2394
|
+
}
|
|
2534
2395
|
}
|
|
2535
2396
|
setConfiguration(configuration = {}) {
|
|
2536
|
-
if (!configuration.websocketProvider
|
|
2397
|
+
if (!configuration.websocketProvider) {
|
|
2537
2398
|
const websocketProviderConfig = configuration;
|
|
2399
|
+
this.manageSocket = true;
|
|
2538
2400
|
this.configuration.websocketProvider = new HocuspocusProviderWebsocket({
|
|
2539
2401
|
url: websocketProviderConfig.url,
|
|
2540
|
-
connect: websocketProviderConfig.connect,
|
|
2541
|
-
parameters: websocketProviderConfig.parameters,
|
|
2542
2402
|
});
|
|
2543
2403
|
}
|
|
2544
2404
|
this.configuration = { ...this.configuration, ...configuration };
|
|
@@ -2561,7 +2421,9 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2561
2421
|
this.emit('unsyncedChanges', this.unsyncedChanges);
|
|
2562
2422
|
}
|
|
2563
2423
|
decrementUnsyncedChanges() {
|
|
2564
|
-
this.unsyncedChanges
|
|
2424
|
+
if (this.unsyncedChanges > 0) {
|
|
2425
|
+
this.unsyncedChanges -= 1;
|
|
2426
|
+
}
|
|
2565
2427
|
if (this.unsyncedChanges === 0) {
|
|
2566
2428
|
this.synced = true;
|
|
2567
2429
|
}
|
|
@@ -2590,7 +2452,7 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2590
2452
|
return;
|
|
2591
2453
|
}
|
|
2592
2454
|
this.incrementUnsyncedChanges();
|
|
2593
|
-
this.send(UpdateMessage, { update, documentName: this.configuration.name }
|
|
2455
|
+
this.send(UpdateMessage, { update, documentName: this.configuration.name });
|
|
2594
2456
|
}
|
|
2595
2457
|
awarenessUpdateHandler({ added, updated, removed }, origin) {
|
|
2596
2458
|
const changedClients = added.concat(updated).concat(removed);
|
|
@@ -2598,7 +2460,7 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2598
2460
|
awareness: this.awareness,
|
|
2599
2461
|
clients: changedClients,
|
|
2600
2462
|
documentName: this.configuration.name,
|
|
2601
|
-
}
|
|
2463
|
+
});
|
|
2602
2464
|
}
|
|
2603
2465
|
/**
|
|
2604
2466
|
* Indicates whether a first handshake with the server has been established
|
|
@@ -2614,34 +2476,22 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2614
2476
|
return;
|
|
2615
2477
|
}
|
|
2616
2478
|
this.isSynced = state;
|
|
2617
|
-
|
|
2618
|
-
|
|
2479
|
+
if (state) {
|
|
2480
|
+
this.emit('synced', { state });
|
|
2481
|
+
}
|
|
2619
2482
|
}
|
|
2620
2483
|
receiveStateless(payload) {
|
|
2621
2484
|
this.emit('stateless', { payload });
|
|
2622
2485
|
}
|
|
2623
|
-
get isAuthenticationRequired() {
|
|
2624
|
-
return !!this.configuration.token && !this.isAuthenticated;
|
|
2625
|
-
}
|
|
2626
2486
|
// not needed, but provides backward compatibility with e.g. lexical/yjs
|
|
2627
2487
|
async connect() {
|
|
2628
|
-
|
|
2629
|
-
this.subscribeToBroadcastChannel();
|
|
2630
|
-
}
|
|
2631
|
-
this.configuration.websocketProvider.shouldConnect = true;
|
|
2632
|
-
return this.configuration.websocketProvider.attach(this);
|
|
2488
|
+
console.warn('HocuspocusProvider::connect() is deprecated and does not do anything. Please connect/disconnect on the websocketProvider, or attach/deattach providers.');
|
|
2633
2489
|
}
|
|
2634
2490
|
disconnect() {
|
|
2635
|
-
|
|
2636
|
-
this.configuration.websocketProvider.detach(this);
|
|
2637
|
-
this.isConnected = false;
|
|
2638
|
-
if (!this.configuration.preserveConnection) {
|
|
2639
|
-
this.configuration.websocketProvider.disconnect();
|
|
2640
|
-
}
|
|
2491
|
+
console.warn('HocuspocusProvider::disconnect() is deprecated and does not do anything. Please connect/disconnect on the websocketProvider, or attach/deattach providers.');
|
|
2641
2492
|
}
|
|
2642
2493
|
async onOpen(event) {
|
|
2643
2494
|
this.isAuthenticated = false;
|
|
2644
|
-
this.isConnected = true;
|
|
2645
2495
|
this.emit('open', { event });
|
|
2646
2496
|
let token;
|
|
2647
2497
|
try {
|
|
@@ -2651,12 +2501,10 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2651
2501
|
this.permissionDeniedHandler(`Failed to get token: ${error}`);
|
|
2652
2502
|
return;
|
|
2653
2503
|
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
});
|
|
2659
|
-
}
|
|
2504
|
+
this.send(AuthenticationMessage, {
|
|
2505
|
+
token: token !== null && token !== void 0 ? token : '',
|
|
2506
|
+
documentName: this.configuration.name,
|
|
2507
|
+
});
|
|
2660
2508
|
this.startSync();
|
|
2661
2509
|
}
|
|
2662
2510
|
async getToken() {
|
|
@@ -2677,13 +2525,7 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2677
2525
|
});
|
|
2678
2526
|
}
|
|
2679
2527
|
}
|
|
2680
|
-
send(message, args
|
|
2681
|
-
if (!this.isConnected) {
|
|
2682
|
-
return;
|
|
2683
|
-
}
|
|
2684
|
-
if (broadcast) {
|
|
2685
|
-
this.mux(() => { this.broadcast(message, args); });
|
|
2686
|
-
}
|
|
2528
|
+
send(message, args) {
|
|
2687
2529
|
const messageSender = new MessageSender(message, args);
|
|
2688
2530
|
this.emit('outgoingMessage', { message: messageSender.message });
|
|
2689
2531
|
messageSender.send(this.configuration.websocketProvider);
|
|
@@ -2695,7 +2537,7 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2695
2537
|
this.emit('message', { event, message: new IncomingMessage(event.data) });
|
|
2696
2538
|
new MessageReceiver(message).apply(this, true);
|
|
2697
2539
|
}
|
|
2698
|
-
onClose(
|
|
2540
|
+
onClose() {
|
|
2699
2541
|
this.isAuthenticated = false;
|
|
2700
2542
|
this.synced = false;
|
|
2701
2543
|
// update awareness (all users except local left)
|
|
@@ -2715,92 +2557,54 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2715
2557
|
}
|
|
2716
2558
|
this.document.off('update', this.boundDocumentUpdateHandler);
|
|
2717
2559
|
this.removeAllListeners();
|
|
2560
|
+
this.detach();
|
|
2561
|
+
if (this.manageSocket) {
|
|
2562
|
+
this.configuration.websocketProvider.destroy();
|
|
2563
|
+
}
|
|
2564
|
+
if (typeof window === 'undefined' || !('removeEventListener' in window)) {
|
|
2565
|
+
return;
|
|
2566
|
+
}
|
|
2567
|
+
window.removeEventListener('pagehide', this.boundPageHide);
|
|
2568
|
+
}
|
|
2569
|
+
detach() {
|
|
2718
2570
|
this.configuration.websocketProvider.off('connect', this.configuration.onConnect);
|
|
2719
2571
|
this.configuration.websocketProvider.off('connect', this.forwardConnect);
|
|
2720
2572
|
this.configuration.websocketProvider.off('open', this.boundOnOpen);
|
|
2721
|
-
this.configuration.websocketProvider.off('open', this.forwardOpen);
|
|
2722
2573
|
this.configuration.websocketProvider.off('close', this.boundOnClose);
|
|
2723
2574
|
this.configuration.websocketProvider.off('close', this.configuration.onClose);
|
|
2724
2575
|
this.configuration.websocketProvider.off('close', this.forwardClose);
|
|
2725
|
-
this.configuration.websocketProvider.off('status', this.boundOnStatus);
|
|
2726
2576
|
this.configuration.websocketProvider.off('disconnect', this.configuration.onDisconnect);
|
|
2727
2577
|
this.configuration.websocketProvider.off('disconnect', this.forwardDisconnect);
|
|
2728
2578
|
this.configuration.websocketProvider.off('destroy', this.configuration.onDestroy);
|
|
2729
2579
|
this.configuration.websocketProvider.off('destroy', this.forwardDestroy);
|
|
2730
|
-
this.
|
|
2731
|
-
this.
|
|
2732
|
-
|
|
2580
|
+
this.configuration.websocketProvider.detach(this);
|
|
2581
|
+
this.isAttached = false;
|
|
2582
|
+
}
|
|
2583
|
+
attach() {
|
|
2584
|
+
if (this.isAttached)
|
|
2733
2585
|
return;
|
|
2734
|
-
|
|
2735
|
-
|
|
2586
|
+
this.configuration.websocketProvider.on('connect', this.configuration.onConnect);
|
|
2587
|
+
this.configuration.websocketProvider.on('connect', this.forwardConnect);
|
|
2588
|
+
this.configuration.websocketProvider.on('open', this.boundOnOpen);
|
|
2589
|
+
this.configuration.websocketProvider.on('close', this.boundOnClose);
|
|
2590
|
+
this.configuration.websocketProvider.on('close', this.configuration.onClose);
|
|
2591
|
+
this.configuration.websocketProvider.on('close', this.forwardClose);
|
|
2592
|
+
this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect);
|
|
2593
|
+
this.configuration.websocketProvider.on('disconnect', this.forwardDisconnect);
|
|
2594
|
+
this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy);
|
|
2595
|
+
this.configuration.websocketProvider.on('destroy', this.forwardDestroy);
|
|
2596
|
+
this.configuration.websocketProvider.attach(this);
|
|
2597
|
+
this.isAttached = true;
|
|
2736
2598
|
}
|
|
2737
2599
|
permissionDeniedHandler(reason) {
|
|
2738
2600
|
this.emit('authenticationFailed', { reason });
|
|
2739
2601
|
this.isAuthenticated = false;
|
|
2740
|
-
this.disconnect();
|
|
2741
|
-
this.status = exports.WebSocketStatus.Disconnected;
|
|
2742
2602
|
}
|
|
2743
2603
|
authenticatedHandler(scope) {
|
|
2744
2604
|
this.isAuthenticated = true;
|
|
2745
2605
|
this.authorizedScope = scope;
|
|
2746
2606
|
this.emit('authenticated');
|
|
2747
2607
|
}
|
|
2748
|
-
get broadcastChannel() {
|
|
2749
|
-
return `${this.configuration.name}`;
|
|
2750
|
-
}
|
|
2751
|
-
broadcastChannelSubscriber(data) {
|
|
2752
|
-
this.mux(() => {
|
|
2753
|
-
const message = new IncomingMessage(data);
|
|
2754
|
-
const documentName = message.readVarString();
|
|
2755
|
-
message.writeVarString(documentName);
|
|
2756
|
-
new MessageReceiver(message)
|
|
2757
|
-
.setBroadcasted(true)
|
|
2758
|
-
.apply(this, false);
|
|
2759
|
-
});
|
|
2760
|
-
}
|
|
2761
|
-
subscribeToBroadcastChannel() {
|
|
2762
|
-
if (!this.subscribedToBroadcastChannel) {
|
|
2763
|
-
subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
|
|
2764
|
-
this.subscribedToBroadcastChannel = true;
|
|
2765
|
-
}
|
|
2766
|
-
this.mux(() => {
|
|
2767
|
-
this.broadcast(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
|
|
2768
|
-
this.broadcast(SyncStepTwoMessage, { document: this.document, documentName: this.configuration.name });
|
|
2769
|
-
this.broadcast(QueryAwarenessMessage, { document: this.document, documentName: this.configuration.name });
|
|
2770
|
-
if (this.awareness) {
|
|
2771
|
-
this.broadcast(AwarenessMessage, {
|
|
2772
|
-
awareness: this.awareness,
|
|
2773
|
-
clients: [this.document.clientID],
|
|
2774
|
-
document: this.document,
|
|
2775
|
-
documentName: this.configuration.name,
|
|
2776
|
-
});
|
|
2777
|
-
}
|
|
2778
|
-
});
|
|
2779
|
-
}
|
|
2780
|
-
disconnectBroadcastChannel() {
|
|
2781
|
-
// broadcast message with local awareness state set to null (indicating disconnect)
|
|
2782
|
-
if (this.awareness) {
|
|
2783
|
-
this.send(AwarenessMessage, {
|
|
2784
|
-
awareness: this.awareness,
|
|
2785
|
-
clients: [this.document.clientID],
|
|
2786
|
-
states: new Map(),
|
|
2787
|
-
documentName: this.configuration.name,
|
|
2788
|
-
}, true);
|
|
2789
|
-
}
|
|
2790
|
-
if (this.subscribedToBroadcastChannel) {
|
|
2791
|
-
unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
|
|
2792
|
-
this.subscribedToBroadcastChannel = false;
|
|
2793
|
-
}
|
|
2794
|
-
}
|
|
2795
|
-
broadcast(Message, args) {
|
|
2796
|
-
if (!this.configuration.broadcast) {
|
|
2797
|
-
return;
|
|
2798
|
-
}
|
|
2799
|
-
if (!this.subscribedToBroadcastChannel) {
|
|
2800
|
-
return;
|
|
2801
|
-
}
|
|
2802
|
-
new MessageSender(Message, args).broadcast(this.broadcastChannel);
|
|
2803
|
-
}
|
|
2804
2608
|
setAwarenessField(key, value) {
|
|
2805
2609
|
if (!this.awareness) {
|
|
2806
2610
|
throw new AwarenessError(`Cannot set awareness field "${key}" to ${JSON.stringify(value)}. You have disabled Awareness for this provider by explicitly passing awareness: null in the provider configuration.`);
|
|
@@ -2809,412 +2613,7 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2809
2613
|
}
|
|
2810
2614
|
}
|
|
2811
2615
|
|
|
2812
|
-
/* eslint-env browser */
|
|
2813
|
-
const getRandomValues = crypto.getRandomValues.bind(crypto);
|
|
2814
|
-
|
|
2815
|
-
/**
|
|
2816
|
-
* Isomorphic module for true random numbers / buffers / uuids.
|
|
2817
|
-
*
|
|
2818
|
-
* Attention: falls back to Math.random if the browser does not support crypto.
|
|
2819
|
-
*
|
|
2820
|
-
* @module random
|
|
2821
|
-
*/
|
|
2822
|
-
|
|
2823
|
-
const uint32 = () => getRandomValues(new Uint32Array(1))[0];
|
|
2824
|
-
|
|
2825
|
-
// @ts-ignore
|
|
2826
|
-
const uuidv4Template = [1e7] + -1e3 + -4e3 + -8e3 + -1e11;
|
|
2827
|
-
|
|
2828
|
-
/**
|
|
2829
|
-
* @return {string}
|
|
2830
|
-
*/
|
|
2831
|
-
const uuidv4 = () => uuidv4Template.replace(/[018]/g, /** @param {number} c */ c =>
|
|
2832
|
-
(c ^ uint32() & 15 >> c / 4).toString(16)
|
|
2833
|
-
);
|
|
2834
|
-
|
|
2835
|
-
class TiptapCollabProviderWebsocket extends HocuspocusProviderWebsocket {
|
|
2836
|
-
constructor(configuration) {
|
|
2837
|
-
var _a;
|
|
2838
|
-
let url = (_a = configuration.baseUrl) !== null && _a !== void 0 ? _a : `wss://${configuration.appId}.collab.tiptap.cloud`;
|
|
2839
|
-
if (configuration.shardKey) {
|
|
2840
|
-
url += url.includes('?') ? '&' : '?';
|
|
2841
|
-
url += `shard=${configuration.shardKey}`;
|
|
2842
|
-
}
|
|
2843
|
-
super({ ...configuration, url });
|
|
2844
|
-
}
|
|
2845
|
-
}
|
|
2846
|
-
|
|
2847
|
-
const defaultDeleteCommentOptions = {
|
|
2848
|
-
deleteContent: false,
|
|
2849
|
-
deleteThread: false,
|
|
2850
|
-
};
|
|
2851
|
-
const defaultGetThreadsOptions = {
|
|
2852
|
-
types: ['unarchived'],
|
|
2853
|
-
};
|
|
2854
|
-
const defaultDeleteThreadOptions = {
|
|
2855
|
-
deleteComments: false,
|
|
2856
|
-
force: false,
|
|
2857
|
-
};
|
|
2858
|
-
class TiptapCollabProvider extends HocuspocusProvider {
|
|
2859
|
-
constructor(configuration) {
|
|
2860
|
-
if (!configuration.websocketProvider) {
|
|
2861
|
-
configuration.websocketProvider = new TiptapCollabProviderWebsocket({ appId: configuration.appId, baseUrl: configuration.baseUrl });
|
|
2862
|
-
}
|
|
2863
|
-
if (!configuration.token) {
|
|
2864
|
-
configuration.token = 'notoken'; // need to send a token anyway (which will be ignored)
|
|
2865
|
-
}
|
|
2866
|
-
super(configuration);
|
|
2867
|
-
this.tiptapCollabConfigurationPrefix = '__tiptapcollab__';
|
|
2868
|
-
if (configuration.user) {
|
|
2869
|
-
this.userData = new Y__namespace.PermanentUserData(this.document, this.document.getMap('__tiptapcollab__users'));
|
|
2870
|
-
this.userData.setUserMapping(this.document, this.document.clientID, configuration.user);
|
|
2871
|
-
}
|
|
2872
|
-
}
|
|
2873
|
-
/**
|
|
2874
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
2875
|
-
*/
|
|
2876
|
-
createVersion(name) {
|
|
2877
|
-
return this.sendStateless(JSON.stringify({ action: 'version.create', name }));
|
|
2878
|
-
}
|
|
2879
|
-
/**
|
|
2880
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
2881
|
-
*/
|
|
2882
|
-
revertToVersion(targetVersion) {
|
|
2883
|
-
return this.sendStateless(JSON.stringify({ action: 'document.revert', version: targetVersion }));
|
|
2884
|
-
}
|
|
2885
|
-
/**
|
|
2886
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
2887
|
-
*
|
|
2888
|
-
* The server will reply with a stateless message (THistoryVersionPreviewEvent)
|
|
2889
|
-
*/
|
|
2890
|
-
previewVersion(targetVersion) {
|
|
2891
|
-
return this.sendStateless(JSON.stringify({ action: 'version.preview', version: targetVersion }));
|
|
2892
|
-
}
|
|
2893
|
-
/**
|
|
2894
|
-
* note: this will only work if your server loaded @hocuspocus-pro/extension-history, or if you are on a Tiptap business plan.
|
|
2895
|
-
*/
|
|
2896
|
-
getVersions() {
|
|
2897
|
-
return this.configuration.document.getArray(`${this.tiptapCollabConfigurationPrefix}versions`).toArray();
|
|
2898
|
-
}
|
|
2899
|
-
watchVersions(callback) {
|
|
2900
|
-
return this.configuration.document.getArray('__tiptapcollab__versions').observe(callback);
|
|
2901
|
-
}
|
|
2902
|
-
unwatchVersions(callback) {
|
|
2903
|
-
return this.configuration.document.getArray('__tiptapcollab__versions').unobserve(callback);
|
|
2904
|
-
}
|
|
2905
|
-
isAutoVersioning() {
|
|
2906
|
-
return !!this.configuration.document.getMap(`${this.tiptapCollabConfigurationPrefix}config`).get('autoVersioning');
|
|
2907
|
-
}
|
|
2908
|
-
enableAutoVersioning() {
|
|
2909
|
-
return this.configuration.document.getMap(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 1);
|
|
2910
|
-
}
|
|
2911
|
-
disableAutoVersioning() {
|
|
2912
|
-
return this.configuration.document.getMap(`${this.tiptapCollabConfigurationPrefix}config`).set('autoVersioning', 0);
|
|
2913
|
-
}
|
|
2914
|
-
/**
|
|
2915
|
-
* Returns all users in the document as Y.Map objects
|
|
2916
|
-
* @returns An array of Y.Map objects
|
|
2917
|
-
*/
|
|
2918
|
-
getYThreads() {
|
|
2919
|
-
return this.configuration.document.getArray(`${this.tiptapCollabConfigurationPrefix}threads`);
|
|
2920
|
-
}
|
|
2921
|
-
/**
|
|
2922
|
-
* Finds all threads in the document and returns them as JSON objects
|
|
2923
|
-
* @options Options to control the output of the threads (e.g. include deleted threads)
|
|
2924
|
-
* @returns An array of threads as JSON objects
|
|
2925
|
-
*/
|
|
2926
|
-
getThreads(options) {
|
|
2927
|
-
const { types } = { ...defaultGetThreadsOptions, ...options };
|
|
2928
|
-
const threads = this.getYThreads().toJSON();
|
|
2929
|
-
if ((types === null || types === void 0 ? void 0 : types.includes('archived')) && (types === null || types === void 0 ? void 0 : types.includes('unarchived'))) {
|
|
2930
|
-
return threads;
|
|
2931
|
-
}
|
|
2932
|
-
return threads.filter(currentThead => {
|
|
2933
|
-
if ((types === null || types === void 0 ? void 0 : types.includes('archived')) && currentThead.deletedAt) {
|
|
2934
|
-
return true;
|
|
2935
|
-
}
|
|
2936
|
-
if ((types === null || types === void 0 ? void 0 : types.includes('unarchived')) && !currentThead.deletedAt) {
|
|
2937
|
-
return true;
|
|
2938
|
-
}
|
|
2939
|
-
return false;
|
|
2940
|
-
});
|
|
2941
|
-
}
|
|
2942
|
-
/**
|
|
2943
|
-
* Find the index of a thread by its id
|
|
2944
|
-
* @param id The thread id
|
|
2945
|
-
* @returns The index of the thread or null if not found
|
|
2946
|
-
*/
|
|
2947
|
-
getThreadIndex(id) {
|
|
2948
|
-
let index = null;
|
|
2949
|
-
let i = 0;
|
|
2950
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
2951
|
-
for (const thread of this.getThreads({ types: ['archived', 'unarchived'] })) {
|
|
2952
|
-
if (thread.id === id) {
|
|
2953
|
-
index = i;
|
|
2954
|
-
break;
|
|
2955
|
-
}
|
|
2956
|
-
i += 1;
|
|
2957
|
-
}
|
|
2958
|
-
return index;
|
|
2959
|
-
}
|
|
2960
|
-
/**
|
|
2961
|
-
* Gets a single thread by its id
|
|
2962
|
-
* @param id The thread id
|
|
2963
|
-
* @returns The thread as a JSON object or null if not found
|
|
2964
|
-
*/
|
|
2965
|
-
getThread(id) {
|
|
2966
|
-
const index = this.getThreadIndex(id);
|
|
2967
|
-
if (index === null) {
|
|
2968
|
-
return null;
|
|
2969
|
-
}
|
|
2970
|
-
return this.getYThreads().get(index).toJSON();
|
|
2971
|
-
}
|
|
2972
|
-
/**
|
|
2973
|
-
* Gets a single thread by its id as a Y.Map object
|
|
2974
|
-
* @param id The thread id
|
|
2975
|
-
* @returns The thread as a Y.Map object or null if not found
|
|
2976
|
-
*/
|
|
2977
|
-
getYThread(id) {
|
|
2978
|
-
const index = this.getThreadIndex(id);
|
|
2979
|
-
if (index === null) {
|
|
2980
|
-
return null;
|
|
2981
|
-
}
|
|
2982
|
-
return this.getYThreads().get(index);
|
|
2983
|
-
}
|
|
2984
|
-
/**
|
|
2985
|
-
* Create a new thread
|
|
2986
|
-
* @param data The thread data
|
|
2987
|
-
* @returns The created thread
|
|
2988
|
-
*/
|
|
2989
|
-
createThread(data) {
|
|
2990
|
-
let createdThread = {};
|
|
2991
|
-
this.document.transact(() => {
|
|
2992
|
-
const thread = new Y__namespace.Map();
|
|
2993
|
-
thread.set('id', uuidv4());
|
|
2994
|
-
thread.set('createdAt', (new Date()).toISOString());
|
|
2995
|
-
thread.set('comments', new Y__namespace.Array());
|
|
2996
|
-
thread.set('deletedComments', new Y__namespace.Array());
|
|
2997
|
-
thread.set('deletedAt', null);
|
|
2998
|
-
this.getYThreads().push([thread]);
|
|
2999
|
-
createdThread = this.updateThread(String(thread.get('id')), data);
|
|
3000
|
-
});
|
|
3001
|
-
return createdThread;
|
|
3002
|
-
}
|
|
3003
|
-
/**
|
|
3004
|
-
* Update a specific thread
|
|
3005
|
-
* @param id The thread id
|
|
3006
|
-
* @param data New data for the thread
|
|
3007
|
-
* @returns The updated thread or null if the thread is not found
|
|
3008
|
-
*/
|
|
3009
|
-
updateThread(id, data) {
|
|
3010
|
-
let updatedThread = {};
|
|
3011
|
-
this.document.transact(() => {
|
|
3012
|
-
const thread = this.getYThread(id);
|
|
3013
|
-
if (thread === null) {
|
|
3014
|
-
return null;
|
|
3015
|
-
}
|
|
3016
|
-
thread.set('updatedAt', (new Date()).toISOString());
|
|
3017
|
-
if (data.data) {
|
|
3018
|
-
thread.set('data', data.data);
|
|
3019
|
-
}
|
|
3020
|
-
if (data.resolvedAt || data.resolvedAt === null) {
|
|
3021
|
-
thread.set('resolvedAt', data.resolvedAt);
|
|
3022
|
-
}
|
|
3023
|
-
updatedThread = thread.toJSON();
|
|
3024
|
-
});
|
|
3025
|
-
return updatedThread;
|
|
3026
|
-
}
|
|
3027
|
-
/**
|
|
3028
|
-
* Handle the deletion of a thread. By default, the thread and it's comments are not deleted, but marked as deleted
|
|
3029
|
-
* via the `deletedAt` property. Forceful deletion can be enabled by setting the `force` option to `true`.
|
|
3030
|
-
*
|
|
3031
|
-
* If you only want to delete the comments of a thread, you can set the `deleteComments` option to `true`.
|
|
3032
|
-
* @param id The thread id
|
|
3033
|
-
* @param options A set of options that control how the thread is deleted
|
|
3034
|
-
* @returns The deleted thread or null if the thread is not found
|
|
3035
|
-
*/
|
|
3036
|
-
deleteThread(id, options) {
|
|
3037
|
-
const { deleteComments, force } = { ...defaultDeleteThreadOptions, ...options };
|
|
3038
|
-
const index = this.getThreadIndex(id);
|
|
3039
|
-
if (index === null) {
|
|
3040
|
-
return null;
|
|
3041
|
-
}
|
|
3042
|
-
if (force) {
|
|
3043
|
-
this.getYThreads().delete(index, 1);
|
|
3044
|
-
return;
|
|
3045
|
-
}
|
|
3046
|
-
const thread = this.getYThreads().get(index);
|
|
3047
|
-
thread.set('deletedAt', (new Date()).toISOString());
|
|
3048
|
-
if (deleteComments) {
|
|
3049
|
-
thread.set('comments', new Y__namespace.Array());
|
|
3050
|
-
thread.set('deletedComments', new Y__namespace.Array());
|
|
3051
|
-
}
|
|
3052
|
-
return thread.toJSON();
|
|
3053
|
-
}
|
|
3054
|
-
/**
|
|
3055
|
-
* Tries to restore a deleted thread
|
|
3056
|
-
* @param id The thread id
|
|
3057
|
-
* @returns The restored thread or null if the thread is not found
|
|
3058
|
-
*/
|
|
3059
|
-
restoreThread(id) {
|
|
3060
|
-
const index = this.getThreadIndex(id);
|
|
3061
|
-
if (index === null) {
|
|
3062
|
-
return null;
|
|
3063
|
-
}
|
|
3064
|
-
const thread = this.getYThreads().get(index);
|
|
3065
|
-
thread.set('deletedAt', null);
|
|
3066
|
-
return thread.toJSON();
|
|
3067
|
-
}
|
|
3068
|
-
/**
|
|
3069
|
-
* Returns comments from a thread, either deleted or not
|
|
3070
|
-
* @param threadId The thread id
|
|
3071
|
-
* @param includeDeleted If you want to include deleted comments, defaults to `false`
|
|
3072
|
-
* @returns The comments or null if the thread is not found
|
|
3073
|
-
*/
|
|
3074
|
-
getThreadComments(threadId, includeDeleted) {
|
|
3075
|
-
var _a, _b, _c;
|
|
3076
|
-
const index = this.getThreadIndex(threadId);
|
|
3077
|
-
if (index === null) {
|
|
3078
|
-
return null;
|
|
3079
|
-
}
|
|
3080
|
-
const comments = !includeDeleted ? (_a = this.getThread(threadId)) === null || _a === void 0 ? void 0 : _a.comments : [...(((_b = this.getThread(threadId)) === null || _b === void 0 ? void 0 : _b.comments) || []), ...(((_c = this.getThread(threadId)) === null || _c === void 0 ? void 0 : _c.deletedComments) || [])].sort((a, b) => {
|
|
3081
|
-
return a.createdAt.localeCompare(b.createdAt);
|
|
3082
|
-
});
|
|
3083
|
-
return comments !== null && comments !== void 0 ? comments : [];
|
|
3084
|
-
}
|
|
3085
|
-
/**
|
|
3086
|
-
* Get a single comment from a specific thread
|
|
3087
|
-
* @param threadId The thread id
|
|
3088
|
-
* @param commentId The comment id
|
|
3089
|
-
* @param includeDeleted If you want to include deleted comments in the search
|
|
3090
|
-
* @returns The comment or null if not found
|
|
3091
|
-
*/
|
|
3092
|
-
getThreadComment(threadId, commentId, includeDeleted) {
|
|
3093
|
-
var _a;
|
|
3094
|
-
const index = this.getThreadIndex(threadId);
|
|
3095
|
-
if (index === null) {
|
|
3096
|
-
return null;
|
|
3097
|
-
}
|
|
3098
|
-
const comments = this.getThreadComments(threadId, includeDeleted);
|
|
3099
|
-
return (_a = comments === null || comments === void 0 ? void 0 : comments.find(comment => comment.id === commentId)) !== null && _a !== void 0 ? _a : null;
|
|
3100
|
-
}
|
|
3101
|
-
/**
|
|
3102
|
-
* Adds a comment to a thread
|
|
3103
|
-
* @param threadId The thread id
|
|
3104
|
-
* @param data The comment data
|
|
3105
|
-
* @returns The updated thread or null if the thread is not found
|
|
3106
|
-
* @example addComment('123', { content: 'Hello world', data: { author: 'Maria Doe' } })
|
|
3107
|
-
*/
|
|
3108
|
-
addComment(threadId, data) {
|
|
3109
|
-
let updatedThread = {};
|
|
3110
|
-
this.document.transact(() => {
|
|
3111
|
-
const thread = this.getYThread(threadId);
|
|
3112
|
-
if (thread === null)
|
|
3113
|
-
return null;
|
|
3114
|
-
const commentMap = new Y__namespace.Map();
|
|
3115
|
-
commentMap.set('id', uuidv4());
|
|
3116
|
-
commentMap.set('createdAt', (new Date()).toISOString());
|
|
3117
|
-
thread.get('comments').push([commentMap]);
|
|
3118
|
-
this.updateComment(threadId, String(commentMap.get('id')), data);
|
|
3119
|
-
updatedThread = thread.toJSON();
|
|
3120
|
-
});
|
|
3121
|
-
return updatedThread;
|
|
3122
|
-
}
|
|
3123
|
-
/**
|
|
3124
|
-
* Update a comment in a thread
|
|
3125
|
-
* @param threadId The thread id
|
|
3126
|
-
* @param commentId The comment id
|
|
3127
|
-
* @param data The new comment data
|
|
3128
|
-
* @returns The updated thread or null if the thread or comment is not found
|
|
3129
|
-
* @example updateComment('123', { content: 'The new content', data: { attachments: ['file1.jpg'] }})
|
|
3130
|
-
*/
|
|
3131
|
-
updateComment(threadId, commentId, data) {
|
|
3132
|
-
let updatedThread = {};
|
|
3133
|
-
this.document.transact(() => {
|
|
3134
|
-
const thread = this.getYThread(threadId);
|
|
3135
|
-
if (thread === null)
|
|
3136
|
-
return null;
|
|
3137
|
-
let comment = null;
|
|
3138
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
3139
|
-
for (const c of thread.get('comments')) {
|
|
3140
|
-
if (c.get('id') === commentId) {
|
|
3141
|
-
comment = c;
|
|
3142
|
-
break;
|
|
3143
|
-
}
|
|
3144
|
-
}
|
|
3145
|
-
if (comment === null)
|
|
3146
|
-
return null;
|
|
3147
|
-
comment.set('updatedAt', (new Date()).toISOString());
|
|
3148
|
-
if (data.data) {
|
|
3149
|
-
comment.set('data', data.data);
|
|
3150
|
-
}
|
|
3151
|
-
if (data.content) {
|
|
3152
|
-
comment.set('content', data.content);
|
|
3153
|
-
}
|
|
3154
|
-
updatedThread = thread.toJSON();
|
|
3155
|
-
});
|
|
3156
|
-
return updatedThread;
|
|
3157
|
-
}
|
|
3158
|
-
/**
|
|
3159
|
-
* Deletes a comment from a thread
|
|
3160
|
-
* @param threadId The thread id
|
|
3161
|
-
* @param commentId The comment id
|
|
3162
|
-
* @param options A set of options that control how the comment is deleted
|
|
3163
|
-
* @returns The updated thread or null if the thread or comment is not found
|
|
3164
|
-
*/
|
|
3165
|
-
deleteComment(threadId, commentId, options) {
|
|
3166
|
-
const { deleteContent, deleteThread } = { ...defaultDeleteCommentOptions, ...options };
|
|
3167
|
-
const thread = this.getYThread(threadId);
|
|
3168
|
-
if (thread === null)
|
|
3169
|
-
return null;
|
|
3170
|
-
let commentIndex = 0;
|
|
3171
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
3172
|
-
for (const c of thread.get('comments')) {
|
|
3173
|
-
if (c.get('id') === commentId) {
|
|
3174
|
-
break;
|
|
3175
|
-
}
|
|
3176
|
-
commentIndex += 1;
|
|
3177
|
-
}
|
|
3178
|
-
// if the first comment of a thread is deleted we also
|
|
3179
|
-
// delete the thread itself as the source comment is gone
|
|
3180
|
-
if (commentIndex === 0 && (deleteThread || this.configuration.deleteThreadOnFirstCommentDelete)) {
|
|
3181
|
-
this.deleteThread(threadId);
|
|
3182
|
-
return;
|
|
3183
|
-
}
|
|
3184
|
-
const comment = thread.get('comments').get(commentIndex);
|
|
3185
|
-
const newComment = new Y__namespace.Map();
|
|
3186
|
-
newComment.set('id', comment.get('id'));
|
|
3187
|
-
newComment.set('createdAt', comment.get('createdAt'));
|
|
3188
|
-
newComment.set('updatedAt', (new Date()).toISOString());
|
|
3189
|
-
newComment.set('deletedAt', (new Date()).toISOString());
|
|
3190
|
-
newComment.set('data', comment.get('data'));
|
|
3191
|
-
newComment.set('content', deleteContent ? null : comment.get('content'));
|
|
3192
|
-
if (!thread.get('deletedComments')) {
|
|
3193
|
-
thread.set('deletedComments', new Y__namespace.Array());
|
|
3194
|
-
}
|
|
3195
|
-
thread.get('deletedComments').push([newComment]);
|
|
3196
|
-
thread.get('comments').delete(commentIndex);
|
|
3197
|
-
return thread.toJSON();
|
|
3198
|
-
}
|
|
3199
|
-
/**
|
|
3200
|
-
* Start watching threads for changes
|
|
3201
|
-
* @param callback The callback function to be called when a thread changes
|
|
3202
|
-
*/
|
|
3203
|
-
watchThreads(callback) {
|
|
3204
|
-
this.getYThreads().observeDeep(callback);
|
|
3205
|
-
}
|
|
3206
|
-
/**
|
|
3207
|
-
* Stop watching threads for changes
|
|
3208
|
-
* @param callback The callback function to be removed
|
|
3209
|
-
*/
|
|
3210
|
-
unwatchThreads(callback) {
|
|
3211
|
-
this.getYThreads().unobserveDeep(callback);
|
|
3212
|
-
}
|
|
3213
|
-
}
|
|
3214
|
-
|
|
3215
2616
|
exports.AwarenessError = AwarenessError;
|
|
3216
2617
|
exports.HocuspocusProvider = HocuspocusProvider;
|
|
3217
2618
|
exports.HocuspocusProviderWebsocket = HocuspocusProviderWebsocket;
|
|
3218
|
-
exports.TiptapCollabProvider = TiptapCollabProvider;
|
|
3219
|
-
exports.TiptapCollabProviderWebsocket = TiptapCollabProviderWebsocket;
|
|
3220
2619
|
//# sourceMappingURL=hocuspocus-provider.cjs.map
|