@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.
- package/dist/hocuspocus-provider.cjs +917 -915
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +917 -915
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/common/src/CloseEvents.d.ts +29 -29
- package/dist/packages/common/src/auth.d.ts +6 -6
- package/dist/packages/common/src/awarenessStatesToArray.d.ts +3 -3
- package/dist/packages/common/src/index.d.ts +4 -4
- package/dist/packages/common/src/types.d.ts +10 -10
- package/dist/packages/extension-database/src/Database.d.ts +30 -30
- package/dist/packages/extension-database/src/index.d.ts +1 -1
- package/dist/packages/extension-logger/src/Logger.d.ts +67 -67
- package/dist/packages/extension-logger/src/index.d.ts +1 -1
- package/dist/packages/extension-redis/src/Redis.d.ts +116 -95
- package/dist/packages/extension-redis/src/index.d.ts +1 -1
- package/dist/packages/extension-sqlite/src/SQLite.d.ts +26 -26
- package/dist/packages/extension-sqlite/src/index.d.ts +1 -1
- package/dist/packages/extension-throttle/src/index.d.ts +31 -31
- package/dist/packages/extension-webhook/src/index.d.ts +57 -57
- package/dist/packages/provider/src/EventEmitter.d.ts +9 -9
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +110 -110
- package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +115 -115
- package/dist/packages/provider/src/IncomingMessage.d.ts +16 -16
- package/dist/packages/provider/src/MessageReceiver.d.ts +13 -13
- package/dist/packages/provider/src/MessageSender.d.ts +10 -10
- package/dist/packages/provider/src/OutgoingMessage.d.ts +9 -9
- package/dist/packages/provider/src/OutgoingMessages/AuthenticationMessage.d.ts +7 -7
- package/dist/packages/provider/src/OutgoingMessages/AwarenessMessage.d.ts +8 -8
- package/dist/packages/provider/src/OutgoingMessages/CloseMessage.d.ts +8 -8
- package/dist/packages/provider/src/OutgoingMessages/QueryAwarenessMessage.d.ts +8 -8
- package/dist/packages/provider/src/OutgoingMessages/StatelessMessage.d.ts +7 -7
- package/dist/packages/provider/src/OutgoingMessages/SyncStepOneMessage.d.ts +8 -8
- package/dist/packages/provider/src/OutgoingMessages/SyncStepTwoMessage.d.ts +8 -8
- package/dist/packages/provider/src/OutgoingMessages/UpdateMessage.d.ts +7 -7
- package/dist/packages/provider/src/TiptapCollabProvider.d.ts +11 -11
- package/dist/packages/provider/src/TiptapCollabProviderWebsocket.d.ts +11 -11
- package/dist/packages/provider/src/index.d.ts +5 -5
- package/dist/packages/provider/src/types.d.ts +84 -84
- package/dist/packages/server/src/ClientConnection.d.ts +56 -0
- package/dist/packages/server/src/Connection.d.ts +71 -71
- package/dist/packages/server/src/Debugger.d.ts +14 -14
- package/dist/packages/server/src/DirectConnection.d.ts +14 -0
- package/dist/packages/server/src/Document.d.ts +91 -88
- package/dist/packages/server/src/Hocuspocus.d.ts +103 -108
- package/dist/packages/server/src/IncomingMessage.d.ts +21 -21
- package/dist/packages/server/src/MessageReceiver.d.ts +12 -12
- package/dist/packages/server/src/OutgoingMessage.d.ts +20 -20
- package/dist/packages/server/src/index.d.ts +8 -8
- package/dist/packages/server/src/types.d.ts +270 -264
- package/dist/packages/server/src/util/getParameters.d.ts +8 -0
- package/dist/packages/transformer/src/Prosemirror.d.ts +11 -11
- package/dist/packages/transformer/src/Tiptap.d.ts +10 -10
- package/dist/packages/transformer/src/index.d.ts +3 -3
- package/dist/packages/transformer/src/types.d.ts +5 -5
- package/dist/playground/backend/src/default.d.ts +1 -1
- package/dist/playground/backend/src/express.d.ts +1 -1
- package/dist/playground/backend/src/koa.d.ts +1 -1
- package/dist/playground/backend/src/load-document.d.ts +1 -1
- package/dist/playground/backend/src/redis.d.ts +1 -1
- package/dist/playground/backend/src/slow.d.ts +1 -1
- package/dist/playground/backend/src/tiptapcollab.d.ts +1 -1
- package/dist/playground/backend/src/webhook.d.ts +1 -1
- package/dist/playground/frontend/src/main.d.ts +1 -1
- package/dist/playground/frontend/vite.config.d.ts +2 -2
- package/dist/tests/extension-database/fetch.d.ts +1 -1
- package/dist/tests/extension-logger/onListen.d.ts +1 -1
- package/dist/tests/extension-redis/closeConnections.d.ts +1 -1
- package/dist/tests/extension-redis/getConnectionCount.d.ts +1 -1
- package/dist/tests/extension-redis/getDocumentsCount.d.ts +1 -1
- package/dist/tests/extension-redis/onAwarenessChange.d.ts +1 -1
- package/dist/tests/extension-redis/onChange.d.ts +1 -1
- package/dist/tests/extension-redis/onStateless.d.ts +1 -1
- package/dist/tests/extension-redis/onStoreDocument.d.ts +1 -1
- package/dist/tests/extension-throttle/banning.d.ts +1 -1
- package/dist/tests/extension-throttle/configuration.d.ts +1 -1
- package/dist/tests/provider/observe.d.ts +1 -1
- package/dist/tests/provider/observeDeep.d.ts +1 -1
- package/dist/tests/provider/onAuthenticated.d.ts +1 -1
- package/dist/tests/provider/onAuthenticationFailed.d.ts +1 -1
- package/dist/tests/provider/onAwarenessChange.d.ts +1 -1
- package/dist/tests/provider/onAwarenessUpdate.d.ts +1 -1
- package/dist/tests/provider/onClose.d.ts +1 -1
- package/dist/tests/provider/onConnect.d.ts +1 -1
- package/dist/tests/provider/onDisconnect.d.ts +1 -1
- package/dist/tests/provider/onMessage.d.ts +1 -1
- package/dist/tests/provider/onOpen.d.ts +1 -1
- package/dist/tests/provider/onStateless.d.ts +1 -1
- package/dist/tests/provider/onSynced.d.ts +1 -1
- package/dist/tests/providerwebsocket/configuration.d.ts +1 -1
- package/dist/tests/server/address.d.ts +1 -1
- package/dist/tests/server/afterStoreDocument.d.ts +1 -1
- package/dist/tests/server/beforeBroadcastStateless.d.ts +1 -1
- package/dist/tests/server/beforeHandleMessage.d.ts +1 -1
- package/dist/tests/server/closeConnections.d.ts +1 -1
- package/dist/tests/server/getConnectionsCount.d.ts +1 -1
- package/dist/tests/server/getDocumentsCount.d.ts +1 -1
- package/dist/tests/server/getMessageLogs.d.ts +1 -1
- package/dist/tests/server/listen.d.ts +1 -1
- package/dist/tests/server/onAuthenticate.d.ts +1 -1
- package/dist/tests/server/onAwarenessUpdate.d.ts +1 -1
- package/dist/tests/server/onChange.d.ts +1 -1
- package/dist/tests/server/onClose.d.ts +1 -1
- package/dist/tests/server/onConfigure.d.ts +1 -1
- package/dist/tests/server/onConnect.d.ts +1 -1
- package/dist/tests/server/onDestroy.d.ts +1 -1
- package/dist/tests/server/onDisconnect.d.ts +1 -1
- package/dist/tests/server/onListen.d.ts +1 -1
- package/dist/tests/server/onLoadDocument.d.ts +1 -1
- package/dist/tests/server/onRequest.d.ts +1 -1
- package/dist/tests/server/onStateless.d.ts +1 -1
- package/dist/tests/server/onStoreDocument.d.ts +1 -1
- package/dist/tests/server/onUpgrade.d.ts +1 -1
- package/dist/tests/server/openDirectConnection.d.ts +1 -0
- package/dist/tests/server/requiresAuthentication.d.ts +1 -1
- package/dist/tests/server/websocketError.d.ts +1 -1
- package/dist/tests/transformer/TiptapTransformer.d.ts +1 -1
- package/dist/tests/utils/createDirectory.d.ts +1 -1
- package/dist/tests/utils/flushRedis.d.ts +1 -1
- package/dist/tests/utils/index.d.ts +9 -9
- package/dist/tests/utils/newHocuspocus.d.ts +2 -2
- package/dist/tests/utils/newHocuspocusProvider.d.ts +3 -3
- package/dist/tests/utils/newHocuspocusProviderWebsocket.d.ts +3 -3
- package/dist/tests/utils/randomInteger.d.ts +1 -1
- package/dist/tests/utils/redisConnectionSettings.d.ts +4 -4
- package/dist/tests/utils/removeDirectory.d.ts +1 -1
- package/dist/tests/utils/retryableAssertion.d.ts +2 -2
- package/dist/tests/utils/sleep.d.ts +1 -1
- package/package.json +4 -3
- package/src/HocuspocusProvider.ts +20 -16
- package/src/HocuspocusProviderWebsocket.ts +4 -3
- package/src/IncomingMessage.ts +1 -1
- package/src/MessageReceiver.ts +4 -4
- package/src/MessageSender.ts +1 -1
- package/src/OutgoingMessage.ts +1 -1
- package/src/OutgoingMessages/AuthenticationMessage.ts +2 -2
- package/src/OutgoingMessages/AwarenessMessage.ts +2 -2
- package/src/OutgoingMessages/CloseMessage.ts +2 -2
- package/src/OutgoingMessages/QueryAwarenessMessage.ts +2 -2
- package/src/OutgoingMessages/StatelessMessage.ts +2 -2
- package/src/OutgoingMessages/SyncStepOneMessage.ts +2 -2
- package/src/OutgoingMessages/SyncStepTwoMessage.ts +2 -2
- package/src/OutgoingMessages/UpdateMessage.ts +2 -2
- package/src/TiptapCollabProvider.ts +2 -2
- package/src/TiptapCollabProviderWebsocket.ts +1 -1
- package/src/index.ts +5 -5
- 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.
|
|
2316
|
-
this.
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
this.
|
|
2321
|
-
this.
|
|
2322
|
-
this.
|
|
2323
|
-
this.configuration
|
|
2324
|
-
this.configuration.
|
|
2325
|
-
this.
|
|
2326
|
-
this.on('
|
|
2327
|
-
this.on('
|
|
2328
|
-
this.on('
|
|
2329
|
-
this.on('
|
|
2330
|
-
this.on('
|
|
2331
|
-
this.on('
|
|
2332
|
-
this.on('
|
|
2333
|
-
this.on('
|
|
2334
|
-
this.on('
|
|
2335
|
-
this.
|
|
2336
|
-
this.configuration.websocketProvider.on('connect',
|
|
2337
|
-
this.configuration.websocketProvider.on('
|
|
2338
|
-
this.configuration.websocketProvider.on('open',
|
|
2339
|
-
this.configuration.websocketProvider.on('
|
|
2340
|
-
this.configuration.websocketProvider.on('
|
|
2341
|
-
this.configuration.websocketProvider.on('close', this.
|
|
2342
|
-
this.configuration.websocketProvider.on('close',
|
|
2343
|
-
this.configuration.websocketProvider.on('
|
|
2344
|
-
this.configuration.websocketProvider.on('
|
|
2345
|
-
this.configuration.websocketProvider.on('disconnect',
|
|
2346
|
-
this.configuration.websocketProvider.on('
|
|
2347
|
-
this.configuration.websocketProvider.on('destroy',
|
|
2348
|
-
this.
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
this.
|
|
2356
|
-
this.
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
this.
|
|
2365
|
-
this.
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
this.
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
this.
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
this.
|
|
2432
|
-
this.emit('
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
this.
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
this.
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
new
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
this.
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
this.
|
|
2509
|
-
this.
|
|
2510
|
-
this.
|
|
2511
|
-
this.
|
|
2512
|
-
this.
|
|
2513
|
-
this.
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
this.
|
|
2522
|
-
this.
|
|
2523
|
-
this.
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
this.
|
|
2528
|
-
this.
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
message
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
this.broadcast(
|
|
2552
|
-
this.broadcast(
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
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 };
|