@hocuspocus/provider 2.0.0-alpha.1 → 2.0.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 +427 -421
- package/dist/hocuspocus-provider.cjs.map +1 -1
- package/dist/hocuspocus-provider.esm.js +428 -422
- package/dist/hocuspocus-provider.esm.js.map +1 -1
- package/dist/packages/extension-monitor/src/Dashboard.d.ts +1 -0
- package/dist/packages/extension-redis/src/Redis.d.ts +1 -1
- package/dist/packages/extension-webhook/src/index.d.ts +1 -2
- package/dist/packages/provider/src/HocuspocusCloudProvider.d.ts +1 -1
- package/dist/packages/provider/src/HocuspocusProvider.d.ts +3 -2
- package/dist/packages/provider/src/HocuspocusProviderWebsocket.d.ts +1 -1
- package/dist/packages/provider/src/TiptapCollabProvider.d.ts +11 -0
- package/dist/packages/provider/src/index.d.ts +1 -1
- package/dist/packages/provider/src/types.d.ts +13 -13
- package/dist/packages/server/src/Connection.d.ts +2 -1
- package/dist/packages/server/src/Hocuspocus.d.ts +1 -0
- package/dist/packages/server/src/types.d.ts +5 -3
- package/dist/tests/server/onClose.d.ts +1 -0
- package/dist/tests/utils/newHocuspocus.d.ts +1 -1
- package/package.json +2 -2
- package/src/HocuspocusProvider.ts +18 -3
- package/src/HocuspocusProviderWebsocket.ts +3 -1
- package/src/TiptapCollabProvider.ts +33 -0
- package/src/index.ts +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
|
-
import { readAuthMessage, writeAuthentication,
|
|
2
|
+
import { readAuthMessage, writeAuthentication, WsReadyStates, Unauthorized, Forbidden, awarenessStatesToArray } from '@hocuspocus/common';
|
|
3
3
|
import { retry } from '@lifeomic/attempt';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -1742,151 +1742,477 @@ class UpdateMessage extends OutgoingMessage {
|
|
|
1742
1742
|
}
|
|
1743
1743
|
}
|
|
1744
1744
|
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
}
|
|
1751
|
-
get(args) {
|
|
1752
|
-
var _a;
|
|
1753
|
-
writeVarString(this.encoder, args.documentName);
|
|
1754
|
-
writeVarUint(this.encoder, this.type);
|
|
1755
|
-
writeVarString(this.encoder, (_a = args.payload) !== null && _a !== void 0 ? _a : '');
|
|
1756
|
-
return this.encoder;
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Utility module to work with urls.
|
|
1747
|
+
*
|
|
1748
|
+
* @module url
|
|
1749
|
+
*/
|
|
1759
1750
|
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
get(args) {
|
|
1767
|
-
writeVarString(this.encoder, args.documentName);
|
|
1768
|
-
writeVarUint(this.encoder, this.type);
|
|
1769
|
-
return this.encoder;
|
|
1770
|
-
}
|
|
1771
|
-
}
|
|
1751
|
+
/**
|
|
1752
|
+
* @param {Object<string,string>} params
|
|
1753
|
+
* @return {string}
|
|
1754
|
+
*/
|
|
1755
|
+
const encodeQueryParams = params =>
|
|
1756
|
+
map(params, (val, key) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&');
|
|
1772
1757
|
|
|
1773
|
-
class
|
|
1758
|
+
class HocuspocusProviderWebsocket extends EventEmitter {
|
|
1774
1759
|
constructor(configuration) {
|
|
1775
1760
|
super();
|
|
1776
1761
|
this.configuration = {
|
|
1777
|
-
|
|
1762
|
+
url: '',
|
|
1778
1763
|
// @ts-ignore
|
|
1779
1764
|
document: undefined,
|
|
1780
1765
|
// @ts-ignore
|
|
1781
1766
|
awareness: undefined,
|
|
1782
|
-
|
|
1767
|
+
WebSocketPolyfill: undefined,
|
|
1783
1768
|
parameters: {},
|
|
1769
|
+
connect: true,
|
|
1784
1770
|
broadcast: true,
|
|
1785
1771
|
forceSyncInterval: false,
|
|
1786
|
-
|
|
1787
|
-
|
|
1772
|
+
// TODO: this should depend on awareness.outdatedTime
|
|
1773
|
+
messageReconnectTimeout: 30000,
|
|
1774
|
+
// 1 second
|
|
1775
|
+
delay: 1000,
|
|
1776
|
+
// instant
|
|
1777
|
+
initialDelay: 0,
|
|
1778
|
+
// double the delay each time
|
|
1779
|
+
factor: 2,
|
|
1780
|
+
// unlimited retries
|
|
1781
|
+
maxAttempts: 0,
|
|
1782
|
+
// wait at least 1 second
|
|
1783
|
+
minDelay: 1000,
|
|
1784
|
+
// at least every 30 seconds
|
|
1785
|
+
maxDelay: 30000,
|
|
1786
|
+
// randomize
|
|
1787
|
+
jitter: true,
|
|
1788
|
+
// retry forever
|
|
1789
|
+
timeout: 0,
|
|
1788
1790
|
onOpen: () => null,
|
|
1789
1791
|
onConnect: () => null,
|
|
1790
1792
|
onMessage: () => null,
|
|
1791
1793
|
onOutgoingMessage: () => null,
|
|
1792
1794
|
onStatus: () => null,
|
|
1793
|
-
onSynced: () => null,
|
|
1794
1795
|
onDisconnect: () => null,
|
|
1795
1796
|
onClose: () => null,
|
|
1796
1797
|
onDestroy: () => null,
|
|
1797
1798
|
onAwarenessUpdate: () => null,
|
|
1798
1799
|
onAwarenessChange: () => null,
|
|
1799
|
-
onStateless: () => null,
|
|
1800
1800
|
quiet: false,
|
|
1801
1801
|
};
|
|
1802
1802
|
this.subscribedToBroadcastChannel = false;
|
|
1803
|
-
this.
|
|
1804
|
-
this.
|
|
1803
|
+
this.webSocket = null;
|
|
1804
|
+
this.shouldConnect = true;
|
|
1805
1805
|
this.status = WebSocketStatus.Disconnected;
|
|
1806
|
-
this.
|
|
1806
|
+
this.lastMessageReceived = 0;
|
|
1807
1807
|
this.mux = createMutex();
|
|
1808
1808
|
this.intervals = {
|
|
1809
1809
|
forceSync: null,
|
|
1810
|
+
connectionChecker: null,
|
|
1810
1811
|
};
|
|
1811
|
-
this.
|
|
1812
|
-
this.
|
|
1812
|
+
this.connectionAttempt = null;
|
|
1813
|
+
this.receivedOnOpenPayload = undefined;
|
|
1814
|
+
this.receivedOnStatusPayload = undefined;
|
|
1815
|
+
this.boundConnect = this.connect.bind(this);
|
|
1813
1816
|
this.setConfiguration(configuration);
|
|
1814
|
-
this.configuration.
|
|
1815
|
-
this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document);
|
|
1817
|
+
this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket;
|
|
1816
1818
|
this.on('open', this.configuration.onOpen);
|
|
1819
|
+
this.on('open', this.onOpen.bind(this));
|
|
1820
|
+
this.on('connect', this.configuration.onConnect);
|
|
1817
1821
|
this.on('message', this.configuration.onMessage);
|
|
1818
1822
|
this.on('outgoingMessage', this.configuration.onOutgoingMessage);
|
|
1819
|
-
this.on('
|
|
1823
|
+
this.on('status', this.configuration.onStatus);
|
|
1824
|
+
this.on('status', this.onStatus.bind(this));
|
|
1825
|
+
this.on('disconnect', this.configuration.onDisconnect);
|
|
1826
|
+
this.on('close', this.configuration.onClose);
|
|
1820
1827
|
this.on('destroy', this.configuration.onDestroy);
|
|
1821
1828
|
this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
|
|
1822
1829
|
this.on('awarenessChange', this.configuration.onAwarenessChange);
|
|
1823
|
-
this.on('
|
|
1824
|
-
this.on('
|
|
1825
|
-
this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
|
|
1826
|
-
this.configuration.websocketProvider.on('connect', this.configuration.onConnect);
|
|
1827
|
-
this.configuration.websocketProvider.on('connect', (e) => this.emit('connect', e));
|
|
1828
|
-
this.configuration.websocketProvider.on('open', this.onOpen.bind(this));
|
|
1829
|
-
this.configuration.websocketProvider.on('open', (e) => this.emit('open', e));
|
|
1830
|
-
this.configuration.websocketProvider.on('message', this.onMessage.bind(this));
|
|
1831
|
-
this.configuration.websocketProvider.on('close', this.onClose.bind(this));
|
|
1832
|
-
this.configuration.websocketProvider.on('close', this.configuration.onClose);
|
|
1833
|
-
this.configuration.websocketProvider.on('close', (e) => this.emit('close', e));
|
|
1834
|
-
this.configuration.websocketProvider.on('status', this.onStatus.bind(this));
|
|
1835
|
-
this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect);
|
|
1836
|
-
this.configuration.websocketProvider.on('disconnect', (e) => this.emit('disconnect', e));
|
|
1837
|
-
this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy);
|
|
1838
|
-
this.configuration.websocketProvider.on('destroy', (e) => this.emit('destroy', e));
|
|
1839
|
-
this.awareness.on('update', () => {
|
|
1840
|
-
this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) });
|
|
1841
|
-
});
|
|
1842
|
-
this.awareness.on('change', () => {
|
|
1843
|
-
this.emit('awarenessChange', { states: awarenessStatesToArray(this.awareness.getStates()) });
|
|
1844
|
-
});
|
|
1845
|
-
this.document.on('update', this.documentUpdateHandler.bind(this));
|
|
1846
|
-
this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
|
|
1830
|
+
this.on('close', this.onClose.bind(this));
|
|
1831
|
+
this.on('message', this.onMessage.bind(this));
|
|
1847
1832
|
this.registerEventListeners();
|
|
1848
|
-
|
|
1849
|
-
|
|
1833
|
+
this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.configuration.messageReconnectTimeout / 10);
|
|
1834
|
+
if (typeof configuration.connect !== 'undefined') {
|
|
1835
|
+
this.shouldConnect = configuration.connect;
|
|
1850
1836
|
}
|
|
1851
|
-
this.
|
|
1837
|
+
if (!this.shouldConnect) {
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
this.connect();
|
|
1852
1841
|
}
|
|
1853
|
-
|
|
1854
|
-
this.
|
|
1855
|
-
|
|
1856
|
-
|
|
1842
|
+
async onOpen(event) {
|
|
1843
|
+
this.receivedOnOpenPayload = event;
|
|
1844
|
+
}
|
|
1845
|
+
async onStatus(data) {
|
|
1846
|
+
this.receivedOnStatusPayload = data;
|
|
1847
|
+
}
|
|
1848
|
+
attach(provider) {
|
|
1849
|
+
if (this.receivedOnOpenPayload) {
|
|
1850
|
+
provider.onOpen(this.receivedOnOpenPayload);
|
|
1851
|
+
}
|
|
1852
|
+
if (this.receivedOnStatusPayload) {
|
|
1853
|
+
provider.onStatus(this.receivedOnStatusPayload);
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
detach(provider) {
|
|
1857
|
+
// tell the server to remove the listener
|
|
1857
1858
|
}
|
|
1858
1859
|
setConfiguration(configuration = {}) {
|
|
1859
1860
|
this.configuration = { ...this.configuration, ...configuration };
|
|
1860
1861
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1862
|
+
async connect() {
|
|
1863
|
+
if (this.status === WebSocketStatus.Connected) {
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
// Always cancel any previously initiated connection retryer instances
|
|
1867
|
+
if (this.cancelWebsocketRetry) {
|
|
1868
|
+
this.cancelWebsocketRetry();
|
|
1869
|
+
this.cancelWebsocketRetry = undefined;
|
|
1870
|
+
}
|
|
1871
|
+
this.shouldConnect = true;
|
|
1872
|
+
const abortableRetry = () => {
|
|
1873
|
+
let cancelAttempt = false;
|
|
1874
|
+
const retryPromise = retry(this.createWebSocketConnection.bind(this), {
|
|
1875
|
+
delay: this.configuration.delay,
|
|
1876
|
+
initialDelay: this.configuration.initialDelay,
|
|
1877
|
+
factor: this.configuration.factor,
|
|
1878
|
+
maxAttempts: this.configuration.maxAttempts,
|
|
1879
|
+
minDelay: this.configuration.minDelay,
|
|
1880
|
+
maxDelay: this.configuration.maxDelay,
|
|
1881
|
+
jitter: this.configuration.jitter,
|
|
1882
|
+
timeout: this.configuration.timeout,
|
|
1883
|
+
beforeAttempt: context => {
|
|
1884
|
+
if (!this.shouldConnect || cancelAttempt) {
|
|
1885
|
+
context.abort();
|
|
1886
|
+
}
|
|
1887
|
+
},
|
|
1888
|
+
}).catch((error) => {
|
|
1889
|
+
// If we aborted the connection attempt then don’t throw an error
|
|
1890
|
+
// ref: https://github.com/lifeomic/attempt/blob/master/src/index.ts#L136
|
|
1891
|
+
if (error && error.code !== 'ATTEMPT_ABORTED') {
|
|
1892
|
+
throw error;
|
|
1893
|
+
}
|
|
1894
|
+
});
|
|
1895
|
+
return {
|
|
1896
|
+
retryPromise,
|
|
1897
|
+
cancelFunc: () => {
|
|
1898
|
+
cancelAttempt = true;
|
|
1899
|
+
},
|
|
1900
|
+
};
|
|
1901
|
+
};
|
|
1902
|
+
const { retryPromise, cancelFunc } = abortableRetry();
|
|
1903
|
+
this.cancelWebsocketRetry = cancelFunc;
|
|
1904
|
+
return retryPromise;
|
|
1863
1905
|
}
|
|
1864
|
-
|
|
1865
|
-
return
|
|
1906
|
+
createWebSocketConnection() {
|
|
1907
|
+
return new Promise((resolve, reject) => {
|
|
1908
|
+
if (this.webSocket) {
|
|
1909
|
+
this.webSocket.close();
|
|
1910
|
+
this.webSocket = null;
|
|
1911
|
+
}
|
|
1912
|
+
// Init the WebSocket connection
|
|
1913
|
+
const ws = new this.configuration.WebSocketPolyfill(this.url);
|
|
1914
|
+
ws.binaryType = 'arraybuffer';
|
|
1915
|
+
ws.onmessage = (payload) => this.emit('message', payload);
|
|
1916
|
+
ws.onclose = (payload) => this.emit('close', { event: payload });
|
|
1917
|
+
ws.onopen = (payload) => this.emit('open', payload);
|
|
1918
|
+
ws.onerror = (err) => {
|
|
1919
|
+
reject(err);
|
|
1920
|
+
};
|
|
1921
|
+
this.webSocket = ws;
|
|
1922
|
+
// Reset the status
|
|
1923
|
+
this.status = WebSocketStatus.Connecting;
|
|
1924
|
+
this.emit('status', { status: WebSocketStatus.Connecting });
|
|
1925
|
+
// Store resolve/reject for later use
|
|
1926
|
+
this.connectionAttempt = {
|
|
1927
|
+
resolve,
|
|
1928
|
+
reject,
|
|
1929
|
+
};
|
|
1930
|
+
});
|
|
1866
1931
|
}
|
|
1867
|
-
|
|
1868
|
-
|
|
1932
|
+
onMessage(event) {
|
|
1933
|
+
this.resolveConnectionAttempt();
|
|
1869
1934
|
}
|
|
1870
|
-
|
|
1871
|
-
this.
|
|
1935
|
+
resolveConnectionAttempt() {
|
|
1936
|
+
if (this.connectionAttempt) {
|
|
1937
|
+
this.connectionAttempt.resolve();
|
|
1938
|
+
this.connectionAttempt = null;
|
|
1939
|
+
this.status = WebSocketStatus.Connected;
|
|
1940
|
+
this.emit('status', { status: WebSocketStatus.Connected });
|
|
1941
|
+
this.emit('connect');
|
|
1942
|
+
}
|
|
1872
1943
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1944
|
+
stopConnectionAttempt() {
|
|
1945
|
+
this.connectionAttempt = null;
|
|
1875
1946
|
}
|
|
1876
|
-
|
|
1877
|
-
|
|
1947
|
+
rejectConnectionAttempt() {
|
|
1948
|
+
var _a;
|
|
1949
|
+
(_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
|
|
1950
|
+
this.connectionAttempt = null;
|
|
1951
|
+
}
|
|
1952
|
+
checkConnection() {
|
|
1953
|
+
var _a;
|
|
1954
|
+
// Don’t check the connection when it’s not even established
|
|
1955
|
+
if (this.status !== WebSocketStatus.Connected) {
|
|
1878
1956
|
return;
|
|
1879
1957
|
}
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
sendStateless(payload) {
|
|
1883
|
-
this.send(StatelessMessage, { documentName: this.configuration.name, payload });
|
|
1884
|
-
}
|
|
1885
|
-
documentUpdateHandler(update, origin) {
|
|
1886
|
-
if (origin === this) {
|
|
1958
|
+
// Don’t close then connection while waiting for the first message
|
|
1959
|
+
if (!this.lastMessageReceived) {
|
|
1887
1960
|
return;
|
|
1888
1961
|
}
|
|
1889
|
-
|
|
1962
|
+
// Don’t close the connection when a message was received recently
|
|
1963
|
+
if (this.configuration.messageReconnectTimeout >= getUnixTime() - this.lastMessageReceived) {
|
|
1964
|
+
return;
|
|
1965
|
+
}
|
|
1966
|
+
// No message received in a long time, not even your own
|
|
1967
|
+
// Awareness updates, which are updated every 15 seconds.
|
|
1968
|
+
(_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.close();
|
|
1969
|
+
}
|
|
1970
|
+
registerEventListeners() {
|
|
1971
|
+
if (typeof window === 'undefined') {
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
window.addEventListener('online', this.boundConnect);
|
|
1975
|
+
}
|
|
1976
|
+
// Ensure that the URL always ends with /
|
|
1977
|
+
get serverUrl() {
|
|
1978
|
+
while (this.configuration.url[this.configuration.url.length - 1] === '/') {
|
|
1979
|
+
return this.configuration.url.slice(0, this.configuration.url.length - 1);
|
|
1980
|
+
}
|
|
1981
|
+
return this.configuration.url;
|
|
1982
|
+
}
|
|
1983
|
+
get url() {
|
|
1984
|
+
const encodedParams = encodeQueryParams(this.configuration.parameters);
|
|
1985
|
+
return `${this.serverUrl}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
|
|
1986
|
+
}
|
|
1987
|
+
disconnect() {
|
|
1988
|
+
this.shouldConnect = false;
|
|
1989
|
+
if (this.webSocket === null) {
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1992
|
+
try {
|
|
1993
|
+
this.webSocket.close();
|
|
1994
|
+
}
|
|
1995
|
+
catch {
|
|
1996
|
+
//
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
send(message) {
|
|
2000
|
+
var _a;
|
|
2001
|
+
if (((_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.readyState) === WsReadyStates.Open) {
|
|
2002
|
+
this.webSocket.send(message);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
onClose({ event }) {
|
|
2006
|
+
this.webSocket = null;
|
|
2007
|
+
if (this.status === WebSocketStatus.Connected) {
|
|
2008
|
+
this.status = WebSocketStatus.Disconnected;
|
|
2009
|
+
this.emit('status', { status: WebSocketStatus.Disconnected });
|
|
2010
|
+
this.emit('disconnect', { event });
|
|
2011
|
+
}
|
|
2012
|
+
if (event.code === Unauthorized.code) {
|
|
2013
|
+
if (event.reason === Unauthorized.reason) {
|
|
2014
|
+
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.');
|
|
2015
|
+
}
|
|
2016
|
+
else {
|
|
2017
|
+
console.warn(`[HocuspocusProvider] Connection closed with status Unauthorized: ${event.reason}`);
|
|
2018
|
+
}
|
|
2019
|
+
this.shouldConnect = false;
|
|
2020
|
+
}
|
|
2021
|
+
if (event.code === Forbidden.code) {
|
|
2022
|
+
if (!this.configuration.quiet) {
|
|
2023
|
+
console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.');
|
|
2024
|
+
return; // TODO REMOVE ME
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
if (this.connectionAttempt) {
|
|
2028
|
+
// That connection attempt failed.
|
|
2029
|
+
this.rejectConnectionAttempt();
|
|
2030
|
+
}
|
|
2031
|
+
else if (this.shouldConnect) {
|
|
2032
|
+
// The connection was closed by the server. Let’s just try again.
|
|
2033
|
+
this.connect();
|
|
2034
|
+
}
|
|
2035
|
+
// If we’ll reconnect, we’re done for now.
|
|
2036
|
+
if (this.shouldConnect) {
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
// The status is set correctly already.
|
|
2040
|
+
if (this.status === WebSocketStatus.Disconnected) {
|
|
2041
|
+
return;
|
|
2042
|
+
}
|
|
2043
|
+
// Let’s update the connection status.
|
|
2044
|
+
this.status = WebSocketStatus.Disconnected;
|
|
2045
|
+
this.emit('status', { status: WebSocketStatus.Disconnected });
|
|
2046
|
+
this.emit('disconnect', { event });
|
|
2047
|
+
}
|
|
2048
|
+
destroy() {
|
|
2049
|
+
this.emit('destroy');
|
|
2050
|
+
if (this.intervals.forceSync) {
|
|
2051
|
+
clearInterval(this.intervals.forceSync);
|
|
2052
|
+
}
|
|
2053
|
+
clearInterval(this.intervals.connectionChecker);
|
|
2054
|
+
// If there is still a connection attempt outstanding then we should stop
|
|
2055
|
+
// it before calling disconnect, otherwise it will be rejected in the onClose
|
|
2056
|
+
// handler and trigger a retry
|
|
2057
|
+
this.stopConnectionAttempt();
|
|
2058
|
+
this.disconnect();
|
|
2059
|
+
this.removeAllListeners();
|
|
2060
|
+
if (typeof window === 'undefined') {
|
|
2061
|
+
return;
|
|
2062
|
+
}
|
|
2063
|
+
window.removeEventListener('online', this.boundConnect);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
class StatelessMessage extends OutgoingMessage {
|
|
2068
|
+
constructor() {
|
|
2069
|
+
super(...arguments);
|
|
2070
|
+
this.type = MessageType.Stateless;
|
|
2071
|
+
this.description = 'A stateless message';
|
|
2072
|
+
}
|
|
2073
|
+
get(args) {
|
|
2074
|
+
var _a;
|
|
2075
|
+
writeVarString(this.encoder, args.documentName);
|
|
2076
|
+
writeVarUint(this.encoder, this.type);
|
|
2077
|
+
writeVarString(this.encoder, (_a = args.payload) !== null && _a !== void 0 ? _a : '');
|
|
2078
|
+
return this.encoder;
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
class CloseMessage extends OutgoingMessage {
|
|
2083
|
+
constructor() {
|
|
2084
|
+
super(...arguments);
|
|
2085
|
+
this.type = MessageType.CLOSE;
|
|
2086
|
+
this.description = 'Ask the server to close the connection';
|
|
2087
|
+
}
|
|
2088
|
+
get(args) {
|
|
2089
|
+
writeVarString(this.encoder, args.documentName);
|
|
2090
|
+
writeVarUint(this.encoder, this.type);
|
|
2091
|
+
return this.encoder;
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
class HocuspocusProvider extends EventEmitter {
|
|
2096
|
+
constructor(configuration) {
|
|
2097
|
+
super();
|
|
2098
|
+
this.configuration = {
|
|
2099
|
+
name: '',
|
|
2100
|
+
// @ts-ignore
|
|
2101
|
+
document: undefined,
|
|
2102
|
+
// @ts-ignore
|
|
2103
|
+
awareness: undefined,
|
|
2104
|
+
token: null,
|
|
2105
|
+
parameters: {},
|
|
2106
|
+
broadcast: true,
|
|
2107
|
+
forceSyncInterval: false,
|
|
2108
|
+
onAuthenticated: () => null,
|
|
2109
|
+
onAuthenticationFailed: () => null,
|
|
2110
|
+
onOpen: () => null,
|
|
2111
|
+
onConnect: () => null,
|
|
2112
|
+
onMessage: () => null,
|
|
2113
|
+
onOutgoingMessage: () => null,
|
|
2114
|
+
onStatus: () => null,
|
|
2115
|
+
onSynced: () => null,
|
|
2116
|
+
onDisconnect: () => null,
|
|
2117
|
+
onClose: () => null,
|
|
2118
|
+
onDestroy: () => null,
|
|
2119
|
+
onAwarenessUpdate: () => null,
|
|
2120
|
+
onAwarenessChange: () => null,
|
|
2121
|
+
onStateless: () => null,
|
|
2122
|
+
quiet: false,
|
|
2123
|
+
};
|
|
2124
|
+
this.subscribedToBroadcastChannel = false;
|
|
2125
|
+
this.isSynced = false;
|
|
2126
|
+
this.unsyncedChanges = 0;
|
|
2127
|
+
this.status = WebSocketStatus.Disconnected;
|
|
2128
|
+
this.isAuthenticated = false;
|
|
2129
|
+
this.mux = createMutex();
|
|
2130
|
+
this.intervals = {
|
|
2131
|
+
forceSync: null,
|
|
2132
|
+
};
|
|
2133
|
+
this.isConnected = true;
|
|
2134
|
+
this.boundBeforeUnload = this.beforeUnload.bind(this);
|
|
2135
|
+
this.boundBroadcastChannelSubscriber = this.broadcastChannelSubscriber.bind(this);
|
|
2136
|
+
this.setConfiguration(configuration);
|
|
2137
|
+
this.configuration.document = configuration.document ? configuration.document : new Y.Doc();
|
|
2138
|
+
this.configuration.awareness = configuration.awareness ? configuration.awareness : new Awareness(this.document);
|
|
2139
|
+
this.on('open', this.configuration.onOpen);
|
|
2140
|
+
this.on('message', this.configuration.onMessage);
|
|
2141
|
+
this.on('outgoingMessage', this.configuration.onOutgoingMessage);
|
|
2142
|
+
this.on('synced', this.configuration.onSynced);
|
|
2143
|
+
this.on('destroy', this.configuration.onDestroy);
|
|
2144
|
+
this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
|
|
2145
|
+
this.on('awarenessChange', this.configuration.onAwarenessChange);
|
|
2146
|
+
this.on('stateless', this.configuration.onStateless);
|
|
2147
|
+
this.on('authenticated', this.configuration.onAuthenticated);
|
|
2148
|
+
this.on('authenticationFailed', this.configuration.onAuthenticationFailed);
|
|
2149
|
+
this.configuration.websocketProvider.on('connect', this.configuration.onConnect);
|
|
2150
|
+
this.configuration.websocketProvider.on('connect', (e) => this.emit('connect', e));
|
|
2151
|
+
this.configuration.websocketProvider.on('open', this.onOpen.bind(this));
|
|
2152
|
+
this.configuration.websocketProvider.on('open', (e) => this.emit('open', e));
|
|
2153
|
+
this.configuration.websocketProvider.on('message', this.onMessage.bind(this));
|
|
2154
|
+
this.configuration.websocketProvider.on('close', this.onClose.bind(this));
|
|
2155
|
+
this.configuration.websocketProvider.on('close', this.configuration.onClose);
|
|
2156
|
+
this.configuration.websocketProvider.on('close', (e) => this.emit('close', e));
|
|
2157
|
+
this.configuration.websocketProvider.on('status', this.onStatus.bind(this));
|
|
2158
|
+
this.configuration.websocketProvider.on('disconnect', this.configuration.onDisconnect);
|
|
2159
|
+
this.configuration.websocketProvider.on('disconnect', (e) => this.emit('disconnect', e));
|
|
2160
|
+
this.configuration.websocketProvider.on('destroy', this.configuration.onDestroy);
|
|
2161
|
+
this.configuration.websocketProvider.on('destroy', (e) => this.emit('destroy', e));
|
|
2162
|
+
this.awareness.on('update', () => {
|
|
2163
|
+
this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) });
|
|
2164
|
+
});
|
|
2165
|
+
this.awareness.on('change', () => {
|
|
2166
|
+
this.emit('awarenessChange', { states: awarenessStatesToArray(this.awareness.getStates()) });
|
|
2167
|
+
});
|
|
2168
|
+
this.document.on('update', this.documentUpdateHandler.bind(this));
|
|
2169
|
+
this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
|
|
2170
|
+
this.registerEventListeners();
|
|
2171
|
+
if (this.configuration.forceSyncInterval) {
|
|
2172
|
+
this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.configuration.forceSyncInterval);
|
|
2173
|
+
}
|
|
2174
|
+
this.configuration.websocketProvider.attach(this);
|
|
2175
|
+
}
|
|
2176
|
+
onStatus({ status }) {
|
|
2177
|
+
this.status = status;
|
|
2178
|
+
this.configuration.onStatus({ status });
|
|
2179
|
+
this.emit('status', { status });
|
|
2180
|
+
}
|
|
2181
|
+
setConfiguration(configuration = {}) {
|
|
2182
|
+
if (!configuration.websocketProvider && configuration.url) {
|
|
2183
|
+
this.configuration.websocketProvider = new HocuspocusProviderWebsocket({ url: configuration.url });
|
|
2184
|
+
}
|
|
2185
|
+
this.configuration = { ...this.configuration, ...configuration };
|
|
2186
|
+
}
|
|
2187
|
+
get document() {
|
|
2188
|
+
return this.configuration.document;
|
|
2189
|
+
}
|
|
2190
|
+
get awareness() {
|
|
2191
|
+
return this.configuration.awareness;
|
|
2192
|
+
}
|
|
2193
|
+
get hasUnsyncedChanges() {
|
|
2194
|
+
return this.unsyncedChanges > 0;
|
|
2195
|
+
}
|
|
2196
|
+
forceSync() {
|
|
2197
|
+
this.send(SyncStepOneMessage, { document: this.document, documentName: this.configuration.name });
|
|
2198
|
+
}
|
|
2199
|
+
beforeUnload() {
|
|
2200
|
+
removeAwarenessStates(this.awareness, [this.document.clientID], 'window unload');
|
|
2201
|
+
}
|
|
2202
|
+
registerEventListeners() {
|
|
2203
|
+
if (typeof window === 'undefined') {
|
|
2204
|
+
return;
|
|
2205
|
+
}
|
|
2206
|
+
window.addEventListener('beforeunload', this.boundBeforeUnload);
|
|
2207
|
+
}
|
|
2208
|
+
sendStateless(payload) {
|
|
2209
|
+
this.send(StatelessMessage, { documentName: this.configuration.name, payload });
|
|
2210
|
+
}
|
|
2211
|
+
documentUpdateHandler(update, origin) {
|
|
2212
|
+
if (origin === this) {
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
this.unsyncedChanges += 1;
|
|
1890
2216
|
this.send(UpdateMessage, { update, documentName: this.configuration.name }, true);
|
|
1891
2217
|
}
|
|
1892
2218
|
awarenessUpdateHandler({ added, updated, removed }, origin) {
|
|
@@ -1950,6 +2276,8 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
1950
2276
|
}
|
|
1951
2277
|
}
|
|
1952
2278
|
send(message, args, broadcast = false) {
|
|
2279
|
+
if (!this.isConnected)
|
|
2280
|
+
return;
|
|
1953
2281
|
if (broadcast) {
|
|
1954
2282
|
this.mux(() => { this.broadcast(message, args); });
|
|
1955
2283
|
}
|
|
@@ -1984,6 +2312,7 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
1984
2312
|
this.document.off('update', this.documentUpdateHandler);
|
|
1985
2313
|
this.removeAllListeners();
|
|
1986
2314
|
this.send(CloseMessage, { documentName: this.configuration.name });
|
|
2315
|
+
this.isConnected = false;
|
|
1987
2316
|
if (typeof window === 'undefined') {
|
|
1988
2317
|
return;
|
|
1989
2318
|
}
|
|
@@ -2052,340 +2381,17 @@ class HocuspocusProvider extends EventEmitter {
|
|
|
2052
2381
|
}
|
|
2053
2382
|
}
|
|
2054
2383
|
|
|
2055
|
-
|
|
2056
|
-
* Utility module to work with urls.
|
|
2057
|
-
*
|
|
2058
|
-
* @module url
|
|
2059
|
-
*/
|
|
2060
|
-
|
|
2061
|
-
/**
|
|
2062
|
-
* @param {Object<string,string>} params
|
|
2063
|
-
* @return {string}
|
|
2064
|
-
*/
|
|
2065
|
-
const encodeQueryParams = params =>
|
|
2066
|
-
map(params, (val, key) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`).join('&');
|
|
2067
|
-
|
|
2068
|
-
class HocuspocusProviderWebsocket extends EventEmitter {
|
|
2069
|
-
constructor(configuration) {
|
|
2070
|
-
super();
|
|
2071
|
-
this.configuration = {
|
|
2072
|
-
url: '',
|
|
2073
|
-
// @ts-ignore
|
|
2074
|
-
document: undefined,
|
|
2075
|
-
// @ts-ignore
|
|
2076
|
-
awareness: undefined,
|
|
2077
|
-
WebSocketPolyfill: undefined,
|
|
2078
|
-
parameters: {},
|
|
2079
|
-
connect: true,
|
|
2080
|
-
broadcast: true,
|
|
2081
|
-
forceSyncInterval: false,
|
|
2082
|
-
// TODO: this should depend on awareness.outdatedTime
|
|
2083
|
-
messageReconnectTimeout: 30000,
|
|
2084
|
-
// 1 second
|
|
2085
|
-
delay: 1000,
|
|
2086
|
-
// instant
|
|
2087
|
-
initialDelay: 0,
|
|
2088
|
-
// double the delay each time
|
|
2089
|
-
factor: 2,
|
|
2090
|
-
// unlimited retries
|
|
2091
|
-
maxAttempts: 0,
|
|
2092
|
-
// wait at least 1 second
|
|
2093
|
-
minDelay: 1000,
|
|
2094
|
-
// at least every 30 seconds
|
|
2095
|
-
maxDelay: 30000,
|
|
2096
|
-
// randomize
|
|
2097
|
-
jitter: true,
|
|
2098
|
-
// retry forever
|
|
2099
|
-
timeout: 0,
|
|
2100
|
-
onOpen: () => null,
|
|
2101
|
-
onConnect: () => null,
|
|
2102
|
-
onMessage: () => null,
|
|
2103
|
-
onOutgoingMessage: () => null,
|
|
2104
|
-
onStatus: () => null,
|
|
2105
|
-
onDisconnect: () => null,
|
|
2106
|
-
onClose: () => null,
|
|
2107
|
-
onDestroy: () => null,
|
|
2108
|
-
onAwarenessUpdate: () => null,
|
|
2109
|
-
onAwarenessChange: () => null,
|
|
2110
|
-
quiet: false,
|
|
2111
|
-
};
|
|
2112
|
-
this.subscribedToBroadcastChannel = false;
|
|
2113
|
-
this.webSocket = null;
|
|
2114
|
-
this.shouldConnect = true;
|
|
2115
|
-
this.status = WebSocketStatus.Disconnected;
|
|
2116
|
-
this.lastMessageReceived = 0;
|
|
2117
|
-
this.mux = createMutex();
|
|
2118
|
-
this.intervals = {
|
|
2119
|
-
forceSync: null,
|
|
2120
|
-
connectionChecker: null,
|
|
2121
|
-
};
|
|
2122
|
-
this.connectionAttempt = null;
|
|
2123
|
-
this.receivedOnOpenPayload = undefined;
|
|
2124
|
-
this.receivedOnStatusPayload = undefined;
|
|
2125
|
-
this.boundConnect = this.connect.bind(this);
|
|
2126
|
-
this.setConfiguration(configuration);
|
|
2127
|
-
this.configuration.WebSocketPolyfill = configuration.WebSocketPolyfill ? configuration.WebSocketPolyfill : WebSocket;
|
|
2128
|
-
this.on('open', this.configuration.onOpen);
|
|
2129
|
-
this.on('open', this.onOpen.bind(this));
|
|
2130
|
-
this.on('connect', this.configuration.onConnect);
|
|
2131
|
-
this.on('message', this.configuration.onMessage);
|
|
2132
|
-
this.on('outgoingMessage', this.configuration.onOutgoingMessage);
|
|
2133
|
-
this.on('status', this.configuration.onStatus);
|
|
2134
|
-
this.on('status', this.onStatus.bind(this));
|
|
2135
|
-
this.on('disconnect', this.configuration.onDisconnect);
|
|
2136
|
-
this.on('close', this.configuration.onClose);
|
|
2137
|
-
this.on('destroy', this.configuration.onDestroy);
|
|
2138
|
-
this.on('awarenessUpdate', this.configuration.onAwarenessUpdate);
|
|
2139
|
-
this.on('awarenessChange', this.configuration.onAwarenessChange);
|
|
2140
|
-
this.on('close', this.onClose.bind(this));
|
|
2141
|
-
this.on('message', this.onMessage.bind(this));
|
|
2142
|
-
this.registerEventListeners();
|
|
2143
|
-
this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.configuration.messageReconnectTimeout / 10);
|
|
2144
|
-
if (typeof configuration.connect !== 'undefined') {
|
|
2145
|
-
this.shouldConnect = configuration.connect;
|
|
2146
|
-
}
|
|
2147
|
-
if (!this.shouldConnect) {
|
|
2148
|
-
return;
|
|
2149
|
-
}
|
|
2150
|
-
this.connect();
|
|
2151
|
-
}
|
|
2152
|
-
async onOpen(event) {
|
|
2153
|
-
this.receivedOnOpenPayload = event;
|
|
2154
|
-
}
|
|
2155
|
-
async onStatus(data) {
|
|
2156
|
-
this.receivedOnStatusPayload = data;
|
|
2157
|
-
}
|
|
2158
|
-
attach(provider) {
|
|
2159
|
-
if (this.receivedOnOpenPayload) {
|
|
2160
|
-
provider.onOpen(this.receivedOnOpenPayload);
|
|
2161
|
-
}
|
|
2162
|
-
if (this.receivedOnStatusPayload) {
|
|
2163
|
-
provider.onStatus(this.receivedOnStatusPayload);
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
detach(provider) {
|
|
2167
|
-
// tell the server to remove the listener
|
|
2168
|
-
}
|
|
2169
|
-
setConfiguration(configuration = {}) {
|
|
2170
|
-
this.configuration = { ...this.configuration, ...configuration };
|
|
2171
|
-
}
|
|
2172
|
-
async connect() {
|
|
2173
|
-
if (this.status === WebSocketStatus.Connected) {
|
|
2174
|
-
return;
|
|
2175
|
-
}
|
|
2176
|
-
// Always cancel any previously initiated connection retryer instances
|
|
2177
|
-
if (this.cancelWebsocketRetry) {
|
|
2178
|
-
this.cancelWebsocketRetry();
|
|
2179
|
-
this.cancelWebsocketRetry = undefined;
|
|
2180
|
-
}
|
|
2181
|
-
this.shouldConnect = true;
|
|
2182
|
-
const abortableRetry = () => {
|
|
2183
|
-
let cancelAttempt = false;
|
|
2184
|
-
const retryPromise = retry(this.createWebSocketConnection.bind(this), {
|
|
2185
|
-
delay: this.configuration.delay,
|
|
2186
|
-
initialDelay: this.configuration.initialDelay,
|
|
2187
|
-
factor: this.configuration.factor,
|
|
2188
|
-
maxAttempts: this.configuration.maxAttempts,
|
|
2189
|
-
minDelay: this.configuration.minDelay,
|
|
2190
|
-
maxDelay: this.configuration.maxDelay,
|
|
2191
|
-
jitter: this.configuration.jitter,
|
|
2192
|
-
timeout: this.configuration.timeout,
|
|
2193
|
-
beforeAttempt: context => {
|
|
2194
|
-
if (!this.shouldConnect || cancelAttempt) {
|
|
2195
|
-
context.abort();
|
|
2196
|
-
}
|
|
2197
|
-
},
|
|
2198
|
-
}).catch((error) => {
|
|
2199
|
-
// If we aborted the connection attempt then don’t throw an error
|
|
2200
|
-
// ref: https://github.com/lifeomic/attempt/blob/master/src/index.ts#L136
|
|
2201
|
-
if (error && error.code !== 'ATTEMPT_ABORTED') {
|
|
2202
|
-
throw error;
|
|
2203
|
-
}
|
|
2204
|
-
});
|
|
2205
|
-
return {
|
|
2206
|
-
retryPromise,
|
|
2207
|
-
cancelFunc: () => {
|
|
2208
|
-
cancelAttempt = true;
|
|
2209
|
-
},
|
|
2210
|
-
};
|
|
2211
|
-
};
|
|
2212
|
-
const { retryPromise, cancelFunc } = abortableRetry();
|
|
2213
|
-
this.cancelWebsocketRetry = cancelFunc;
|
|
2214
|
-
return retryPromise;
|
|
2215
|
-
}
|
|
2216
|
-
createWebSocketConnection() {
|
|
2217
|
-
return new Promise((resolve, reject) => {
|
|
2218
|
-
if (this.webSocket) {
|
|
2219
|
-
this.webSocket.close();
|
|
2220
|
-
this.webSocket = null;
|
|
2221
|
-
}
|
|
2222
|
-
// Init the WebSocket connection
|
|
2223
|
-
const ws = new this.configuration.WebSocketPolyfill(this.url);
|
|
2224
|
-
ws.binaryType = 'arraybuffer';
|
|
2225
|
-
ws.onmessage = (payload) => this.emit('message', payload);
|
|
2226
|
-
ws.onclose = (payload) => this.emit('close', { event: payload });
|
|
2227
|
-
ws.onopen = (payload) => this.emit('open', payload);
|
|
2228
|
-
ws.onerror = (err) => {
|
|
2229
|
-
reject(err);
|
|
2230
|
-
};
|
|
2231
|
-
this.webSocket = ws;
|
|
2232
|
-
// Reset the status
|
|
2233
|
-
this.status = WebSocketStatus.Connecting;
|
|
2234
|
-
this.emit('status', { status: WebSocketStatus.Connecting });
|
|
2235
|
-
// Store resolve/reject for later use
|
|
2236
|
-
this.connectionAttempt = {
|
|
2237
|
-
resolve,
|
|
2238
|
-
reject,
|
|
2239
|
-
};
|
|
2240
|
-
});
|
|
2241
|
-
}
|
|
2242
|
-
onMessage(event) {
|
|
2243
|
-
this.resolveConnectionAttempt();
|
|
2244
|
-
}
|
|
2245
|
-
resolveConnectionAttempt() {
|
|
2246
|
-
if (this.connectionAttempt) {
|
|
2247
|
-
this.connectionAttempt.resolve();
|
|
2248
|
-
this.connectionAttempt = null;
|
|
2249
|
-
this.status = WebSocketStatus.Connected;
|
|
2250
|
-
this.emit('status', { status: WebSocketStatus.Connected });
|
|
2251
|
-
this.emit('connect');
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
stopConnectionAttempt() {
|
|
2255
|
-
this.connectionAttempt = null;
|
|
2256
|
-
}
|
|
2257
|
-
rejectConnectionAttempt() {
|
|
2258
|
-
var _a;
|
|
2259
|
-
(_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
|
|
2260
|
-
this.connectionAttempt = null;
|
|
2261
|
-
}
|
|
2262
|
-
checkConnection() {
|
|
2263
|
-
var _a;
|
|
2264
|
-
// Don’t check the connection when it’s not even established
|
|
2265
|
-
if (this.status !== WebSocketStatus.Connected) {
|
|
2266
|
-
return;
|
|
2267
|
-
}
|
|
2268
|
-
// Don’t close then connection while waiting for the first message
|
|
2269
|
-
if (!this.lastMessageReceived) {
|
|
2270
|
-
return;
|
|
2271
|
-
}
|
|
2272
|
-
// Don’t close the connection when a message was received recently
|
|
2273
|
-
if (this.configuration.messageReconnectTimeout >= getUnixTime() - this.lastMessageReceived) {
|
|
2274
|
-
return;
|
|
2275
|
-
}
|
|
2276
|
-
// No message received in a long time, not even your own
|
|
2277
|
-
// Awareness updates, which are updated every 15 seconds.
|
|
2278
|
-
(_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.close();
|
|
2279
|
-
}
|
|
2280
|
-
registerEventListeners() {
|
|
2281
|
-
if (typeof window === 'undefined') {
|
|
2282
|
-
return;
|
|
2283
|
-
}
|
|
2284
|
-
window.addEventListener('online', this.boundConnect);
|
|
2285
|
-
}
|
|
2286
|
-
// Ensure that the URL always ends with /
|
|
2287
|
-
get serverUrl() {
|
|
2288
|
-
while (this.configuration.url[this.configuration.url.length - 1] === '/') {
|
|
2289
|
-
return this.configuration.url.slice(0, this.configuration.url.length - 1);
|
|
2290
|
-
}
|
|
2291
|
-
return this.configuration.url;
|
|
2292
|
-
}
|
|
2293
|
-
get url() {
|
|
2294
|
-
const encodedParams = encodeQueryParams(this.configuration.parameters);
|
|
2295
|
-
return `${this.serverUrl}${encodedParams.length === 0 ? '' : `?${encodedParams}`}`;
|
|
2296
|
-
}
|
|
2297
|
-
disconnect() {
|
|
2298
|
-
this.shouldConnect = false;
|
|
2299
|
-
if (this.webSocket === null) {
|
|
2300
|
-
return;
|
|
2301
|
-
}
|
|
2302
|
-
try {
|
|
2303
|
-
this.webSocket.close();
|
|
2304
|
-
}
|
|
2305
|
-
catch {
|
|
2306
|
-
//
|
|
2307
|
-
}
|
|
2308
|
-
}
|
|
2309
|
-
send(message) {
|
|
2310
|
-
var _a;
|
|
2311
|
-
if (((_a = this.webSocket) === null || _a === void 0 ? void 0 : _a.readyState) === WsReadyStates.Open) {
|
|
2312
|
-
this.webSocket.send(message);
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
onClose({ event }) {
|
|
2316
|
-
this.webSocket = null;
|
|
2317
|
-
if (this.status === WebSocketStatus.Connected) {
|
|
2318
|
-
this.status = WebSocketStatus.Disconnected;
|
|
2319
|
-
this.emit('status', { status: WebSocketStatus.Disconnected });
|
|
2320
|
-
this.emit('disconnect', { event });
|
|
2321
|
-
}
|
|
2322
|
-
if (event.code === Unauthorized.code) {
|
|
2323
|
-
if (!this.configuration.quiet) {
|
|
2324
|
-
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.');
|
|
2325
|
-
}
|
|
2326
|
-
this.shouldConnect = false;
|
|
2327
|
-
}
|
|
2328
|
-
if (event.code === Forbidden.code) {
|
|
2329
|
-
if (!this.configuration.quiet) {
|
|
2330
|
-
console.warn('[HocuspocusProvider] The provided authentication token isn’t allowed to connect to this server. Will try again.');
|
|
2331
|
-
return; // TODO REMOVE ME
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
if (this.connectionAttempt) {
|
|
2335
|
-
// That connection attempt failed.
|
|
2336
|
-
this.rejectConnectionAttempt();
|
|
2337
|
-
}
|
|
2338
|
-
else if (this.shouldConnect) {
|
|
2339
|
-
// The connection was closed by the server. Let’s just try again.
|
|
2340
|
-
this.connect();
|
|
2341
|
-
}
|
|
2342
|
-
// If we’ll reconnect, we’re done for now.
|
|
2343
|
-
if (this.shouldConnect) {
|
|
2344
|
-
return;
|
|
2345
|
-
}
|
|
2346
|
-
// The status is set correctly already.
|
|
2347
|
-
if (this.status === WebSocketStatus.Disconnected) {
|
|
2348
|
-
return;
|
|
2349
|
-
}
|
|
2350
|
-
// Let’s update the connection status.
|
|
2351
|
-
this.status = WebSocketStatus.Disconnected;
|
|
2352
|
-
this.emit('status', { status: WebSocketStatus.Disconnected });
|
|
2353
|
-
this.emit('disconnect', { event });
|
|
2354
|
-
}
|
|
2355
|
-
destroy() {
|
|
2356
|
-
this.emit('destroy');
|
|
2357
|
-
if (this.intervals.forceSync) {
|
|
2358
|
-
clearInterval(this.intervals.forceSync);
|
|
2359
|
-
}
|
|
2360
|
-
clearInterval(this.intervals.connectionChecker);
|
|
2361
|
-
// If there is still a connection attempt outstanding then we should stop
|
|
2362
|
-
// it before calling disconnect, otherwise it will be rejected in the onClose
|
|
2363
|
-
// handler and trigger a retry
|
|
2364
|
-
this.stopConnectionAttempt();
|
|
2365
|
-
this.disconnect();
|
|
2366
|
-
this.removeAllListeners();
|
|
2367
|
-
if (typeof window === 'undefined') {
|
|
2368
|
-
return;
|
|
2369
|
-
}
|
|
2370
|
-
window.removeEventListener('online', this.boundConnect);
|
|
2371
|
-
}
|
|
2372
|
-
}
|
|
2373
|
-
|
|
2374
|
-
class HocuspocusCloudProvider extends HocuspocusProvider {
|
|
2384
|
+
class TiptapCollabProvider extends HocuspocusProvider {
|
|
2375
2385
|
constructor(configuration) {
|
|
2376
|
-
if (!configuration.
|
|
2377
|
-
configuration.
|
|
2386
|
+
if (!configuration.websocketProvider) {
|
|
2387
|
+
configuration.websocketProvider = new HocuspocusProviderWebsocket({ url: `wss://${configuration.appId}.collab.tiptap.cloud` });
|
|
2378
2388
|
}
|
|
2379
|
-
if (configuration.
|
|
2380
|
-
|
|
2381
|
-
configuration.parameters = {};
|
|
2382
|
-
}
|
|
2383
|
-
configuration.parameters.key = configuration.key;
|
|
2389
|
+
if (!configuration.token) {
|
|
2390
|
+
configuration.token = 'notoken';
|
|
2384
2391
|
}
|
|
2385
|
-
configuration.websocketProvider = new HocuspocusProviderWebsocket({ url: configuration.url });
|
|
2386
2392
|
super(configuration);
|
|
2387
2393
|
}
|
|
2388
2394
|
}
|
|
2389
2395
|
|
|
2390
|
-
export {
|
|
2396
|
+
export { HocuspocusProvider, HocuspocusProviderWebsocket, MessageType, TiptapCollabProvider, WebSocketStatus };
|
|
2391
2397
|
//# sourceMappingURL=hocuspocus-provider.esm.js.map
|