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