@unshared/client 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{Nc32BOzg.d.ts → BGG3bT7O.d.ts} +3 -2
- package/dist/chunks/{Cyb3f4h0.js → C83nLcQu.js} +2 -4
- package/dist/chunks/C83nLcQu.js.map +1 -0
- package/dist/chunks/{SZf7rksc.cjs → DJyo3R5b.cjs} +3 -4
- package/dist/chunks/DJyo3R5b.cjs.map +1 -0
- package/dist/chunks/{5p4H9ZaI.d.ts → DMWBcqy0.d.ts} +3 -2
- package/dist/createClient.cjs +1 -1
- package/dist/createClient.cjs.map +1 -1
- package/dist/createClient.d.ts +5 -7
- package/dist/createClient.js +1 -1
- package/dist/createClient.js.map +1 -1
- package/dist/createService.d.ts +4 -3
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -4
- package/dist/index.js +1 -1
- package/dist/openapi.d.ts +4 -3
- package/dist/utils.d.ts +4 -3
- package/dist/websocket.cjs +1 -2
- package/dist/websocket.cjs.map +1 -1
- package/dist/websocket.d.ts +3 -3
- package/dist/websocket.js +1 -2
- package/dist/websocket.js.map +1 -1
- package/package.json +4 -4
- package/dist/chunks/Cyb3f4h0.js.map +0 -1
- package/dist/chunks/SZf7rksc.cjs.map +0 -1
@@ -1,4 +1,4 @@
|
|
1
|
-
import { MaybeArray,
|
1
|
+
import { MaybeArray, ObjectLike, Loose, UnionMerge, MaybeLiteral } from '@unshared/types';
|
2
2
|
import { HttpHeader } from '../HttpHeaders.js';
|
3
3
|
import { HttpMethod } from '../HttpMethods.js';
|
4
4
|
|
@@ -161,4 +161,5 @@ interface RequestOptions<Method extends FetchMethod = FetchMethod, BaseUrl exten
|
|
161
161
|
*/
|
162
162
|
declare function request(route: string, options?: RequestOptions): Promise<unknown>;
|
163
163
|
|
164
|
-
export {
|
164
|
+
export { parseRequest as p, request as r, toSearchParams as t };
|
165
|
+
export type { FetchOptions as F, RequestOptions as R, SearchParamsObject as S, ToSearchParamsOptions as T, FetchMethod as a, RequestContext as b, FetchHeaders as c, SearchArrayFormat as d };
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import { awaitable } from "@unshared/functions/awaitable";
|
2
1
|
import { p as parseRequestParameters, a as parseRequestQuery } from "./B6pUErTM.js";
|
3
2
|
const EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\/{2}[^/]+)?(?<path>\/[^\s?]*)/i, PROTOCOLS = /* @__PURE__ */ new Set(["ws", "wss"]);
|
4
3
|
function parseConnectUrl(parameters, channel, options) {
|
@@ -75,12 +74,11 @@ class WebSocketChannel {
|
|
75
74
|
}
|
76
75
|
}
|
77
76
|
function connect(route, options) {
|
78
|
-
|
79
|
-
return awaitable(channel, () => channel.open());
|
77
|
+
return new WebSocketChannel(route, options);
|
80
78
|
}
|
81
79
|
export {
|
82
80
|
WebSocketChannel as W,
|
83
81
|
connect as c,
|
84
82
|
parseConnectOptions as p
|
85
83
|
};
|
86
|
-
//# sourceMappingURL=
|
84
|
+
//# sourceMappingURL=C83nLcQu.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"C83nLcQu.js","sources":["../../websocket/parseConnectOptions.ts","../../websocket/connect.ts"],"sourcesContent":["import type { Loose, ObjectLike, UnionMerge } from '@unshared/types'\nimport { parseRequestParameters } from '../utils/parseRequestParameters'\nimport { parseRequestQuery } from '../utils/parseRequestQuery'\n\n/** Regular expression to match the request method and URL. */\nconst EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\\/{2}[^/]+)?(?<path>\\/[^\\s?]*)/i\n\n/** Valid WebSocket protocols. */\nconst PROTOCOLS = new Set(['ws', 'wss'])\n\n/** The protocols to use for the connection. */\nexport type ConnectProtocol = 'WS' | 'WSS'\n\n/** Options to pass to the `createChannel` function. */\nexport interface ConnectOptions<\n BaseUrl extends string = string,\n Query extends ObjectLike = ObjectLike,\n Parameters extends ObjectLike = ObjectLike,\n ClientData extends ObjectLike = any,\n ServerData extends ObjectLike = any,\n> {\n\n /** The protocol to use when connecting to the server. */\n protocol?: Lowercase<ConnectProtocol> | Uppercase<ConnectProtocol>\n\n /** The base URL to connect to. */\n baseUrl?: BaseUrl\n\n /**\n * The path parameters to use when connecting to the server. These parameters will be used to\n * fill in the path parameters of the connection URL.\n *\n * @example { id: 1 }\n */\n parameters?: Parameters\n\n /**\n * The query parameters to use when connecting to the server. These parameters will be used to\n * fill in the query parameters of the connection URL.\n *\n * @example { limit: 10, offset: 0 }\n */\n query?: Loose<Query>\n\n /**\n * The data to send when creating the connection. Namely, the path parameters\n * to use when connecting to the server.\n *\n * @example\n *\n * // Create a new connection to `http://localhost:8080/users/1`.\n * connect('GET /users/:id', {\n * data: { id: 1 },\n * baseUrl: 'http://localhost:8080'\n * })\n */\n data?: UnionMerge<Loose<Query> | Parameters>\n\n /**\n * The payload to send when creating the connection. Namely, the initial message\n * to send to the server when the connection is established.\n */\n initialPayload?: Loose<ClientData>\n\n /**\n * Weather to reconnect the connection when it is closed unexpectedly. If `true`,\n * the connection will automatically reconnect when it is closed. If `false`, the\n * connection will not reconnect when it is closed.\n *\n * @default false\n */\n autoReconnect?: boolean\n\n /**\n * The delay in milliseconds to wait before reconnecting the connection. This delay\n * will be used to wait before reconnecting the connection after it is closed.\n *\n * @default 0\n */\n reconnectDelay?: number\n\n /**\n * The maximum number of times to reconnect the connection before giving up. This\n * number will be used to determine when to stop trying to reconnect the connection.\n *\n * @default 3\n */\n reconnectLimit?: number\n\n /**\n * The function to call when the connection is opened. This function will be called\n * when the connection is successfully opened or reconnected.\n */\n onOpen?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed with an error. This function will\n * be called when the connection is closed unexpectedly with an error.\n */\n onError?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed. This function will be called\n * when the connection is closed unexpectedly or when the connection is closed manually.\n */\n onClose?: (event: CloseEvent) => void\n\n /**\n * The function to call when a message is received from the server. This function will\n * be called when a message is received from the server.\n */\n onMessage?: (data: ServerData) => void\n}\n\nexport interface WebSocketParameters {\n url: URL\n protocol?: 'ws' | 'wss'\n}\n\nfunction parseConnectUrl(parameters: WebSocketParameters, channel: string, options: ConnectOptions): void {\n const { baseUrl, protocol } = options\n\n // --- Extract the path, method, and base URL from the route name.\n const match = EXP_CONNECTION_CHANNEL.exec(channel)\n if (!match?.groups) throw new Error('Could not resolve the `RequestInit` object: Invalid route name.')\n const routeProtocol = protocol ?? match.groups.protocol ?? 'ws'\n const routeBaseUrl = baseUrl ?? match.groups.url\n\n // --- Assert the base URL is provided, either in the options or the route name.\n if (!routeBaseUrl) throw new Error('Could not resolve the `RequestInit` object: the `baseUrl` is missing.')\n\n // --- Assert the method is valid.\n const protocolLower = routeProtocol.toLowerCase()\n const protocolIsValid = PROTOCOLS.has(protocolLower)\n if (!protocolIsValid) throw new Error(`Could not resolve the \\`RequestInit\\` object:, the method \\`${routeProtocol}\\` is invalid.`)\n\n // --- Create the url and apply the method.\n parameters.url = new URL(routeBaseUrl)\n parameters.url.pathname += parameters.url.pathname.endsWith('/') ? match.groups.path.slice(1) : match.groups.path\n parameters.protocol = protocolLower as 'ws' | 'wss'\n}\n\nexport function parseConnectOptions(channel: string, options: ConnectOptions): WebSocketParameters {\n const { baseUrl, protocol, data, parameters = data, query = data } = options\n const wsParameters: WebSocketParameters = { url: new URL('about:blank') }\n parseConnectUrl(wsParameters, channel, { baseUrl, protocol })\n parseRequestParameters(wsParameters, { parameters })\n parseRequestQuery(wsParameters, { query })\n return wsParameters\n}\n","import type { ConnectOptions } from './parseConnectOptions'\nimport { parseConnectOptions } from './parseConnectOptions'\n\ntype RemoveListener = () => void\n\ntype ClientData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, infer R, any> ? R : any\n\ntype ServerData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, any, infer R> ? R : any\n\nexport class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {\n constructor(public channel: string, public options: T) {}\n\n /** The WebSocket connection to the server. */\n public webSocket: undefined | WebSocket\n\n /**\n * Open a new WebSocket connection to the server. The connection will be opened with the given\n * URL and protocols. If the connection is already open, the connection will be closed before\n * opening a new connection. Also add the event listeners that were passed in the options.\n *\n * @returns The WebSocket connection.\n */\n async open(): Promise<this> {\n if (this.webSocket) await this.close()\n const { url, protocol } = parseConnectOptions(this.channel, this.options)\n this.webSocket = new WebSocket(url, protocol)\n\n // --- Return a promise that resolves when the connection is opened.\n const promise = new Promise<void>((resolve, rejects) => {\n this.webSocket!.addEventListener('error', () => rejects(new Error('Failed to open the WebSocket connection')), { once: true })\n this.webSocket!.addEventListener('open', () => {\n if (this.options.initialPayload) this.send(this.options.initialPayload as ClientData<T>)\n resolve()\n }, { once: true })\n })\n\n // --- Add the options' hooks to the WebSocket connection.\n if (this.options.onOpen) this.on('open', this.options.onOpen, { once: true })\n if (this.options.onClose) this.on('close', this.options.onClose, { once: true })\n if (this.options.onError) this.on('error', this.options.onError)\n if (this.options.onMessage) this.on('message', message => this.options.onMessage!(message))\n\n // --- Handle reconnection when the connection is closed unexpectedly.\n this.webSocket.addEventListener('close', (event) => {\n if (event.code === 1000) return\n if (!this.options.autoReconnect) return\n if (this.options.reconnectLimit && event.wasClean) return\n setTimeout(() => void this.open(), this.options.reconnectDelay ?? 0)\n }, { once: true })\n\n return promise.then(() => this)\n }\n\n /**\n * Send a payload to the server. The payload will be serialized to JSON before sending.\n *\n * @param payload The data to send to the server.\n */\n send(payload: ClientData<T>) {\n if (!this.webSocket) throw new Error('WebSocket connection is not open')\n const json = JSON.stringify(payload)\n this.webSocket.send(json)\n }\n\n /**\n * Listen for events from the server. The event will be deserialized from JSON before calling the callback.\n *\n * @param event The event to listen for.\n * @param callback The callback to call when the event is received.\n * @returns A function to remove the event listener.\n */\n on(event: 'message', callback: (data: ServerData<T>) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'close', callback: (event: CloseEvent) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'error', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'open', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: string, callback: (data: any) => any, options?: AddEventListenerOptions) {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n\n const listener = async(event: CloseEvent | Event | MessageEvent<Blob>): Promise<void> => {\n if (event.type !== 'message') callback(event)\n // @ts-expect-error: `data` exists on the event.\n let data = event.data as unknown\n if (data instanceof Blob) data = await data.text()\n try { data = JSON.parse(data as string) }\n catch { console.error('Failed to parse the message:', data) }\n callback(data)\n }\n\n /* eslint-disable @typescript-eslint/no-misused-promises */\n this.webSocket.addEventListener(event, listener, options)\n return () => this.webSocket!.removeEventListener(event, listener)\n /* eslint-enable @typescript-eslint/no-misused-promises */\n }\n\n /**\n * Close the WebSocket connection to the server. The connection will not be able to send or receive\n * messages after it is closed.\n */\n async close() {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n if (this.webSocket.readyState === WebSocket.CLOSED) return\n if (this.webSocket.readyState === WebSocket.CLOSING) return\n this.webSocket.close(1000, 'Client closed the connection')\n await new Promise<void>(resolve => this.webSocket!.addEventListener('close', () => resolve()))\n }\n}\n\n/**\n * Create a new WebSocket connection to the server with the given path. The connection will\n * automatically reconnect if the connection is closed unexpectedly.\n *\n * @param route The name of the route to connect to.\n * @param options The options to pass to the connection.\n * @returns The WebSocket connection.\n */\nexport function connect(route: string, options: ConnectOptions): WebSocketChannel {\n return new WebSocketChannel(route, options)\n}\n"],"names":["event"],"mappings":"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"}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
"use strict";
|
2
|
-
var
|
2
|
+
var parseRequestQuery = require("./BDxlAULu.cjs");
|
3
3
|
const EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\/{2}[^/]+)?(?<path>\/[^\s?]*)/i, PROTOCOLS = /* @__PURE__ */ new Set(["ws", "wss"]);
|
4
4
|
function parseConnectUrl(parameters, channel, options) {
|
5
5
|
const { baseUrl, protocol } = options, match = EXP_CONNECTION_CHANNEL.exec(channel);
|
@@ -75,10 +75,9 @@ class WebSocketChannel {
|
|
75
75
|
}
|
76
76
|
}
|
77
77
|
function connect(route, options) {
|
78
|
-
|
79
|
-
return awaitable.awaitable(channel, () => channel.open());
|
78
|
+
return new WebSocketChannel(route, options);
|
80
79
|
}
|
81
80
|
exports.WebSocketChannel = WebSocketChannel;
|
82
81
|
exports.connect = connect;
|
83
82
|
exports.parseConnectOptions = parseConnectOptions;
|
84
|
-
//# sourceMappingURL=
|
83
|
+
//# sourceMappingURL=DJyo3R5b.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"DJyo3R5b.cjs","sources":["../../websocket/parseConnectOptions.ts","../../websocket/connect.ts"],"sourcesContent":["import type { Loose, ObjectLike, UnionMerge } from '@unshared/types'\nimport { parseRequestParameters } from '../utils/parseRequestParameters'\nimport { parseRequestQuery } from '../utils/parseRequestQuery'\n\n/** Regular expression to match the request method and URL. */\nconst EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\\/{2}[^/]+)?(?<path>\\/[^\\s?]*)/i\n\n/** Valid WebSocket protocols. */\nconst PROTOCOLS = new Set(['ws', 'wss'])\n\n/** The protocols to use for the connection. */\nexport type ConnectProtocol = 'WS' | 'WSS'\n\n/** Options to pass to the `createChannel` function. */\nexport interface ConnectOptions<\n BaseUrl extends string = string,\n Query extends ObjectLike = ObjectLike,\n Parameters extends ObjectLike = ObjectLike,\n ClientData extends ObjectLike = any,\n ServerData extends ObjectLike = any,\n> {\n\n /** The protocol to use when connecting to the server. */\n protocol?: Lowercase<ConnectProtocol> | Uppercase<ConnectProtocol>\n\n /** The base URL to connect to. */\n baseUrl?: BaseUrl\n\n /**\n * The path parameters to use when connecting to the server. These parameters will be used to\n * fill in the path parameters of the connection URL.\n *\n * @example { id: 1 }\n */\n parameters?: Parameters\n\n /**\n * The query parameters to use when connecting to the server. These parameters will be used to\n * fill in the query parameters of the connection URL.\n *\n * @example { limit: 10, offset: 0 }\n */\n query?: Loose<Query>\n\n /**\n * The data to send when creating the connection. Namely, the path parameters\n * to use when connecting to the server.\n *\n * @example\n *\n * // Create a new connection to `http://localhost:8080/users/1`.\n * connect('GET /users/:id', {\n * data: { id: 1 },\n * baseUrl: 'http://localhost:8080'\n * })\n */\n data?: UnionMerge<Loose<Query> | Parameters>\n\n /**\n * The payload to send when creating the connection. Namely, the initial message\n * to send to the server when the connection is established.\n */\n initialPayload?: Loose<ClientData>\n\n /**\n * Weather to reconnect the connection when it is closed unexpectedly. If `true`,\n * the connection will automatically reconnect when it is closed. If `false`, the\n * connection will not reconnect when it is closed.\n *\n * @default false\n */\n autoReconnect?: boolean\n\n /**\n * The delay in milliseconds to wait before reconnecting the connection. This delay\n * will be used to wait before reconnecting the connection after it is closed.\n *\n * @default 0\n */\n reconnectDelay?: number\n\n /**\n * The maximum number of times to reconnect the connection before giving up. This\n * number will be used to determine when to stop trying to reconnect the connection.\n *\n * @default 3\n */\n reconnectLimit?: number\n\n /**\n * The function to call when the connection is opened. This function will be called\n * when the connection is successfully opened or reconnected.\n */\n onOpen?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed with an error. This function will\n * be called when the connection is closed unexpectedly with an error.\n */\n onError?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed. This function will be called\n * when the connection is closed unexpectedly or when the connection is closed manually.\n */\n onClose?: (event: CloseEvent) => void\n\n /**\n * The function to call when a message is received from the server. This function will\n * be called when a message is received from the server.\n */\n onMessage?: (data: ServerData) => void\n}\n\nexport interface WebSocketParameters {\n url: URL\n protocol?: 'ws' | 'wss'\n}\n\nfunction parseConnectUrl(parameters: WebSocketParameters, channel: string, options: ConnectOptions): void {\n const { baseUrl, protocol } = options\n\n // --- Extract the path, method, and base URL from the route name.\n const match = EXP_CONNECTION_CHANNEL.exec(channel)\n if (!match?.groups) throw new Error('Could not resolve the `RequestInit` object: Invalid route name.')\n const routeProtocol = protocol ?? match.groups.protocol ?? 'ws'\n const routeBaseUrl = baseUrl ?? match.groups.url\n\n // --- Assert the base URL is provided, either in the options or the route name.\n if (!routeBaseUrl) throw new Error('Could not resolve the `RequestInit` object: the `baseUrl` is missing.')\n\n // --- Assert the method is valid.\n const protocolLower = routeProtocol.toLowerCase()\n const protocolIsValid = PROTOCOLS.has(protocolLower)\n if (!protocolIsValid) throw new Error(`Could not resolve the \\`RequestInit\\` object:, the method \\`${routeProtocol}\\` is invalid.`)\n\n // --- Create the url and apply the method.\n parameters.url = new URL(routeBaseUrl)\n parameters.url.pathname += parameters.url.pathname.endsWith('/') ? match.groups.path.slice(1) : match.groups.path\n parameters.protocol = protocolLower as 'ws' | 'wss'\n}\n\nexport function parseConnectOptions(channel: string, options: ConnectOptions): WebSocketParameters {\n const { baseUrl, protocol, data, parameters = data, query = data } = options\n const wsParameters: WebSocketParameters = { url: new URL('about:blank') }\n parseConnectUrl(wsParameters, channel, { baseUrl, protocol })\n parseRequestParameters(wsParameters, { parameters })\n parseRequestQuery(wsParameters, { query })\n return wsParameters\n}\n","import type { ConnectOptions } from './parseConnectOptions'\nimport { parseConnectOptions } from './parseConnectOptions'\n\ntype RemoveListener = () => void\n\ntype ClientData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, infer R, any> ? R : any\n\ntype ServerData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, any, infer R> ? R : any\n\nexport class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {\n constructor(public channel: string, public options: T) {}\n\n /** The WebSocket connection to the server. */\n public webSocket: undefined | WebSocket\n\n /**\n * Open a new WebSocket connection to the server. The connection will be opened with the given\n * URL and protocols. If the connection is already open, the connection will be closed before\n * opening a new connection. Also add the event listeners that were passed in the options.\n *\n * @returns The WebSocket connection.\n */\n async open(): Promise<this> {\n if (this.webSocket) await this.close()\n const { url, protocol } = parseConnectOptions(this.channel, this.options)\n this.webSocket = new WebSocket(url, protocol)\n\n // --- Return a promise that resolves when the connection is opened.\n const promise = new Promise<void>((resolve, rejects) => {\n this.webSocket!.addEventListener('error', () => rejects(new Error('Failed to open the WebSocket connection')), { once: true })\n this.webSocket!.addEventListener('open', () => {\n if (this.options.initialPayload) this.send(this.options.initialPayload as ClientData<T>)\n resolve()\n }, { once: true })\n })\n\n // --- Add the options' hooks to the WebSocket connection.\n if (this.options.onOpen) this.on('open', this.options.onOpen, { once: true })\n if (this.options.onClose) this.on('close', this.options.onClose, { once: true })\n if (this.options.onError) this.on('error', this.options.onError)\n if (this.options.onMessage) this.on('message', message => this.options.onMessage!(message))\n\n // --- Handle reconnection when the connection is closed unexpectedly.\n this.webSocket.addEventListener('close', (event) => {\n if (event.code === 1000) return\n if (!this.options.autoReconnect) return\n if (this.options.reconnectLimit && event.wasClean) return\n setTimeout(() => void this.open(), this.options.reconnectDelay ?? 0)\n }, { once: true })\n\n return promise.then(() => this)\n }\n\n /**\n * Send a payload to the server. The payload will be serialized to JSON before sending.\n *\n * @param payload The data to send to the server.\n */\n send(payload: ClientData<T>) {\n if (!this.webSocket) throw new Error('WebSocket connection is not open')\n const json = JSON.stringify(payload)\n this.webSocket.send(json)\n }\n\n /**\n * Listen for events from the server. The event will be deserialized from JSON before calling the callback.\n *\n * @param event The event to listen for.\n * @param callback The callback to call when the event is received.\n * @returns A function to remove the event listener.\n */\n on(event: 'message', callback: (data: ServerData<T>) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'close', callback: (event: CloseEvent) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'error', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'open', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: string, callback: (data: any) => any, options?: AddEventListenerOptions) {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n\n const listener = async(event: CloseEvent | Event | MessageEvent<Blob>): Promise<void> => {\n if (event.type !== 'message') callback(event)\n // @ts-expect-error: `data` exists on the event.\n let data = event.data as unknown\n if (data instanceof Blob) data = await data.text()\n try { data = JSON.parse(data as string) }\n catch { console.error('Failed to parse the message:', data) }\n callback(data)\n }\n\n /* eslint-disable @typescript-eslint/no-misused-promises */\n this.webSocket.addEventListener(event, listener, options)\n return () => this.webSocket!.removeEventListener(event, listener)\n /* eslint-enable @typescript-eslint/no-misused-promises */\n }\n\n /**\n * Close the WebSocket connection to the server. The connection will not be able to send or receive\n * messages after it is closed.\n */\n async close() {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n if (this.webSocket.readyState === WebSocket.CLOSED) return\n if (this.webSocket.readyState === WebSocket.CLOSING) return\n this.webSocket.close(1000, 'Client closed the connection')\n await new Promise<void>(resolve => this.webSocket!.addEventListener('close', () => resolve()))\n }\n}\n\n/**\n * Create a new WebSocket connection to the server with the given path. The connection will\n * automatically reconnect if the connection is closed unexpectedly.\n *\n * @param route The name of the route to connect to.\n * @param options The options to pass to the connection.\n * @returns The WebSocket connection.\n */\nexport function connect(route: string, options: ConnectOptions): WebSocketChannel {\n return new WebSocketChannel(route, options)\n}\n"],"names":["parseRequestParameters","parseRequestQuery","event"],"mappings":"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"}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Loose, UnionMerge, Override, Pretty, CollectKey, MaybeLiteral, StringSplit } from '@unshared/types';
|
2
|
-
import { a as FetchMethod, R as RequestOptions } from './
|
2
|
+
import { a as FetchMethod, R as RequestOptions } from './BGG3bT7O.js';
|
3
3
|
import { OpenAPI } from 'openapi-types';
|
4
4
|
|
5
5
|
/** Get the base URL of an OpenAPI specification. */
|
@@ -227,4 +227,5 @@ type OpenAPIOptionsMap<T> = {
|
|
227
227
|
[K in OperationRoute<T>]: OperationOptions<T, OperationByRoute<T, K>>;
|
228
228
|
};
|
229
229
|
|
230
|
-
export {
|
230
|
+
export { OpenAPIV3 as b, getServerUrl as g, OpenAPIV2 as h, resolveOperation as r };
|
231
|
+
export type { OpenAPILike as O, ServerUrl as S, OpenAPIOptionsMap as a, OperationId as c, OperationById as d, OperationOptions as e, OperationResult as f, Operation as i, OpenAPIV2Like as j, OpenAPIV3Like as k, OperationResponse as l, OperationRoute as m, OperationByRoute as n };
|
package/dist/createClient.cjs
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
"use strict";
|
2
|
-
var attempt = require("@unshared/functions/attempt"), request = require("./chunks/DXrQkl1A.cjs"), connect = require("./chunks/
|
2
|
+
var attempt = require("@unshared/functions/attempt"), request = require("./chunks/DXrQkl1A.cjs"), connect = require("./chunks/DJyo3R5b.cjs");
|
3
3
|
require("./chunks/CVzmr2NA.cjs");
|
4
4
|
require("./chunks/BDxlAULu.cjs");
|
5
5
|
require("@unshared/functions/awaitable");
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"createClient.cjs","sources":["../createClient.ts"],"sourcesContent":["import type {
|
1
|
+
{"version":3,"file":"createClient.cjs","sources":["../createClient.ts"],"sourcesContent":["import type { Result } from '@unshared/functions/attempt'\nimport type { ServiceOptions } from './createService'\nimport type { OpenAPILike, OpenAPIOptionsMap } from './openapi'\nimport type { FetchOptions, RequestOptions } from './utils'\nimport type { ConnectOptions, WebSocketChannel } from './websocket'\nimport { attempt } from '@unshared/functions/attempt'\nimport { fetch } from './utils/fetch'\nimport { request } from './utils/request'\nimport { connect } from './websocket/connect'\n\ntype Data<T extends RequestOptions> = T extends RequestOptions<any, any, any, any, any, any, infer R, any> ? R : unknown\ntype Routes = Record<string, RequestOptions>\ntype Channels = Record<string, ConnectOptions>\n\nexport class Client<T extends Routes = Routes, U extends Channels = Channels> {\n\n /**\n * Create a new client for the application.\n *\n * @param options The options to pass to the client.\n * @example new Client({ baseUrl: 'https://api.example.com' })\n */\n constructor(public options: RequestOptions = {}) {}\n\n /**\n * Fetch a route from the API and return the `Response` object. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns The response from the server.\n */\n public fetch<K extends keyof T & string>(route: K, options?: T[K]): Promise<Response & { json: () => Promise<Data<T[K]>> }>\n public fetch(route: string, options?: FetchOptions): Promise<Response>\n public fetch(route: string, options?: FetchOptions): Promise<Response> {\n return fetch(route, { ...this.options, ...options })\n }\n\n /**\n * Fetch a route from the API and return the data. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns The data from the API.\n * @example\n * // Declare the application type.\n * type App = Application<[ModuleProduct]>\n *\n * // Create a type-safe client for the application.\n * const request = createClient<App>()\n *\n * // Fetch the data from the API.\n * const data = request('GET /api/product/:id', { data: { id: '1' } })\n */\n public request<K extends keyof T & string>(route: K, options?: T[K]): Promise<Data<T[K]>>\n public request(route: string, options?: RequestOptions): Promise<unknown>\n public request(route: string, options?: RequestOptions): Promise<unknown> {\n return request(route, { ...this.options, ...options })\n }\n\n /**\n * Attempt to fetch a route from the API and return the data. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns A result object with either the data or an error.\n * @example\n * // Declare the application type.\n * type App = Application<[ModuleProduct]>\n *\n * // Create a type-safe client for the application.\n * const request = createClient<App>()\n *\n * // Fetch the data from the API.\n * const { data, error } = requestAttempt('GET /api/product/:id', { data: { id: '1' } })\n * if (error) console.error(error)\n * else console.log(data)\n */\n public requestAttempt<K extends keyof T & string>(route: K, options?: T[K]): Promise<Result<Data<T[K]>>>\n public requestAttempt(route: string, options?: RequestOptions): Promise<Result<unknown>>\n public requestAttempt(route: string, options?: RequestOptions): Promise<Result<unknown>> {\n return attempt(() => this.request(route, options))\n }\n\n /**\n * Create a new WebSocket connection to the server with the given path. The connection will\n * automatically reconnect if the connection is closed unexpectedly.\n *\n * @param channel The path to connect to.\n * @param options The options to pass to the connection.\n * @returns The WebSocket connection.\n */\n public connect<P extends keyof U & string>(channel: P, options?: U[P]): WebSocketChannel<U[P]>\n public connect(channel: string, options?: ConnectOptions): WebSocketChannel\n public connect(channel: string, options?: ConnectOptions): WebSocketChannel {\n return connect(channel, { baseUrl: this.options.baseUrl, ...options })\n }\n}\n\n/**\n * Create a new type-safe client for the application. The client can be used to fetch data from\n * the API and connect to the server using WebSockets with the given path.\n *\n * @param options The options to pass to the client.\n * @returns The client object with the request method.\n * @example\n * // Create a type-safe client for the application.\n * const client = createClient<[ModuleUser]>()\n *\n * // Fetch the data from the API.\n * const data = await client.request('GET /api/user/:id', { id: '1' })\n *\n * // Use the data from the API.\n * console.log(data) // { id: '1', name: 'John Doe' }\n */\nexport function createClient<T extends OpenAPILike>(options?: ServiceOptions<T>): Client<OpenAPIOptionsMap<T>>\n\n/**\n * Create a new type-safe client for the application. The client can be used to fetch data from\n * the API and connect to the server using WebSockets with the given path.\n *\n * @param options The options to pass to the client.\n * @returns The client object with the request method.\n * @example\n * // Create a type-safe client for the application.\n * const client = createClient<[ModuleUser]>()\n *\n * // Fetch the data from the API.\n * const data = await client.request('GET /api/user/:id', { id: '1' })\n *\n * // Use the data from the API.\n * console.log(data) // { id: '1', name: 'John Doe' }\n */\nexport function createClient<T extends Routes = Routes, V extends Channels = Channels>(options?: RequestOptions): Client<T, V>\nexport function createClient(options?: RequestOptions): Client {\n return new Client(options)\n}\n"],"names":["fetch","request","attempt","connect"],"mappings":";;;;;AAcO,MAAM,OAAiE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5E,YAAmB,UAA0B,IAAI;AAA9B,SAAA,UAAA;AAAA,EAAA;AAAA,EAaZ,MAAM,OAAe,SAA2C;AAC9D,WAAAA,QAAAA,MAAM,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA,EAuB9C,QAAQ,OAAe,SAA4C;AACjE,WAAAC,QAAAA,QAAQ,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA,EAyBhD,eAAe,OAAe,SAAoD;AACvF,WAAOC,QAAAA,QAAQ,MAAM,KAAK,QAAQ,OAAO,OAAO,CAAC;AAAA,EAAA;AAAA,EAa5C,QAAQ,SAAiB,SAA4C;AACnE,WAAAC,QAAA,QAAQ,SAAS,EAAE,SAAS,KAAK,QAAQ,SAAS,GAAG,SAAS;AAAA,EAAA;AAEzE;AAqCO,SAAS,aAAa,SAAkC;AACtD,SAAA,IAAI,OAAO,OAAO;AAC3B;;;"}
|
package/dist/createClient.d.ts
CHANGED
@@ -1,14 +1,12 @@
|
|
1
|
-
import { Awaitable } from '@unshared/functions';
|
2
1
|
import { Result } from '@unshared/functions/attempt';
|
3
2
|
import { ServiceOptions } from './createService.js';
|
4
|
-
import { R as RequestOptions, F as FetchOptions } from './chunks/
|
5
|
-
import { O as OpenAPILike, a as OpenAPIOptionsMap } from './chunks/
|
6
|
-
import {
|
3
|
+
import { R as RequestOptions, F as FetchOptions } from './chunks/BGG3bT7O.js';
|
4
|
+
import { O as OpenAPILike, a as OpenAPIOptionsMap } from './chunks/DMWBcqy0.js';
|
5
|
+
import { ConnectOptions, WebSocketChannel } from './websocket.js';
|
7
6
|
import '@unshared/types';
|
8
7
|
import './HttpHeaders.js';
|
9
8
|
import './HttpMethods.js';
|
10
9
|
import 'openapi-types';
|
11
|
-
import '@unshared/functions/awaitable';
|
12
10
|
|
13
11
|
type Data<T extends RequestOptions> = T extends RequestOptions<any, any, any, any, any, any, infer R, any> ? R : unknown;
|
14
12
|
type Routes = Record<string, RequestOptions>;
|
@@ -85,8 +83,8 @@ declare class Client<T extends Routes = Routes, U extends Channels = Channels> {
|
|
85
83
|
* @param options The options to pass to the connection.
|
86
84
|
* @returns The WebSocket connection.
|
87
85
|
*/
|
88
|
-
connect<P extends keyof U & string>(channel: P, options?: U[P]):
|
89
|
-
connect(channel: string, options?: ConnectOptions):
|
86
|
+
connect<P extends keyof U & string>(channel: P, options?: U[P]): WebSocketChannel<U[P]>;
|
87
|
+
connect(channel: string, options?: ConnectOptions): WebSocketChannel;
|
90
88
|
}
|
91
89
|
/**
|
92
90
|
* Create a new type-safe client for the application. The client can be used to fetch data from
|
package/dist/createClient.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { attempt } from "@unshared/functions/attempt";
|
2
2
|
import { f as fetch, r as request } from "./chunks/D1QsGr3A.js";
|
3
|
-
import { c as connect } from "./chunks/
|
3
|
+
import { c as connect } from "./chunks/C83nLcQu.js";
|
4
4
|
import "./chunks/CS5r-m4U.js";
|
5
5
|
import "./chunks/B6pUErTM.js";
|
6
6
|
import "@unshared/functions/awaitable";
|
package/dist/createClient.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"createClient.js","sources":["../createClient.ts"],"sourcesContent":["import type {
|
1
|
+
{"version":3,"file":"createClient.js","sources":["../createClient.ts"],"sourcesContent":["import type { Result } from '@unshared/functions/attempt'\nimport type { ServiceOptions } from './createService'\nimport type { OpenAPILike, OpenAPIOptionsMap } from './openapi'\nimport type { FetchOptions, RequestOptions } from './utils'\nimport type { ConnectOptions, WebSocketChannel } from './websocket'\nimport { attempt } from '@unshared/functions/attempt'\nimport { fetch } from './utils/fetch'\nimport { request } from './utils/request'\nimport { connect } from './websocket/connect'\n\ntype Data<T extends RequestOptions> = T extends RequestOptions<any, any, any, any, any, any, infer R, any> ? R : unknown\ntype Routes = Record<string, RequestOptions>\ntype Channels = Record<string, ConnectOptions>\n\nexport class Client<T extends Routes = Routes, U extends Channels = Channels> {\n\n /**\n * Create a new client for the application.\n *\n * @param options The options to pass to the client.\n * @example new Client({ baseUrl: 'https://api.example.com' })\n */\n constructor(public options: RequestOptions = {}) {}\n\n /**\n * Fetch a route from the API and return the `Response` object. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns The response from the server.\n */\n public fetch<K extends keyof T & string>(route: K, options?: T[K]): Promise<Response & { json: () => Promise<Data<T[K]>> }>\n public fetch(route: string, options?: FetchOptions): Promise<Response>\n public fetch(route: string, options?: FetchOptions): Promise<Response> {\n return fetch(route, { ...this.options, ...options })\n }\n\n /**\n * Fetch a route from the API and return the data. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns The data from the API.\n * @example\n * // Declare the application type.\n * type App = Application<[ModuleProduct]>\n *\n * // Create a type-safe client for the application.\n * const request = createClient<App>()\n *\n * // Fetch the data from the API.\n * const data = request('GET /api/product/:id', { data: { id: '1' } })\n */\n public request<K extends keyof T & string>(route: K, options?: T[K]): Promise<Data<T[K]>>\n public request(route: string, options?: RequestOptions): Promise<unknown>\n public request(route: string, options?: RequestOptions): Promise<unknown> {\n return request(route, { ...this.options, ...options })\n }\n\n /**\n * Attempt to fetch a route from the API and return the data. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns A result object with either the data or an error.\n * @example\n * // Declare the application type.\n * type App = Application<[ModuleProduct]>\n *\n * // Create a type-safe client for the application.\n * const request = createClient<App>()\n *\n * // Fetch the data from the API.\n * const { data, error } = requestAttempt('GET /api/product/:id', { data: { id: '1' } })\n * if (error) console.error(error)\n * else console.log(data)\n */\n public requestAttempt<K extends keyof T & string>(route: K, options?: T[K]): Promise<Result<Data<T[K]>>>\n public requestAttempt(route: string, options?: RequestOptions): Promise<Result<unknown>>\n public requestAttempt(route: string, options?: RequestOptions): Promise<Result<unknown>> {\n return attempt(() => this.request(route, options))\n }\n\n /**\n * Create a new WebSocket connection to the server with the given path. The connection will\n * automatically reconnect if the connection is closed unexpectedly.\n *\n * @param channel The path to connect to.\n * @param options The options to pass to the connection.\n * @returns The WebSocket connection.\n */\n public connect<P extends keyof U & string>(channel: P, options?: U[P]): WebSocketChannel<U[P]>\n public connect(channel: string, options?: ConnectOptions): WebSocketChannel\n public connect(channel: string, options?: ConnectOptions): WebSocketChannel {\n return connect(channel, { baseUrl: this.options.baseUrl, ...options })\n }\n}\n\n/**\n * Create a new type-safe client for the application. The client can be used to fetch data from\n * the API and connect to the server using WebSockets with the given path.\n *\n * @param options The options to pass to the client.\n * @returns The client object with the request method.\n * @example\n * // Create a type-safe client for the application.\n * const client = createClient<[ModuleUser]>()\n *\n * // Fetch the data from the API.\n * const data = await client.request('GET /api/user/:id', { id: '1' })\n *\n * // Use the data from the API.\n * console.log(data) // { id: '1', name: 'John Doe' }\n */\nexport function createClient<T extends OpenAPILike>(options?: ServiceOptions<T>): Client<OpenAPIOptionsMap<T>>\n\n/**\n * Create a new type-safe client for the application. The client can be used to fetch data from\n * the API and connect to the server using WebSockets with the given path.\n *\n * @param options The options to pass to the client.\n * @returns The client object with the request method.\n * @example\n * // Create a type-safe client for the application.\n * const client = createClient<[ModuleUser]>()\n *\n * // Fetch the data from the API.\n * const data = await client.request('GET /api/user/:id', { id: '1' })\n *\n * // Use the data from the API.\n * console.log(data) // { id: '1', name: 'John Doe' }\n */\nexport function createClient<T extends Routes = Routes, V extends Channels = Channels>(options?: RequestOptions): Client<T, V>\nexport function createClient(options?: RequestOptions): Client {\n return new Client(options)\n}\n"],"names":[],"mappings":";;;;;;AAcO,MAAM,OAAiE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5E,YAAmB,UAA0B,IAAI;AAA9B,SAAA,UAAA;AAAA,EAAA;AAAA,EAaZ,MAAM,OAAe,SAA2C;AAC9D,WAAA,MAAM,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA,EAuB9C,QAAQ,OAAe,SAA4C;AACjE,WAAA,QAAQ,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA,EAyBhD,eAAe,OAAe,SAAoD;AACvF,WAAO,QAAQ,MAAM,KAAK,QAAQ,OAAO,OAAO,CAAC;AAAA,EAAA;AAAA,EAa5C,QAAQ,SAAiB,SAA4C;AACnE,WAAA,QAAQ,SAAS,EAAE,SAAS,KAAK,QAAQ,SAAS,GAAG,SAAS;AAAA,EAAA;AAEzE;AAqCO,SAAS,aAAa,SAAkC;AACtD,SAAA,IAAI,OAAO,OAAO;AAC3B;"}
|
package/dist/createService.d.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { MaybeLiteral } from '@unshared/types';
|
2
|
-
import {
|
3
|
-
import { R as RequestOptions } from './chunks/
|
2
|
+
import { S as ServerUrl, b as OpenAPIV3, c as OperationId, d as OperationById, e as OperationOptions, f as OperationResult } from './chunks/DMWBcqy0.js';
|
3
|
+
import { R as RequestOptions } from './chunks/BGG3bT7O.js';
|
4
4
|
import 'openapi-types';
|
5
5
|
import './HttpHeaders.js';
|
6
6
|
import './HttpMethods.js';
|
@@ -35,4 +35,5 @@ interface ServiceOptions<T = any> extends RequestOptions {
|
|
35
35
|
*/
|
36
36
|
declare function createService<T extends object>(document: Readonly<T>, initialOptions?: ServiceOptions<T>): Service<T>;
|
37
37
|
|
38
|
-
export {
|
38
|
+
export { createService };
|
39
|
+
export type { Service, ServiceOptions };
|
package/dist/index.cjs
CHANGED
@@ -5,7 +5,7 @@ require("./chunks/DXrQkl1A.cjs");
|
|
5
5
|
require("./chunks/CVzmr2NA.cjs");
|
6
6
|
require("./chunks/BDxlAULu.cjs");
|
7
7
|
require("@unshared/functions/awaitable");
|
8
|
-
require("./chunks/
|
8
|
+
require("./chunks/DJyo3R5b.cjs");
|
9
9
|
require("./chunks/DEyigyGy.cjs");
|
10
10
|
exports.Client = createClient.Client;
|
11
11
|
exports.createClient = createClient.createClient;
|
package/dist/index.d.ts
CHANGED
@@ -3,11 +3,9 @@ export { Service, ServiceOptions, createService } from './createService.js';
|
|
3
3
|
export { HttpHeader } from './HttpHeaders.js';
|
4
4
|
export { HttpMethod } from './HttpMethods.js';
|
5
5
|
export { HttpStatusCode } from './HttpStatusCodes.js';
|
6
|
-
import '@unshared/functions';
|
7
6
|
import '@unshared/functions/attempt';
|
8
|
-
import './chunks/
|
7
|
+
import './chunks/BGG3bT7O.js';
|
9
8
|
import '@unshared/types';
|
10
|
-
import './chunks/
|
9
|
+
import './chunks/DMWBcqy0.js';
|
11
10
|
import 'openapi-types';
|
12
11
|
import './websocket.js';
|
13
|
-
import '@unshared/functions/awaitable';
|
package/dist/index.js
CHANGED
@@ -8,7 +8,7 @@ import "./chunks/D1QsGr3A.js";
|
|
8
8
|
import "./chunks/CS5r-m4U.js";
|
9
9
|
import "./chunks/B6pUErTM.js";
|
10
10
|
import "@unshared/functions/awaitable";
|
11
|
-
import "./chunks/
|
11
|
+
import "./chunks/C83nLcQu.js";
|
12
12
|
import "./chunks/B_Gz6Yz8.js";
|
13
13
|
export {
|
14
14
|
Client,
|
package/dist/openapi.d.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
export { O as OpenAPILike, a as OpenAPIOptionsMap, h as OpenAPIV2, j as OpenAPIV2Like,
|
1
|
+
export { O as OpenAPILike, a as OpenAPIOptionsMap, h as OpenAPIV2, j as OpenAPIV2Like, b as OpenAPIV3, k as OpenAPIV3Like, i as Operation, d as OperationById, n as OperationByRoute, c as OperationId, e as OperationOptions, l as OperationResponse, f as OperationResult, m as OperationRoute, S as ServerUrl, g as getServerUrl, r as resolveOperation } from './chunks/DMWBcqy0.js';
|
2
2
|
import { OpenAPIV2, OpenAPIV3, OpenAPIV3_1, OpenAPI } from 'openapi-types';
|
3
3
|
import { StringReplace, WriteableDeep, StringJoin, Substract } from '@unshared/types';
|
4
|
-
import './chunks/
|
4
|
+
import './chunks/BGG3bT7O.js';
|
5
5
|
import './HttpHeaders.js';
|
6
6
|
import './HttpMethods.js';
|
7
7
|
|
@@ -133,4 +133,5 @@ interface TokenOptions {
|
|
133
133
|
*/
|
134
134
|
declare function resolveOperationTokenOptions(document: object, operation: OpenAPI.Operation): TokenOptions;
|
135
135
|
|
136
|
-
export {
|
136
|
+
export { isOpenAPIV2, isOpenAPIV3, isOpenAPIV3_1, isReferenceObject, resolveDocument, resolveOperationTokenOptions, resolveReference };
|
137
|
+
export type { OpenAPIReference, OpenAPIReferenceDecoded, OpenAPIReferencePath, OpenAPIReferenceResolved, OpenAPIResolved, TokenOptions };
|
package/dist/utils.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { F as FetchOptions, R as RequestOptions, b as RequestContext } from './chunks/
|
2
|
-
export { c as FetchHeaders, a as FetchMethod, d as SearchArrayFormat, S as SearchParamsObject, T as ToSearchParamsOptions, p as parseRequest, r as request, t as toSearchParams } from './chunks/
|
1
|
+
import { F as FetchOptions, R as RequestOptions, b as RequestContext } from './chunks/BGG3bT7O.js';
|
2
|
+
export { c as FetchHeaders, a as FetchMethod, d as SearchArrayFormat, S as SearchParamsObject, T as ToSearchParamsOptions, p as parseRequest, r as request, t as toSearchParams } from './chunks/BGG3bT7O.js';
|
3
3
|
import { Awaitable } from '@unshared/functions/awaitable';
|
4
4
|
import { ObjectLike } from '@unshared/types';
|
5
5
|
import './HttpHeaders.js';
|
@@ -240,4 +240,5 @@ declare function setHeader(headers: HeadersInit, key: string, value: number | st
|
|
240
240
|
*/
|
241
241
|
declare function toFormData(object: FormDataLike): FormData;
|
242
242
|
|
243
|
-
export { FetchOptions,
|
243
|
+
export { FetchOptions, RequestContext, RequestOptions, fetch, getCookie, getCookies, getHeader, handleResponse, handleResponseStreamJson, isFormDataLike, isObjectLike, parseRequestBasicAuth, parseRequestBody, parseRequestHeaders, parseRequestParameters, parseRequestQuery, parseRequestToken, parseRequestUrl, setCookie, setHeader, toFormData };
|
244
|
+
export type { FormDataLike };
|
package/dist/websocket.cjs
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
"use strict";
|
2
|
-
var connect = require("./chunks/
|
3
|
-
require("@unshared/functions/awaitable");
|
2
|
+
var connect = require("./chunks/DJyo3R5b.cjs");
|
4
3
|
require("./chunks/BDxlAULu.cjs");
|
5
4
|
exports.WebSocketChannel = connect.WebSocketChannel;
|
6
5
|
exports.connect = connect.connect;
|
package/dist/websocket.cjs.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"websocket.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"websocket.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
|
package/dist/websocket.d.ts
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import { Awaitable } from '@unshared/functions/awaitable';
|
2
1
|
import { ObjectLike, Loose, UnionMerge } from '@unshared/types';
|
3
2
|
|
4
3
|
/** The protocols to use for the connection. */
|
@@ -138,6 +137,7 @@ declare class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {
|
|
138
137
|
* @param options The options to pass to the connection.
|
139
138
|
* @returns The WebSocket connection.
|
140
139
|
*/
|
141
|
-
declare function connect(route: string, options: ConnectOptions):
|
140
|
+
declare function connect(route: string, options: ConnectOptions): WebSocketChannel;
|
142
141
|
|
143
|
-
export {
|
142
|
+
export { WebSocketChannel, connect, parseConnectOptions };
|
143
|
+
export type { ConnectOptions, ConnectProtocol, WebSocketParameters };
|
package/dist/websocket.js
CHANGED
package/dist/websocket.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"websocket.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"websocket.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@unshared/client",
|
3
3
|
"type": "module",
|
4
|
-
"version": "0.
|
4
|
+
"version": "0.6.0",
|
5
5
|
"license": "MIT",
|
6
6
|
"sideEffects": false,
|
7
7
|
"author": "Stanley Horwood <stanley@hsjm.io>",
|
@@ -68,12 +68,12 @@
|
|
68
68
|
"LICENSE.md"
|
69
69
|
],
|
70
70
|
"dependencies": {
|
71
|
-
"@unshared/functions": "0.
|
72
|
-
"@unshared/types": "0.
|
71
|
+
"@unshared/functions": "0.6.0",
|
72
|
+
"@unshared/types": "0.6.0",
|
73
73
|
"openapi-types": "12.1.3"
|
74
74
|
},
|
75
75
|
"devDependencies": {
|
76
|
-
"@unshared/scripts": "0.
|
76
|
+
"@unshared/scripts": "0.6.0"
|
77
77
|
},
|
78
78
|
"scripts": {
|
79
79
|
"build:httpMethods": "tsx ./scripts/buildHttpMethods.ts",
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"Cyb3f4h0.js","sources":["../../websocket/parseConnectOptions.ts","../../websocket/connect.ts"],"sourcesContent":["import type { Loose, ObjectLike, UnionMerge } from '@unshared/types'\nimport { parseRequestParameters } from '../utils/parseRequestParameters'\nimport { parseRequestQuery } from '../utils/parseRequestQuery'\n\n/** Regular expression to match the request method and URL. */\nconst EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\\/{2}[^/]+)?(?<path>\\/[^\\s?]*)/i\n\n/** Valid WebSocket protocols. */\nconst PROTOCOLS = new Set(['ws', 'wss'])\n\n/** The protocols to use for the connection. */\nexport type ConnectProtocol = 'WS' | 'WSS'\n\n/** Options to pass to the `createChannel` function. */\nexport interface ConnectOptions<\n BaseUrl extends string = string,\n Query extends ObjectLike = ObjectLike,\n Parameters extends ObjectLike = ObjectLike,\n ClientData extends ObjectLike = any,\n ServerData extends ObjectLike = any,\n> {\n\n /** The protocol to use when connecting to the server. */\n protocol?: Lowercase<ConnectProtocol> | Uppercase<ConnectProtocol>\n\n /** The base URL to connect to. */\n baseUrl?: BaseUrl\n\n /**\n * The path parameters to use when connecting to the server. These parameters will be used to\n * fill in the path parameters of the connection URL.\n *\n * @example { id: 1 }\n */\n parameters?: Parameters\n\n /**\n * The query parameters to use when connecting to the server. These parameters will be used to\n * fill in the query parameters of the connection URL.\n *\n * @example { limit: 10, offset: 0 }\n */\n query?: Loose<Query>\n\n /**\n * The data to send when creating the connection. Namely, the path parameters\n * to use when connecting to the server.\n *\n * @example\n *\n * // Create a new connection to `http://localhost:8080/users/1`.\n * connect('GET /users/:id', {\n * data: { id: 1 },\n * baseUrl: 'http://localhost:8080'\n * })\n */\n data?: UnionMerge<Loose<Query> | Parameters>\n\n /**\n * The payload to send when creating the connection. Namely, the initial message\n * to send to the server when the connection is established.\n */\n initialPayload?: Loose<ClientData>\n\n /**\n * Weather to reconnect the connection when it is closed unexpectedly. If `true`,\n * the connection will automatically reconnect when it is closed. If `false`, the\n * connection will not reconnect when it is closed.\n *\n * @default false\n */\n autoReconnect?: boolean\n\n /**\n * The delay in milliseconds to wait before reconnecting the connection. This delay\n * will be used to wait before reconnecting the connection after it is closed.\n *\n * @default 0\n */\n reconnectDelay?: number\n\n /**\n * The maximum number of times to reconnect the connection before giving up. This\n * number will be used to determine when to stop trying to reconnect the connection.\n *\n * @default 3\n */\n reconnectLimit?: number\n\n /**\n * The function to call when the connection is opened. This function will be called\n * when the connection is successfully opened or reconnected.\n */\n onOpen?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed with an error. This function will\n * be called when the connection is closed unexpectedly with an error.\n */\n onError?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed. This function will be called\n * when the connection is closed unexpectedly or when the connection is closed manually.\n */\n onClose?: (event: CloseEvent) => void\n\n /**\n * The function to call when a message is received from the server. This function will\n * be called when a message is received from the server.\n */\n onMessage?: (data: ServerData) => void\n}\n\nexport interface WebSocketParameters {\n url: URL\n protocol?: 'ws' | 'wss'\n}\n\nfunction parseConnectUrl(parameters: WebSocketParameters, channel: string, options: ConnectOptions): void {\n const { baseUrl, protocol } = options\n\n // --- Extract the path, method, and base URL from the route name.\n const match = EXP_CONNECTION_CHANNEL.exec(channel)\n if (!match?.groups) throw new Error('Could not resolve the `RequestInit` object: Invalid route name.')\n const routeProtocol = protocol ?? match.groups.protocol ?? 'ws'\n const routeBaseUrl = baseUrl ?? match.groups.url\n\n // --- Assert the base URL is provided, either in the options or the route name.\n if (!routeBaseUrl) throw new Error('Could not resolve the `RequestInit` object: the `baseUrl` is missing.')\n\n // --- Assert the method is valid.\n const protocolLower = routeProtocol.toLowerCase()\n const protocolIsValid = PROTOCOLS.has(protocolLower)\n if (!protocolIsValid) throw new Error(`Could not resolve the \\`RequestInit\\` object:, the method \\`${routeProtocol}\\` is invalid.`)\n\n // --- Create the url and apply the method.\n parameters.url = new URL(routeBaseUrl)\n parameters.url.pathname += parameters.url.pathname.endsWith('/') ? match.groups.path.slice(1) : match.groups.path\n parameters.protocol = protocolLower as 'ws' | 'wss'\n}\n\nexport function parseConnectOptions(channel: string, options: ConnectOptions): WebSocketParameters {\n const { baseUrl, protocol, data, parameters = data, query = data } = options\n const wsParameters: WebSocketParameters = { url: new URL('about:blank') }\n parseConnectUrl(wsParameters, channel, { baseUrl, protocol })\n parseRequestParameters(wsParameters, { parameters })\n parseRequestQuery(wsParameters, { query })\n return wsParameters\n}\n","import type { Awaitable } from '@unshared/functions/awaitable'\nimport type { ConnectOptions } from './parseConnectOptions'\nimport { awaitable } from '@unshared/functions/awaitable'\nimport { parseConnectOptions } from './parseConnectOptions'\n\ntype RemoveListener = () => void\n\ntype ClientData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, infer R, any> ? R : any\n\ntype ServerData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, any, infer R> ? R : any\n\nexport class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {\n constructor(public channel: string, public options: T) {}\n\n /** The WebSocket connection to the server. */\n public webSocket: undefined | WebSocket\n\n /**\n * Open a new WebSocket connection to the server. The connection will be opened with the given\n * URL and protocols. If the connection is already open, the connection will be closed before\n * opening a new connection. Also add the event listeners that were passed in the options.\n *\n * @returns The WebSocket connection.\n */\n async open(): Promise<this> {\n if (this.webSocket) await this.close()\n const { url, protocol } = parseConnectOptions(this.channel, this.options)\n this.webSocket = new WebSocket(url, protocol)\n\n // --- Return a promise that resolves when the connection is opened.\n const promise = new Promise<void>((resolve, rejects) => {\n this.webSocket!.addEventListener('error', () => rejects(new Error('Failed to open the WebSocket connection')), { once: true })\n this.webSocket!.addEventListener('open', () => {\n if (this.options.initialPayload) this.send(this.options.initialPayload as ClientData<T>)\n resolve()\n }, { once: true })\n })\n\n // --- Add the options' hooks to the WebSocket connection.\n if (this.options.onOpen) this.on('open', this.options.onOpen, { once: true })\n if (this.options.onClose) this.on('close', this.options.onClose, { once: true })\n if (this.options.onError) this.on('error', this.options.onError)\n if (this.options.onMessage) this.on('message', message => this.options.onMessage!(message))\n\n // --- Handle reconnection when the connection is closed unexpectedly.\n this.webSocket.addEventListener('close', (event) => {\n if (event.code === 1000) return\n if (!this.options.autoReconnect) return\n if (this.options.reconnectLimit && event.wasClean) return\n setTimeout(() => void this.open(), this.options.reconnectDelay ?? 0)\n }, { once: true })\n\n return promise.then(() => this)\n }\n\n /**\n * Send a payload to the server. The payload will be serialized to JSON before sending.\n *\n * @param payload The data to send to the server.\n */\n send(payload: ClientData<T>) {\n if (!this.webSocket) throw new Error('WebSocket connection is not open')\n const json = JSON.stringify(payload)\n this.webSocket.send(json)\n }\n\n /**\n * Listen for events from the server. The event will be deserialized from JSON before calling the callback.\n *\n * @param event The event to listen for.\n * @param callback The callback to call when the event is received.\n * @returns A function to remove the event listener.\n */\n on(event: 'message', callback: (data: ServerData<T>) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'close', callback: (event: CloseEvent) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'error', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'open', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: string, callback: (data: any) => any, options?: AddEventListenerOptions) {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n\n const listener = async(event: CloseEvent | Event | MessageEvent<Blob>): Promise<void> => {\n if (event.type !== 'message') callback(event)\n // @ts-expect-error: `data` exists on the event.\n let data = event.data as unknown\n if (data instanceof Blob) data = await data.text()\n try { data = JSON.parse(data as string) }\n catch { console.error('Failed to parse the message:', data) }\n callback(data)\n }\n\n /* eslint-disable @typescript-eslint/no-misused-promises */\n this.webSocket.addEventListener(event, listener, options)\n return () => this.webSocket!.removeEventListener(event, listener)\n /* eslint-enable @typescript-eslint/no-misused-promises */\n }\n\n /**\n * Close the WebSocket connection to the server. The connection will not be able to send or receive\n * messages after it is closed.\n */\n async close() {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n if (this.webSocket.readyState === WebSocket.CLOSED) return\n if (this.webSocket.readyState === WebSocket.CLOSING) return\n this.webSocket.close(1000, 'Client closed the connection')\n await new Promise<void>(resolve => this.webSocket!.addEventListener('close', () => resolve()))\n }\n}\n\n/**\n * Create a new WebSocket connection to the server with the given path. The connection will\n * automatically reconnect if the connection is closed unexpectedly.\n *\n * @param route The name of the route to connect to.\n * @param options The options to pass to the connection.\n * @returns The WebSocket connection.\n */\nexport function connect(route: string, options: ConnectOptions): Awaitable<WebSocketChannel, WebSocketChannel> {\n const channel = new WebSocketChannel(route, options)\n return awaitable(channel, () => channel.open())\n}\n"],"names":["event"],"mappings":"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"}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"SZf7rksc.cjs","sources":["../../websocket/parseConnectOptions.ts","../../websocket/connect.ts"],"sourcesContent":["import type { Loose, ObjectLike, UnionMerge } from '@unshared/types'\nimport { parseRequestParameters } from '../utils/parseRequestParameters'\nimport { parseRequestQuery } from '../utils/parseRequestQuery'\n\n/** Regular expression to match the request method and URL. */\nconst EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\\/{2}[^/]+)?(?<path>\\/[^\\s?]*)/i\n\n/** Valid WebSocket protocols. */\nconst PROTOCOLS = new Set(['ws', 'wss'])\n\n/** The protocols to use for the connection. */\nexport type ConnectProtocol = 'WS' | 'WSS'\n\n/** Options to pass to the `createChannel` function. */\nexport interface ConnectOptions<\n BaseUrl extends string = string,\n Query extends ObjectLike = ObjectLike,\n Parameters extends ObjectLike = ObjectLike,\n ClientData extends ObjectLike = any,\n ServerData extends ObjectLike = any,\n> {\n\n /** The protocol to use when connecting to the server. */\n protocol?: Lowercase<ConnectProtocol> | Uppercase<ConnectProtocol>\n\n /** The base URL to connect to. */\n baseUrl?: BaseUrl\n\n /**\n * The path parameters to use when connecting to the server. These parameters will be used to\n * fill in the path parameters of the connection URL.\n *\n * @example { id: 1 }\n */\n parameters?: Parameters\n\n /**\n * The query parameters to use when connecting to the server. These parameters will be used to\n * fill in the query parameters of the connection URL.\n *\n * @example { limit: 10, offset: 0 }\n */\n query?: Loose<Query>\n\n /**\n * The data to send when creating the connection. Namely, the path parameters\n * to use when connecting to the server.\n *\n * @example\n *\n * // Create a new connection to `http://localhost:8080/users/1`.\n * connect('GET /users/:id', {\n * data: { id: 1 },\n * baseUrl: 'http://localhost:8080'\n * })\n */\n data?: UnionMerge<Loose<Query> | Parameters>\n\n /**\n * The payload to send when creating the connection. Namely, the initial message\n * to send to the server when the connection is established.\n */\n initialPayload?: Loose<ClientData>\n\n /**\n * Weather to reconnect the connection when it is closed unexpectedly. If `true`,\n * the connection will automatically reconnect when it is closed. If `false`, the\n * connection will not reconnect when it is closed.\n *\n * @default false\n */\n autoReconnect?: boolean\n\n /**\n * The delay in milliseconds to wait before reconnecting the connection. This delay\n * will be used to wait before reconnecting the connection after it is closed.\n *\n * @default 0\n */\n reconnectDelay?: number\n\n /**\n * The maximum number of times to reconnect the connection before giving up. This\n * number will be used to determine when to stop trying to reconnect the connection.\n *\n * @default 3\n */\n reconnectLimit?: number\n\n /**\n * The function to call when the connection is opened. This function will be called\n * when the connection is successfully opened or reconnected.\n */\n onOpen?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed with an error. This function will\n * be called when the connection is closed unexpectedly with an error.\n */\n onError?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed. This function will be called\n * when the connection is closed unexpectedly or when the connection is closed manually.\n */\n onClose?: (event: CloseEvent) => void\n\n /**\n * The function to call when a message is received from the server. This function will\n * be called when a message is received from the server.\n */\n onMessage?: (data: ServerData) => void\n}\n\nexport interface WebSocketParameters {\n url: URL\n protocol?: 'ws' | 'wss'\n}\n\nfunction parseConnectUrl(parameters: WebSocketParameters, channel: string, options: ConnectOptions): void {\n const { baseUrl, protocol } = options\n\n // --- Extract the path, method, and base URL from the route name.\n const match = EXP_CONNECTION_CHANNEL.exec(channel)\n if (!match?.groups) throw new Error('Could not resolve the `RequestInit` object: Invalid route name.')\n const routeProtocol = protocol ?? match.groups.protocol ?? 'ws'\n const routeBaseUrl = baseUrl ?? match.groups.url\n\n // --- Assert the base URL is provided, either in the options or the route name.\n if (!routeBaseUrl) throw new Error('Could not resolve the `RequestInit` object: the `baseUrl` is missing.')\n\n // --- Assert the method is valid.\n const protocolLower = routeProtocol.toLowerCase()\n const protocolIsValid = PROTOCOLS.has(protocolLower)\n if (!protocolIsValid) throw new Error(`Could not resolve the \\`RequestInit\\` object:, the method \\`${routeProtocol}\\` is invalid.`)\n\n // --- Create the url and apply the method.\n parameters.url = new URL(routeBaseUrl)\n parameters.url.pathname += parameters.url.pathname.endsWith('/') ? match.groups.path.slice(1) : match.groups.path\n parameters.protocol = protocolLower as 'ws' | 'wss'\n}\n\nexport function parseConnectOptions(channel: string, options: ConnectOptions): WebSocketParameters {\n const { baseUrl, protocol, data, parameters = data, query = data } = options\n const wsParameters: WebSocketParameters = { url: new URL('about:blank') }\n parseConnectUrl(wsParameters, channel, { baseUrl, protocol })\n parseRequestParameters(wsParameters, { parameters })\n parseRequestQuery(wsParameters, { query })\n return wsParameters\n}\n","import type { Awaitable } from '@unshared/functions/awaitable'\nimport type { ConnectOptions } from './parseConnectOptions'\nimport { awaitable } from '@unshared/functions/awaitable'\nimport { parseConnectOptions } from './parseConnectOptions'\n\ntype RemoveListener = () => void\n\ntype ClientData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, infer R, any> ? R : any\n\ntype ServerData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, any, infer R> ? R : any\n\nexport class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {\n constructor(public channel: string, public options: T) {}\n\n /** The WebSocket connection to the server. */\n public webSocket: undefined | WebSocket\n\n /**\n * Open a new WebSocket connection to the server. The connection will be opened with the given\n * URL and protocols. If the connection is already open, the connection will be closed before\n * opening a new connection. Also add the event listeners that were passed in the options.\n *\n * @returns The WebSocket connection.\n */\n async open(): Promise<this> {\n if (this.webSocket) await this.close()\n const { url, protocol } = parseConnectOptions(this.channel, this.options)\n this.webSocket = new WebSocket(url, protocol)\n\n // --- Return a promise that resolves when the connection is opened.\n const promise = new Promise<void>((resolve, rejects) => {\n this.webSocket!.addEventListener('error', () => rejects(new Error('Failed to open the WebSocket connection')), { once: true })\n this.webSocket!.addEventListener('open', () => {\n if (this.options.initialPayload) this.send(this.options.initialPayload as ClientData<T>)\n resolve()\n }, { once: true })\n })\n\n // --- Add the options' hooks to the WebSocket connection.\n if (this.options.onOpen) this.on('open', this.options.onOpen, { once: true })\n if (this.options.onClose) this.on('close', this.options.onClose, { once: true })\n if (this.options.onError) this.on('error', this.options.onError)\n if (this.options.onMessage) this.on('message', message => this.options.onMessage!(message))\n\n // --- Handle reconnection when the connection is closed unexpectedly.\n this.webSocket.addEventListener('close', (event) => {\n if (event.code === 1000) return\n if (!this.options.autoReconnect) return\n if (this.options.reconnectLimit && event.wasClean) return\n setTimeout(() => void this.open(), this.options.reconnectDelay ?? 0)\n }, { once: true })\n\n return promise.then(() => this)\n }\n\n /**\n * Send a payload to the server. The payload will be serialized to JSON before sending.\n *\n * @param payload The data to send to the server.\n */\n send(payload: ClientData<T>) {\n if (!this.webSocket) throw new Error('WebSocket connection is not open')\n const json = JSON.stringify(payload)\n this.webSocket.send(json)\n }\n\n /**\n * Listen for events from the server. The event will be deserialized from JSON before calling the callback.\n *\n * @param event The event to listen for.\n * @param callback The callback to call when the event is received.\n * @returns A function to remove the event listener.\n */\n on(event: 'message', callback: (data: ServerData<T>) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'close', callback: (event: CloseEvent) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'error', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'open', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: string, callback: (data: any) => any, options?: AddEventListenerOptions) {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n\n const listener = async(event: CloseEvent | Event | MessageEvent<Blob>): Promise<void> => {\n if (event.type !== 'message') callback(event)\n // @ts-expect-error: `data` exists on the event.\n let data = event.data as unknown\n if (data instanceof Blob) data = await data.text()\n try { data = JSON.parse(data as string) }\n catch { console.error('Failed to parse the message:', data) }\n callback(data)\n }\n\n /* eslint-disable @typescript-eslint/no-misused-promises */\n this.webSocket.addEventListener(event, listener, options)\n return () => this.webSocket!.removeEventListener(event, listener)\n /* eslint-enable @typescript-eslint/no-misused-promises */\n }\n\n /**\n * Close the WebSocket connection to the server. The connection will not be able to send or receive\n * messages after it is closed.\n */\n async close() {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n if (this.webSocket.readyState === WebSocket.CLOSED) return\n if (this.webSocket.readyState === WebSocket.CLOSING) return\n this.webSocket.close(1000, 'Client closed the connection')\n await new Promise<void>(resolve => this.webSocket!.addEventListener('close', () => resolve()))\n }\n}\n\n/**\n * Create a new WebSocket connection to the server with the given path. The connection will\n * automatically reconnect if the connection is closed unexpectedly.\n *\n * @param route The name of the route to connect to.\n * @param options The options to pass to the connection.\n * @returns The WebSocket connection.\n */\nexport function connect(route: string, options: ConnectOptions): Awaitable<WebSocketChannel, WebSocketChannel> {\n const channel = new WebSocketChannel(route, options)\n return awaitable(channel, () => channel.open())\n}\n"],"names":["parseRequestParameters","parseRequestQuery","event","awaitable"],"mappings":"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"}
|