@unshared/client 0.3.1 → 0.3.2

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.
@@ -1,3 +1,4 @@
1
+ import { awaitable } from "@unshared/functions";
1
2
  import { p as parseRequestParameters, a as parseRequestQuery } from "./0ZzUT3m_.js";
2
3
  const EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\/{2}[^/]+)?(?<path>\/[^\s?]*)/i, PROTOCOLS = /* @__PURE__ */ new Set(["ws", "wss"]);
3
4
  function parseConnectUrl(parameters, channel, options) {
@@ -10,8 +11,8 @@ function parseConnectUrl(parameters, channel, options) {
10
11
  parameters.url = new URL(routeBaseUrl), parameters.url.pathname += parameters.url.pathname.endsWith("/") ? match.groups.path.slice(1) : match.groups.path, parameters.protocol = protocolLower;
11
12
  }
12
13
  function parseConnectOptions(channel, options) {
13
- const { baseUrl, protocol, data, parameters = data, query = data } = options, context = { url: new URL("about:blank") };
14
- return parseConnectUrl(context, channel, { baseUrl, protocol }), parseRequestParameters(context, { parameters }), parseRequestQuery(context, { query }), context;
14
+ const { baseUrl, protocol, data, parameters = data, query = data } = options, wsParameters = { url: new URL("about:blank") };
15
+ return parseConnectUrl(wsParameters, channel, { baseUrl, protocol }), parseRequestParameters(wsParameters, { parameters }), parseRequestQuery(wsParameters, { query }), wsParameters;
15
16
  }
16
17
  class WebSocketChannel {
17
18
  constructor(channel, options) {
@@ -23,15 +24,21 @@ class WebSocketChannel {
23
24
  * Open a new WebSocket connection to the server. The connection will be opened with the given
24
25
  * URL and protocols. If the connection is already open, the connection will be closed before
25
26
  * opening a new connection. Also add the event listeners that were passed in the options.
27
+ *
28
+ * @returns The WebSocket connection.
26
29
  */
27
30
  async open() {
28
31
  this.webSocket && await this.close();
29
32
  const { url, protocol } = parseConnectOptions(this.channel, this.options);
30
- return this.webSocket = new WebSocket(url, protocol), this.options.onOpen && this.on("open", this.options.onOpen), this.options.onClose && this.on("close", this.options.onClose), this.options.onError && this.on("error", this.options.onError), this.options.onMessage && this.on("message", (message) => this.options.onMessage(message)), this.webSocket.addEventListener("close", (event) => {
31
- event.code !== 1e3 && this.options.autoReconnect && (this.options.reconnectLimit && event.wasClean || setTimeout(() => void this.open(), this.options.reconnectDelay ?? 0));
32
- }), new Promise((resolve, rejects) => {
33
- this.webSocket.addEventListener("open", () => resolve()), this.webSocket.addEventListener("error", () => rejects(new Error("Failed to open the WebSocket connection")));
33
+ this.webSocket = new WebSocket(url, protocol);
34
+ const promise = new Promise((resolve, rejects) => {
35
+ this.webSocket.addEventListener("error", () => rejects(new Error("Failed to open the WebSocket connection")), { once: !0 }), this.webSocket.addEventListener("open", () => {
36
+ this.options.initialPayload && this.send(this.options.initialPayload), resolve();
37
+ }, { once: !0 });
34
38
  });
39
+ return this.options.onOpen && this.on("open", this.options.onOpen, { once: !0 }), this.options.onClose && this.on("close", this.options.onClose, { once: !0 }), this.options.onError && this.on("error", this.options.onError), this.options.onMessage && this.on("message", (message) => this.options.onMessage(message)), this.webSocket.addEventListener("close", (event) => {
40
+ event.code !== 1e3 && this.options.autoReconnect && (this.options.reconnectLimit && event.wasClean || setTimeout(() => void this.open(), this.options.reconnectDelay ?? 0));
41
+ }, { once: !0 }), promise.then(() => this);
35
42
  }
36
43
  /**
37
44
  * Send a payload to the server. The payload will be serialized to JSON before sending.
@@ -43,17 +50,20 @@ class WebSocketChannel {
43
50
  const json = JSON.stringify(payload);
44
51
  this.webSocket.send(json);
45
52
  }
46
- on(event, callback) {
53
+ on(event, callback, options) {
47
54
  if (!this.webSocket) throw new Error("WebSocket connection has not been opened yet");
48
- const listener = (event2) => {
55
+ const listener = async (event2) => {
56
+ if (event2.type !== "message") return callback(event2);
49
57
  let data = event2.data;
58
+ data instanceof Blob && (data = await data.text());
50
59
  try {
51
60
  data = JSON.parse(data);
52
61
  } catch {
62
+ console.error("Failed to parse the message:", data);
53
63
  }
54
64
  callback(data);
55
65
  };
56
- return this.webSocket.addEventListener(event, listener), () => this.webSocket.removeEventListener(event, listener);
66
+ return this.webSocket.addEventListener(event, listener, options), () => this.webSocket.removeEventListener(event, listener);
57
67
  }
58
68
  /**
59
69
  * Close the WebSocket connection to the server. The connection will not be able to send or receive
@@ -65,11 +75,12 @@ class WebSocketChannel {
65
75
  }
66
76
  }
67
77
  function connect(route, options) {
68
- return new WebSocketChannel(route, options);
78
+ const channel = new WebSocketChannel(route, options);
79
+ return awaitable(channel, () => channel.open());
69
80
  }
70
81
  export {
71
82
  WebSocketChannel as W,
72
83
  connect as c,
73
84
  parseConnectOptions as p
74
85
  };
75
- //# sourceMappingURL=Du56lBvc.js.map
86
+ //# sourceMappingURL=6IxvRpmY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"6IxvRpmY.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'\nimport type { ConnectOptions } from './parseConnectOptions'\nimport { awaitable } from '@unshared/functions'\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>) => void, options?: AddEventListenerOptions): RemoveListener\n on(event: 'close', callback: (event: CloseEvent) => void, options?: AddEventListenerOptions): RemoveListener\n on(event: 'error', callback: (event: Event) => void, options?: AddEventListenerOptions): RemoveListener\n on(event: 'open', callback: (event: Event) => void, options?: AddEventListenerOptions): RemoveListener\n on(event: string, callback: (data: any) => void, 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') return 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,5 +1,5 @@
1
1
  "use strict";
2
- var parseRequestQuery = require("./CYmaYL5B.cjs");
2
+ var functions = require("@unshared/functions"), parseRequestQuery = require("./CYmaYL5B.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);
@@ -11,8 +11,8 @@ function parseConnectUrl(parameters, channel, options) {
11
11
  parameters.url = new URL(routeBaseUrl), parameters.url.pathname += parameters.url.pathname.endsWith("/") ? match.groups.path.slice(1) : match.groups.path, parameters.protocol = protocolLower;
12
12
  }
13
13
  function parseConnectOptions(channel, options) {
14
- const { baseUrl, protocol, data, parameters = data, query = data } = options, context = { url: new URL("about:blank") };
15
- return parseConnectUrl(context, channel, { baseUrl, protocol }), parseRequestQuery.parseRequestParameters(context, { parameters }), parseRequestQuery.parseRequestQuery(context, { query }), context;
14
+ const { baseUrl, protocol, data, parameters = data, query = data } = options, wsParameters = { url: new URL("about:blank") };
15
+ return parseConnectUrl(wsParameters, channel, { baseUrl, protocol }), parseRequestQuery.parseRequestParameters(wsParameters, { parameters }), parseRequestQuery.parseRequestQuery(wsParameters, { query }), wsParameters;
16
16
  }
17
17
  class WebSocketChannel {
18
18
  constructor(channel, options) {
@@ -24,15 +24,21 @@ class WebSocketChannel {
24
24
  * Open a new WebSocket connection to the server. The connection will be opened with the given
25
25
  * URL and protocols. If the connection is already open, the connection will be closed before
26
26
  * opening a new connection. Also add the event listeners that were passed in the options.
27
+ *
28
+ * @returns The WebSocket connection.
27
29
  */
28
30
  async open() {
29
31
  this.webSocket && await this.close();
30
32
  const { url, protocol } = parseConnectOptions(this.channel, this.options);
31
- return this.webSocket = new WebSocket(url, protocol), this.options.onOpen && this.on("open", this.options.onOpen), this.options.onClose && this.on("close", this.options.onClose), this.options.onError && this.on("error", this.options.onError), this.options.onMessage && this.on("message", (message) => this.options.onMessage(message)), this.webSocket.addEventListener("close", (event) => {
32
- event.code !== 1e3 && this.options.autoReconnect && (this.options.reconnectLimit && event.wasClean || setTimeout(() => void this.open(), this.options.reconnectDelay ?? 0));
33
- }), new Promise((resolve, rejects) => {
34
- this.webSocket.addEventListener("open", () => resolve()), this.webSocket.addEventListener("error", () => rejects(new Error("Failed to open the WebSocket connection")));
33
+ this.webSocket = new WebSocket(url, protocol);
34
+ const promise = new Promise((resolve, rejects) => {
35
+ this.webSocket.addEventListener("error", () => rejects(new Error("Failed to open the WebSocket connection")), { once: !0 }), this.webSocket.addEventListener("open", () => {
36
+ this.options.initialPayload && this.send(this.options.initialPayload), resolve();
37
+ }, { once: !0 });
35
38
  });
39
+ return this.options.onOpen && this.on("open", this.options.onOpen, { once: !0 }), this.options.onClose && this.on("close", this.options.onClose, { once: !0 }), this.options.onError && this.on("error", this.options.onError), this.options.onMessage && this.on("message", (message) => this.options.onMessage(message)), this.webSocket.addEventListener("close", (event) => {
40
+ event.code !== 1e3 && this.options.autoReconnect && (this.options.reconnectLimit && event.wasClean || setTimeout(() => void this.open(), this.options.reconnectDelay ?? 0));
41
+ }, { once: !0 }), promise.then(() => this);
36
42
  }
37
43
  /**
38
44
  * Send a payload to the server. The payload will be serialized to JSON before sending.
@@ -44,17 +50,20 @@ class WebSocketChannel {
44
50
  const json = JSON.stringify(payload);
45
51
  this.webSocket.send(json);
46
52
  }
47
- on(event, callback) {
53
+ on(event, callback, options) {
48
54
  if (!this.webSocket) throw new Error("WebSocket connection has not been opened yet");
49
- const listener = (event2) => {
55
+ const listener = async (event2) => {
56
+ if (event2.type !== "message") return callback(event2);
50
57
  let data = event2.data;
58
+ data instanceof Blob && (data = await data.text());
51
59
  try {
52
60
  data = JSON.parse(data);
53
61
  } catch {
62
+ console.error("Failed to parse the message:", data);
54
63
  }
55
64
  callback(data);
56
65
  };
57
- return this.webSocket.addEventListener(event, listener), () => this.webSocket.removeEventListener(event, listener);
66
+ return this.webSocket.addEventListener(event, listener, options), () => this.webSocket.removeEventListener(event, listener);
58
67
  }
59
68
  /**
60
69
  * Close the WebSocket connection to the server. The connection will not be able to send or receive
@@ -66,9 +75,10 @@ class WebSocketChannel {
66
75
  }
67
76
  }
68
77
  function connect(route, options) {
69
- return new WebSocketChannel(route, options);
78
+ const channel = new WebSocketChannel(route, options);
79
+ return functions.awaitable(channel, () => channel.open());
70
80
  }
71
81
  exports.WebSocketChannel = WebSocketChannel;
72
82
  exports.connect = connect;
73
83
  exports.parseConnectOptions = parseConnectOptions;
74
- //# sourceMappingURL=VkINJoq7.cjs.map
84
+ //# sourceMappingURL=BMbsYNBd.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BMbsYNBd.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'\nimport type { ConnectOptions } from './parseConnectOptions'\nimport { awaitable } from '@unshared/functions'\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>) => void, options?: AddEventListenerOptions): RemoveListener\n on(event: 'close', callback: (event: CloseEvent) => void, options?: AddEventListenerOptions): RemoveListener\n on(event: 'error', callback: (event: Event) => void, options?: AddEventListenerOptions): RemoveListener\n on(event: 'open', callback: (event: Event) => void, options?: AddEventListenerOptions): RemoveListener\n on(event: string, callback: (data: any) => void, 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') return 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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Biic1J5b.js","sources":["../../openapi/getServerUrl.ts","../../openapi/resolveOperation.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/** Get the base URL of an OpenAPI specification. */\nexport type ServerUrl<T> =\n\n // --- Handle OpenAPI 2.0 specifications.\n T extends {\n host: infer Host extends string\n basePath?: infer BasePath extends string\n schemes?: Array<infer Scheme extends string>\n }\n ? `${Scheme}://${Host}${BasePath}`\n\n // --- Handle OpenAPI 3.0 specifications.\n : T extends { servers: Array<{ url: infer U extends string }> }\n ? U\n : string\n\n/**\n * Given an OpenAPI specification, get the first base URL.\n *\n * @param specification The OpenAPI specification.\n * @returns The first base URL.\n * @example getBaseUrl(specification) // 'https://api.example.com/v1'\n */\nexport function getServerUrl<T>(specification: T): ServerUrl<T> {\n\n // --- Ensure the specification is an object.\n if (\n !specification\n || typeof specification !== 'object'\n || specification === null)\n throw new Error('Invalid OpenAPI specification.')\n\n // --- Handle OpenAPI 3.0 specifications.\n if (\n 'servers' in specification\n && Array.isArray(specification.servers)\n && specification.servers.length > 0\n && 'url' in specification.servers[0]\n && typeof specification.servers[0].url === 'string'\n && specification.servers[0].url.length > 0)\n return specification.servers[0].url as ServerUrl<T>\n\n // --- Handle OpenAPI 2.0 specifications.\n if (\n 'host' in specification\n && typeof specification.host === 'string') {\n\n const scheme = (\n 'schemes' in specification\n && Array.isArray(specification.schemes)\n && specification.schemes.length > 0\n && typeof specification.schemes[0] === 'string')\n ? specification.schemes[0]\n : 'https'\n\n const basePath = (\n 'basePath' in specification\n && typeof specification.basePath === 'string'\n && specification.basePath.length > 0)\n ? specification.basePath\n : '/'\n\n return `${scheme}://${specification.host}${basePath}` as ServerUrl<T>\n }\n\n throw new Error('No base URL found in the OpenAPI specification.')\n}\n","import type { CollectKey, Pretty } from '@unshared/types'\nimport type { OpenAPI } from 'openapi-types'\nimport type { FetchMethod } from '../utils/parseRequest'\n\n/** The HTTP methods supported by OpenAPI. */\nconst methods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch'] as const\n\n/** Union of all operation IDs in the specification. */\nexport type OperationId<T> =\nT extends { paths: infer P }\n ? P extends Record<string, infer R>\n ? R extends Record<string, infer O>\n ? O extends { operationId: infer N }\n ? N\n : string\n : string\n : string\n : string\n\n/** A union of possible Operations types in the specification. */\nexport type Operation = { method: FetchMethod; path: string } & OpenAPI.Operation\n\n/** Find an operation by its operationId in an OpenAPI specification. */\nexport type OperationById<T, U extends OperationId<T>> =\n T extends { paths: infer P }\n ? CollectKey<P> extends Record<string, infer R>\n ? CollectKey<R> extends Record<string, infer O>\n ? O extends { $key: [infer P extends string, infer M extends string]; operationId: U }\n ? Pretty<{ method: M; path: P } & Omit<O, '$key'>>\n : never\n : never\n : never\n : never\n\n/**\n * Given an OpenAPI specification, find an operation by its operationId.\n *\n * @param document The OpenAPI specification document.\n * @param operationId The operationId of the operation to resolve.\n * @returns The resolved operation.\n * @example resolveOperation(document, 'getUser') // { method: 'get', path: '/users/{username}', ... }\n */\nexport function resolveOperation<T, U extends OperationId<T>>(document: T, operationId: U): OperationById<T, U> {\n\n // --- Validate the specification.\n if (!document\n || typeof document !== 'object'\n || document === null\n || 'paths' in document === false\n || typeof document.paths !== 'object'\n || document.paths === null)\n throw new Error('Missing paths object in the OpenAPI specification.')\n\n // --- Search for the operation in the specification's paths.\n const paths = document.paths as OpenAPI.Document['paths']\n for (const path in paths) {\n const route = paths[path]\n if (typeof route !== 'object' || route === null) continue\n\n // --- Search in each method for the operation.\n for (const method of methods) {\n const operation = route[method]\n if (method in route === false\n || typeof operation !== 'object'\n || operation === null\n || 'operationId' in operation === false\n || operation.operationId !== operationId) continue\n\n // --- Route was found, return the operation.\n return { ...route[method], method, path } as OperationById<T, U>\n }\n }\n\n // --- Throw an error if the operation was not found.\n throw new Error(`Operation \"${operationId}\" not found in specification.`)\n}\n"],"names":[],"mappings":"AAwBO,SAAS,aAAgB,eAAgC;AAG9D,MACE,CAAC,iBACE,OAAO,iBAAkB,YACzB,kBAAkB;AACf,UAAA,IAAI,MAAM,gCAAgC;AAGlD,MACE,aAAa,iBACV,MAAM,QAAQ,cAAc,OAAO,KACnC,cAAc,QAAQ,SAAS,KAC/B,SAAS,cAAc,QAAQ,CAAC,KAChC,OAAO,cAAc,QAAQ,CAAC,EAAE,OAAQ,YACxC,cAAc,QAAQ,CAAC,EAAE,IAAI,SAAS;AAClC,WAAA,cAAc,QAAQ,CAAC,EAAE;AAGlC,MACE,UAAU,iBACP,OAAO,cAAc,QAAS,UAAU;AAE3C,UAAM,SACJ,aAAa,iBACV,MAAM,QAAQ,cAAc,OAAO,KACnC,cAAc,QAAQ,SAAS,KAC/B,OAAO,cAAc,QAAQ,CAAC,KAAM,WACrC,cAAc,QAAQ,CAAC,IACvB,SAEE,WACJ,cAAc,iBACX,OAAO,cAAc,YAAa,YAClC,cAAc,SAAS,SAAS,IACjC,cAAc,WACd;AAEJ,WAAO,GAAG,MAAM,MAAM,cAAc,IAAI,GAAG,QAAQ;AAAA,EAAA;AAG/C,QAAA,IAAI,MAAM,iDAAiD;AACnE;AC9DA,MAAM,UAAU,CAAC,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,OAAO;AAqC3D,SAAA,iBAA8C,UAAa,aAAqC;AAG9G,MAAI,CAAC,YACA,OAAO,YAAa,YACpB,aAAa,QACb,EAAW,WAAA,aACX,OAAO,SAAS,SAAU,YAC1B,SAAS,UAAU;AAChB,UAAA,IAAI,MAAM,oDAAoD;AAGtE,QAAM,QAAQ,SAAS;AACvB,aAAW,QAAQ,OAAO;AAClB,UAAA,QAAQ,MAAM,IAAI;AACpB,QAAA,EAAA,OAAO,SAAU,YAAY,UAAU;AAG3C,iBAAW,UAAU,SAAS;AACtB,cAAA,YAAY,MAAM,MAAM;AAC1B,YAAA,EAAA,EAAA,UAAU,UACT,OAAO,aAAc,YACrB,cAAc,QACd,EAAA,iBAAiB,cACjB,UAAU,gBAAgB;AAG/B,iBAAO,EAAE,GAAG,MAAM,MAAM,GAAG,QAAQ,KAAK;AAAA,MAAA;AAAA,EAC1C;AAIF,QAAM,IAAI,MAAM,cAAc,WAAW,+BAA+B;AAC1E;"}
1
+ {"version":3,"file":"Biic1J5b.js","sources":["../../openapi/getServerUrl.ts","../../openapi/resolveOperation.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/** Get the base URL of an OpenAPI specification. */\nexport type ServerUrl<T> =\n\n // --- Handle OpenAPI 2.0 specifications.\n T extends {\n host: infer Host extends string\n basePath?: infer BasePath extends string\n schemes?: Array<infer Scheme extends string>\n }\n ? `${Scheme}://${Host}${BasePath}`\n\n // --- Handle OpenAPI 3.0 specifications.\n : T extends { servers: Array<{ url: infer U extends string }> }\n ? U\n : string\n\n/**\n * Given an OpenAPI specification, get the first base URL.\n *\n * @param specification The OpenAPI specification.\n * @returns The first base URL.\n * @example getBaseUrl(specification) // 'https://api.example.com/v1'\n */\nexport function getServerUrl<T>(specification: T): ServerUrl<T> {\n\n // --- Ensure the specification is an object.\n if (\n !specification\n || typeof specification !== 'object'\n || specification === null)\n throw new Error('Invalid OpenAPI specification.')\n\n // --- Handle OpenAPI 3.0 specifications.\n if (\n 'servers' in specification\n && Array.isArray(specification.servers)\n && specification.servers.length > 0\n && 'url' in specification.servers[0]\n && typeof specification.servers[0].url === 'string'\n && specification.servers[0].url.length > 0)\n return specification.servers[0].url as ServerUrl<T>\n\n // --- Handle OpenAPI 2.0 specifications.\n if (\n 'host' in specification\n && typeof specification.host === 'string') {\n\n const scheme = (\n 'schemes' in specification\n && Array.isArray(specification.schemes)\n && specification.schemes.length > 0\n && typeof specification.schemes[0] === 'string')\n ? specification.schemes[0]\n : 'https'\n\n const basePath = (\n 'basePath' in specification\n && typeof specification.basePath === 'string'\n && specification.basePath.length > 0)\n ? specification.basePath\n : '/'\n\n return `${scheme}://${specification.host}${basePath}` as ServerUrl<T>\n }\n\n throw new Error('No base URL found in the OpenAPI specification.')\n}\n","import type { CollectKey, Pretty } from '@unshared/types'\nimport type { OpenAPI } from 'openapi-types'\nimport type { FetchMethod } from '../utils/parseRequest'\n\n/** The HTTP methods supported by OpenAPI. */\nconst methods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch'] as const\n\n/** Union of all operation IDs in the specification. */\nexport type OperationId<T> =\nT extends { paths: infer P }\n ? P extends Record<string, infer R>\n ? R extends Record<string, infer O>\n ? O extends { operationId: infer N }\n ? N\n : string\n : string\n : string\n : string\n\n/** A union of possible Operations types in the specification. */\nexport type Operation = OpenAPI.Operation & { method: FetchMethod; path: string }\n\n/** Find an operation by its operationId in an OpenAPI specification. */\nexport type OperationById<T, U extends OperationId<T>> =\n T extends { paths: infer P }\n ? CollectKey<P> extends Record<string, infer R>\n ? CollectKey<R> extends Record<string, infer O>\n ? O extends { $key: [infer P extends string, infer M extends string]; operationId: U }\n ? Pretty<Omit<O, '$key'> & { method: M; path: P }>\n : never\n : never\n : never\n : never\n\n/**\n * Given an OpenAPI specification, find an operation by its operationId.\n *\n * @param document The OpenAPI specification document.\n * @param operationId The operationId of the operation to resolve.\n * @returns The resolved operation.\n * @example resolveOperation(document, 'getUser') // { method: 'get', path: '/users/{username}', ... }\n */\nexport function resolveOperation<T, U extends OperationId<T>>(document: T, operationId: U): OperationById<T, U> {\n\n // --- Validate the specification.\n if (!document\n || typeof document !== 'object'\n || document === null\n || 'paths' in document === false\n || typeof document.paths !== 'object'\n || document.paths === null)\n throw new Error('Missing paths object in the OpenAPI specification.')\n\n // --- Search for the operation in the specification's paths.\n const paths = document.paths as OpenAPI.Document['paths']\n for (const path in paths) {\n const route = paths[path]\n if (typeof route !== 'object' || route === null) continue\n\n // --- Search in each method for the operation.\n for (const method of methods) {\n const operation = route[method]\n if (method in route === false\n || typeof operation !== 'object'\n || operation === null\n || 'operationId' in operation === false\n || operation.operationId !== operationId) continue\n\n // --- Route was found, return the operation.\n return { ...route[method], method, path } as OperationById<T, U>\n }\n }\n\n // --- Throw an error if the operation was not found.\n throw new Error(`Operation \"${operationId}\" not found in specification.`)\n}\n"],"names":[],"mappings":"AAwBO,SAAS,aAAgB,eAAgC;AAG9D,MACE,CAAC,iBACE,OAAO,iBAAkB,YACzB,kBAAkB;AACf,UAAA,IAAI,MAAM,gCAAgC;AAGlD,MACE,aAAa,iBACV,MAAM,QAAQ,cAAc,OAAO,KACnC,cAAc,QAAQ,SAAS,KAC/B,SAAS,cAAc,QAAQ,CAAC,KAChC,OAAO,cAAc,QAAQ,CAAC,EAAE,OAAQ,YACxC,cAAc,QAAQ,CAAC,EAAE,IAAI,SAAS;AAClC,WAAA,cAAc,QAAQ,CAAC,EAAE;AAGlC,MACE,UAAU,iBACP,OAAO,cAAc,QAAS,UAAU;AAE3C,UAAM,SACJ,aAAa,iBACV,MAAM,QAAQ,cAAc,OAAO,KACnC,cAAc,QAAQ,SAAS,KAC/B,OAAO,cAAc,QAAQ,CAAC,KAAM,WACrC,cAAc,QAAQ,CAAC,IACvB,SAEE,WACJ,cAAc,iBACX,OAAO,cAAc,YAAa,YAClC,cAAc,SAAS,SAAS,IACjC,cAAc,WACd;AAEJ,WAAO,GAAG,MAAM,MAAM,cAAc,IAAI,GAAG,QAAQ;AAAA,EAAA;AAG/C,QAAA,IAAI,MAAM,iDAAiD;AACnE;AC9DA,MAAM,UAAU,CAAC,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,OAAO;AAqC3D,SAAA,iBAA8C,UAAa,aAAqC;AAG9G,MAAI,CAAC,YACA,OAAO,YAAa,YACpB,aAAa,QACb,EAAW,WAAA,aACX,OAAO,SAAS,SAAU,YAC1B,SAAS,UAAU;AAChB,UAAA,IAAI,MAAM,oDAAoD;AAGtE,QAAM,QAAQ,SAAS;AACvB,aAAW,QAAQ,OAAO;AAClB,UAAA,QAAQ,MAAM,IAAI;AACpB,QAAA,EAAA,OAAO,SAAU,YAAY,UAAU;AAG3C,iBAAW,UAAU,SAAS;AACtB,cAAA,YAAY,MAAM,MAAM;AAC1B,YAAA,EAAA,EAAA,UAAU,UACT,OAAO,aAAc,YACrB,cAAc,QACd,EAAA,iBAAiB,cACjB,UAAU,gBAAgB;AAG/B,iBAAO,EAAE,GAAG,MAAM,MAAM,GAAG,QAAQ,KAAK;AAAA,MAAA;AAAA,EAC1C;AAIF,QAAM,IAAI,MAAM,cAAc,WAAW,+BAA+B;AAC1E;"}
@@ -1 +1 @@
1
- {"version":3,"file":"CtW2aMuA.cjs","sources":["../../openapi/getServerUrl.ts","../../openapi/resolveOperation.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/** Get the base URL of an OpenAPI specification. */\nexport type ServerUrl<T> =\n\n // --- Handle OpenAPI 2.0 specifications.\n T extends {\n host: infer Host extends string\n basePath?: infer BasePath extends string\n schemes?: Array<infer Scheme extends string>\n }\n ? `${Scheme}://${Host}${BasePath}`\n\n // --- Handle OpenAPI 3.0 specifications.\n : T extends { servers: Array<{ url: infer U extends string }> }\n ? U\n : string\n\n/**\n * Given an OpenAPI specification, get the first base URL.\n *\n * @param specification The OpenAPI specification.\n * @returns The first base URL.\n * @example getBaseUrl(specification) // 'https://api.example.com/v1'\n */\nexport function getServerUrl<T>(specification: T): ServerUrl<T> {\n\n // --- Ensure the specification is an object.\n if (\n !specification\n || typeof specification !== 'object'\n || specification === null)\n throw new Error('Invalid OpenAPI specification.')\n\n // --- Handle OpenAPI 3.0 specifications.\n if (\n 'servers' in specification\n && Array.isArray(specification.servers)\n && specification.servers.length > 0\n && 'url' in specification.servers[0]\n && typeof specification.servers[0].url === 'string'\n && specification.servers[0].url.length > 0)\n return specification.servers[0].url as ServerUrl<T>\n\n // --- Handle OpenAPI 2.0 specifications.\n if (\n 'host' in specification\n && typeof specification.host === 'string') {\n\n const scheme = (\n 'schemes' in specification\n && Array.isArray(specification.schemes)\n && specification.schemes.length > 0\n && typeof specification.schemes[0] === 'string')\n ? specification.schemes[0]\n : 'https'\n\n const basePath = (\n 'basePath' in specification\n && typeof specification.basePath === 'string'\n && specification.basePath.length > 0)\n ? specification.basePath\n : '/'\n\n return `${scheme}://${specification.host}${basePath}` as ServerUrl<T>\n }\n\n throw new Error('No base URL found in the OpenAPI specification.')\n}\n","import type { CollectKey, Pretty } from '@unshared/types'\nimport type { OpenAPI } from 'openapi-types'\nimport type { FetchMethod } from '../utils/parseRequest'\n\n/** The HTTP methods supported by OpenAPI. */\nconst methods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch'] as const\n\n/** Union of all operation IDs in the specification. */\nexport type OperationId<T> =\nT extends { paths: infer P }\n ? P extends Record<string, infer R>\n ? R extends Record<string, infer O>\n ? O extends { operationId: infer N }\n ? N\n : string\n : string\n : string\n : string\n\n/** A union of possible Operations types in the specification. */\nexport type Operation = { method: FetchMethod; path: string } & OpenAPI.Operation\n\n/** Find an operation by its operationId in an OpenAPI specification. */\nexport type OperationById<T, U extends OperationId<T>> =\n T extends { paths: infer P }\n ? CollectKey<P> extends Record<string, infer R>\n ? CollectKey<R> extends Record<string, infer O>\n ? O extends { $key: [infer P extends string, infer M extends string]; operationId: U }\n ? Pretty<{ method: M; path: P } & Omit<O, '$key'>>\n : never\n : never\n : never\n : never\n\n/**\n * Given an OpenAPI specification, find an operation by its operationId.\n *\n * @param document The OpenAPI specification document.\n * @param operationId The operationId of the operation to resolve.\n * @returns The resolved operation.\n * @example resolveOperation(document, 'getUser') // { method: 'get', path: '/users/{username}', ... }\n */\nexport function resolveOperation<T, U extends OperationId<T>>(document: T, operationId: U): OperationById<T, U> {\n\n // --- Validate the specification.\n if (!document\n || typeof document !== 'object'\n || document === null\n || 'paths' in document === false\n || typeof document.paths !== 'object'\n || document.paths === null)\n throw new Error('Missing paths object in the OpenAPI specification.')\n\n // --- Search for the operation in the specification's paths.\n const paths = document.paths as OpenAPI.Document['paths']\n for (const path in paths) {\n const route = paths[path]\n if (typeof route !== 'object' || route === null) continue\n\n // --- Search in each method for the operation.\n for (const method of methods) {\n const operation = route[method]\n if (method in route === false\n || typeof operation !== 'object'\n || operation === null\n || 'operationId' in operation === false\n || operation.operationId !== operationId) continue\n\n // --- Route was found, return the operation.\n return { ...route[method], method, path } as OperationById<T, U>\n }\n }\n\n // --- Throw an error if the operation was not found.\n throw new Error(`Operation \"${operationId}\" not found in specification.`)\n}\n"],"names":[],"mappings":";AAwBO,SAAS,aAAgB,eAAgC;AAG9D,MACE,CAAC,iBACE,OAAO,iBAAkB,YACzB,kBAAkB;AACf,UAAA,IAAI,MAAM,gCAAgC;AAGlD,MACE,aAAa,iBACV,MAAM,QAAQ,cAAc,OAAO,KACnC,cAAc,QAAQ,SAAS,KAC/B,SAAS,cAAc,QAAQ,CAAC,KAChC,OAAO,cAAc,QAAQ,CAAC,EAAE,OAAQ,YACxC,cAAc,QAAQ,CAAC,EAAE,IAAI,SAAS;AAClC,WAAA,cAAc,QAAQ,CAAC,EAAE;AAGlC,MACE,UAAU,iBACP,OAAO,cAAc,QAAS,UAAU;AAE3C,UAAM,SACJ,aAAa,iBACV,MAAM,QAAQ,cAAc,OAAO,KACnC,cAAc,QAAQ,SAAS,KAC/B,OAAO,cAAc,QAAQ,CAAC,KAAM,WACrC,cAAc,QAAQ,CAAC,IACvB,SAEE,WACJ,cAAc,iBACX,OAAO,cAAc,YAAa,YAClC,cAAc,SAAS,SAAS,IACjC,cAAc,WACd;AAEJ,WAAO,GAAG,MAAM,MAAM,cAAc,IAAI,GAAG,QAAQ;AAAA,EAAA;AAG/C,QAAA,IAAI,MAAM,iDAAiD;AACnE;AC9DA,MAAM,UAAU,CAAC,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,OAAO;AAqC3D,SAAA,iBAA8C,UAAa,aAAqC;AAG9G,MAAI,CAAC,YACA,OAAO,YAAa,YACpB,aAAa,QACb,EAAW,WAAA,aACX,OAAO,SAAS,SAAU,YAC1B,SAAS,UAAU;AAChB,UAAA,IAAI,MAAM,oDAAoD;AAGtE,QAAM,QAAQ,SAAS;AACvB,aAAW,QAAQ,OAAO;AAClB,UAAA,QAAQ,MAAM,IAAI;AACpB,QAAA,EAAA,OAAO,SAAU,YAAY,UAAU;AAG3C,iBAAW,UAAU,SAAS;AACtB,cAAA,YAAY,MAAM,MAAM;AAC1B,YAAA,EAAA,EAAA,UAAU,UACT,OAAO,aAAc,YACrB,cAAc,QACd,EAAA,iBAAiB,cACjB,UAAU,gBAAgB;AAG/B,iBAAO,EAAE,GAAG,MAAM,MAAM,GAAG,QAAQ,KAAK;AAAA,MAAA;AAAA,EAC1C;AAIF,QAAM,IAAI,MAAM,cAAc,WAAW,+BAA+B;AAC1E;;;"}
1
+ {"version":3,"file":"CtW2aMuA.cjs","sources":["../../openapi/getServerUrl.ts","../../openapi/resolveOperation.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/** Get the base URL of an OpenAPI specification. */\nexport type ServerUrl<T> =\n\n // --- Handle OpenAPI 2.0 specifications.\n T extends {\n host: infer Host extends string\n basePath?: infer BasePath extends string\n schemes?: Array<infer Scheme extends string>\n }\n ? `${Scheme}://${Host}${BasePath}`\n\n // --- Handle OpenAPI 3.0 specifications.\n : T extends { servers: Array<{ url: infer U extends string }> }\n ? U\n : string\n\n/**\n * Given an OpenAPI specification, get the first base URL.\n *\n * @param specification The OpenAPI specification.\n * @returns The first base URL.\n * @example getBaseUrl(specification) // 'https://api.example.com/v1'\n */\nexport function getServerUrl<T>(specification: T): ServerUrl<T> {\n\n // --- Ensure the specification is an object.\n if (\n !specification\n || typeof specification !== 'object'\n || specification === null)\n throw new Error('Invalid OpenAPI specification.')\n\n // --- Handle OpenAPI 3.0 specifications.\n if (\n 'servers' in specification\n && Array.isArray(specification.servers)\n && specification.servers.length > 0\n && 'url' in specification.servers[0]\n && typeof specification.servers[0].url === 'string'\n && specification.servers[0].url.length > 0)\n return specification.servers[0].url as ServerUrl<T>\n\n // --- Handle OpenAPI 2.0 specifications.\n if (\n 'host' in specification\n && typeof specification.host === 'string') {\n\n const scheme = (\n 'schemes' in specification\n && Array.isArray(specification.schemes)\n && specification.schemes.length > 0\n && typeof specification.schemes[0] === 'string')\n ? specification.schemes[0]\n : 'https'\n\n const basePath = (\n 'basePath' in specification\n && typeof specification.basePath === 'string'\n && specification.basePath.length > 0)\n ? specification.basePath\n : '/'\n\n return `${scheme}://${specification.host}${basePath}` as ServerUrl<T>\n }\n\n throw new Error('No base URL found in the OpenAPI specification.')\n}\n","import type { CollectKey, Pretty } from '@unshared/types'\nimport type { OpenAPI } from 'openapi-types'\nimport type { FetchMethod } from '../utils/parseRequest'\n\n/** The HTTP methods supported by OpenAPI. */\nconst methods = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch'] as const\n\n/** Union of all operation IDs in the specification. */\nexport type OperationId<T> =\nT extends { paths: infer P }\n ? P extends Record<string, infer R>\n ? R extends Record<string, infer O>\n ? O extends { operationId: infer N }\n ? N\n : string\n : string\n : string\n : string\n\n/** A union of possible Operations types in the specification. */\nexport type Operation = OpenAPI.Operation & { method: FetchMethod; path: string }\n\n/** Find an operation by its operationId in an OpenAPI specification. */\nexport type OperationById<T, U extends OperationId<T>> =\n T extends { paths: infer P }\n ? CollectKey<P> extends Record<string, infer R>\n ? CollectKey<R> extends Record<string, infer O>\n ? O extends { $key: [infer P extends string, infer M extends string]; operationId: U }\n ? Pretty<Omit<O, '$key'> & { method: M; path: P }>\n : never\n : never\n : never\n : never\n\n/**\n * Given an OpenAPI specification, find an operation by its operationId.\n *\n * @param document The OpenAPI specification document.\n * @param operationId The operationId of the operation to resolve.\n * @returns The resolved operation.\n * @example resolveOperation(document, 'getUser') // { method: 'get', path: '/users/{username}', ... }\n */\nexport function resolveOperation<T, U extends OperationId<T>>(document: T, operationId: U): OperationById<T, U> {\n\n // --- Validate the specification.\n if (!document\n || typeof document !== 'object'\n || document === null\n || 'paths' in document === false\n || typeof document.paths !== 'object'\n || document.paths === null)\n throw new Error('Missing paths object in the OpenAPI specification.')\n\n // --- Search for the operation in the specification's paths.\n const paths = document.paths as OpenAPI.Document['paths']\n for (const path in paths) {\n const route = paths[path]\n if (typeof route !== 'object' || route === null) continue\n\n // --- Search in each method for the operation.\n for (const method of methods) {\n const operation = route[method]\n if (method in route === false\n || typeof operation !== 'object'\n || operation === null\n || 'operationId' in operation === false\n || operation.operationId !== operationId) continue\n\n // --- Route was found, return the operation.\n return { ...route[method], method, path } as OperationById<T, U>\n }\n }\n\n // --- Throw an error if the operation was not found.\n throw new Error(`Operation \"${operationId}\" not found in specification.`)\n}\n"],"names":[],"mappings":";AAwBO,SAAS,aAAgB,eAAgC;AAG9D,MACE,CAAC,iBACE,OAAO,iBAAkB,YACzB,kBAAkB;AACf,UAAA,IAAI,MAAM,gCAAgC;AAGlD,MACE,aAAa,iBACV,MAAM,QAAQ,cAAc,OAAO,KACnC,cAAc,QAAQ,SAAS,KAC/B,SAAS,cAAc,QAAQ,CAAC,KAChC,OAAO,cAAc,QAAQ,CAAC,EAAE,OAAQ,YACxC,cAAc,QAAQ,CAAC,EAAE,IAAI,SAAS;AAClC,WAAA,cAAc,QAAQ,CAAC,EAAE;AAGlC,MACE,UAAU,iBACP,OAAO,cAAc,QAAS,UAAU;AAE3C,UAAM,SACJ,aAAa,iBACV,MAAM,QAAQ,cAAc,OAAO,KACnC,cAAc,QAAQ,SAAS,KAC/B,OAAO,cAAc,QAAQ,CAAC,KAAM,WACrC,cAAc,QAAQ,CAAC,IACvB,SAEE,WACJ,cAAc,iBACX,OAAO,cAAc,YAAa,YAClC,cAAc,SAAS,SAAS,IACjC,cAAc,WACd;AAEJ,WAAO,GAAG,MAAM,MAAM,cAAc,IAAI,GAAG,QAAQ;AAAA,EAAA;AAG/C,QAAA,IAAI,MAAM,iDAAiD;AACnE;AC9DA,MAAM,UAAU,CAAC,OAAO,OAAO,QAAQ,UAAU,WAAW,QAAQ,OAAO;AAqC3D,SAAA,iBAA8C,UAAa,aAAqC;AAG9G,MAAI,CAAC,YACA,OAAO,YAAa,YACpB,aAAa,QACb,EAAW,WAAA,aACX,OAAO,SAAS,SAAU,YAC1B,SAAS,UAAU;AAChB,UAAA,IAAI,MAAM,oDAAoD;AAGtE,QAAM,QAAQ,SAAS;AACvB,aAAW,QAAQ,OAAO;AAClB,UAAA,QAAQ,MAAM,IAAI;AACpB,QAAA,EAAA,OAAO,SAAU,YAAY,UAAU;AAG3C,iBAAW,UAAU,SAAS;AACtB,cAAA,YAAY,MAAM,MAAM;AAC1B,YAAA,EAAA,EAAA,UAAU,UACT,OAAO,aAAc,YACrB,cAAc,QACd,EAAA,iBAAiB,cACjB,UAAU,gBAAgB;AAG/B,iBAAO,EAAE,GAAG,MAAM,MAAM,GAAG,QAAQ,KAAK;AAAA,MAAA;AAAA,EAC1C;AAIF,QAAM,IAAI,MAAM,cAAc,WAAW,+BAA+B;AAC1E;;;"}
@@ -166,20 +166,20 @@ type OperationId<T> = T extends {
166
166
  operationId: infer N;
167
167
  } ? N : string : string : string : string;
168
168
  /** A union of possible Operations types in the specification. */
169
- type Operation = {
169
+ type Operation = OpenAPI.Operation & {
170
170
  method: FetchMethod;
171
171
  path: string;
172
- } & OpenAPI.Operation;
172
+ };
173
173
  /** Find an operation by its operationId in an OpenAPI specification. */
174
174
  type OperationById<T, U extends OperationId<T>> = T extends {
175
175
  paths: infer P;
176
176
  } ? CollectKey<P> extends Record<string, infer R> ? CollectKey<R> extends Record<string, infer O> ? O extends {
177
177
  $key: [infer P extends string, infer M extends string];
178
178
  operationId: U;
179
- } ? Pretty<{
179
+ } ? Pretty<Omit<O, '$key'> & {
180
180
  method: M;
181
181
  path: P;
182
- } & Omit<O, '$key'>> : never : never : never : never;
182
+ }> : never : never : never : never;
183
183
  /**
184
184
  * Given an OpenAPI specification, find an operation by its operationId.
185
185
  *
@@ -212,10 +212,10 @@ type OperationRoute<T> = T extends {
212
212
  /** Find an operation by its route name in an OpenAPI specification. */
213
213
  type OperationByRoute<T, U extends OperationRoute<T>> = StringSplit<U, ' '> extends [infer M extends string, infer P extends string] ? T extends {
214
214
  paths: infer U;
215
- } ? U extends Record<P, infer R> ? R extends Record<Lowercase<M>, infer O> ? Pretty<{
215
+ } ? U extends Record<P, infer R> ? R extends Record<Lowercase<M>, infer O> ? Pretty<O & {
216
216
  method: Lowercase<M>;
217
217
  path: P;
218
- } & O> : never : never : never : never;
218
+ }> : never : never : never : never;
219
219
  /** The `ClientRoutes` inferred from the OpenAPI specification. */
220
220
  type OpenAPIOptionsMap<T> = {
221
221
  [K in OperationRoute<T>]: OperationOptions<T, OperationByRoute<T, K>>;
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
- var attempt = require("@unshared/functions/attempt"), handleResponse = require("./chunks/Cayg8606.cjs"), request = require("./chunks/Du_W5H6e.cjs"), connect = require("./chunks/VkINJoq7.cjs");
2
+ var attempt = require("@unshared/functions/attempt"), handleResponse = require("./chunks/Cayg8606.cjs"), request = require("./chunks/Du_W5H6e.cjs"), connect = require("./chunks/BMbsYNBd.cjs");
3
3
  require("./chunks/CYmaYL5B.cjs");
4
4
  require("@unshared/functions/awaitable");
5
+ require("@unshared/functions");
5
6
  class Client {
6
7
  /**
7
8
  * Create a new client for the application.
@@ -1 +1 @@
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 { 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, V extends T[K]>(route: K, options?: V): 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, V extends T[K]>(route: K, options?: V): Promise<Data<V>> {\n return request(route, { ...this.options, ...options }) as Promise<Data<V>>\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, V extends T[K]>(route: K, options?: V): Promise<Result<Data<V>>> {\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, V extends U[P]>(channel: P, options?: V): WebSocketChannel<V> {\n return connect(channel, { baseUrl: this.options.baseUrl, ...options }) as WebSocketChannel<V>\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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWZ,MAAkD,OAAU,SAAgC;AAC1F,WAAAA,eAAAA,MAAM,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqB9C,QAAoD,OAAU,SAA+B;AAC3F,WAAAC,QAAAA,QAAQ,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBhD,eAA2D,OAAU,SAAuC;AACjH,WAAOC,QAAAA,QAAQ,MAAM,KAAK,QAAQ,OAAO,OAAO,CAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW5C,QAAoD,SAAY,SAAkC;AAChG,WAAAC,QAAA,QAAQ,SAAS,EAAE,SAAS,KAAK,QAAQ,SAAS,GAAG,SAAS;AAAA,EAAA;AAEzE;AAqCO,SAAS,aAAa,SAAkC;AACtD,SAAA,IAAI,OAAO,OAAO;AAC3B;;;"}
1
+ {"version":3,"file":"createClient.cjs","sources":["../createClient.ts"],"sourcesContent":["import type { Awaitable } from '@unshared/functions'\nimport type { Result } from '@unshared/functions/attempt'\nimport type { ServiceOptions } from './createService'\nimport type { OpenAPILike, OpenAPIOptionsMap } from './openapi'\nimport type { 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, V extends T[K]>(route: K, options?: V): 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, V extends T[K]>(route: K, options?: V): Promise<Data<V>> {\n return request(route, { ...this.options, ...options }) as Promise<Data<V>>\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, V extends T[K]>(route: K, options?: V): Promise<Result<Data<V>>> {\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, V extends U[P]>(channel: P, options?: V): Awaitable<WebSocketChannel<V>, WebSocketChannel<V>> {\n return connect(channel, { baseUrl: this.options.baseUrl, ...options }) as Awaitable<WebSocketChannel<V>, WebSocketChannel<V>>\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":";;;;;AAeO,MAAM,OAAiE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5E,YAAmB,UAA0B,IAAI;AAA9B,SAAA,UAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWZ,MAAkD,OAAU,SAAgC;AAC1F,WAAAA,eAAAA,MAAM,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqB9C,QAAoD,OAAU,SAA+B;AAC3F,WAAAC,QAAAA,QAAQ,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBhD,eAA2D,OAAU,SAAuC;AACjH,WAAOC,QAAAA,QAAQ,MAAM,KAAK,QAAQ,OAAO,OAAO,CAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW5C,QAAoD,SAAY,SAAkE;AAChI,WAAAC,QAAA,QAAQ,SAAS,EAAE,SAAS,KAAK,QAAQ,SAAS,GAAG,SAAS;AAAA,EAAA;AAEzE;AAqCO,SAAS,aAAa,SAAkC;AACtD,SAAA,IAAI,OAAO,OAAO;AAC3B;;;"}
@@ -1,6 +1,7 @@
1
+ import { Awaitable } from '@unshared/functions';
1
2
  import { Result } from '@unshared/functions/attempt';
2
3
  import { ServiceOptions } from './createService.js';
3
- import { O as OpenAPILike, a as OpenAPIOptionsMap } from './chunks/DOZHjge0.js';
4
+ import { O as OpenAPILike, a as OpenAPIOptionsMap } from './chunks/mHfUAX_g.js';
4
5
  import { R as RequestOptions } from './chunks/CO11DuYE.js';
5
6
  import { WebSocketChannel, ConnectOptions } from './websocket.js';
6
7
  import '@unshared/types';
@@ -78,7 +79,7 @@ declare class Client<T extends Routes = Routes, U extends Channels = Channels> {
78
79
  * @param options The options to pass to the connection.
79
80
  * @returns The WebSocket connection.
80
81
  */
81
- connect<P extends keyof U & string, V extends U[P]>(channel: P, options?: V): WebSocketChannel<V>;
82
+ connect<P extends keyof U & string, V extends U[P]>(channel: P, options?: V): Awaitable<WebSocketChannel<V>, WebSocketChannel<V>>;
82
83
  }
83
84
  /**
84
85
  * Create a new type-safe client for the application. The client can be used to fetch data from
@@ -1,9 +1,10 @@
1
1
  import { attempt } from "@unshared/functions/attempt";
2
2
  import { f as fetch } from "./chunks/BUeqbyph.js";
3
3
  import { r as request } from "./chunks/DJJsADWD.js";
4
- import { c as connect } from "./chunks/Du56lBvc.js";
4
+ import { c as connect } from "./chunks/6IxvRpmY.js";
5
5
  import "./chunks/0ZzUT3m_.js";
6
6
  import "@unshared/functions/awaitable";
7
+ import "@unshared/functions";
7
8
  class Client {
8
9
  /**
9
10
  * Create a new client for the application.
@@ -1 +1 @@
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 { 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, V extends T[K]>(route: K, options?: V): 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, V extends T[K]>(route: K, options?: V): Promise<Data<V>> {\n return request(route, { ...this.options, ...options }) as Promise<Data<V>>\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, V extends T[K]>(route: K, options?: V): Promise<Result<Data<V>>> {\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, V extends U[P]>(channel: P, options?: V): WebSocketChannel<V> {\n return connect(channel, { baseUrl: this.options.baseUrl, ...options }) as WebSocketChannel<V>\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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWZ,MAAkD,OAAU,SAAgC;AAC1F,WAAA,MAAM,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqB9C,QAAoD,OAAU,SAA+B;AAC3F,WAAA,QAAQ,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBhD,eAA2D,OAAU,SAAuC;AACjH,WAAO,QAAQ,MAAM,KAAK,QAAQ,OAAO,OAAO,CAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW5C,QAAoD,SAAY,SAAkC;AAChG,WAAA,QAAQ,SAAS,EAAE,SAAS,KAAK,QAAQ,SAAS,GAAG,SAAS;AAAA,EAAA;AAEzE;AAqCO,SAAS,aAAa,SAAkC;AACtD,SAAA,IAAI,OAAO,OAAO;AAC3B;"}
1
+ {"version":3,"file":"createClient.js","sources":["../createClient.ts"],"sourcesContent":["import type { Awaitable } from '@unshared/functions'\nimport type { Result } from '@unshared/functions/attempt'\nimport type { ServiceOptions } from './createService'\nimport type { OpenAPILike, OpenAPIOptionsMap } from './openapi'\nimport type { 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, V extends T[K]>(route: K, options?: V): 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, V extends T[K]>(route: K, options?: V): Promise<Data<V>> {\n return request(route, { ...this.options, ...options }) as Promise<Data<V>>\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, V extends T[K]>(route: K, options?: V): Promise<Result<Data<V>>> {\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, V extends U[P]>(channel: P, options?: V): Awaitable<WebSocketChannel<V>, WebSocketChannel<V>> {\n return connect(channel, { baseUrl: this.options.baseUrl, ...options }) as Awaitable<WebSocketChannel<V>, WebSocketChannel<V>>\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":";;;;;;;AAeO,MAAM,OAAiE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5E,YAAmB,UAA0B,IAAI;AAA9B,SAAA,UAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWZ,MAAkD,OAAU,SAAgC;AAC1F,WAAA,MAAM,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqB9C,QAAoD,OAAU,SAA+B;AAC3F,WAAA,QAAQ,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBhD,eAA2D,OAAU,SAAuC;AACjH,WAAO,QAAQ,MAAM,KAAK,QAAQ,OAAO,OAAO,CAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAW5C,QAAoD,SAAY,SAAkE;AAChI,WAAA,QAAQ,SAAS,EAAE,SAAS,KAAK,QAAQ,SAAS,GAAG,SAAS;AAAA,EAAA;AAEzE;AAqCO,SAAS,aAAa,SAAkC;AACtD,SAAA,IAAI,OAAO,OAAO;AAC3B;"}
@@ -1,5 +1,5 @@
1
1
  import { MaybeLiteral } from '@unshared/types';
2
- import { b as OperationId, c as OperationById, d as OperationOptions, e as OperationResult, S as ServerUrl, f as OpenAPIV3 } from './chunks/DOZHjge0.js';
2
+ import { b as OperationId, c as OperationById, d as OperationOptions, e as OperationResult, S as ServerUrl, f as OpenAPIV3 } from './chunks/mHfUAX_g.js';
3
3
  import { R as RequestOptions } from './chunks/CO11DuYE.js';
4
4
  import 'openapi-types';
5
5
  import './HttpHeaders.js';
package/dist/index.cjs CHANGED
@@ -5,7 +5,8 @@ require("./chunks/Cayg8606.cjs");
5
5
  require("./chunks/CYmaYL5B.cjs");
6
6
  require("@unshared/functions/awaitable");
7
7
  require("./chunks/Du_W5H6e.cjs");
8
- require("./chunks/VkINJoq7.cjs");
8
+ require("./chunks/BMbsYNBd.cjs");
9
+ require("@unshared/functions");
9
10
  require("./chunks/CtW2aMuA.cjs");
10
11
  exports.Client = createClient.Client;
11
12
  exports.createClient = createClient.createClient;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -3,8 +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';
6
7
  import '@unshared/functions/attempt';
7
- import './chunks/DOZHjge0.js';
8
+ import './chunks/mHfUAX_g.js';
8
9
  import '@unshared/types';
9
10
  import './chunks/CO11DuYE.js';
10
11
  import 'openapi-types';
package/dist/index.js CHANGED
@@ -8,7 +8,8 @@ import "./chunks/BUeqbyph.js";
8
8
  import "./chunks/0ZzUT3m_.js";
9
9
  import "@unshared/functions/awaitable";
10
10
  import "./chunks/DJJsADWD.js";
11
- import "./chunks/Du56lBvc.js";
11
+ import "./chunks/6IxvRpmY.js";
12
+ import "@unshared/functions";
12
13
  import "./chunks/Biic1J5b.js";
13
14
  export {
14
15
  Client,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}
package/dist/openapi.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { O as OpenAPILike, a as OpenAPIOptionsMap, h as OpenAPIV2, j as OpenAPIV2Like, f as OpenAPIV3, k as OpenAPIV3Like, i as Operation, c as OperationById, n as OperationByRoute, b as OperationId, d as OperationOptions, l as OperationResponse, e as OperationResult, m as OperationRoute, S as ServerUrl, g as getServerUrl, r as resolveOperation } from './chunks/DOZHjge0.js';
1
+ export { O as OpenAPILike, a as OpenAPIOptionsMap, h as OpenAPIV2, j as OpenAPIV2Like, f as OpenAPIV3, k as OpenAPIV3Like, i as Operation, c as OperationById, n as OperationByRoute, b as OperationId, d as OperationOptions, l as OperationResponse, e as OperationResult, m as OperationRoute, S as ServerUrl, g as getServerUrl, r as resolveOperation } from './chunks/mHfUAX_g.js';
2
2
  import { OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
3
3
  import { StringReplace, WriteableDeep, StringJoin, Substract } from '@unshared/types';
4
4
  import './chunks/CO11DuYE.js';
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
- var connect = require("./chunks/VkINJoq7.cjs");
2
+ var connect = require("./chunks/BMbsYNBd.cjs");
3
+ require("@unshared/functions");
3
4
  require("./chunks/CYmaYL5B.cjs");
4
5
  exports.WebSocketChannel = connect.WebSocketChannel;
5
6
  exports.connect = connect.connect;
@@ -1 +1 @@
1
- {"version":3,"file":"websocket.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
1
+ {"version":3,"file":"websocket.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
@@ -1,9 +1,10 @@
1
+ import { Awaitable } from '@unshared/functions';
1
2
  import { ObjectLike, Loose, UnionMerge } from '@unshared/types';
2
3
 
3
4
  /** The protocols to use for the connection. */
4
5
  type ConnectProtocol = 'WS' | 'WSS';
5
6
  /** Options to pass to the `createChannel` function. */
6
- interface ConnectOptions<BaseUrl extends string = string, Query extends ObjectLike = ObjectLike, Parameters extends ObjectLike = ObjectLike, ClientData extends ObjectLike = ObjectLike, ServerData extends ObjectLike = ObjectLike> {
7
+ interface ConnectOptions<BaseUrl extends string = string, Query extends ObjectLike = ObjectLike, Parameters extends ObjectLike = ObjectLike, ClientData extends ObjectLike = any, ServerData extends ObjectLike = any> {
7
8
  /** The protocol to use when connecting to the server. */
8
9
  protocol?: Lowercase<ConnectProtocol> | Uppercase<ConnectProtocol>;
9
10
  /** The base URL to connect to. */
@@ -40,14 +41,6 @@ interface ConnectOptions<BaseUrl extends string = string, Query extends ObjectLi
40
41
  * to send to the server when the connection is established.
41
42
  */
42
43
  initialPayload?: Loose<ClientData>;
43
- /**
44
- * Automatically open the connection when it is created. If `true`, the connection
45
- * will automatically open when it is created. If `false`, the connection will not
46
- * open when it is created.
47
- *
48
- * @default false
49
- */
50
- autoOpen?: boolean;
51
44
  /**
52
45
  * Weather to reconnect the connection when it is closed unexpectedly. If `true`,
53
46
  * the connection will automatically reconnect when it is closed. If `false`, the
@@ -98,20 +91,22 @@ interface WebSocketParameters {
98
91
  declare function parseConnectOptions(channel: string, options: ConnectOptions): WebSocketParameters;
99
92
 
100
93
  type RemoveListener = () => void;
101
- type ClientData<T extends ConnectOptions> = T extends ConnectOptions<any, any, any, infer R extends ObjectLike, any> ? R : ObjectLike;
102
- type ServerData<T extends ConnectOptions> = T extends ConnectOptions<any, any, any, any, infer R extends ObjectLike> ? R : ObjectLike;
94
+ type ClientData<T extends ConnectOptions> = T extends ConnectOptions<any, any, any, infer R, any> ? R : any;
95
+ type ServerData<T extends ConnectOptions> = T extends ConnectOptions<any, any, any, any, infer R> ? R : any;
103
96
  declare class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {
104
97
  channel: string;
105
98
  options: T;
106
99
  constructor(channel: string, options: T);
107
100
  /** The WebSocket connection to the server. */
108
- webSocket: WebSocket | undefined;
101
+ webSocket: undefined | WebSocket;
109
102
  /**
110
103
  * Open a new WebSocket connection to the server. The connection will be opened with the given
111
104
  * URL and protocols. If the connection is already open, the connection will be closed before
112
105
  * opening a new connection. Also add the event listeners that were passed in the options.
106
+ *
107
+ * @returns The WebSocket connection.
113
108
  */
114
- open(): Promise<void>;
109
+ open(): Promise<this>;
115
110
  /**
116
111
  * Send a payload to the server. The payload will be serialized to JSON before sending.
117
112
  *
@@ -125,10 +120,10 @@ declare class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {
125
120
  * @param callback The callback to call when the event is received.
126
121
  * @returns A function to remove the event listener.
127
122
  */
128
- on(event: 'message', callback: (data: ServerData<T>) => void): RemoveListener;
129
- on(event: 'close', callback: (event: CloseEvent) => void): RemoveListener;
130
- on(event: 'error', callback: (event: Event) => void): RemoveListener;
131
- on(event: 'open', callback: (event: Event) => void): RemoveListener;
123
+ on(event: 'message', callback: (data: ServerData<T>) => void, options?: AddEventListenerOptions): RemoveListener;
124
+ on(event: 'close', callback: (event: CloseEvent) => void, options?: AddEventListenerOptions): RemoveListener;
125
+ on(event: 'error', callback: (event: Event) => void, options?: AddEventListenerOptions): RemoveListener;
126
+ on(event: 'open', callback: (event: Event) => void, options?: AddEventListenerOptions): RemoveListener;
132
127
  /**
133
128
  * Close the WebSocket connection to the server. The connection will not be able to send or receive
134
129
  * messages after it is closed.
@@ -143,6 +138,6 @@ declare class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {
143
138
  * @param options The options to pass to the connection.
144
139
  * @returns The WebSocket connection.
145
140
  */
146
- declare function connect(route: string, options: ConnectOptions): WebSocketChannel;
141
+ declare function connect(route: string, options: ConnectOptions): Awaitable<WebSocketChannel, WebSocketChannel>;
147
142
 
148
143
  export { type ConnectOptions, type ConnectProtocol, WebSocketChannel, type WebSocketParameters, connect, parseConnectOptions };
package/dist/websocket.js CHANGED
@@ -1,4 +1,5 @@
1
- import { W, c, p } from "./chunks/Du56lBvc.js";
1
+ import { W, c, p } from "./chunks/6IxvRpmY.js";
2
+ import "@unshared/functions";
2
3
  import "./chunks/0ZzUT3m_.js";
3
4
  export {
4
5
  W as WebSocketChannel,
@@ -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.3.1",
4
+ "version": "0.3.2",
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.3.1",
72
- "@unshared/types": "0.3.1",
71
+ "@unshared/functions": "0.3.2",
72
+ "@unshared/types": "0.3.2",
73
73
  "openapi-types": "12.1.3"
74
74
  },
75
75
  "devDependencies": {
76
- "@unshared/scripts": "0.3.1"
76
+ "@unshared/scripts": "0.3.2"
77
77
  },
78
78
  "scripts": {
79
79
  "build:httpMethods": "tsx ./scripts/buildHttpMethods.ts",
@@ -1 +0,0 @@
1
- {"version":3,"file":"Du56lBvc.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 = ObjectLike,\n ServerData extends ObjectLike = ObjectLike,\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 * Automatically open the connection when it is created. If `true`, the connection\n * will automatically open when it is created. If `false`, the connection will not\n * open when it is created.\n *\n * @default false\n */\n autoOpen?: boolean\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 context: WebSocketParameters = { url: new URL('about:blank') }\n parseConnectUrl(context, channel, { baseUrl, protocol })\n parseRequestParameters(context, { parameters })\n parseRequestQuery(context, { query })\n return context\n}\n","import type { ObjectLike } from '@unshared/types'\nimport 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 extends ObjectLike, any> ? R : ObjectLike\n\ntype ServerData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, any, infer R extends ObjectLike> ? R : ObjectLike\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: WebSocket | undefined\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 async open() {\n if (this.webSocket) await this.close()\n const { url, protocol } = parseConnectOptions(this.channel, this.options)\n this.webSocket = new WebSocket(url, protocol)\n if (this.options.onOpen) this.on('open', this.options.onOpen)\n if (this.options.onClose) this.on('close', this.options.onClose)\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 // --- Reconnect to the server if 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 })\n\n // --- Return a promise that resolves when the connection is opened.\n return new Promise<void>((resolve, rejects) => {\n this.webSocket!.addEventListener('open', () => resolve())\n this.webSocket!.addEventListener('error', () => rejects(new Error('Failed to open the WebSocket connection')))\n })\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>) => void): RemoveListener\n on(event: 'close', callback: (event: CloseEvent) => void): RemoveListener\n on(event: 'error', callback: (event: Event) => void): RemoveListener\n on(event: 'open', callback: (event: Event) => void): RemoveListener\n on(event: string, callback: (data: any) => void) {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n\n const listener = (event: CloseEvent | Event | MessageEvent<any>) => {\n // @ts-expect-error: `data` exists on the event.\n let data = event.data as unknown\n try { data = JSON.parse(data as string) }\n catch { /* Ignore the error. */ }\n callback(data as T)\n }\n\n this.webSocket.addEventListener(event, listener)\n return () => this.webSocket!.removeEventListener(event, listener)\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":";AAKA,MAAM,yBAAyB,yEAGzB,YAAY,oBAAI,IAAI,CAAC,MAAM,KAAK,CAAC;AAwHvC,SAAS,gBAAgB,YAAiC,SAAiB,SAA+B;AAClG,QAAA,EAAE,SAAS,aAAa,SAGxB,QAAQ,uBAAuB,KAAK,OAAO;AACjD,MAAI,CAAC,OAAO,OAAc,OAAA,IAAI,MAAM,iEAAiE;AAC/F,QAAA,gBAAgB,YAAY,MAAM,OAAO,YAAY,MACrD,eAAe,WAAW,MAAM,OAAO;AAG7C,MAAI,CAAC,aAAoB,OAAA,IAAI,MAAM,uEAAuE;AAGpG,QAAA,gBAAgB,cAAc,YAAY;AAE5C,MAAA,CADoB,UAAU,IAAI,aAAa,SACvB,IAAI,MAAM,+DAA+D,aAAa,gBAAgB;AAGvH,aAAA,MAAM,IAAI,IAAI,YAAY,GACrC,WAAW,IAAI,YAAY,WAAW,IAAI,SAAS,SAAS,GAAG,IAAI,MAAM,OAAO,KAAK,MAAM,CAAC,IAAI,MAAM,OAAO,MAC7G,WAAW,WAAW;AACxB;AAEgB,SAAA,oBAAoB,SAAiB,SAA8C;AACjG,QAAM,EAAE,SAAS,UAAU,MAAM,aAAa,MAAM,QAAQ,KAAA,IAAS,SAC/D,UAA+B,EAAE,KAAK,IAAI,IAAI,aAAa,EAAE;AACnE,SAAA,gBAAgB,SAAS,SAAS,EAAE,SAAS,SAAU,CAAA,GACvD,uBAAuB,SAAS,EAAE,WAAY,CAAA,GAC9C,kBAAkB,SAAS,EAAE,MAAO,CAAA,GAC7B;AACT;AClJO,MAAM,iBAA4D;AAAA,EACvE,YAAmB,SAAwB,SAAY;AAApC,SAAA,UAAA,SAAwB,KAAA,UAAA;AAAA,EAAA;AAAA;AAAA,EAGpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOP,MAAM,OAAO;AACP,SAAK,aAAW,MAAM,KAAK,MAAM;AAC/B,UAAA,EAAE,KAAK,aAAa,oBAAoB,KAAK,SAAS,KAAK,OAAO;AACnE,WAAA,KAAA,YAAY,IAAI,UAAU,KAAK,QAAQ,GACxC,KAAK,QAAQ,UAAQ,KAAK,GAAG,QAAQ,KAAK,QAAQ,MAAM,GACxD,KAAK,QAAQ,WAAS,KAAK,GAAG,SAAS,KAAK,QAAQ,OAAO,GAC3D,KAAK,QAAQ,WAAS,KAAK,GAAG,SAAS,KAAK,QAAQ,OAAO,GAC3D,KAAK,QAAQ,aAAW,KAAK,GAAG,WAAW,CAAW,YAAA,KAAK,QAAQ,UAAW,OAAO,CAAC,GAG1F,KAAK,UAAU,iBAAiB,SAAS,CAAC,UAAU;AAC9C,YAAM,SAAS,OACd,KAAK,QAAQ,kBACd,KAAK,QAAQ,kBAAkB,MAAM,YACzC,WAAW,MAAM,KAAK,KAAK,KAAA,GAAQ,KAAK,QAAQ,kBAAkB,CAAC;AAAA,IACpE,CAAA,GAGM,IAAI,QAAc,CAAC,SAAS,YAAY;AAC7C,WAAK,UAAW,iBAAiB,QAAQ,MAAM,QAAS,CAAA,GACxD,KAAK,UAAW,iBAAiB,SAAS,MAAM,QAAQ,IAAI,MAAM,yCAAyC,CAAC,CAAC;AAAA,IAAA,CAC9G;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQH,KAAK,SAAwB;AAC3B,QAAI,CAAC,KAAK,UAAiB,OAAA,IAAI,MAAM,kCAAkC;AACjE,UAAA,OAAO,KAAK,UAAU,OAAO;AAC9B,SAAA,UAAU,KAAK,IAAI;AAAA,EAAA;AAAA,EAc1B,GAAG,OAAe,UAA+B;AAC/C,QAAI,CAAC,KAAK,UAAiB,OAAA,IAAI,MAAM,8CAA8C;AAE7E,UAAA,WAAW,CAACA,WAAkD;AAElE,UAAI,OAAOA,OAAM;AACb,UAAA;AAAS,eAAA,KAAK,MAAM,IAAc;AAAA,MAAA,QAChC;AAAA,MAAA;AACN,eAAS,IAAS;AAAA,IACpB;AAEK,WAAA,KAAA,UAAU,iBAAiB,OAAO,QAAQ,GACxC,MAAM,KAAK,UAAW,oBAAoB,OAAO,QAAQ;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlE,MAAM,QAAQ;AACZ,QAAI,CAAC,KAAK,UAAiB,OAAA,IAAI,MAAM,8CAA8C;AAC/E,SAAK,UAAU,eAAe,UAAU,UACxC,KAAK,UAAU,eAAe,UAAU,YAC5C,KAAK,UAAU,MAAM,KAAM,8BAA8B,GACzD,MAAM,IAAI,QAAc,CAAW,YAAA,KAAK,UAAW,iBAAiB,SAAS,MAAM,QAAQ,CAAC,CAAC;AAAA,EAAA;AAEjG;AAUgB,SAAA,QAAQ,OAAe,SAA2C;AACzE,SAAA,IAAI,iBAAiB,OAAO,OAAO;AAC5C;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"VkINJoq7.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 = ObjectLike,\n ServerData extends ObjectLike = ObjectLike,\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 * Automatically open the connection when it is created. If `true`, the connection\n * will automatically open when it is created. If `false`, the connection will not\n * open when it is created.\n *\n * @default false\n */\n autoOpen?: boolean\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 context: WebSocketParameters = { url: new URL('about:blank') }\n parseConnectUrl(context, channel, { baseUrl, protocol })\n parseRequestParameters(context, { parameters })\n parseRequestQuery(context, { query })\n return context\n}\n","import type { ObjectLike } from '@unshared/types'\nimport 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 extends ObjectLike, any> ? R : ObjectLike\n\ntype ServerData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, any, infer R extends ObjectLike> ? R : ObjectLike\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: WebSocket | undefined\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 async open() {\n if (this.webSocket) await this.close()\n const { url, protocol } = parseConnectOptions(this.channel, this.options)\n this.webSocket = new WebSocket(url, protocol)\n if (this.options.onOpen) this.on('open', this.options.onOpen)\n if (this.options.onClose) this.on('close', this.options.onClose)\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 // --- Reconnect to the server if 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 })\n\n // --- Return a promise that resolves when the connection is opened.\n return new Promise<void>((resolve, rejects) => {\n this.webSocket!.addEventListener('open', () => resolve())\n this.webSocket!.addEventListener('error', () => rejects(new Error('Failed to open the WebSocket connection')))\n })\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>) => void): RemoveListener\n on(event: 'close', callback: (event: CloseEvent) => void): RemoveListener\n on(event: 'error', callback: (event: Event) => void): RemoveListener\n on(event: 'open', callback: (event: Event) => void): RemoveListener\n on(event: string, callback: (data: any) => void) {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n\n const listener = (event: CloseEvent | Event | MessageEvent<any>) => {\n // @ts-expect-error: `data` exists on the event.\n let data = event.data as unknown\n try { data = JSON.parse(data as string) }\n catch { /* Ignore the error. */ }\n callback(data as T)\n }\n\n this.webSocket.addEventListener(event, listener)\n return () => this.webSocket!.removeEventListener(event, listener)\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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"}