@unshared/client 0.3.3 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{Bs2VarsP.d.ts → 5p4H9ZaI.d.ts} +9 -5
- package/dist/chunks/{Bys4-xE2.js → Cyb3f4h0.js} +3 -3
- package/dist/chunks/Cyb3f4h0.js.map +1 -0
- package/dist/chunks/{BZ5qrH6f.d.ts → Nc32BOzg.d.ts} +1 -1
- package/dist/chunks/{BdFNzMcu.cjs → SZf7rksc.cjs} +4 -4
- package/dist/chunks/SZf7rksc.cjs.map +1 -0
- package/dist/createClient.cjs +1 -57
- package/dist/createClient.cjs.map +1 -1
- package/dist/createClient.d.ts +8 -3
- package/dist/createClient.js +1 -57
- package/dist/createClient.js.map +1 -1
- package/dist/createService.d.ts +2 -2
- package/dist/index.cjs +1 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/openapi.d.ts +2 -2
- package/dist/utils.d.ts +2 -2
- package/dist/websocket.cjs +2 -2
- package/dist/websocket.d.ts +5 -5
- package/dist/websocket.js +2 -2
- package/package.json +4 -4
- package/dist/chunks/BdFNzMcu.cjs.map +0 -1
- package/dist/chunks/Bys4-xE2.js.map +0 -1
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Loose, UnionMerge, Override, Pretty, CollectKey, MaybeLiteral, StringSplit } from '@unshared/types';
|
2
|
-
import {
|
2
|
+
import { a as FetchMethod, R as RequestOptions } from './Nc32BOzg.js';
|
3
3
|
import { OpenAPI } from 'openapi-types';
|
4
4
|
|
5
5
|
/** Get the base URL of an OpenAPI specification. */
|
@@ -29,7 +29,7 @@ declare namespace OpenAPIV2 {
|
|
29
29
|
} ? `${Scheme}://${Host}${BasePath}` : string;
|
30
30
|
/*************************************************************************/
|
31
31
|
/*************************************************************************/
|
32
|
-
type InferSchemaObject<T> = T extends {
|
32
|
+
type InferSchemaObject<T> = (T extends {
|
33
33
|
properties: infer P extends Record<string, any>;
|
34
34
|
required: Array<infer R extends string>;
|
35
35
|
} ? ({
|
@@ -40,11 +40,15 @@ declare namespace OpenAPIV2 {
|
|
40
40
|
properties: infer P extends Record<string, any>;
|
41
41
|
} ? {
|
42
42
|
[K in keyof P]?: InferSchema<P[K]>;
|
43
|
-
} : T extends {
|
43
|
+
} : object) & (T extends {
|
44
44
|
additionalProperties: infer U extends Record<string, any>;
|
45
|
-
} ? Record<string, InferSchema<U>> :
|
45
|
+
} ? Record<string, InferSchema<U>> : T extends {
|
46
|
+
additionalProperties: true;
|
47
|
+
} ? Record<string, any> : T extends {
|
48
|
+
additionalProperties: false;
|
49
|
+
} ? object : Record<string, any>);
|
46
50
|
type InferSchemaArray<T> = T extends {
|
47
|
-
items?: infer U;
|
51
|
+
items?: infer U extends object;
|
48
52
|
} ? Array<InferSchema<U>> : unknown[];
|
49
53
|
type InferSchema<T> = Loose<(T extends {
|
50
54
|
anyOf: Array<infer U>;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { awaitable } from "@unshared/functions";
|
1
|
+
import { awaitable } from "@unshared/functions/awaitable";
|
2
2
|
import { p as parseRequestParameters, a as parseRequestQuery } from "./B6pUErTM.js";
|
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) {
|
@@ -53,7 +53,7 @@ class WebSocketChannel {
|
|
53
53
|
on(event, callback, options) {
|
54
54
|
if (!this.webSocket) throw new Error("WebSocket connection has not been opened yet");
|
55
55
|
const listener = async (event2) => {
|
56
|
-
|
56
|
+
event2.type !== "message" && callback(event2);
|
57
57
|
let data = event2.data;
|
58
58
|
data instanceof Blob && (data = await data.text());
|
59
59
|
try {
|
@@ -83,4 +83,4 @@ export {
|
|
83
83
|
connect as c,
|
84
84
|
parseConnectOptions as p
|
85
85
|
};
|
86
|
-
//# sourceMappingURL=
|
86
|
+
//# sourceMappingURL=Cyb3f4h0.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"Cyb3f4h0.js","sources":["../../websocket/parseConnectOptions.ts","../../websocket/connect.ts"],"sourcesContent":["import type { Loose, ObjectLike, UnionMerge } from '@unshared/types'\nimport { parseRequestParameters } from '../utils/parseRequestParameters'\nimport { parseRequestQuery } from '../utils/parseRequestQuery'\n\n/** Regular expression to match the request method and URL. */\nconst EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\\/{2}[^/]+)?(?<path>\\/[^\\s?]*)/i\n\n/** Valid WebSocket protocols. */\nconst PROTOCOLS = new Set(['ws', 'wss'])\n\n/** The protocols to use for the connection. */\nexport type ConnectProtocol = 'WS' | 'WSS'\n\n/** Options to pass to the `createChannel` function. */\nexport interface ConnectOptions<\n BaseUrl extends string = string,\n Query extends ObjectLike = ObjectLike,\n Parameters extends ObjectLike = ObjectLike,\n ClientData extends ObjectLike = any,\n ServerData extends ObjectLike = any,\n> {\n\n /** The protocol to use when connecting to the server. */\n protocol?: Lowercase<ConnectProtocol> | Uppercase<ConnectProtocol>\n\n /** The base URL to connect to. */\n baseUrl?: BaseUrl\n\n /**\n * The path parameters to use when connecting to the server. These parameters will be used to\n * fill in the path parameters of the connection URL.\n *\n * @example { id: 1 }\n */\n parameters?: Parameters\n\n /**\n * The query parameters to use when connecting to the server. These parameters will be used to\n * fill in the query parameters of the connection URL.\n *\n * @example { limit: 10, offset: 0 }\n */\n query?: Loose<Query>\n\n /**\n * The data to send when creating the connection. Namely, the path parameters\n * to use when connecting to the server.\n *\n * @example\n *\n * // Create a new connection to `http://localhost:8080/users/1`.\n * connect('GET /users/:id', {\n * data: { id: 1 },\n * baseUrl: 'http://localhost:8080'\n * })\n */\n data?: UnionMerge<Loose<Query> | Parameters>\n\n /**\n * The payload to send when creating the connection. Namely, the initial message\n * to send to the server when the connection is established.\n */\n initialPayload?: Loose<ClientData>\n\n /**\n * Weather to reconnect the connection when it is closed unexpectedly. If `true`,\n * the connection will automatically reconnect when it is closed. If `false`, the\n * connection will not reconnect when it is closed.\n *\n * @default false\n */\n autoReconnect?: boolean\n\n /**\n * The delay in milliseconds to wait before reconnecting the connection. This delay\n * will be used to wait before reconnecting the connection after it is closed.\n *\n * @default 0\n */\n reconnectDelay?: number\n\n /**\n * The maximum number of times to reconnect the connection before giving up. This\n * number will be used to determine when to stop trying to reconnect the connection.\n *\n * @default 3\n */\n reconnectLimit?: number\n\n /**\n * The function to call when the connection is opened. This function will be called\n * when the connection is successfully opened or reconnected.\n */\n onOpen?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed with an error. This function will\n * be called when the connection is closed unexpectedly with an error.\n */\n onError?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed. This function will be called\n * when the connection is closed unexpectedly or when the connection is closed manually.\n */\n onClose?: (event: CloseEvent) => void\n\n /**\n * The function to call when a message is received from the server. This function will\n * be called when a message is received from the server.\n */\n onMessage?: (data: ServerData) => void\n}\n\nexport interface WebSocketParameters {\n url: URL\n protocol?: 'ws' | 'wss'\n}\n\nfunction parseConnectUrl(parameters: WebSocketParameters, channel: string, options: ConnectOptions): void {\n const { baseUrl, protocol } = options\n\n // --- Extract the path, method, and base URL from the route name.\n const match = EXP_CONNECTION_CHANNEL.exec(channel)\n if (!match?.groups) throw new Error('Could not resolve the `RequestInit` object: Invalid route name.')\n const routeProtocol = protocol ?? match.groups.protocol ?? 'ws'\n const routeBaseUrl = baseUrl ?? match.groups.url\n\n // --- Assert the base URL is provided, either in the options or the route name.\n if (!routeBaseUrl) throw new Error('Could not resolve the `RequestInit` object: the `baseUrl` is missing.')\n\n // --- Assert the method is valid.\n const protocolLower = routeProtocol.toLowerCase()\n const protocolIsValid = PROTOCOLS.has(protocolLower)\n if (!protocolIsValid) throw new Error(`Could not resolve the \\`RequestInit\\` object:, the method \\`${routeProtocol}\\` is invalid.`)\n\n // --- Create the url and apply the method.\n parameters.url = new URL(routeBaseUrl)\n parameters.url.pathname += parameters.url.pathname.endsWith('/') ? match.groups.path.slice(1) : match.groups.path\n parameters.protocol = protocolLower as 'ws' | 'wss'\n}\n\nexport function parseConnectOptions(channel: string, options: ConnectOptions): WebSocketParameters {\n const { baseUrl, protocol, data, parameters = data, query = data } = options\n const wsParameters: WebSocketParameters = { url: new URL('about:blank') }\n parseConnectUrl(wsParameters, channel, { baseUrl, protocol })\n parseRequestParameters(wsParameters, { parameters })\n parseRequestQuery(wsParameters, { query })\n return wsParameters\n}\n","import type { Awaitable } from '@unshared/functions/awaitable'\nimport type { ConnectOptions } from './parseConnectOptions'\nimport { awaitable } from '@unshared/functions/awaitable'\nimport { parseConnectOptions } from './parseConnectOptions'\n\ntype RemoveListener = () => void\n\ntype ClientData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, infer R, any> ? R : any\n\ntype ServerData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, any, infer R> ? R : any\n\nexport class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {\n constructor(public channel: string, public options: T) {}\n\n /** The WebSocket connection to the server. */\n public webSocket: undefined | WebSocket\n\n /**\n * Open a new WebSocket connection to the server. The connection will be opened with the given\n * URL and protocols. If the connection is already open, the connection will be closed before\n * opening a new connection. Also add the event listeners that were passed in the options.\n *\n * @returns The WebSocket connection.\n */\n async open(): Promise<this> {\n if (this.webSocket) await this.close()\n const { url, protocol } = parseConnectOptions(this.channel, this.options)\n this.webSocket = new WebSocket(url, protocol)\n\n // --- Return a promise that resolves when the connection is opened.\n const promise = new Promise<void>((resolve, rejects) => {\n this.webSocket!.addEventListener('error', () => rejects(new Error('Failed to open the WebSocket connection')), { once: true })\n this.webSocket!.addEventListener('open', () => {\n if (this.options.initialPayload) this.send(this.options.initialPayload as ClientData<T>)\n resolve()\n }, { once: true })\n })\n\n // --- Add the options' hooks to the WebSocket connection.\n if (this.options.onOpen) this.on('open', this.options.onOpen, { once: true })\n if (this.options.onClose) this.on('close', this.options.onClose, { once: true })\n if (this.options.onError) this.on('error', this.options.onError)\n if (this.options.onMessage) this.on('message', message => this.options.onMessage!(message))\n\n // --- Handle reconnection when the connection is closed unexpectedly.\n this.webSocket.addEventListener('close', (event) => {\n if (event.code === 1000) return\n if (!this.options.autoReconnect) return\n if (this.options.reconnectLimit && event.wasClean) return\n setTimeout(() => void this.open(), this.options.reconnectDelay ?? 0)\n }, { once: true })\n\n return promise.then(() => this)\n }\n\n /**\n * Send a payload to the server. The payload will be serialized to JSON before sending.\n *\n * @param payload The data to send to the server.\n */\n send(payload: ClientData<T>) {\n if (!this.webSocket) throw new Error('WebSocket connection is not open')\n const json = JSON.stringify(payload)\n this.webSocket.send(json)\n }\n\n /**\n * Listen for events from the server. The event will be deserialized from JSON before calling the callback.\n *\n * @param event The event to listen for.\n * @param callback The callback to call when the event is received.\n * @returns A function to remove the event listener.\n */\n on(event: 'message', callback: (data: ServerData<T>) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'close', callback: (event: CloseEvent) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'error', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'open', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: string, callback: (data: any) => any, options?: AddEventListenerOptions) {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n\n const listener = async(event: CloseEvent | Event | MessageEvent<Blob>): Promise<void> => {\n if (event.type !== 'message') callback(event)\n // @ts-expect-error: `data` exists on the event.\n let data = event.data as unknown\n if (data instanceof Blob) data = await data.text()\n try { data = JSON.parse(data as string) }\n catch { console.error('Failed to parse the message:', data) }\n callback(data)\n }\n\n /* eslint-disable @typescript-eslint/no-misused-promises */\n this.webSocket.addEventListener(event, listener, options)\n return () => this.webSocket!.removeEventListener(event, listener)\n /* eslint-enable @typescript-eslint/no-misused-promises */\n }\n\n /**\n * Close the WebSocket connection to the server. The connection will not be able to send or receive\n * messages after it is closed.\n */\n async close() {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n if (this.webSocket.readyState === WebSocket.CLOSED) return\n if (this.webSocket.readyState === WebSocket.CLOSING) return\n this.webSocket.close(1000, 'Client closed the connection')\n await new Promise<void>(resolve => this.webSocket!.addEventListener('close', () => resolve()))\n }\n}\n\n/**\n * Create a new WebSocket connection to the server with the given path. The connection will\n * automatically reconnect if the connection is closed unexpectedly.\n *\n * @param route The name of the route to connect to.\n * @param options The options to pass to the connection.\n * @returns The WebSocket connection.\n */\nexport function connect(route: string, options: ConnectOptions): Awaitable<WebSocketChannel, WebSocketChannel> {\n const channel = new WebSocketChannel(route, options)\n return awaitable(channel, () => channel.open())\n}\n"],"names":["event"],"mappings":"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"}
|
@@ -161,4 +161,4 @@ interface RequestOptions<Method extends FetchMethod = FetchMethod, BaseUrl exten
|
|
161
161
|
*/
|
162
162
|
declare function request(route: string, options?: RequestOptions): Promise<unknown>;
|
163
163
|
|
164
|
-
export { type
|
164
|
+
export { type FetchOptions as F, type RequestOptions as R, type SearchParamsObject as S, type ToSearchParamsOptions as T, type FetchMethod as a, type RequestContext as b, type FetchHeaders as c, type SearchArrayFormat as d, parseRequest as p, request as r, toSearchParams as t };
|
@@ -1,5 +1,5 @@
|
|
1
1
|
"use strict";
|
2
|
-
var
|
2
|
+
var awaitable = require("@unshared/functions/awaitable"), parseRequestQuery = require("./BDxlAULu.cjs");
|
3
3
|
const EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\/{2}[^/]+)?(?<path>\/[^\s?]*)/i, PROTOCOLS = /* @__PURE__ */ new Set(["ws", "wss"]);
|
4
4
|
function parseConnectUrl(parameters, channel, options) {
|
5
5
|
const { baseUrl, protocol } = options, match = EXP_CONNECTION_CHANNEL.exec(channel);
|
@@ -53,7 +53,7 @@ class WebSocketChannel {
|
|
53
53
|
on(event, callback, options) {
|
54
54
|
if (!this.webSocket) throw new Error("WebSocket connection has not been opened yet");
|
55
55
|
const listener = async (event2) => {
|
56
|
-
|
56
|
+
event2.type !== "message" && callback(event2);
|
57
57
|
let data = event2.data;
|
58
58
|
data instanceof Blob && (data = await data.text());
|
59
59
|
try {
|
@@ -76,9 +76,9 @@ class WebSocketChannel {
|
|
76
76
|
}
|
77
77
|
function connect(route, options) {
|
78
78
|
const channel = new WebSocketChannel(route, options);
|
79
|
-
return
|
79
|
+
return awaitable.awaitable(channel, () => channel.open());
|
80
80
|
}
|
81
81
|
exports.WebSocketChannel = WebSocketChannel;
|
82
82
|
exports.connect = connect;
|
83
83
|
exports.parseConnectOptions = parseConnectOptions;
|
84
|
-
//# sourceMappingURL=
|
84
|
+
//# sourceMappingURL=SZf7rksc.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"SZf7rksc.cjs","sources":["../../websocket/parseConnectOptions.ts","../../websocket/connect.ts"],"sourcesContent":["import type { Loose, ObjectLike, UnionMerge } from '@unshared/types'\nimport { parseRequestParameters } from '../utils/parseRequestParameters'\nimport { parseRequestQuery } from '../utils/parseRequestQuery'\n\n/** Regular expression to match the request method and URL. */\nconst EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\\/{2}[^/]+)?(?<path>\\/[^\\s?]*)/i\n\n/** Valid WebSocket protocols. */\nconst PROTOCOLS = new Set(['ws', 'wss'])\n\n/** The protocols to use for the connection. */\nexport type ConnectProtocol = 'WS' | 'WSS'\n\n/** Options to pass to the `createChannel` function. */\nexport interface ConnectOptions<\n BaseUrl extends string = string,\n Query extends ObjectLike = ObjectLike,\n Parameters extends ObjectLike = ObjectLike,\n ClientData extends ObjectLike = any,\n ServerData extends ObjectLike = any,\n> {\n\n /** The protocol to use when connecting to the server. */\n protocol?: Lowercase<ConnectProtocol> | Uppercase<ConnectProtocol>\n\n /** The base URL to connect to. */\n baseUrl?: BaseUrl\n\n /**\n * The path parameters to use when connecting to the server. These parameters will be used to\n * fill in the path parameters of the connection URL.\n *\n * @example { id: 1 }\n */\n parameters?: Parameters\n\n /**\n * The query parameters to use when connecting to the server. These parameters will be used to\n * fill in the query parameters of the connection URL.\n *\n * @example { limit: 10, offset: 0 }\n */\n query?: Loose<Query>\n\n /**\n * The data to send when creating the connection. Namely, the path parameters\n * to use when connecting to the server.\n *\n * @example\n *\n * // Create a new connection to `http://localhost:8080/users/1`.\n * connect('GET /users/:id', {\n * data: { id: 1 },\n * baseUrl: 'http://localhost:8080'\n * })\n */\n data?: UnionMerge<Loose<Query> | Parameters>\n\n /**\n * The payload to send when creating the connection. Namely, the initial message\n * to send to the server when the connection is established.\n */\n initialPayload?: Loose<ClientData>\n\n /**\n * Weather to reconnect the connection when it is closed unexpectedly. If `true`,\n * the connection will automatically reconnect when it is closed. If `false`, the\n * connection will not reconnect when it is closed.\n *\n * @default false\n */\n autoReconnect?: boolean\n\n /**\n * The delay in milliseconds to wait before reconnecting the connection. This delay\n * will be used to wait before reconnecting the connection after it is closed.\n *\n * @default 0\n */\n reconnectDelay?: number\n\n /**\n * The maximum number of times to reconnect the connection before giving up. This\n * number will be used to determine when to stop trying to reconnect the connection.\n *\n * @default 3\n */\n reconnectLimit?: number\n\n /**\n * The function to call when the connection is opened. This function will be called\n * when the connection is successfully opened or reconnected.\n */\n onOpen?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed with an error. This function will\n * be called when the connection is closed unexpectedly with an error.\n */\n onError?: (event: Event) => void\n\n /**\n * The function to call when the connection is closed. This function will be called\n * when the connection is closed unexpectedly or when the connection is closed manually.\n */\n onClose?: (event: CloseEvent) => void\n\n /**\n * The function to call when a message is received from the server. This function will\n * be called when a message is received from the server.\n */\n onMessage?: (data: ServerData) => void\n}\n\nexport interface WebSocketParameters {\n url: URL\n protocol?: 'ws' | 'wss'\n}\n\nfunction parseConnectUrl(parameters: WebSocketParameters, channel: string, options: ConnectOptions): void {\n const { baseUrl, protocol } = options\n\n // --- Extract the path, method, and base URL from the route name.\n const match = EXP_CONNECTION_CHANNEL.exec(channel)\n if (!match?.groups) throw new Error('Could not resolve the `RequestInit` object: Invalid route name.')\n const routeProtocol = protocol ?? match.groups.protocol ?? 'ws'\n const routeBaseUrl = baseUrl ?? match.groups.url\n\n // --- Assert the base URL is provided, either in the options or the route name.\n if (!routeBaseUrl) throw new Error('Could not resolve the `RequestInit` object: the `baseUrl` is missing.')\n\n // --- Assert the method is valid.\n const protocolLower = routeProtocol.toLowerCase()\n const protocolIsValid = PROTOCOLS.has(protocolLower)\n if (!protocolIsValid) throw new Error(`Could not resolve the \\`RequestInit\\` object:, the method \\`${routeProtocol}\\` is invalid.`)\n\n // --- Create the url and apply the method.\n parameters.url = new URL(routeBaseUrl)\n parameters.url.pathname += parameters.url.pathname.endsWith('/') ? match.groups.path.slice(1) : match.groups.path\n parameters.protocol = protocolLower as 'ws' | 'wss'\n}\n\nexport function parseConnectOptions(channel: string, options: ConnectOptions): WebSocketParameters {\n const { baseUrl, protocol, data, parameters = data, query = data } = options\n const wsParameters: WebSocketParameters = { url: new URL('about:blank') }\n parseConnectUrl(wsParameters, channel, { baseUrl, protocol })\n parseRequestParameters(wsParameters, { parameters })\n parseRequestQuery(wsParameters, { query })\n return wsParameters\n}\n","import type { Awaitable } from '@unshared/functions/awaitable'\nimport type { ConnectOptions } from './parseConnectOptions'\nimport { awaitable } from '@unshared/functions/awaitable'\nimport { parseConnectOptions } from './parseConnectOptions'\n\ntype RemoveListener = () => void\n\ntype ClientData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, infer R, any> ? R : any\n\ntype ServerData<T extends ConnectOptions> =\n T extends ConnectOptions<any, any, any, any, infer R> ? R : any\n\nexport class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {\n constructor(public channel: string, public options: T) {}\n\n /** The WebSocket connection to the server. */\n public webSocket: undefined | WebSocket\n\n /**\n * Open a new WebSocket connection to the server. The connection will be opened with the given\n * URL and protocols. If the connection is already open, the connection will be closed before\n * opening a new connection. Also add the event listeners that were passed in the options.\n *\n * @returns The WebSocket connection.\n */\n async open(): Promise<this> {\n if (this.webSocket) await this.close()\n const { url, protocol } = parseConnectOptions(this.channel, this.options)\n this.webSocket = new WebSocket(url, protocol)\n\n // --- Return a promise that resolves when the connection is opened.\n const promise = new Promise<void>((resolve, rejects) => {\n this.webSocket!.addEventListener('error', () => rejects(new Error('Failed to open the WebSocket connection')), { once: true })\n this.webSocket!.addEventListener('open', () => {\n if (this.options.initialPayload) this.send(this.options.initialPayload as ClientData<T>)\n resolve()\n }, { once: true })\n })\n\n // --- Add the options' hooks to the WebSocket connection.\n if (this.options.onOpen) this.on('open', this.options.onOpen, { once: true })\n if (this.options.onClose) this.on('close', this.options.onClose, { once: true })\n if (this.options.onError) this.on('error', this.options.onError)\n if (this.options.onMessage) this.on('message', message => this.options.onMessage!(message))\n\n // --- Handle reconnection when the connection is closed unexpectedly.\n this.webSocket.addEventListener('close', (event) => {\n if (event.code === 1000) return\n if (!this.options.autoReconnect) return\n if (this.options.reconnectLimit && event.wasClean) return\n setTimeout(() => void this.open(), this.options.reconnectDelay ?? 0)\n }, { once: true })\n\n return promise.then(() => this)\n }\n\n /**\n * Send a payload to the server. The payload will be serialized to JSON before sending.\n *\n * @param payload The data to send to the server.\n */\n send(payload: ClientData<T>) {\n if (!this.webSocket) throw new Error('WebSocket connection is not open')\n const json = JSON.stringify(payload)\n this.webSocket.send(json)\n }\n\n /**\n * Listen for events from the server. The event will be deserialized from JSON before calling the callback.\n *\n * @param event The event to listen for.\n * @param callback The callback to call when the event is received.\n * @returns A function to remove the event listener.\n */\n on(event: 'message', callback: (data: ServerData<T>) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'close', callback: (event: CloseEvent) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'error', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: 'open', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener\n on(event: string, callback: (data: any) => any, options?: AddEventListenerOptions) {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n\n const listener = async(event: CloseEvent | Event | MessageEvent<Blob>): Promise<void> => {\n if (event.type !== 'message') callback(event)\n // @ts-expect-error: `data` exists on the event.\n let data = event.data as unknown\n if (data instanceof Blob) data = await data.text()\n try { data = JSON.parse(data as string) }\n catch { console.error('Failed to parse the message:', data) }\n callback(data)\n }\n\n /* eslint-disable @typescript-eslint/no-misused-promises */\n this.webSocket.addEventListener(event, listener, options)\n return () => this.webSocket!.removeEventListener(event, listener)\n /* eslint-enable @typescript-eslint/no-misused-promises */\n }\n\n /**\n * Close the WebSocket connection to the server. The connection will not be able to send or receive\n * messages after it is closed.\n */\n async close() {\n if (!this.webSocket) throw new Error('WebSocket connection has not been opened yet')\n if (this.webSocket.readyState === WebSocket.CLOSED) return\n if (this.webSocket.readyState === WebSocket.CLOSING) return\n this.webSocket.close(1000, 'Client closed the connection')\n await new Promise<void>(resolve => this.webSocket!.addEventListener('close', () => resolve()))\n }\n}\n\n/**\n * Create a new WebSocket connection to the server with the given path. The connection will\n * automatically reconnect if the connection is closed unexpectedly.\n *\n * @param route The name of the route to connect to.\n * @param options The options to pass to the connection.\n * @returns The WebSocket connection.\n */\nexport function connect(route: string, options: ConnectOptions): Awaitable<WebSocketChannel, WebSocketChannel> {\n const channel = new WebSocketChannel(route, options)\n return awaitable(channel, () => channel.open())\n}\n"],"names":["parseRequestParameters","parseRequestQuery","event","awaitable"],"mappings":"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"}
|
package/dist/createClient.cjs
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
"use strict";
|
2
|
-
var attempt = require("@unshared/functions/attempt"), request = require("./chunks/DXrQkl1A.cjs"), connect = require("./chunks/
|
2
|
+
var attempt = require("@unshared/functions/attempt"), request = require("./chunks/DXrQkl1A.cjs"), connect = require("./chunks/SZf7rksc.cjs");
|
3
3
|
require("./chunks/CVzmr2NA.cjs");
|
4
4
|
require("./chunks/BDxlAULu.cjs");
|
5
5
|
require("@unshared/functions/awaitable");
|
6
|
-
require("@unshared/functions");
|
7
6
|
class Client {
|
8
7
|
/**
|
9
8
|
* Create a new client for the application.
|
@@ -14,70 +13,15 @@ class Client {
|
|
14
13
|
constructor(options = {}) {
|
15
14
|
this.options = options;
|
16
15
|
}
|
17
|
-
/**
|
18
|
-
* Fetch a route from the API and return the `Response` object. If the client was instantiated with an
|
19
|
-
* application, the route name will be inferred from the application routes. Otherwise, you
|
20
|
-
* can pass the route name as a string.
|
21
|
-
*
|
22
|
-
* @param route The name of the route to fetch.
|
23
|
-
* @param options The options to pass to the request.
|
24
|
-
* @returns The response from the server.
|
25
|
-
*/
|
26
16
|
fetch(route, options) {
|
27
17
|
return request.fetch(route, { ...this.options, ...options });
|
28
18
|
}
|
29
|
-
/**
|
30
|
-
* Fetch a route from the API and return the data. If the client was instantiated with an
|
31
|
-
* application, the route name will be inferred from the application routes. Otherwise, you
|
32
|
-
* can pass the route name as a string.
|
33
|
-
*
|
34
|
-
* @param route The name of the route to fetch.
|
35
|
-
* @param options The options to pass to the request.
|
36
|
-
* @returns The data from the API.
|
37
|
-
* @example
|
38
|
-
* // Declare the application type.
|
39
|
-
* type App = Application<[ModuleProduct]>
|
40
|
-
*
|
41
|
-
* // Create a type-safe client for the application.
|
42
|
-
* const request = createClient<App>()
|
43
|
-
*
|
44
|
-
* // Fetch the data from the API.
|
45
|
-
* const data = request('GET /api/product/:id', { data: { id: '1' } })
|
46
|
-
*/
|
47
19
|
request(route, options) {
|
48
20
|
return request.request(route, { ...this.options, ...options });
|
49
21
|
}
|
50
|
-
/**
|
51
|
-
* Attempt to fetch a route from the API and return the data. If the client was instantiated with an
|
52
|
-
* application, the route name will be inferred from the application routes. Otherwise, you
|
53
|
-
* can pass the route name as a string.
|
54
|
-
*
|
55
|
-
* @param route The name of the route to fetch.
|
56
|
-
* @param options The options to pass to the request.
|
57
|
-
* @returns A result object with either the data or an error.
|
58
|
-
* @example
|
59
|
-
* // Declare the application type.
|
60
|
-
* type App = Application<[ModuleProduct]>
|
61
|
-
*
|
62
|
-
* // Create a type-safe client for the application.
|
63
|
-
* const request = createClient<App>()
|
64
|
-
*
|
65
|
-
* // Fetch the data from the API.
|
66
|
-
* const { data, error } = requestAttempt('GET /api/product/:id', { data: { id: '1' } })
|
67
|
-
* if (error) console.error(error)
|
68
|
-
* else console.log(data)
|
69
|
-
*/
|
70
22
|
requestAttempt(route, options) {
|
71
23
|
return attempt.attempt(() => this.request(route, options));
|
72
24
|
}
|
73
|
-
/**
|
74
|
-
* Create a new WebSocket connection to the server with the given path. The connection will
|
75
|
-
* automatically reconnect if the connection is closed unexpectedly.
|
76
|
-
*
|
77
|
-
* @param channel The path to connect to.
|
78
|
-
* @param options The options to pass to the connection.
|
79
|
-
* @returns The WebSocket connection.
|
80
|
-
*/
|
81
25
|
connect(channel, options) {
|
82
26
|
return connect.connect(channel, { baseUrl: this.options.baseUrl, ...options });
|
83
27
|
}
|
@@ -1 +1 @@
|
|
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>(route: K, options?: T[K]): Promise<Response & { json: () => Promise<Data<T[K]>> }> {\n return fetch(route, { ...this.options, ...options })\n }\n\n /**\n * Fetch a route from the API and return the data. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns The data from the API.\n * @example\n * // Declare the application type.\n * type App = Application<[ModuleProduct]>\n *\n * // Create a type-safe client for the application.\n * const request = createClient<App>()\n *\n * // Fetch the data from the API.\n * const data = request('GET /api/product/:id', { data: { id: '1' } })\n */\n public request<K extends keyof T & string>(route: K, options?: T[K]): Promise<Data<T[K]
|
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 { FetchOptions, RequestOptions } from './utils'\nimport type { ConnectOptions, WebSocketChannel } from './websocket'\nimport { attempt } from '@unshared/functions/attempt'\nimport { fetch } from './utils/fetch'\nimport { request } from './utils/request'\nimport { connect } from './websocket/connect'\n\ntype Data<T extends RequestOptions> = T extends RequestOptions<any, any, any, any, any, any, infer R, any> ? R : unknown\ntype Routes = Record<string, RequestOptions>\ntype Channels = Record<string, ConnectOptions>\n\nexport class Client<T extends Routes = Routes, U extends Channels = Channels> {\n\n /**\n * Create a new client for the application.\n *\n * @param options The options to pass to the client.\n * @example new Client({ baseUrl: 'https://api.example.com' })\n */\n constructor(public options: RequestOptions = {}) {}\n\n /**\n * Fetch a route from the API and return the `Response` object. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns The response from the server.\n */\n public fetch<K extends keyof T & string>(route: K, options?: T[K]): Promise<Response & { json: () => Promise<Data<T[K]>> }>\n public fetch(route: string, options?: FetchOptions): Promise<Response>\n public fetch(route: string, options?: FetchOptions): Promise<Response> {\n return fetch(route, { ...this.options, ...options })\n }\n\n /**\n * Fetch a route from the API and return the data. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns The data from the API.\n * @example\n * // Declare the application type.\n * type App = Application<[ModuleProduct]>\n *\n * // Create a type-safe client for the application.\n * const request = createClient<App>()\n *\n * // Fetch the data from the API.\n * const data = request('GET /api/product/:id', { data: { id: '1' } })\n */\n public request<K extends keyof T & string>(route: K, options?: T[K]): Promise<Data<T[K]>>\n public request(route: string, options?: RequestOptions): Promise<unknown>\n public request(route: string, options?: RequestOptions): Promise<unknown> {\n return request(route, { ...this.options, ...options })\n }\n\n /**\n * Attempt to fetch a route from the API and return the data. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns A result object with either the data or an error.\n * @example\n * // Declare the application type.\n * type App = Application<[ModuleProduct]>\n *\n * // Create a type-safe client for the application.\n * const request = createClient<App>()\n *\n * // Fetch the data from the API.\n * const { data, error } = requestAttempt('GET /api/product/:id', { data: { id: '1' } })\n * if (error) console.error(error)\n * else console.log(data)\n */\n public requestAttempt<K extends keyof T & string>(route: K, options?: T[K]): Promise<Result<Data<T[K]>>>\n public requestAttempt(route: string, options?: RequestOptions): Promise<Result<unknown>>\n public requestAttempt(route: string, options?: RequestOptions): Promise<Result<unknown>> {\n return attempt(() => this.request(route, options))\n }\n\n /**\n * Create a new WebSocket connection to the server with the given path. The connection will\n * automatically reconnect if the connection is closed unexpectedly.\n *\n * @param channel The path to connect to.\n * @param options The options to pass to the connection.\n * @returns The WebSocket connection.\n */\n public connect<P extends keyof U & string>(channel: P, options?: U[P]): Awaitable<WebSocketChannel<U[P]>, WebSocketChannel<U[P]>>\n public connect(channel: string, options?: ConnectOptions): Awaitable<WebSocketChannel, WebSocketChannel>\n public connect(channel: string, options?: ConnectOptions): Awaitable<WebSocketChannel, WebSocketChannel> {\n return connect(channel, { baseUrl: this.options.baseUrl, ...options })\n }\n}\n\n/**\n * Create a new type-safe client for the application. The client can be used to fetch data from\n * the API and connect to the server using WebSockets with the given path.\n *\n * @param options The options to pass to the client.\n * @returns The client object with the request method.\n * @example\n * // Create a type-safe client for the application.\n * const client = createClient<[ModuleUser]>()\n *\n * // Fetch the data from the API.\n * const data = await client.request('GET /api/user/:id', { id: '1' })\n *\n * // Use the data from the API.\n * console.log(data) // { id: '1', name: 'John Doe' }\n */\nexport function createClient<T extends OpenAPILike>(options?: ServiceOptions<T>): Client<OpenAPIOptionsMap<T>>\n\n/**\n * Create a new type-safe client for the application. The client can be used to fetch data from\n * the API and connect to the server using WebSockets with the given path.\n *\n * @param options The options to pass to the client.\n * @returns The client object with the request method.\n * @example\n * // Create a type-safe client for the application.\n * const client = createClient<[ModuleUser]>()\n *\n * // Fetch the data from the API.\n * const data = await client.request('GET /api/user/:id', { id: '1' })\n *\n * // Use the data from the API.\n * console.log(data) // { id: '1', name: 'John Doe' }\n */\nexport function createClient<T extends Routes = Routes, V extends Channels = Channels>(options?: RequestOptions): Client<T, V>\nexport function createClient(options?: RequestOptions): Client {\n return new Client(options)\n}\n"],"names":["fetch","request","attempt","connect"],"mappings":";;;;;AAeO,MAAM,OAAiE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5E,YAAmB,UAA0B,IAAI;AAA9B,SAAA,UAAA;AAAA,EAAA;AAAA,EAaZ,MAAM,OAAe,SAA2C;AAC9D,WAAAA,QAAAA,MAAM,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA,EAuB9C,QAAQ,OAAe,SAA4C;AACjE,WAAAC,QAAAA,QAAQ,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA,EAyBhD,eAAe,OAAe,SAAoD;AACvF,WAAOC,QAAAA,QAAQ,MAAM,KAAK,QAAQ,OAAO,OAAO,CAAC;AAAA,EAAA;AAAA,EAa5C,QAAQ,SAAiB,SAAyE;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;;;"}
|
package/dist/createClient.d.ts
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
import { Awaitable } from '@unshared/functions';
|
2
2
|
import { Result } from '@unshared/functions/attempt';
|
3
3
|
import { ServiceOptions } from './createService.js';
|
4
|
-
import {
|
5
|
-
import {
|
4
|
+
import { R as RequestOptions, F as FetchOptions } from './chunks/Nc32BOzg.js';
|
5
|
+
import { O as OpenAPILike, a as OpenAPIOptionsMap } from './chunks/5p4H9ZaI.js';
|
6
6
|
import { WebSocketChannel, ConnectOptions } from './websocket.js';
|
7
7
|
import '@unshared/types';
|
8
|
-
import 'openapi-types';
|
9
8
|
import './HttpHeaders.js';
|
10
9
|
import './HttpMethods.js';
|
10
|
+
import 'openapi-types';
|
11
|
+
import '@unshared/functions/awaitable';
|
11
12
|
|
12
13
|
type Data<T extends RequestOptions> = T extends RequestOptions<any, any, any, any, any, any, infer R, any> ? R : unknown;
|
13
14
|
type Routes = Record<string, RequestOptions>;
|
@@ -33,6 +34,7 @@ declare class Client<T extends Routes = Routes, U extends Channels = Channels> {
|
|
33
34
|
fetch<K extends keyof T & string>(route: K, options?: T[K]): Promise<Response & {
|
34
35
|
json: () => Promise<Data<T[K]>>;
|
35
36
|
}>;
|
37
|
+
fetch(route: string, options?: FetchOptions): Promise<Response>;
|
36
38
|
/**
|
37
39
|
* Fetch a route from the API and return the data. If the client was instantiated with an
|
38
40
|
* application, the route name will be inferred from the application routes. Otherwise, you
|
@@ -52,6 +54,7 @@ declare class Client<T extends Routes = Routes, U extends Channels = Channels> {
|
|
52
54
|
* const data = request('GET /api/product/:id', { data: { id: '1' } })
|
53
55
|
*/
|
54
56
|
request<K extends keyof T & string>(route: K, options?: T[K]): Promise<Data<T[K]>>;
|
57
|
+
request(route: string, options?: RequestOptions): Promise<unknown>;
|
55
58
|
/**
|
56
59
|
* Attempt to fetch a route from the API and return the data. If the client was instantiated with an
|
57
60
|
* application, the route name will be inferred from the application routes. Otherwise, you
|
@@ -73,6 +76,7 @@ declare class Client<T extends Routes = Routes, U extends Channels = Channels> {
|
|
73
76
|
* else console.log(data)
|
74
77
|
*/
|
75
78
|
requestAttempt<K extends keyof T & string>(route: K, options?: T[K]): Promise<Result<Data<T[K]>>>;
|
79
|
+
requestAttempt(route: string, options?: RequestOptions): Promise<Result<unknown>>;
|
76
80
|
/**
|
77
81
|
* Create a new WebSocket connection to the server with the given path. The connection will
|
78
82
|
* automatically reconnect if the connection is closed unexpectedly.
|
@@ -82,6 +86,7 @@ declare class Client<T extends Routes = Routes, U extends Channels = Channels> {
|
|
82
86
|
* @returns The WebSocket connection.
|
83
87
|
*/
|
84
88
|
connect<P extends keyof U & string>(channel: P, options?: U[P]): Awaitable<WebSocketChannel<U[P]>, WebSocketChannel<U[P]>>;
|
89
|
+
connect(channel: string, options?: ConnectOptions): Awaitable<WebSocketChannel, WebSocketChannel>;
|
85
90
|
}
|
86
91
|
/**
|
87
92
|
* Create a new type-safe client for the application. The client can be used to fetch data from
|
package/dist/createClient.js
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
import { attempt } from "@unshared/functions/attempt";
|
2
2
|
import { f as fetch, r as request } from "./chunks/D1QsGr3A.js";
|
3
|
-
import { c as connect } from "./chunks/
|
3
|
+
import { c as connect } from "./chunks/Cyb3f4h0.js";
|
4
4
|
import "./chunks/CS5r-m4U.js";
|
5
5
|
import "./chunks/B6pUErTM.js";
|
6
6
|
import "@unshared/functions/awaitable";
|
7
|
-
import "@unshared/functions";
|
8
7
|
class Client {
|
9
8
|
/**
|
10
9
|
* Create a new client for the application.
|
@@ -15,70 +14,15 @@ class Client {
|
|
15
14
|
constructor(options = {}) {
|
16
15
|
this.options = options;
|
17
16
|
}
|
18
|
-
/**
|
19
|
-
* Fetch a route from the API and return the `Response` object. If the client was instantiated with an
|
20
|
-
* application, the route name will be inferred from the application routes. Otherwise, you
|
21
|
-
* can pass the route name as a string.
|
22
|
-
*
|
23
|
-
* @param route The name of the route to fetch.
|
24
|
-
* @param options The options to pass to the request.
|
25
|
-
* @returns The response from the server.
|
26
|
-
*/
|
27
17
|
fetch(route, options) {
|
28
18
|
return fetch(route, { ...this.options, ...options });
|
29
19
|
}
|
30
|
-
/**
|
31
|
-
* Fetch a route from the API and return the data. If the client was instantiated with an
|
32
|
-
* application, the route name will be inferred from the application routes. Otherwise, you
|
33
|
-
* can pass the route name as a string.
|
34
|
-
*
|
35
|
-
* @param route The name of the route to fetch.
|
36
|
-
* @param options The options to pass to the request.
|
37
|
-
* @returns The data from the API.
|
38
|
-
* @example
|
39
|
-
* // Declare the application type.
|
40
|
-
* type App = Application<[ModuleProduct]>
|
41
|
-
*
|
42
|
-
* // Create a type-safe client for the application.
|
43
|
-
* const request = createClient<App>()
|
44
|
-
*
|
45
|
-
* // Fetch the data from the API.
|
46
|
-
* const data = request('GET /api/product/:id', { data: { id: '1' } })
|
47
|
-
*/
|
48
20
|
request(route, options) {
|
49
21
|
return request(route, { ...this.options, ...options });
|
50
22
|
}
|
51
|
-
/**
|
52
|
-
* Attempt to fetch a route from the API and return the data. If the client was instantiated with an
|
53
|
-
* application, the route name will be inferred from the application routes. Otherwise, you
|
54
|
-
* can pass the route name as a string.
|
55
|
-
*
|
56
|
-
* @param route The name of the route to fetch.
|
57
|
-
* @param options The options to pass to the request.
|
58
|
-
* @returns A result object with either the data or an error.
|
59
|
-
* @example
|
60
|
-
* // Declare the application type.
|
61
|
-
* type App = Application<[ModuleProduct]>
|
62
|
-
*
|
63
|
-
* // Create a type-safe client for the application.
|
64
|
-
* const request = createClient<App>()
|
65
|
-
*
|
66
|
-
* // Fetch the data from the API.
|
67
|
-
* const { data, error } = requestAttempt('GET /api/product/:id', { data: { id: '1' } })
|
68
|
-
* if (error) console.error(error)
|
69
|
-
* else console.log(data)
|
70
|
-
*/
|
71
23
|
requestAttempt(route, options) {
|
72
24
|
return attempt(() => this.request(route, options));
|
73
25
|
}
|
74
|
-
/**
|
75
|
-
* Create a new WebSocket connection to the server with the given path. The connection will
|
76
|
-
* automatically reconnect if the connection is closed unexpectedly.
|
77
|
-
*
|
78
|
-
* @param channel The path to connect to.
|
79
|
-
* @param options The options to pass to the connection.
|
80
|
-
* @returns The WebSocket connection.
|
81
|
-
*/
|
82
26
|
connect(channel, options) {
|
83
27
|
return connect(channel, { baseUrl: this.options.baseUrl, ...options });
|
84
28
|
}
|
package/dist/createClient.js.map
CHANGED
@@ -1 +1 @@
|
|
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>(route: K, options?: T[K]): Promise<Response & { json: () => Promise<Data<T[K]>> }> {\n return fetch(route, { ...this.options, ...options })\n }\n\n /**\n * Fetch a route from the API and return the data. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns The data from the API.\n * @example\n * // Declare the application type.\n * type App = Application<[ModuleProduct]>\n *\n * // Create a type-safe client for the application.\n * const request = createClient<App>()\n *\n * // Fetch the data from the API.\n * const data = request('GET /api/product/:id', { data: { id: '1' } })\n */\n public request<K extends keyof T & string>(route: K, options?: T[K]): Promise<Data<T[K]
|
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 { FetchOptions, RequestOptions } from './utils'\nimport type { ConnectOptions, WebSocketChannel } from './websocket'\nimport { attempt } from '@unshared/functions/attempt'\nimport { fetch } from './utils/fetch'\nimport { request } from './utils/request'\nimport { connect } from './websocket/connect'\n\ntype Data<T extends RequestOptions> = T extends RequestOptions<any, any, any, any, any, any, infer R, any> ? R : unknown\ntype Routes = Record<string, RequestOptions>\ntype Channels = Record<string, ConnectOptions>\n\nexport class Client<T extends Routes = Routes, U extends Channels = Channels> {\n\n /**\n * Create a new client for the application.\n *\n * @param options The options to pass to the client.\n * @example new Client({ baseUrl: 'https://api.example.com' })\n */\n constructor(public options: RequestOptions = {}) {}\n\n /**\n * Fetch a route from the API and return the `Response` object. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns The response from the server.\n */\n public fetch<K extends keyof T & string>(route: K, options?: T[K]): Promise<Response & { json: () => Promise<Data<T[K]>> }>\n public fetch(route: string, options?: FetchOptions): Promise<Response>\n public fetch(route: string, options?: FetchOptions): Promise<Response> {\n return fetch(route, { ...this.options, ...options })\n }\n\n /**\n * Fetch a route from the API and return the data. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns The data from the API.\n * @example\n * // Declare the application type.\n * type App = Application<[ModuleProduct]>\n *\n * // Create a type-safe client for the application.\n * const request = createClient<App>()\n *\n * // Fetch the data from the API.\n * const data = request('GET /api/product/:id', { data: { id: '1' } })\n */\n public request<K extends keyof T & string>(route: K, options?: T[K]): Promise<Data<T[K]>>\n public request(route: string, options?: RequestOptions): Promise<unknown>\n public request(route: string, options?: RequestOptions): Promise<unknown> {\n return request(route, { ...this.options, ...options })\n }\n\n /**\n * Attempt to fetch a route from the API and return the data. If the client was instantiated with an\n * application, the route name will be inferred from the application routes. Otherwise, you\n * can pass the route name as a string.\n *\n * @param route The name of the route to fetch.\n * @param options The options to pass to the request.\n * @returns A result object with either the data or an error.\n * @example\n * // Declare the application type.\n * type App = Application<[ModuleProduct]>\n *\n * // Create a type-safe client for the application.\n * const request = createClient<App>()\n *\n * // Fetch the data from the API.\n * const { data, error } = requestAttempt('GET /api/product/:id', { data: { id: '1' } })\n * if (error) console.error(error)\n * else console.log(data)\n */\n public requestAttempt<K extends keyof T & string>(route: K, options?: T[K]): Promise<Result<Data<T[K]>>>\n public requestAttempt(route: string, options?: RequestOptions): Promise<Result<unknown>>\n public requestAttempt(route: string, options?: RequestOptions): Promise<Result<unknown>> {\n return attempt(() => this.request(route, options))\n }\n\n /**\n * Create a new WebSocket connection to the server with the given path. The connection will\n * automatically reconnect if the connection is closed unexpectedly.\n *\n * @param channel The path to connect to.\n * @param options The options to pass to the connection.\n * @returns The WebSocket connection.\n */\n public connect<P extends keyof U & string>(channel: P, options?: U[P]): Awaitable<WebSocketChannel<U[P]>, WebSocketChannel<U[P]>>\n public connect(channel: string, options?: ConnectOptions): Awaitable<WebSocketChannel, WebSocketChannel>\n public connect(channel: string, options?: ConnectOptions): Awaitable<WebSocketChannel, WebSocketChannel> {\n return connect(channel, { baseUrl: this.options.baseUrl, ...options })\n }\n}\n\n/**\n * Create a new type-safe client for the application. The client can be used to fetch data from\n * the API and connect to the server using WebSockets with the given path.\n *\n * @param options The options to pass to the client.\n * @returns The client object with the request method.\n * @example\n * // Create a type-safe client for the application.\n * const client = createClient<[ModuleUser]>()\n *\n * // Fetch the data from the API.\n * const data = await client.request('GET /api/user/:id', { id: '1' })\n *\n * // Use the data from the API.\n * console.log(data) // { id: '1', name: 'John Doe' }\n */\nexport function createClient<T extends OpenAPILike>(options?: ServiceOptions<T>): Client<OpenAPIOptionsMap<T>>\n\n/**\n * Create a new type-safe client for the application. The client can be used to fetch data from\n * the API and connect to the server using WebSockets with the given path.\n *\n * @param options The options to pass to the client.\n * @returns The client object with the request method.\n * @example\n * // Create a type-safe client for the application.\n * const client = createClient<[ModuleUser]>()\n *\n * // Fetch the data from the API.\n * const data = await client.request('GET /api/user/:id', { id: '1' })\n *\n * // Use the data from the API.\n * console.log(data) // { id: '1', name: 'John Doe' }\n */\nexport function createClient<T extends Routes = Routes, V extends Channels = Channels>(options?: RequestOptions): Client<T, V>\nexport function createClient(options?: RequestOptions): Client {\n return new Client(options)\n}\n"],"names":[],"mappings":";;;;;;AAeO,MAAM,OAAiE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5E,YAAmB,UAA0B,IAAI;AAA9B,SAAA,UAAA;AAAA,EAAA;AAAA,EAaZ,MAAM,OAAe,SAA2C;AAC9D,WAAA,MAAM,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA,EAuB9C,QAAQ,OAAe,SAA4C;AACjE,WAAA,QAAQ,OAAO,EAAE,GAAG,KAAK,SAAS,GAAG,SAAS;AAAA,EAAA;AAAA,EAyBhD,eAAe,OAAe,SAAoD;AACvF,WAAO,QAAQ,MAAM,KAAK,QAAQ,OAAO,OAAO,CAAC;AAAA,EAAA;AAAA,EAa5C,QAAQ,SAAiB,SAAyE;AAChG,WAAA,QAAQ,SAAS,EAAE,SAAS,KAAK,QAAQ,SAAS,GAAG,SAAS;AAAA,EAAA;AAEzE;AAqCO,SAAS,aAAa,SAAkC;AACtD,SAAA,IAAI,OAAO,OAAO;AAC3B;"}
|
package/dist/createService.d.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { MaybeLiteral } from '@unshared/types';
|
2
|
-
import { b as OperationId, c as OperationById, d as OperationOptions, e as OperationResult, S as ServerUrl, f as OpenAPIV3 } from './chunks/
|
3
|
-
import { R as RequestOptions } from './chunks/
|
2
|
+
import { b as OperationId, c as OperationById, d as OperationOptions, e as OperationResult, S as ServerUrl, f as OpenAPIV3 } from './chunks/5p4H9ZaI.js';
|
3
|
+
import { R as RequestOptions } from './chunks/Nc32BOzg.js';
|
4
4
|
import 'openapi-types';
|
5
5
|
import './HttpHeaders.js';
|
6
6
|
import './HttpMethods.js';
|
package/dist/index.cjs
CHANGED
@@ -5,8 +5,7 @@ require("./chunks/DXrQkl1A.cjs");
|
|
5
5
|
require("./chunks/CVzmr2NA.cjs");
|
6
6
|
require("./chunks/BDxlAULu.cjs");
|
7
7
|
require("@unshared/functions/awaitable");
|
8
|
-
require("./chunks/
|
9
|
-
require("@unshared/functions");
|
8
|
+
require("./chunks/SZf7rksc.cjs");
|
10
9
|
require("./chunks/DEyigyGy.cjs");
|
11
10
|
exports.Client = createClient.Client;
|
12
11
|
exports.createClient = createClient.createClient;
|
package/dist/index.cjs.map
CHANGED
@@ -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
@@ -5,8 +5,9 @@ export { HttpMethod } from './HttpMethods.js';
|
|
5
5
|
export { HttpStatusCode } from './HttpStatusCodes.js';
|
6
6
|
import '@unshared/functions';
|
7
7
|
import '@unshared/functions/attempt';
|
8
|
-
import './chunks/
|
8
|
+
import './chunks/Nc32BOzg.js';
|
9
9
|
import '@unshared/types';
|
10
|
-
import './chunks/
|
10
|
+
import './chunks/5p4H9ZaI.js';
|
11
11
|
import 'openapi-types';
|
12
12
|
import './websocket.js';
|
13
|
+
import '@unshared/functions/awaitable';
|
package/dist/index.js
CHANGED
@@ -8,8 +8,7 @@ import "./chunks/D1QsGr3A.js";
|
|
8
8
|
import "./chunks/CS5r-m4U.js";
|
9
9
|
import "./chunks/B6pUErTM.js";
|
10
10
|
import "@unshared/functions/awaitable";
|
11
|
-
import "./chunks/
|
12
|
-
import "@unshared/functions";
|
11
|
+
import "./chunks/Cyb3f4h0.js";
|
13
12
|
import "./chunks/B_Gz6Yz8.js";
|
14
13
|
export {
|
15
14
|
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,7 +1,7 @@
|
|
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/
|
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/5p4H9ZaI.js';
|
2
2
|
import { OpenAPIV2, OpenAPIV3, OpenAPIV3_1, OpenAPI } from 'openapi-types';
|
3
3
|
import { StringReplace, WriteableDeep, StringJoin, Substract } from '@unshared/types';
|
4
|
-
import './chunks/
|
4
|
+
import './chunks/Nc32BOzg.js';
|
5
5
|
import './HttpHeaders.js';
|
6
6
|
import './HttpMethods.js';
|
7
7
|
|
package/dist/utils.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import {
|
2
|
-
export { c as FetchHeaders,
|
1
|
+
import { F as FetchOptions, R as RequestOptions, b as RequestContext } from './chunks/Nc32BOzg.js';
|
2
|
+
export { c as FetchHeaders, a as FetchMethod, d as SearchArrayFormat, S as SearchParamsObject, T as ToSearchParamsOptions, p as parseRequest, r as request, t as toSearchParams } from './chunks/Nc32BOzg.js';
|
3
3
|
import { Awaitable } from '@unshared/functions/awaitable';
|
4
4
|
import { ObjectLike } from '@unshared/types';
|
5
5
|
import './HttpHeaders.js';
|
package/dist/websocket.cjs
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"use strict";
|
2
|
-
var connect = require("./chunks/
|
3
|
-
require("@unshared/functions");
|
2
|
+
var connect = require("./chunks/SZf7rksc.cjs");
|
3
|
+
require("@unshared/functions/awaitable");
|
4
4
|
require("./chunks/BDxlAULu.cjs");
|
5
5
|
exports.WebSocketChannel = connect.WebSocketChannel;
|
6
6
|
exports.connect = connect.connect;
|
package/dist/websocket.d.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Awaitable } from '@unshared/functions';
|
1
|
+
import { Awaitable } from '@unshared/functions/awaitable';
|
2
2
|
import { ObjectLike, Loose, UnionMerge } from '@unshared/types';
|
3
3
|
|
4
4
|
/** The protocols to use for the connection. */
|
@@ -120,10 +120,10 @@ declare class WebSocketChannel<T extends ConnectOptions = ConnectOptions> {
|
|
120
120
|
* @param callback The callback to call when the event is received.
|
121
121
|
* @returns A function to remove the event listener.
|
122
122
|
*/
|
123
|
-
on(event: 'message', callback: (data: ServerData<T>) =>
|
124
|
-
on(event: 'close', callback: (event: CloseEvent) =>
|
125
|
-
on(event: 'error', callback: (event: Event) =>
|
126
|
-
on(event: 'open', callback: (event: Event) =>
|
123
|
+
on(event: 'message', callback: (data: ServerData<T>) => any, options?: AddEventListenerOptions): RemoveListener;
|
124
|
+
on(event: 'close', callback: (event: CloseEvent) => any, options?: AddEventListenerOptions): RemoveListener;
|
125
|
+
on(event: 'error', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener;
|
126
|
+
on(event: 'open', callback: (event: Event) => any, options?: AddEventListenerOptions): RemoveListener;
|
127
127
|
/**
|
128
128
|
* Close the WebSocket connection to the server. The connection will not be able to send or receive
|
129
129
|
* messages after it is closed.
|
package/dist/websocket.js
CHANGED
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@unshared/client",
|
3
3
|
"type": "module",
|
4
|
-
"version": "0.
|
4
|
+
"version": "0.4.1",
|
5
5
|
"license": "MIT",
|
6
6
|
"sideEffects": false,
|
7
7
|
"author": "Stanley Horwood <stanley@hsjm.io>",
|
@@ -68,12 +68,12 @@
|
|
68
68
|
"LICENSE.md"
|
69
69
|
],
|
70
70
|
"dependencies": {
|
71
|
-
"@unshared/functions": "0.
|
72
|
-
"@unshared/types": "0.
|
71
|
+
"@unshared/functions": "0.4.1",
|
72
|
+
"@unshared/types": "0.4.1",
|
73
73
|
"openapi-types": "12.1.3"
|
74
74
|
},
|
75
75
|
"devDependencies": {
|
76
|
-
"@unshared/scripts": "0.
|
76
|
+
"@unshared/scripts": "0.4.1"
|
77
77
|
},
|
78
78
|
"scripts": {
|
79
79
|
"build:httpMethods": "tsx ./scripts/buildHttpMethods.ts",
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"BdFNzMcu.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 +0,0 @@
|
|
1
|
-
{"version":3,"file":"Bys4-xE2.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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"}
|