@hocuspocus/provider 1.0.0-alpha.12 → 1.0.0-alpha.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,41 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [1.0.0-alpha.16](https://github.com/ueberdosis/hocuspocus/compare/@hocuspocus/provider@1.0.0-alpha.15...@hocuspocus/provider@1.0.0-alpha.16) (2021-09-17)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Remote client awareness not immediately available ([37703f6](https://github.com/ueberdosis/hocuspocus/commit/37703f695f6bf3b0508c287294c5d26d2e888c37))
12
+
13
+
14
+
15
+
16
+
17
+ # [1.0.0-alpha.15](https://github.com/ueberdosis/hocuspocus/compare/@hocuspocus/provider@1.0.0-alpha.14...@hocuspocus/provider@1.0.0-alpha.15) (2021-09-14)
18
+
19
+ **Note:** Version bump only for package @hocuspocus/provider
20
+
21
+
22
+
23
+
24
+
25
+ # [1.0.0-alpha.14](https://github.com/ueberdosis/hocuspocus/compare/@hocuspocus/provider@1.0.0-alpha.13...@hocuspocus/provider@1.0.0-alpha.14) (2021-09-03)
26
+
27
+ **Note:** Version bump only for package @hocuspocus/provider
28
+
29
+
30
+
31
+
32
+
33
+ # [1.0.0-alpha.13](https://github.com/ueberdosis/hocuspocus/compare/@hocuspocus/provider@1.0.0-alpha.12...@hocuspocus/provider@1.0.0-alpha.13) (2021-09-01)
34
+
35
+ **Note:** Version bump only for package @hocuspocus/provider
36
+
37
+
38
+
39
+
40
+
6
41
  # [1.0.0-alpha.12](https://github.com/ueberdosis/hocuspocus/compare/@hocuspocus/provider@1.0.0-alpha.11...@hocuspocus/provider@1.0.0-alpha.12) (2021-08-31)
7
42
 
8
43
  **Note:** Version bump only for package @hocuspocus/provider
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var Y = require('yjs');
6
+ var attempt = require('@lifeomic/attempt');
6
7
 
7
8
  function _interopNamespace(e) {
8
9
  if (e && e.__esModule) return e;
@@ -284,8 +285,6 @@ hasConf('production');
284
285
  */
285
286
 
286
287
  const floor = Math.floor;
287
- const round = Math.round;
288
- const log10 = Math.log10;
289
288
 
290
289
  /**
291
290
  * @function
@@ -1416,6 +1415,9 @@ class IncomingMessage {
1416
1415
  writeVarUint8Array(data) {
1417
1416
  return writeVarUint8Array(this.encoder, data);
1418
1417
  }
1418
+ length() {
1419
+ return length$1(this.encoder);
1420
+ }
1419
1421
  }
1420
1422
 
1421
1423
  /**
@@ -1576,12 +1578,30 @@ const readAuthMessage = (decoder, permissionDeniedHandler, authenticatedHandler)
1576
1578
  }
1577
1579
  };
1578
1580
 
1581
+ class OutgoingMessage {
1582
+ constructor() {
1583
+ this.encoder = createEncoder();
1584
+ }
1585
+ get(args) {
1586
+ return args.encoder;
1587
+ }
1588
+ toUint8Array() {
1589
+ return toUint8Array(this.encoder);
1590
+ }
1591
+ }
1592
+
1579
1593
  class MessageReceiver {
1580
1594
  constructor(message) {
1595
+ this.broadcasted = false;
1581
1596
  this.message = message;
1582
1597
  }
1598
+ setBroadcasted(value) {
1599
+ this.broadcasted = value;
1600
+ return this;
1601
+ }
1583
1602
  apply(provider, emitSynced = true) {
1584
- const type = this.message.readVarUint();
1603
+ const { message } = this;
1604
+ const type = message.readVarUint();
1585
1605
  switch (type) {
1586
1606
  case exports.MessageType.Sync:
1587
1607
  this.applySyncMessage(provider, emitSynced);
@@ -1598,11 +1618,26 @@ class MessageReceiver {
1598
1618
  default:
1599
1619
  throw new Error(`Can’t apply message of unknown type: ${type}`);
1600
1620
  }
1621
+ // Reply
1622
+ if (message.length() > 1) {
1623
+ if (this.broadcasted) {
1624
+ // TODO: Some weird TypeScript error
1625
+ // @ts-ignore
1626
+ provider.broadcast(OutgoingMessage, { encoder: message.encoder });
1627
+ }
1628
+ else {
1629
+ // TODO: Some weird TypeScript error
1630
+ // @ts-ignore
1631
+ provider.send(OutgoingMessage, { encoder: message.encoder });
1632
+ }
1633
+ }
1601
1634
  }
1602
1635
  applySyncMessage(provider, emitSynced) {
1603
1636
  const { message } = this;
1604
1637
  message.writeVarUint(exports.MessageType.Sync);
1638
+ // Apply update
1605
1639
  const syncMessageType = readSyncMessage(message.decoder, message.encoder, provider.document, provider);
1640
+ // Synced once we receive Step2
1606
1641
  if (emitSynced && syncMessageType === messageYjsSyncStep2) {
1607
1642
  provider.synced = true;
1608
1643
  }
@@ -1638,15 +1673,6 @@ class MessageSender {
1638
1673
  }
1639
1674
  }
1640
1675
 
1641
- class OutgoingMessage {
1642
- constructor() {
1643
- this.encoder = createEncoder();
1644
- }
1645
- toUint8Array() {
1646
- return toUint8Array(this.encoder);
1647
- }
1648
- }
1649
-
1650
1676
  class SyncStepOneMessage extends OutgoingMessage {
1651
1677
  constructor() {
1652
1678
  super(...arguments);
@@ -1755,7 +1781,6 @@ var awarenessStatesToArray = (states) => {
1755
1781
  });
1756
1782
  };
1757
1783
 
1758
- // @ts-nocheck
1759
1784
  exports.WebSocketStatus = void 0;
1760
1785
  (function (WebSocketStatus) {
1761
1786
  WebSocketStatus["Connecting"] = "connecting";
@@ -1766,18 +1791,36 @@ class HocuspocusProvider extends EventEmitter {
1766
1791
  constructor(options = {}) {
1767
1792
  super();
1768
1793
  this.options = {
1794
+ // @ts-ignore
1795
+ document: undefined,
1796
+ // @ts-ignore
1797
+ awareness: undefined,
1798
+ WebSocketPolyfill: undefined,
1769
1799
  url: '',
1770
1800
  name: '',
1771
1801
  token: null,
1772
1802
  parameters: {},
1773
- debug: false,
1774
1803
  connect: true,
1775
1804
  broadcast: true,
1776
1805
  forceSyncInterval: false,
1777
- reconnectTimeoutBase: 1200,
1778
- maxReconnectTimeout: 2500,
1779
1806
  // TODO: this should depend on awareness.outdatedTime
1780
1807
  messageReconnectTimeout: 30000,
1808
+ // 1 second
1809
+ delay: 1000,
1810
+ // instant
1811
+ initialDelay: 0,
1812
+ // double the delay each time
1813
+ factor: 2,
1814
+ // unlimited retries
1815
+ maxAttempts: 0,
1816
+ // wait at least 1 second
1817
+ minDelay: 1000,
1818
+ // at least every 30 seconds
1819
+ maxDelay: 30000,
1820
+ // randomize
1821
+ jitter: true,
1822
+ // retry forever
1823
+ timeout: 0,
1781
1824
  onAuthenticated: () => null,
1782
1825
  onAuthenticationFailed: () => null,
1783
1826
  onOpen: () => null,
@@ -1796,7 +1839,6 @@ class HocuspocusProvider extends EventEmitter {
1796
1839
  this.webSocket = null;
1797
1840
  this.shouldConnect = true;
1798
1841
  this.status = exports.WebSocketStatus.Disconnected;
1799
- this.failedConnectionAttempts = 0;
1800
1842
  this.isSynced = false;
1801
1843
  this.isAuthenticated = false;
1802
1844
  this.lastMessageReceived = 0;
@@ -1805,11 +1847,10 @@ class HocuspocusProvider extends EventEmitter {
1805
1847
  forceSync: null,
1806
1848
  connectionChecker: null,
1807
1849
  };
1850
+ this.connectionAttempt = null;
1808
1851
  this.setOptions(options);
1809
- this.options.document = options.document ? options.document : new Y__namespace.Doc();
1810
1852
  this.options.awareness = options.awareness ? options.awareness : new Awareness(this.document);
1811
1853
  this.options.WebSocketPolyfill = options.WebSocketPolyfill ? options.WebSocketPolyfill : WebSocket;
1812
- this.shouldConnect = options.connect !== undefined ? options.connect : this.shouldConnect;
1813
1854
  this.on('open', this.options.onOpen);
1814
1855
  this.on('authenticated', this.options.onAuthenticated);
1815
1856
  this.on('authenticationFailed', this.options.onAuthenticationFailed);
@@ -1824,29 +1865,78 @@ class HocuspocusProvider extends EventEmitter {
1824
1865
  this.on('awarenessUpdate', this.options.onAwarenessUpdate);
1825
1866
  this.on('awarenessChange', this.options.onAwarenessChange);
1826
1867
  this.awareness.on('update', () => {
1827
- this.emit('awarenessUpdate', {
1828
- states: awarenessStatesToArray(this.awareness.getStates()),
1829
- });
1868
+ this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) });
1830
1869
  });
1831
1870
  this.awareness.on('change', () => {
1832
- this.emit('awarenessChange', {
1833
- states: awarenessStatesToArray(this.awareness.getStates()),
1834
- });
1871
+ this.emit('awarenessChange', { states: awarenessStatesToArray(this.awareness.getStates()) });
1835
1872
  });
1836
- this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.options.messageReconnectTimeout / 10);
1837
1873
  this.document.on('update', this.documentUpdateHandler.bind(this));
1838
1874
  this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
1839
1875
  this.registerBeforeUnloadEventListener();
1876
+ this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.options.messageReconnectTimeout / 10);
1840
1877
  if (this.options.forceSyncInterval) {
1841
1878
  this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.options.forceSyncInterval);
1842
1879
  }
1843
- if (this.options.connect) {
1844
- this.connect();
1880
+ if (typeof options.connect !== 'undefined') {
1881
+ this.shouldConnect = options.connect;
1882
+ }
1883
+ if (!this.shouldConnect) {
1884
+ return;
1845
1885
  }
1886
+ this.connect();
1846
1887
  }
1847
1888
  setOptions(options = {}) {
1848
1889
  this.options = { ...this.options, ...options };
1849
1890
  }
1891
+ connect() {
1892
+ if (this.status === exports.WebSocketStatus.Connected) {
1893
+ return;
1894
+ }
1895
+ this.shouldConnect = true;
1896
+ this.subscribeToBroadcastChannel();
1897
+ attempt.retry(this.createWebSocketConnection.bind(this), {
1898
+ delay: this.options.delay,
1899
+ initialDelay: this.options.initialDelay,
1900
+ factor: this.options.factor,
1901
+ maxAttempts: this.options.maxAttempts,
1902
+ minDelay: this.options.minDelay,
1903
+ maxDelay: this.options.maxDelay,
1904
+ jitter: this.options.jitter,
1905
+ timeout: this.options.timeout,
1906
+ });
1907
+ }
1908
+ createWebSocketConnection() {
1909
+ return new Promise((resolve, reject) => {
1910
+ // Init the WebSocket connection
1911
+ this.webSocket = new this.options.WebSocketPolyfill(this.url);
1912
+ this.webSocket.binaryType = 'arraybuffer';
1913
+ this.webSocket.onmessage = this.onMessage.bind(this);
1914
+ this.webSocket.onclose = this.onClose.bind(this);
1915
+ this.webSocket.onopen = this.onOpen.bind(this);
1916
+ this.webSocket.onerror = () => {
1917
+ reject();
1918
+ };
1919
+ // Reset the status
1920
+ this.synced = false;
1921
+ this.status = exports.WebSocketStatus.Connecting;
1922
+ this.emit('status', { status: 'connecting' });
1923
+ // Store resolve/reject for later use
1924
+ this.connectionAttempt = {
1925
+ resolve,
1926
+ reject,
1927
+ };
1928
+ });
1929
+ }
1930
+ resolveConnectionAttempt() {
1931
+ var _a;
1932
+ (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.resolve();
1933
+ this.connectionAttempt = null;
1934
+ }
1935
+ rejectConnectionAttempt() {
1936
+ var _a;
1937
+ (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
1938
+ this.connectionAttempt = null;
1939
+ }
1850
1940
  get document() {
1851
1941
  return this.options.document;
1852
1942
  }
@@ -1854,11 +1944,11 @@ class HocuspocusProvider extends EventEmitter {
1854
1944
  return this.options.awareness;
1855
1945
  }
1856
1946
  checkConnection() {
1857
- // Don’t close the connection when it’s not established anyway
1947
+ // Don’t check the connection when it’s not even established
1858
1948
  if (this.status !== exports.WebSocketStatus.Connected) {
1859
1949
  return;
1860
1950
  }
1861
- // Don’t just close then connection while waiting for the first message
1951
+ // Don’t close then connection while waiting for the first message
1862
1952
  if (!this.lastMessageReceived) {
1863
1953
  return;
1864
1954
  }
@@ -1899,7 +1989,6 @@ class HocuspocusProvider extends EventEmitter {
1899
1989
  }
1900
1990
  permissionDeniedHandler(reason) {
1901
1991
  this.emit('authenticationFailed', { reason });
1902
- this.log('Permission denied', reason);
1903
1992
  this.isAuthenticated = false;
1904
1993
  this.shouldConnect = false;
1905
1994
  }
@@ -1933,13 +2022,6 @@ class HocuspocusProvider extends EventEmitter {
1933
2022
  get isAuthenticationRequired() {
1934
2023
  return !!this.options.token && !this.isAuthenticated;
1935
2024
  }
1936
- connect() {
1937
- this.shouldConnect = true;
1938
- if (this.status !== exports.WebSocketStatus.Connected) {
1939
- this.createWebSocketConnection();
1940
- this.subscribeToBroadcastChannel();
1941
- }
1942
- }
1943
2025
  disconnect() {
1944
2026
  this.shouldConnect = false;
1945
2027
  this.disconnectBroadcastChannel();
@@ -1953,35 +2035,31 @@ class HocuspocusProvider extends EventEmitter {
1953
2035
  //
1954
2036
  }
1955
2037
  }
1956
- createWebSocketConnection() {
1957
- if (this.webSocket !== null) {
1958
- return;
1959
- }
1960
- this.webSocket = new this.options.WebSocketPolyfill(this.url);
1961
- this.webSocket.binaryType = 'arraybuffer';
1962
- this.status = exports.WebSocketStatus.Connecting;
1963
- this.synced = false;
1964
- this.webSocket.onmessage = this.onMessage.bind(this);
1965
- this.webSocket.onclose = this.onClose.bind(this);
1966
- this.webSocket.onopen = this.onOpen.bind(this);
1967
- this.emit('status', { status: 'connecting' });
1968
- }
1969
2038
  onOpen(event) {
1970
2039
  this.emit('open', { event });
1971
2040
  if (this.status !== exports.WebSocketStatus.Connected) {
1972
2041
  this.webSocketConnectionEstablished();
1973
2042
  }
1974
2043
  }
1975
- webSocketConnectionEstablished() {
1976
- this.failedConnectionAttempts = 0;
2044
+ async getToken() {
2045
+ if (typeof this.options.token === 'function') {
2046
+ const token = await this.options.token();
2047
+ return token;
2048
+ }
2049
+ return this.options.token;
2050
+ }
2051
+ async webSocketConnectionEstablished() {
1977
2052
  this.status = exports.WebSocketStatus.Connected;
1978
2053
  this.emit('status', { status: 'connected' });
1979
2054
  this.emit('connect');
1980
2055
  if (this.isAuthenticationRequired) {
1981
- this.send(AuthenticationMessage, { token: this.options.token });
2056
+ this.send(AuthenticationMessage, {
2057
+ token: await this.getToken(),
2058
+ });
1982
2059
  return;
1983
2060
  }
1984
2061
  this.startSync();
2062
+ this.resolveConnectionAttempt();
1985
2063
  }
1986
2064
  startSync() {
1987
2065
  this.send(SyncStepOneMessage, { document: this.document });
@@ -1994,9 +2072,7 @@ class HocuspocusProvider extends EventEmitter {
1994
2072
  }
1995
2073
  send(Message, args, broadcast = false) {
1996
2074
  if (broadcast) {
1997
- this.mux(() => {
1998
- this.broadcast(Message, args);
1999
- });
2075
+ this.mux(() => { this.broadcast(Message, args); });
2000
2076
  }
2001
2077
  if (this.status === exports.WebSocketStatus.Connected) {
2002
2078
  const messageSender = new MessageSender(Message, args);
@@ -2009,38 +2085,39 @@ class HocuspocusProvider extends EventEmitter {
2009
2085
  const message = new IncomingMessage(event.data);
2010
2086
  this.emit('message', { event, message });
2011
2087
  new MessageReceiver(message).apply(this);
2012
- // TODO: What’s that doing?
2013
- // Move to the MessageReceiver
2014
- // if (encoding.length(encoder) > 1) {
2015
- // this.send(encoding.toUint8Array(encoder))
2016
- // }
2017
2088
  }
2018
2089
  onClose(event) {
2019
2090
  this.emit('close', { event });
2020
- this.isAuthenticated = false;
2021
2091
  this.webSocket = null;
2092
+ this.isAuthenticated = false;
2093
+ this.synced = false;
2022
2094
  if (this.status === exports.WebSocketStatus.Connected) {
2023
- this.synced = false;
2024
2095
  // update awareness (all users except local left)
2025
2096
  removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID), this);
2026
2097
  this.status = exports.WebSocketStatus.Disconnected;
2027
2098
  this.emit('status', { status: 'disconnected' });
2028
2099
  this.emit('disconnect', { event });
2029
2100
  }
2030
- else {
2031
- this.failedConnectionAttempts += 1;
2101
+ if (this.connectionAttempt) {
2102
+ // Okay, that connection attempt failed …
2103
+ this.rejectConnectionAttempt();
2032
2104
  }
2105
+ else if (this.shouldConnect) {
2106
+ // The connection was closed by the server, so let’s just try again.
2107
+ this.connect();
2108
+ }
2109
+ // If we’ll reconnect anyway, we’re done for now.
2033
2110
  if (this.shouldConnect) {
2034
- const wait = round(min(log10(this.failedConnectionAttempts + 1) * this.options.reconnectTimeoutBase, this.options.maxReconnectTimeout));
2035
- this.log(`[close] Reconnecting in ${wait}ms …`);
2036
- setTimeout(this.createWebSocketConnection.bind(this), wait);
2037
2111
  return;
2038
2112
  }
2039
- if (this.status !== exports.WebSocketStatus.Disconnected) {
2040
- this.status = exports.WebSocketStatus.Disconnected;
2041
- this.emit('status', { status: 'disconnected' });
2042
- this.emit('disconnect', { event });
2113
+ // The status is set correctly already.
2114
+ if (this.status === exports.WebSocketStatus.Disconnected) {
2115
+ return;
2043
2116
  }
2117
+ // Let’s update the connection status.
2118
+ this.status = exports.WebSocketStatus.Disconnected;
2119
+ this.emit('status', { status: 'disconnected' });
2120
+ this.emit('disconnect', { event });
2044
2121
  }
2045
2122
  destroy() {
2046
2123
  this.emit('destroy');
@@ -2059,11 +2136,9 @@ class HocuspocusProvider extends EventEmitter {
2059
2136
  broadcastChannelSubscriber(data) {
2060
2137
  this.mux(() => {
2061
2138
  const message = new IncomingMessage(data);
2062
- new MessageReceiver(message, this).apply(this, false);
2063
- // TODO: What’s that doing?
2064
- // if (encoding.length(encoder) > 1) {
2065
- // this.broadcast(encoding.toUint8Array(encoder))
2066
- // }
2139
+ new MessageReceiver(message)
2140
+ .setBroadcasted(true)
2141
+ .apply(this, false);
2067
2142
  });
2068
2143
  }
2069
2144
  subscribeToBroadcastChannel() {
@@ -2099,12 +2174,6 @@ class HocuspocusProvider extends EventEmitter {
2099
2174
  }
2100
2175
  new MessageSender(Message, args).broadcast(this.broadcastChannel);
2101
2176
  }
2102
- log(message) {
2103
- if (!this.options.debug) {
2104
- return;
2105
- }
2106
- console.log(message);
2107
- }
2108
2177
  setAwarenessField(key, value) {
2109
2178
  this.awareness.setLocalStateField(key, value);
2110
2179
  }