"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var _class;
var _class2; // src/ws.ts
if (!globalThis.EventTarget || !globalThis.Event) {
console.error(`
PartySocket requires a global 'EventTarget' class to be available!
You can polyfill this global by adding this to your code before any partysocket imports:
\`\`\`
import 'partysocket/event-target-polyfill';
\`\`\`
Please file an issue at https://github.com/partykit/partykit if you're still having trouble.
`);
}
var ErrorEvent = class extends Event {
// biome-ignore lint/suspicious/noExplicitAny: vibes
constructor(error, target) {
super("error", target);
this.message = error.message;
this.error = error;
}
};
var CloseEvent =
((_class = class extends Event {
__init() {
this.wasClean = true;
}
// biome-ignore lint/suspicious/noExplicitAny: legacy
constructor(code = 1e3, reason = "", target) {
super("close", target);
_class.prototype.__init.call(this);
this.code = code;
this.reason = reason;
}
}),
_class);
var Events = {
Event,
ErrorEvent,
CloseEvent
};
function assert(condition, msg) {
if (!condition) {
throw new Error(msg);
}
}
function cloneEventBrowser(e) {
return new e.constructor(e.type, e);
}
function cloneEventNode(e) {
if ("data" in e) {
const evt2 = new MessageEvent(e.type, e);
return evt2;
}
if ("code" in e || "reason" in e) {
const evt2 = new CloseEvent(
// @ts-expect-error we need to fix event/listener types
e.code || 1999,
// @ts-expect-error we need to fix event/listener types
e.reason || "unknown reason",
e
);
return evt2;
}
if ("error" in e) {
const evt2 = new ErrorEvent(e.error, e);
return evt2;
}
const evt = new Event(e.type, e);
return evt;
}
var _a;
var isNode =
typeof process !== "undefined" &&
typeof ((_a = process.versions) == null ? void 0 : _a.node) !== "undefined" &&
typeof document === "undefined";
var cloneEvent = isNode ? cloneEventNode : cloneEventBrowser;
var DEFAULT = {
maxReconnectionDelay: 1e4,
minReconnectionDelay: 1e3 + Math.random() * 4e3,
minUptime: 5e3,
reconnectionDelayGrowFactor: 1.3,
connectionTimeout: 4e3,
maxRetries: Number.POSITIVE_INFINITY,
maxEnqueuedMessages: Number.POSITIVE_INFINITY,
startClosed: false,
debug: false
};
var didWarnAboutMissingWebSocket = false;
var ReconnectingWebSocket =
((_class2 = class _ReconnectingWebSocket extends EventTarget {
__init2() {
this._retryCount = -1;
}
__init3() {
this._shouldReconnect = true;
}
__init4() {
this._connectLock = false;
}
__init5() {
this._binaryType = "blob";
}
__init6() {
this._closeCalled = false;
}
__init7() {
this._messageQueue = [];
}
__init8() {
this._debugLogger = console.log.bind(console);
}
constructor(url, protocols, options = {}) {
super();
_class2.prototype.__init2.call(this);
_class2.prototype.__init3.call(this);
_class2.prototype.__init4.call(this);
_class2.prototype.__init5.call(this);
_class2.prototype.__init6.call(this);
_class2.prototype.__init7.call(this);
_class2.prototype.__init8.call(this);
_class2.prototype.__init9.call(this);
_class2.prototype.__init10.call(this);
_class2.prototype.__init11.call(this);
_class2.prototype.__init12.call(this);
_class2.prototype.__init13.call(this);
_class2.prototype.__init14.call(this);
_class2.prototype.__init15.call(this);
_class2.prototype.__init16.call(this);
this._url = url;
this._protocols = protocols;
this._options = options;
if (this._options.startClosed) {
this._shouldReconnect = false;
}
if (this._options.debugLogger) {
this._debugLogger = this._options.debugLogger;
}
this._connect();
}
static get CONNECTING() {
return 0;
}
static get OPEN() {
return 1;
}
static get CLOSING() {
return 2;
}
static get CLOSED() {
return 3;
}
get CONNECTING() {
return _ReconnectingWebSocket.CONNECTING;
}
get OPEN() {
return _ReconnectingWebSocket.OPEN;
}
get CLOSING() {
return _ReconnectingWebSocket.CLOSING;
}
get CLOSED() {
return _ReconnectingWebSocket.CLOSED;
}
get binaryType() {
return this._ws ? this._ws.binaryType : this._binaryType;
}
set binaryType(value) {
this._binaryType = value;
if (this._ws) {
this._ws.binaryType = value;
}
}
/**
* Returns the number or connection retries
*/
get retryCount() {
return Math.max(this._retryCount, 0);
}
/**
* The number of bytes of data that have been queued using calls to send() but not yet
* transmitted to the network. This value resets to zero once all queued data has been sent.
* This value does not reset to zero when the connection is closed; if you keep calling send(),
* this will continue to climb. Read only
*/
get bufferedAmount() {
const bytes = this._messageQueue.reduce((acc, message) => {
if (typeof message === "string") {
acc += message.length;
} else if (message instanceof Blob) {
acc += message.size;
} else {
acc += message.byteLength;
}
return acc;
}, 0);
return bytes + (this._ws ? this._ws.bufferedAmount : 0);
}
/**
* The extensions selected by the server. This is currently only the empty string or a list of
* extensions as negotiated by the connection
*/
get extensions() {
return this._ws ? this._ws.extensions : "";
}
/**
* A string indicating the name of the sub-protocol the server selected;
* this will be one of the strings specified in the protocols parameter when creating the
* WebSocket object
*/
get protocol() {
return this._ws ? this._ws.protocol : "";
}
/**
* The current state of the connection; this is one of the Ready state constants
*/
get readyState() {
if (this._ws) {
return this._ws.readyState;
}
return this._options.startClosed
? _ReconnectingWebSocket.CLOSED
: _ReconnectingWebSocket.CONNECTING;
}
/**
* The URL as resolved by the constructor
*/
get url() {
return this._ws ? this._ws.url : "";
}
/**
* Whether the websocket object is now in reconnectable state
*/
get shouldReconnect() {
return this._shouldReconnect;
}
/**
* An event listener to be called when the WebSocket connection's readyState changes to CLOSED
*/
__init9() {
this.onclose = null;
}
/**
* An event listener to be called when an error occurs
*/
__init10() {
this.onerror = null;
}
/**
* An event listener to be called when a message is received from the server
*/
__init11() {
this.onmessage = null;
}
/**
* An event listener to be called when the WebSocket connection's readyState changes to OPEN;
* this indicates that the connection is ready to send and receive data
*/
__init12() {
this.onopen = null;
}
/**
* Closes the WebSocket connection or connection attempt, if any. If the connection is already
* CLOSED, this method does nothing
*/
close(code = 1e3, reason) {
this._closeCalled = true;
this._shouldReconnect = false;
this._clearTimeouts();
if (!this._ws) {
this._debug("close enqueued: no ws instance");
return;
}
if (this._ws.readyState === this.CLOSED) {
this._debug("close: already closed");
return;
}
this._ws.close(code, reason);
}
/**
* Closes the WebSocket connection or connection attempt and connects again.
* Resets retry counter;
*/
reconnect(code, reason) {
this._shouldReconnect = true;
this._closeCalled = false;
this._retryCount = -1;
if (!this._ws || this._ws.readyState === this.CLOSED) {
this._connect();
} else {
this._disconnect(code, reason);
this._connect();
}
}
/**
* Enqueue specified data to be transmitted to the server over the WebSocket connection
*/
send(data) {
if (this._ws && this._ws.readyState === this.OPEN) {
this._debug("send", data);
this._ws.send(data);
} else {
const { maxEnqueuedMessages = DEFAULT.maxEnqueuedMessages } =
this._options;
if (this._messageQueue.length < maxEnqueuedMessages) {
this._debug("enqueue", data);
this._messageQueue.push(data);
}
}
}
_debug(...args) {
if (this._options.debug) {
this._debugLogger("RWS>", ...args);
}
}
_getNextDelay() {
const {
reconnectionDelayGrowFactor = DEFAULT.reconnectionDelayGrowFactor,
minReconnectionDelay = DEFAULT.minReconnectionDelay,
maxReconnectionDelay = DEFAULT.maxReconnectionDelay
} = this._options;
let delay = 0;
if (this._retryCount > 0) {
delay =
minReconnectionDelay *
reconnectionDelayGrowFactor ** (this._retryCount - 1);
if (delay > maxReconnectionDelay) {
delay = maxReconnectionDelay;
}
}
this._debug("next delay", delay);
return delay;
}
_wait() {
return new Promise((resolve) => {
setTimeout(resolve, this._getNextDelay());
});
}
_getNextProtocols(protocolsProvider) {
if (!protocolsProvider) return Promise.resolve(null);
if (
typeof protocolsProvider === "string" ||
Array.isArray(protocolsProvider)
) {
return Promise.resolve(protocolsProvider);
}
if (typeof protocolsProvider === "function") {
const protocols = protocolsProvider();
if (!protocols) return Promise.resolve(null);
if (typeof protocols === "string" || Array.isArray(protocols)) {
return Promise.resolve(protocols);
}
if (protocols.then) {
return protocols;
}
}
throw Error("Invalid protocols");
}
_getNextUrl(urlProvider) {
if (typeof urlProvider === "string") {
return Promise.resolve(urlProvider);
}
if (typeof urlProvider === "function") {
const url = urlProvider();
if (typeof url === "string") {
return Promise.resolve(url);
}
if (url.then) {
return url;
}
}
throw Error("Invalid URL");
}
_connect() {
if (this._connectLock || !this._shouldReconnect) {
return;
}
this._connectLock = true;
const {
maxRetries = DEFAULT.maxRetries,
connectionTimeout = DEFAULT.connectionTimeout
} = this._options;
if (this._retryCount >= maxRetries) {
this._debug("max retries reached", this._retryCount, ">=", maxRetries);
return;
}
this._retryCount++;
this._debug("connect", this._retryCount);
this._removeListeners();
this._wait()
.then(() =>
Promise.all([
this._getNextUrl(this._url),
this._getNextProtocols(this._protocols || null)
])
)
.then(([url, protocols]) => {
if (this._closeCalled) {
this._connectLock = false;
return;
}
if (
!this._options.WebSocket &&
typeof WebSocket === "undefined" &&
!didWarnAboutMissingWebSocket
) {
console.error(`\u203C\uFE0F No WebSocket implementation available. You should define options.WebSocket.
For example, if you're using node.js, run \`npm install ws\`, and then in your code:
import PartySocket from 'partysocket';
import WS from 'ws';
const partysocket = new PartySocket({
host: "127.0.0.1:1999",
room: "test-room",
WebSocket: WS
});
`);
didWarnAboutMissingWebSocket = true;
}
const WS = this._options.WebSocket || WebSocket;
this._debug("connect", { url, protocols });
this._ws = protocols ? new WS(url, protocols) : new WS(url);
this._ws.binaryType = this._binaryType;
this._connectLock = false;
this._addListeners();
this._connectTimeout = setTimeout(
() => this._handleTimeout(),
connectionTimeout
);
})
.catch((err) => {
this._connectLock = false;
this._handleError(new Events.ErrorEvent(Error(err.message), this));
});
}
_handleTimeout() {
this._debug("timeout event");
this._handleError(new Events.ErrorEvent(Error("TIMEOUT"), this));
}
_disconnect(code = 1e3, reason) {
this._clearTimeouts();
if (!this._ws) {
return;
}
this._removeListeners();
try {
if (
this._ws.readyState === this.OPEN ||
this._ws.readyState === this.CONNECTING
) {
this._ws.close(code, reason);
}
this._handleClose(new Events.CloseEvent(code, reason, this));
} catch (_error) {}
}
_acceptOpen() {
this._debug("accept open");
this._retryCount = 0;
}
__init13() {
this._handleOpen = (event) => {
this._debug("open event");
const { minUptime = DEFAULT.minUptime } = this._options;
clearTimeout(this._connectTimeout);
this._uptimeTimeout = setTimeout(() => this._acceptOpen(), minUptime);
assert(this._ws, "WebSocket is not defined");
this._ws.binaryType = this._binaryType;
this._messageQueue.forEach((message) => {
var _a2;
(_a2 = this._ws) == null ? void 0 : _a2.send(message);
});
this._messageQueue = [];
if (this.onopen) {
this.onopen(event);
}
this.dispatchEvent(cloneEvent(event));
};
}
__init14() {
this._handleMessage = (event) => {
this._debug("message event");
if (this.onmessage) {
this.onmessage(event);
}
this.dispatchEvent(cloneEvent(event));
};
}
__init15() {
this._handleError = (event) => {
this._debug("error event", event.message);
this._disconnect(
void 0,
event.message === "TIMEOUT" ? "timeout" : void 0
);
if (this.onerror) {
this.onerror(event);
}
this._debug("exec error listeners");
this.dispatchEvent(cloneEvent(event));
this._connect();
};
}
__init16() {
this._handleClose = (event) => {
this._debug("close event");
this._clearTimeouts();
if (this._shouldReconnect) {
this._connect();
}
if (this.onclose) {
this.onclose(event);
}
this.dispatchEvent(cloneEvent(event));
};
}
_removeListeners() {
if (!this._ws) {
return;
}
this._debug("removeListeners");
this._ws.removeEventListener("open", this._handleOpen);
this._ws.removeEventListener("close", this._handleClose);
this._ws.removeEventListener("message", this._handleMessage);
this._ws.removeEventListener("error", this._handleError);
}
_addListeners() {
if (!this._ws) {
return;
}
this._debug("addListeners");
this._ws.addEventListener("open", this._handleOpen);
this._ws.addEventListener("close", this._handleClose);
this._ws.addEventListener("message", this._handleMessage);
this._ws.addEventListener("error", this._handleError);
}
_clearTimeouts() {
clearTimeout(this._connectTimeout);
clearTimeout(this._uptimeTimeout);
}
}),
_class2);
exports.ErrorEvent = ErrorEvent;
exports.CloseEvent = CloseEvent;
exports.ReconnectingWebSocket = ReconnectingWebSocket;
/*!
* Reconnecting WebSocket
* by Pedro Ladaria <pedro.ladaria@gmail.com>
* https://github.com/pladaria/reconnecting-websocket
* License MIT
*/
//# sourceMappingURL=chunk-FCGNWPJI.js.map