UNPKG

partysocket

Version:
1 lines 11.9 kB
{"version":3,"file":"index.cjs","names":["query","ReconnectingWebSocket","partySocketOptions: PartySocketOptions"],"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 disableNameValidation?: boolean; // disable validation of party/room names\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 (crypto?.randomUUID) {\n return crypto.randomUUID();\n }\n let d = Date.now(); //Timestamp\n let d2 = (performance?.now && performance.now() * 1000) || 0; //Time in microseconds since page-load or 0 if unsupported\n // biome-ignore lint/complexity/useArrowFunction: it's fine\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 basePath?: 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 if (!partySocketOptions.startClosed && !this.room && !this.basePath) {\n this.close();\n throw new Error(\n \"Either room or basePath must be provided to connect. Use startClosed: true to create a socket and set them via updateProperties before calling reconnect().\"\n );\n }\n\n if (!partySocketOptions.disableNameValidation) {\n if (partySocketOptions.party?.includes(\"/\")) {\n console.warn(\n `PartySocket: party name \"${partySocketOptions.party}\" contains forward slash which may cause routing issues. Consider using a name without forward slashes or set disableNameValidation: true to bypass this warning.`\n );\n }\n if (partySocketOptions.room?.includes(\"/\")) {\n console.warn(\n `PartySocket: room name \"${partySocketOptions.room}\" contains forward slash which may cause routing issues. Consider using a name without forward slashes or set disableNameValidation: true to bypass this warning.`\n );\n }\n }\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 basePath: partySocketOptions.basePath ?? this.basePath\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, basePath } = 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 this.basePath = basePath;\n }\n\n public reconnect(\n code?: number | undefined,\n reason?: string | undefined\n ): void {\n if (!this.host) {\n throw new Error(\n \"The host must be set before connecting, use `updateProperties` method to set it or pass it to the constructor.\"\n );\n }\n if (!this.room && !this.basePath) {\n throw new Error(\n \"The room (or basePath) must be set before connecting, use `updateProperties` method to set it or pass it 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 basePath: partySocketOptions.basePath,\n protocols: protocols,\n socketOptions: socketOptions,\n urlProvider: party.urlProvider\n };\n}\n"],"mappings":"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"}