@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
@@ -3,8 +3,8 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var Y = require('yjs');
6
- var attempt = require('@lifeomic/attempt');
7
6
  var common = require('@hocuspocus/common');
7
+ var attempt = require('@lifeomic/attempt');
8
8
 
9
9
  function _interopNamespace(e) {
10
10
  if (e && e.__esModule) return e;
@@ -1348,19 +1348,6 @@ const createMutex = () => {
1348
1348
  }
1349
1349
  };
1350
1350
 
1351
- /**
1352
- * Utility module to work with urls.
1353
- *
1354
- * @module url
1355
- */
1356
-
1357
- /**
1358
- * @param {Object<string,string>} params
1359
- * @return {string}
1360
- */
1361
- const encodeQueryParams = params =>
1362
- map(params, (val, key) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&');
1363
-
1364
1351
  class EventEmitter {
1365
1352
  constructor() {
1366
1353
  this.callbacks = {};
@@ -1405,12 +1392,18 @@ class IncomingMessage {
1405
1392
  readVarUint() {
1406
1393
  return readVarUint(this.decoder);
1407
1394
  }
1395
+ readVarString() {
1396
+ return readVarString(this.decoder);
1397
+ }
1408
1398
  readVarUint8Array() {
1409
1399
  return readVarUint8Array(this.decoder);
1410
1400
  }
1411
1401
  writeVarUint(type) {
1412
1402
  return writeVarUint(this.encoder, type);
1413
1403
  }
1404
+ writeVarString(string) {
1405
+ return writeVarString(this.encoder, string);
1406
+ }
1414
1407
  writeVarUint8Array(data) {
1415
1408
  return writeVarUint8Array(this.encoder, data);
1416
1409
  }
@@ -1585,6 +1578,7 @@ class MessageReceiver {
1585
1578
  apply(provider, emitSynced = true) {
1586
1579
  const { message } = this;
1587
1580
  const type = message.readVarUint();
1581
+ const emptyMessageLength = message.length();
1588
1582
  switch (type) {
1589
1583
  case exports.MessageType.Sync:
1590
1584
  this.applySyncMessage(provider, emitSynced);
@@ -1605,7 +1599,7 @@ class MessageReceiver {
1605
1599
  throw new Error(`Can’t apply message of unknown type: ${type}`);
1606
1600
  }
1607
1601
  // Reply
1608
- if (message.length() > 1) {
1602
+ if (message.length() > emptyMessageLength + 1) { // length of documentName (considered in emptyMessageLength plus length of yjs sync type, set in applySyncMessage)
1609
1603
  if (this.broadcasted) {
1610
1604
  // TODO: Some weird TypeScript error
1611
1605
  // @ts-ignore
@@ -1624,7 +1618,7 @@ class MessageReceiver {
1624
1618
  // Apply update
1625
1619
  const syncMessageType = readSyncMessage(message.decoder, message.encoder, provider.document, provider);
1626
1620
  // Synced once we receive Step2
1627
- if (emitSynced && (syncMessageType === messageYjsSyncStep2)) {
1621
+ if (emitSynced && syncMessageType === messageYjsSyncStep2) {
1628
1622
  provider.synced = true;
1629
1623
  }
1630
1624
  if (syncMessageType === messageYjsUpdate || syncMessageType === messageYjsSyncStep2) {
@@ -1674,6 +1668,7 @@ class SyncStepOneMessage extends OutgoingMessage {
1674
1668
  if (typeof args.document === 'undefined') {
1675
1669
  throw new Error('The sync step one message requires document as an argument');
1676
1670
  }
1671
+ writeVarString(this.encoder, args.documentName);
1677
1672
  writeVarUint(this.encoder, this.type);
1678
1673
  writeSyncStep1(this.encoder, args.document);
1679
1674
  return this.encoder;
@@ -1690,6 +1685,7 @@ class SyncStepTwoMessage extends OutgoingMessage {
1690
1685
  if (typeof args.document === 'undefined') {
1691
1686
  throw new Error('The sync step two message requires document as an argument');
1692
1687
  }
1688
+ writeVarString(this.encoder, args.documentName);
1693
1689
  writeVarUint(this.encoder, this.type);
1694
1690
  writeSyncStep2(this.encoder, args.document);
1695
1691
  return this.encoder;
@@ -1703,6 +1699,9 @@ class QueryAwarenessMessage extends OutgoingMessage {
1703
1699
  this.description = 'Queries awareness states';
1704
1700
  }
1705
1701
  get(args) {
1702
+ console.log('queryAwareness: writing string docName', args.documentName);
1703
+ console.log(this.encoder.cpos);
1704
+ writeVarString(this.encoder, args.documentName);
1706
1705
  writeVarUint(this.encoder, this.type);
1707
1706
  return this.encoder;
1708
1707
  }
@@ -1718,6 +1717,7 @@ class AuthenticationMessage extends OutgoingMessage {
1718
1717
  if (typeof args.token === 'undefined') {
1719
1718
  throw new Error('The authentication message requires `token` as an argument.');
1720
1719
  }
1720
+ writeVarString(this.encoder, args.documentName);
1721
1721
  writeVarUint(this.encoder, this.type);
1722
1722
  common.writeAuthentication(this.encoder, args.token);
1723
1723
  return this.encoder;
@@ -1737,6 +1737,7 @@ class AwarenessMessage extends OutgoingMessage {
1737
1737
  if (typeof args.clients === 'undefined') {
1738
1738
  throw new Error('The awareness message requires clients as an argument');
1739
1739
  }
1740
+ writeVarString(this.encoder, args.documentName);
1740
1741
  writeVarUint(this.encoder, this.type);
1741
1742
  let awarenessUpdate;
1742
1743
  if (args.states === undefined) {
@@ -1757,6 +1758,7 @@ class UpdateMessage extends OutgoingMessage {
1757
1758
  this.description = 'A document update';
1758
1759
  }
1759
1760
  get(args) {
1761
+ writeVarString(this.encoder, args.documentName);
1760
1762
  writeVarUint(this.encoder, this.type);
1761
1763
  writeUpdate(this.encoder, args.update);
1762
1764
  return this.encoder;
@@ -1771,6 +1773,7 @@ class StatelessMessage extends OutgoingMessage {
1771
1773
  }
1772
1774
  get(args) {
1773
1775
  var _a;
1776
+ writeVarString(this.encoder, args.documentName);
1774
1777
  writeVarUint(this.encoder, this.type);
1775
1778
  writeVarString(this.encoder, (_a = args.payload) !== null && _a !== void 0 ? _a : '');
1776
1779
  return this.encoder;
@@ -1782,13 +1785,302 @@ class HocuspocusProvider extends EventEmitter {
1782
1785
  super();
1783
1786
  this.configuration = {
1784
1787
  name: '',
1788
+ // @ts-ignore
1789
+ document: undefined,
1790
+ // @ts-ignore
1791
+ awareness: undefined,
1792
+ token: null,
1793
+ parameters: {},
1794
+ broadcast: true,
1795
+ forceSyncInterval: false,
1796
+ onAuthenticated: () => null,
1797
+ onAuthenticationFailed: () => null,
1798
+ onOpen: () => null,
1799
+ onConnect: () => null,
1800
+ onMessage: () => null,
1801
+ onOutgoingMessage: () => null,
1802
+ onStatus: () => null,
1803
+ onSynced: () => null,
1804
+ onDisconnect: () => null,
1805
+ onClose: () => null,
1806
+ onDestroy: () => null,
1807
+ onAwarenessUpdate: () => null,
1808
+ onAwarenessChange: () => null,
1809
+ onStateless: () => null,
1810
+ quiet: false,
1811
+ };
1812
+ this.subscribedToBroadcastChannel = false;
1813
+ this.isSynced = false;
1814
+ this.unsyncedChanges = 0;
1815
+ this.status = exports.WebSocketStatus.Disconnected;
1816
+ this.isAuthenticated = false;
1817
+ this.mux = createMutex();
1818
+ this.intervals = {
1819
+ forceSync: null,
1820
+ };
1821
+ this.boundBeforeUnload = this.beforeUnload.bind(this);
1822
+ this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
1823
+ this.setConfiguration(configuration);
1824
+ this.configuration.document = configuration.document ? configuration.document : new Y__namespace.Doc();
1825
+ this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document);
1826
+ this.on('open', this.configuration.onOpen);
1827
+ this.on('message', this.configuration.onMessage);
1828
+ this.on('outgoingMessage', this.configuration.onOutgoingMessage);
1829
+ this.on('synced', this.configuration.onSynced);
1830
+ this.on('destroy', this.configuration.onDestroy);
1831
+ this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
1832
+ this.on('awarenessChange', this.configuration.onAwarenessChange);
1833
+ this.on('stateless', this.configuration.onStateless);
1834
+ this.on('authenticated', this.configuration.onAuthenticated);
1835
+ this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
1836
+ this.configuration.websocketProvider.on('connect', this.configuration.onConnect);
1837
+ this.configuration.websocketProvider.on('connect', (e) => this.emit('connect', e));
1838
+ this.configuration.websocketProvider.on('open', this.onOpen.bind(this));
1839
+ this.configuration.websocketProvider.on('open', (e) => this.emit('open', e));
1840
+ this.configuration.websocketProvider.on('message', this.onMessage.bind(this));
1841
+ this.configuration.websocketProvider.on('close', this.onClose.bind(this));
1842
+ this.configuration.websocketProvider.on('close', this.configuration.onClose);
1843
+ this.configuration.websocketProvider.on('close', (e) => this.emit('close', e));
1844
+ this.configuration.websocketProvider.on('status', this.onStatus.bind(this));
1845
+ this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect);
1846
+ this.configuration.websocketProvider.on('disconnect', (e) => this.emit('disconnect', e));
1847
+ this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy);
1848
+ this.configuration.websocketProvider.on('destroy', (e) => this.emit('destroy', e));
1849
+ this.awareness.on('update', () => {
1850
+ this.emit('awarenessUpdate', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
1851
+ });
1852
+ this.awareness.on('change', () => {
1853
+ this.emit('awarenessChange', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
1854
+ });
1855
+ this.document.on('update', this.documentUpdateHandler.bind(this));
1856
+ this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
1857
+ this.registerEventListeners();
1858
+ if (this.configuration.forceSyncInterval) {
1859
+ this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
1860
+ }
1861
+ this.configuration.websocketProvider.attach(this);
1862
+ }
1863
+ onStatus({ status }) {
1864
+ this.status = status;
1865
+ this.configuration.onStatus({ status });
1866
+ this.emit('status', { status });
1867
+ }
1868
+ setConfiguration(configuration = {}) {
1869
+ this.configuration = { ...this.configuration, ...configuration };
1870
+ }
1871
+ get document() {
1872
+ return this.configuration.document;
1873
+ }
1874
+ get awareness() {
1875
+ return this.configuration.awareness;
1876
+ }
1877
+ get hasUnsyncedChanges() {
1878
+ return this.unsyncedChanges > 0;
1879
+ }
1880
+ forceSync() {
1881
+ this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
1882
+ }
1883
+ beforeUnload() {
1884
+ removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload');
1885
+ }
1886
+ registerEventListeners() {
1887
+ if (typeof window === 'undefined') {
1888
+ return;
1889
+ }
1890
+ window.addEventListener('beforeunload', this.boundBeforeUnload);
1891
+ }
1892
+ sendStateless(payload) {
1893
+ this.send(StatelessMessage, { documentName: this.configuration.name, payload });
1894
+ }
1895
+ documentUpdateHandler(update, origin) {
1896
+ if (origin === this) {
1897
+ return;
1898
+ }
1899
+ this.unsyncedChanges += 1;
1900
+ this.send(UpdateMessage, { update, documentName: this.configuration.name }, true);
1901
+ }
1902
+ awarenessUpdateHandler({ added, updated, removed }, origin) {
1903
+ const changedClients = added.concat(updated).concat(removed);
1904
+ this.send(AwarenessMessage, {
1905
+ awareness: this.awareness,
1906
+ clients: changedClients,
1907
+ documentName: this.configuration.name,
1908
+ }, true);
1909
+ }
1910
+ get synced() {
1911
+ return this.isSynced;
1912
+ }
1913
+ set synced(state) {
1914
+ if (this.isSynced === state) {
1915
+ return;
1916
+ }
1917
+ this.isSynced = state;
1918
+ this.emit('synced', { state });
1919
+ this.emit('sync', { state });
1920
+ }
1921
+ receiveStateless(payload) {
1922
+ this.emit('stateless', { payload });
1923
+ }
1924
+ get isAuthenticationRequired() {
1925
+ return !!this.configuration.token && !this.isAuthenticated;
1926
+ }
1927
+ disconnect() {
1928
+ this.disconnectBroadcastChannel();
1929
+ this.configuration.websocketProvider.detach(this);
1930
+ }
1931
+ async onOpen(event) {
1932
+ this.emit('open', { event });
1933
+ if (this.isAuthenticationRequired) {
1934
+ this.send(AuthenticationMessage, {
1935
+ token: await this.getToken(),
1936
+ documentName: this.configuration.name,
1937
+ });
1938
+ return;
1939
+ }
1940
+ this.startSync();
1941
+ }
1942
+ async getToken() {
1943
+ if (typeof this.configuration.token === 'function') {
1944
+ const token = await this.configuration.token();
1945
+ return token;
1946
+ }
1947
+ return this.configuration.token;
1948
+ }
1949
+ startSync() {
1950
+ this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
1951
+ if (this.awareness.getLocalState() !== null) {
1952
+ this.send(AwarenessMessage, {
1953
+ awareness: this.awareness,
1954
+ clients: [this.document.clientID],
1955
+ documentName: this.configuration.name,
1956
+ });
1957
+ }
1958
+ }
1959
+ send(message, args, broadcast = false) {
1960
+ if (broadcast) {
1961
+ this.mux(() => { this.broadcast(message, args); });
1962
+ }
1963
+ const messageSender = new MessageSender(message, args);
1964
+ this.emit('outgoingMessage', { message: messageSender.message });
1965
+ messageSender.send(this.configuration.websocketProvider);
1966
+ }
1967
+ onMessage(event) {
1968
+ const message = new IncomingMessage(event.data);
1969
+ const documentName = message.readVarString();
1970
+ if (documentName !== this.configuration.name) {
1971
+ return; // message is meant for another provider
1972
+ }
1973
+ message.writeVarString(documentName);
1974
+ this.emit('message', { event, message: new IncomingMessage(event.data) });
1975
+ new MessageReceiver(message).apply(this);
1976
+ }
1977
+ onClose(event) {
1978
+ this.isAuthenticated = false;
1979
+ this.synced = false;
1980
+ // update awareness (all users except local left)
1981
+ removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID), this);
1982
+ }
1983
+ destroy() {
1984
+ this.emit('destroy');
1985
+ if (this.intervals.forceSync) {
1986
+ clearInterval(this.intervals.forceSync);
1987
+ }
1988
+ removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
1989
+ this.disconnect();
1990
+ this.awareness.off('update', this.awarenessUpdateHandler);
1991
+ this.document.off('update', this.documentUpdateHandler);
1992
+ this.removeAllListeners();
1993
+ if (typeof window === 'undefined') {
1994
+ return;
1995
+ }
1996
+ window.removeEventListener('beforeunload', this.boundBeforeUnload);
1997
+ }
1998
+ permissionDeniedHandler(reason) {
1999
+ this.emit('authenticationFailed', { reason });
2000
+ this.isAuthenticated = false;
2001
+ this.disconnect();
2002
+ this.status = exports.WebSocketStatus.Disconnected;
2003
+ }
2004
+ authenticatedHandler() {
2005
+ this.isAuthenticated = true;
2006
+ this.emit('authenticated');
2007
+ this.startSync();
2008
+ }
2009
+ get broadcastChannel() {
2010
+ return `${this.configuration.name}`;
2011
+ }
2012
+ broadcastChannelSubscriber(data) {
2013
+ this.mux(() => {
2014
+ const message = new IncomingMessage(data);
2015
+ const documentName = message.readVarString();
2016
+ message.writeVarString(documentName);
2017
+ new MessageReceiver(message)
2018
+ .setBroadcasted(true)
2019
+ .apply(this, false);
2020
+ });
2021
+ }
2022
+ subscribeToBroadcastChannel() {
2023
+ if (!this.subscribedToBroadcastChannel) {
2024
+ subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2025
+ this.subscribedToBroadcastChannel = true;
2026
+ }
2027
+ this.mux(() => {
2028
+ this.broadcast(SyncStepOneMessage, { document: this.document });
2029
+ this.broadcast(SyncStepTwoMessage, { document: this.document });
2030
+ this.broadcast(QueryAwarenessMessage, { document: this.document });
2031
+ this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID], document: this.document });
2032
+ });
2033
+ }
2034
+ disconnectBroadcastChannel() {
2035
+ // broadcast message with local awareness state set to null (indicating disconnect)
2036
+ this.send(AwarenessMessage, {
2037
+ awareness: this.awareness,
2038
+ clients: [this.document.clientID],
2039
+ states: new Map(),
2040
+ documentName: this.configuration.name,
2041
+ }, true);
2042
+ if (this.subscribedToBroadcastChannel) {
2043
+ unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2044
+ this.subscribedToBroadcastChannel = false;
2045
+ }
2046
+ }
2047
+ broadcast(Message, args) {
2048
+ if (!this.configuration.broadcast) {
2049
+ return;
2050
+ }
2051
+ if (!this.subscribedToBroadcastChannel) {
2052
+ return;
2053
+ }
2054
+ new MessageSender(Message, args).broadcast(this.broadcastChannel);
2055
+ }
2056
+ setAwarenessField(key, value) {
2057
+ this.awareness.setLocalStateField(key, value);
2058
+ }
2059
+ }
2060
+
2061
+ /**
2062
+ * Utility module to work with urls.
2063
+ *
2064
+ * @module url
2065
+ */
2066
+
2067
+ /**
2068
+ * @param {Object<string,string>} params
2069
+ * @return {string}
2070
+ */
2071
+ const encodeQueryParams = params =>
2072
+ map(params, (val, key) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&');
2073
+
2074
+ class HocuspocusProviderWebsocket extends EventEmitter {
2075
+ constructor(configuration) {
2076
+ super();
2077
+ this.configuration = {
1785
2078
  url: '',
1786
2079
  // @ts-ignore
1787
2080
  document: undefined,
1788
2081
  // @ts-ignore
1789
2082
  awareness: undefined,
1790
2083
  WebSocketPolyfill: undefined,
1791
- token: null,
1792
2084
  parameters: {},
1793
2085
  connect: true,
1794
2086
  broadcast: true,
@@ -1811,29 +2103,22 @@ class HocuspocusProvider extends EventEmitter {
1811
2103
  jitter: true,
1812
2104
  // retry forever
1813
2105
  timeout: 0,
1814
- onAuthenticated: () => null,
1815
- onAuthenticationFailed: () => null,
1816
2106
  onOpen: () => null,
1817
2107
  onConnect: () => null,
1818
2108
  onMessage: () => null,
1819
2109
  onOutgoingMessage: () => null,
1820
2110
  onStatus: () => null,
1821
- onSynced: () => null,
1822
2111
  onDisconnect: () => null,
1823
2112
  onClose: () => null,
1824
2113
  onDestroy: () => null,
1825
2114
  onAwarenessUpdate: () => null,
1826
2115
  onAwarenessChange: () => null,
1827
- onStateless: () => null,
1828
2116
  quiet: false,
1829
2117
  };
1830
2118
  this.subscribedToBroadcastChannel = false;
1831
2119
  this.webSocket = null;
1832
2120
  this.shouldConnect = true;
1833
2121
  this.status = exports.WebSocketStatus.Disconnected;
1834
- this.isSynced = false;
1835
- this.unsyncedChanges = 0;
1836
- this.isAuthenticated = false;
1837
2122
  this.lastMessageReceived = 0;
1838
2123
  this.mux = createMutex();
1839
2124
  this.intervals = {
@@ -1841,40 +2126,27 @@ class HocuspocusProvider extends EventEmitter {
1841
2126
  connectionChecker: null,
1842
2127
  };
1843
2128
  this.connectionAttempt = null;
2129
+ this.receivedOnOpenPayload = undefined;
2130
+ this.receivedOnStatusPayload = undefined;
1844
2131
  this.boundConnect = this.connect.bind(this);
1845
- this.boundBeforeUnload = this.beforeUnload.bind(this);
1846
- this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
1847
2132
  this.setConfiguration(configuration);
1848
- this.configuration.document = configuration.document ? configuration.document : new Y__namespace.Doc();
1849
- this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document);
1850
2133
  this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket;
1851
2134
  this.on('open', this.configuration.onOpen);
1852
- this.on('authenticated', this.configuration.onAuthenticated);
1853
- this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
2135
+ this.on('open', this.onOpen.bind(this));
1854
2136
  this.on('connect', this.configuration.onConnect);
1855
2137
  this.on('message', this.configuration.onMessage);
1856
2138
  this.on('outgoingMessage', this.configuration.onOutgoingMessage);
1857
- this.on('synced', this.configuration.onSynced);
1858
2139
  this.on('status', this.configuration.onStatus);
2140
+ this.on('status', this.onStatus.bind(this));
1859
2141
  this.on('disconnect', this.configuration.onDisconnect);
1860
2142
  this.on('close', this.configuration.onClose);
1861
2143
  this.on('destroy', this.configuration.onDestroy);
1862
2144
  this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
1863
2145
  this.on('awarenessChange', this.configuration.onAwarenessChange);
1864
- this.on('stateless', this.configuration.onStateless);
1865
- this.awareness.on('update', () => {
1866
- this.emit('awarenessUpdate', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
1867
- });
1868
- this.awareness.on('change', () => {
1869
- this.emit('awarenessChange', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
1870
- });
1871
- this.document.on('update', this.documentUpdateHandler.bind(this));
1872
- this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
2146
+ this.on('close', this.onClose.bind(this));
2147
+ this.on('message', this.onMessage.bind(this));
1873
2148
  this.registerEventListeners();
1874
2149
  this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.configuration.messageReconnectTimeout / 10);
1875
- if (this.configuration.forceSyncInterval) {
1876
- this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
1877
- }
1878
2150
  if (typeof configuration.connect !== 'undefined') {
1879
2151
  this.shouldConnect = configuration.connect;
1880
2152
  }
@@ -1883,6 +2155,23 @@ class HocuspocusProvider extends EventEmitter {
1883
2155
  }
1884
2156
  this.connect();
1885
2157
  }
2158
+ async onOpen(event) {
2159
+ this.receivedOnOpenPayload = event;
2160
+ }
2161
+ async onStatus(data) {
2162
+ this.receivedOnStatusPayload = data;
2163
+ }
2164
+ attach(provider) {
2165
+ if (this.receivedOnOpenPayload) {
2166
+ provider.onOpen(this.receivedOnOpenPayload);
2167
+ }
2168
+ if (this.receivedOnStatusPayload) {
2169
+ provider.onStatus(this.receivedOnStatusPayload);
2170
+ }
2171
+ }
2172
+ detach(provider) {
2173
+ // tell the server to remove the listener
2174
+ }
1886
2175
  setConfiguration(configuration = {}) {
1887
2176
  this.configuration = { ...this.configuration, ...configuration };
1888
2177
  }
@@ -1895,9 +2184,7 @@ class HocuspocusProvider extends EventEmitter {
1895
2184
  this.cancelWebsocketRetry();
1896
2185
  this.cancelWebsocketRetry = undefined;
1897
2186
  }
1898
- this.unsyncedChanges = 0; // set to 0 in case we got reconnected
1899
2187
  this.shouldConnect = true;
1900
- this.subscribeToBroadcastChannel();
1901
2188
  const abortableRetry = () => {
1902
2189
  let cancelAttempt = false;
1903
2190
  const retryPromise = attempt.retry(this.createWebSocketConnection.bind(this), {
@@ -1941,15 +2228,14 @@ class HocuspocusProvider extends EventEmitter {
1941
2228
  // Init the WebSocket connection
1942
2229
  const ws = new this.configuration.WebSocketPolyfill(this.url);
1943
2230
  ws.binaryType = 'arraybuffer';
1944
- ws.onmessage = this.onMessage.bind(this);
1945
- ws.onclose = this.onClose.bind(this);
1946
- ws.onopen = this.onOpen.bind(this);
2231
+ ws.onmessage = (payload) => this.emit('message', payload);
2232
+ ws.onclose = (payload) => this.emit('close', { event: payload });
2233
+ ws.onopen = (payload) => this.emit('open', payload);
1947
2234
  ws.onerror = (err) => {
1948
2235
  reject(err);
1949
2236
  };
1950
2237
  this.webSocket = ws;
1951
2238
  // Reset the status
1952
- this.synced = false;
1953
2239
  this.status = exports.WebSocketStatus.Connecting;
1954
2240
  this.emit('status', { status: exports.WebSocketStatus.Connecting });
1955
2241
  // Store resolve/reject for later use
@@ -1959,13 +2245,17 @@ class HocuspocusProvider extends EventEmitter {
1959
2245
  };
1960
2246
  });
1961
2247
  }
2248
+ onMessage(event) {
2249
+ this.resolveConnectionAttempt();
2250
+ }
1962
2251
  resolveConnectionAttempt() {
1963
- var _a;
1964
- (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.resolve();
1965
- this.connectionAttempt = null;
1966
- this.status = exports.WebSocketStatus.Connected;
1967
- this.emit('status', { status: exports.WebSocketStatus.Connected });
1968
- this.emit('connect');
2252
+ if (this.connectionAttempt) {
2253
+ this.connectionAttempt.resolve();
2254
+ this.connectionAttempt = null;
2255
+ this.status = exports.WebSocketStatus.Connected;
2256
+ this.emit('status', { status: exports.WebSocketStatus.Connected });
2257
+ this.emit('connect');
2258
+ }
1969
2259
  }
1970
2260
  stopConnectionAttempt() {
1971
2261
  this.connectionAttempt = null;
@@ -1975,15 +2265,6 @@ class HocuspocusProvider extends EventEmitter {
1975
2265
  (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
1976
2266
  this.connectionAttempt = null;
1977
2267
  }
1978
- get document() {
1979
- return this.configuration.document;
1980
- }
1981
- get awareness() {
1982
- return this.configuration.awareness;
1983
- }
1984
- get hasUnsyncedChanges() {
1985
- return this.unsyncedChanges > 0;
1986
- }
1987
2268
  checkConnection() {
1988
2269
  var _a;
1989
2270
  // Don’t check the connection when it’s not even established
@@ -2002,48 +2283,11 @@ class HocuspocusProvider extends EventEmitter {
2002
2283
  // Awareness updates, which are updated every 15 seconds.
2003
2284
  (_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.close();
2004
2285
  }
2005
- forceSync() {
2006
- if (!this.webSocket) {
2007
- return;
2008
- }
2009
- this.send(SyncStepOneMessage, { document: this.document });
2010
- }
2011
- beforeUnload() {
2012
- removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload');
2013
- }
2014
2286
  registerEventListeners() {
2015
2287
  if (typeof window === 'undefined') {
2016
2288
  return;
2017
2289
  }
2018
2290
  window.addEventListener('online', this.boundConnect);
2019
- window.addEventListener('beforeunload', this.boundBeforeUnload);
2020
- }
2021
- sendStateless(payload) {
2022
- this.send(StatelessMessage, { payload });
2023
- }
2024
- documentUpdateHandler(update, origin) {
2025
- if (origin === this) {
2026
- return;
2027
- }
2028
- this.unsyncedChanges += 1;
2029
- this.send(UpdateMessage, { update }, true);
2030
- }
2031
- awarenessUpdateHandler({ added, updated, removed }, origin) {
2032
- const changedClients = added.concat(updated).concat(removed);
2033
- this.send(AwarenessMessage, {
2034
- awareness: this.awareness,
2035
- clients: changedClients,
2036
- }, true);
2037
- }
2038
- permissionDeniedHandler(reason) {
2039
- this.emit('authenticationFailed', { reason });
2040
- this.isAuthenticated = false;
2041
- this.shouldConnect = false;
2042
- }
2043
- authenticatedHandler() {
2044
- this.isAuthenticated = true;
2045
- this.emit('authenticated');
2046
- this.startSync();
2047
2291
  }
2048
2292
  // Ensure that the URL always ends with /
2049
2293
  get serverUrl() {
@@ -2054,28 +2298,10 @@ class HocuspocusProvider extends EventEmitter {
2054
2298
  }
2055
2299
  get url() {
2056
2300
  const encodedParams = encodeQueryParams(this.configuration.parameters);
2057
- return `${this.serverUrl}/${this.configuration.name}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
2058
- }
2059
- get synced() {
2060
- return this.isSynced;
2061
- }
2062
- set synced(state) {
2063
- if (this.isSynced === state) {
2064
- return;
2065
- }
2066
- this.isSynced = state;
2067
- this.emit('synced', { state });
2068
- this.emit('sync', { state });
2069
- }
2070
- receiveStateless(payload) {
2071
- this.emit('stateless', { payload });
2072
- }
2073
- get isAuthenticationRequired() {
2074
- return !!this.configuration.token && !this.isAuthenticated;
2301
+ return `${this.serverUrl}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
2075
2302
  }
2076
2303
  disconnect() {
2077
2304
  this.shouldConnect = false;
2078
- this.disconnectBroadcastChannel();
2079
2305
  if (this.webSocket === null) {
2080
2306
  return;
2081
2307
  }
@@ -2086,58 +2312,15 @@ class HocuspocusProvider extends EventEmitter {
2086
2312
  //
2087
2313
  }
2088
2314
  }
2089
- async onOpen(event) {
2090
- this.emit('open', { event });
2091
- if (this.isAuthenticationRequired) {
2092
- this.send(AuthenticationMessage, {
2093
- token: await this.getToken(),
2094
- });
2095
- return;
2096
- }
2097
- this.startSync();
2098
- }
2099
- async getToken() {
2100
- if (typeof this.configuration.token === 'function') {
2101
- const token = await this.configuration.token();
2102
- return token;
2103
- }
2104
- return this.configuration.token;
2105
- }
2106
- startSync() {
2107
- this.send(SyncStepOneMessage, { document: this.document });
2108
- if (this.awareness.getLocalState() !== null) {
2109
- this.send(AwarenessMessage, {
2110
- awareness: this.awareness,
2111
- clients: [this.document.clientID],
2112
- });
2113
- }
2114
- }
2115
- send(Message, args, broadcast = false) {
2315
+ send(message) {
2116
2316
  var _a;
2117
- if (broadcast) {
2118
- this.mux(() => { this.broadcast(Message, args); });
2119
- }
2120
2317
  if (((_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.readyState) === common.WsReadyStates.Open) {
2121
- const messageSender = new MessageSender(Message, args);
2122
- this.emit('outgoingMessage', { message: messageSender.message });
2123
- messageSender.send(this.webSocket);
2318
+ this.webSocket.send(message);
2124
2319
  }
2125
2320
  }
2126
- onMessage(event) {
2127
- this.resolveConnectionAttempt();
2128
- this.lastMessageReceived = getUnixTime();
2129
- const message = new IncomingMessage(event.data);
2130
- this.emit('message', { event, message });
2131
- new MessageReceiver(message).apply(this);
2132
- }
2133
- onClose(event) {
2134
- this.emit('close', { event });
2321
+ onClose({ event }) {
2135
2322
  this.webSocket = null;
2136
- this.isAuthenticated = false;
2137
- this.synced = false;
2138
2323
  if (this.status === exports.WebSocketStatus.Connected) {
2139
- // update awareness (all users except local left)
2140
- removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID), this);
2141
2324
  this.status = exports.WebSocketStatus.Disconnected;
2142
2325
  this.emit('status', { status: exports.WebSocketStatus.Disconnected });
2143
2326
  this.emit('disconnect', { event });
@@ -2151,6 +2334,7 @@ class HocuspocusProvider extends EventEmitter {
2151
2334
  if (event.code === common.Forbidden.code) {
2152
2335
  if (!this.configuration.quiet) {
2153
2336
  console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.');
2337
+ return; // TODO REMOVE ME
2154
2338
  }
2155
2339
  }
2156
2340
  if (this.connectionAttempt) {
@@ -2180,67 +2364,16 @@ class HocuspocusProvider extends EventEmitter {
2180
2364
  clearInterval(this.intervals.forceSync);
2181
2365
  }
2182
2366
  clearInterval(this.intervals.connectionChecker);
2183
- removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
2184
2367
  // If there is still a connection attempt outstanding then we should stop
2185
2368
  // it before calling disconnect, otherwise it will be rejected in the onClose
2186
2369
  // handler and trigger a retry
2187
2370
  this.stopConnectionAttempt();
2188
2371
  this.disconnect();
2189
- this.awareness.off('update', this.awarenessUpdateHandler);
2190
- this.document.off('update', this.documentUpdateHandler);
2191
2372
  this.removeAllListeners();
2192
2373
  if (typeof window === 'undefined') {
2193
2374
  return;
2194
2375
  }
2195
2376
  window.removeEventListener('online', this.boundConnect);
2196
- window.removeEventListener('beforeunload', this.boundBeforeUnload);
2197
- }
2198
- get broadcastChannel() {
2199
- return `${this.serverUrl}/${this.configuration.name}`;
2200
- }
2201
- broadcastChannelSubscriber(data) {
2202
- this.mux(() => {
2203
- const message = new IncomingMessage(data);
2204
- new MessageReceiver(message)
2205
- .setBroadcasted(true)
2206
- .apply(this, false);
2207
- });
2208
- }
2209
- subscribeToBroadcastChannel() {
2210
- if (!this.subscribedToBroadcastChannel) {
2211
- subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2212
- this.subscribedToBroadcastChannel = true;
2213
- }
2214
- this.mux(() => {
2215
- this.broadcast(SyncStepOneMessage, { document: this.document });
2216
- this.broadcast(SyncStepTwoMessage, { document: this.document });
2217
- this.broadcast(QueryAwarenessMessage);
2218
- this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID] });
2219
- });
2220
- }
2221
- disconnectBroadcastChannel() {
2222
- // broadcast message with local awareness state set to null (indicating disconnect)
2223
- this.send(AwarenessMessage, {
2224
- awareness: this.awareness,
2225
- clients: [this.document.clientID],
2226
- states: new Map(),
2227
- }, true);
2228
- if (this.subscribedToBroadcastChannel) {
2229
- unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2230
- this.subscribedToBroadcastChannel = false;
2231
- }
2232
- }
2233
- broadcast(Message, args) {
2234
- if (!this.configuration.broadcast) {
2235
- return;
2236
- }
2237
- if (!this.subscribedToBroadcastChannel) {
2238
- return;
2239
- }
2240
- new MessageSender(Message, args).broadcast(this.broadcastChannel);
2241
- }
2242
- setAwarenessField(key, value) {
2243
- this.awareness.setLocalStateField(key, value);
2244
2377
  }
2245
2378
  }
2246
2379
 
@@ -2255,10 +2388,12 @@ class HocuspocusCloudProvider extends HocuspocusProvider {
2255
2388
  }
2256
2389
  configuration.parameters.key = configuration.key;
2257
2390
  }
2391
+ configuration.websocketProvider = new HocuspocusProviderWebsocket({ url: configuration.url });
2258
2392
  super(configuration);
2259
2393
  }
2260
2394
  }
2261
2395
 
2262
2396
  exports.HocuspocusCloudProvider = HocuspocusCloudProvider;
2263
2397
  exports.HocuspocusProvider = HocuspocusProvider;
2398
+ exports.HocuspocusProviderWebsocket = HocuspocusProviderWebsocket;
2264
2399
  //# sourceMappingURL=hocuspocus-provider.cjs.map