Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@hocuspocus/provider

Package Overview
Dependencies
Maintainers
3
Versions
104
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@hocuspocus/provider - npm Package Compare versions

Comparing version 1.0.0-alpha.14 to 1.0.0-alpha.15

8

CHANGELOG.md

@@ -6,2 +6,10 @@ # Change Log

# [1.0.0-alpha.15](https://github.com/ueberdosis/hocuspocus/compare/@hocuspocus/provider@1.0.0-alpha.14...@hocuspocus/provider@1.0.0-alpha.15) (2021-09-14)
**Note:** Version bump only for package @hocuspocus/provider
# [1.0.0-alpha.14](https://github.com/ueberdosis/hocuspocus/compare/@hocuspocus/provider@1.0.0-alpha.13...@hocuspocus/provider@1.0.0-alpha.14) (2021-09-03)

@@ -8,0 +16,0 @@

171

dist/hocuspocus-provider.esm.js
import * as Y from 'yjs';
import { retry } from '@lifeomic/attempt';

@@ -261,4 +262,2 @@ /**

const floor = Math.floor;
const round = Math.round;
const log10 = Math.log10;

@@ -1759,3 +1758,2 @@ /**

// @ts-nocheck
var WebSocketStatus;

@@ -1771,2 +1769,7 @@ (function (WebSocketStatus) {

this.options = {
// @ts-ignore
document: undefined,
// @ts-ignore
awareness: undefined,
WebSocketPolyfill: undefined,
url: '',

@@ -1776,10 +1779,23 @@ name: '',

parameters: {},
debug: false,
connect: true,
broadcast: true,
forceSyncInterval: false,
reconnectTimeoutBase: 1200,
maxReconnectTimeout: 2500,
// TODO: this should depend on awareness.outdatedTime
messageReconnectTimeout: 30000,
// 1 second
delay: 1000,
// instant
initialDelay: 0,
// double the delay each time
factor: 2,
// unlimited retries
maxAttempts: 0,
// wait at least 1 second
minDelay: 1000,
// at least every 30 seconds
maxDelay: 30000,
// randomize
jitter: true,
// retry forever
timeout: 0,
onAuthenticated: () => null,

@@ -1803,3 +1819,2 @@ onAuthenticationFailed: () => null,

this.status = WebSocketStatus.Disconnected;
this.failedConnectionAttempts = 0;
this.isSynced = false;

@@ -1813,7 +1828,6 @@ this.isAuthenticated = false;

};
this.connectionAttempt = null;
this.setOptions(options);
this.options.document = options.document ? options.document : new Y.Doc();
this.options.awareness = options.awareness ? options.awareness : new Awareness(this.document);
this.options.WebSocketPolyfill = options.WebSocketPolyfill ? options.WebSocketPolyfill : WebSocket;
this.shouldConnect = options.connect !== undefined ? options.connect : this.shouldConnect;
this.on('open', this.options.onOpen);

@@ -1833,21 +1847,21 @@ this.on('authenticated', this.options.onAuthenticated);

this.awareness.on('update', () => {
this.emit('awarenessUpdate', {
states: awarenessStatesToArray(this.awareness.getStates()),
});
this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) });
});
this.awareness.on('change', () => {
this.emit('awarenessChange', {
states: awarenessStatesToArray(this.awareness.getStates()),
});
this.emit('awarenessChange', { states: awarenessStatesToArray(this.awareness.getStates()) });
});
this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.options.messageReconnectTimeout / 10);
this.document.on('update', this.documentUpdateHandler.bind(this));
this.awareness.on('update', this.awarenessUpdateHandler.bind(this));
this.registerBeforeUnloadEventListener();
this.intervals.connectionChecker = setInterval(this.checkConnection.bind(this), this.options.messageReconnectTimeout / 10);
if (this.options.forceSyncInterval) {
this.intervals.forceSync = setInterval(this.forceSync.bind(this), this.options.forceSyncInterval);
}
if (this.options.connect) {
this.connect();
if (typeof options.connect !== 'undefined') {
this.shouldConnect = options.connect;
}
if (!this.shouldConnect) {
return;
}
this.connect();
}

@@ -1857,2 +1871,51 @@ setOptions(options = {}) {

}
connect() {
if (this.status === WebSocketStatus.Connected) {
return;
}
this.shouldConnect = true;
this.subscribeToBroadcastChannel();
retry(this.createWebSocketConnection.bind(this), {
delay: this.options.delay,
initialDelay: this.options.initialDelay,
factor: this.options.factor,
maxAttempts: this.options.maxAttempts,
minDelay: this.options.minDelay,
maxDelay: this.options.maxDelay,
jitter: this.options.jitter,
timeout: this.options.timeout,
});
}
createWebSocketConnection() {
return new Promise((resolve, reject) => {
// Init the WebSocket connection
this.webSocket = new this.options.WebSocketPolyfill(this.url);
this.webSocket.binaryType = 'arraybuffer';
this.webSocket.onmessage = this.onMessage.bind(this);
this.webSocket.onclose = this.onClose.bind(this);
this.webSocket.onopen = this.onOpen.bind(this);
this.webSocket.onerror = () => {
reject();
};
// Reset the status
this.synced = false;
this.status = WebSocketStatus.Connecting;
this.emit('status', { status: 'connecting' });
// Store resolve/reject for later use
this.connectionAttempt = {
resolve,
reject,
};
});
}
resolveConnectionAttempt() {
var _a;
(_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.resolve();
this.connectionAttempt = null;
}
rejectConnectionAttempt() {
var _a;
(_a = this.connectionAttempt) === null || _a === void 0 ? void 0 : _a.reject();
this.connectionAttempt = null;
}
get document() {

@@ -1865,7 +1928,7 @@ return this.options.document;

checkConnection() {
// Don’t close the connection when it’s not established anyway
// Don’t check the connection when it’s not even established
if (this.status !== WebSocketStatus.Connected) {
return;
}
// Don’t just close then connection while waiting for the first message
// Don’t close then connection while waiting for the first message
if (!this.lastMessageReceived) {

@@ -1911,3 +1974,2 @@ return;

this.emit('authenticationFailed', { reason });
this.log('Permission denied', reason);
this.isAuthenticated = false;

@@ -1946,9 +2008,2 @@ this.shouldConnect = false;

}
connect() {
this.shouldConnect = true;
if (this.status !== WebSocketStatus.Connected) {
this.createWebSocketConnection();
this.subscribeToBroadcastChannel();
}
}
disconnect() {

@@ -1967,15 +2022,2 @@ this.shouldConnect = false;

}
createWebSocketConnection() {
if (this.webSocket !== null) {
return;
}
this.webSocket = new this.options.WebSocketPolyfill(this.url);
this.webSocket.binaryType = 'arraybuffer';
this.status = WebSocketStatus.Connecting;
this.synced = false;
this.webSocket.onmessage = this.onMessage.bind(this);
this.webSocket.onclose = this.onClose.bind(this);
this.webSocket.onopen = this.onOpen.bind(this);
this.emit('status', { status: 'connecting' });
}
onOpen(event) {

@@ -1995,3 +2037,2 @@ this.emit('open', { event });

async webSocketConnectionEstablished() {
this.failedConnectionAttempts = 0;
this.status = WebSocketStatus.Connected;

@@ -2001,7 +2042,9 @@ this.emit('status', { status: 'connected' });

if (this.isAuthenticationRequired) {
const token = await this.getToken();
this.send(AuthenticationMessage, { token });
this.send(AuthenticationMessage, {
token: await this.getToken(),
});
return;
}
this.startSync();
this.resolveConnectionAttempt();
}

@@ -2019,5 +2062,3 @@ startSync() {

if (broadcast) {
this.mux(() => {
this.broadcast(Message, args);
});
this.mux(() => { this.broadcast(Message, args); });
}

@@ -2038,6 +2079,6 @@ if (this.status === WebSocketStatus.Connected) {

this.emit('close', { event });
this.webSocket = null;
this.isAuthenticated = false;
this.webSocket = null;
this.synced = false;
if (this.status === WebSocketStatus.Connected) {
this.synced = false;
// update awareness (all users except local left)

@@ -2049,16 +2090,22 @@ removeAwarenessStates(this.awareness, Array.from(this.awareness.getStates().keys()).filter(client => client !== this.document.clientID), this);

}
else {
this.failedConnectionAttempts += 1;
if (this.connectionAttempt) {
// Okay, that connection attempt failed …
this.rejectConnectionAttempt();
}
else if (this.shouldConnect) {
// The connection was closed by the server, so let’s just try again.
this.connect();
}
// If we’ll reconnect anyway, we’re done for now.
if (this.shouldConnect) {
const wait = round(min(log10(this.failedConnectionAttempts + 1) * this.options.reconnectTimeoutBase, this.options.maxReconnectTimeout));
this.log(`[close] Reconnecting in ${wait}ms …`);
setTimeout(this.createWebSocketConnection.bind(this), wait);
return;
}
if (this.status !== WebSocketStatus.Disconnected) {
this.status = WebSocketStatus.Disconnected;
this.emit('status', { status: 'disconnected' });
this.emit('disconnect', { event });
// The status is set correctly already.
if (this.status === WebSocketStatus.Disconnected) {
return;
}
// Let’s update the connection status.
this.status = WebSocketStatus.Disconnected;
this.emit('status', { status: 'disconnected' });
this.emit('disconnect', { event });
}

@@ -2082,3 +2129,3 @@ destroy() {

const message = new IncomingMessage(data);
new MessageReceiver(message, this)
new MessageReceiver(message)
.setBroadcasted(true)

@@ -2121,8 +2168,2 @@ .apply(this, false);

}
log(message) {
if (!this.options.debug) {
return;
}
console.log(message);
}
setAwarenessField(key, value) {

@@ -2129,0 +2170,0 @@ this.awareness.setLocalStateField(key, value);

@@ -7,2 +7,3 @@ import * as Y from 'yjs';

import { OutgoingMessage } from './OutgoingMessage';
import { ConstructableOutgoingMessage } from './types';
export declare enum WebSocketStatus {

@@ -14,20 +15,83 @@ Connecting = "connecting",

export interface HocuspocusProviderOptions {
/**
* URL of your @hocuspocus/server instance
*/
url: string;
/**
* The identifier/name of your document
*/
name: string;
/**
* The actual Y.js document
*/
document: Y.Doc;
/**
* Pass `false` to start the connection manually.
*/
connect: boolean;
/**
* Pass false to disable broadcasting between browser tabs.
*/
broadcast: boolean;
/**
* An Awareness instance to keep the presence state of all clients.
*/
awareness: Awareness;
token: string | (() => string) | (() => Promise<string>);
/**
* A token that’s sent to the backend for authentication purposes.
*/
token: string | (() => string) | (() => Promise<string>) | null;
/**
* URL parameters that should be added.
*/
parameters: {
[key: string]: any;
};
/**
* An optional WebSocket polyfill, for example for Node.js
*/
WebSocketPolyfill: any;
/**
* Force syncing the document in the defined interval.
*/
forceSyncInterval: false | number;
reconnectTimeoutBase: number;
maxReconnectTimeout: number;
/**
* Disconnect when no message is received for the defined amount of milliseconds.
*/
messageReconnectTimeout: number;
/**
* The delay between each attempt in milliseconds. You can provide a factor to have the delay grow exponentially.
*/
delay: number;
/**
* The intialDelay is the amount of time to wait before making the first attempt. This option should typically be 0 since you typically want the first attempt to happen immediately.
*/
initialDelay: number;
/**
* The factor option is used to grow the delay exponentially.
*/
factor: number;
/**
* The maximum number of attempts or 0 if there is no limit on number of attempts.
*/
maxAttempts: number;
/**
* minDelay is used to set a lower bound of delay when jitter is enabled. This property has no effect if jitter is disabled.
*/
minDelay: number;
/**
* The maxDelay option is used to set an upper bound for the delay when factor is enabled. A value of 0 can be provided if there should be no upper bound when calculating delay.
*/
maxDelay: number;
/**
* If jitter is true then the calculated delay will be a random integer value between minDelay and the calculated delay for the current iteration.
*/
jitter: boolean;
/**
* A timeout in milliseconds. If timeout is non-zero then a timer is set using setTimeout. If the timeout is triggered then future attempts will be aborted.
*/
timeout: number;
onAuthenticated: () => void;
onAuthenticationFailed: ({ reason: string }: {
reason: any;
onAuthenticationFailed: ({ reason }: {
reason: string;
}) => void;

@@ -45,7 +109,5 @@ onOpen: (event: OpenEvent) => void;

onAwarenessChange: (states: any) => void;
debug: boolean;
}
export declare class HocuspocusProvider extends EventEmitter {
options: HocuspocusProviderOptions;
awareness: Awareness;
subscribedToBroadcastChannel: boolean;

@@ -55,3 +117,2 @@ webSocket: any;

status: WebSocketStatus;
failedConnectionAttempts: number;
isSynced: boolean;

@@ -62,4 +123,12 @@ isAuthenticated: boolean;

intervals: any;
connectionAttempt: {
resolve: (value?: any) => void;
reject: (reason?: any) => void;
} | null;
constructor(options?: Partial<HocuspocusProviderOptions>);
setOptions(options?: Partial<HocuspocusProviderOptions>): void;
connect(): void;
createWebSocketConnection(): Promise<unknown>;
resolveConnectionAttempt(): void;
rejectConnectionAttempt(): void;
get document(): Y.Doc;

@@ -79,10 +148,8 @@ get awareness(): Awareness;

get isAuthenticationRequired(): boolean;
connect(): void;
disconnect(): void;
createWebSocketConnection(): void;
onOpen(event: OpenEvent): void;
getToken(): Promise<string>;
getToken(): Promise<string | null>;
webSocketConnectionEstablished(): Promise<void>;
startSync(): void;
send(Message: OutgoingMessage, args: any, broadcast?: boolean): void;
send(Message: ConstructableOutgoingMessage, args: any, broadcast?: boolean): void;
onMessage(event: MessageEvent): void;

@@ -95,5 +162,4 @@ onClose(event: CloseEvent): void;

disconnectBroadcastChannel(): void;
broadcast(Message: OutgoingMessage, args: any): void;
log(message: string): void;
broadcast(Message: ConstructableOutgoingMessage, args?: any): void;
setAwarenessField(key: string, value: any): void;
}
import { Encoder } from 'lib0/encoding';
import { AuthenticationMessage } from './OutgoingMessages/AuthenticationMessage';
import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage';
import { QueryAwarenessMessage } from './OutgoingMessages/QueryAwarenessMessage';
import { SyncStepOneMessage } from './OutgoingMessages/SyncStepOneMessage';
import { SyncStepTwoMessage } from './OutgoingMessages/SyncStepTwoMessage';
import { UpdateMessage } from './OutgoingMessages/UpdateMessage';
import { Constructable } from './types';
import { ConstructableOutgoingMessage } from './types';
export declare class MessageSender {
encoder: Encoder;
message: any;
constructor(Message: Constructable<AuthenticationMessage> | Constructable<AwarenessMessage> | Constructable<QueryAwarenessMessage> | Constructable<SyncStepOneMessage> | Constructable<SyncStepTwoMessage> | Constructable<UpdateMessage>, args?: any);
constructor(Message: ConstructableOutgoingMessage, args?: any);
create(): Uint8Array;

@@ -14,0 +8,0 @@ send(webSocket: any): void;

import { Awareness } from 'y-protocols/awareness';
import * as Y from 'yjs';
import { Encoder } from 'lib0/encoding';
import { AuthenticationMessage } from './OutgoingMessages/AuthenticationMessage';
import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage';
import { QueryAwarenessMessage } from './OutgoingMessages/QueryAwarenessMessage';
import { SyncStepOneMessage } from './OutgoingMessages/SyncStepOneMessage';
import { SyncStepTwoMessage } from './OutgoingMessages/SyncStepTwoMessage';
import { UpdateMessage } from './OutgoingMessages/UpdateMessage';
export declare enum MessageType {

@@ -28,1 +34,2 @@ Sync = 0,

}
export declare type ConstructableOutgoingMessage = Constructable<AuthenticationMessage> | Constructable<AwarenessMessage> | Constructable<QueryAwarenessMessage> | Constructable<SyncStepOneMessage> | Constructable<SyncStepTwoMessage> | Constructable<UpdateMessage>;

@@ -46,5 +46,5 @@ import WebSocket from 'ws';

/**
* Get the number of active connections
* Get the number of active connections for this document
*/
connectionsCount(): number;
getConnectionsCount(): number;
/**

@@ -51,0 +51,0 @@ * Get an array of registered connections

@@ -29,2 +29,10 @@ /// <reference types="node" />

/**
* Get the total number of active documents
*/
getDocumentsCount(): number;
/**
* Get the total number of active connections
*/
getConnectionsCount(): number;
/**
* Force close one or more connections

@@ -31,0 +39,0 @@ */

@@ -80,2 +80,3 @@ /// <reference types="node" />

documentName: string;
instance: Hocuspocus;
requestHeaders: IncomingHttpHeaders;

@@ -91,2 +92,3 @@ requestParameters: URLSearchParams;

documentName: string;
instance: Hocuspocus;
requestHeaders: IncomingHttpHeaders;

@@ -102,2 +104,3 @@ requestParameters: URLSearchParams;

documentName: string;
instance: Hocuspocus;
requestHeaders: IncomingHttpHeaders;

@@ -122,2 +125,3 @@ requestParameters: URLSearchParams;

export interface onDestroyPayload {
instance: Hocuspocus;
}

@@ -124,0 +128,0 @@ export interface onConfigurePayload {

{
"name": "@hocuspocus/provider",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "hocuspocus provider",

@@ -26,2 +26,3 @@ "homepage": "https://hocuspocus.dev",

"dependencies": {
"@lifeomic/attempt": "^3.0.0",
"lib0": "^0.2.42",

@@ -31,3 +32,3 @@ "y-protocols": "^1.0.5",

},
"gitHead": "3b1ae61a70eca44992e40e545b2d893a4cef75b2",
"gitHead": "d84b5516f65dfe096fd6c633f96309a6f65a9811",
"publishConfig": {

@@ -34,0 +35,0 @@ "access": "public"

@@ -1,2 +0,1 @@

// @ts-nocheck
import * as Y from 'yjs'

@@ -7,6 +6,5 @@ import * as bc from 'lib0/broadcastchannel'

import * as mutex from 'lib0/mutex'
import * as math from 'lib0/math'
import * as url from 'lib0/url'
import { CloseEvent, MessageEvent, OpenEvent } from 'ws'
import { retry } from '@lifeomic/attempt'
import EventEmitter from './EventEmitter'

@@ -24,2 +22,3 @@ import { IncomingMessage } from './IncomingMessage'

import awarenessStatesToArray from './utils/awarenessStatesToArray'
import { ConstructableOutgoingMessage } from './types'

@@ -33,17 +32,80 @@ export enum WebSocketStatus {

export interface HocuspocusProviderOptions {
/**
* URL of your @hocuspocus/server instance
*/
url: string,
/**
* The identifier/name of your document
*/
name: string,
/**
* The actual Y.js document
*/
document: Y.Doc,
/**
* Pass `false` to start the connection manually.
*/
connect: boolean,
/**
* Pass false to disable broadcasting between browser tabs.
*/
broadcast: boolean,
/**
* An Awareness instance to keep the presence state of all clients.
*/
awareness: Awareness,
token: string | (() => string) | (() => Promise<string>),
/**
* A token that’s sent to the backend for authentication purposes.
*/
token: string | (() => string) | (() => Promise<string>) | null,
/**
* URL parameters that should be added.
*/
parameters: { [key: string]: any },
/**
* An optional WebSocket polyfill, for example for Node.js
*/
WebSocketPolyfill: any,
/**
* Force syncing the document in the defined interval.
*/
forceSyncInterval: false | number,
reconnectTimeoutBase: number,
maxReconnectTimeout: number,
/**
* Disconnect when no message is received for the defined amount of milliseconds.
*/
messageReconnectTimeout: number,
/**
* The delay between each attempt in milliseconds. You can provide a factor to have the delay grow exponentially.
*/
delay: number,
/**
* The intialDelay is the amount of time to wait before making the first attempt. This option should typically be 0 since you typically want the first attempt to happen immediately.
*/
initialDelay: number,
/**
* The factor option is used to grow the delay exponentially.
*/
factor: number,
/**
* The maximum number of attempts or 0 if there is no limit on number of attempts.
*/
maxAttempts: number,
/**
* minDelay is used to set a lower bound of delay when jitter is enabled. This property has no effect if jitter is disabled.
*/
minDelay: number,
/**
* The maxDelay option is used to set an upper bound for the delay when factor is enabled. A value of 0 can be provided if there should be no upper bound when calculating delay.
*/
maxDelay: number,
/**
* If jitter is true then the calculated delay will be a random integer value between minDelay and the calculated delay for the current iteration.
*/
jitter: boolean,
/**
* A timeout in milliseconds. If timeout is non-zero then a timer is set using setTimeout. If the timeout is triggered then future attempts will be aborted.
*/
timeout: number,
onAuthenticated: () => void,
onAuthenticationFailed: ({ reason: string }) => void,
onAuthenticationFailed: ({ reason }: { reason: string }) => void,
onOpen: (event: OpenEvent) => void,

@@ -60,3 +122,2 @@ onConnect: () => void,

onAwarenessChange: (states: any) => void,
debug: boolean,
}

@@ -66,2 +127,7 @@

public options: HocuspocusProviderOptions = {
// @ts-ignore
document: undefined,
// @ts-ignore
awareness: undefined,
WebSocketPolyfill: undefined,
url: '',

@@ -71,10 +137,23 @@ name: '',

parameters: {},
debug: false,
connect: true,
broadcast: true,
forceSyncInterval: false,
reconnectTimeoutBase: 1200,
maxReconnectTimeout: 2500,
// TODO: this should depend on awareness.outdatedTime
messageReconnectTimeout: 30000,
// 1 second
delay: 1000,
// instant
initialDelay: 0,
// double the delay each time
factor: 2,
// unlimited retries
maxAttempts: 0,
// wait at least 1 second
minDelay: 1000,
// at least every 30 seconds
maxDelay: 30000,
// randomize
jitter: true,
// retry forever
timeout: 0,
onAuthenticated: () => null,

@@ -95,4 +174,2 @@ onAuthenticationFailed: () => null,

awareness: Awareness
subscribedToBroadcastChannel = false

@@ -104,6 +181,4 @@

status: WebSocketStatus = WebSocketStatus.Disconnected
status = WebSocketStatus.Disconnected
failedConnectionAttempts = 0
isSynced = false

@@ -122,11 +197,13 @@

connectionAttempt: {
resolve: (value?: any) => void
reject: (reason?: any) => void
} | null = null
constructor(options: Partial<HocuspocusProviderOptions> = {}) {
super()
this.setOptions(options)
this.options.document = options.document ? options.document : new Y.Doc()
this.options.awareness = options.awareness ? options.awareness : new Awareness(this.document)
this.options.WebSocketPolyfill = options.WebSocketPolyfill ? options.WebSocketPolyfill : WebSocket
this.shouldConnect = options.connect !== undefined ? options.connect : this.shouldConnect

@@ -148,13 +225,13 @@ this.on('open', this.options.onOpen)

this.awareness.on('update', () => {
this.emit('awarenessUpdate', {
states: awarenessStatesToArray(this.awareness.getStates()),
})
this.emit('awarenessUpdate', { states: awarenessStatesToArray(this.awareness.getStates()) })
})
this.awareness.on('change', () => {
this.emit('awarenessChange', {
states: awarenessStatesToArray(this.awareness.getStates()),
})
this.emit('awarenessChange', { states: awarenessStatesToArray(this.awareness.getStates()) })
})
this.document.on('update', this.documentUpdateHandler.bind(this))
this.awareness.on('update', this.awarenessUpdateHandler.bind(this))
this.registerBeforeUnloadEventListener()
this.intervals.connectionChecker = setInterval(

@@ -165,6 +242,2 @@ this.checkConnection.bind(this),

this.document.on('update', this.documentUpdateHandler.bind(this))
this.awareness.on('update', this.awarenessUpdateHandler.bind(this))
this.registerBeforeUnloadEventListener()
if (this.options.forceSyncInterval) {

@@ -177,5 +250,11 @@ this.intervals.forceSync = setInterval(

if (this.options.connect) {
this.connect()
if (typeof options.connect !== 'undefined') {
this.shouldConnect = options.connect
}
if (!this.shouldConnect) {
return
}
this.connect()
}

@@ -187,2 +266,57 @@

connect() {
if (this.status === WebSocketStatus.Connected) {
return
}
this.shouldConnect = true
this.subscribeToBroadcastChannel()
retry(this.createWebSocketConnection.bind(this), {
delay: this.options.delay,
initialDelay: this.options.initialDelay,
factor: this.options.factor,
maxAttempts: this.options.maxAttempts,
minDelay: this.options.minDelay,
maxDelay: this.options.maxDelay,
jitter: this.options.jitter,
timeout: this.options.timeout,
})
}
createWebSocketConnection() {
return new Promise((resolve, reject) => {
// Init the WebSocket connection
this.webSocket = new this.options.WebSocketPolyfill(this.url)
this.webSocket.binaryType = 'arraybuffer'
this.webSocket.onmessage = this.onMessage.bind(this)
this.webSocket.onclose = this.onClose.bind(this)
this.webSocket.onopen = this.onOpen.bind(this)
this.webSocket.onerror = () => {
reject()
}
// Reset the status
this.synced = false
this.status = WebSocketStatus.Connecting
this.emit('status', { status: 'connecting' })
// Store resolve/reject for later use
this.connectionAttempt = {
resolve,
reject,
}
})
}
resolveConnectionAttempt() {
this.connectionAttempt?.resolve()
this.connectionAttempt = null
}
rejectConnectionAttempt() {
this.connectionAttempt?.reject()
this.connectionAttempt = null
}
get document() {

@@ -197,3 +331,3 @@ return this.options.document

checkConnection() {
// Don’t close the connection when it’s not established anyway
// Don’t check the connection when it’s not even established
if (this.status !== WebSocketStatus.Connected) {

@@ -203,3 +337,3 @@ return

// Don’t just close then connection while waiting for the first message
// Don’t close then connection while waiting for the first message
if (!this.lastMessageReceived) {

@@ -256,4 +390,2 @@ return

this.emit('authenticationFailed', { reason })
this.log('Permission denied', reason)
this.isAuthenticated = false

@@ -303,11 +435,2 @@ this.shouldConnect = false

connect() {
this.shouldConnect = true
if (this.status !== WebSocketStatus.Connected) {
this.createWebSocketConnection()
this.subscribeToBroadcastChannel()
}
}
disconnect() {

@@ -328,20 +451,2 @@ this.shouldConnect = false

createWebSocketConnection() {
if (this.webSocket !== null) {
return
}
this.webSocket = new this.options.WebSocketPolyfill(this.url)
this.webSocket.binaryType = 'arraybuffer'
this.status = WebSocketStatus.Connecting
this.synced = false
this.webSocket.onmessage = this.onMessage.bind(this)
this.webSocket.onclose = this.onClose.bind(this)
this.webSocket.onopen = this.onOpen.bind(this)
this.emit('status', { status: 'connecting' })
}
onOpen(event: OpenEvent) {

@@ -365,3 +470,2 @@ this.emit('open', { event })

async webSocketConnectionEstablished() {
this.failedConnectionAttempts = 0
this.status = WebSocketStatus.Connected

@@ -372,4 +476,5 @@ this.emit('status', { status: 'connected' })

if (this.isAuthenticationRequired) {
const token = await this.getToken()
this.send(AuthenticationMessage, { token })
this.send(AuthenticationMessage, {
token: await this.getToken(),
})
return

@@ -379,2 +484,4 @@ }

this.startSync()
this.resolveConnectionAttempt()
}

@@ -393,7 +500,5 @@

send(Message: OutgoingMessage, args: any, broadcast = false) {
send(Message: ConstructableOutgoingMessage, args: any, broadcast = false) {
if (broadcast) {
this.mux(() => {
this.broadcast(Message, args)
})
this.mux(() => { this.broadcast(Message, args) })
}

@@ -422,8 +527,7 @@

this.webSocket = null
this.isAuthenticated = false
this.webSocket = null
this.synced = false
if (this.status === WebSocketStatus.Connected) {
this.synced = false
// update awareness (all users except local left)

@@ -439,23 +543,26 @@ removeAwarenessStates(

this.emit('disconnect', { event })
} else {
this.failedConnectionAttempts += 1
}
if (this.connectionAttempt) {
// Okay, that connection attempt failed …
this.rejectConnectionAttempt()
} else if (this.shouldConnect) {
// The connection was closed by the server, so let’s just try again.
this.connect()
}
// If we’ll reconnect anyway, we’re done for now.
if (this.shouldConnect) {
const wait = math.round(math.min(
math.log10(this.failedConnectionAttempts + 1) * this.options.reconnectTimeoutBase,
this.options.maxReconnectTimeout,
))
return
}
this.log(`[close] Reconnecting in ${wait}ms …`)
setTimeout(this.createWebSocketConnection.bind(this), wait)
// The status is set correctly already.
if (this.status === WebSocketStatus.Disconnected) {
return
}
if (this.status !== WebSocketStatus.Disconnected) {
this.status = WebSocketStatus.Disconnected
this.emit('status', { status: 'disconnected' })
this.emit('disconnect', { event })
}
// Let’s update the connection status.
this.status = WebSocketStatus.Disconnected
this.emit('status', { status: 'disconnected' })
this.emit('disconnect', { event })
}

@@ -487,3 +594,3 @@

const message = new IncomingMessage(data)
new MessageReceiver(message, this)
new MessageReceiver(message)
.setBroadcasted(true)

@@ -522,3 +629,3 @@ .apply(this, false)

broadcast(Message: OutgoingMessage, args: any) {
broadcast(Message: ConstructableOutgoingMessage, args?: any) {
if (!this.options.broadcast) {

@@ -535,10 +642,2 @@ return

log(message: string): void {
if (!this.options.debug) {
return
}
console.log(message)
}
setAwarenessField(key: string, value: any) {

@@ -545,0 +644,0 @@ this.awareness.setLocalStateField(key, value)

import { Encoder, toUint8Array } from 'lib0/encoding'
import * as bc from 'lib0/broadcastchannel'
import { AuthenticationMessage } from './OutgoingMessages/AuthenticationMessage'
import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage'
import { QueryAwarenessMessage } from './OutgoingMessages/QueryAwarenessMessage'
import { SyncStepOneMessage } from './OutgoingMessages/SyncStepOneMessage'
import { SyncStepTwoMessage } from './OutgoingMessages/SyncStepTwoMessage'
import { UpdateMessage } from './OutgoingMessages/UpdateMessage'
import { Constructable } from './types'
import { ConstructableOutgoingMessage } from './types'

@@ -17,10 +11,3 @@ export class MessageSender {

constructor(Message:
Constructable<AuthenticationMessage> |
Constructable<AwarenessMessage> |
Constructable<QueryAwarenessMessage> |
Constructable<SyncStepOneMessage> |
Constructable<SyncStepTwoMessage> |
Constructable<UpdateMessage>,
args: any = {}) {
constructor(Message: ConstructableOutgoingMessage, args: any = {}) {
this.message = new Message()

@@ -27,0 +14,0 @@ this.encoder = this.message.get(args)

import { Awareness } from 'y-protocols/awareness'
import * as Y from 'yjs'
import { Encoder } from 'lib0/encoding'
import { AuthenticationMessage } from './OutgoingMessages/AuthenticationMessage'
import { AwarenessMessage } from './OutgoingMessages/AwarenessMessage'
import { QueryAwarenessMessage } from './OutgoingMessages/QueryAwarenessMessage'
import { SyncStepOneMessage } from './OutgoingMessages/SyncStepOneMessage'
import { SyncStepTwoMessage } from './OutgoingMessages/SyncStepTwoMessage'
import { UpdateMessage } from './OutgoingMessages/UpdateMessage'

@@ -30,1 +36,9 @@ export enum MessageType {

}
export type ConstructableOutgoingMessage =
Constructable<AuthenticationMessage> |
Constructable<AwarenessMessage> |
Constructable<QueryAwarenessMessage> |
Constructable<SyncStepOneMessage> |
Constructable<SyncStepTwoMessage> |
Constructable<UpdateMessage>

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc