partysocket
Advanced tools
Comparing version
@@ -23,2 +23,4 @@ "use strict"; | ||
__export(src_exports, { | ||
PartySocket: () => PartySocket, | ||
WebSocket: () => ReconnectingWebSocket, | ||
default: () => PartySocket | ||
@@ -29,10 +31,13 @@ }); | ||
// src/ws.ts | ||
var Event = class { | ||
target; | ||
type; | ||
constructor(type, target) { | ||
this.target = target; | ||
this.type = type; | ||
} | ||
}; | ||
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 { | ||
@@ -67,5 +72,29 @@ message; | ||
} | ||
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); | ||
} | ||
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 = { | ||
@@ -82,10 +111,5 @@ maxReconnectionDelay: 1e4, | ||
}; | ||
var ReconnectingWebSocket = class { | ||
var didWarnAboutMissingWebSocket = false; | ||
var ReconnectingWebSocket = class _ReconnectingWebSocket extends EventTarget { | ||
_ws; | ||
_listeners = { | ||
error: [], | ||
message: [], | ||
open: [], | ||
close: [] | ||
}; | ||
_retryCount = -1; | ||
@@ -99,2 +123,3 @@ _uptimeTimeout; | ||
_messageQueue = []; | ||
_debugLogger = console.log.bind(console); | ||
_url; | ||
@@ -104,2 +129,3 @@ _protocols; | ||
constructor(url, protocols, options = {}) { | ||
super(); | ||
this._url = url; | ||
@@ -111,2 +137,5 @@ this._protocols = protocols; | ||
} | ||
if (this._options.debugLogger) { | ||
this._debugLogger = this._options.debugLogger; | ||
} | ||
this._connect(); | ||
@@ -127,12 +156,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; | ||
} | ||
@@ -195,3 +224,3 @@ get binaryType() { | ||
} | ||
return this._options.startClosed ? ReconnectingWebSocket.CLOSED : ReconnectingWebSocket.CONNECTING; | ||
return this._options.startClosed ? _ReconnectingWebSocket.CLOSED : _ReconnectingWebSocket.CONNECTING; | ||
} | ||
@@ -275,33 +304,5 @@ /** | ||
} | ||
/** | ||
* Register an event handler of a specific event type | ||
*/ | ||
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; | ||
} | ||
/** | ||
* Removes an event listener | ||
*/ | ||
removeEventListener(type, listener) { | ||
if (this._listeners[type]) { | ||
this._listeners[type] = this._listeners[type].filter( | ||
// @ts-expect-error we need to fix event/listerner types | ||
(l) => l !== listener | ||
); | ||
} | ||
} | ||
_debug(...args) { | ||
if (this._options.debug) { | ||
console.log.apply(console, ["RWS>", ...args]); | ||
this._debugLogger("RWS>", ...args); | ||
} | ||
@@ -380,5 +381,2 @@ } | ||
this._removeListeners(); | ||
if (!isWebSocket(WebSocket)) { | ||
throw Error("No valid WebSocket class provided"); | ||
} | ||
this._wait().then( | ||
@@ -394,4 +392,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; | ||
@@ -429,9 +445,2 @@ this._connectLock = false; | ||
} | ||
_callEventListener(event, listener) { | ||
if ("handleEvent" in listener) { | ||
listener.handleEvent(event); | ||
} else { | ||
listener(event); | ||
} | ||
} | ||
_handleOpen = (event) => { | ||
@@ -449,5 +458,3 @@ this._debug("open event"); | ||
} | ||
this._listeners.open.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
}; | ||
@@ -459,5 +466,3 @@ _handleMessage = (event) => { | ||
} | ||
this._listeners.message.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
}; | ||
@@ -474,5 +479,3 @@ _handleError = (event) => { | ||
this._debug("exec error listeners"); | ||
this._listeners.error.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
this._connect(); | ||
@@ -489,5 +492,3 @@ }; | ||
} | ||
this._listeners.close.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
}; | ||
@@ -521,4 +522,5 @@ _removeListeners() { | ||
// src/index.ts | ||
var valueIsNotNil = (keyValuePair) => keyValuePair[1] !== null && keyValuePair[1] !== void 0; | ||
function generateUUID() { | ||
if (crypto.randomUUID) { | ||
if (typeof crypto !== "undefined" && crypto.randomUUID) { | ||
return crypto.randomUUID(); | ||
@@ -540,18 +542,135 @@ } | ||
} | ||
function getPartyInfo(partySocketOptions, defaultProtocol, defaultParams = {}) { | ||
const { | ||
host: rawHost, | ||
path: rawPath, | ||
protocol: rawProtocol, | ||
room, | ||
party, | ||
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}/${party ? `parties/${party}` : "party"}/${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 { host, room, protocol, query, protocols, ...socketOptions } = partySocketOptions; | ||
const _pk = generateUUID(); | ||
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()}`; | ||
} else { | ||
url += `?_pk=${_pk}`; | ||
} | ||
super(url, protocols, socketOptions); | ||
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 | ||
}; | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
PartySocket, | ||
WebSocket | ||
}); | ||
/*! | ||
@@ -558,0 +677,0 @@ * Reconnecting WebSocket |
@@ -23,16 +23,20 @@ "use strict"; | ||
__export(react_exports, { | ||
default: () => usePartySocket | ||
default: () => usePartySocket, | ||
usePartySocket: () => usePartySocket, | ||
useWebSocket: () => useWebSocket | ||
}); | ||
module.exports = __toCommonJS(react_exports); | ||
var import_react = require("react"); | ||
// src/ws.ts | ||
var Event = class { | ||
target; | ||
type; | ||
constructor(type, target) { | ||
this.target = target; | ||
this.type = type; | ||
} | ||
}; | ||
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 { | ||
@@ -67,5 +71,29 @@ message; | ||
} | ||
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); | ||
} | ||
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 = { | ||
@@ -82,10 +110,5 @@ maxReconnectionDelay: 1e4, | ||
}; | ||
var ReconnectingWebSocket = class { | ||
var didWarnAboutMissingWebSocket = false; | ||
var ReconnectingWebSocket = class _ReconnectingWebSocket extends EventTarget { | ||
_ws; | ||
_listeners = { | ||
error: [], | ||
message: [], | ||
open: [], | ||
close: [] | ||
}; | ||
_retryCount = -1; | ||
@@ -99,2 +122,3 @@ _uptimeTimeout; | ||
_messageQueue = []; | ||
_debugLogger = console.log.bind(console); | ||
_url; | ||
@@ -104,2 +128,3 @@ _protocols; | ||
constructor(url, protocols, options = {}) { | ||
super(); | ||
this._url = url; | ||
@@ -111,2 +136,5 @@ this._protocols = protocols; | ||
} | ||
if (this._options.debugLogger) { | ||
this._debugLogger = this._options.debugLogger; | ||
} | ||
this._connect(); | ||
@@ -127,12 +155,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; | ||
} | ||
@@ -195,3 +223,3 @@ get binaryType() { | ||
} | ||
return this._options.startClosed ? ReconnectingWebSocket.CLOSED : ReconnectingWebSocket.CONNECTING; | ||
return this._options.startClosed ? _ReconnectingWebSocket.CLOSED : _ReconnectingWebSocket.CONNECTING; | ||
} | ||
@@ -275,33 +303,5 @@ /** | ||
} | ||
/** | ||
* Register an event handler of a specific event type | ||
*/ | ||
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; | ||
} | ||
/** | ||
* Removes an event listener | ||
*/ | ||
removeEventListener(type, listener) { | ||
if (this._listeners[type]) { | ||
this._listeners[type] = this._listeners[type].filter( | ||
// @ts-expect-error we need to fix event/listerner types | ||
(l) => l !== listener | ||
); | ||
} | ||
} | ||
_debug(...args) { | ||
if (this._options.debug) { | ||
console.log.apply(console, ["RWS>", ...args]); | ||
this._debugLogger("RWS>", ...args); | ||
} | ||
@@ -380,5 +380,2 @@ } | ||
this._removeListeners(); | ||
if (!isWebSocket(WebSocket)) { | ||
throw Error("No valid WebSocket class provided"); | ||
} | ||
this._wait().then( | ||
@@ -394,4 +391,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; | ||
@@ -429,9 +444,2 @@ this._connectLock = false; | ||
} | ||
_callEventListener(event, listener) { | ||
if ("handleEvent" in listener) { | ||
listener.handleEvent(event); | ||
} else { | ||
listener(event); | ||
} | ||
} | ||
_handleOpen = (event) => { | ||
@@ -449,5 +457,3 @@ this._debug("open event"); | ||
} | ||
this._listeners.open.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
}; | ||
@@ -459,5 +465,3 @@ _handleMessage = (event) => { | ||
} | ||
this._listeners.message.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
}; | ||
@@ -474,5 +478,3 @@ _handleError = (event) => { | ||
this._debug("exec error listeners"); | ||
this._listeners.error.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
this._connect(); | ||
@@ -489,5 +491,3 @@ }; | ||
} | ||
this._listeners.close.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
}; | ||
@@ -521,4 +521,5 @@ _removeListeners() { | ||
// src/index.ts | ||
var valueIsNotNil = (keyValuePair) => keyValuePair[1] !== null && keyValuePair[1] !== void 0; | ||
function generateUUID() { | ||
if (crypto.randomUUID) { | ||
if (typeof crypto !== "undefined" && crypto.randomUUID) { | ||
return crypto.randomUUID(); | ||
@@ -540,35 +541,255 @@ } | ||
} | ||
function getPartyInfo(partySocketOptions, defaultProtocol, defaultParams = {}) { | ||
const { | ||
host: rawHost, | ||
path: rawPath, | ||
protocol: rawProtocol, | ||
room, | ||
party, | ||
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}/${party ? `parties/${party}` : "party"}/${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 { host, room, protocol, query, protocols, ...socketOptions } = partySocketOptions; | ||
const _pk = generateUUID(); | ||
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()}`; | ||
} else { | ||
url += `?_pk=${_pk}`; | ||
} | ||
super(url, protocols, socketOptions); | ||
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 | ||
}; | ||
} | ||
// src/react.ts | ||
function usePartySocket(options) { | ||
const socketRef = (0, import_react.useRef)( | ||
new PartySocket({ | ||
...options, | ||
startClosed: true | ||
}) | ||
); | ||
// 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)(() => { | ||
socketRef.current.reconnect(); | ||
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); | ||
}; | ||
}, []); | ||
return socketRef.current; | ||
}, [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; | ||
} | ||
// 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 | ||
}); | ||
/*! | ||
@@ -575,0 +796,0 @@ * Reconnecting WebSocket |
151
dist/ws.js
@@ -25,14 +25,16 @@ "use strict"; | ||
ErrorEvent: () => ErrorEvent, | ||
Event: () => Event, | ||
default: () => ReconnectingWebSocket | ||
}); | ||
module.exports = __toCommonJS(ws_exports); | ||
var Event = class { | ||
target; | ||
type; | ||
constructor(type, target) { | ||
this.target = target; | ||
this.type = type; | ||
} | ||
}; | ||
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 { | ||
@@ -67,5 +69,29 @@ message; | ||
} | ||
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); | ||
} | ||
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 = { | ||
@@ -82,10 +108,5 @@ maxReconnectionDelay: 1e4, | ||
}; | ||
var ReconnectingWebSocket = class { | ||
var didWarnAboutMissingWebSocket = false; | ||
var ReconnectingWebSocket = class _ReconnectingWebSocket extends EventTarget { | ||
_ws; | ||
_listeners = { | ||
error: [], | ||
message: [], | ||
open: [], | ||
close: [] | ||
}; | ||
_retryCount = -1; | ||
@@ -99,2 +120,3 @@ _uptimeTimeout; | ||
_messageQueue = []; | ||
_debugLogger = console.log.bind(console); | ||
_url; | ||
@@ -104,2 +126,3 @@ _protocols; | ||
constructor(url, protocols, options = {}) { | ||
super(); | ||
this._url = url; | ||
@@ -111,2 +134,5 @@ this._protocols = protocols; | ||
} | ||
if (this._options.debugLogger) { | ||
this._debugLogger = this._options.debugLogger; | ||
} | ||
this._connect(); | ||
@@ -127,12 +153,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; | ||
} | ||
@@ -195,3 +221,3 @@ get binaryType() { | ||
} | ||
return this._options.startClosed ? ReconnectingWebSocket.CLOSED : ReconnectingWebSocket.CONNECTING; | ||
return this._options.startClosed ? _ReconnectingWebSocket.CLOSED : _ReconnectingWebSocket.CONNECTING; | ||
} | ||
@@ -275,33 +301,5 @@ /** | ||
} | ||
/** | ||
* Register an event handler of a specific event type | ||
*/ | ||
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; | ||
} | ||
/** | ||
* Removes an event listener | ||
*/ | ||
removeEventListener(type, listener) { | ||
if (this._listeners[type]) { | ||
this._listeners[type] = this._listeners[type].filter( | ||
// @ts-expect-error we need to fix event/listerner types | ||
(l) => l !== listener | ||
); | ||
} | ||
} | ||
_debug(...args) { | ||
if (this._options.debug) { | ||
console.log.apply(console, ["RWS>", ...args]); | ||
this._debugLogger("RWS>", ...args); | ||
} | ||
@@ -380,5 +378,2 @@ } | ||
this._removeListeners(); | ||
if (!isWebSocket(WebSocket)) { | ||
throw Error("No valid WebSocket class provided"); | ||
} | ||
this._wait().then( | ||
@@ -394,4 +389,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; | ||
@@ -429,9 +442,2 @@ this._connectLock = false; | ||
} | ||
_callEventListener(event, listener) { | ||
if ("handleEvent" in listener) { | ||
listener.handleEvent(event); | ||
} else { | ||
listener(event); | ||
} | ||
} | ||
_handleOpen = (event) => { | ||
@@ -449,5 +455,3 @@ this._debug("open event"); | ||
} | ||
this._listeners.open.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
}; | ||
@@ -459,5 +463,3 @@ _handleMessage = (event) => { | ||
} | ||
this._listeners.message.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
}; | ||
@@ -474,5 +476,3 @@ _handleError = (event) => { | ||
this._debug("exec error listeners"); | ||
this._listeners.error.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
this._connect(); | ||
@@ -489,5 +489,3 @@ }; | ||
} | ||
this._listeners.close.forEach( | ||
(listener) => this._callEventListener(event, listener) | ||
); | ||
this.dispatchEvent(cloneEvent(event)); | ||
}; | ||
@@ -522,4 +520,3 @@ _removeListeners() { | ||
CloseEvent, | ||
ErrorEvent, | ||
Event | ||
ErrorEvent | ||
}); | ||
@@ -526,0 +523,0 @@ /*! |
import ReconnectingWebSocket, { Options } from './ws.js'; | ||
type PartySocketOptions = Omit<Options, "WebSocket" | "constructor"> & { | ||
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; | ||
protocol?: "ws" | "wss"; | ||
protocols?: string[]; | ||
query?: Record<string, string>; | ||
path?: string; | ||
query?: Params | (() => Params | Promise<Params>); | ||
}; | ||
type PartyFetchOptions = { | ||
host: string; | ||
room: string; | ||
party?: 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>; | ||
} | ||
export { PartySocketOptions, PartySocket as default }; | ||
export { type PartyFetchOptions, PartySocket, type PartySocketOptions, ReconnectingWebSocket as WebSocket, PartySocket as default }; |
{ | ||
"name": "partysocket", | ||
"version": "0.0.0-46d871d", | ||
"description": "party hotline", | ||
"version": "0.0.0-479e6b5", | ||
"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", | ||
@@ -22,2 +24,12 @@ "module": "./dist/index.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" | ||
} | ||
@@ -29,3 +41,5 @@ }, | ||
"src/react.ts", | ||
"src/ws.ts" | ||
"src/ws.ts", | ||
"src/use-ws.ts", | ||
"src/event-target-polyfill.ts" | ||
], | ||
@@ -38,9 +52,6 @@ "format": [ | ||
}, | ||
"dependencies": { | ||
"react": "^18.2.0" | ||
}, | ||
"scripts": { | ||
"clean": "rm -rf dist *.d.ts", | ||
"post-build": "mv dist/*.d.ts* .", | ||
"build": "npm run clean && tsup && npm run post-build", | ||
"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" | ||
@@ -50,7 +61,26 @@ }, | ||
"dist", | ||
"*.d.ts" | ||
"*.d.ts", | ||
"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,6 +0,11 @@ | ||
import PartySocket, { PartySocketOptions } from './index.js'; | ||
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'; | ||
declare function usePartySocket(options: PartySocketOptions): PartySocket; | ||
type UsePartySocketOptions = Omit<PartySocketOptions, "host"> & EventHandlerOptions & { | ||
host?: string | undefined; | ||
}; | ||
declare function usePartySocket(options: UsePartySocketOptions): PartySocket; | ||
export { usePartySocket as default }; | ||
export { usePartySocket as default, usePartySocket }; |
110
README.md
@@ -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 | ||
[](https://travis-ci.org/pladaria/reconnecting-websocket) | ||
[](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 |
61
ws.d.ts
@@ -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,7 +17,3 @@ * Reconnecting WebSocket | ||
*/ | ||
declare class Event { | ||
target: any; | ||
type: string; | ||
constructor(type: string, target: any); | ||
} | ||
declare class ErrorEvent extends Event { | ||
@@ -30,17 +36,4 @@ message: string; | ||
} | ||
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; | ||
}; | ||
} | ||
type Options = { | ||
WebSocket?: any; | ||
maxReconnectionDelay?: number; | ||
@@ -55,2 +48,3 @@ minReconnectionDelay?: number; | ||
debug?: boolean; | ||
debugLogger?: (...args: any[]) => void; | ||
}; | ||
@@ -60,11 +54,5 @@ type UrlProvider = string | (() => string) | (() => Promise<string>); | ||
type Message = string | ArrayBuffer | Blob | ArrayBufferView; | ||
type ListenersMap = { | ||
error: WebSocketEventListenerMap["error"][]; | ||
message: WebSocketEventListenerMap["message"][]; | ||
open: WebSocketEventListenerMap["open"][]; | ||
close: WebSocketEventListenerMap["close"][]; | ||
}; | ||
declare class ReconnectingWebSocket { | ||
declare const ReconnectingWebSocket_base: TypedEventTarget<WebSocketEventMap>; | ||
declare class ReconnectingWebSocket extends ReconnectingWebSocket_base { | ||
private _ws; | ||
private _listeners; | ||
private _retryCount; | ||
@@ -78,5 +66,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); | ||
@@ -158,11 +147,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; | ||
@@ -177,3 +157,2 @@ private _getNextDelay; | ||
private _acceptOpen; | ||
private _callEventListener; | ||
private _handleOpen; | ||
@@ -188,2 +167,2 @@ private _handleMessage; | ||
export { CloseEvent, ErrorEvent, Event, ListenersMap, Message, Options, ProtocolsProvider, UrlProvider, WebSocketEventListenerMap, WebSocketEventMap, ReconnectingWebSocket as default }; | ||
export { CloseEvent, ErrorEvent, type Message, type Options, type ProtocolsProvider, type UrlProvider, type WebSocketEventMap, ReconnectingWebSocket as default }; |
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
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
116086
56.68%23
76.92%3562
49.73%2
-33.33%230
7.48%0
-100%1
Infinity%3
200%+ Added
+ Added
- Removed
- Removed
- Removed
- Removed