partysocket
Version:
A better WebSocket that Just Works™
1 lines • 10.7 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import ReconnectingWebSocket from \"./ws\";\n\nimport type * as RWS from \"./ws\";\n\ntype Maybe<T> = T | null | undefined;\ntype Params = Record<string, Maybe<string>>;\nconst valueIsNotNil = <T>(\n keyValuePair: [string, Maybe<T>]\n): keyValuePair is [string, T] =>\n keyValuePair[1] !== null && keyValuePair[1] !== undefined;\n\nexport type PartySocketOptions = Omit<RWS.Options, \"constructor\"> & {\n id?: string; // the id of the client\n host: string; // base url for the party\n room?: string; // the room to connect to\n party?: string; // the party to connect to (defaults to main)\n basePath?: string; // the base path to use for the party\n prefix?: string; // the prefix to use for the party\n protocol?: \"ws\" | \"wss\";\n protocols?: string[];\n path?: string; // the path to connect to\n query?: Params | (() => Params | Promise<Params>);\n // headers\n};\n\nexport type PartyFetchOptions = {\n host: string; // base url for the party\n room: string; // the room to connect to\n party?: string; // the party to fetch from (defaults to main)\n basePath?: string; // the base path to use for the party\n prefix?: string; // the prefix to use for the party\n path?: string; // the path to fetch from\n protocol?: \"http\" | \"https\";\n query?: Params | (() => Params | Promise<Params>);\n fetch?: typeof fetch;\n};\n\nfunction generateUUID(): string {\n // Public Domain/MIT\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n let d = new Date().getTime(); //Timestamp\n let d2 =\n (typeof performance !== \"undefined\" &&\n performance.now &&\n performance.now() * 1000) ||\n 0; //Time in microseconds since page-load or 0 if unsupported\n // biome-ignore lint/complexity/useArrowFunction: <explanation>\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, function (c) {\n let r = Math.random() * 16; //random number between 0 and 16\n if (d > 0) {\n //Use timestamp until depleted\n r = (d + r) % 16 | 0;\n d = Math.floor(d / 16);\n } else {\n //Use microseconds since page-load if supported\n r = (d2 + r) % 16 | 0;\n d2 = Math.floor(d2 / 16);\n }\n return (c === \"x\" ? r : (r & 0x3) | 0x8).toString(16);\n });\n}\n\nfunction getPartyInfo(\n partySocketOptions: PartySocketOptions | PartyFetchOptions,\n defaultProtocol: \"http\" | \"ws\",\n defaultParams: Record<string, string> = {}\n) {\n const {\n host: rawHost,\n path: rawPath,\n protocol: rawProtocol,\n room,\n party,\n basePath,\n prefix,\n query\n } = partySocketOptions;\n\n // strip the protocol from the beginning of `host` if any\n let host = rawHost.replace(/^(http|https|ws|wss):\\/\\//, \"\");\n // if user provided a trailing slash, remove it\n if (host.endsWith(\"/\")) {\n host = host.slice(0, -1);\n }\n\n if (rawPath?.startsWith(\"/\")) {\n throw new Error(\"path must not start with a slash\");\n }\n\n const name = party ?? \"main\";\n const path = rawPath ? `/${rawPath}` : \"\";\n const protocol =\n rawProtocol ||\n (host.startsWith(\"localhost:\") ||\n host.startsWith(\"127.0.0.1:\") ||\n host.startsWith(\"192.168.\") ||\n host.startsWith(\"10.\") ||\n (host.startsWith(\"172.\") &&\n host.split(\".\")[1] >= \"16\" &&\n host.split(\".\")[1] <= \"31\") ||\n host.startsWith(\"[::ffff:7f00:1]:\")\n ? // http / ws\n defaultProtocol\n : // https / wss\n `${defaultProtocol}s`);\n\n const baseUrl = `${protocol}://${host}/${basePath || `${prefix || \"parties\"}/${name}/${room}`}${path}`;\n\n const makeUrl = (query: Params = {}) =>\n `${baseUrl}?${new URLSearchParams([\n ...Object.entries(defaultParams),\n ...Object.entries(query).filter(valueIsNotNil)\n ])}`;\n\n // allow urls to be defined as functions\n const urlProvider =\n typeof query === \"function\"\n ? async () => makeUrl(await query())\n : makeUrl(query);\n\n return {\n host,\n path,\n room,\n name,\n protocol,\n partyUrl: baseUrl,\n urlProvider\n };\n}\n\n// things that nathanboktae/robust-websocket claims are better:\n// doesn't do anything in offline mode (?)\n// \"natively aware of error codes\"\n// can do custom reconnect strategies\n\n// TODO: incorporate the above notes\nexport default class PartySocket extends ReconnectingWebSocket {\n _pk!: string;\n _pkurl!: string;\n name!: string;\n room?: string;\n host!: string;\n path!: string;\n\n constructor(readonly partySocketOptions: PartySocketOptions) {\n const wsOptions = getWSOptions(partySocketOptions);\n\n super(wsOptions.urlProvider, wsOptions.protocols, wsOptions.socketOptions);\n\n this.setWSProperties(wsOptions);\n }\n\n public updateProperties(partySocketOptions: Partial<PartySocketOptions>) {\n const wsOptions = getWSOptions({\n ...this.partySocketOptions,\n ...partySocketOptions,\n host: partySocketOptions.host ?? this.host,\n room: partySocketOptions.room ?? this.room,\n path: partySocketOptions.path ?? this.path\n });\n\n this._url = wsOptions.urlProvider;\n this._protocols = wsOptions.protocols;\n this._options = wsOptions.socketOptions;\n\n this.setWSProperties(wsOptions);\n }\n\n private setWSProperties(wsOptions: ReturnType<typeof getWSOptions>) {\n const { _pk, _pkurl, name, room, host, path } = wsOptions;\n\n this._pk = _pk;\n this._pkurl = _pkurl;\n this.name = name;\n this.room = room;\n this.host = host;\n this.path = path;\n }\n\n public reconnect(\n code?: number | undefined,\n reason?: string | undefined\n ): void {\n if (!this.room || !this.host) {\n throw new Error(\n \"The room and host must be set before connecting, use `updateProperties` method to set them or pass them to the constructor.\"\n );\n }\n super.reconnect(code, reason);\n }\n\n get id() {\n return this._pk;\n }\n\n /**\n * Exposes the static PartyKit room URL without applying query parameters.\n * To access the currently connected WebSocket url, use PartySocket#url.\n */\n get roomUrl(): string {\n return this._pkurl;\n }\n\n // a `fetch` method that uses (almost) the same options as `PartySocket`\n static async fetch(\n options: PartyFetchOptions,\n init?: RequestInit\n ): Promise<Response> {\n const party = getPartyInfo(options, \"http\");\n const url =\n typeof party.urlProvider === \"string\"\n ? party.urlProvider\n : await party.urlProvider();\n const doFetch = options.fetch ?? fetch;\n return doFetch(url, init);\n }\n}\n\nexport { PartySocket };\n\nexport { ReconnectingWebSocket as WebSocket };\n\nfunction getWSOptions(partySocketOptions: PartySocketOptions) {\n const {\n id,\n host: _host,\n path: _path,\n party: _party,\n room: _room,\n protocol: _protocol,\n query: _query,\n protocols,\n ...socketOptions\n } = partySocketOptions;\n\n const _pk = id || generateUUID();\n const party = getPartyInfo(partySocketOptions, \"ws\", { _pk });\n\n return {\n _pk: _pk,\n _pkurl: party.partyUrl,\n name: party.name,\n room: party.room,\n host: party.host,\n path: party.path,\n protocols: protocols,\n socketOptions: socketOptions,\n urlProvider: party.urlProvider\n };\n}\n"],"mappings":"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","names":["query"]}