@hocuspocus/provider 2.1.0-alpha.0 → 2.1.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 (144) hide show
  1. package/dist/hocuspocus-provider.cjs +917 -915
  2. package/dist/hocuspocus-provider.cjs.map +1 -1
  3. package/dist/hocuspocus-provider.esm.js +917 -915
  4. package/dist/hocuspocus-provider.esm.js.map +1 -1
  5. package/dist/packages/common/src/CloseEvents.d.ts +29 -29
  6. package/dist/packages/common/src/auth.d.ts +6 -6
  7. package/dist/packages/common/src/awarenessStatesToArray.d.ts +3 -3
  8. package/dist/packages/common/src/index.d.ts +4 -4
  9. package/dist/packages/common/src/types.d.ts +10 -10
  10. package/dist/packages/extension-database/src/Database.d.ts +30 -30
  11. package/dist/packages/extension-database/src/index.d.ts +1 -1
  12. package/dist/packages/extension-logger/src/Logger.d.ts +67 -67
  13. package/dist/packages/extension-logger/src/index.d.ts +1 -1
  14. package/dist/packages/extension-redis/src/Redis.d.ts +116 -95
  15. package/dist/packages/extension-redis/src/index.d.ts +1 -1
  16. package/dist/packages/extension-sqlite/src/SQLite.d.ts +26 -26
  17. package/dist/packages/extension-sqlite/src/index.d.ts +1 -1
  18. package/dist/packages/extension-throttle/src/index.d.ts +31 -31
  19. package/dist/packages/extension-webhook/src/index.d.ts +57 -57
  20. package/dist/packages/provider/src/EventEmitter.d.ts +9 -9
  21. package/dist/packages/provider/src/HocuspocusProvider.d.ts +110 -110
  22. package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +115 -115
  23. package/dist/packages/provider/src/IncomingMessage.d.ts +16 -16
  24. package/dist/packages/provider/src/MessageReceiver.d.ts +13 -13
  25. package/dist/packages/provider/src/MessageSender.d.ts +10 -10
  26. package/dist/packages/provider/src/OutgoingMessage.d.ts +9 -9
  27. package/dist/packages/provider/src/OutgoingMessages/AuthenticationMessage.d.ts +7 -7
  28. package/dist/packages/provider/src/OutgoingMessages/AwarenessMessage.d.ts +8 -8
  29. package/dist/packages/provider/src/OutgoingMessages/CloseMessage.d.ts +8 -8
  30. package/dist/packages/provider/src/OutgoingMessages/QueryAwarenessMessage.d.ts +8 -8
  31. package/dist/packages/provider/src/OutgoingMessages/StatelessMessage.d.ts +7 -7
  32. package/dist/packages/provider/src/OutgoingMessages/SyncStepOneMessage.d.ts +8 -8
  33. package/dist/packages/provider/src/OutgoingMessages/SyncStepTwoMessage.d.ts +8 -8
  34. package/dist/packages/provider/src/OutgoingMessages/UpdateMessage.d.ts +7 -7
  35. package/dist/packages/provider/src/TiptapCollabProvider.d.ts +11 -11
  36. package/dist/packages/provider/src/TiptapCollabProviderWebsocket.d.ts +11 -11
  37. package/dist/packages/provider/src/index.d.ts +5 -5
  38. package/dist/packages/provider/src/types.d.ts +84 -84
  39. package/dist/packages/server/src/Connection.d.ts +71 -71
  40. package/dist/packages/server/src/Debugger.d.ts +14 -14
  41. package/dist/packages/server/src/DirectConnection.d.ts +13 -0
  42. package/dist/packages/server/src/Document.d.ts +91 -88
  43. package/dist/packages/server/src/Hocuspocus.d.ts +111 -108
  44. package/dist/packages/server/src/IncomingMessage.d.ts +21 -21
  45. package/dist/packages/server/src/MessageReceiver.d.ts +12 -12
  46. package/dist/packages/server/src/OutgoingMessage.d.ts +20 -20
  47. package/dist/packages/server/src/index.d.ts +8 -8
  48. package/dist/packages/server/src/types.d.ts +266 -264
  49. package/dist/packages/transformer/src/Prosemirror.d.ts +11 -11
  50. package/dist/packages/transformer/src/Tiptap.d.ts +10 -10
  51. package/dist/packages/transformer/src/index.d.ts +3 -3
  52. package/dist/packages/transformer/src/types.d.ts +5 -5
  53. package/dist/playground/backend/src/default.d.ts +1 -1
  54. package/dist/playground/backend/src/express.d.ts +1 -1
  55. package/dist/playground/backend/src/koa.d.ts +1 -1
  56. package/dist/playground/backend/src/load-document.d.ts +1 -1
  57. package/dist/playground/backend/src/redis.d.ts +1 -1
  58. package/dist/playground/backend/src/slow.d.ts +1 -1
  59. package/dist/playground/backend/src/tiptapcollab.d.ts +1 -1
  60. package/dist/playground/backend/src/webhook.d.ts +1 -1
  61. package/dist/playground/frontend/src/main.d.ts +1 -1
  62. package/dist/playground/frontend/vite.config.d.ts +2 -2
  63. package/dist/tests/extension-database/fetch.d.ts +1 -1
  64. package/dist/tests/extension-logger/onListen.d.ts +1 -1
  65. package/dist/tests/extension-redis/closeConnections.d.ts +1 -1
  66. package/dist/tests/extension-redis/getConnectionCount.d.ts +1 -1
  67. package/dist/tests/extension-redis/getDocumentsCount.d.ts +1 -1
  68. package/dist/tests/extension-redis/onAwarenessChange.d.ts +1 -1
  69. package/dist/tests/extension-redis/onChange.d.ts +1 -1
  70. package/dist/tests/extension-redis/onStateless.d.ts +1 -1
  71. package/dist/tests/extension-redis/onStoreDocument.d.ts +1 -1
  72. package/dist/tests/extension-throttle/banning.d.ts +1 -1
  73. package/dist/tests/extension-throttle/configuration.d.ts +1 -1
  74. package/dist/tests/provider/observe.d.ts +1 -1
  75. package/dist/tests/provider/observeDeep.d.ts +1 -1
  76. package/dist/tests/provider/onAuthenticated.d.ts +1 -1
  77. package/dist/tests/provider/onAuthenticationFailed.d.ts +1 -1
  78. package/dist/tests/provider/onAwarenessChange.d.ts +1 -1
  79. package/dist/tests/provider/onAwarenessUpdate.d.ts +1 -1
  80. package/dist/tests/provider/onClose.d.ts +1 -1
  81. package/dist/tests/provider/onConnect.d.ts +1 -1
  82. package/dist/tests/provider/onDisconnect.d.ts +1 -1
  83. package/dist/tests/provider/onMessage.d.ts +1 -1
  84. package/dist/tests/provider/onOpen.d.ts +1 -1
  85. package/dist/tests/provider/onStateless.d.ts +1 -1
  86. package/dist/tests/provider/onSynced.d.ts +1 -1
  87. package/dist/tests/providerwebsocket/configuration.d.ts +1 -1
  88. package/dist/tests/server/address.d.ts +1 -1
  89. package/dist/tests/server/afterStoreDocument.d.ts +1 -1
  90. package/dist/tests/server/beforeBroadcastStateless.d.ts +1 -1
  91. package/dist/tests/server/beforeHandleMessage.d.ts +1 -1
  92. package/dist/tests/server/closeConnections.d.ts +1 -1
  93. package/dist/tests/server/getConnectionsCount.d.ts +1 -1
  94. package/dist/tests/server/getDocumentsCount.d.ts +1 -1
  95. package/dist/tests/server/getMessageLogs.d.ts +1 -1
  96. package/dist/tests/server/listen.d.ts +1 -1
  97. package/dist/tests/server/onAuthenticate.d.ts +1 -1
  98. package/dist/tests/server/onAwarenessUpdate.d.ts +1 -1
  99. package/dist/tests/server/onChange.d.ts +1 -1
  100. package/dist/tests/server/onClose.d.ts +1 -1
  101. package/dist/tests/server/onConfigure.d.ts +1 -1
  102. package/dist/tests/server/onConnect.d.ts +1 -1
  103. package/dist/tests/server/onDestroy.d.ts +1 -1
  104. package/dist/tests/server/onDisconnect.d.ts +1 -1
  105. package/dist/tests/server/onListen.d.ts +1 -1
  106. package/dist/tests/server/onLoadDocument.d.ts +1 -1
  107. package/dist/tests/server/onRequest.d.ts +1 -1
  108. package/dist/tests/server/onStateless.d.ts +1 -1
  109. package/dist/tests/server/onStoreDocument.d.ts +1 -1
  110. package/dist/tests/server/onUpgrade.d.ts +1 -1
  111. package/dist/tests/server/openDirectConnection.d.ts +1 -0
  112. package/dist/tests/server/requiresAuthentication.d.ts +1 -1
  113. package/dist/tests/server/websocketError.d.ts +1 -1
  114. package/dist/tests/transformer/TiptapTransformer.d.ts +1 -1
  115. package/dist/tests/utils/createDirectory.d.ts +1 -1
  116. package/dist/tests/utils/flushRedis.d.ts +1 -1
  117. package/dist/tests/utils/index.d.ts +9 -9
  118. package/dist/tests/utils/newHocuspocus.d.ts +2 -2
  119. package/dist/tests/utils/newHocuspocusProvider.d.ts +3 -3
  120. package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +3 -3
  121. package/dist/tests/utils/randomInteger.d.ts +1 -1
  122. package/dist/tests/utils/redisConnectionSettings.d.ts +4 -4
  123. package/dist/tests/utils/removeDirectory.d.ts +1 -1
  124. package/dist/tests/utils/retryableAssertion.d.ts +2 -2
  125. package/dist/tests/utils/sleep.d.ts +1 -1
  126. package/package.json +4 -3
  127. package/src/HocuspocusProvider.ts +20 -16
  128. package/src/HocuspocusProviderWebsocket.ts +4 -3
  129. package/src/IncomingMessage.ts +1 -1
  130. package/src/MessageReceiver.ts +4 -4
  131. package/src/MessageSender.ts +1 -1
  132. package/src/OutgoingMessage.ts +1 -1
  133. package/src/OutgoingMessages/AuthenticationMessage.ts +2 -2
  134. package/src/OutgoingMessages/AwarenessMessage.ts +2 -2
  135. package/src/OutgoingMessages/CloseMessage.ts +2 -2
  136. package/src/OutgoingMessages/QueryAwarenessMessage.ts +2 -2
  137. package/src/OutgoingMessages/StatelessMessage.ts +2 -2
  138. package/src/OutgoingMessages/SyncStepOneMessage.ts +2 -2
  139. package/src/OutgoingMessages/SyncStepTwoMessage.ts +2 -2
  140. package/src/OutgoingMessages/UpdateMessage.ts +2 -2
  141. package/src/TiptapCollabProvider.ts +2 -2
  142. package/src/TiptapCollabProviderWebsocket.ts +1 -1
  143. package/src/index.ts +5 -5
  144. package/src/types.ts +8 -8
@@ -1530,68 +1530,68 @@ const createMutex = () => {
1530
1530
  }
1531
1531
  };
1532
1532
 
1533
- class EventEmitter {
1534
- constructor() {
1535
- this.callbacks = {};
1536
- }
1537
- on(event, fn) {
1538
- if (!this.callbacks[event]) {
1539
- this.callbacks[event] = [];
1540
- }
1541
- this.callbacks[event].push(fn);
1542
- return this;
1543
- }
1544
- emit(event, ...args) {
1545
- const callbacks = this.callbacks[event];
1546
- if (callbacks) {
1547
- callbacks.forEach(callback => callback.apply(this, args));
1548
- }
1549
- return this;
1550
- }
1551
- off(event, fn) {
1552
- const callbacks = this.callbacks[event];
1553
- if (callbacks) {
1554
- if (fn) {
1555
- this.callbacks[event] = callbacks.filter(callback => callback !== fn);
1556
- }
1557
- else {
1558
- delete this.callbacks[event];
1559
- }
1560
- }
1561
- return this;
1562
- }
1563
- removeAllListeners() {
1564
- this.callbacks = {};
1565
- }
1533
+ class EventEmitter {
1534
+ constructor() {
1535
+ this.callbacks = {};
1536
+ }
1537
+ on(event, fn) {
1538
+ if (!this.callbacks[event]) {
1539
+ this.callbacks[event] = [];
1540
+ }
1541
+ this.callbacks[event].push(fn);
1542
+ return this;
1543
+ }
1544
+ emit(event, ...args) {
1545
+ const callbacks = this.callbacks[event];
1546
+ if (callbacks) {
1547
+ callbacks.forEach(callback => callback.apply(this, args));
1548
+ }
1549
+ return this;
1550
+ }
1551
+ off(event, fn) {
1552
+ const callbacks = this.callbacks[event];
1553
+ if (callbacks) {
1554
+ if (fn) {
1555
+ this.callbacks[event] = callbacks.filter(callback => callback !== fn);
1556
+ }
1557
+ else {
1558
+ delete this.callbacks[event];
1559
+ }
1560
+ }
1561
+ return this;
1562
+ }
1563
+ removeAllListeners() {
1564
+ this.callbacks = {};
1565
+ }
1566
1566
  }
1567
1567
 
1568
- class IncomingMessage {
1569
- constructor(data) {
1570
- this.data = data;
1571
- this.encoder = createEncoder();
1572
- this.decoder = createDecoder(new Uint8Array(this.data));
1573
- }
1574
- readVarUint() {
1575
- return readVarUint(this.decoder);
1576
- }
1577
- readVarString() {
1578
- return readVarString(this.decoder);
1579
- }
1580
- readVarUint8Array() {
1581
- return readVarUint8Array(this.decoder);
1582
- }
1583
- writeVarUint(type) {
1584
- return writeVarUint(this.encoder, type);
1585
- }
1586
- writeVarString(string) {
1587
- return writeVarString(this.encoder, string);
1588
- }
1589
- writeVarUint8Array(data) {
1590
- return writeVarUint8Array(this.encoder, data);
1591
- }
1592
- length() {
1593
- return length(this.encoder);
1594
- }
1568
+ class IncomingMessage {
1569
+ constructor(data) {
1570
+ this.data = data;
1571
+ this.encoder = createEncoder();
1572
+ this.decoder = createDecoder(new Uint8Array(this.data));
1573
+ }
1574
+ readVarUint() {
1575
+ return readVarUint(this.decoder);
1576
+ }
1577
+ readVarString() {
1578
+ return readVarString(this.decoder);
1579
+ }
1580
+ readVarUint8Array() {
1581
+ return readVarUint8Array(this.decoder);
1582
+ }
1583
+ writeVarUint(type) {
1584
+ return writeVarUint(this.encoder, type);
1585
+ }
1586
+ writeVarString(string) {
1587
+ return writeVarString(this.encoder, string);
1588
+ }
1589
+ writeVarUint8Array(data) {
1590
+ return writeVarUint8Array(this.encoder, data);
1591
+ }
1592
+ length() {
1593
+ return length(this.encoder);
1594
+ }
1595
1595
  }
1596
1596
 
1597
1597
  /**
@@ -1721,231 +1721,231 @@ const readSyncMessage = (decoder, encoder, doc, transactionOrigin) => {
1721
1721
  return messageType
1722
1722
  };
1723
1723
 
1724
- exports.MessageType = void 0;
1725
- (function (MessageType) {
1726
- MessageType[MessageType["Sync"] = 0] = "Sync";
1727
- MessageType[MessageType["Awareness"] = 1] = "Awareness";
1728
- MessageType[MessageType["Auth"] = 2] = "Auth";
1729
- MessageType[MessageType["QueryAwareness"] = 3] = "QueryAwareness";
1730
- MessageType[MessageType["Stateless"] = 5] = "Stateless";
1731
- MessageType[MessageType["CLOSE"] = 7] = "CLOSE";
1732
- })(exports.MessageType || (exports.MessageType = {}));
1733
- exports.WebSocketStatus = void 0;
1734
- (function (WebSocketStatus) {
1735
- WebSocketStatus["Connecting"] = "connecting";
1736
- WebSocketStatus["Connected"] = "connected";
1737
- WebSocketStatus["Disconnected"] = "disconnected";
1724
+ exports.MessageType = void 0;
1725
+ (function (MessageType) {
1726
+ MessageType[MessageType["Sync"] = 0] = "Sync";
1727
+ MessageType[MessageType["Awareness"] = 1] = "Awareness";
1728
+ MessageType[MessageType["Auth"] = 2] = "Auth";
1729
+ MessageType[MessageType["QueryAwareness"] = 3] = "QueryAwareness";
1730
+ MessageType[MessageType["Stateless"] = 5] = "Stateless";
1731
+ MessageType[MessageType["CLOSE"] = 7] = "CLOSE";
1732
+ })(exports.MessageType || (exports.MessageType = {}));
1733
+ exports.WebSocketStatus = void 0;
1734
+ (function (WebSocketStatus) {
1735
+ WebSocketStatus["Connecting"] = "connecting";
1736
+ WebSocketStatus["Connected"] = "connected";
1737
+ WebSocketStatus["Disconnected"] = "disconnected";
1738
1738
  })(exports.WebSocketStatus || (exports.WebSocketStatus = {}));
1739
1739
 
1740
- class OutgoingMessage {
1741
- constructor() {
1742
- this.encoder = createEncoder();
1743
- }
1744
- get(args) {
1745
- return args.encoder;
1746
- }
1747
- toUint8Array() {
1748
- return toUint8Array(this.encoder);
1749
- }
1740
+ class OutgoingMessage {
1741
+ constructor() {
1742
+ this.encoder = createEncoder();
1743
+ }
1744
+ get(args) {
1745
+ return args.encoder;
1746
+ }
1747
+ toUint8Array() {
1748
+ return toUint8Array(this.encoder);
1749
+ }
1750
1750
  }
1751
1751
 
1752
- class MessageReceiver {
1753
- constructor(message) {
1754
- this.broadcasted = false;
1755
- this.message = message;
1756
- }
1757
- setBroadcasted(value) {
1758
- this.broadcasted = value;
1759
- return this;
1760
- }
1761
- apply(provider, emitSynced = true) {
1762
- const { message } = this;
1763
- const type = message.readVarUint();
1764
- const emptyMessageLength = message.length();
1765
- switch (type) {
1766
- case exports.MessageType.Sync:
1767
- this.applySyncMessage(provider, emitSynced);
1768
- break;
1769
- case exports.MessageType.Awareness:
1770
- this.applyAwarenessMessage(provider);
1771
- break;
1772
- case exports.MessageType.Auth:
1773
- this.applyAuthMessage(provider);
1774
- break;
1775
- case exports.MessageType.QueryAwareness:
1776
- this.applyQueryAwarenessMessage(provider);
1777
- break;
1778
- case exports.MessageType.Stateless:
1779
- provider.receiveStateless(readVarString(message.decoder));
1780
- break;
1781
- default:
1782
- throw new Error(`Can’t apply message of unknown type: ${type}`);
1783
- }
1784
- // Reply
1785
- if (message.length() > emptyMessageLength + 1) { // length of documentName (considered in emptyMessageLength plus length of yjs sync type, set in applySyncMessage)
1786
- if (this.broadcasted) {
1787
- // TODO: Some weird TypeScript error
1788
- // @ts-ignore
1789
- provider.broadcast(OutgoingMessage, { encoder: message.encoder });
1790
- }
1791
- else {
1792
- // TODO: Some weird TypeScript error
1793
- // @ts-ignore
1794
- provider.send(OutgoingMessage, { encoder: message.encoder });
1795
- }
1796
- }
1797
- }
1798
- applySyncMessage(provider, emitSynced) {
1799
- const { message } = this;
1800
- message.writeVarUint(exports.MessageType.Sync);
1801
- // Apply update
1802
- const syncMessageType = readSyncMessage(message.decoder, message.encoder, provider.document, provider);
1803
- // Synced once we receive Step2
1804
- if (emitSynced && syncMessageType === messageYjsSyncStep2) {
1805
- provider.synced = true;
1806
- }
1807
- if (syncMessageType === messageYjsUpdate || syncMessageType === messageYjsSyncStep2) {
1808
- if (provider.unsyncedChanges > 0) {
1809
- provider.updateUnsyncedChanges(-1);
1810
- }
1811
- }
1812
- }
1813
- applyAwarenessMessage(provider) {
1814
- const { message } = this;
1815
- applyAwarenessUpdate(provider.awareness, message.readVarUint8Array(), provider);
1816
- }
1817
- applyAuthMessage(provider) {
1818
- const { message } = this;
1819
- common.readAuthMessage(message.decoder, provider.permissionDeniedHandler.bind(provider), provider.authenticatedHandler.bind(provider));
1820
- }
1821
- applyQueryAwarenessMessage(provider) {
1822
- const { message } = this;
1823
- message.writeVarUint(exports.MessageType.Awareness);
1824
- message.writeVarUint8Array(encodeAwarenessUpdate(provider.awareness, Array.from(provider.awareness.getStates().keys())));
1825
- }
1752
+ class MessageReceiver {
1753
+ constructor(message) {
1754
+ this.broadcasted = false;
1755
+ this.message = message;
1756
+ }
1757
+ setBroadcasted(value) {
1758
+ this.broadcasted = value;
1759
+ return this;
1760
+ }
1761
+ apply(provider, emitSynced = true) {
1762
+ const { message } = this;
1763
+ const type = message.readVarUint();
1764
+ const emptyMessageLength = message.length();
1765
+ switch (type) {
1766
+ case exports.MessageType.Sync:
1767
+ this.applySyncMessage(provider, emitSynced);
1768
+ break;
1769
+ case exports.MessageType.Awareness:
1770
+ this.applyAwarenessMessage(provider);
1771
+ break;
1772
+ case exports.MessageType.Auth:
1773
+ this.applyAuthMessage(provider);
1774
+ break;
1775
+ case exports.MessageType.QueryAwareness:
1776
+ this.applyQueryAwarenessMessage(provider);
1777
+ break;
1778
+ case exports.MessageType.Stateless:
1779
+ provider.receiveStateless(readVarString(message.decoder));
1780
+ break;
1781
+ default:
1782
+ throw new Error(`Can’t apply message of unknown type: ${type}`);
1783
+ }
1784
+ // Reply
1785
+ if (message.length() > emptyMessageLength + 1) { // length of documentName (considered in emptyMessageLength plus length of yjs sync type, set in applySyncMessage)
1786
+ if (this.broadcasted) {
1787
+ // TODO: Some weird TypeScript error
1788
+ // @ts-ignore
1789
+ provider.broadcast(OutgoingMessage, { encoder: message.encoder });
1790
+ }
1791
+ else {
1792
+ // TODO: Some weird TypeScript error
1793
+ // @ts-ignore
1794
+ provider.send(OutgoingMessage, { encoder: message.encoder });
1795
+ }
1796
+ }
1797
+ }
1798
+ applySyncMessage(provider, emitSynced) {
1799
+ const { message } = this;
1800
+ message.writeVarUint(exports.MessageType.Sync);
1801
+ // Apply update
1802
+ const syncMessageType = readSyncMessage(message.decoder, message.encoder, provider.document, provider);
1803
+ // Synced once we receive Step2
1804
+ if (emitSynced && syncMessageType === messageYjsSyncStep2) {
1805
+ provider.synced = true;
1806
+ }
1807
+ if (syncMessageType === messageYjsUpdate || syncMessageType === messageYjsSyncStep2) {
1808
+ if (provider.unsyncedChanges > 0) {
1809
+ provider.updateUnsyncedChanges(-1);
1810
+ }
1811
+ }
1812
+ }
1813
+ applyAwarenessMessage(provider) {
1814
+ const { message } = this;
1815
+ applyAwarenessUpdate(provider.awareness, message.readVarUint8Array(), provider);
1816
+ }
1817
+ applyAuthMessage(provider) {
1818
+ const { message } = this;
1819
+ common.readAuthMessage(message.decoder, provider.permissionDeniedHandler.bind(provider), provider.authenticatedHandler.bind(provider));
1820
+ }
1821
+ applyQueryAwarenessMessage(provider) {
1822
+ const { message } = this;
1823
+ message.writeVarUint(exports.MessageType.Awareness);
1824
+ message.writeVarUint8Array(encodeAwarenessUpdate(provider.awareness, Array.from(provider.awareness.getStates().keys())));
1825
+ }
1826
1826
  }
1827
1827
 
1828
- class MessageSender {
1829
- constructor(Message, args = {}) {
1830
- this.message = new Message();
1831
- this.encoder = this.message.get(args);
1832
- }
1833
- create() {
1834
- return toUint8Array(this.encoder);
1835
- }
1836
- send(webSocket) {
1837
- webSocket === null || webSocket === void 0 ? void 0 : webSocket.send(this.create());
1838
- }
1839
- broadcast(channel) {
1840
- publish(channel, this.create());
1841
- }
1828
+ class MessageSender {
1829
+ constructor(Message, args = {}) {
1830
+ this.message = new Message();
1831
+ this.encoder = this.message.get(args);
1832
+ }
1833
+ create() {
1834
+ return toUint8Array(this.encoder);
1835
+ }
1836
+ send(webSocket) {
1837
+ webSocket === null || webSocket === void 0 ? void 0 : webSocket.send(this.create());
1838
+ }
1839
+ broadcast(channel) {
1840
+ publish(channel, this.create());
1841
+ }
1842
1842
  }
1843
1843
 
1844
- class SyncStepOneMessage extends OutgoingMessage {
1845
- constructor() {
1846
- super(...arguments);
1847
- this.type = exports.MessageType.Sync;
1848
- this.description = 'First sync step';
1849
- }
1850
- get(args) {
1851
- if (typeof args.document === 'undefined') {
1852
- throw new Error('The sync step one message requires document as an argument');
1853
- }
1854
- writeVarString(this.encoder, args.documentName);
1855
- writeVarUint(this.encoder, this.type);
1856
- writeSyncStep1(this.encoder, args.document);
1857
- return this.encoder;
1858
- }
1844
+ class SyncStepOneMessage extends OutgoingMessage {
1845
+ constructor() {
1846
+ super(...arguments);
1847
+ this.type = exports.MessageType.Sync;
1848
+ this.description = 'First sync step';
1849
+ }
1850
+ get(args) {
1851
+ if (typeof args.document === 'undefined') {
1852
+ throw new Error('The sync step one message requires document as an argument');
1853
+ }
1854
+ writeVarString(this.encoder, args.documentName);
1855
+ writeVarUint(this.encoder, this.type);
1856
+ writeSyncStep1(this.encoder, args.document);
1857
+ return this.encoder;
1858
+ }
1859
1859
  }
1860
1860
 
1861
- class SyncStepTwoMessage extends OutgoingMessage {
1862
- constructor() {
1863
- super(...arguments);
1864
- this.type = exports.MessageType.Sync;
1865
- this.description = 'Second sync step';
1866
- }
1867
- get(args) {
1868
- if (typeof args.document === 'undefined') {
1869
- throw new Error('The sync step two message requires document as an argument');
1870
- }
1871
- writeVarString(this.encoder, args.documentName);
1872
- writeVarUint(this.encoder, this.type);
1873
- writeSyncStep2(this.encoder, args.document);
1874
- return this.encoder;
1875
- }
1861
+ class SyncStepTwoMessage extends OutgoingMessage {
1862
+ constructor() {
1863
+ super(...arguments);
1864
+ this.type = exports.MessageType.Sync;
1865
+ this.description = 'Second sync step';
1866
+ }
1867
+ get(args) {
1868
+ if (typeof args.document === 'undefined') {
1869
+ throw new Error('The sync step two message requires document as an argument');
1870
+ }
1871
+ writeVarString(this.encoder, args.documentName);
1872
+ writeVarUint(this.encoder, this.type);
1873
+ writeSyncStep2(this.encoder, args.document);
1874
+ return this.encoder;
1875
+ }
1876
1876
  }
1877
1877
 
1878
- class QueryAwarenessMessage extends OutgoingMessage {
1879
- constructor() {
1880
- super(...arguments);
1881
- this.type = exports.MessageType.QueryAwareness;
1882
- this.description = 'Queries awareness states';
1883
- }
1884
- get(args) {
1885
- console.log('queryAwareness: writing string docName', args.documentName);
1886
- console.log(this.encoder.cpos);
1887
- writeVarString(this.encoder, args.documentName);
1888
- writeVarUint(this.encoder, this.type);
1889
- return this.encoder;
1890
- }
1878
+ class QueryAwarenessMessage extends OutgoingMessage {
1879
+ constructor() {
1880
+ super(...arguments);
1881
+ this.type = exports.MessageType.QueryAwareness;
1882
+ this.description = 'Queries awareness states';
1883
+ }
1884
+ get(args) {
1885
+ console.log('queryAwareness: writing string docName', args.documentName);
1886
+ console.log(this.encoder.cpos);
1887
+ writeVarString(this.encoder, args.documentName);
1888
+ writeVarUint(this.encoder, this.type);
1889
+ return this.encoder;
1890
+ }
1891
1891
  }
1892
1892
 
1893
- class AuthenticationMessage extends OutgoingMessage {
1894
- constructor() {
1895
- super(...arguments);
1896
- this.type = exports.MessageType.Auth;
1897
- this.description = 'Authentication';
1898
- }
1899
- get(args) {
1900
- if (typeof args.token === 'undefined') {
1901
- throw new Error('The authentication message requires `token` as an argument.');
1902
- }
1903
- writeVarString(this.encoder, args.documentName);
1904
- writeVarUint(this.encoder, this.type);
1905
- common.writeAuthentication(this.encoder, args.token);
1906
- return this.encoder;
1907
- }
1893
+ class AuthenticationMessage extends OutgoingMessage {
1894
+ constructor() {
1895
+ super(...arguments);
1896
+ this.type = exports.MessageType.Auth;
1897
+ this.description = 'Authentication';
1898
+ }
1899
+ get(args) {
1900
+ if (typeof args.token === 'undefined') {
1901
+ throw new Error('The authentication message requires `token` as an argument.');
1902
+ }
1903
+ writeVarString(this.encoder, args.documentName);
1904
+ writeVarUint(this.encoder, this.type);
1905
+ common.writeAuthentication(this.encoder, args.token);
1906
+ return this.encoder;
1907
+ }
1908
1908
  }
1909
1909
 
1910
- class AwarenessMessage extends OutgoingMessage {
1911
- constructor() {
1912
- super(...arguments);
1913
- this.type = exports.MessageType.Awareness;
1914
- this.description = 'Awareness states update';
1915
- }
1916
- get(args) {
1917
- if (typeof args.awareness === 'undefined') {
1918
- throw new Error('The awareness message requires awareness as an argument');
1919
- }
1920
- if (typeof args.clients === 'undefined') {
1921
- throw new Error('The awareness message requires clients as an argument');
1922
- }
1923
- writeVarString(this.encoder, args.documentName);
1924
- writeVarUint(this.encoder, this.type);
1925
- let awarenessUpdate;
1926
- if (args.states === undefined) {
1927
- awarenessUpdate = encodeAwarenessUpdate(args.awareness, args.clients);
1928
- }
1929
- else {
1930
- awarenessUpdate = encodeAwarenessUpdate(args.awareness, args.clients, args.states);
1931
- }
1932
- writeVarUint8Array(this.encoder, awarenessUpdate);
1933
- return this.encoder;
1934
- }
1910
+ class AwarenessMessage extends OutgoingMessage {
1911
+ constructor() {
1912
+ super(...arguments);
1913
+ this.type = exports.MessageType.Awareness;
1914
+ this.description = 'Awareness states update';
1915
+ }
1916
+ get(args) {
1917
+ if (typeof args.awareness === 'undefined') {
1918
+ throw new Error('The awareness message requires awareness as an argument');
1919
+ }
1920
+ if (typeof args.clients === 'undefined') {
1921
+ throw new Error('The awareness message requires clients as an argument');
1922
+ }
1923
+ writeVarString(this.encoder, args.documentName);
1924
+ writeVarUint(this.encoder, this.type);
1925
+ let awarenessUpdate;
1926
+ if (args.states === undefined) {
1927
+ awarenessUpdate = encodeAwarenessUpdate(args.awareness, args.clients);
1928
+ }
1929
+ else {
1930
+ awarenessUpdate = encodeAwarenessUpdate(args.awareness, args.clients, args.states);
1931
+ }
1932
+ writeVarUint8Array(this.encoder, awarenessUpdate);
1933
+ return this.encoder;
1934
+ }
1935
1935
  }
1936
1936
 
1937
- class UpdateMessage extends OutgoingMessage {
1938
- constructor() {
1939
- super(...arguments);
1940
- this.type = exports.MessageType.Sync;
1941
- this.description = 'A document update';
1942
- }
1943
- get(args) {
1944
- writeVarString(this.encoder, args.documentName);
1945
- writeVarUint(this.encoder, this.type);
1946
- writeUpdate(this.encoder, args.update);
1947
- return this.encoder;
1948
- }
1937
+ class UpdateMessage extends OutgoingMessage {
1938
+ constructor() {
1939
+ super(...arguments);
1940
+ this.type = exports.MessageType.Sync;
1941
+ this.description = 'A document update';
1942
+ }
1943
+ get(args) {
1944
+ writeVarString(this.encoder, args.documentName);
1945
+ writeVarUint(this.encoder, this.type);
1946
+ writeUpdate(this.encoder, args.update);
1947
+ return this.encoder;
1948
+ }
1949
1949
  }
1950
1950
 
1951
1951
  /**
@@ -1961,664 +1961,666 @@ class UpdateMessage extends OutgoingMessage {
1961
1961
  const encodeQueryParams = params =>
1962
1962
  map(params, (val, key) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&');
1963
1963
 
1964
- class HocuspocusProviderWebsocket extends EventEmitter {
1965
- constructor(configuration) {
1966
- super();
1967
- this.configuration = {
1968
- url: '',
1969
- // @ts-ignore
1970
- document: undefined,
1971
- // @ts-ignore
1972
- awareness: undefined,
1973
- WebSocketPolyfill: undefined,
1974
- parameters: {},
1975
- connect: true,
1976
- broadcast: true,
1977
- forceSyncInterval: false,
1978
- // TODO: this should depend on awareness.outdatedTime
1979
- messageReconnectTimeout: 30000,
1980
- // 1 second
1981
- delay: 1000,
1982
- // instant
1983
- initialDelay: 0,
1984
- // double the delay each time
1985
- factor: 2,
1986
- // unlimited retries
1987
- maxAttempts: 0,
1988
- // wait at least 1 second
1989
- minDelay: 1000,
1990
- // at least every 30 seconds
1991
- maxDelay: 30000,
1992
- // randomize
1993
- jitter: true,
1994
- // retry forever
1995
- timeout: 0,
1996
- onOpen: () => null,
1997
- onConnect: () => null,
1998
- onMessage: () => null,
1999
- onOutgoingMessage: () => null,
2000
- onStatus: () => null,
2001
- onDisconnect: () => null,
2002
- onClose: () => null,
2003
- onDestroy: () => null,
2004
- onAwarenessUpdate: () => null,
2005
- onAwarenessChange: () => null,
2006
- quiet: false,
2007
- };
2008
- this.subscribedToBroadcastChannel = false;
2009
- this.webSocket = null;
2010
- this.shouldConnect = true;
2011
- this.status = exports.WebSocketStatus.Disconnected;
2012
- this.lastMessageReceived = 0;
2013
- this.mux = createMutex();
2014
- this.intervals = {
2015
- forceSync: null,
2016
- connectionChecker: null,
2017
- };
2018
- this.connectionAttempt = null;
2019
- this.receivedOnOpenPayload = undefined;
2020
- this.receivedOnStatusPayload = undefined;
2021
- this.boundConnect = this.connect.bind(this);
2022
- this.setConfiguration(configuration);
2023
- this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket;
2024
- this.on('open', this.configuration.onOpen);
2025
- this.on('open', this.onOpen.bind(this));
2026
- this.on('connect', this.configuration.onConnect);
2027
- this.on('message', this.configuration.onMessage);
2028
- this.on('outgoingMessage', this.configuration.onOutgoingMessage);
2029
- this.on('status', this.configuration.onStatus);
2030
- this.on('status', this.onStatus.bind(this));
2031
- this.on('disconnect', this.configuration.onDisconnect);
2032
- this.on('close', this.configuration.onClose);
2033
- this.on('destroy', this.configuration.onDestroy);
2034
- this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
2035
- this.on('awarenessChange', this.configuration.onAwarenessChange);
2036
- this.on('close', this.onClose.bind(this));
2037
- this.on('message', this.onMessage.bind(this));
2038
- this.registerEventListeners();
2039
- this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.configuration.messageReconnectTimeout / 10);
2040
- if (typeof configuration.connect !== 'undefined') {
2041
- this.shouldConnect = configuration.connect;
2042
- }
2043
- if (!this.shouldConnect) {
2044
- return;
2045
- }
2046
- this.connect();
2047
- }
2048
- async onOpen(event) {
2049
- this.receivedOnOpenPayload = event;
2050
- }
2051
- async onStatus(data) {
2052
- this.receivedOnStatusPayload = data;
2053
- }
2054
- attach(provider) {
2055
- if (this.receivedOnOpenPayload) {
2056
- provider.onOpen(this.receivedOnOpenPayload);
2057
- }
2058
- if (this.receivedOnStatusPayload) {
2059
- provider.onStatus(this.receivedOnStatusPayload);
2060
- }
2061
- }
2062
- detach(provider) {
2063
- // tell the server to remove the listener
2064
- }
2065
- setConfiguration(configuration = {}) {
2066
- this.configuration = { ...this.configuration, ...configuration };
2067
- }
2068
- async connect() {
2069
- if (this.status === exports.WebSocketStatus.Connected) {
2070
- return;
2071
- }
2072
- // Always cancel any previously initiated connection retryer instances
2073
- if (this.cancelWebsocketRetry) {
2074
- this.cancelWebsocketRetry();
2075
- this.cancelWebsocketRetry = undefined;
2076
- }
2077
- this.shouldConnect = true;
2078
- const abortableRetry = () => {
2079
- let cancelAttempt = false;
2080
- const retryPromise = attempt.retry(this.createWebSocketConnection.bind(this), {
2081
- delay: this.configuration.delay,
2082
- initialDelay: this.configuration.initialDelay,
2083
- factor: this.configuration.factor,
2084
- maxAttempts: this.configuration.maxAttempts,
2085
- minDelay: this.configuration.minDelay,
2086
- maxDelay: this.configuration.maxDelay,
2087
- jitter: this.configuration.jitter,
2088
- timeout: this.configuration.timeout,
2089
- beforeAttempt: context => {
2090
- if (!this.shouldConnect || cancelAttempt) {
2091
- context.abort();
2092
- }
2093
- },
2094
- }).catch((error) => {
2095
- // If we aborted the connection attempt then don’t throw an error
2096
- // ref: https://github.com/lifeomic/attempt/blob/master/src/index.ts#L136
2097
- if (error && error.code !== 'ATTEMPT_ABORTED') {
2098
- throw error;
2099
- }
2100
- });
2101
- return {
2102
- retryPromise,
2103
- cancelFunc: () => {
2104
- cancelAttempt = true;
2105
- },
2106
- };
2107
- };
2108
- const { retryPromise, cancelFunc } = abortableRetry();
2109
- this.cancelWebsocketRetry = cancelFunc;
2110
- return retryPromise;
2111
- }
2112
- createWebSocketConnection() {
2113
- return new Promise((resolve, reject) => {
2114
- if (this.webSocket) {
2115
- this.webSocket.close();
2116
- this.webSocket = null;
2117
- }
2118
- // Init the WebSocket connection
2119
- const ws = new this.configuration.WebSocketPolyfill(this.url);
2120
- ws.binaryType = 'arraybuffer';
2121
- ws.onmessage = (payload) => this.emit('message', payload);
2122
- ws.onclose = (payload) => this.emit('close', { event: payload });
2123
- ws.onopen = (payload) => this.emit('open', payload);
2124
- ws.onerror = (err) => {
2125
- reject(err);
2126
- };
2127
- this.webSocket = ws;
2128
- // Reset the status
2129
- this.status = exports.WebSocketStatus.Connecting;
2130
- this.emit('status', { status: exports.WebSocketStatus.Connecting });
2131
- // Store resolve/reject for later use
2132
- this.connectionAttempt = {
2133
- resolve,
2134
- reject,
2135
- };
2136
- });
2137
- }
2138
- onMessage(event) {
2139
- this.resolveConnectionAttempt();
2140
- }
2141
- resolveConnectionAttempt() {
2142
- if (this.connectionAttempt) {
2143
- this.connectionAttempt.resolve();
2144
- this.connectionAttempt = null;
2145
- this.status = exports.WebSocketStatus.Connected;
2146
- this.emit('status', { status: exports.WebSocketStatus.Connected });
2147
- this.emit('connect');
2148
- }
2149
- }
2150
- stopConnectionAttempt() {
2151
- this.connectionAttempt = null;
2152
- }
2153
- rejectConnectionAttempt() {
2154
- var _a;
2155
- (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
2156
- this.connectionAttempt = null;
2157
- }
2158
- checkConnection() {
2159
- var _a;
2160
- // Don’t check the connection when it’s not even established
2161
- if (this.status !== exports.WebSocketStatus.Connected) {
2162
- return;
2163
- }
2164
- // Don’t close then connection while waiting for the first message
2165
- if (!this.lastMessageReceived) {
2166
- return;
2167
- }
2168
- // Don’t close the connection when a message was received recently
2169
- if (this.configuration.messageReconnectTimeout >= getUnixTime() - this.lastMessageReceived) {
2170
- return;
2171
- }
2172
- // No message received in a long time, not even your own
2173
- // Awareness updates, which are updated every 15 seconds.
2174
- (_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.close();
2175
- }
2176
- registerEventListeners() {
2177
- if (typeof window === 'undefined') {
2178
- return;
2179
- }
2180
- window.addEventListener('online', this.boundConnect);
2181
- }
2182
- // Ensure that the URL always ends with /
2183
- get serverUrl() {
2184
- while (this.configuration.url[this.configuration.url.length - 1] === '/') {
2185
- return this.configuration.url.slice(0, this.configuration.url.length - 1);
2186
- }
2187
- return this.configuration.url;
2188
- }
2189
- get url() {
2190
- const encodedParams = encodeQueryParams(this.configuration.parameters);
2191
- return `${this.serverUrl}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
2192
- }
2193
- disconnect() {
2194
- this.shouldConnect = false;
2195
- if (this.webSocket === null) {
2196
- return;
2197
- }
2198
- try {
2199
- this.webSocket.close();
2200
- }
2201
- catch {
2202
- //
2203
- }
2204
- }
2205
- send(message) {
2206
- var _a;
2207
- if (((_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.readyState) === common.WsReadyStates.Open) {
2208
- this.webSocket.send(message);
2209
- }
2210
- }
2211
- onClose({ event }) {
2212
- this.webSocket = null;
2213
- if (this.status === exports.WebSocketStatus.Connected) {
2214
- this.status = exports.WebSocketStatus.Disconnected;
2215
- this.emit('status', { status: exports.WebSocketStatus.Disconnected });
2216
- this.emit('disconnect', { event });
2217
- }
2218
- if (event.code === common.Unauthorized.code) {
2219
- if (event.reason === common.Unauthorized.reason) {
2220
- console.warn('[HocuspocusProvider] An authentication token is required, but you didn’t send one. Try adding a `token` to your HocuspocusProvider configuration. Won’t try again.');
2221
- }
2222
- else {
2223
- console.warn(`[HocuspocusProvider] Connection closed with status Unauthorized: ${event.reason}`);
2224
- }
2225
- this.shouldConnect = false;
2226
- }
2227
- if (event.code === common.Forbidden.code) {
2228
- if (!this.configuration.quiet) {
2229
- console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.');
2230
- return; // TODO REMOVE ME
2231
- }
2232
- }
2233
- if (event.code === common.MessageTooBig.code) {
2234
- console.warn(`[HocuspocusProvider] Connection closed with status MessageTooBig: ${event.reason}`);
2235
- this.shouldConnect = false;
2236
- }
2237
- if (this.connectionAttempt) {
2238
- // That connection attempt failed.
2239
- this.rejectConnectionAttempt();
2240
- }
2241
- else if (this.shouldConnect) {
2242
- // The connection was closed by the server. Let’s just try again.
2243
- this.connect();
2244
- }
2245
- // If we’ll reconnect, we’re done for now.
2246
- if (this.shouldConnect) {
2247
- return;
2248
- }
2249
- // The status is set correctly already.
2250
- if (this.status === exports.WebSocketStatus.Disconnected) {
2251
- return;
2252
- }
2253
- // Let’s update the connection status.
2254
- this.status = exports.WebSocketStatus.Disconnected;
2255
- this.emit('status', { status: exports.WebSocketStatus.Disconnected });
2256
- this.emit('disconnect', { event });
2257
- }
2258
- destroy() {
2259
- this.emit('destroy');
2260
- if (this.intervals.forceSync) {
2261
- clearInterval(this.intervals.forceSync);
2262
- }
2263
- clearInterval(this.intervals.connectionChecker);
2264
- // If there is still a connection attempt outstanding then we should stop
2265
- // it before calling disconnect, otherwise it will be rejected in the onClose
2266
- // handler and trigger a retry
2267
- this.stopConnectionAttempt();
2268
- this.disconnect();
2269
- this.removeAllListeners();
2270
- if (typeof window === 'undefined') {
2271
- return;
2272
- }
2273
- window.removeEventListener('online', this.boundConnect);
2274
- }
1964
+ class HocuspocusProviderWebsocket extends EventEmitter {
1965
+ constructor(configuration) {
1966
+ super();
1967
+ this.configuration = {
1968
+ url: '',
1969
+ // @ts-ignore
1970
+ document: undefined,
1971
+ // @ts-ignore
1972
+ awareness: undefined,
1973
+ WebSocketPolyfill: undefined,
1974
+ parameters: {},
1975
+ connect: true,
1976
+ broadcast: true,
1977
+ forceSyncInterval: false,
1978
+ // TODO: this should depend on awareness.outdatedTime
1979
+ messageReconnectTimeout: 30000,
1980
+ // 1 second
1981
+ delay: 1000,
1982
+ // instant
1983
+ initialDelay: 0,
1984
+ // double the delay each time
1985
+ factor: 2,
1986
+ // unlimited retries
1987
+ maxAttempts: 0,
1988
+ // wait at least 1 second
1989
+ minDelay: 1000,
1990
+ // at least every 30 seconds
1991
+ maxDelay: 30000,
1992
+ // randomize
1993
+ jitter: true,
1994
+ // retry forever
1995
+ timeout: 0,
1996
+ onOpen: () => null,
1997
+ onConnect: () => null,
1998
+ onMessage: () => null,
1999
+ onOutgoingMessage: () => null,
2000
+ onStatus: () => null,
2001
+ onDisconnect: () => null,
2002
+ onClose: () => null,
2003
+ onDestroy: () => null,
2004
+ onAwarenessUpdate: () => null,
2005
+ onAwarenessChange: () => null,
2006
+ quiet: false,
2007
+ };
2008
+ this.subscribedToBroadcastChannel = false;
2009
+ this.webSocket = null;
2010
+ this.shouldConnect = true;
2011
+ this.status = exports.WebSocketStatus.Disconnected;
2012
+ this.lastMessageReceived = 0;
2013
+ this.mux = createMutex();
2014
+ this.intervals = {
2015
+ forceSync: null,
2016
+ connectionChecker: null,
2017
+ };
2018
+ this.connectionAttempt = null;
2019
+ this.receivedOnOpenPayload = undefined;
2020
+ this.receivedOnStatusPayload = undefined;
2021
+ this.boundConnect = this.connect.bind(this);
2022
+ this.setConfiguration(configuration);
2023
+ this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket;
2024
+ this.on('open', this.configuration.onOpen);
2025
+ this.on('open', this.onOpen.bind(this));
2026
+ this.on('connect', this.configuration.onConnect);
2027
+ this.on('message', this.configuration.onMessage);
2028
+ this.on('outgoingMessage', this.configuration.onOutgoingMessage);
2029
+ this.on('status', this.configuration.onStatus);
2030
+ this.on('status', this.onStatus.bind(this));
2031
+ this.on('disconnect', this.configuration.onDisconnect);
2032
+ this.on('close', this.configuration.onClose);
2033
+ this.on('destroy', this.configuration.onDestroy);
2034
+ this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
2035
+ this.on('awarenessChange', this.configuration.onAwarenessChange);
2036
+ this.on('close', this.onClose.bind(this));
2037
+ this.on('message', this.onMessage.bind(this));
2038
+ this.registerEventListeners();
2039
+ this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.configuration.messageReconnectTimeout / 10);
2040
+ if (typeof configuration.connect !== 'undefined') {
2041
+ this.shouldConnect = configuration.connect;
2042
+ }
2043
+ if (!this.shouldConnect) {
2044
+ return;
2045
+ }
2046
+ this.connect();
2047
+ }
2048
+ async onOpen(event) {
2049
+ this.receivedOnOpenPayload = event;
2050
+ }
2051
+ async onStatus(data) {
2052
+ this.receivedOnStatusPayload = data;
2053
+ }
2054
+ attach(provider) {
2055
+ if (this.receivedOnOpenPayload) {
2056
+ provider.onOpen(this.receivedOnOpenPayload);
2057
+ }
2058
+ if (this.receivedOnStatusPayload) {
2059
+ provider.onStatus(this.receivedOnStatusPayload);
2060
+ }
2061
+ }
2062
+ detach(provider) {
2063
+ // tell the server to remove the listener
2064
+ }
2065
+ setConfiguration(configuration = {}) {
2066
+ this.configuration = { ...this.configuration, ...configuration };
2067
+ }
2068
+ async connect() {
2069
+ if (this.status === exports.WebSocketStatus.Connected) {
2070
+ return;
2071
+ }
2072
+ // Always cancel any previously initiated connection retryer instances
2073
+ if (this.cancelWebsocketRetry) {
2074
+ this.cancelWebsocketRetry();
2075
+ this.cancelWebsocketRetry = undefined;
2076
+ }
2077
+ this.shouldConnect = true;
2078
+ const abortableRetry = () => {
2079
+ let cancelAttempt = false;
2080
+ const retryPromise = attempt.retry(this.createWebSocketConnection.bind(this), {
2081
+ delay: this.configuration.delay,
2082
+ initialDelay: this.configuration.initialDelay,
2083
+ factor: this.configuration.factor,
2084
+ maxAttempts: this.configuration.maxAttempts,
2085
+ minDelay: this.configuration.minDelay,
2086
+ maxDelay: this.configuration.maxDelay,
2087
+ jitter: this.configuration.jitter,
2088
+ timeout: this.configuration.timeout,
2089
+ beforeAttempt: context => {
2090
+ if (!this.shouldConnect || cancelAttempt) {
2091
+ context.abort();
2092
+ }
2093
+ },
2094
+ }).catch((error) => {
2095
+ // If we aborted the connection attempt then don’t throw an error
2096
+ // ref: https://github.com/lifeomic/attempt/blob/master/src/index.ts#L136
2097
+ if (error && error.code !== 'ATTEMPT_ABORTED') {
2098
+ throw error;
2099
+ }
2100
+ });
2101
+ return {
2102
+ retryPromise,
2103
+ cancelFunc: () => {
2104
+ cancelAttempt = true;
2105
+ },
2106
+ };
2107
+ };
2108
+ const { retryPromise, cancelFunc } = abortableRetry();
2109
+ this.cancelWebsocketRetry = cancelFunc;
2110
+ return retryPromise;
2111
+ }
2112
+ createWebSocketConnection() {
2113
+ return new Promise((resolve, reject) => {
2114
+ if (this.webSocket) {
2115
+ this.webSocket.close();
2116
+ this.webSocket = null;
2117
+ }
2118
+ // Init the WebSocket connection
2119
+ const ws = new this.configuration.WebSocketPolyfill(this.url);
2120
+ ws.binaryType = 'arraybuffer';
2121
+ ws.onmessage = (payload) => this.emit('message', payload);
2122
+ ws.onclose = (payload) => this.emit('close', { event: payload });
2123
+ ws.onopen = (payload) => this.emit('open', payload);
2124
+ ws.onerror = (err) => {
2125
+ reject(err);
2126
+ };
2127
+ this.webSocket = ws;
2128
+ // Reset the status
2129
+ this.status = exports.WebSocketStatus.Connecting;
2130
+ this.emit('status', { status: exports.WebSocketStatus.Connecting });
2131
+ // Store resolve/reject for later use
2132
+ this.connectionAttempt = {
2133
+ resolve,
2134
+ reject,
2135
+ };
2136
+ });
2137
+ }
2138
+ onMessage(event) {
2139
+ this.resolveConnectionAttempt();
2140
+ }
2141
+ resolveConnectionAttempt() {
2142
+ if (this.connectionAttempt) {
2143
+ this.connectionAttempt.resolve();
2144
+ this.connectionAttempt = null;
2145
+ this.status = exports.WebSocketStatus.Connected;
2146
+ this.emit('status', { status: exports.WebSocketStatus.Connected });
2147
+ this.emit('connect');
2148
+ }
2149
+ }
2150
+ stopConnectionAttempt() {
2151
+ this.connectionAttempt = null;
2152
+ }
2153
+ rejectConnectionAttempt() {
2154
+ var _a;
2155
+ (_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
2156
+ this.connectionAttempt = null;
2157
+ }
2158
+ checkConnection() {
2159
+ var _a;
2160
+ // Don’t check the connection when it’s not even established
2161
+ if (this.status !== exports.WebSocketStatus.Connected) {
2162
+ return;
2163
+ }
2164
+ // Don’t close then connection while waiting for the first message
2165
+ if (!this.lastMessageReceived) {
2166
+ return;
2167
+ }
2168
+ // Don’t close the connection when a message was received recently
2169
+ if (this.configuration.messageReconnectTimeout >= getUnixTime() - this.lastMessageReceived) {
2170
+ return;
2171
+ }
2172
+ // No message received in a long time, not even your own
2173
+ // Awareness updates, which are updated every 15 seconds.
2174
+ (_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.close();
2175
+ }
2176
+ registerEventListeners() {
2177
+ if (typeof window === 'undefined') {
2178
+ return;
2179
+ }
2180
+ window.addEventListener('online', this.boundConnect);
2181
+ }
2182
+ // Ensure that the URL always ends with /
2183
+ get serverUrl() {
2184
+ while (this.configuration.url[this.configuration.url.length - 1] === '/') {
2185
+ return this.configuration.url.slice(0, this.configuration.url.length - 1);
2186
+ }
2187
+ return this.configuration.url;
2188
+ }
2189
+ get url() {
2190
+ const encodedParams = encodeQueryParams(this.configuration.parameters);
2191
+ return `${this.serverUrl}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
2192
+ }
2193
+ disconnect() {
2194
+ this.shouldConnect = false;
2195
+ if (this.webSocket === null) {
2196
+ return;
2197
+ }
2198
+ try {
2199
+ this.webSocket.close();
2200
+ }
2201
+ catch {
2202
+ //
2203
+ }
2204
+ }
2205
+ send(message) {
2206
+ var _a;
2207
+ if (((_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.readyState) === common.WsReadyStates.Open) {
2208
+ this.webSocket.send(message);
2209
+ }
2210
+ }
2211
+ onClose({ event }) {
2212
+ this.webSocket = null;
2213
+ if (this.status === exports.WebSocketStatus.Connected) {
2214
+ this.status = exports.WebSocketStatus.Disconnected;
2215
+ this.emit('status', { status: exports.WebSocketStatus.Disconnected });
2216
+ this.emit('disconnect', { event });
2217
+ }
2218
+ if (event.code === common.Unauthorized.code) {
2219
+ if (event.reason === common.Unauthorized.reason) {
2220
+ console.warn('[HocuspocusProvider] An authentication token is required, but you didn’t send one. Try adding a `token` to your HocuspocusProvider configuration. Won’t try again.');
2221
+ }
2222
+ else {
2223
+ console.warn(`[HocuspocusProvider] Connection closed with status Unauthorized: ${event.reason}`);
2224
+ }
2225
+ this.shouldConnect = false;
2226
+ }
2227
+ if (event.code === common.Forbidden.code) {
2228
+ if (!this.configuration.quiet) {
2229
+ console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.');
2230
+ return; // TODO REMOVE ME
2231
+ }
2232
+ }
2233
+ if (event.code === common.MessageTooBig.code) {
2234
+ console.warn(`[HocuspocusProvider] Connection closed with status MessageTooBig: ${event.reason}`);
2235
+ this.shouldConnect = false;
2236
+ }
2237
+ if (this.connectionAttempt) {
2238
+ // That connection attempt failed.
2239
+ this.rejectConnectionAttempt();
2240
+ }
2241
+ else if (this.shouldConnect) {
2242
+ // The connection was closed by the server. Let’s just try again.
2243
+ this.connect();
2244
+ }
2245
+ // If we’ll reconnect, we’re done for now.
2246
+ if (this.shouldConnect) {
2247
+ return;
2248
+ }
2249
+ // The status is set correctly already.
2250
+ if (this.status === exports.WebSocketStatus.Disconnected) {
2251
+ return;
2252
+ }
2253
+ // Let’s update the connection status.
2254
+ this.status = exports.WebSocketStatus.Disconnected;
2255
+ this.emit('status', { status: exports.WebSocketStatus.Disconnected });
2256
+ this.emit('disconnect', { event });
2257
+ }
2258
+ destroy() {
2259
+ this.emit('destroy');
2260
+ if (this.intervals.forceSync) {
2261
+ clearInterval(this.intervals.forceSync);
2262
+ }
2263
+ clearInterval(this.intervals.connectionChecker);
2264
+ // If there is still a connection attempt outstanding then we should stop
2265
+ // it before calling disconnect, otherwise it will be rejected in the onClose
2266
+ // handler and trigger a retry
2267
+ this.stopConnectionAttempt();
2268
+ this.disconnect();
2269
+ this.removeAllListeners();
2270
+ if (typeof window === 'undefined') {
2271
+ return;
2272
+ }
2273
+ window.removeEventListener('online', this.boundConnect);
2274
+ }
2275
2275
  }
2276
2276
 
2277
- class StatelessMessage extends OutgoingMessage {
2278
- constructor() {
2279
- super(...arguments);
2280
- this.type = exports.MessageType.Stateless;
2281
- this.description = 'A stateless message';
2282
- }
2283
- get(args) {
2284
- var _a;
2285
- writeVarString(this.encoder, args.documentName);
2286
- writeVarUint(this.encoder, this.type);
2287
- writeVarString(this.encoder, (_a = args.payload) !== null && _a !== void 0 ? _a : '');
2288
- return this.encoder;
2289
- }
2277
+ class StatelessMessage extends OutgoingMessage {
2278
+ constructor() {
2279
+ super(...arguments);
2280
+ this.type = exports.MessageType.Stateless;
2281
+ this.description = 'A stateless message';
2282
+ }
2283
+ get(args) {
2284
+ var _a;
2285
+ writeVarString(this.encoder, args.documentName);
2286
+ writeVarUint(this.encoder, this.type);
2287
+ writeVarString(this.encoder, (_a = args.payload) !== null && _a !== void 0 ? _a : '');
2288
+ return this.encoder;
2289
+ }
2290
2290
  }
2291
2291
 
2292
- class CloseMessage extends OutgoingMessage {
2293
- constructor() {
2294
- super(...arguments);
2295
- this.type = exports.MessageType.CLOSE;
2296
- this.description = 'Ask the server to close the connection';
2297
- }
2298
- get(args) {
2299
- writeVarString(this.encoder, args.documentName);
2300
- writeVarUint(this.encoder, this.type);
2301
- return this.encoder;
2302
- }
2292
+ class CloseMessage extends OutgoingMessage {
2293
+ constructor() {
2294
+ super(...arguments);
2295
+ this.type = exports.MessageType.CLOSE;
2296
+ this.description = 'Ask the server to close the connection';
2297
+ }
2298
+ get(args) {
2299
+ writeVarString(this.encoder, args.documentName);
2300
+ writeVarUint(this.encoder, this.type);
2301
+ return this.encoder;
2302
+ }
2303
2303
  }
2304
2304
 
2305
- class HocuspocusProvider extends EventEmitter {
2306
- constructor(configuration) {
2307
- super();
2308
- this.configuration = {
2309
- name: '',
2310
- // @ts-ignore
2311
- document: undefined,
2312
- // @ts-ignore
2313
- awareness: undefined,
2314
- token: null,
2315
- parameters: {},
2316
- broadcast: true,
2317
- forceSyncInterval: false,
2318
- onAuthenticated: () => null,
2319
- onAuthenticationFailed: () => null,
2320
- onOpen: () => null,
2321
- onConnect: () => null,
2322
- onMessage: () => null,
2323
- onOutgoingMessage: () => null,
2324
- onStatus: () => null,
2325
- onSynced: () => null,
2326
- onDisconnect: () => null,
2327
- onClose: () => null,
2328
- onDestroy: () => null,
2329
- onAwarenessUpdate: () => null,
2330
- onAwarenessChange: () => null,
2331
- onStateless: () => null,
2332
- quiet: false,
2333
- };
2334
- this.subscribedToBroadcastChannel = false;
2335
- this.isSynced = false;
2336
- this.unsyncedChanges = 0;
2337
- this.status = exports.WebSocketStatus.Disconnected;
2338
- this.isAuthenticated = false;
2339
- this.mux = createMutex();
2340
- this.intervals = {
2341
- forceSync: null,
2342
- };
2343
- this.isConnected = true;
2344
- this.boundBeforeUnload = this.beforeUnload.bind(this);
2345
- this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
2346
- this.setConfiguration(configuration);
2347
- this.configuration.document = configuration.document ? configuration.document : new Y__namespace.Doc();
2348
- this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document);
2349
- this.on('open', this.configuration.onOpen);
2350
- this.on('message', this.configuration.onMessage);
2351
- this.on('outgoingMessage', this.configuration.onOutgoingMessage);
2352
- this.on('synced', this.configuration.onSynced);
2353
- this.on('destroy', this.configuration.onDestroy);
2354
- this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
2355
- this.on('awarenessChange', this.configuration.onAwarenessChange);
2356
- this.on('stateless', this.configuration.onStateless);
2357
- this.on('authenticated', this.configuration.onAuthenticated);
2358
- this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
2359
- this.configuration.websocketProvider.on('connect', this.configuration.onConnect);
2360
- this.configuration.websocketProvider.on('connect', (e) => this.emit('connect', e));
2361
- this.configuration.websocketProvider.on('open', this.onOpen.bind(this));
2362
- this.configuration.websocketProvider.on('open', (e) => this.emit('open', e));
2363
- this.configuration.websocketProvider.on('message', this.onMessage.bind(this));
2364
- this.configuration.websocketProvider.on('close', this.onClose.bind(this));
2365
- this.configuration.websocketProvider.on('close', this.configuration.onClose);
2366
- this.configuration.websocketProvider.on('close', (e) => this.emit('close', e));
2367
- this.configuration.websocketProvider.on('status', this.onStatus.bind(this));
2368
- this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect);
2369
- this.configuration.websocketProvider.on('disconnect', (e) => this.emit('disconnect', e));
2370
- this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy);
2371
- this.configuration.websocketProvider.on('destroy', (e) => this.emit('destroy', e));
2372
- this.awareness.on('update', () => {
2373
- this.emit('awarenessUpdate', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
2374
- });
2375
- this.awareness.on('change', () => {
2376
- this.emit('awarenessChange', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
2377
- });
2378
- this.document.on('update', this.documentUpdateHandler.bind(this));
2379
- this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
2380
- this.registerEventListeners();
2381
- if (this.configuration.forceSyncInterval) {
2382
- this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
2383
- }
2384
- this.configuration.websocketProvider.attach(this);
2385
- }
2386
- onStatus({ status }) {
2387
- this.status = status;
2388
- this.configuration.onStatus({ status });
2389
- this.emit('status', { status });
2390
- }
2391
- setConfiguration(configuration = {}) {
2392
- if (!configuration.websocketProvider && configuration.url) {
2393
- const websocketProviderConfig = configuration;
2394
- this.configuration.websocketProvider = new HocuspocusProviderWebsocket({
2395
- url: websocketProviderConfig.url,
2396
- parameters: websocketProviderConfig.parameters,
2397
- });
2398
- }
2399
- this.configuration = { ...this.configuration, ...configuration };
2400
- }
2401
- get document() {
2402
- return this.configuration.document;
2403
- }
2404
- get awareness() {
2405
- return this.configuration.awareness;
2406
- }
2407
- get hasUnsyncedChanges() {
2408
- return this.unsyncedChanges > 0;
2409
- }
2410
- updateUnsyncedChanges(unsyncedChanges = 0) {
2411
- this.unsyncedChanges += unsyncedChanges;
2412
- this.emit('unsyncedChanges', this.unsyncedChanges);
2413
- }
2414
- forceSync() {
2415
- this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
2416
- }
2417
- beforeUnload() {
2418
- removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload');
2419
- }
2420
- registerEventListeners() {
2421
- if (typeof window === 'undefined') {
2422
- return;
2423
- }
2424
- window.addEventListener('beforeunload', this.boundBeforeUnload);
2425
- }
2426
- sendStateless(payload) {
2427
- this.send(StatelessMessage, { documentName: this.configuration.name, payload });
2428
- }
2429
- documentUpdateHandler(update, origin) {
2430
- if (origin === this) {
2431
- return;
2432
- }
2433
- this.updateUnsyncedChanges(1);
2434
- this.send(UpdateMessage, { update, documentName: this.configuration.name }, true);
2435
- }
2436
- awarenessUpdateHandler({ added, updated, removed }, origin) {
2437
- const changedClients = added.concat(updated).concat(removed);
2438
- this.send(AwarenessMessage, {
2439
- awareness: this.awareness,
2440
- clients: changedClients,
2441
- documentName: this.configuration.name,
2442
- }, true);
2443
- }
2444
- get synced() {
2445
- return this.isSynced;
2446
- }
2447
- set synced(state) {
2448
- if (this.isSynced === state) {
2449
- return;
2450
- }
2451
- if (state && this.unsyncedChanges > 0) {
2452
- this.updateUnsyncedChanges(-1 * this.unsyncedChanges);
2453
- }
2454
- this.isSynced = state;
2455
- this.emit('synced', { state });
2456
- this.emit('sync', { state });
2457
- }
2458
- receiveStateless(payload) {
2459
- this.emit('stateless', { payload });
2460
- }
2461
- get isAuthenticationRequired() {
2462
- return !!this.configuration.token && !this.isAuthenticated;
2463
- }
2464
- // not needed, but provides backward compatibility with e.g. lexicla/yjs
2465
- async connect() {
2466
- return this.configuration.websocketProvider.connect();
2467
- }
2468
- disconnect() {
2469
- this.disconnectBroadcastChannel();
2470
- this.configuration.websocketProvider.detach(this);
2471
- }
2472
- async onOpen(event) {
2473
- this.isAuthenticated = false;
2474
- this.emit('open', { event });
2475
- if (this.isAuthenticationRequired) {
2476
- this.send(AuthenticationMessage, {
2477
- token: await this.getToken(),
2478
- documentName: this.configuration.name,
2479
- });
2480
- }
2481
- this.startSync();
2482
- }
2483
- async getToken() {
2484
- if (typeof this.configuration.token === 'function') {
2485
- const token = await this.configuration.token();
2486
- return token;
2487
- }
2488
- return this.configuration.token;
2489
- }
2490
- startSync() {
2491
- this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
2492
- if (this.awareness.getLocalState() !== null) {
2493
- this.send(AwarenessMessage, {
2494
- awareness: this.awareness,
2495
- clients: [this.document.clientID],
2496
- documentName: this.configuration.name,
2497
- });
2498
- }
2499
- }
2500
- send(message, args, broadcast = false) {
2501
- if (!this.isConnected)
2502
- return;
2503
- if (broadcast) {
2504
- this.mux(() => { this.broadcast(message, args); });
2505
- }
2506
- const messageSender = new MessageSender(message, args);
2507
- this.emit('outgoingMessage', { message: messageSender.message });
2508
- messageSender.send(this.configuration.websocketProvider);
2509
- }
2510
- onMessage(event) {
2511
- const message = new IncomingMessage(event.data);
2512
- const documentName = message.readVarString();
2513
- if (documentName !== this.configuration.name) {
2514
- return; // message is meant for another provider
2515
- }
2516
- message.writeVarString(documentName);
2517
- this.emit('message', { event, message: new IncomingMessage(event.data) });
2518
- new MessageReceiver(message).apply(this);
2519
- }
2520
- onClose(event) {
2521
- this.isAuthenticated = false;
2522
- this.synced = false;
2523
- // update awareness (all users except local left)
2524
- removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID), this);
2525
- }
2526
- destroy() {
2527
- this.emit('destroy');
2528
- if (this.intervals.forceSync) {
2529
- clearInterval(this.intervals.forceSync);
2530
- }
2531
- removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
2532
- this.disconnect();
2533
- this.awareness.off('update', this.awarenessUpdateHandler);
2534
- this.document.off('update', this.documentUpdateHandler);
2535
- this.removeAllListeners();
2536
- this.send(CloseMessage, { documentName: this.configuration.name });
2537
- this.isConnected = false;
2538
- if (typeof window === 'undefined') {
2539
- return;
2540
- }
2541
- window.removeEventListener('beforeunload', this.boundBeforeUnload);
2542
- }
2543
- permissionDeniedHandler(reason) {
2544
- this.emit('authenticationFailed', { reason });
2545
- this.isAuthenticated = false;
2546
- this.disconnect();
2547
- this.status = exports.WebSocketStatus.Disconnected;
2548
- }
2549
- authenticatedHandler() {
2550
- this.isAuthenticated = true;
2551
- this.emit('authenticated');
2552
- this.startSync();
2553
- }
2554
- get broadcastChannel() {
2555
- return `${this.configuration.name}`;
2556
- }
2557
- broadcastChannelSubscriber(data) {
2558
- this.mux(() => {
2559
- const message = new IncomingMessage(data);
2560
- const documentName = message.readVarString();
2561
- message.writeVarString(documentName);
2562
- new MessageReceiver(message)
2563
- .setBroadcasted(true)
2564
- .apply(this, false);
2565
- });
2566
- }
2567
- subscribeToBroadcastChannel() {
2568
- if (!this.subscribedToBroadcastChannel) {
2569
- subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2570
- this.subscribedToBroadcastChannel = true;
2571
- }
2572
- this.mux(() => {
2573
- this.broadcast(SyncStepOneMessage, { document: this.document });
2574
- this.broadcast(SyncStepTwoMessage, { document: this.document });
2575
- this.broadcast(QueryAwarenessMessage, { document: this.document });
2576
- this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID], document: this.document });
2577
- });
2578
- }
2579
- disconnectBroadcastChannel() {
2580
- // broadcast message with local awareness state set to null (indicating disconnect)
2581
- this.send(AwarenessMessage, {
2582
- awareness: this.awareness,
2583
- clients: [this.document.clientID],
2584
- states: new Map(),
2585
- documentName: this.configuration.name,
2586
- }, true);
2587
- if (this.subscribedToBroadcastChannel) {
2588
- unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2589
- this.subscribedToBroadcastChannel = false;
2590
- }
2591
- }
2592
- broadcast(Message, args) {
2593
- if (!this.configuration.broadcast) {
2594
- return;
2595
- }
2596
- if (!this.subscribedToBroadcastChannel) {
2597
- return;
2598
- }
2599
- new MessageSender(Message, args).broadcast(this.broadcastChannel);
2600
- }
2601
- setAwarenessField(key, value) {
2602
- this.awareness.setLocalStateField(key, value);
2603
- }
2305
+ class HocuspocusProvider extends EventEmitter {
2306
+ constructor(configuration) {
2307
+ super();
2308
+ this.configuration = {
2309
+ name: '',
2310
+ // @ts-ignore
2311
+ document: undefined,
2312
+ // @ts-ignore
2313
+ awareness: undefined,
2314
+ token: null,
2315
+ parameters: {},
2316
+ broadcast: true,
2317
+ forceSyncInterval: false,
2318
+ onAuthenticated: () => null,
2319
+ onAuthenticationFailed: () => null,
2320
+ onOpen: () => null,
2321
+ onConnect: () => null,
2322
+ onMessage: () => null,
2323
+ onOutgoingMessage: () => null,
2324
+ onStatus: () => null,
2325
+ onSynced: () => null,
2326
+ onDisconnect: () => null,
2327
+ onClose: () => null,
2328
+ onDestroy: () => null,
2329
+ onAwarenessUpdate: () => null,
2330
+ onAwarenessChange: () => null,
2331
+ onStateless: () => null,
2332
+ quiet: false,
2333
+ };
2334
+ this.subscribedToBroadcastChannel = false;
2335
+ this.isSynced = false;
2336
+ this.unsyncedChanges = 0;
2337
+ this.status = exports.WebSocketStatus.Disconnected;
2338
+ this.isAuthenticated = false;
2339
+ this.authorizedScope = undefined;
2340
+ this.mux = createMutex();
2341
+ this.intervals = {
2342
+ forceSync: null,
2343
+ };
2344
+ this.isConnected = true;
2345
+ this.boundBeforeUnload = this.beforeUnload.bind(this);
2346
+ this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
2347
+ this.setConfiguration(configuration);
2348
+ this.configuration.document = configuration.document ? configuration.document : new Y__namespace.Doc();
2349
+ this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document);
2350
+ this.on('open', this.configuration.onOpen);
2351
+ this.on('message', this.configuration.onMessage);
2352
+ this.on('outgoingMessage', this.configuration.onOutgoingMessage);
2353
+ this.on('synced', this.configuration.onSynced);
2354
+ this.on('destroy', this.configuration.onDestroy);
2355
+ this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
2356
+ this.on('awarenessChange', this.configuration.onAwarenessChange);
2357
+ this.on('stateless', this.configuration.onStateless);
2358
+ this.on('authenticated', this.configuration.onAuthenticated);
2359
+ this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
2360
+ this.configuration.websocketProvider.on('connect', this.configuration.onConnect);
2361
+ this.configuration.websocketProvider.on('connect', (e) => this.emit('connect', e));
2362
+ this.configuration.websocketProvider.on('open', this.onOpen.bind(this));
2363
+ this.configuration.websocketProvider.on('open', (e) => this.emit('open', e));
2364
+ this.configuration.websocketProvider.on('message', this.onMessage.bind(this));
2365
+ this.configuration.websocketProvider.on('close', this.onClose.bind(this));
2366
+ this.configuration.websocketProvider.on('close', this.configuration.onClose);
2367
+ this.configuration.websocketProvider.on('close', (e) => this.emit('close', e));
2368
+ this.configuration.websocketProvider.on('status', this.onStatus.bind(this));
2369
+ this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect);
2370
+ this.configuration.websocketProvider.on('disconnect', (e) => this.emit('disconnect', e));
2371
+ this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy);
2372
+ this.configuration.websocketProvider.on('destroy', (e) => this.emit('destroy', e));
2373
+ this.awareness.on('update', () => {
2374
+ this.emit('awarenessUpdate', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
2375
+ });
2376
+ this.awareness.on('change', () => {
2377
+ this.emit('awarenessChange', { states: common.awarenessStatesToArray(this.awareness.getStates()) });
2378
+ });
2379
+ this.document.on('update', this.documentUpdateHandler.bind(this));
2380
+ this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
2381
+ this.registerEventListeners();
2382
+ if (this.configuration.forceSyncInterval) {
2383
+ this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
2384
+ }
2385
+ this.configuration.websocketProvider.attach(this);
2386
+ }
2387
+ onStatus({ status }) {
2388
+ this.status = status;
2389
+ this.configuration.onStatus({ status });
2390
+ this.emit('status', { status });
2391
+ }
2392
+ setConfiguration(configuration = {}) {
2393
+ if (!configuration.websocketProvider && configuration.url) {
2394
+ const websocketProviderConfig = configuration;
2395
+ this.configuration.websocketProvider = new HocuspocusProviderWebsocket({
2396
+ url: websocketProviderConfig.url,
2397
+ parameters: websocketProviderConfig.parameters,
2398
+ });
2399
+ }
2400
+ this.configuration = { ...this.configuration, ...configuration };
2401
+ }
2402
+ get document() {
2403
+ return this.configuration.document;
2404
+ }
2405
+ get awareness() {
2406
+ return this.configuration.awareness;
2407
+ }
2408
+ get hasUnsyncedChanges() {
2409
+ return this.unsyncedChanges > 0;
2410
+ }
2411
+ updateUnsyncedChanges(unsyncedChanges = 0) {
2412
+ this.unsyncedChanges += unsyncedChanges;
2413
+ this.emit('unsyncedChanges', this.unsyncedChanges);
2414
+ }
2415
+ forceSync() {
2416
+ this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
2417
+ }
2418
+ beforeUnload() {
2419
+ removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload');
2420
+ }
2421
+ registerEventListeners() {
2422
+ if (typeof window === 'undefined') {
2423
+ return;
2424
+ }
2425
+ window.addEventListener('beforeunload', this.boundBeforeUnload);
2426
+ }
2427
+ sendStateless(payload) {
2428
+ this.send(StatelessMessage, { documentName: this.configuration.name, payload });
2429
+ }
2430
+ documentUpdateHandler(update, origin) {
2431
+ if (origin === this) {
2432
+ return;
2433
+ }
2434
+ this.updateUnsyncedChanges(1);
2435
+ this.send(UpdateMessage, { update, documentName: this.configuration.name }, true);
2436
+ }
2437
+ awarenessUpdateHandler({ added, updated, removed }, origin) {
2438
+ const changedClients = added.concat(updated).concat(removed);
2439
+ this.send(AwarenessMessage, {
2440
+ awareness: this.awareness,
2441
+ clients: changedClients,
2442
+ documentName: this.configuration.name,
2443
+ }, true);
2444
+ }
2445
+ get synced() {
2446
+ return this.isSynced;
2447
+ }
2448
+ set synced(state) {
2449
+ if (this.isSynced === state) {
2450
+ return;
2451
+ }
2452
+ if (state && this.unsyncedChanges > 0) {
2453
+ this.updateUnsyncedChanges(-1 * this.unsyncedChanges);
2454
+ }
2455
+ this.isSynced = state;
2456
+ this.emit('synced', { state });
2457
+ this.emit('sync', { state });
2458
+ }
2459
+ receiveStateless(payload) {
2460
+ this.emit('stateless', { payload });
2461
+ }
2462
+ get isAuthenticationRequired() {
2463
+ return !!this.configuration.token && !this.isAuthenticated;
2464
+ }
2465
+ // not needed, but provides backward compatibility with e.g. lexicla/yjs
2466
+ async connect() {
2467
+ return this.configuration.websocketProvider.connect();
2468
+ }
2469
+ disconnect() {
2470
+ this.disconnectBroadcastChannel();
2471
+ this.configuration.websocketProvider.detach(this);
2472
+ }
2473
+ async onOpen(event) {
2474
+ this.isAuthenticated = false;
2475
+ this.emit('open', { event });
2476
+ if (this.isAuthenticationRequired) {
2477
+ this.send(AuthenticationMessage, {
2478
+ token: await this.getToken(),
2479
+ documentName: this.configuration.name,
2480
+ });
2481
+ }
2482
+ this.startSync();
2483
+ }
2484
+ async getToken() {
2485
+ if (typeof this.configuration.token === 'function') {
2486
+ const token = await this.configuration.token();
2487
+ return token;
2488
+ }
2489
+ return this.configuration.token;
2490
+ }
2491
+ startSync() {
2492
+ this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
2493
+ if (this.awareness.getLocalState() !== null) {
2494
+ this.send(AwarenessMessage, {
2495
+ awareness: this.awareness,
2496
+ clients: [this.document.clientID],
2497
+ documentName: this.configuration.name,
2498
+ });
2499
+ }
2500
+ }
2501
+ send(message, args, broadcast = false) {
2502
+ if (!this.isConnected)
2503
+ return;
2504
+ if (broadcast) {
2505
+ this.mux(() => { this.broadcast(message, args); });
2506
+ }
2507
+ const messageSender = new MessageSender(message, args);
2508
+ this.emit('outgoingMessage', { message: messageSender.message });
2509
+ messageSender.send(this.configuration.websocketProvider);
2510
+ }
2511
+ onMessage(event) {
2512
+ const message = new IncomingMessage(event.data);
2513
+ const documentName = message.readVarString();
2514
+ if (documentName !== this.configuration.name) {
2515
+ return; // message is meant for another provider
2516
+ }
2517
+ message.writeVarString(documentName);
2518
+ this.emit('message', { event, message: new IncomingMessage(event.data) });
2519
+ new MessageReceiver(message).apply(this);
2520
+ }
2521
+ onClose(event) {
2522
+ this.isAuthenticated = false;
2523
+ this.synced = false;
2524
+ // update awareness (all users except local left)
2525
+ removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID), this);
2526
+ }
2527
+ destroy() {
2528
+ this.emit('destroy');
2529
+ if (this.intervals.forceSync) {
2530
+ clearInterval(this.intervals.forceSync);
2531
+ }
2532
+ removeAwarenessStates(this.awareness, [this.document.clientID], 'provider destroy');
2533
+ this.disconnect();
2534
+ this.awareness.off('update', this.awarenessUpdateHandler);
2535
+ this.document.off('update', this.documentUpdateHandler);
2536
+ this.removeAllListeners();
2537
+ this.send(CloseMessage, { documentName: this.configuration.name });
2538
+ this.isConnected = false;
2539
+ if (typeof window === 'undefined') {
2540
+ return;
2541
+ }
2542
+ window.removeEventListener('beforeunload', this.boundBeforeUnload);
2543
+ }
2544
+ permissionDeniedHandler(reason) {
2545
+ this.emit('authenticationFailed', { reason });
2546
+ this.isAuthenticated = false;
2547
+ this.disconnect();
2548
+ this.status = exports.WebSocketStatus.Disconnected;
2549
+ }
2550
+ authenticatedHandler(scope) {
2551
+ this.isAuthenticated = true;
2552
+ this.authorizedScope = scope;
2553
+ this.emit('authenticated');
2554
+ this.startSync();
2555
+ }
2556
+ get broadcastChannel() {
2557
+ return `${this.configuration.name}`;
2558
+ }
2559
+ broadcastChannelSubscriber(data) {
2560
+ this.mux(() => {
2561
+ const message = new IncomingMessage(data);
2562
+ const documentName = message.readVarString();
2563
+ message.writeVarString(documentName);
2564
+ new MessageReceiver(message)
2565
+ .setBroadcasted(true)
2566
+ .apply(this, false);
2567
+ });
2568
+ }
2569
+ subscribeToBroadcastChannel() {
2570
+ if (!this.subscribedToBroadcastChannel) {
2571
+ subscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2572
+ this.subscribedToBroadcastChannel = true;
2573
+ }
2574
+ this.mux(() => {
2575
+ this.broadcast(SyncStepOneMessage, { document: this.document });
2576
+ this.broadcast(SyncStepTwoMessage, { document: this.document });
2577
+ this.broadcast(QueryAwarenessMessage, { document: this.document });
2578
+ this.broadcast(AwarenessMessage, { awareness: this.awareness, clients: [this.document.clientID], document: this.document });
2579
+ });
2580
+ }
2581
+ disconnectBroadcastChannel() {
2582
+ // broadcast message with local awareness state set to null (indicating disconnect)
2583
+ this.send(AwarenessMessage, {
2584
+ awareness: this.awareness,
2585
+ clients: [this.document.clientID],
2586
+ states: new Map(),
2587
+ documentName: this.configuration.name,
2588
+ }, true);
2589
+ if (this.subscribedToBroadcastChannel) {
2590
+ unsubscribe(this.broadcastChannel, this.boundBroadcastChannelSubscriber);
2591
+ this.subscribedToBroadcastChannel = false;
2592
+ }
2593
+ }
2594
+ broadcast(Message, args) {
2595
+ if (!this.configuration.broadcast) {
2596
+ return;
2597
+ }
2598
+ if (!this.subscribedToBroadcastChannel) {
2599
+ return;
2600
+ }
2601
+ new MessageSender(Message, args).broadcast(this.broadcastChannel);
2602
+ }
2603
+ setAwarenessField(key, value) {
2604
+ this.awareness.setLocalStateField(key, value);
2605
+ }
2604
2606
  }
2605
2607
 
2606
- class TiptapCollabProviderWebsocket extends HocuspocusProviderWebsocket {
2607
- constructor(configuration) {
2608
- super({ ...configuration, url: `wss://${configuration.appId}.collab.tiptap.cloud` });
2609
- }
2608
+ class TiptapCollabProviderWebsocket extends HocuspocusProviderWebsocket {
2609
+ constructor(configuration) {
2610
+ super({ ...configuration, url: `wss://${configuration.appId}.collab.tiptap.cloud` });
2611
+ }
2610
2612
  }
2611
2613
 
2612
- class TiptapCollabProvider extends HocuspocusProvider {
2613
- constructor(configuration) {
2614
- if (!configuration.websocketProvider) {
2615
- configuration.websocketProvider = new TiptapCollabProviderWebsocket({ appId: configuration.appId });
2616
- }
2617
- if (!configuration.token) {
2618
- configuration.token = 'notoken'; // need to send a token anyway (which will be ignored)
2619
- }
2620
- super(configuration);
2621
- }
2614
+ class TiptapCollabProvider extends HocuspocusProvider {
2615
+ constructor(configuration) {
2616
+ if (!configuration.websocketProvider) {
2617
+ configuration.websocketProvider = new TiptapCollabProviderWebsocket({ appId: configuration.appId });
2618
+ }
2619
+ if (!configuration.token) {
2620
+ configuration.token = 'notoken'; // need to send a token anyway (which will be ignored)
2621
+ }
2622
+ super(configuration);
2623
+ }
2622
2624
  }
2623
2625
 
2624
2626
  exports.HocuspocusProvider = HocuspocusProvider;