@hocuspocus/provider 1.0.2 → 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 (45) hide show
  1. package/dist/hocuspocus-provider.cjs +370 -209
  2. package/dist/hocuspocus-provider.cjs.map +1 -1
  3. package/dist/hocuspocus-provider.esm.js +370 -210
  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/extension-redis/src/Redis.d.ts +19 -5
  8. package/dist/packages/provider/src/HocuspocusCloudProvider.d.ts +2 -1
  9. package/dist/packages/provider/src/HocuspocusProvider.d.ts +14 -70
  10. package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +115 -0
  11. package/dist/packages/provider/src/IncomingMessage.d.ts +2 -0
  12. package/dist/packages/provider/src/OutgoingMessages/StatelessMessage.d.ts +7 -0
  13. package/dist/packages/provider/src/index.d.ts +1 -0
  14. package/dist/packages/provider/src/types.d.ts +7 -1
  15. package/dist/packages/server/src/Connection.d.ts +10 -11
  16. package/dist/packages/server/src/Document.d.ts +9 -0
  17. package/dist/packages/server/src/Hocuspocus.d.ts +3 -8
  18. package/dist/packages/server/src/IncomingMessage.d.ts +2 -0
  19. package/dist/packages/server/src/MessageReceiver.d.ts +1 -2
  20. package/dist/packages/server/src/OutgoingMessage.d.ts +3 -1
  21. package/dist/packages/server/src/types.d.ts +19 -3
  22. package/dist/packages/transformer/src/Prosemirror.d.ts +1 -1
  23. package/dist/tests/providerwebsocket/configuration.d.ts +1 -0
  24. package/dist/tests/server/beforeBroadcastStateless.d.ts +1 -0
  25. package/dist/tests/server/onStateless.d.ts +1 -0
  26. package/dist/tests/utils/index.d.ts +1 -0
  27. package/dist/tests/utils/newHocuspocusProvider.d.ts +2 -2
  28. package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +3 -0
  29. package/package.json +3 -3
  30. package/src/HocuspocusCloudProvider.ts +8 -1
  31. package/src/HocuspocusProvider.ts +111 -358
  32. package/src/HocuspocusProviderWebsocket.ts +475 -0
  33. package/src/IncomingMessage.ts +10 -0
  34. package/src/MessageReceiver.ts +9 -2
  35. package/src/OutgoingMessages/AuthenticationMessage.ts +2 -1
  36. package/src/OutgoingMessages/AwarenessMessage.ts +1 -0
  37. package/src/OutgoingMessages/QueryAwarenessMessage.ts +5 -0
  38. package/src/OutgoingMessages/StatelessMessage.ts +17 -0
  39. package/src/OutgoingMessages/SyncStepOneMessage.ts +1 -0
  40. package/src/OutgoingMessages/SyncStepTwoMessage.ts +1 -1
  41. package/src/OutgoingMessages/UpdateMessage.ts +3 -1
  42. package/src/index.ts +1 -0
  43. package/src/types.ts +7 -0
  44. /package/dist/tests/{provider/configuration.d.ts → extension-redis/onStateless.d.ts} +0 -0
  45. /package/dist/tests/{server/getDocumentName.d.ts → provider/onStateless.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
  }
@@ -1552,6 +1545,7 @@ exports.MessageType = void 0;
1552
1545
  MessageType[MessageType["Awareness"] = 1] = "Awareness";
1553
1546
  MessageType[MessageType["Auth"] = 2] = "Auth";
1554
1547
  MessageType[MessageType["QueryAwareness"] = 3] = "QueryAwareness";
1548
+ MessageType[MessageType["Stateless"] = 5] = "Stateless";
1555
1549
  })(exports.MessageType || (exports.MessageType = {}));
1556
1550
  exports.WebSocketStatus = void 0;
1557
1551
  (function (WebSocketStatus) {
@@ -1584,6 +1578,7 @@ class MessageReceiver {
1584
1578
  apply(provider, emitSynced = true) {
1585
1579
  const { message } = this;
1586
1580
  const type = message.readVarUint();
1581
+ const emptyMessageLength = message.length();
1587
1582
  switch (type) {
1588
1583
  case exports.MessageType.Sync:
1589
1584
  this.applySyncMessage(provider, emitSynced);
@@ -1597,11 +1592,14 @@ class MessageReceiver {
1597
1592
  case exports.MessageType.QueryAwareness:
1598
1593
  this.applyQueryAwarenessMessage(provider);
1599
1594
  break;
1595
+ case exports.MessageType.Stateless:
1596
+ provider.receiveStateless(readVarString(message.decoder));
1597
+ break;
1600
1598
  default:
1601
1599
  throw new Error(`Can’t apply message of unknown type: ${type}`);
1602
1600
  }
1603
1601
  // Reply
1604
- 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)
1605
1603
  if (this.broadcasted) {
1606
1604
  // TODO: Some weird TypeScript error
1607
1605
  // @ts-ignore
@@ -1620,7 +1618,7 @@ class MessageReceiver {
1620
1618
  // Apply update
1621
1619
  const syncMessageType = readSyncMessage(message.decoder, message.encoder, provider.document, provider);
1622
1620
  // Synced once we receive Step2
1623
- if (emitSynced && (syncMessageType === messageYjsSyncStep2)) {
1621
+ if (emitSynced && syncMessageType === messageYjsSyncStep2) {
1624
1622
  provider.synced = true;
1625
1623
  }
1626
1624
  if (syncMessageType === messageYjsUpdate || syncMessageType === messageYjsSyncStep2) {
@@ -1670,6 +1668,7 @@ class SyncStepOneMessage extends OutgoingMessage {
1670
1668
  if (typeof args.document === 'undefined') {
1671
1669
  throw new Error('The sync step one message requires document as an argument');
1672
1670
  }
1671
+ writeVarString(this.encoder, args.documentName);
1673
1672
  writeVarUint(this.encoder, this.type);
1674
1673
  writeSyncStep1(this.encoder, args.document);
1675
1674
  return this.encoder;
@@ -1686,6 +1685,7 @@ class SyncStepTwoMessage extends OutgoingMessage {
1686
1685
  if (typeof args.document === 'undefined') {
1687
1686
  throw new Error('The sync step two message requires document as an argument');
1688
1687
  }
1688
+ writeVarString(this.encoder, args.documentName);
1689
1689
  writeVarUint(this.encoder, this.type);
1690
1690
  writeSyncStep2(this.encoder, args.document);
1691
1691
  return this.encoder;
@@ -1699,6 +1699,9 @@ class QueryAwarenessMessage extends OutgoingMessage {
1699
1699
  this.description = 'Queries awareness states';
1700
1700
  }
1701
1701
  get(args) {
1702
+ console.log('queryAwareness: writing string docName', args.documentName);
1703
+ console.log(this.encoder.cpos);
1704
+ writeVarString(this.encoder, args.documentName);
1702
1705
  writeVarUint(this.encoder, this.type);
1703
1706
  return this.encoder;
1704
1707
  }
@@ -1714,6 +1717,7 @@ class AuthenticationMessage extends OutgoingMessage {
1714
1717
  if (typeof args.token === 'undefined') {
1715
1718
  throw new Error('The authentication message requires `token` as an argument.');
1716
1719
  }
1720
+ writeVarString(this.encoder, args.documentName);
1717
1721
  writeVarUint(this.encoder, this.type);
1718
1722
  common.writeAuthentication(this.encoder, args.token);
1719
1723
  return this.encoder;
@@ -1733,6 +1737,7 @@ class AwarenessMessage extends OutgoingMessage {
1733
1737
  if (typeof args.clients === 'undefined') {
1734
1738
  throw new Error('The awareness message requires clients as an argument');
1735
1739
  }
1740
+ writeVarString(this.encoder, args.documentName);
1736
1741
  writeVarUint(this.encoder, this.type);
1737
1742
  let awarenessUpdate;
1738
1743
  if (args.states === undefined) {
@@ -1753,24 +1758,329 @@ class UpdateMessage extends OutgoingMessage {
1753
1758
  this.description = 'A document update';
1754
1759
  }
1755
1760
  get(args) {
1761
+ writeVarString(this.encoder, args.documentName);
1756
1762
  writeVarUint(this.encoder, this.type);
1757
1763
  writeUpdate(this.encoder, args.update);
1758
1764
  return this.encoder;
1759
1765
  }
1760
1766
  }
1761
1767
 
1768
+ class StatelessMessage extends OutgoingMessage {
1769
+ constructor() {
1770
+ super(...arguments);
1771
+ this.type = exports.MessageType.Stateless;
1772
+ this.description = 'A stateless message';
1773
+ }
1774
+ get(args) {
1775
+ var _a;
1776
+ writeVarString(this.encoder, args.documentName);
1777
+ writeVarUint(this.encoder, this.type);
1778
+ writeVarString(this.encoder, (_a = args.payload) !== null && _a !== void 0 ? _a : '');
1779
+ return this.encoder;
1780
+ }
1781
+ }
1782
+
1762
1783
  class HocuspocusProvider extends EventEmitter {
1763
1784
  constructor(configuration) {
1764
1785
  super();
1765
1786
  this.configuration = {
1766
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 = {
1767
2078
  url: '',
1768
2079
  // @ts-ignore
1769
2080
  document: undefined,
1770
2081
  // @ts-ignore
1771
2082
  awareness: undefined,
1772
2083
  WebSocketPolyfill: undefined,
1773
- token: null,
1774
2084
  parameters: {},
1775
2085
  connect: true,
1776
2086
  broadcast: true,
@@ -1793,14 +2103,11 @@ class HocuspocusProvider extends EventEmitter {
1793
2103
  jitter: true,
1794
2104
  // retry forever
1795
2105
  timeout: 0,
1796
- onAuthenticated: () => null,
1797
- onAuthenticationFailed: () => null,
1798
2106
  onOpen: () => null,
1799
2107
  onConnect: () => null,
1800
2108
  onMessage: () => null,
1801
2109
  onOutgoingMessage: () => null,
1802
2110
  onStatus: () => null,
1803
- onSynced: () => null,
1804
2111
  onDisconnect: () => null,
1805
2112
  onClose: () => null,
1806
2113
  onDestroy: () => null,
@@ -1812,9 +2119,6 @@ class HocuspocusProvider extends EventEmitter {
1812
2119
  this.webSocket = null;
1813
2120
  this.shouldConnect = true;
1814
2121
  this.status = exports.WebSocketStatus.Disconnected;
1815
- this.isSynced = false;
1816
- this.unsyncedChanges = 0;
1817
- this.isAuthenticated = false;
1818
2122
  this.lastMessageReceived = 0;
1819
2123
  this.mux = createMutex();
1820
2124
  this.intervals = {
@@ -1822,39 +2126,27 @@ class HocuspocusProvider extends EventEmitter {
1822
2126
  connectionChecker: null,
1823
2127
  };
1824
2128
  this.connectionAttempt = null;
2129
+ this.receivedOnOpenPayload = undefined;
2130
+ this.receivedOnStatusPayload = undefined;
1825
2131
  this.boundConnect = this.connect.bind(this);
1826
- this.boundBeforeUnload = this.beforeUnload.bind(this);
1827
- this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
1828
2132
  this.setConfiguration(configuration);
1829
- this.configuration.document = configuration.document ? configuration.document : new Y__namespace.Doc();
1830
- this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document);
1831
2133
  this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket;
1832
2134
  this.on('open', this.configuration.onOpen);
1833
- this.on('authenticated', this.configuration.onAuthenticated);
1834
- this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
2135
+ this.on('open', this.onOpen.bind(this));
1835
2136
  this.on('connect', this.configuration.onConnect);
1836
2137
  this.on('message', this.configuration.onMessage);
1837
2138
  this.on('outgoingMessage', this.configuration.onOutgoingMessage);
1838
- this.on('synced', this.configuration.onSynced);
1839
2139
  this.on('status', this.configuration.onStatus);
2140
+ this.on('status', this.onStatus.bind(this));
1840
2141
  this.on('disconnect', this.configuration.onDisconnect);
1841
2142
  this.on('close', this.configuration.onClose);
1842
2143
  this.on('destroy', this.configuration.onDestroy);
1843
2144
  this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
1844
2145
  this.on('awarenessChange', this.configuration.onAwarenessChange);
1845
- this.awareness.on('update', () => {
1846
- this.emit('awarenessUpdate', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
1847
- });
1848
- this.awareness.on('change', () => {
1849
- this.emit('awarenessChange', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
1850
- });
1851
- this.document.on('update', this.documentUpdateHandler.bind(this));
1852
- this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
2146
+ this.on('close', this.onClose.bind(this));
2147
+ this.on('message', this.onMessage.bind(this));
1853
2148
  this.registerEventListeners();
1854
2149
  this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.configuration.messageReconnectTimeout / 10);
1855
- if (this.configuration.forceSyncInterval) {
1856
- this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
1857
- }
1858
2150
  if (typeof configuration.connect !== 'undefined') {
1859
2151
  this.shouldConnect = configuration.connect;
1860
2152
  }
@@ -1863,6 +2155,23 @@ class HocuspocusProvider extends EventEmitter {
1863
2155
  }
1864
2156
  this.connect();
1865
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
+ }
1866
2175
  setConfiguration(configuration = {}) {
1867
2176
  this.configuration = { ...this.configuration, ...configuration };
1868
2177
  }
@@ -1875,9 +2184,7 @@ class HocuspocusProvider extends EventEmitter {
1875
2184
  this.cancelWebsocketRetry();
1876
2185
  this.cancelWebsocketRetry = undefined;
1877
2186
  }
1878
- this.unsyncedChanges = 0; // set to 0 in case we got reconnected
1879
2187
  this.shouldConnect = true;
1880
- this.subscribeToBroadcastChannel();
1881
2188
  const abortableRetry = () => {
1882
2189
  let cancelAttempt = false;
1883
2190
  const retryPromise = attempt.retry(this.createWebSocketConnection.bind(this), {
@@ -1921,15 +2228,14 @@ class HocuspocusProvider extends EventEmitter {
1921
2228
  // Init the WebSocket connection
1922
2229
  const ws = new this.configuration.WebSocketPolyfill(this.url);
1923
2230
  ws.binaryType = 'arraybuffer';
1924
- ws.onmessage = this.onMessage.bind(this);
1925
- ws.onclose = this.onClose.bind(this);
1926
- 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);
1927
2234
  ws.onerror = (err) => {
1928
2235
  reject(err);
1929
2236
  };
1930
2237
  this.webSocket = ws;
1931
2238
  // Reset the status
1932
- this.synced = false;
1933
2239
  this.status = exports.WebSocketStatus.Connecting;
1934
2240
  this.emit('status', { status: exports.WebSocketStatus.Connecting });
1935
2241
  // Store resolve/reject for later use
@@ -1939,13 +2245,17 @@ class HocuspocusProvider extends EventEmitter {
1939
2245
  };
1940
2246
  });
1941
2247
  }
2248
+ onMessage(event) {
2249
+ this.resolveConnectionAttempt();
2250
+ }
1942
2251
  resolveConnectionAttempt() {
1943
- var _a;
1944
- (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.resolve();
1945
- this.connectionAttempt = null;
1946
- this.status = exports.WebSocketStatus.Connected;
1947
- this.emit('status', { status: exports.WebSocketStatus.Connected });
1948
- 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
+ }
1949
2259
  }
1950
2260
  stopConnectionAttempt() {
1951
2261
  this.connectionAttempt = null;
@@ -1955,15 +2265,6 @@ class HocuspocusProvider extends EventEmitter {
1955
2265
  (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
1956
2266
  this.connectionAttempt = null;
1957
2267
  }
1958
- get document() {
1959
- return this.configuration.document;
1960
- }
1961
- get awareness() {
1962
- return this.configuration.awareness;
1963
- }
1964
- get hasUnsyncedChanges() {
1965
- return this.unsyncedChanges > 0;
1966
- }
1967
2268
  checkConnection() {
1968
2269
  var _a;
1969
2270
  // Don’t check the connection when it’s not even established
@@ -1982,45 +2283,11 @@ class HocuspocusProvider extends EventEmitter {
1982
2283
  // Awareness updates, which are updated every 15 seconds.
1983
2284
  (_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.close();
1984
2285
  }
1985
- forceSync() {
1986
- if (!this.webSocket) {
1987
- return;
1988
- }
1989
- this.send(SyncStepOneMessage, { document: this.document });
1990
- }
1991
- beforeUnload() {
1992
- removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload');
1993
- }
1994
2286
  registerEventListeners() {
1995
2287
  if (typeof window === 'undefined') {
1996
2288
  return;
1997
2289
  }
1998
2290
  window.addEventListener('online', this.boundConnect);
1999
- window.addEventListener('beforeunload', this.boundBeforeUnload);
2000
- }
2001
- documentUpdateHandler(update, origin) {
2002
- if (origin === this) {
2003
- return;
2004
- }
2005
- this.unsyncedChanges += 1;
2006
- this.send(UpdateMessage, { update }, true);
2007
- }
2008
- awarenessUpdateHandler({ added, updated, removed }, origin) {
2009
- const changedClients = added.concat(updated).concat(removed);
2010
- this.send(AwarenessMessage, {
2011
- awareness: this.awareness,
2012
- clients: changedClients,
2013
- }, true);
2014
- }
2015
- permissionDeniedHandler(reason) {
2016
- this.emit('authenticationFailed', { reason });
2017
- this.isAuthenticated = false;
2018
- this.shouldConnect = false;
2019
- }
2020
- authenticatedHandler() {
2021
- this.isAuthenticated = true;
2022
- this.emit('authenticated');
2023
- this.startSync();
2024
2291
  }
2025
2292
  // Ensure that the URL always ends with /
2026
2293
  get serverUrl() {
@@ -2031,25 +2298,10 @@ class HocuspocusProvider extends EventEmitter {
2031
2298
  }
2032
2299
  get url() {
2033
2300
  const encodedParams = encodeQueryParams(this.configuration.parameters);
2034
- return `${this.serverUrl}/${this.configuration.name}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
2035
- }
2036
- get synced() {
2037
- return this.isSynced;
2038
- }
2039
- set synced(state) {
2040
- if (this.isSynced === state) {
2041
- return;
2042
- }
2043
- this.isSynced = state;
2044
- this.emit('synced', { state });
2045
- this.emit('sync', { state });
2046
- }
2047
- get isAuthenticationRequired() {
2048
- return !!this.configuration.token && !this.isAuthenticated;
2301
+ return `${this.serverUrl}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
2049
2302
  }
2050
2303
  disconnect() {
2051
2304
  this.shouldConnect = false;
2052
- this.disconnectBroadcastChannel();
2053
2305
  if (this.webSocket === null) {
2054
2306
  return;
2055
2307
  }
@@ -2060,58 +2312,15 @@ class HocuspocusProvider extends EventEmitter {
2060
2312
  //
2061
2313
  }
2062
2314
  }
2063
- async onOpen(event) {
2064
- this.emit('open', { event });
2065
- if (this.isAuthenticationRequired) {
2066
- this.send(AuthenticationMessage, {
2067
- token: await this.getToken(),
2068
- });
2069
- return;
2070
- }
2071
- this.startSync();
2072
- }
2073
- async getToken() {
2074
- if (typeof this.configuration.token === 'function') {
2075
- const token = await this.configuration.token();
2076
- return token;
2077
- }
2078
- return this.configuration.token;
2079
- }
2080
- startSync() {
2081
- this.send(SyncStepOneMessage, { document: this.document });
2082
- if (this.awareness.getLocalState() !== null) {
2083
- this.send(AwarenessMessage, {
2084
- awareness: this.awareness,
2085
- clients: [this.document.clientID],
2086
- });
2087
- }
2088
- }
2089
- send(Message, args, broadcast = false) {
2315
+ send(message) {
2090
2316
  var _a;
2091
- if (broadcast) {
2092
- this.mux(() => { this.broadcast(Message, args); });
2093
- }
2094
2317
  if (((_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.readyState) === common.WsReadyStates.Open) {
2095
- const messageSender = new MessageSender(Message, args);
2096
- this.emit('outgoingMessage', { message: messageSender.message });
2097
- messageSender.send(this.webSocket);
2318
+ this.webSocket.send(message);
2098
2319
  }
2099
2320
  }
2100
- onMessage(event) {
2101
- this.resolveConnectionAttempt();
2102
- this.lastMessageReceived = getUnixTime();
2103
- const message = new IncomingMessage(event.data);
2104
- this.emit('message', { event, message });
2105
- new MessageReceiver(message).apply(this);
2106
- }
2107
- onClose(event) {
2108
- this.emit('close', { event });
2321
+ onClose({ event }) {
2109
2322
  this.webSocket = null;
2110
- this.isAuthenticated = false;
2111
- this.synced = false;
2112
2323
  if (this.status === exports.WebSocketStatus.Connected) {
2113
- // update awareness (all users except local left)
2114
- removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID), this);
2115
2324
  this.status = exports.WebSocketStatus.Disconnected;
2116
2325
  this.emit('status', { status: exports.WebSocketStatus.Disconnected });
2117
2326
  this.emit('disconnect', { event });
@@ -2125,6 +2334,7 @@ class HocuspocusProvider extends EventEmitter {
2125
2334
  if (event.code === common.Forbidden.code) {
2126
2335
  if (!this.configuration.quiet) {
2127
2336
  console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.');
2337
+ return; // TODO REMOVE ME
2128
2338
  }
2129
2339
  }
2130
2340
  if (this.connectionAttempt) {
@@ -2154,67 +2364,16 @@ class HocuspocusProvider extends EventEmitter {
2154
2364
  clearInterval(this.intervals.forceSync);
2155
2365
  }
2156
2366
  clearInterval(this.intervals.connectionChecker);
2157
- removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
2158
2367
  // If there is still a connection attempt outstanding then we should stop
2159
2368
  // it before calling disconnect, otherwise it will be rejected in the onClose
2160
2369
  // handler and trigger a retry
2161
2370
  this.stopConnectionAttempt();
2162
2371
  this.disconnect();
2163
- this.awareness.off('update', this.awarenessUpdateHandler);
2164
- this.document.off('update', this.documentUpdateHandler);
2165
2372
  this.removeAllListeners();
2166
2373
  if (typeof window === 'undefined') {
2167
2374
  return;
2168
2375
  }
2169
2376
  window.removeEventListener('online', this.boundConnect);
2170
- window.removeEventListener('beforeunload', this.boundBeforeUnload);
2171
- }
2172
- get broadcastChannel() {
2173
- return `${this.serverUrl}/${this.configuration.name}`;
2174
- }
2175
- broadcastChannelSubscriber(data) {
2176
- this.mux(() => {
2177
- const message = new IncomingMessage(data);
2178
- new MessageReceiver(message)
2179
- .setBroadcasted(true)
2180
- .apply(this, false);
2181
- });
2182
- }
2183
- subscribeToBroadcastChannel() {
2184
- if (!this.subscribedToBroadcastChannel) {
2185
- subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2186
- this.subscribedToBroadcastChannel = true;
2187
- }
2188
- this.mux(() => {
2189
- this.broadcast(SyncStepOneMessage, { document: this.document });
2190
- this.broadcast(SyncStepTwoMessage, { document: this.document });
2191
- this.broadcast(QueryAwarenessMessage);
2192
- this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID] });
2193
- });
2194
- }
2195
- disconnectBroadcastChannel() {
2196
- // broadcast message with local awareness state set to null (indicating disconnect)
2197
- this.send(AwarenessMessage, {
2198
- awareness: this.awareness,
2199
- clients: [this.document.clientID],
2200
- states: new Map(),
2201
- }, true);
2202
- if (this.subscribedToBroadcastChannel) {
2203
- unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2204
- this.subscribedToBroadcastChannel = false;
2205
- }
2206
- }
2207
- broadcast(Message, args) {
2208
- if (!this.configuration.broadcast) {
2209
- return;
2210
- }
2211
- if (!this.subscribedToBroadcastChannel) {
2212
- return;
2213
- }
2214
- new MessageSender(Message, args).broadcast(this.broadcastChannel);
2215
- }
2216
- setAwarenessField(key, value) {
2217
- this.awareness.setLocalStateField(key, value);
2218
2377
  }
2219
2378
  }
2220
2379
 
@@ -2229,10 +2388,12 @@ class HocuspocusCloudProvider extends HocuspocusProvider {
2229
2388
  }
2230
2389
  configuration.parameters.key = configuration.key;
2231
2390
  }
2391
+ configuration.websocketProvider = new HocuspocusProviderWebsocket({ url: configuration.url });
2232
2392
  super(configuration);
2233
2393
  }
2234
2394
  }
2235
2395
 
2236
2396
  exports.HocuspocusCloudProvider = HocuspocusCloudProvider;
2237
2397
  exports.HocuspocusProvider = HocuspocusProvider;
2398
+ exports.HocuspocusProviderWebsocket = HocuspocusProviderWebsocket;
2238
2399
  //# sourceMappingURL=hocuspocus-provider.cjs.map