@unshared/client 0.2.3 → 0.3.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.
Files changed (85) hide show
  1. package/dist/HttpHeaders.cjs +4 -0
  2. package/dist/HttpHeaders.cjs.map +1 -0
  3. package/dist/{chunks/8BFCFxqa.d.ts → HttpHeaders.d.ts} +1 -1
  4. package/dist/HttpHeaders.js +5 -0
  5. package/dist/HttpHeaders.js.map +1 -0
  6. package/dist/HttpMethods.cjs +4 -0
  7. package/dist/HttpMethods.cjs.map +1 -0
  8. package/dist/{chunks/DZp6zyqV.d.ts → HttpMethods.d.ts} +1 -98
  9. package/dist/HttpMethods.js +5 -0
  10. package/dist/HttpMethods.js.map +1 -0
  11. package/dist/HttpStatusCodes.cjs +4 -0
  12. package/dist/HttpStatusCodes.cjs.map +1 -0
  13. package/dist/HttpStatusCodes.d.ts +615 -0
  14. package/dist/HttpStatusCodes.js +5 -0
  15. package/dist/HttpStatusCodes.js.map +1 -0
  16. package/dist/chunks/0ZzUT3m_.js +46 -0
  17. package/dist/chunks/0ZzUT3m_.js.map +1 -0
  18. package/dist/chunks/BUeqbyph.js +114 -0
  19. package/dist/chunks/BUeqbyph.js.map +1 -0
  20. package/dist/chunks/{D-WqCFul.js → Biic1J5b.js} +11 -9
  21. package/dist/chunks/Biic1J5b.js.map +1 -0
  22. package/dist/chunks/CO11DuYE.d.ts +144 -0
  23. package/dist/chunks/CYmaYL5B.cjs +45 -0
  24. package/dist/chunks/CYmaYL5B.cjs.map +1 -0
  25. package/dist/chunks/Cayg8606.cjs +112 -0
  26. package/dist/chunks/Cayg8606.cjs.map +1 -0
  27. package/dist/chunks/{xRZPkxch.cjs → CtW2aMuA.cjs} +11 -9
  28. package/dist/chunks/CtW2aMuA.cjs.map +1 -0
  29. package/dist/chunks/DJJsADWD.js +9 -0
  30. package/dist/chunks/DJJsADWD.js.map +1 -0
  31. package/dist/chunks/DOZHjge0.d.ts +224 -0
  32. package/dist/chunks/Du56lBvc.js +75 -0
  33. package/dist/chunks/Du56lBvc.js.map +1 -0
  34. package/dist/chunks/Du_W5H6e.cjs +8 -0
  35. package/dist/chunks/Du_W5H6e.cjs.map +1 -0
  36. package/dist/chunks/VkINJoq7.cjs +74 -0
  37. package/dist/chunks/VkINJoq7.cjs.map +1 -0
  38. package/dist/createClient.cjs +82 -18
  39. package/dist/createClient.cjs.map +1 -1
  40. package/dist/createClient.d.ts +109 -29
  41. package/dist/createClient.js +85 -20
  42. package/dist/createClient.js.map +1 -1
  43. package/dist/createService.cjs +18 -0
  44. package/dist/createService.cjs.map +1 -0
  45. package/dist/createService.d.ts +38 -0
  46. package/dist/createService.js +20 -0
  47. package/dist/createService.js.map +1 -0
  48. package/dist/index.cjs +12 -26
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.ts +9 -625
  51. package/dist/index.js +13 -27
  52. package/dist/index.js.map +1 -1
  53. package/dist/openapi.cjs +3 -15
  54. package/dist/openapi.cjs.map +1 -1
  55. package/dist/openapi.d.ts +7 -35
  56. package/dist/openapi.js +3 -15
  57. package/dist/openapi.js.map +1 -1
  58. package/dist/utils.cjs +13 -11
  59. package/dist/utils.cjs.map +1 -1
  60. package/dist/utils.d.ts +57 -27
  61. package/dist/utils.js +12 -9
  62. package/dist/utils.js.map +1 -1
  63. package/dist/websocket.cjs +7 -0
  64. package/dist/websocket.cjs.map +1 -0
  65. package/dist/websocket.d.ts +148 -0
  66. package/dist/websocket.js +8 -0
  67. package/dist/websocket.js.map +1 -0
  68. package/package.json +28 -8
  69. package/dist/chunks/B2OrSen1.d.ts +0 -185
  70. package/dist/chunks/B5hc73Po.js +0 -96
  71. package/dist/chunks/B5hc73Po.js.map +0 -1
  72. package/dist/chunks/CKfJvIQ8.js +0 -50
  73. package/dist/chunks/CKfJvIQ8.js.map +0 -1
  74. package/dist/chunks/Cx8m1YzL.cjs +0 -49
  75. package/dist/chunks/Cx8m1YzL.cjs.map +0 -1
  76. package/dist/chunks/D-WqCFul.js.map +0 -1
  77. package/dist/chunks/D8Tsm7xC.cjs +0 -95
  78. package/dist/chunks/D8Tsm7xC.cjs.map +0 -1
  79. package/dist/chunks/r8pYO6Hx.d.ts +0 -38
  80. package/dist/chunks/xRZPkxch.cjs.map +0 -1
  81. package/dist/fetch.cjs +0 -8
  82. package/dist/fetch.cjs.map +0 -1
  83. package/dist/fetch.d.ts +0 -16
  84. package/dist/fetch.js +0 -9
  85. package/dist/fetch.js.map +0 -1
@@ -0,0 +1,224 @@
1
+ import { Loose, UnionMerge, Override, Pretty, CollectKey, MaybeLiteral, StringSplit } from '@unshared/types';
2
+ import { F as FetchMethod, R as RequestOptions } from './CO11DuYE.js';
3
+ import { OpenAPI } from 'openapi-types';
4
+
5
+ /** Get the base URL of an OpenAPI specification. */
6
+ type ServerUrl<T> = T extends {
7
+ host: infer Host extends string;
8
+ basePath?: infer BasePath extends string;
9
+ schemes?: Array<infer Scheme extends string>;
10
+ } ? `${Scheme}://${Host}${BasePath}` : T extends {
11
+ servers: Array<{
12
+ url: infer U extends string;
13
+ }>;
14
+ } ? U : string;
15
+ /**
16
+ * Given an OpenAPI specification, get the first base URL.
17
+ *
18
+ * @param specification The OpenAPI specification.
19
+ * @returns The first base URL.
20
+ * @example getBaseUrl(specification) // 'https://api.example.com/v1'
21
+ */
22
+ declare function getServerUrl<T>(specification: T): ServerUrl<T>;
23
+
24
+ declare namespace OpenAPIV2 {
25
+ type ServerUrl<T> = T extends {
26
+ host: infer Host extends string;
27
+ basePath?: infer BasePath extends string;
28
+ schemes?: Array<infer Scheme extends string>;
29
+ } ? `${Scheme}://${Host}${BasePath}` : string;
30
+ /*************************************************************************/
31
+ /*************************************************************************/
32
+ type InferSchemaObject<T> = T extends {
33
+ properties: infer P extends Record<string, any>;
34
+ required: Array<infer R extends string>;
35
+ } ? ({
36
+ [K in keyof P as K extends R ? K : never]: InferSchema<P[K]>;
37
+ } & {
38
+ [K in keyof P as K extends R ? never : K]?: InferSchema<P[K]>;
39
+ }) : T extends {
40
+ properties: infer P extends Record<string, any>;
41
+ } ? {
42
+ [K in keyof P]?: InferSchema<P[K]>;
43
+ } : Record<string, unknown>;
44
+ type InferSchemaArray<T> = T extends {
45
+ items?: infer U;
46
+ additionalItems?: infer U;
47
+ } ? Array<InferSchema<U>> : unknown[];
48
+ type InferSchema<T> = Loose<(T extends {
49
+ anyOf: Array<infer U>;
50
+ } ? InferSchema<U> : T extends {
51
+ oneOf: Array<infer U>;
52
+ } ? InferSchema<U> : T extends {
53
+ allOf: Array<infer U>;
54
+ } ? UnionMerge<InferSchema<U>> : T extends {
55
+ schema: infer U;
56
+ } ? InferSchema<U> : T extends {
57
+ type: 'array';
58
+ } ? InferSchemaArray<T> : T extends {
59
+ type: 'object';
60
+ } ? InferSchemaObject<T> : T extends {
61
+ type: 'string';
62
+ } ? T extends {
63
+ enum: Array<infer U>;
64
+ } ? U : string : T extends {
65
+ type: 'integer' | 'number';
66
+ } ? number : T extends {
67
+ type: 'boolean';
68
+ } ? boolean : never) | (T extends {
69
+ nullable: true;
70
+ } ? undefined : never)>;
71
+ /*************************************************************************/
72
+ /*************************************************************************/
73
+ type Parameters<T, I extends string> = UnionMerge<T extends {
74
+ parameters: Array<infer U>;
75
+ } ? U extends {
76
+ in: I;
77
+ name: infer N extends string;
78
+ } ? Record<N, InferSchema<U> | (U extends {
79
+ required: true;
80
+ } ? never : undefined)> : never : never>;
81
+ type RequestBody<T> = Parameters<T, 'body'> extends Record<string, infer U> ? U : never;
82
+ type RequestQuery<T> = Parameters<T, 'query'>;
83
+ type RequestParameters<T> = Parameters<T, 'path'>;
84
+ type RequestHeaders<T> = UnionMerge<Parameters<T, 'header'> | (T extends {
85
+ consumes: Array<infer C>;
86
+ } ? {
87
+ 'Content-Type'?: C;
88
+ } : never)>;
89
+ /*************************************************************************/
90
+ /*************************************************************************/
91
+ type ResponseBody<T> = T extends {
92
+ responses: Record<200, {
93
+ schema: infer S;
94
+ }>;
95
+ } ? NonNullable<InferSchema<S>> : never;
96
+ type Response<T> = T extends {
97
+ responses: infer R;
98
+ } ? ({
99
+ [P in keyof R]: Override<globalThis.Response, {
100
+ status: P extends 'default' ? number : P;
101
+ json: InferSchema<R[P]> extends (infer V | undefined) ? [never] extends V ? never : () => Promise<V> : never;
102
+ }>;
103
+ }) extends infer Result ? Result[keyof Result] : never : never;
104
+ }
105
+
106
+ declare namespace OpenAPIV3 {
107
+ type ServerUrl<T> = T extends {
108
+ servers: Array<{
109
+ url: infer U extends string;
110
+ }>;
111
+ } ? U : string;
112
+ type ServerHeaders<T> = T extends {
113
+ servers: Array<{
114
+ variables: infer V;
115
+ }>;
116
+ } ? V extends {
117
+ [K in keyof V]: {
118
+ enum: Array<infer U>;
119
+ };
120
+ } ? {
121
+ [K in keyof V]: U;
122
+ } : never : never;
123
+ type ServerQuery<T> = T extends {
124
+ components: {
125
+ securitySchemes: Record<string, {
126
+ in: 'query';
127
+ name: infer U extends string;
128
+ }>;
129
+ };
130
+ } ? Partial<Record<U, string>> : object;
131
+ /*************************************************************************/
132
+ /*************************************************************************/
133
+ type RequestBody<T> = T extends {
134
+ requestBody: {
135
+ content: infer C;
136
+ };
137
+ } ? C extends Record<string, {
138
+ schema: infer S;
139
+ }> ? OpenAPIV2.InferSchema<S> : object : object;
140
+ type RequestQuery<T> = OpenAPIV2.Parameters<T, 'query'>;
141
+ /*************************************************************************/
142
+ /*************************************************************************/
143
+ type ResponseBody<U> = U extends {
144
+ responses: Record<200, {
145
+ content: Record<string, {
146
+ schema: infer Schema;
147
+ }>;
148
+ }>;
149
+ } ? NonNullable<OpenAPIV2.InferSchema<Schema>> : never;
150
+ type Response<U> = U extends {
151
+ responses: infer Responses;
152
+ } ? ({
153
+ [Status in keyof Responses]: Responses[Status] extends {
154
+ content: Record<'application/json', infer Schema>;
155
+ } ? Pretty<Override<globalThis.Response, {
156
+ status: Status;
157
+ json: OpenAPIV2.InferSchema<Schema> extends (infer V | undefined) ? [never] extends V ? never : () => Promise<V> : never;
158
+ }>> : never;
159
+ }) extends infer Result ? Result[keyof Result] : never : never;
160
+ }
161
+
162
+ /** Union of all operation IDs in the specification. */
163
+ type OperationId<T> = T extends {
164
+ paths: infer P;
165
+ } ? P extends Record<string, infer R> ? R extends Record<string, infer O> ? O extends {
166
+ operationId: infer N;
167
+ } ? N : string : string : string : string;
168
+ /** A union of possible Operations types in the specification. */
169
+ type Operation = {
170
+ method: FetchMethod;
171
+ path: string;
172
+ } & OpenAPI.Operation;
173
+ /** Find an operation by its operationId in an OpenAPI specification. */
174
+ type OperationById<T, U extends OperationId<T>> = T extends {
175
+ paths: infer P;
176
+ } ? CollectKey<P> extends Record<string, infer R> ? CollectKey<R> extends Record<string, infer O> ? O extends {
177
+ $key: [infer P extends string, infer M extends string];
178
+ operationId: U;
179
+ } ? Pretty<{
180
+ method: M;
181
+ path: P;
182
+ } & Omit<O, '$key'>> : never : never : never : never;
183
+ /**
184
+ * Given an OpenAPI specification, find an operation by its operationId.
185
+ *
186
+ * @param document The OpenAPI specification document.
187
+ * @param operationId The operationId of the operation to resolve.
188
+ * @returns The resolved operation.
189
+ * @example resolveOperation(document, 'getUser') // { method: 'get', path: '/users/{username}', ... }
190
+ */
191
+ declare function resolveOperation<T, U extends OperationId<T>>(document: T, operationId: U): OperationById<T, U>;
192
+
193
+ interface OpenAPIV2Like {
194
+ swagger: string;
195
+ }
196
+ interface OpenAPIV3Like {
197
+ openapi: string;
198
+ }
199
+ type OpenAPILike = OpenAPIV2Like | OpenAPIV3Like;
200
+ /** The options to pass to the request. */
201
+ type OperationOptions<T, V extends Operation> = T extends OpenAPIV2Like ? RequestOptions<never, MaybeLiteral<ServerUrl<T>>, OpenAPIV2.RequestParameters<V>, OpenAPIV2.RequestQuery<V>, OpenAPIV2.RequestBody<V>, OpenAPIV2.RequestHeaders<V>, OpenAPIV2.ResponseBody<V>> : T extends OpenAPIV3Like ? RequestOptions<never, MaybeLiteral<ServerUrl<T>>, OpenAPIV2.RequestParameters<V>, OpenAPIV3.RequestQuery<V> & OpenAPIV3.ServerQuery<T>, OpenAPIV3.RequestBody<V>, OpenAPIV2.RequestHeaders<V>, OpenAPIV3.ResponseBody<V>> : RequestOptions;
202
+ /** The response data from the operation. */
203
+ type OperationResult<T, V extends Operation> = T extends OpenAPIV2Like ? OpenAPIV2.ResponseBody<V> : T extends OpenAPIV3Like ? OpenAPIV3.ResponseBody<V> : unknown;
204
+ /** The `Response` object from the operation. */
205
+ type OperationResponse<T, V extends Operation> = T extends OpenAPIV2Like ? OpenAPIV2.Response<V> : T extends OpenAPIV3Like ? OpenAPIV3.Response<V> : globalThis.Response;
206
+ /** Union of all operation route names in the specification. */
207
+ type OperationRoute<T> = T extends {
208
+ paths: infer P;
209
+ } ? CollectKey<P> extends Record<string, infer R> ? CollectKey<R> extends Record<string, infer O> ? O extends {
210
+ $key: [infer P extends string, infer M extends string];
211
+ } ? `${Uppercase<M>} ${P}` : string : string : string : string;
212
+ /** Find an operation by its route name in an OpenAPI specification. */
213
+ type OperationByRoute<T, U extends OperationRoute<T>> = StringSplit<U, ' '> extends [infer M extends string, infer P extends string] ? T extends {
214
+ paths: infer U;
215
+ } ? U extends Record<P, infer R> ? R extends Record<Lowercase<M>, infer O> ? Pretty<{
216
+ method: Lowercase<M>;
217
+ path: P;
218
+ } & O> : never : never : never : never;
219
+ /** The `ClientRoutes` inferred from the OpenAPI specification. */
220
+ type OpenAPIOptionsMap<T> = {
221
+ [K in OperationRoute<T>]: OperationOptions<T, OperationByRoute<T, K>>;
222
+ };
223
+
224
+ export { type OpenAPILike as O, type ServerUrl as S, type OpenAPIOptionsMap as a, type OperationId as b, type OperationById as c, type OperationOptions as d, type OperationResult as e, OpenAPIV3 as f, getServerUrl as g, OpenAPIV2 as h, type Operation as i, type OpenAPIV2Like as j, type OpenAPIV3Like as k, type OperationResponse as l, type OperationRoute as m, type OperationByRoute as n, resolveOperation as r };
@@ -0,0 +1,75 @@
1
+ import { p as parseRequestParameters, a as parseRequestQuery } from "./0ZzUT3m_.js";
2
+ const EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\/{2}[^/]+)?(?<path>\/[^\s?]*)/i, PROTOCOLS = /* @__PURE__ */ new Set(["ws", "wss"]);
3
+ function parseConnectUrl(parameters, channel, options) {
4
+ const { baseUrl, protocol } = options, match = EXP_CONNECTION_CHANNEL.exec(channel);
5
+ if (!match?.groups) throw new Error("Could not resolve the `RequestInit` object: Invalid route name.");
6
+ const routeProtocol = protocol ?? match.groups.protocol ?? "ws", routeBaseUrl = baseUrl ?? match.groups.url;
7
+ if (!routeBaseUrl) throw new Error("Could not resolve the `RequestInit` object: the `baseUrl` is missing.");
8
+ const protocolLower = routeProtocol.toLowerCase();
9
+ if (!PROTOCOLS.has(protocolLower)) throw new Error(`Could not resolve the \`RequestInit\` object:, the method \`${routeProtocol}\` is invalid.`);
10
+ 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
+ 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;
15
+ }
16
+ class WebSocketChannel {
17
+ constructor(channel, options) {
18
+ this.channel = channel, this.options = options;
19
+ }
20
+ /** The WebSocket connection to the server. */
21
+ webSocket;
22
+ /**
23
+ * Open a new WebSocket connection to the server. The connection will be opened with the given
24
+ * URL and protocols. If the connection is already open, the connection will be closed before
25
+ * opening a new connection. Also add the event listeners that were passed in the options.
26
+ */
27
+ async open() {
28
+ this.webSocket && await this.close();
29
+ 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")));
34
+ });
35
+ }
36
+ /**
37
+ * Send a payload to the server. The payload will be serialized to JSON before sending.
38
+ *
39
+ * @param payload The data to send to the server.
40
+ */
41
+ send(payload) {
42
+ if (!this.webSocket) throw new Error("WebSocket connection is not open");
43
+ const json = JSON.stringify(payload);
44
+ this.webSocket.send(json);
45
+ }
46
+ on(event, callback) {
47
+ if (!this.webSocket) throw new Error("WebSocket connection has not been opened yet");
48
+ const listener = (event2) => {
49
+ let data = event2.data;
50
+ try {
51
+ data = JSON.parse(data);
52
+ } catch {
53
+ }
54
+ callback(data);
55
+ };
56
+ return this.webSocket.addEventListener(event, listener), () => this.webSocket.removeEventListener(event, listener);
57
+ }
58
+ /**
59
+ * Close the WebSocket connection to the server. The connection will not be able to send or receive
60
+ * messages after it is closed.
61
+ */
62
+ async close() {
63
+ if (!this.webSocket) throw new Error("WebSocket connection has not been opened yet");
64
+ this.webSocket.readyState !== WebSocket.CLOSED && this.webSocket.readyState !== WebSocket.CLOSING && (this.webSocket.close(1e3, "Client closed the connection"), await new Promise((resolve) => this.webSocket.addEventListener("close", () => resolve())));
65
+ }
66
+ }
67
+ function connect(route, options) {
68
+ return new WebSocketChannel(route, options);
69
+ }
70
+ export {
71
+ WebSocketChannel as W,
72
+ connect as c,
73
+ parseConnectOptions as p
74
+ };
75
+ //# sourceMappingURL=Du56lBvc.js.map
@@ -0,0 +1 @@
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":"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"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var handleResponse = require("./Cayg8606.cjs");
3
+ async function request(route, options = {}) {
4
+ const response = await handleResponse.fetch(route, options);
5
+ return await handleResponse.handleResponse(response, options);
6
+ }
7
+ exports.request = request;
8
+ //# sourceMappingURL=Du_W5H6e.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Du_W5H6e.cjs","sources":["../../utils/request.ts"],"sourcesContent":["import type { ObjectLike } from '@unshared/types'\nimport type { FetchMethod, FetchOptions } from './parseRequest'\nimport { fetch } from './fetch'\nimport { handleResponse } from './handleResponse'\n\nexport interface RequestOptions<\n Method extends FetchMethod = FetchMethod,\n BaseUrl extends string = string,\n Parameters extends ObjectLike = ObjectLike,\n Query extends ObjectLike = ObjectLike,\n Body = unknown,\n Headers extends ObjectLike = ObjectLike,\n Data = any,\n Response = globalThis.Response,\n> extends\n FetchOptions<Method, BaseUrl, Parameters, Query, Body, Headers> {\n\n /**\n * The callback that is called when an error occurs during the request.\n */\n onError?: (error: Error) => any\n\n /**\n * The callback that is called when data is received from the request. This callback\n * will be called for each chunk of data that is received from the request.\n */\n onData?: (data: Data) => any\n\n /**\n * The callback that is called when the request is successful. This callback will be\n * called after the request is complete and all data has been received.\n */\n onSuccess?: (response: Response) => any\n\n /**\n * The callback that is called when the status code is not OK. This callback will be called\n * after the request is complete and before the data is consumed.\n */\n onFailure?: (response: Response) => any\n\n /**\n * The callback that is called when the request is complete. This callback will be called\n * after the request is complete and all data has been received.\n */\n onEnd?: (response: Response) => any\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 */\nexport async function request(route: string, options?: RequestOptions): Promise<unknown>\nexport async function request(route: string, options: RequestOptions = {}): Promise<unknown> {\n const response = await fetch(route, options)\n return await handleResponse(response, options)\n}\n"],"names":["fetch","handleResponse"],"mappings":";;AAkEA,eAAsB,QAAQ,OAAe,UAA0B,IAAsB;AAC3F,QAAM,WAAW,MAAMA,qBAAM,OAAO,OAAO;AACpC,SAAA,MAAMC,eAAAA,eAAe,UAAU,OAAO;AAC/C;;"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ var parseRequestQuery = require("./CYmaYL5B.cjs");
3
+ const EXP_CONNECTION_CHANNEL = /^((?<protocol>[a-z]+) )?(?<url>[^:]+?:\/{2}[^/]+)?(?<path>\/[^\s?]*)/i, PROTOCOLS = /* @__PURE__ */ new Set(["ws", "wss"]);
4
+ function parseConnectUrl(parameters, channel, options) {
5
+ const { baseUrl, protocol } = options, match = EXP_CONNECTION_CHANNEL.exec(channel);
6
+ if (!match?.groups) throw new Error("Could not resolve the `RequestInit` object: Invalid route name.");
7
+ const routeProtocol = protocol ?? match.groups.protocol ?? "ws", routeBaseUrl = baseUrl ?? match.groups.url;
8
+ if (!routeBaseUrl) throw new Error("Could not resolve the `RequestInit` object: the `baseUrl` is missing.");
9
+ const protocolLower = routeProtocol.toLowerCase();
10
+ if (!PROTOCOLS.has(protocolLower)) throw new Error(`Could not resolve the \`RequestInit\` object:, the method \`${routeProtocol}\` is invalid.`);
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
+ }
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;
16
+ }
17
+ class WebSocketChannel {
18
+ constructor(channel, options) {
19
+ this.channel = channel, this.options = options;
20
+ }
21
+ /** The WebSocket connection to the server. */
22
+ webSocket;
23
+ /**
24
+ * Open a new WebSocket connection to the server. The connection will be opened with the given
25
+ * URL and protocols. If the connection is already open, the connection will be closed before
26
+ * opening a new connection. Also add the event listeners that were passed in the options.
27
+ */
28
+ async open() {
29
+ this.webSocket && await this.close();
30
+ 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")));
35
+ });
36
+ }
37
+ /**
38
+ * Send a payload to the server. The payload will be serialized to JSON before sending.
39
+ *
40
+ * @param payload The data to send to the server.
41
+ */
42
+ send(payload) {
43
+ if (!this.webSocket) throw new Error("WebSocket connection is not open");
44
+ const json = JSON.stringify(payload);
45
+ this.webSocket.send(json);
46
+ }
47
+ on(event, callback) {
48
+ if (!this.webSocket) throw new Error("WebSocket connection has not been opened yet");
49
+ const listener = (event2) => {
50
+ let data = event2.data;
51
+ try {
52
+ data = JSON.parse(data);
53
+ } catch {
54
+ }
55
+ callback(data);
56
+ };
57
+ return this.webSocket.addEventListener(event, listener), () => this.webSocket.removeEventListener(event, listener);
58
+ }
59
+ /**
60
+ * Close the WebSocket connection to the server. The connection will not be able to send or receive
61
+ * messages after it is closed.
62
+ */
63
+ async close() {
64
+ if (!this.webSocket) throw new Error("WebSocket connection has not been opened yet");
65
+ this.webSocket.readyState !== WebSocket.CLOSED && this.webSocket.readyState !== WebSocket.CLOSING && (this.webSocket.close(1e3, "Client closed the connection"), await new Promise((resolve) => this.webSocket.addEventListener("close", () => resolve())));
66
+ }
67
+ }
68
+ function connect(route, options) {
69
+ return new WebSocketChannel(route, options);
70
+ }
71
+ exports.WebSocketChannel = WebSocketChannel;
72
+ exports.connect = connect;
73
+ exports.parseConnectOptions = parseConnectOptions;
74
+ //# sourceMappingURL=VkINJoq7.cjs.map
@@ -0,0 +1 @@
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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"}
@@ -1,24 +1,88 @@
1
1
  "use strict";
2
- var fetch = require("./fetch.cjs"), getOperationById = require("./chunks/xRZPkxch.cjs"), handleResponse = require("./chunks/Cx8m1YzL.cjs");
3
- require("./chunks/D8Tsm7xC.cjs");
2
+ var attempt = require("@unshared/functions/attempt"), handleResponse = require("./chunks/Cayg8606.cjs"), request = require("./chunks/Du_W5H6e.cjs"), connect = require("./chunks/VkINJoq7.cjs");
3
+ require("./chunks/CYmaYL5B.cjs");
4
4
  require("@unshared/functions/awaitable");
5
- function createClient(documentOrUrl, initialOptions = {}) {
6
- const specifications = typeof documentOrUrl == "string" ? void 0 : documentOrUrl;
7
- typeof documentOrUrl == "string" && (initialOptions.baseUrl = documentOrUrl);
8
- async function fetchByOperationId(operationId, options) {
9
- if (!specifications) throw new Error("No OpenAPI specification provided.");
10
- const operation = getOperationById.getOperationById(specifications, operationId);
11
- if (!operation) throw new Error(`Operation ID "${operationId}" not found.`);
12
- const { method, path, responses = {} } = operation, fetchOptions = { method, baseUrl: getOperationById.getBaseUrl(specifications), ...initialOptions, ...options }, response = await fetch.fetch(path, fetchOptions);
13
- if (response.ok) return handleResponse.handleResponse(response, fetchOptions);
14
- const status = response.status.toString();
15
- throw status in responses && typeof responses[status] == "object" && responses[status] !== null && "description" in responses[status] && typeof responses[status].description == "string" ? new Error(responses[status].description) : new Error(response.statusText);
5
+ class Client {
6
+ /**
7
+ * Create a new client for the application.
8
+ *
9
+ * @param options The options to pass to the client.
10
+ * @example new Client({ baseUrl: 'https://api.example.com' })
11
+ */
12
+ constructor(options = {}) {
13
+ this.options = options;
16
14
  }
17
- return new Proxy({}, {
18
- get(_, property) {
19
- return property === "fetch" ? (route, options) => fetch.fetch(route, { ...initialOptions, ...options }) : (options) => fetchByOperationId(property, options);
20
- }
21
- });
15
+ /**
16
+ * Fetch a route from the API and return the `Response` object. If the client was instantiated with an
17
+ * application, the route name will be inferred from the application routes. Otherwise, you
18
+ * can pass the route name as a string.
19
+ *
20
+ * @param route The name of the route to fetch.
21
+ * @param options The options to pass to the request.
22
+ * @returns The response from the server.
23
+ */
24
+ fetch(route, options) {
25
+ return handleResponse.fetch(route, { ...this.options, ...options });
26
+ }
27
+ /**
28
+ * Fetch a route from the API and return the data. If the client was instantiated with an
29
+ * application, the route name will be inferred from the application routes. Otherwise, you
30
+ * can pass the route name as a string.
31
+ *
32
+ * @param route The name of the route to fetch.
33
+ * @param options The options to pass to the request.
34
+ * @returns The data from the API.
35
+ * @example
36
+ * // Declare the application type.
37
+ * type App = Application<[ModuleProduct]>
38
+ *
39
+ * // Create a type-safe client for the application.
40
+ * const request = createClient<App>()
41
+ *
42
+ * // Fetch the data from the API.
43
+ * const data = request('GET /api/product/:id', { data: { id: '1' } })
44
+ */
45
+ request(route, options) {
46
+ return request.request(route, { ...this.options, ...options });
47
+ }
48
+ /**
49
+ * Attempt to fetch a route from the API and return the data. If the client was instantiated with an
50
+ * application, the route name will be inferred from the application routes. Otherwise, you
51
+ * can pass the route name as a string.
52
+ *
53
+ * @param route The name of the route to fetch.
54
+ * @param options The options to pass to the request.
55
+ * @returns A result object with either the data or an error.
56
+ * @example
57
+ * // Declare the application type.
58
+ * type App = Application<[ModuleProduct]>
59
+ *
60
+ * // Create a type-safe client for the application.
61
+ * const request = createClient<App>()
62
+ *
63
+ * // Fetch the data from the API.
64
+ * const { data, error } = requestAttempt('GET /api/product/:id', { data: { id: '1' } })
65
+ * if (error) console.error(error)
66
+ * else console.log(data)
67
+ */
68
+ requestAttempt(route, options) {
69
+ return attempt.attempt(() => this.request(route, options));
70
+ }
71
+ /**
72
+ * Create a new WebSocket connection to the server with the given path. The connection will
73
+ * automatically reconnect if the connection is closed unexpectedly.
74
+ *
75
+ * @param channel The path to connect to.
76
+ * @param options The options to pass to the connection.
77
+ * @returns The WebSocket connection.
78
+ */
79
+ connect(channel, options) {
80
+ return connect.connect(channel, { baseUrl: this.options.baseUrl, ...options });
81
+ }
82
+ }
83
+ function createClient(options) {
84
+ return new Client(options);
22
85
  }
86
+ exports.Client = Client;
23
87
  exports.createClient = createClient;
24
88
  //# sourceMappingURL=createClient.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"createClient.cjs","sources":["../createClient.ts"],"sourcesContent":["import type { MaybeLiteral, Override, Pretty } from '@unshared/types'\nimport type { OpenAPI, OpenAPIV2 as V2, OpenAPIV3 as V3, OpenAPIV3_1 as V3_1 } from 'openapi-types'\nimport type { OpenAPIV2, OpenAPIV3 } from './openapi/index'\nimport type { RequestHooks } from './utils/handleResponse'\nimport type { RequestMethod, RequestOptions } from './utils/parseRequest'\nimport { fetch } from './fetch'\nimport { getBaseUrl } from './openapi/getBaseUrl'\nimport { getOperationById } from './openapi/getOperationById'\nimport { handleResponse } from './utils/handleResponse'\n\ntype ClientBaseUrl<T> =\n MaybeLiteral<\n T extends V2.Document ? OpenAPIV2.ServerUrl<T>\n : T extends V3.Document ? OpenAPIV3.ServerUrl<T>\n : T extends V3_1.Document ? OpenAPIV3.ServerUrl<T>\n : string\n >\n\ntype ClientFetch<T> =\n T extends V2.Document ? <P extends OpenAPIV2.Route<T>>(name: P, options: OpenAPIV2.RequestInit<T, OpenAPIV2.OperationByRoute<T, P>>) => Promise<OpenAPIV2.Response<OpenAPIV2.OperationByRoute<T, P>>>\n : T extends V3.Document ? <P extends OpenAPIV2.Route<T>>(name: P, options: OpenAPIV3.RequestInit<T, OpenAPIV2.OperationByRoute<T, P>>) => Promise<OpenAPIV3.Response<OpenAPIV2.OperationByRoute<T, P>>>\n : T extends V3_1.Document ? <P extends OpenAPIV2.Route<T>>(name: P, options: OpenAPIV3.RequestInit<T, OpenAPIV2.OperationByRoute<T, P>>) => Promise<OpenAPIV3.Response<OpenAPIV2.OperationByRoute<T, P>>>\n : typeof globalThis.fetch\n\ntype ClientFetchOperation<T, U extends OpenAPIV2.OperationId<T>> =\n T extends V2.Document ? (options: OpenAPIV2.RequestInit<T, OpenAPIV2.OperationById<T, U>>) => Promise<OpenAPIV2.ResponseBody<OpenAPIV2.OperationById<T, U>>>\n : T extends V3.Document ? (options: OpenAPIV3.RequestInit<T, OpenAPIV2.OperationById<T, U>>) => Promise<OpenAPIV3.ResponseBody<OpenAPIV2.OperationById<T, U>>>\n : T extends V3_1.Document ? (options: OpenAPIV3.RequestInit<T, OpenAPIV2.OperationById<T, U>>) => Promise<OpenAPIV3.ResponseBody<OpenAPIV2.OperationById<T, U>>>\n : (options: RequestOptions) => Promise<Response>\n\nexport type Client<T = OpenAPI.Document> =\n Pretty<\n & { [K in OpenAPIV2.OperationId<T>]: ClientFetchOperation<T, K> }\n & { fetch: ClientFetch<T> }\n >\n\nexport type ClientOptions<T = any> = Override<RequestHooks & RequestOptions, {\n baseUrl?: ClientBaseUrl<T>\n\n /**\n * The headers to include in every request made by the client.\n *\n * @example { 'Authorization': 'Bearer ...' }\n */\n headers?: T extends V3.Document\n ? OpenAPIV3.ServerHeaders<T>\n : never\n}>\n\n/**\n * Create a new client instance for the given OpenAPI specification.\n *\n * @param document The OpenAPI specification document.\n * @param initialOptions The initial options to use for every request.\n * @returns The client instance.\n * @example\n * const client = createClient(document)\n * await client.fetch({ ... })\n */\n// @ts-expect-error: `ClientOptions` is not assignable to `ClientOptions<T>`.\nexport function createClient<T extends OpenAPI.Document>(document: Readonly<T>, initialOptions?: ClientOptions<T>): Client<T>\nexport function createClient<T extends OpenAPI.Document>(url: ClientBaseUrl<T>, initialOptions?: ClientOptions<T>): Client<T>\nexport function createClient(documentOrUrl: Readonly<OpenAPI.Document> | string, initialOptions: ClientOptions = {}): Client {\n const specifications = typeof documentOrUrl === 'string' ? undefined : documentOrUrl\n if (typeof documentOrUrl === 'string') initialOptions.baseUrl = documentOrUrl\n\n async function fetchByOperationId(operationId: string, options: ClientOptions<any>) {\n if (!specifications) throw new Error('No OpenAPI specification provided.')\n const operation = getOperationById(specifications, operationId) as { method: RequestMethod; path: string } & OpenAPI.Operation\n if (!operation) throw new Error(`Operation ID \"${operationId}\" not found.`)\n const { method, path, responses = {} } = operation\n const fetchOptions = { method, baseUrl: getBaseUrl(specifications), ...initialOptions, ...options }\n const response = await fetch(path, fetchOptions)\n\n // --- Return the JSON response if successful.\n if (response.ok) return handleResponse(response, fetchOptions)\n\n // --- Throw an error if the response was not successful.\n const status = response.status.toString()\n if (status in responses\n && typeof responses[status] === 'object'\n && responses[status] !== null\n && 'description' in responses[status]\n && typeof responses[status].description === 'string')\n throw new Error(responses[status].description)\n\n // --- Throw a generic error if the response was not successful.\n throw new Error(response.statusText)\n }\n\n return new Proxy({}, {\n get(_, property: string) {\n if (property === 'fetch') return (route: string, options: RequestOptions) => fetch(route, ({ ...initialOptions, ...options }))\n return (options: Record<string, unknown>) => fetchByOperationId(property, options)\n },\n }) as unknown as Client\n}\n"],"names":["getOperationById","getBaseUrl","fetch","handleResponse"],"mappings":";;;;AA8DO,SAAS,aAAa,eAAoD,iBAAgC,IAAY;AAC3H,QAAM,iBAAiB,OAAO,iBAAkB,WAAW,SAAY;AACnE,SAAO,iBAAkB,aAAU,eAAe,UAAU;AAEjD,iBAAA,mBAAmB,aAAqB,SAA6B;AAClF,QAAI,CAAC,eAAsB,OAAA,IAAI,MAAM,oCAAoC;AACnE,UAAA,YAAYA,iBAAAA,iBAAiB,gBAAgB,WAAW;AAC9D,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,iBAAiB,WAAW,cAAc;AACpE,UAAA,EAAE,QAAQ,MAAM,YAAY,CAAA,EAAO,IAAA,WACnC,eAAe,EAAE,QAAQ,SAASC,4BAAW,cAAc,GAAG,GAAG,gBAAgB,GAAG,QAAA,GACpF,WAAW,MAAMC,MAAAA,MAAM,MAAM,YAAY;AAG/C,QAAI,SAAS,GAAW,QAAAC,eAAAA,eAAe,UAAU,YAAY;AAGvD,UAAA,SAAS,SAAS,OAAO,SAAS;AACxC,UAAI,UAAU,aACT,OAAO,UAAU,MAAM,KAAM,YAC7B,UAAU,MAAM,MAAM,QACtB,iBAAiB,UAAU,MAAM,KACjC,OAAO,UAAU,MAAM,EAAE,eAAgB,WACtC,IAAI,MAAM,UAAU,MAAM,EAAE,WAAW,IAGzC,IAAI,MAAM,SAAS,UAAU;AAAA,EAAA;AAG9B,SAAA,IAAI,MAAM,IAAI;AAAA,IACnB,IAAI,GAAG,UAAkB;AACvB,aAAI,aAAa,UAAgB,CAAC,OAAe,YAA4BD,MAAA,MAAM,OAAQ,EAAE,GAAG,gBAAgB,GAAG,SAAU,IACtH,CAAC,YAAqC,mBAAmB,UAAU,OAAO;AAAA,IAAA;AAAA,EACnF,CACD;AACH;;"}
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;;;"}