New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

partysocket

Package Overview
Dependencies
Maintainers
1
Versions
931
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

partysocket - npm Package Compare versions

Comparing version 0.0.0-66c5e5d to 0.0.0-66d07f5

dist/chunk-4SNNYC7I.mjs

669

dist/index.js

@@ -1,20 +0,661 @@

import ReconnectingWebSocket from "./ws";
class PartySocket extends ReconnectingWebSocket {
constructor(partySocketOptions) {
const { host, room, protocol, query, protocols, ...socketOptions } = partySocketOptions;
const _pk = crypto.randomUUID();
let url = `${protocol || (host.startsWith("localhost:") || host.startsWith("127.0.0.1:") ? "ws" : "wss")}://${host}/party/${room}`;
if (query) {
url += `?${new URLSearchParams({ ...query, _pk }).toString()}`;
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
PartySocket: () => PartySocket,
WebSocket: () => ReconnectingWebSocket,
default: () => PartySocket
});
module.exports = __toCommonJS(src_exports);
// 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 {
message;
error;
constructor(error, target) {
super("error", target);
this.message = error.message;
this.error = error;
}
};
var CloseEvent = class extends Event {
code;
reason;
wasClean = true;
constructor(code = 1e3, reason = "", target) {
super("close", target);
this.code = code;
this.reason = reason;
}
};
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 isNode = typeof process !== "undefined" && typeof process.versions?.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: Infinity,
maxEnqueuedMessages: Infinity,
startClosed: false,
debug: false
};
var didWarnAboutMissingWebSocket = false;
var ReconnectingWebSocket = class _ReconnectingWebSocket extends EventTarget {
_ws;
_retryCount = -1;
_uptimeTimeout;
_connectTimeout;
_shouldReconnect = true;
_connectLock = false;
_binaryType = "blob";
_closeCalled = false;
_messageQueue = [];
_debugLogger = console.log.bind(console);
_url;
_protocols;
_options;
constructor(url, protocols, options = {}) {
super();
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
*/
onclose = null;
/**
* An event listener to be called when an error occurs
*/
onerror = null;
/**
* An event listener to be called when a message is received from the server
*/
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
*/
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 {
url += `?_pk=${_pk}`;
this._disconnect(code, reason);
this._connect();
}
super(url, protocols, socketOptions);
}
/**
* 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 * Math.pow(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 {
this._ws.close(code, reason);
this._handleClose(new Events.CloseEvent(code, reason, this));
} catch (error) {
}
}
_acceptOpen() {
this._debug("accept open");
this._retryCount = 0;
}
_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) => this._ws?.send(message));
this._messageQueue = [];
if (this.onopen) {
this.onopen(event);
}
this.dispatchEvent(cloneEvent(event));
};
_handleMessage = (event) => {
this._debug("message event");
if (this.onmessage) {
this.onmessage(event);
}
this.dispatchEvent(cloneEvent(event));
};
_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();
};
_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);
}
};
// src/index.ts
var valueIsNotNil = (keyValuePair) => keyValuePair[1] !== null && keyValuePair[1] !== void 0;
function generateUUID() {
if (typeof crypto !== "undefined" && crypto.randomUUID) {
return crypto.randomUUID();
}
let d = (/* @__PURE__ */ new Date()).getTime();
let d2 = typeof performance !== "undefined" && performance.now && performance.now() * 1e3 || 0;
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
let r = Math.random() * 16;
if (d > 0) {
r = (d + r) % 16 | 0;
d = Math.floor(d / 16);
} else {
r = (d2 + r) % 16 | 0;
d2 = Math.floor(d2 / 16);
}
return (c === "x" ? r : r & 3 | 8).toString(16);
});
}
function getPartyInfo(partySocketOptions, defaultProtocol, defaultParams = {}) {
const {
host: rawHost,
path: rawPath,
protocol: rawProtocol,
room,
party,
prefix,
query
} = partySocketOptions;
let host = rawHost.replace(/^(http|https|ws|wss):\/\//, "");
if (host.endsWith("/")) {
host = host.slice(0, -1);
}
if (rawPath && rawPath.startsWith("/")) {
throw new Error("path must not start with a slash");
}
const name = party ?? "main";
const path = rawPath ? `/${rawPath}` : "";
const protocol = rawProtocol || (host.startsWith("localhost:") || host.startsWith("127.0.0.1:") || host.startsWith("192.168.") || host.startsWith("10.") || host.startsWith("172.") && host.split(".")[1] >= "16" && host.split(".")[1] <= "31" || host.startsWith("[::ffff:7f00:1]:") ? (
// http / ws
defaultProtocol
) : (
// https / wss
defaultProtocol + "s"
));
const baseUrl = `${protocol}://${host}/${prefix || `parties/${name}/${room}`}${path}`;
const makeUrl = (query2 = {}) => `${baseUrl}?${new URLSearchParams([
...Object.entries(defaultParams),
...Object.entries(query2).filter(valueIsNotNil)
])}`;
const urlProvider = typeof query === "function" ? async () => makeUrl(await query()) : makeUrl(query);
return {
host,
path,
room,
name,
protocol,
partyUrl: baseUrl,
urlProvider
};
}
var PartySocket = class extends ReconnectingWebSocket {
constructor(partySocketOptions) {
const wsOptions = getWSOptions(partySocketOptions);
super(wsOptions.urlProvider, wsOptions.protocols, wsOptions.socketOptions);
this.partySocketOptions = partySocketOptions;
this.setWSProperties(wsOptions);
}
_pk;
_pkurl;
name;
room;
host;
path;
updateProperties(partySocketOptions) {
const wsOptions = getWSOptions({
...this.partySocketOptions,
...partySocketOptions,
host: partySocketOptions.host ?? this.host,
room: partySocketOptions.room ?? this.room,
path: partySocketOptions.path ?? this.path
});
this._url = wsOptions.urlProvider;
this._protocols = wsOptions.protocols;
this._options = wsOptions.socketOptions;
this.setWSProperties(wsOptions);
}
setWSProperties(wsOptions) {
const { _pk, _pkurl, name, room, host, path } = wsOptions;
this._pk = _pk;
this._pkurl = _pkurl;
this.name = name;
this.room = room;
this.host = host;
this.path = path;
}
_pk;
reconnect(code, reason) {
if (!this.room || !this.host) {
throw new Error(
"The room and host must be set before connecting, use `updateProperties` method to set them or pass them to the constructor."
);
}
super.reconnect(code, reason);
}
get id() {
return this._pk;
}
/**
* Exposes the static PartyKit room URL without applying query parameters.
* To access the currently connected WebSocket url, use PartySocket#url.
*/
get roomUrl() {
return this._pkurl;
}
// a `fetch` method that uses (almost) the same options as `PartySocket`
static async fetch(options, init) {
const party = getPartyInfo(options, "http");
const url = typeof party.urlProvider === "string" ? party.urlProvider : await party.urlProvider();
const doFetch = options.fetch ?? fetch;
return doFetch(url, init);
}
};
function getWSOptions(partySocketOptions) {
const {
id,
host: _host,
path: _path,
party: _party,
room: _room,
protocol: _protocol,
query: _query,
protocols,
...socketOptions
} = partySocketOptions;
const _pk = id || generateUUID();
const party = getPartyInfo(partySocketOptions, "ws", { _pk });
return {
_pk,
_pkurl: party.partyUrl,
name: party.name,
room: party.room,
host: party.host,
path: party.path,
protocols,
socketOptions,
urlProvider: party.urlProvider
};
}
export {
PartySocket as default
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
PartySocket,
WebSocket
});
/*!
* Reconnecting WebSocket
* by Pedro Ladaria <pedro.ladaria@gmail.com>
* https://github.com/pladaria/reconnecting-websocket
* License MIT
*/

@@ -1,19 +0,781 @@

import PartySocket from ".";
import { useRef, useEffect } from "react";
function usePartySocket(options) {
const socketRef = useRef(
new PartySocket({
...options,
startClosed: true
})
);
useEffect(() => {
socketRef.current.reconnect();
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/react.ts
var react_exports = {};
__export(react_exports, {
default: () => usePartySocket,
usePartySocket: () => usePartySocket,
useWebSocket: () => useWebSocket
});
module.exports = __toCommonJS(react_exports);
// 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 {
message;
error;
constructor(error, target) {
super("error", target);
this.message = error.message;
this.error = error;
}
};
var CloseEvent = class extends Event {
code;
reason;
wasClean = true;
constructor(code = 1e3, reason = "", target) {
super("close", target);
this.code = code;
this.reason = reason;
}
};
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 isNode = typeof process !== "undefined" && typeof process.versions?.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: Infinity,
maxEnqueuedMessages: Infinity,
startClosed: false,
debug: false
};
var didWarnAboutMissingWebSocket = false;
var ReconnectingWebSocket = class _ReconnectingWebSocket extends EventTarget {
_ws;
_retryCount = -1;
_uptimeTimeout;
_connectTimeout;
_shouldReconnect = true;
_connectLock = false;
_binaryType = "blob";
_closeCalled = false;
_messageQueue = [];
_debugLogger = console.log.bind(console);
_url;
_protocols;
_options;
constructor(url, protocols, options = {}) {
super();
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
*/
onclose = null;
/**
* An event listener to be called when an error occurs
*/
onerror = null;
/**
* An event listener to be called when a message is received from the server
*/
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
*/
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 * Math.pow(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 {
this._ws.close(code, reason);
this._handleClose(new Events.CloseEvent(code, reason, this));
} catch (error) {
}
}
_acceptOpen() {
this._debug("accept open");
this._retryCount = 0;
}
_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) => this._ws?.send(message));
this._messageQueue = [];
if (this.onopen) {
this.onopen(event);
}
this.dispatchEvent(cloneEvent(event));
};
_handleMessage = (event) => {
this._debug("message event");
if (this.onmessage) {
this.onmessage(event);
}
this.dispatchEvent(cloneEvent(event));
};
_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();
};
_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);
}
};
// src/index.ts
var valueIsNotNil = (keyValuePair) => keyValuePair[1] !== null && keyValuePair[1] !== void 0;
function generateUUID() {
if (typeof crypto !== "undefined" && crypto.randomUUID) {
return crypto.randomUUID();
}
let d = (/* @__PURE__ */ new Date()).getTime();
let d2 = typeof performance !== "undefined" && performance.now && performance.now() * 1e3 || 0;
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
let r = Math.random() * 16;
if (d > 0) {
r = (d + r) % 16 | 0;
d = Math.floor(d / 16);
} else {
r = (d2 + r) % 16 | 0;
d2 = Math.floor(d2 / 16);
}
return (c === "x" ? r : r & 3 | 8).toString(16);
});
}
function getPartyInfo(partySocketOptions, defaultProtocol, defaultParams = {}) {
const {
host: rawHost,
path: rawPath,
protocol: rawProtocol,
room,
party,
prefix,
query
} = partySocketOptions;
let host = rawHost.replace(/^(http|https|ws|wss):\/\//, "");
if (host.endsWith("/")) {
host = host.slice(0, -1);
}
if (rawPath && rawPath.startsWith("/")) {
throw new Error("path must not start with a slash");
}
const name = party ?? "main";
const path = rawPath ? `/${rawPath}` : "";
const protocol = rawProtocol || (host.startsWith("localhost:") || host.startsWith("127.0.0.1:") || host.startsWith("192.168.") || host.startsWith("10.") || host.startsWith("172.") && host.split(".")[1] >= "16" && host.split(".")[1] <= "31" || host.startsWith("[::ffff:7f00:1]:") ? (
// http / ws
defaultProtocol
) : (
// https / wss
defaultProtocol + "s"
));
const baseUrl = `${protocol}://${host}/${prefix || `parties/${name}/${room}`}${path}`;
const makeUrl = (query2 = {}) => `${baseUrl}?${new URLSearchParams([
...Object.entries(defaultParams),
...Object.entries(query2).filter(valueIsNotNil)
])}`;
const urlProvider = typeof query === "function" ? async () => makeUrl(await query()) : makeUrl(query);
return {
host,
path,
room,
name,
protocol,
partyUrl: baseUrl,
urlProvider
};
}
var PartySocket = class extends ReconnectingWebSocket {
constructor(partySocketOptions) {
const wsOptions = getWSOptions(partySocketOptions);
super(wsOptions.urlProvider, wsOptions.protocols, wsOptions.socketOptions);
this.partySocketOptions = partySocketOptions;
this.setWSProperties(wsOptions);
}
_pk;
_pkurl;
name;
room;
host;
path;
updateProperties(partySocketOptions) {
const wsOptions = getWSOptions({
...this.partySocketOptions,
...partySocketOptions,
host: partySocketOptions.host ?? this.host,
room: partySocketOptions.room ?? this.room,
path: partySocketOptions.path ?? this.path
});
this._url = wsOptions.urlProvider;
this._protocols = wsOptions.protocols;
this._options = wsOptions.socketOptions;
this.setWSProperties(wsOptions);
}
setWSProperties(wsOptions) {
const { _pk, _pkurl, name, room, host, path } = wsOptions;
this._pk = _pk;
this._pkurl = _pkurl;
this.name = name;
this.room = room;
this.host = host;
this.path = path;
}
reconnect(code, reason) {
if (!this.room || !this.host) {
throw new Error(
"The room and host must be set before connecting, use `updateProperties` method to set them or pass them to the constructor."
);
}
super.reconnect(code, reason);
}
get id() {
return this._pk;
}
/**
* Exposes the static PartyKit room URL without applying query parameters.
* To access the currently connected WebSocket url, use PartySocket#url.
*/
get roomUrl() {
return this._pkurl;
}
// a `fetch` method that uses (almost) the same options as `PartySocket`
static async fetch(options, init) {
const party = getPartyInfo(options, "http");
const url = typeof party.urlProvider === "string" ? party.urlProvider : await party.urlProvider();
const doFetch = options.fetch ?? fetch;
return doFetch(url, init);
}
};
function getWSOptions(partySocketOptions) {
const {
id,
host: _host,
path: _path,
party: _party,
room: _room,
protocol: _protocol,
query: _query,
protocols,
...socketOptions
} = partySocketOptions;
const _pk = id || generateUUID();
const party = getPartyInfo(partySocketOptions, "ws", { _pk });
return {
_pk,
_pkurl: party.partyUrl,
name: party.name,
room: party.room,
host: party.host,
path: party.path,
protocols,
socketOptions,
urlProvider: party.urlProvider
};
}
// src/use-handlers.ts
var import_react = require("react");
var useAttachWebSocketEventHandlers = (socket, options) => {
const handlersRef = (0, import_react.useRef)(options);
handlersRef.current = options;
(0, import_react.useEffect)(() => {
const onOpen = (event) => handlersRef.current?.onOpen?.(event);
const onMessage = (event) => handlersRef.current?.onMessage?.(event);
const onClose = (event) => handlersRef.current?.onClose?.(event);
const onError = (event) => handlersRef.current?.onError?.(event);
socket.addEventListener("open", onOpen);
socket.addEventListener("close", onClose);
socket.addEventListener("error", onError);
socket.addEventListener("message", onMessage);
return () => {
socketRef.current.close();
socket.removeEventListener("open", onOpen);
socket.removeEventListener("close", onClose);
socket.removeEventListener("error", onError);
socket.removeEventListener("message", onMessage);
};
}, []);
}, [socket]);
};
// src/use-socket.ts
var import_react2 = require("react");
var getOptionsThatShouldCauseRestartWhenChanged = (options) => [
options.startClosed,
options.minUptime,
options.maxRetries,
options.connectionTimeout,
options.maxEnqueuedMessages,
options.maxReconnectionDelay,
options.minReconnectionDelay,
options.reconnectionDelayGrowFactor,
options.debug
];
function useStableSocket({
options,
createSocket,
createSocketMemoKey: createOptionsMemoKey
}) {
const shouldReconnect = createOptionsMemoKey(options);
const socketOptions = (0, import_react2.useMemo)(() => {
return options;
}, [shouldReconnect]);
const [socket, setSocket] = (0, import_react2.useState)(
() => (
// only connect on first mount
createSocket({ ...socketOptions, startClosed: true })
)
);
const socketInitializedRef = (0, import_react2.useRef)(null);
const createSocketRef = (0, import_react2.useRef)(createSocket);
createSocketRef.current = createSocket;
(0, import_react2.useEffect)(() => {
if (socketInitializedRef.current === socket) {
const newSocket = createSocketRef.current({
...socketOptions,
// when reconnecting because of options change, we always reconnect
// (startClosed only applies to initial mount)
startClosed: false
});
setSocket(newSocket);
} else {
if (!socketInitializedRef.current && socketOptions.startClosed !== true) {
socket.reconnect();
}
socketInitializedRef.current = socket;
return () => {
socket.close();
};
}
}, [socket, socketOptions]);
return socket;
}
export {
usePartySocket as default
};
// src/use-ws.ts
function useWebSocket(url, protocols, options = {}) {
const socket = useStableSocket({
options,
createSocket: (options2) => new ReconnectingWebSocket(url, protocols, options2),
createSocketMemoKey: (options2) => JSON.stringify([
// will reconnect if url or protocols are specified as a string.
// if they are functions, the WebSocket will handle reconnection
url,
protocols,
...getOptionsThatShouldCauseRestartWhenChanged(options2)
])
});
useAttachWebSocketEventHandlers(socket, options);
return socket;
}
// src/react.ts
function usePartySocket(options) {
const { host, ...otherOptions } = options;
const socket = useStableSocket({
options: {
host: host || (typeof window !== "undefined" ? window.location.host : "dummy-domain.com"),
...otherOptions
},
createSocket: (options2) => new PartySocket(options2),
createSocketMemoKey: (options2) => JSON.stringify([
// NOTE: if query is defined as a function, the socket
// won't reconnect when you change the function identity
options2.query,
options2.id,
options2.host,
options2.room,
options2.party,
options2.path,
options2.protocol,
options2.protocols,
...getOptionsThatShouldCauseRestartWhenChanged(options2)
])
});
useAttachWebSocketEventHandlers(socket, options);
return socket;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
usePartySocket,
useWebSocket
});
/*!
* Reconnecting WebSocket
* by Pedro Ladaria <pedro.ladaria@gmail.com>
* https://github.com/pladaria/reconnecting-websocket
* License MIT
*/

255

dist/ws.js

@@ -1,16 +0,40 @@

/*!
* Reconnecting WebSocket
* by Pedro Ladaria <pedro.ladaria@gmail.com>
* https://github.com/pladaria/reconnecting-websocket
* License MIT
*/
class Event {
target;
type;
constructor(type, target) {
this.target = target;
this.type = type;
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/ws.ts
var ws_exports = {};
__export(ws_exports, {
CloseEvent: () => CloseEvent,
ErrorEvent: () => ErrorEvent,
default: () => ReconnectingWebSocket
});
module.exports = __toCommonJS(ws_exports);
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.
`);
}
class ErrorEvent extends Event {
var ErrorEvent = class extends Event {
message;

@@ -23,4 +47,4 @@ error;

}
}
class CloseEvent extends Event {
};
var CloseEvent = class extends Event {
code;

@@ -34,4 +58,4 @@ reason;

}
}
const Events = {
};
var Events = {
Event,

@@ -46,6 +70,30 @@ ErrorEvent,

}
function isWebSocket(w) {
return typeof w !== "undefined" && !!w && typeof w === "function" && "CLOSING" in w && w.CLOSING === 2;
function cloneEventBrowser(e) {
return new e.constructor(e.type, e);
}
const DEFAULT = {
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 isNode = typeof process !== "undefined" && typeof process.versions?.node !== "undefined" && typeof document === "undefined";
var cloneEvent = isNode ? cloneEventNode : cloneEventBrowser;
var DEFAULT = {
maxReconnectionDelay: 1e4,

@@ -61,10 +109,5 @@ minReconnectionDelay: 1e3 + Math.random() * 4e3,

};
class ReconnectingWebSocket {
var didWarnAboutMissingWebSocket = false;
var ReconnectingWebSocket = class _ReconnectingWebSocket extends EventTarget {
_ws;
_listeners = {
error: [],
message: [],
open: [],
close: []
};
_retryCount = -1;

@@ -78,2 +121,3 @@ _uptimeTimeout;

_messageQueue = [];
_debugLogger = console.log.bind(console);
_url;

@@ -83,2 +127,3 @@ _protocols;

constructor(url, protocols, options = {}) {
super();
this._url = url;

@@ -90,2 +135,5 @@ this._protocols = protocols;

}
if (this._options.debugLogger) {
this._debugLogger = this._options.debugLogger;
}
this._connect();

@@ -106,12 +154,12 @@ }

get CONNECTING() {
return ReconnectingWebSocket.CONNECTING;
return _ReconnectingWebSocket.CONNECTING;
}
get OPEN() {
return ReconnectingWebSocket.OPEN;
return _ReconnectingWebSocket.OPEN;
}
get CLOSING() {
return ReconnectingWebSocket.CLOSING;
return _ReconnectingWebSocket.CLOSING;
}
get CLOSED() {
return ReconnectingWebSocket.CLOSED;
return _ReconnectingWebSocket.CLOSED;
}

@@ -127,5 +175,14 @@ get binaryType() {

}
/**
* 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() {

@@ -144,8 +201,20 @@ const bytes = this._messageQueue.reduce((acc, message) => {

}
/**
* 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() {

@@ -155,14 +224,37 @@ if (this._ws) {

}
return this._options.startClosed ? ReconnectingWebSocket.CLOSED : ReconnectingWebSocket.CONNECTING;
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
*/
onclose = null;
/**
* An event listener to be called when an error occurs
*/
onerror = null;
/**
* An event listener to be called when a message is received from the server
*/
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
*/
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) {

@@ -182,2 +274,6 @@ this._closeCalled = true;

}
/**
* Closes the WebSocket connection or connection attempt and connects again.
* Resets retry counter;
*/
reconnect(code, reason) {

@@ -194,2 +290,5 @@ this._shouldReconnect = true;

}
/**
* Enqueue specified data to be transmitted to the server over the WebSocket connection
*/
send(data) {

@@ -207,26 +306,5 @@ if (this._ws && this._ws.readyState === this.OPEN) {

}
addEventListener(type, listener) {
if (this._listeners[type]) {
this._listeners[type].push(listener);
}
}
dispatchEvent(event) {
const listeners = this._listeners[event.type];
if (listeners) {
for (const listener of listeners) {
this._callEventListener(event, listener);
}
}
return true;
}
removeEventListener(type, listener) {
if (this._listeners[type]) {
this._listeners[type] = this._listeners[type].filter(
(l) => l !== listener
);
}
}
_debug(...args) {
if (this._options.debug) {
console.log.apply(console, ["RWS>", ...args]);
this._debugLogger("RWS>", ...args);
}

@@ -256,4 +334,3 @@ }

_getNextProtocols(protocolsProvider) {
if (!protocolsProvider)
return Promise.resolve(null);
if (!protocolsProvider) return Promise.resolve(null);
if (typeof protocolsProvider === "string" || Array.isArray(protocolsProvider)) {

@@ -264,4 +341,3 @@ return Promise.resolve(protocolsProvider);

const protocols = protocolsProvider();
if (!protocols)
return Promise.resolve(null);
if (!protocols) return Promise.resolve(null);
if (typeof protocols === "string" || Array.isArray(protocols)) {

@@ -307,5 +383,2 @@ return Promise.resolve(protocols);

this._removeListeners();
if (!isWebSocket(WebSocket)) {
throw Error("No valid WebSocket class provided");
}
this._wait().then(

@@ -321,4 +394,22 @@ () => Promise.all([

}
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 WebSocket(url, protocols) : new WebSocket(url);
this._ws = protocols ? new WS(url, protocols) : new WS(url);
this._ws.binaryType = this._binaryType;

@@ -356,9 +447,2 @@ this._connectLock = false;

}
_callEventListener(event, listener) {
if ("handleEvent" in listener) {
listener.handleEvent(event);
} else {
listener(event);
}
}
_handleOpen = (event) => {

@@ -376,5 +460,3 @@ this._debug("open event");

}
this._listeners.open.forEach(
(listener) => this._callEventListener(event, listener)
);
this.dispatchEvent(cloneEvent(event));
};

@@ -386,5 +468,3 @@ _handleMessage = (event) => {

}
this._listeners.message.forEach(
(listener) => this._callEventListener(event, listener)
);
this.dispatchEvent(cloneEvent(event));
};

@@ -401,5 +481,3 @@ _handleError = (event) => {

this._debug("exec error listeners");
this._listeners.error.forEach(
(listener) => this._callEventListener(event, listener)
);
this.dispatchEvent(cloneEvent(event));
this._connect();

@@ -416,5 +494,3 @@ };

}
this._listeners.close.forEach(
(listener) => this._callEventListener(event, listener)
);
this.dispatchEvent(cloneEvent(event));
};

@@ -445,8 +521,13 @@ _removeListeners() {

}
}
export {
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CloseEvent,
ErrorEvent,
Event,
ReconnectingWebSocket as default
};
ErrorEvent
});
/*!
* Reconnecting WebSocket
* by Pedro Ladaria <pedro.ladaria@gmail.com>
* https://github.com/pladaria/reconnecting-websocket
* License MIT
*/

@@ -1,15 +0,47 @@

import type * as RWS from "./ws";
import ReconnectingWebSocket from "./ws";
export type PartySocketOptions = Omit<RWS.Options, "WebSocket" | "constructor"> & {
import ReconnectingWebSocket, { Options } from './ws.js';
type Maybe<T> = T | null | undefined;
type Params = Record<string, Maybe<string>>;
type PartySocketOptions = Omit<Options, "constructor"> & {
id?: string;
host: string;
room: string;
protocol?: string;
room?: string;
party?: string;
prefix?: string;
protocol?: "ws" | "wss";
protocols?: string[];
query?: Record<string, string>;
path?: string;
query?: Params | (() => Params | Promise<Params>);
};
export default class PartySocket extends ReconnectingWebSocket {
type PartyFetchOptions = {
host: string;
room: string;
party?: string;
prefix?: string;
path?: string;
protocol?: "http" | "https";
query?: Params | (() => Params | Promise<Params>);
fetch?: typeof fetch;
};
declare class PartySocket extends ReconnectingWebSocket {
readonly partySocketOptions: PartySocketOptions;
_pk: string;
_pkurl: string;
name: string;
room?: string;
host: string;
path: string;
constructor(partySocketOptions: PartySocketOptions);
updateProperties(partySocketOptions: Partial<PartySocketOptions>): void;
private setWSProperties;
reconnect(code?: number | undefined, reason?: string | undefined): void;
get id(): string;
/**
* Exposes the static PartyKit room URL without applying query parameters.
* To access the currently connected WebSocket url, use PartySocket#url.
*/
get roomUrl(): string;
static fetch(options: PartyFetchOptions, init?: RequestInit): Promise<Response>;
}
//# sourceMappingURL=index.d.ts.map
export { type PartyFetchOptions, PartySocket, type PartySocketOptions, ReconnectingWebSocket as WebSocket, PartySocket as default };
{
"name": "partysocket",
"version": "0.0.0-66c5e5d",
"description": "party hotline",
"main": "dist/index.js",
"version": "0.0.0-66d07f5",
"description": "A better WebSocket that Just Works™",
"homepage": "https://docs.partykit.io/reference/partysocket-api",
"bugs": "https://github.com/partykit/partykit/issues",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"exports": {
".": "./dist/index.js",
"./ws": "./dist/ws.js",
"./react": "./dist/react.js"
".": {
"types": "./index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./ws": {
"types": "./ws.d.ts",
"import": "./dist/ws.mjs",
"require": "./dist/ws.js"
},
"./react": {
"types": "./react.d.ts",
"import": "./dist/react.mjs",
"require": "./dist/react.js"
},
"./use-ws": {
"types": "./use-ws.d.ts",
"import": "./dist/use-ws.mjs",
"require": "./dist/use-ws.js"
},
"./event-target-polyfill": {
"types": "./event-target-polyfill.d.ts",
"import": "./dist/event-target-polyfill.mjs",
"require": "./dist/event-target-polyfill.js"
}
},
"tsup": {
"entry": [
"src/index.ts",
"src/react.ts",
"src/ws.ts",
"src/use-ws.ts",
"src/event-target-polyfill.ts"
],
"format": [
"esm",
"cjs"
],
"dts": true
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"clean": "rm -rf dist && rm -rf *.d.ts*",
"build": "npm run clean && npx esbuild src/index.ts src/react.ts src/ws.ts --format=esm --outdir=dist && tsc --project tsconfig.extract.json && mv dist/*.d.ts* ."
"clean": "shx rm -rf dist *.d.ts *.d.mts event-target-polyfill.*",
"post-build": "shx mv dist/*.d.ts dist/*.d.mts* . && shx mv dist/event-target-polyfill.* .",
"build": "npm run clean && tsup --external react && npm run post-build",
"test": "echo \"Error: no test specified\" && exit 1"
},
"files": [
"dist/*.js",
"dist",
"*.d.ts",
"*.d.ts.map"
"event-target-polyfill.*"
],
"keywords": [],
"keywords": [
"websocket",
"client",
"reconnecting",
"reconnection",
"reconnect",
"forever",
"persistent",
"forever",
"automatic"
],
"repository": {
"type": "git",
"url": "https://github.com/partykit/partykit.git",
"directory": "packages/partykit"
},
"author": "",
"license": "ISC"
"license": "ISC",
"dependencies": {
"event-target-shim": "^6.0.2"
}
}

@@ -1,3 +0,11 @@

import type { PartySocketOptions } from ".";
export default function usePartySocket(options: PartySocketOptions): void;
//# sourceMappingURL=react.d.ts.map
import { PartySocket, PartySocketOptions } from './index.js';
import { E as EventHandlerOptions } from './use-ws-CnrrNZS2.js';
export { u as useWebSocket } from './use-ws-CnrrNZS2.js';
import './ws.js';
type UsePartySocketOptions = Omit<PartySocketOptions, "host"> & EventHandlerOptions & {
host?: string | undefined;
};
declare function usePartySocket(options: UsePartySocketOptions): PartySocket;
export { usePartySocket as default, usePartySocket };

@@ -1,35 +0,26 @@

- https://github.com/pladaria/reconnecting-websocket/pull/166 Fix: handle error if getNextUrl throws (TODO: add test for this one )
- https://github.com/pladaria/reconnecting-websocket/pull/132 feat: make protocols updatable
- https://github.com/pladaria/reconnecting-websocket/pull/141 [Fix] Socket doesn't connect again after closing while connecting
# PartySocket
(TODO: more)
_(Forked from the wonderful [reconnecting-websocket](https://github.com/joewalnes/reconnecting-websocket/) project, updated with pending PRs and bugfixes)_
- https://github.com/pladaria/reconnecting-websocket/pull/163 Support for Dynamic Protocols
- https://github.com/pladaria/reconnecting-websocket/pull/47 reconnecting and reconnectscheduled custom events
A better WebSocket that Just Works™
# Reconnecting WebSocket
## Install
[![Build Status](https://travis-ci.org/pladaria/reconnecting-websocket.svg?branch=master&v=1)](https://travis-ci.org/pladaria/reconnecting-websocket)
[![Coverage Status](https://coveralls.io/repos/github/pladaria/reconnecting-websocket/badge.svg?branch=master&v=3)](https://coveralls.io/github/pladaria/reconnecting-websocket?branch=master)
```bash
npm install partysocket
```
WebSocket that will automatically reconnect if the connection is closed.
## Features
- WebSocket API compatible (same interface, Level0 and Level2 event model)
- Reconnects when a connection drops
- Buffers messages when not connected, and sends accumulated messages when open
- Handle connection timeouts
- Allows changing server URL between reconnections
- Fully configurable
- Multi-platform (Web, ServiceWorkers, Node.js, React Native)
- Multi-platform (Web, ServiceWorkers, Node.js, React Native, Cloudflare Workers, Deno, Bun)
- Dependency free (does not depend on Window, DOM or any EventEmitter library)
- Handle connection timeouts
- Allows changing server URL between reconnections
- Buffering. Will send accumulated messages on open
- Multiple builds available (see dist folder)
- Debug mode
- Works everywhere, not just with PartyKit!
## Install
```bash
npm install --save reconnecting-websocket
```
## Usage

@@ -39,23 +30,47 @@

So this documentation should be valid:
[MDN WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket).
Ping me if you find any problems. Or, even better, write a test for your case and make a pull
request :)
### Simple usage
```javascript
import ReconnectingWebSocket from "reconnecting-websocket";
import { WebSocket } from "partysocket";
const rws = new ReconnectingWebSocket("ws://my.site.com");
const ws = new WebSocket("wss://my.site.com");
rws.addEventListener("open", () => {
rws.send("hello!");
ws.addEventListener("open", () => {
ws.send("hello!");
});
```
### Usage with PartyKit
```javascript
import PartySocket from "partysocket";
// optional: only needed if creating using inside node.js. Run `npm install ws`, and then add:
// import WS from "ws";
const ws = new PartySocket({
host: "project.name.partykit.dev", // or localhost:1999 in dev
room: "my-room",
// add an optional id to identify the client,
// if not provided, a random id will be generated
id: "some-connection-id"
// optional: if used from node.js, you need to pass the WebSocket polyfill imported from `ws`
// WebSocket: WS
});
// optionally, update the properties of the connection
// (e.g. to change the host or room)
ws.updateProperties({
host: "another-project.username.partykit.dev",
room: "my-new-room"
});
ws.reconnect(); // make sure to call reconnect() after updating the properties
```
### Update URL
The `url` parameter will be resolved before connecting, possible types:
The `url` parameter will be resolved before connecting, with possible types:

@@ -67,5 +82,9 @@ - `string`

```javascript
import ReconnectingWebSocket from "reconnecting-websocket";
import { WebSocket } from "partysocket";
const urls = ["ws://my.site.com", "ws://your.site.com", "ws://their.site.com"];
const urls = [
"wss://my.site.com",
"wss://your.site.com",
"wss://their.site.com"
];
let urlIndex = 0;

@@ -76,7 +95,7 @@

const rws = new ReconnectingWebSocket(urlProvider);
const ws = new WebSocket(urlProvider);
```
```javascript
import ReconnectingWebSocket from "reconnecting-websocket";
import { WebSocket } from "partysocket";

@@ -89,3 +108,3 @@ // async url provider

const rws = new ReconnectingWebSocket(urlProvider);
const ws = new WebSocket(urlProvider);
```

@@ -104,8 +123,9 @@

```javascript
import ReconnectingWebSocket from 'reconnecting-websocket`;
const rws = new ReconnectingWebSocket('ws://your.site.com', 'your protocol');
import { WebSocket } from "partysocket";
const ws = new WebSocket("wss://your.site.com", "your protocol");
```
```javascript
import ReconnectingWebSocket from 'reconnecting-websocket`;
import WebSocket from 'partysocket`;

@@ -118,3 +138,3 @@ const protocols = ['p1', 'p2', ['p3.1', 'p3.2']];

const rws = new ReconnectingWebSocket('ws://your.site.com', protocolsProvider);
const ws = new WebSocket('wss://your.site.com', protocolsProvider);
```

@@ -127,3 +147,3 @@

```javascript
import ReconnectingWebSocket from "reconnecting-websocket";
import { WebSocket } from "partysocket";
import WS from "ws";

@@ -134,5 +154,5 @@

connectionTimeout: 1000,
maxRetries: 10,
maxRetries: 10
};
const rws = new ReconnectingWebSocket("ws://my.site.com", [], options);
const ws = new WebSocket("wss://my.site.com", [], options);
```

@@ -215,8 +235,4 @@

## Contributing
[Read here](./CONTRIBUTING.md)
## License
MIT

@@ -0,1 +1,11 @@

type TypedEventTarget<EventMap extends object> = {
new (): IntermediateEventTarget<EventMap>;
};
interface IntermediateEventTarget<EventMap> extends EventTarget {
addEventListener<K extends keyof EventMap>(type: K, callback: (event: EventMap[K] extends Event ? EventMap[K] : never) => EventMap[K] extends Event ? void : never, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean): void;
removeEventListener<K extends keyof EventMap>(type: K, callback: (event: EventMap[K] extends Event ? EventMap[K] : never) => EventMap[K] extends Event ? void : never, options?: boolean | AddEventListenerOptions): void;
removeEventListener(type: string, callback: EventListenerOrEventListenerObject | null, options?: EventListenerOptions | boolean): void;
}
/*!

@@ -7,8 +17,4 @@ * Reconnecting WebSocket

*/
export declare class Event {
target: any;
type: string;
constructor(type: string, target: any);
}
export declare class ErrorEvent extends Event {
declare class ErrorEvent extends Event {
message: string;

@@ -18,3 +24,3 @@ error: Error;

}
export declare class CloseEvent extends Event {
declare class CloseEvent extends Event {
code: number;

@@ -25,3 +31,3 @@ reason: string;

}
export interface WebSocketEventMap {
interface WebSocketEventMap {
close: CloseEvent;

@@ -32,17 +38,4 @@ error: ErrorEvent;

}
export interface WebSocketEventListenerMap {
close: (event: CloseEvent) => void | {
handleEvent: (event: CloseEvent) => void;
};
error: (event: ErrorEvent) => void | {
handleEvent: (event: ErrorEvent) => void;
};
message: (event: MessageEvent) => void | {
handleEvent: (event: MessageEvent) => void;
};
open: (event: Event) => void | {
handleEvent: (event: Event) => void;
};
}
export type Options = {
type Options = {
WebSocket?: any;
maxReconnectionDelay?: number;

@@ -57,15 +50,10 @@ minReconnectionDelay?: number;

debug?: boolean;
debugLogger?: (...args: any[]) => void;
};
export type UrlProvider = string | (() => string) | (() => Promise<string>);
export type ProtocolsProvider = null | string | string[] | (() => string | string[] | null) | (() => Promise<string | string[] | null>);
export type Message = string | ArrayBuffer | Blob | ArrayBufferView;
export type ListenersMap = {
error: WebSocketEventListenerMap["error"][];
message: WebSocketEventListenerMap["message"][];
open: WebSocketEventListenerMap["open"][];
close: WebSocketEventListenerMap["close"][];
};
export default class ReconnectingWebSocket {
type UrlProvider = string | (() => string) | (() => Promise<string>);
type ProtocolsProvider = null | string | string[] | (() => string | string[] | null) | (() => Promise<string | string[] | null>);
type Message = string | ArrayBuffer | Blob | ArrayBufferView;
declare const ReconnectingWebSocket_base: TypedEventTarget<WebSocketEventMap>;
declare class ReconnectingWebSocket extends ReconnectingWebSocket_base {
private _ws;
private _listeners;
private _retryCount;

@@ -79,5 +67,6 @@ private _uptimeTimeout;

private _messageQueue;
private readonly _url;
private readonly _protocols?;
private readonly _options;
private _debugLogger;
protected _url: UrlProvider;
protected _protocols?: ProtocolsProvider;
protected _options: Options;
constructor(url: UrlProvider, protocols?: ProtocolsProvider, options?: Options);

@@ -159,11 +148,2 @@ static get CONNECTING(): number;

send(data: Message): void;
/**
* Register an event handler of a specific event type
*/
addEventListener<T extends keyof WebSocketEventListenerMap>(type: T, listener: WebSocketEventListenerMap[T]): void;
dispatchEvent(event: Event): boolean;
/**
* Removes an event listener
*/
removeEventListener<T extends keyof WebSocketEventListenerMap>(type: T, listener: WebSocketEventListenerMap[T]): void;
private _debug;

@@ -178,3 +158,2 @@ private _getNextDelay;

private _acceptOpen;
private _callEventListener;
private _handleOpen;

@@ -188,2 +167,3 @@ private _handleMessage;

}
//# sourceMappingURL=ws.d.ts.map
export { CloseEvent, ErrorEvent, type Message, type Options, type ProtocolsProvider, type UrlProvider, type WebSocketEventMap, ReconnectingWebSocket as default };
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