@hocuspocus/provider 1.1.1 → 2.0.0-alpha.1

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 (40) hide show
  1. package/dist/hocuspocus-provider.cjs +370 -217
  2. package/dist/hocuspocus-provider.cjs.map +1 -1
  3. package/dist/hocuspocus-provider.esm.js +370 -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 +11 -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/OutgoingMessages/CloseMessage.d.ts +8 -0
  12. package/dist/packages/provider/src/index.d.ts +1 -0
  13. package/dist/packages/provider/src/types.d.ts +3 -1
  14. package/dist/packages/server/src/Connection.d.ts +6 -12
  15. package/dist/packages/server/src/Hocuspocus.d.ts +3 -8
  16. package/dist/packages/server/src/IncomingMessage.d.ts +1 -0
  17. package/dist/packages/server/src/MessageReceiver.d.ts +1 -2
  18. package/dist/packages/server/src/OutgoingMessage.d.ts +1 -1
  19. package/dist/packages/server/src/types.d.ts +2 -1
  20. package/dist/tests/utils/index.d.ts +1 -0
  21. package/dist/tests/utils/newHocuspocusProvider.d.ts +2 -2
  22. package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +3 -0
  23. package/package.json +4 -3
  24. package/src/HocuspocusCloudProvider.ts +8 -1
  25. package/src/HocuspocusProvider.ts +108 -361
  26. package/src/HocuspocusProviderWebsocket.ts +475 -0
  27. package/src/IncomingMessage.ts +10 -0
  28. package/src/MessageReceiver.ts +4 -2
  29. package/src/OutgoingMessages/AuthenticationMessage.ts +2 -1
  30. package/src/OutgoingMessages/AwarenessMessage.ts +1 -0
  31. package/src/OutgoingMessages/CloseMessage.ts +16 -0
  32. package/src/OutgoingMessages/QueryAwarenessMessage.ts +5 -0
  33. package/src/OutgoingMessages/StatelessMessage.ts +1 -0
  34. package/src/OutgoingMessages/SyncStepOneMessage.ts +1 -0
  35. package/src/OutgoingMessages/SyncStepTwoMessage.ts +1 -1
  36. package/src/OutgoingMessages/UpdateMessage.ts +3 -1
  37. package/src/index.ts +1 -0
  38. package/src/types.ts +2 -0
  39. package/dist/tests/server/getDocumentName.d.ts +0 -1
  40. /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
  }
@@ -1553,6 +1546,7 @@ exports.MessageType = void 0;
1553
1546
  MessageType[MessageType["Auth"] = 2] = "Auth";
1554
1547
  MessageType[MessageType["QueryAwareness"] = 3] = "QueryAwareness";
1555
1548
  MessageType[MessageType["Stateless"] = 5] = "Stateless";
1549
+ MessageType[MessageType["CLOSE"] = 7] = "CLOSE";
1556
1550
  })(exports.MessageType || (exports.MessageType = {}));
1557
1551
  exports.WebSocketStatus = void 0;
1558
1552
  (function (WebSocketStatus) {
@@ -1585,6 +1579,7 @@ class MessageReceiver {
1585
1579
  apply(provider, emitSynced = true) {
1586
1580
  const { message } = this;
1587
1581
  const type = message.readVarUint();
1582
+ const emptyMessageLength = message.length();
1588
1583
  switch (type) {
1589
1584
  case exports.MessageType.Sync:
1590
1585
  this.applySyncMessage(provider, emitSynced);
@@ -1605,7 +1600,7 @@ class MessageReceiver {
1605
1600
  throw new Error(`Can’t apply message of unknown type: ${type}`);
1606
1601
  }
1607
1602
  // Reply
1608
- if (message.length() > 1) {
1603
+ if (message.length() > emptyMessageLength + 1) { // length of documentName (considered in emptyMessageLength plus length of yjs sync type, set in applySyncMessage)
1609
1604
  if (this.broadcasted) {
1610
1605
  // TODO: Some weird TypeScript error
1611
1606
  // @ts-ignore
@@ -1624,7 +1619,7 @@ class MessageReceiver {
1624
1619
  // Apply update
1625
1620
  const syncMessageType = readSyncMessage(message.decoder, message.encoder, provider.document, provider);
1626
1621
  // Synced once we receive Step2
1627
- if (emitSynced && (syncMessageType === messageYjsSyncStep2)) {
1622
+ if (emitSynced && syncMessageType === messageYjsSyncStep2) {
1628
1623
  provider.synced = true;
1629
1624
  }
1630
1625
  if (syncMessageType === messageYjsUpdate || syncMessageType === messageYjsSyncStep2) {
@@ -1674,6 +1669,7 @@ class SyncStepOneMessage extends OutgoingMessage {
1674
1669
  if (typeof args.document === 'undefined') {
1675
1670
  throw new Error('The sync step one message requires document as an argument');
1676
1671
  }
1672
+ writeVarString(this.encoder, args.documentName);
1677
1673
  writeVarUint(this.encoder, this.type);
1678
1674
  writeSyncStep1(this.encoder, args.document);
1679
1675
  return this.encoder;
@@ -1690,6 +1686,7 @@ class SyncStepTwoMessage extends OutgoingMessage {
1690
1686
  if (typeof args.document === 'undefined') {
1691
1687
  throw new Error('The sync step two message requires document as an argument');
1692
1688
  }
1689
+ writeVarString(this.encoder, args.documentName);
1693
1690
  writeVarUint(this.encoder, this.type);
1694
1691
  writeSyncStep2(this.encoder, args.document);
1695
1692
  return this.encoder;
@@ -1703,6 +1700,9 @@ class QueryAwarenessMessage extends OutgoingMessage {
1703
1700
  this.description = 'Queries awareness states';
1704
1701
  }
1705
1702
  get(args) {
1703
+ console.log('queryAwareness: writing string docName', args.documentName);
1704
+ console.log(this.encoder.cpos);
1705
+ writeVarString(this.encoder, args.documentName);
1706
1706
  writeVarUint(this.encoder, this.type);
1707
1707
  return this.encoder;
1708
1708
  }
@@ -1718,6 +1718,7 @@ class AuthenticationMessage extends OutgoingMessage {
1718
1718
  if (typeof args.token === 'undefined') {
1719
1719
  throw new Error('The authentication message requires `token` as an argument.');
1720
1720
  }
1721
+ writeVarString(this.encoder, args.documentName);
1721
1722
  writeVarUint(this.encoder, this.type);
1722
1723
  common.writeAuthentication(this.encoder, args.token);
1723
1724
  return this.encoder;
@@ -1737,6 +1738,7 @@ class AwarenessMessage extends OutgoingMessage {
1737
1738
  if (typeof args.clients === 'undefined') {
1738
1739
  throw new Error('The awareness message requires clients as an argument');
1739
1740
  }
1741
+ writeVarString(this.encoder, args.documentName);
1740
1742
  writeVarUint(this.encoder, this.type);
1741
1743
  let awarenessUpdate;
1742
1744
  if (args.states === undefined) {
@@ -1757,6 +1759,7 @@ class UpdateMessage extends OutgoingMessage {
1757
1759
  this.description = 'A document update';
1758
1760
  }
1759
1761
  get(args) {
1762
+ writeVarString(this.encoder, args.documentName);
1760
1763
  writeVarUint(this.encoder, this.type);
1761
1764
  writeUpdate(this.encoder, args.update);
1762
1765
  return this.encoder;
@@ -1771,24 +1774,331 @@ class StatelessMessage extends OutgoingMessage {
1771
1774
  }
1772
1775
  get(args) {
1773
1776
  var _a;
1777
+ writeVarString(this.encoder, args.documentName);
1774
1778
  writeVarUint(this.encoder, this.type);
1775
1779
  writeVarString(this.encoder, (_a = args.payload) !== null && _a !== void 0 ? _a : '');
1776
1780
  return this.encoder;
1777
1781
  }
1778
1782
  }
1779
1783
 
1784
+ class CloseMessage extends OutgoingMessage {
1785
+ constructor() {
1786
+ super(...arguments);
1787
+ this.type = exports.MessageType.CLOSE;
1788
+ this.description = 'Ask the server to close the connection';
1789
+ }
1790
+ get(args) {
1791
+ writeVarString(this.encoder, args.documentName);
1792
+ writeVarUint(this.encoder, this.type);
1793
+ return this.encoder;
1794
+ }
1795
+ }
1796
+
1780
1797
  class HocuspocusProvider extends EventEmitter {
1781
1798
  constructor(configuration) {
1782
1799
  super();
1783
1800
  this.configuration = {
1784
1801
  name: '',
1802
+ // @ts-ignore
1803
+ document: undefined,
1804
+ // @ts-ignore
1805
+ awareness: undefined,
1806
+ token: null,
1807
+ parameters: {},
1808
+ broadcast: true,
1809
+ forceSyncInterval: false,
1810
+ onAuthenticated: () => null,
1811
+ onAuthenticationFailed: () => null,
1812
+ onOpen: () => null,
1813
+ onConnect: () => null,
1814
+ onMessage: () => null,
1815
+ onOutgoingMessage: () => null,
1816
+ onStatus: () => null,
1817
+ onSynced: () => null,
1818
+ onDisconnect: () => null,
1819
+ onClose: () => null,
1820
+ onDestroy: () => null,
1821
+ onAwarenessUpdate: () => null,
1822
+ onAwarenessChange: () => null,
1823
+ onStateless: () => null,
1824
+ quiet: false,
1825
+ };
1826
+ this.subscribedToBroadcastChannel = false;
1827
+ this.isSynced = false;
1828
+ this.unsyncedChanges = 0;
1829
+ this.status = exports.WebSocketStatus.Disconnected;
1830
+ this.isAuthenticated = false;
1831
+ this.mux = createMutex();
1832
+ this.intervals = {
1833
+ forceSync: null,
1834
+ };
1835
+ this.boundBeforeUnload = this.beforeUnload.bind(this);
1836
+ this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
1837
+ this.setConfiguration(configuration);
1838
+ this.configuration.document = configuration.document ? configuration.document : new Y__namespace.Doc();
1839
+ this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document);
1840
+ this.on('open', this.configuration.onOpen);
1841
+ this.on('message', this.configuration.onMessage);
1842
+ this.on('outgoingMessage', this.configuration.onOutgoingMessage);
1843
+ this.on('synced', this.configuration.onSynced);
1844
+ this.on('destroy', this.configuration.onDestroy);
1845
+ this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
1846
+ this.on('awarenessChange', this.configuration.onAwarenessChange);
1847
+ this.on('stateless', this.configuration.onStateless);
1848
+ this.on('authenticated', this.configuration.onAuthenticated);
1849
+ this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
1850
+ this.configuration.websocketProvider.on('connect', this.configuration.onConnect);
1851
+ this.configuration.websocketProvider.on('connect', (e) => this.emit('connect', e));
1852
+ this.configuration.websocketProvider.on('open', this.onOpen.bind(this));
1853
+ this.configuration.websocketProvider.on('open', (e) => this.emit('open', e));
1854
+ this.configuration.websocketProvider.on('message', this.onMessage.bind(this));
1855
+ this.configuration.websocketProvider.on('close', this.onClose.bind(this));
1856
+ this.configuration.websocketProvider.on('close', this.configuration.onClose);
1857
+ this.configuration.websocketProvider.on('close', (e) => this.emit('close', e));
1858
+ this.configuration.websocketProvider.on('status', this.onStatus.bind(this));
1859
+ this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect);
1860
+ this.configuration.websocketProvider.on('disconnect', (e) => this.emit('disconnect', e));
1861
+ this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy);
1862
+ this.configuration.websocketProvider.on('destroy', (e) => this.emit('destroy', e));
1863
+ this.awareness.on('update', () => {
1864
+ this.emit('awarenessUpdate', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
1865
+ });
1866
+ this.awareness.on('change', () => {
1867
+ this.emit('awarenessChange', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
1868
+ });
1869
+ this.document.on('update', this.documentUpdateHandler.bind(this));
1870
+ this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
1871
+ this.registerEventListeners();
1872
+ if (this.configuration.forceSyncInterval) {
1873
+ this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
1874
+ }
1875
+ this.configuration.websocketProvider.attach(this);
1876
+ }
1877
+ onStatus({ status }) {
1878
+ this.status = status;
1879
+ this.configuration.onStatus({ status });
1880
+ this.emit('status', { status });
1881
+ }
1882
+ setConfiguration(configuration = {}) {
1883
+ this.configuration = { ...this.configuration, ...configuration };
1884
+ }
1885
+ get document() {
1886
+ return this.configuration.document;
1887
+ }
1888
+ get awareness() {
1889
+ return this.configuration.awareness;
1890
+ }
1891
+ get hasUnsyncedChanges() {
1892
+ return this.unsyncedChanges > 0;
1893
+ }
1894
+ forceSync() {
1895
+ this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
1896
+ }
1897
+ beforeUnload() {
1898
+ removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload');
1899
+ }
1900
+ registerEventListeners() {
1901
+ if (typeof window === 'undefined') {
1902
+ return;
1903
+ }
1904
+ window.addEventListener('beforeunload', this.boundBeforeUnload);
1905
+ }
1906
+ sendStateless(payload) {
1907
+ this.send(StatelessMessage, { documentName: this.configuration.name, payload });
1908
+ }
1909
+ documentUpdateHandler(update, origin) {
1910
+ if (origin === this) {
1911
+ return;
1912
+ }
1913
+ this.unsyncedChanges += 1;
1914
+ this.send(UpdateMessage, { update, documentName: this.configuration.name }, true);
1915
+ }
1916
+ awarenessUpdateHandler({ added, updated, removed }, origin) {
1917
+ const changedClients = added.concat(updated).concat(removed);
1918
+ this.send(AwarenessMessage, {
1919
+ awareness: this.awareness,
1920
+ clients: changedClients,
1921
+ documentName: this.configuration.name,
1922
+ }, true);
1923
+ }
1924
+ get synced() {
1925
+ return this.isSynced;
1926
+ }
1927
+ set synced(state) {
1928
+ if (this.isSynced === state) {
1929
+ return;
1930
+ }
1931
+ this.isSynced = state;
1932
+ this.emit('synced', { state });
1933
+ this.emit('sync', { state });
1934
+ }
1935
+ receiveStateless(payload) {
1936
+ this.emit('stateless', { payload });
1937
+ }
1938
+ get isAuthenticationRequired() {
1939
+ return !!this.configuration.token && !this.isAuthenticated;
1940
+ }
1941
+ // not needed, but provides backward compatibility with e.g. lexicla/yjs
1942
+ async connect() {
1943
+ return this.configuration.websocketProvider.connect();
1944
+ }
1945
+ disconnect() {
1946
+ this.disconnectBroadcastChannel();
1947
+ this.configuration.websocketProvider.detach(this);
1948
+ }
1949
+ async onOpen(event) {
1950
+ this.emit('open', { event });
1951
+ if (this.isAuthenticationRequired) {
1952
+ this.send(AuthenticationMessage, {
1953
+ token: await this.getToken(),
1954
+ documentName: this.configuration.name,
1955
+ });
1956
+ }
1957
+ this.startSync();
1958
+ }
1959
+ async getToken() {
1960
+ if (typeof this.configuration.token === 'function') {
1961
+ const token = await this.configuration.token();
1962
+ return token;
1963
+ }
1964
+ return this.configuration.token;
1965
+ }
1966
+ startSync() {
1967
+ this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
1968
+ if (this.awareness.getLocalState() !== null) {
1969
+ this.send(AwarenessMessage, {
1970
+ awareness: this.awareness,
1971
+ clients: [this.document.clientID],
1972
+ documentName: this.configuration.name,
1973
+ });
1974
+ }
1975
+ }
1976
+ send(message, args, broadcast = false) {
1977
+ if (broadcast) {
1978
+ this.mux(() => { this.broadcast(message, args); });
1979
+ }
1980
+ const messageSender = new MessageSender(message, args);
1981
+ this.emit('outgoingMessage', { message: messageSender.message });
1982
+ messageSender.send(this.configuration.websocketProvider);
1983
+ }
1984
+ onMessage(event) {
1985
+ const message = new IncomingMessage(event.data);
1986
+ const documentName = message.readVarString();
1987
+ if (documentName !== this.configuration.name) {
1988
+ return; // message is meant for another provider
1989
+ }
1990
+ message.writeVarString(documentName);
1991
+ this.emit('message', { event, message: new IncomingMessage(event.data) });
1992
+ new MessageReceiver(message).apply(this);
1993
+ }
1994
+ onClose(event) {
1995
+ this.isAuthenticated = false;
1996
+ this.synced = false;
1997
+ // update awareness (all users except local left)
1998
+ removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID), this);
1999
+ }
2000
+ destroy() {
2001
+ this.emit('destroy');
2002
+ if (this.intervals.forceSync) {
2003
+ clearInterval(this.intervals.forceSync);
2004
+ }
2005
+ removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
2006
+ this.disconnect();
2007
+ this.awareness.off('update', this.awarenessUpdateHandler);
2008
+ this.document.off('update', this.documentUpdateHandler);
2009
+ this.removeAllListeners();
2010
+ this.send(CloseMessage, { documentName: this.configuration.name });
2011
+ if (typeof window === 'undefined') {
2012
+ return;
2013
+ }
2014
+ window.removeEventListener('beforeunload', this.boundBeforeUnload);
2015
+ }
2016
+ permissionDeniedHandler(reason) {
2017
+ this.emit('authenticationFailed', { reason });
2018
+ this.isAuthenticated = false;
2019
+ this.disconnect();
2020
+ this.status = exports.WebSocketStatus.Disconnected;
2021
+ }
2022
+ authenticatedHandler() {
2023
+ this.isAuthenticated = true;
2024
+ this.emit('authenticated');
2025
+ this.startSync();
2026
+ }
2027
+ get broadcastChannel() {
2028
+ return `${this.configuration.name}`;
2029
+ }
2030
+ broadcastChannelSubscriber(data) {
2031
+ this.mux(() => {
2032
+ const message = new IncomingMessage(data);
2033
+ const documentName = message.readVarString();
2034
+ message.writeVarString(documentName);
2035
+ new MessageReceiver(message)
2036
+ .setBroadcasted(true)
2037
+ .apply(this, false);
2038
+ });
2039
+ }
2040
+ subscribeToBroadcastChannel() {
2041
+ if (!this.subscribedToBroadcastChannel) {
2042
+ subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2043
+ this.subscribedToBroadcastChannel = true;
2044
+ }
2045
+ this.mux(() => {
2046
+ this.broadcast(SyncStepOneMessage, { document: this.document });
2047
+ this.broadcast(SyncStepTwoMessage, { document: this.document });
2048
+ this.broadcast(QueryAwarenessMessage, { document: this.document });
2049
+ this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID], document: this.document });
2050
+ });
2051
+ }
2052
+ disconnectBroadcastChannel() {
2053
+ // broadcast message with local awareness state set to null (indicating disconnect)
2054
+ this.send(AwarenessMessage, {
2055
+ awareness: this.awareness,
2056
+ clients: [this.document.clientID],
2057
+ states: new Map(),
2058
+ documentName: this.configuration.name,
2059
+ }, true);
2060
+ if (this.subscribedToBroadcastChannel) {
2061
+ unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2062
+ this.subscribedToBroadcastChannel = false;
2063
+ }
2064
+ }
2065
+ broadcast(Message, args) {
2066
+ if (!this.configuration.broadcast) {
2067
+ return;
2068
+ }
2069
+ if (!this.subscribedToBroadcastChannel) {
2070
+ return;
2071
+ }
2072
+ new MessageSender(Message, args).broadcast(this.broadcastChannel);
2073
+ }
2074
+ setAwarenessField(key, value) {
2075
+ this.awareness.setLocalStateField(key, value);
2076
+ }
2077
+ }
2078
+
2079
+ /**
2080
+ * Utility module to work with urls.
2081
+ *
2082
+ * @module url
2083
+ */
2084
+
2085
+ /**
2086
+ * @param {Object<string,string>} params
2087
+ * @return {string}
2088
+ */
2089
+ const encodeQueryParams = params =>
2090
+ map(params, (val, key) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&');
2091
+
2092
+ class HocuspocusProviderWebsocket extends EventEmitter {
2093
+ constructor(configuration) {
2094
+ super();
2095
+ this.configuration = {
1785
2096
  url: '',
1786
2097
  // @ts-ignore
1787
2098
  document: undefined,
1788
2099
  // @ts-ignore
1789
2100
  awareness: undefined,
1790
2101
  WebSocketPolyfill: undefined,
1791
- token: null,
1792
2102
  parameters: {},
1793
2103
  connect: true,
1794
2104
  broadcast: true,
@@ -1811,29 +2121,22 @@ class HocuspocusProvider extends EventEmitter {
1811
2121
  jitter: true,
1812
2122
  // retry forever
1813
2123
  timeout: 0,
1814
- onAuthenticated: () => null,
1815
- onAuthenticationFailed: () => null,
1816
2124
  onOpen: () => null,
1817
2125
  onConnect: () => null,
1818
2126
  onMessage: () => null,
1819
2127
  onOutgoingMessage: () => null,
1820
2128
  onStatus: () => null,
1821
- onSynced: () => null,
1822
2129
  onDisconnect: () => null,
1823
2130
  onClose: () => null,
1824
2131
  onDestroy: () => null,
1825
2132
  onAwarenessUpdate: () => null,
1826
2133
  onAwarenessChange: () => null,
1827
- onStateless: () => null,
1828
2134
  quiet: false,
1829
2135
  };
1830
2136
  this.subscribedToBroadcastChannel = false;
1831
2137
  this.webSocket = null;
1832
2138
  this.shouldConnect = true;
1833
2139
  this.status = exports.WebSocketStatus.Disconnected;
1834
- this.isSynced = false;
1835
- this.unsyncedChanges = 0;
1836
- this.isAuthenticated = false;
1837
2140
  this.lastMessageReceived = 0;
1838
2141
  this.mux = createMutex();
1839
2142
  this.intervals = {
@@ -1841,40 +2144,27 @@ class HocuspocusProvider extends EventEmitter {
1841
2144
  connectionChecker: null,
1842
2145
  };
1843
2146
  this.connectionAttempt = null;
2147
+ this.receivedOnOpenPayload = undefined;
2148
+ this.receivedOnStatusPayload = undefined;
1844
2149
  this.boundConnect = this.connect.bind(this);
1845
- this.boundBeforeUnload = this.beforeUnload.bind(this);
1846
- this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
1847
2150
  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
2151
  this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket;
1851
2152
  this.on('open', this.configuration.onOpen);
1852
- this.on('authenticated', this.configuration.onAuthenticated);
1853
- this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
2153
+ this.on('open', this.onOpen.bind(this));
1854
2154
  this.on('connect', this.configuration.onConnect);
1855
2155
  this.on('message', this.configuration.onMessage);
1856
2156
  this.on('outgoingMessage', this.configuration.onOutgoingMessage);
1857
- this.on('synced', this.configuration.onSynced);
1858
2157
  this.on('status', this.configuration.onStatus);
2158
+ this.on('status', this.onStatus.bind(this));
1859
2159
  this.on('disconnect', this.configuration.onDisconnect);
1860
2160
  this.on('close', this.configuration.onClose);
1861
2161
  this.on('destroy', this.configuration.onDestroy);
1862
2162
  this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
1863
2163
  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));
2164
+ this.on('close', this.onClose.bind(this));
2165
+ this.on('message', this.onMessage.bind(this));
1873
2166
  this.registerEventListeners();
1874
2167
  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
2168
  if (typeof configuration.connect !== 'undefined') {
1879
2169
  this.shouldConnect = configuration.connect;
1880
2170
  }
@@ -1883,6 +2173,23 @@ class HocuspocusProvider extends EventEmitter {
1883
2173
  }
1884
2174
  this.connect();
1885
2175
  }
2176
+ async onOpen(event) {
2177
+ this.receivedOnOpenPayload = event;
2178
+ }
2179
+ async onStatus(data) {
2180
+ this.receivedOnStatusPayload = data;
2181
+ }
2182
+ attach(provider) {
2183
+ if (this.receivedOnOpenPayload) {
2184
+ provider.onOpen(this.receivedOnOpenPayload);
2185
+ }
2186
+ if (this.receivedOnStatusPayload) {
2187
+ provider.onStatus(this.receivedOnStatusPayload);
2188
+ }
2189
+ }
2190
+ detach(provider) {
2191
+ // tell the server to remove the listener
2192
+ }
1886
2193
  setConfiguration(configuration = {}) {
1887
2194
  this.configuration = { ...this.configuration, ...configuration };
1888
2195
  }
@@ -1895,9 +2202,7 @@ class HocuspocusProvider extends EventEmitter {
1895
2202
  this.cancelWebsocketRetry();
1896
2203
  this.cancelWebsocketRetry = undefined;
1897
2204
  }
1898
- this.unsyncedChanges = 0; // set to 0 in case we got reconnected
1899
2205
  this.shouldConnect = true;
1900
- this.subscribeToBroadcastChannel();
1901
2206
  const abortableRetry = () => {
1902
2207
  let cancelAttempt = false;
1903
2208
  const retryPromise = attempt.retry(this.createWebSocketConnection.bind(this), {
@@ -1941,15 +2246,14 @@ class HocuspocusProvider extends EventEmitter {
1941
2246
  // Init the WebSocket connection
1942
2247
  const ws = new this.configuration.WebSocketPolyfill(this.url);
1943
2248
  ws.binaryType = 'arraybuffer';
1944
- ws.onmessage = this.onMessage.bind(this);
1945
- ws.onclose = this.onClose.bind(this);
1946
- ws.onopen = this.onOpen.bind(this);
2249
+ ws.onmessage = (payload) => this.emit('message', payload);
2250
+ ws.onclose = (payload) => this.emit('close', { event: payload });
2251
+ ws.onopen = (payload) => this.emit('open', payload);
1947
2252
  ws.onerror = (err) => {
1948
2253
  reject(err);
1949
2254
  };
1950
2255
  this.webSocket = ws;
1951
2256
  // Reset the status
1952
- this.synced = false;
1953
2257
  this.status = exports.WebSocketStatus.Connecting;
1954
2258
  this.emit('status', { status: exports.WebSocketStatus.Connecting });
1955
2259
  // Store resolve/reject for later use
@@ -1959,13 +2263,17 @@ class HocuspocusProvider extends EventEmitter {
1959
2263
  };
1960
2264
  });
1961
2265
  }
2266
+ onMessage(event) {
2267
+ this.resolveConnectionAttempt();
2268
+ }
1962
2269
  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');
2270
+ if (this.connectionAttempt) {
2271
+ this.connectionAttempt.resolve();
2272
+ this.connectionAttempt = null;
2273
+ this.status = exports.WebSocketStatus.Connected;
2274
+ this.emit('status', { status: exports.WebSocketStatus.Connected });
2275
+ this.emit('connect');
2276
+ }
1969
2277
  }
1970
2278
  stopConnectionAttempt() {
1971
2279
  this.connectionAttempt = null;
@@ -1975,15 +2283,6 @@ class HocuspocusProvider extends EventEmitter {
1975
2283
  (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
1976
2284
  this.connectionAttempt = null;
1977
2285
  }
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
2286
  checkConnection() {
1988
2287
  var _a;
1989
2288
  // Don’t check the connection when it’s not even established
@@ -2002,48 +2301,11 @@ class HocuspocusProvider extends EventEmitter {
2002
2301
  // Awareness updates, which are updated every 15 seconds.
2003
2302
  (_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.close();
2004
2303
  }
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
2304
  registerEventListeners() {
2015
2305
  if (typeof window === 'undefined') {
2016
2306
  return;
2017
2307
  }
2018
2308
  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
2309
  }
2048
2310
  // Ensure that the URL always ends with /
2049
2311
  get serverUrl() {
@@ -2054,28 +2316,10 @@ class HocuspocusProvider extends EventEmitter {
2054
2316
  }
2055
2317
  get url() {
2056
2318
  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;
2319
+ return `${this.serverUrl}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
2075
2320
  }
2076
2321
  disconnect() {
2077
2322
  this.shouldConnect = false;
2078
- this.disconnectBroadcastChannel();
2079
2323
  if (this.webSocket === null) {
2080
2324
  return;
2081
2325
  }
@@ -2086,58 +2330,15 @@ class HocuspocusProvider extends EventEmitter {
2086
2330
  //
2087
2331
  }
2088
2332
  }
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) {
2333
+ send(message) {
2116
2334
  var _a;
2117
- if (broadcast) {
2118
- this.mux(() => { this.broadcast(Message, args); });
2119
- }
2120
2335
  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);
2336
+ this.webSocket.send(message);
2124
2337
  }
2125
2338
  }
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 });
2339
+ onClose({ event }) {
2135
2340
  this.webSocket = null;
2136
- this.isAuthenticated = false;
2137
- this.synced = false;
2138
2341
  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
2342
  this.status = exports.WebSocketStatus.Disconnected;
2142
2343
  this.emit('status', { status: exports.WebSocketStatus.Disconnected });
2143
2344
  this.emit('disconnect', { event });
@@ -2151,6 +2352,7 @@ class HocuspocusProvider extends EventEmitter {
2151
2352
  if (event.code === common.Forbidden.code) {
2152
2353
  if (!this.configuration.quiet) {
2153
2354
  console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.');
2355
+ return; // TODO REMOVE ME
2154
2356
  }
2155
2357
  }
2156
2358
  if (this.connectionAttempt) {
@@ -2180,67 +2382,16 @@ class HocuspocusProvider extends EventEmitter {
2180
2382
  clearInterval(this.intervals.forceSync);
2181
2383
  }
2182
2384
  clearInterval(this.intervals.connectionChecker);
2183
- removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
2184
2385
  // If there is still a connection attempt outstanding then we should stop
2185
2386
  // it before calling disconnect, otherwise it will be rejected in the onClose
2186
2387
  // handler and trigger a retry
2187
2388
  this.stopConnectionAttempt();
2188
2389
  this.disconnect();
2189
- this.awareness.off('update', this.awarenessUpdateHandler);
2190
- this.document.off('update', this.documentUpdateHandler);
2191
2390
  this.removeAllListeners();
2192
2391
  if (typeof window === 'undefined') {
2193
2392
  return;
2194
2393
  }
2195
2394
  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
2395
  }
2245
2396
  }
2246
2397
 
@@ -2255,10 +2406,12 @@ class HocuspocusCloudProvider extends HocuspocusProvider {
2255
2406
  }
2256
2407
  configuration.parameters.key = configuration.key;
2257
2408
  }
2409
+ configuration.websocketProvider = new HocuspocusProviderWebsocket({ url: configuration.url });
2258
2410
  super(configuration);
2259
2411
  }
2260
2412
  }
2261
2413
 
2262
2414
  exports.HocuspocusCloudProvider = HocuspocusCloudProvider;
2263
2415
  exports.HocuspocusProvider = HocuspocusProvider;
2416
+ exports.HocuspocusProviderWebsocket = HocuspocusProviderWebsocket;
2264
2417
  //# sourceMappingURL=hocuspocus-provider.cjs.map