@hocuspocus/provider 1.1.0 → 2.0.0-alpha.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.
Files changed (37) hide show
  1. package/dist/hocuspocus-provider.cjs +352 -217
  2. package/dist/hocuspocus-provider.cjs.map +1 -1
  3. package/dist/hocuspocus-provider.esm.js +352 -218
  4. package/dist/hocuspocus-provider.esm.js.map +1 -1
  5. package/dist/packages/extension-monitor/src/Dashboard.d.ts +1 -1
  6. package/dist/packages/extension-monitor/src/index.d.ts +1 -1
  7. package/dist/packages/provider/src/HocuspocusCloudProvider.d.ts +2 -1
  8. package/dist/packages/provider/src/HocuspocusProvider.d.ts +10 -69
  9. package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +115 -0
  10. package/dist/packages/provider/src/IncomingMessage.d.ts +2 -0
  11. package/dist/packages/provider/src/index.d.ts +1 -0
  12. package/dist/packages/provider/src/types.d.ts +1 -0
  13. package/dist/packages/server/src/Connection.d.ts +1 -11
  14. package/dist/packages/server/src/Hocuspocus.d.ts +3 -8
  15. package/dist/packages/server/src/IncomingMessage.d.ts +1 -0
  16. package/dist/packages/server/src/MessageReceiver.d.ts +1 -2
  17. package/dist/packages/server/src/OutgoingMessage.d.ts +1 -1
  18. package/dist/tests/utils/index.d.ts +1 -0
  19. package/dist/tests/utils/newHocuspocusProvider.d.ts +2 -2
  20. package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +3 -0
  21. package/package.json +3 -3
  22. package/src/HocuspocusCloudProvider.ts +8 -1
  23. package/src/HocuspocusProvider.ts +100 -360
  24. package/src/HocuspocusProviderWebsocket.ts +475 -0
  25. package/src/IncomingMessage.ts +10 -0
  26. package/src/MessageReceiver.ts +4 -2
  27. package/src/OutgoingMessages/AuthenticationMessage.ts +2 -1
  28. package/src/OutgoingMessages/AwarenessMessage.ts +1 -0
  29. package/src/OutgoingMessages/QueryAwarenessMessage.ts +5 -0
  30. package/src/OutgoingMessages/StatelessMessage.ts +1 -0
  31. package/src/OutgoingMessages/SyncStepOneMessage.ts +1 -0
  32. package/src/OutgoingMessages/SyncStepTwoMessage.ts +1 -1
  33. package/src/OutgoingMessages/UpdateMessage.ts +3 -1
  34. package/src/index.ts +1 -0
  35. package/src/types.ts +1 -0
  36. package/dist/tests/server/getDocumentName.d.ts +0 -1
  37. /package/dist/tests/{provider → providerwebsocket}/configuration.d.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  import * as Y from 'yjs';
2
- import { retry } from '@lifeomic/attempt';
3
2
  import { readAuthMessage, writeAuthentication, awarenessStatesToArray, WsReadyStates, Unauthorized, Forbidden } from '@hocuspocus/common';
3
+ import { retry } from '@lifeomic/attempt';
4
4
 
5
5
  /**
6
6
  * Utility module to work with key-value stores.
@@ -1324,19 +1324,6 @@ const createMutex = () => {
1324
1324
  }
1325
1325
  };
1326
1326
 
1327
- /**
1328
- * Utility module to work with urls.
1329
- *
1330
- * @module url
1331
- */
1332
-
1333
- /**
1334
- * @param {Object<string,string>} params
1335
- * @return {string}
1336
- */
1337
- const encodeQueryParams = params =>
1338
- map(params, (val, key) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&');
1339
-
1340
1327
  class EventEmitter {
1341
1328
  constructor() {
1342
1329
  this.callbacks = {};
@@ -1381,12 +1368,18 @@ class IncomingMessage {
1381
1368
  readVarUint() {
1382
1369
  return readVarUint(this.decoder);
1383
1370
  }
1371
+ readVarString() {
1372
+ return readVarString(this.decoder);
1373
+ }
1384
1374
  readVarUint8Array() {
1385
1375
  return readVarUint8Array(this.decoder);
1386
1376
  }
1387
1377
  writeVarUint(type) {
1388
1378
  return writeVarUint(this.encoder, type);
1389
1379
  }
1380
+ writeVarString(string) {
1381
+ return writeVarString(this.encoder, string);
1382
+ }
1390
1383
  writeVarUint8Array(data) {
1391
1384
  return writeVarUint8Array(this.encoder, data);
1392
1385
  }
@@ -1561,6 +1554,7 @@ class MessageReceiver {
1561
1554
  apply(provider, emitSynced = true) {
1562
1555
  const { message } = this;
1563
1556
  const type = message.readVarUint();
1557
+ const emptyMessageLength = message.length();
1564
1558
  switch (type) {
1565
1559
  case MessageType.Sync:
1566
1560
  this.applySyncMessage(provider, emitSynced);
@@ -1581,7 +1575,7 @@ class MessageReceiver {
1581
1575
  throw new Error(`Can’t apply message of unknown type: ${type}`);
1582
1576
  }
1583
1577
  // Reply
1584
- if (message.length() > 1) {
1578
+ if (message.length() > emptyMessageLength + 1) { // length of documentName (considered in emptyMessageLength plus length of yjs sync type, set in applySyncMessage)
1585
1579
  if (this.broadcasted) {
1586
1580
  // TODO: Some weird TypeScript error
1587
1581
  // @ts-ignore
@@ -1600,7 +1594,7 @@ class MessageReceiver {
1600
1594
  // Apply update
1601
1595
  const syncMessageType = readSyncMessage(message.decoder, message.encoder, provider.document, provider);
1602
1596
  // Synced once we receive Step2
1603
- if (emitSynced && (syncMessageType === messageYjsSyncStep2)) {
1597
+ if (emitSynced && syncMessageType === messageYjsSyncStep2) {
1604
1598
  provider.synced = true;
1605
1599
  }
1606
1600
  if (syncMessageType === messageYjsUpdate || syncMessageType === messageYjsSyncStep2) {
@@ -1650,6 +1644,7 @@ class SyncStepOneMessage extends OutgoingMessage {
1650
1644
  if (typeof args.document === 'undefined') {
1651
1645
  throw new Error('The sync step one message requires document as an argument');
1652
1646
  }
1647
+ writeVarString(this.encoder, args.documentName);
1653
1648
  writeVarUint(this.encoder, this.type);
1654
1649
  writeSyncStep1(this.encoder, args.document);
1655
1650
  return this.encoder;
@@ -1666,6 +1661,7 @@ class SyncStepTwoMessage extends OutgoingMessage {
1666
1661
  if (typeof args.document === 'undefined') {
1667
1662
  throw new Error('The sync step two message requires document as an argument');
1668
1663
  }
1664
+ writeVarString(this.encoder, args.documentName);
1669
1665
  writeVarUint(this.encoder, this.type);
1670
1666
  writeSyncStep2(this.encoder, args.document);
1671
1667
  return this.encoder;
@@ -1679,6 +1675,9 @@ class QueryAwarenessMessage extends OutgoingMessage {
1679
1675
  this.description = 'Queries awareness states';
1680
1676
  }
1681
1677
  get(args) {
1678
+ console.log('queryAwareness: writing string docName', args.documentName);
1679
+ console.log(this.encoder.cpos);
1680
+ writeVarString(this.encoder, args.documentName);
1682
1681
  writeVarUint(this.encoder, this.type);
1683
1682
  return this.encoder;
1684
1683
  }
@@ -1694,6 +1693,7 @@ class AuthenticationMessage extends OutgoingMessage {
1694
1693
  if (typeof args.token === 'undefined') {
1695
1694
  throw new Error('The authentication message requires `token` as an argument.');
1696
1695
  }
1696
+ writeVarString(this.encoder, args.documentName);
1697
1697
  writeVarUint(this.encoder, this.type);
1698
1698
  writeAuthentication(this.encoder, args.token);
1699
1699
  return this.encoder;
@@ -1713,6 +1713,7 @@ class AwarenessMessage extends OutgoingMessage {
1713
1713
  if (typeof args.clients === 'undefined') {
1714
1714
  throw new Error('The awareness message requires clients as an argument');
1715
1715
  }
1716
+ writeVarString(this.encoder, args.documentName);
1716
1717
  writeVarUint(this.encoder, this.type);
1717
1718
  let awarenessUpdate;
1718
1719
  if (args.states === undefined) {
@@ -1733,6 +1734,7 @@ class UpdateMessage extends OutgoingMessage {
1733
1734
  this.description = 'A document update';
1734
1735
  }
1735
1736
  get(args) {
1737
+ writeVarString(this.encoder, args.documentName);
1736
1738
  writeVarUint(this.encoder, this.type);
1737
1739
  writeUpdate(this.encoder, args.update);
1738
1740
  return this.encoder;
@@ -1747,6 +1749,7 @@ class StatelessMessage extends OutgoingMessage {
1747
1749
  }
1748
1750
  get(args) {
1749
1751
  var _a;
1752
+ writeVarString(this.encoder, args.documentName);
1750
1753
  writeVarUint(this.encoder, this.type);
1751
1754
  writeVarString(this.encoder, (_a = args.payload) !== null && _a !== void 0 ? _a : '');
1752
1755
  return this.encoder;
@@ -1758,13 +1761,302 @@ class HocuspocusProvider extends EventEmitter {
1758
1761
  super();
1759
1762
  this.configuration = {
1760
1763
  name: '',
1764
+ // @ts-ignore
1765
+ document: undefined,
1766
+ // @ts-ignore
1767
+ awareness: undefined,
1768
+ token: null,
1769
+ parameters: {},
1770
+ broadcast: true,
1771
+ forceSyncInterval: false,
1772
+ onAuthenticated: () => null,
1773
+ onAuthenticationFailed: () => null,
1774
+ onOpen: () => null,
1775
+ onConnect: () => null,
1776
+ onMessage: () => null,
1777
+ onOutgoingMessage: () => null,
1778
+ onStatus: () => null,
1779
+ onSynced: () => null,
1780
+ onDisconnect: () => null,
1781
+ onClose: () => null,
1782
+ onDestroy: () => null,
1783
+ onAwarenessUpdate: () => null,
1784
+ onAwarenessChange: () => null,
1785
+ onStateless: () => null,
1786
+ quiet: false,
1787
+ };
1788
+ this.subscribedToBroadcastChannel = false;
1789
+ this.isSynced = false;
1790
+ this.unsyncedChanges = 0;
1791
+ this.status = WebSocketStatus.Disconnected;
1792
+ this.isAuthenticated = false;
1793
+ this.mux = createMutex();
1794
+ this.intervals = {
1795
+ forceSync: null,
1796
+ };
1797
+ this.boundBeforeUnload = this.beforeUnload.bind(this);
1798
+ this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
1799
+ this.setConfiguration(configuration);
1800
+ this.configuration.document = configuration.document ? configuration.document : new Y.Doc();
1801
+ this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document);
1802
+ this.on('open', this.configuration.onOpen);
1803
+ this.on('message', this.configuration.onMessage);
1804
+ this.on('outgoingMessage', this.configuration.onOutgoingMessage);
1805
+ this.on('synced', this.configuration.onSynced);
1806
+ this.on('destroy', this.configuration.onDestroy);
1807
+ this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
1808
+ this.on('awarenessChange', this.configuration.onAwarenessChange);
1809
+ this.on('stateless', this.configuration.onStateless);
1810
+ this.on('authenticated', this.configuration.onAuthenticated);
1811
+ this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
1812
+ this.configuration.websocketProvider.on('connect', this.configuration.onConnect);
1813
+ this.configuration.websocketProvider.on('connect', (e) => this.emit('connect', e));
1814
+ this.configuration.websocketProvider.on('open', this.onOpen.bind(this));
1815
+ this.configuration.websocketProvider.on('open', (e) => this.emit('open', e));
1816
+ this.configuration.websocketProvider.on('message', this.onMessage.bind(this));
1817
+ this.configuration.websocketProvider.on('close', this.onClose.bind(this));
1818
+ this.configuration.websocketProvider.on('close', this.configuration.onClose);
1819
+ this.configuration.websocketProvider.on('close', (e) => this.emit('close', e));
1820
+ this.configuration.websocketProvider.on('status', this.onStatus.bind(this));
1821
+ this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect);
1822
+ this.configuration.websocketProvider.on('disconnect', (e) => this.emit('disconnect', e));
1823
+ this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy);
1824
+ this.configuration.websocketProvider.on('destroy', (e) => this.emit('destroy', e));
1825
+ this.awareness.on('update', () => {
1826
+ this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) });
1827
+ });
1828
+ this.awareness.on('change', () => {
1829
+ this.emit('awarenessChange', { states: awarenessStatesToArray(this.awareness.getStates()) });
1830
+ });
1831
+ this.document.on('update', this.documentUpdateHandler.bind(this));
1832
+ this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
1833
+ this.registerEventListeners();
1834
+ if (this.configuration.forceSyncInterval) {
1835
+ this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
1836
+ }
1837
+ this.configuration.websocketProvider.attach(this);
1838
+ }
1839
+ onStatus({ status }) {
1840
+ this.status = status;
1841
+ this.configuration.onStatus({ status });
1842
+ this.emit('status', { status });
1843
+ }
1844
+ setConfiguration(configuration = {}) {
1845
+ this.configuration = { ...this.configuration, ...configuration };
1846
+ }
1847
+ get document() {
1848
+ return this.configuration.document;
1849
+ }
1850
+ get awareness() {
1851
+ return this.configuration.awareness;
1852
+ }
1853
+ get hasUnsyncedChanges() {
1854
+ return this.unsyncedChanges > 0;
1855
+ }
1856
+ forceSync() {
1857
+ this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
1858
+ }
1859
+ beforeUnload() {
1860
+ removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload');
1861
+ }
1862
+ registerEventListeners() {
1863
+ if (typeof window === 'undefined') {
1864
+ return;
1865
+ }
1866
+ window.addEventListener('beforeunload', this.boundBeforeUnload);
1867
+ }
1868
+ sendStateless(payload) {
1869
+ this.send(StatelessMessage, { documentName: this.configuration.name, payload });
1870
+ }
1871
+ documentUpdateHandler(update, origin) {
1872
+ if (origin === this) {
1873
+ return;
1874
+ }
1875
+ this.unsyncedChanges += 1;
1876
+ this.send(UpdateMessage, { update, documentName: this.configuration.name }, true);
1877
+ }
1878
+ awarenessUpdateHandler({ added, updated, removed }, origin) {
1879
+ const changedClients = added.concat(updated).concat(removed);
1880
+ this.send(AwarenessMessage, {
1881
+ awareness: this.awareness,
1882
+ clients: changedClients,
1883
+ documentName: this.configuration.name,
1884
+ }, true);
1885
+ }
1886
+ get synced() {
1887
+ return this.isSynced;
1888
+ }
1889
+ set synced(state) {
1890
+ if (this.isSynced === state) {
1891
+ return;
1892
+ }
1893
+ this.isSynced = state;
1894
+ this.emit('synced', { state });
1895
+ this.emit('sync', { state });
1896
+ }
1897
+ receiveStateless(payload) {
1898
+ this.emit('stateless', { payload });
1899
+ }
1900
+ get isAuthenticationRequired() {
1901
+ return !!this.configuration.token && !this.isAuthenticated;
1902
+ }
1903
+ disconnect() {
1904
+ this.disconnectBroadcastChannel();
1905
+ this.configuration.websocketProvider.detach(this);
1906
+ }
1907
+ async onOpen(event) {
1908
+ this.emit('open', { event });
1909
+ if (this.isAuthenticationRequired) {
1910
+ this.send(AuthenticationMessage, {
1911
+ token: await this.getToken(),
1912
+ documentName: this.configuration.name,
1913
+ });
1914
+ return;
1915
+ }
1916
+ this.startSync();
1917
+ }
1918
+ async getToken() {
1919
+ if (typeof this.configuration.token === 'function') {
1920
+ const token = await this.configuration.token();
1921
+ return token;
1922
+ }
1923
+ return this.configuration.token;
1924
+ }
1925
+ startSync() {
1926
+ this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
1927
+ if (this.awareness.getLocalState() !== null) {
1928
+ this.send(AwarenessMessage, {
1929
+ awareness: this.awareness,
1930
+ clients: [this.document.clientID],
1931
+ documentName: this.configuration.name,
1932
+ });
1933
+ }
1934
+ }
1935
+ send(message, args, broadcast = false) {
1936
+ if (broadcast) {
1937
+ this.mux(() => { this.broadcast(message, args); });
1938
+ }
1939
+ const messageSender = new MessageSender(message, args);
1940
+ this.emit('outgoingMessage', { message: messageSender.message });
1941
+ messageSender.send(this.configuration.websocketProvider);
1942
+ }
1943
+ onMessage(event) {
1944
+ const message = new IncomingMessage(event.data);
1945
+ const documentName = message.readVarString();
1946
+ if (documentName !== this.configuration.name) {
1947
+ return; // message is meant for another provider
1948
+ }
1949
+ message.writeVarString(documentName);
1950
+ this.emit('message', { event, message: new IncomingMessage(event.data) });
1951
+ new MessageReceiver(message).apply(this);
1952
+ }
1953
+ onClose(event) {
1954
+ this.isAuthenticated = false;
1955
+ this.synced = false;
1956
+ // update awareness (all users except local left)
1957
+ removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID), this);
1958
+ }
1959
+ destroy() {
1960
+ this.emit('destroy');
1961
+ if (this.intervals.forceSync) {
1962
+ clearInterval(this.intervals.forceSync);
1963
+ }
1964
+ removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
1965
+ this.disconnect();
1966
+ this.awareness.off('update', this.awarenessUpdateHandler);
1967
+ this.document.off('update', this.documentUpdateHandler);
1968
+ this.removeAllListeners();
1969
+ if (typeof window === 'undefined') {
1970
+ return;
1971
+ }
1972
+ window.removeEventListener('beforeunload', this.boundBeforeUnload);
1973
+ }
1974
+ permissionDeniedHandler(reason) {
1975
+ this.emit('authenticationFailed', { reason });
1976
+ this.isAuthenticated = false;
1977
+ this.disconnect();
1978
+ this.status = WebSocketStatus.Disconnected;
1979
+ }
1980
+ authenticatedHandler() {
1981
+ this.isAuthenticated = true;
1982
+ this.emit('authenticated');
1983
+ this.startSync();
1984
+ }
1985
+ get broadcastChannel() {
1986
+ return `${this.configuration.name}`;
1987
+ }
1988
+ broadcastChannelSubscriber(data) {
1989
+ this.mux(() => {
1990
+ const message = new IncomingMessage(data);
1991
+ const documentName = message.readVarString();
1992
+ message.writeVarString(documentName);
1993
+ new MessageReceiver(message)
1994
+ .setBroadcasted(true)
1995
+ .apply(this, false);
1996
+ });
1997
+ }
1998
+ subscribeToBroadcastChannel() {
1999
+ if (!this.subscribedToBroadcastChannel) {
2000
+ subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2001
+ this.subscribedToBroadcastChannel = true;
2002
+ }
2003
+ this.mux(() => {
2004
+ this.broadcast(SyncStepOneMessage, { document: this.document });
2005
+ this.broadcast(SyncStepTwoMessage, { document: this.document });
2006
+ this.broadcast(QueryAwarenessMessage, { document: this.document });
2007
+ this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID], document: this.document });
2008
+ });
2009
+ }
2010
+ disconnectBroadcastChannel() {
2011
+ // broadcast message with local awareness state set to null (indicating disconnect)
2012
+ this.send(AwarenessMessage, {
2013
+ awareness: this.awareness,
2014
+ clients: [this.document.clientID],
2015
+ states: new Map(),
2016
+ documentName: this.configuration.name,
2017
+ }, true);
2018
+ if (this.subscribedToBroadcastChannel) {
2019
+ unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2020
+ this.subscribedToBroadcastChannel = false;
2021
+ }
2022
+ }
2023
+ broadcast(Message, args) {
2024
+ if (!this.configuration.broadcast) {
2025
+ return;
2026
+ }
2027
+ if (!this.subscribedToBroadcastChannel) {
2028
+ return;
2029
+ }
2030
+ new MessageSender(Message, args).broadcast(this.broadcastChannel);
2031
+ }
2032
+ setAwarenessField(key, value) {
2033
+ this.awareness.setLocalStateField(key, value);
2034
+ }
2035
+ }
2036
+
2037
+ /**
2038
+ * Utility module to work with urls.
2039
+ *
2040
+ * @module url
2041
+ */
2042
+
2043
+ /**
2044
+ * @param {Object<string,string>} params
2045
+ * @return {string}
2046
+ */
2047
+ const encodeQueryParams = params =>
2048
+ map(params, (val, key) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&');
2049
+
2050
+ class HocuspocusProviderWebsocket extends EventEmitter {
2051
+ constructor(configuration) {
2052
+ super();
2053
+ this.configuration = {
1761
2054
  url: '',
1762
2055
  // @ts-ignore
1763
2056
  document: undefined,
1764
2057
  // @ts-ignore
1765
2058
  awareness: undefined,
1766
2059
  WebSocketPolyfill: undefined,
1767
- token: null,
1768
2060
  parameters: {},
1769
2061
  connect: true,
1770
2062
  broadcast: true,
@@ -1787,29 +2079,22 @@ class HocuspocusProvider extends EventEmitter {
1787
2079
  jitter: true,
1788
2080
  // retry forever
1789
2081
  timeout: 0,
1790
- onAuthenticated: () => null,
1791
- onAuthenticationFailed: () => null,
1792
2082
  onOpen: () => null,
1793
2083
  onConnect: () => null,
1794
2084
  onMessage: () => null,
1795
2085
  onOutgoingMessage: () => null,
1796
2086
  onStatus: () => null,
1797
- onSynced: () => null,
1798
2087
  onDisconnect: () => null,
1799
2088
  onClose: () => null,
1800
2089
  onDestroy: () => null,
1801
2090
  onAwarenessUpdate: () => null,
1802
2091
  onAwarenessChange: () => null,
1803
- onStateless: () => null,
1804
2092
  quiet: false,
1805
2093
  };
1806
2094
  this.subscribedToBroadcastChannel = false;
1807
2095
  this.webSocket = null;
1808
2096
  this.shouldConnect = true;
1809
2097
  this.status = WebSocketStatus.Disconnected;
1810
- this.isSynced = false;
1811
- this.unsyncedChanges = 0;
1812
- this.isAuthenticated = false;
1813
2098
  this.lastMessageReceived = 0;
1814
2099
  this.mux = createMutex();
1815
2100
  this.intervals = {
@@ -1817,40 +2102,27 @@ class HocuspocusProvider extends EventEmitter {
1817
2102
  connectionChecker: null,
1818
2103
  };
1819
2104
  this.connectionAttempt = null;
2105
+ this.receivedOnOpenPayload = undefined;
2106
+ this.receivedOnStatusPayload = undefined;
1820
2107
  this.boundConnect = this.connect.bind(this);
1821
- this.boundBeforeUnload = this.beforeUnload.bind(this);
1822
- this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
1823
2108
  this.setConfiguration(configuration);
1824
- this.configuration.document = configuration.document ? configuration.document : new Y.Doc();
1825
- this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document);
1826
2109
  this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket;
1827
2110
  this.on('open', this.configuration.onOpen);
1828
- this.on('authenticated', this.configuration.onAuthenticated);
1829
- this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
2111
+ this.on('open', this.onOpen.bind(this));
1830
2112
  this.on('connect', this.configuration.onConnect);
1831
2113
  this.on('message', this.configuration.onMessage);
1832
2114
  this.on('outgoingMessage', this.configuration.onOutgoingMessage);
1833
- this.on('synced', this.configuration.onSynced);
1834
2115
  this.on('status', this.configuration.onStatus);
2116
+ this.on('status', this.onStatus.bind(this));
1835
2117
  this.on('disconnect', this.configuration.onDisconnect);
1836
2118
  this.on('close', this.configuration.onClose);
1837
2119
  this.on('destroy', this.configuration.onDestroy);
1838
2120
  this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
1839
2121
  this.on('awarenessChange', this.configuration.onAwarenessChange);
1840
- this.on('stateless', this.configuration.onStateless);
1841
- this.awareness.on('update', () => {
1842
- this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) });
1843
- });
1844
- this.awareness.on('change', () => {
1845
- this.emit('awarenessChange', { states: awarenessStatesToArray(this.awareness.getStates()) });
1846
- });
1847
- this.document.on('update', this.documentUpdateHandler.bind(this));
1848
- this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
2122
+ this.on('close', this.onClose.bind(this));
2123
+ this.on('message', this.onMessage.bind(this));
1849
2124
  this.registerEventListeners();
1850
2125
  this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.configuration.messageReconnectTimeout / 10);
1851
- if (this.configuration.forceSyncInterval) {
1852
- this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
1853
- }
1854
2126
  if (typeof configuration.connect !== 'undefined') {
1855
2127
  this.shouldConnect = configuration.connect;
1856
2128
  }
@@ -1859,6 +2131,23 @@ class HocuspocusProvider extends EventEmitter {
1859
2131
  }
1860
2132
  this.connect();
1861
2133
  }
2134
+ async onOpen(event) {
2135
+ this.receivedOnOpenPayload = event;
2136
+ }
2137
+ async onStatus(data) {
2138
+ this.receivedOnStatusPayload = data;
2139
+ }
2140
+ attach(provider) {
2141
+ if (this.receivedOnOpenPayload) {
2142
+ provider.onOpen(this.receivedOnOpenPayload);
2143
+ }
2144
+ if (this.receivedOnStatusPayload) {
2145
+ provider.onStatus(this.receivedOnStatusPayload);
2146
+ }
2147
+ }
2148
+ detach(provider) {
2149
+ // tell the server to remove the listener
2150
+ }
1862
2151
  setConfiguration(configuration = {}) {
1863
2152
  this.configuration = { ...this.configuration, ...configuration };
1864
2153
  }
@@ -1871,9 +2160,7 @@ class HocuspocusProvider extends EventEmitter {
1871
2160
  this.cancelWebsocketRetry();
1872
2161
  this.cancelWebsocketRetry = undefined;
1873
2162
  }
1874
- this.unsyncedChanges = 0; // set to 0 in case we got reconnected
1875
2163
  this.shouldConnect = true;
1876
- this.subscribeToBroadcastChannel();
1877
2164
  const abortableRetry = () => {
1878
2165
  let cancelAttempt = false;
1879
2166
  const retryPromise = retry(this.createWebSocketConnection.bind(this), {
@@ -1917,15 +2204,14 @@ class HocuspocusProvider extends EventEmitter {
1917
2204
  // Init the WebSocket connection
1918
2205
  const ws = new this.configuration.WebSocketPolyfill(this.url);
1919
2206
  ws.binaryType = 'arraybuffer';
1920
- ws.onmessage = this.onMessage.bind(this);
1921
- ws.onclose = this.onClose.bind(this);
1922
- ws.onopen = this.onOpen.bind(this);
2207
+ ws.onmessage = (payload) => this.emit('message', payload);
2208
+ ws.onclose = (payload) => this.emit('close', { event: payload });
2209
+ ws.onopen = (payload) => this.emit('open', payload);
1923
2210
  ws.onerror = (err) => {
1924
2211
  reject(err);
1925
2212
  };
1926
2213
  this.webSocket = ws;
1927
2214
  // Reset the status
1928
- this.synced = false;
1929
2215
  this.status = WebSocketStatus.Connecting;
1930
2216
  this.emit('status', { status: WebSocketStatus.Connecting });
1931
2217
  // Store resolve/reject for later use
@@ -1935,13 +2221,17 @@ class HocuspocusProvider extends EventEmitter {
1935
2221
  };
1936
2222
  });
1937
2223
  }
2224
+ onMessage(event) {
2225
+ this.resolveConnectionAttempt();
2226
+ }
1938
2227
  resolveConnectionAttempt() {
1939
- var _a;
1940
- (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.resolve();
1941
- this.connectionAttempt = null;
1942
- this.status = WebSocketStatus.Connected;
1943
- this.emit('status', { status: WebSocketStatus.Connected });
1944
- this.emit('connect');
2228
+ if (this.connectionAttempt) {
2229
+ this.connectionAttempt.resolve();
2230
+ this.connectionAttempt = null;
2231
+ this.status = WebSocketStatus.Connected;
2232
+ this.emit('status', { status: WebSocketStatus.Connected });
2233
+ this.emit('connect');
2234
+ }
1945
2235
  }
1946
2236
  stopConnectionAttempt() {
1947
2237
  this.connectionAttempt = null;
@@ -1951,15 +2241,6 @@ class HocuspocusProvider extends EventEmitter {
1951
2241
  (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
1952
2242
  this.connectionAttempt = null;
1953
2243
  }
1954
- get document() {
1955
- return this.configuration.document;
1956
- }
1957
- get awareness() {
1958
- return this.configuration.awareness;
1959
- }
1960
- get hasUnsyncedChanges() {
1961
- return this.unsyncedChanges > 0;
1962
- }
1963
2244
  checkConnection() {
1964
2245
  var _a;
1965
2246
  // Don’t check the connection when it’s not even established
@@ -1978,48 +2259,11 @@ class HocuspocusProvider extends EventEmitter {
1978
2259
  // Awareness updates, which are updated every 15 seconds.
1979
2260
  (_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.close();
1980
2261
  }
1981
- forceSync() {
1982
- if (!this.webSocket) {
1983
- return;
1984
- }
1985
- this.send(SyncStepOneMessage, { document: this.document });
1986
- }
1987
- beforeUnload() {
1988
- removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload');
1989
- }
1990
2262
  registerEventListeners() {
1991
2263
  if (typeof window === 'undefined') {
1992
2264
  return;
1993
2265
  }
1994
2266
  window.addEventListener('online', this.boundConnect);
1995
- window.addEventListener('beforeunload', this.boundBeforeUnload);
1996
- }
1997
- sendStateless(payload) {
1998
- this.send(StatelessMessage, { payload });
1999
- }
2000
- documentUpdateHandler(update, origin) {
2001
- if (origin === this) {
2002
- return;
2003
- }
2004
- this.unsyncedChanges += 1;
2005
- this.send(UpdateMessage, { update }, true);
2006
- }
2007
- awarenessUpdateHandler({ added, updated, removed }, origin) {
2008
- const changedClients = added.concat(updated).concat(removed);
2009
- this.send(AwarenessMessage, {
2010
- awareness: this.awareness,
2011
- clients: changedClients,
2012
- }, true);
2013
- }
2014
- permissionDeniedHandler(reason) {
2015
- this.emit('authenticationFailed', { reason });
2016
- this.isAuthenticated = false;
2017
- this.shouldConnect = false;
2018
- }
2019
- authenticatedHandler() {
2020
- this.isAuthenticated = true;
2021
- this.emit('authenticated');
2022
- this.startSync();
2023
2267
  }
2024
2268
  // Ensure that the URL always ends with /
2025
2269
  get serverUrl() {
@@ -2030,28 +2274,10 @@ class HocuspocusProvider extends EventEmitter {
2030
2274
  }
2031
2275
  get url() {
2032
2276
  const encodedParams = encodeQueryParams(this.configuration.parameters);
2033
- return `${this.serverUrl}/${this.configuration.name}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
2034
- }
2035
- get synced() {
2036
- return this.isSynced;
2037
- }
2038
- set synced(state) {
2039
- if (this.isSynced === state) {
2040
- return;
2041
- }
2042
- this.isSynced = state;
2043
- this.emit('synced', { state });
2044
- this.emit('sync', { state });
2045
- }
2046
- receiveStateless(payload) {
2047
- this.emit('stateless', { payload });
2048
- }
2049
- get isAuthenticationRequired() {
2050
- return !!this.configuration.token && !this.isAuthenticated;
2277
+ return `${this.serverUrl}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
2051
2278
  }
2052
2279
  disconnect() {
2053
2280
  this.shouldConnect = false;
2054
- this.disconnectBroadcastChannel();
2055
2281
  if (this.webSocket === null) {
2056
2282
  return;
2057
2283
  }
@@ -2062,58 +2288,15 @@ class HocuspocusProvider extends EventEmitter {
2062
2288
  //
2063
2289
  }
2064
2290
  }
2065
- async onOpen(event) {
2066
- this.emit('open', { event });
2067
- if (this.isAuthenticationRequired) {
2068
- this.send(AuthenticationMessage, {
2069
- token: await this.getToken(),
2070
- });
2071
- return;
2072
- }
2073
- this.startSync();
2074
- }
2075
- async getToken() {
2076
- if (typeof this.configuration.token === 'function') {
2077
- const token = await this.configuration.token();
2078
- return token;
2079
- }
2080
- return this.configuration.token;
2081
- }
2082
- startSync() {
2083
- this.send(SyncStepOneMessage, { document: this.document });
2084
- if (this.awareness.getLocalState() !== null) {
2085
- this.send(AwarenessMessage, {
2086
- awareness: this.awareness,
2087
- clients: [this.document.clientID],
2088
- });
2089
- }
2090
- }
2091
- send(Message, args, broadcast = false) {
2291
+ send(message) {
2092
2292
  var _a;
2093
- if (broadcast) {
2094
- this.mux(() => { this.broadcast(Message, args); });
2095
- }
2096
2293
  if (((_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.readyState) === WsReadyStates.Open) {
2097
- const messageSender = new MessageSender(Message, args);
2098
- this.emit('outgoingMessage', { message: messageSender.message });
2099
- messageSender.send(this.webSocket);
2294
+ this.webSocket.send(message);
2100
2295
  }
2101
2296
  }
2102
- onMessage(event) {
2103
- this.resolveConnectionAttempt();
2104
- this.lastMessageReceived = getUnixTime();
2105
- const message = new IncomingMessage(event.data);
2106
- this.emit('message', { event, message });
2107
- new MessageReceiver(message).apply(this);
2108
- }
2109
- onClose(event) {
2110
- this.emit('close', { event });
2297
+ onClose({ event }) {
2111
2298
  this.webSocket = null;
2112
- this.isAuthenticated = false;
2113
- this.synced = false;
2114
2299
  if (this.status === WebSocketStatus.Connected) {
2115
- // update awareness (all users except local left)
2116
- removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID), this);
2117
2300
  this.status = WebSocketStatus.Disconnected;
2118
2301
  this.emit('status', { status: WebSocketStatus.Disconnected });
2119
2302
  this.emit('disconnect', { event });
@@ -2127,6 +2310,7 @@ class HocuspocusProvider extends EventEmitter {
2127
2310
  if (event.code === Forbidden.code) {
2128
2311
  if (!this.configuration.quiet) {
2129
2312
  console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.');
2313
+ return; // TODO REMOVE ME
2130
2314
  }
2131
2315
  }
2132
2316
  if (this.connectionAttempt) {
@@ -2156,67 +2340,16 @@ class HocuspocusProvider extends EventEmitter {
2156
2340
  clearInterval(this.intervals.forceSync);
2157
2341
  }
2158
2342
  clearInterval(this.intervals.connectionChecker);
2159
- removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
2160
2343
  // If there is still a connection attempt outstanding then we should stop
2161
2344
  // it before calling disconnect, otherwise it will be rejected in the onClose
2162
2345
  // handler and trigger a retry
2163
2346
  this.stopConnectionAttempt();
2164
2347
  this.disconnect();
2165
- this.awareness.off('update', this.awarenessUpdateHandler);
2166
- this.document.off('update', this.documentUpdateHandler);
2167
2348
  this.removeAllListeners();
2168
2349
  if (typeof window === 'undefined') {
2169
2350
  return;
2170
2351
  }
2171
2352
  window.removeEventListener('online', this.boundConnect);
2172
- window.removeEventListener('beforeunload', this.boundBeforeUnload);
2173
- }
2174
- get broadcastChannel() {
2175
- return `${this.serverUrl}/${this.configuration.name}`;
2176
- }
2177
- broadcastChannelSubscriber(data) {
2178
- this.mux(() => {
2179
- const message = new IncomingMessage(data);
2180
- new MessageReceiver(message)
2181
- .setBroadcasted(true)
2182
- .apply(this, false);
2183
- });
2184
- }
2185
- subscribeToBroadcastChannel() {
2186
- if (!this.subscribedToBroadcastChannel) {
2187
- subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2188
- this.subscribedToBroadcastChannel = true;
2189
- }
2190
- this.mux(() => {
2191
- this.broadcast(SyncStepOneMessage, { document: this.document });
2192
- this.broadcast(SyncStepTwoMessage, { document: this.document });
2193
- this.broadcast(QueryAwarenessMessage);
2194
- this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID] });
2195
- });
2196
- }
2197
- disconnectBroadcastChannel() {
2198
- // broadcast message with local awareness state set to null (indicating disconnect)
2199
- this.send(AwarenessMessage, {
2200
- awareness: this.awareness,
2201
- clients: [this.document.clientID],
2202
- states: new Map(),
2203
- }, true);
2204
- if (this.subscribedToBroadcastChannel) {
2205
- unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2206
- this.subscribedToBroadcastChannel = false;
2207
- }
2208
- }
2209
- broadcast(Message, args) {
2210
- if (!this.configuration.broadcast) {
2211
- return;
2212
- }
2213
- if (!this.subscribedToBroadcastChannel) {
2214
- return;
2215
- }
2216
- new MessageSender(Message, args).broadcast(this.broadcastChannel);
2217
- }
2218
- setAwarenessField(key, value) {
2219
- this.awareness.setLocalStateField(key, value);
2220
2353
  }
2221
2354
  }
2222
2355
 
@@ -2231,9 +2364,10 @@ class HocuspocusCloudProvider extends HocuspocusProvider {
2231
2364
  }
2232
2365
  configuration.parameters.key = configuration.key;
2233
2366
  }
2367
+ configuration.websocketProvider = new HocuspocusProviderWebsocket({ url: configuration.url });
2234
2368
  super(configuration);
2235
2369
  }
2236
2370
  }
2237
2371
 
2238
- export { HocuspocusCloudProvider, HocuspocusProvider, MessageType, WebSocketStatus };
2372
+ export { HocuspocusCloudProvider, HocuspocusProvider, HocuspocusProviderWebsocket, MessageType, WebSocketStatus };
2239
2373
  //# sourceMappingURL=hocuspocus-provider.esm.js.map