@pushwoosh/http-client 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # Pushwoosh / Micro Frontends / Http Client
2
+ Этот пакет является частью микрофронтендовой архитектуры.
3
+ Предназначен для выполнения запросов к API.
4
+
5
+ ## Термины
6
+ [Аутентификация](https://auth0.com/docs/authentication) - процесс подтверждения личности пользователя
7
+
8
+ [Авторизация](https://auth0.com/docs/authorization) - процесс проверки доступа к защищенным ресурсам
9
+
10
+ [Клиент](https://www.rfc-editor.org/rfc/rfc6749.html#section-1.1) - приложение запрашивающее защищенные ресурсы от имени пользователя
11
+
12
+ [Сервер авторизации](https://www.rfc-editor.org/rfc/rfc6749.html#section-1.1) - сервер, выдающий токены доступа клиенту после успешной аутентификации владельца ресурса и получение авторизации
13
+
14
+ ## Установка
15
+ 0. Установить переменную окружения "NPM_TOKEN" в терминале (токен можно получить у администраторов)
16
+ 1. Выполнить `npm ci` для установки зависимостей
17
+
18
+ ## Запуск разработки
19
+ 0. Скопировать файл `.env.example` в `.env`. Удалить из него комментарии к переменным
20
+ 1. Выполнить `npm start`
21
+
22
+ ## Работа с библиотекой
23
+ ### Создание экземпляра HttpClient
24
+ При создании экземпляра класса HttpClient необходимо указать следующие параметры:
25
+
26
+ ```typescript
27
+ import { HttpClient } from './http-client';
28
+
29
+ const httpClient = new HttpClient({
30
+ urls: {
31
+ authorize: 'https://sso.pushwoosh.com/authorize', // адрес старта сеанса
32
+ token: 'https://sso.pushwoosh.com/token', // адрес для получения или обновления пары токенов для авторизации в приватных API
33
+ logout: 'https://sso.pushwoosh.com/logout', // адрес для завершения сеанса
34
+ },
35
+ clientId: 'APP_FRONT', // уникальный идентификатор клиента (связан с доменом)
36
+ })
37
+ ```
38
+
39
+ ### Инициализация экземпляра HttpClient
40
+ Экземпляр класса HttpClient необходимо инициализировать, для получения информации аутентифицирован ли пользователь на клиенте
41
+
42
+ ```typescript
43
+ const introspect = await httpClient.initialize(authorizationCode, authorizationState);
44
+ ```
45
+
46
+ `authorizationCode` - это код авторизации переданный со стороны `сервера авторизации`
47
+
48
+ `authorizationState` - это случайная строка, сгенерированная и сохраненная клиентом перед началом сеанса, возвращается вместе с `authorizationCode`
49
+
50
+ `introspect` - это данные о текущем сеансе (идентификатор пользователя, идентификатор аккаунта и т.п.), которые зашиты в `accessToken`
51
+
52
+ ### Начать сеанс
53
+ Выполнить аутентификацию пользователя на клиенте
54
+
55
+ ```typescript
56
+ await httpClient.login();
57
+ ```
58
+
59
+ ### Завершить сеанс
60
+ Завершить сеанс клиента
61
+
62
+ ```typescript
63
+ await httpClient.logout();
64
+ ```
65
+
66
+ ### Выполнение запросов
67
+
68
+ #### Базовая конфигурация:
69
+
70
+ Запрос:
71
+ ```
72
+ curl 'https://app.pushwoosh.com/entrypoint/XXXXX-XXXXX?query1=0' \
73
+ -H 'accept: application/json, text/plain, */*' \
74
+ -H 'content-type: application/json' \
75
+ --data-raw '{"body1":true}' \
76
+ ```
77
+ Можно выполнить следующей конструкцией:
78
+ ```
79
+ try {
80
+ const response = httpClient.request<'POST', { param1: string }, { query1: number }, { body1: boolean }, { result1: string, result2: number, result3: boolean }>('/entrypoint/:param1', {
81
+ method: 'POST',
82
+ params: {
83
+ param1: 'XXXXX-XXXXX',
84
+ },
85
+ query: {
86
+ query1: 0,
87
+ },
88
+ body: {
89
+ body1: true,
90
+ },
91
+ withAuthorization: false,
92
+ withCredentials: false,
93
+ });
94
+ } catch (error: unknown) {
95
+ // TODO: error capture
96
+ }
97
+ ```
98
+
99
+ `method` - тип запроса (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`)
100
+
101
+ `params` - параметры, которые будут подставлены в шаблон `URL`
102
+
103
+ `query` - параметры, которые будут преобразованы к `Query String`
104
+
105
+ `body` - параметры, которые будут преобразованы к `Request Payload` (недоступен для `GET`)
106
+
107
+ `withAuthorization` - передавать заголовок `Authorization` в заголовке
108
+
109
+ `withCredentials` - передавать идентификационные данные в кросс доменных запросах
110
+
111
+ `response` - ответ от сервера:
112
+ ```typescript
113
+ type Response<R extends Result> = {
114
+ readonly result: R;
115
+ readonly status: number;
116
+ readonly message: string;
117
+ };
118
+ ```
119
+
120
+ #### GET, POST, PUT, UPDATE, DELETE запросы
121
+ Все запросы имеют упрощенную структуру с указанием типа запроса:
122
+
123
+ ```
124
+ curl 'https://app.pushwoosh.com/entrypoint' \
125
+ -H 'accept: application/json, text/plain, */*' \
126
+ -H 'authorization: Bearer SOME-ACCESS-TOKEN' \
127
+ ```
128
+ Можно выполнить следующей конструкцией:
129
+ ```typescript
130
+ try {
131
+ const result = httpClient.get<never, never, never>('/entrypoint', {
132
+ withAuthorization: true,
133
+ withCredentials: false,
134
+ });
135
+ } catch (error: unknown) {
136
+ // TODO: error capture
137
+ }
138
+ ```
139
+ В данном примере приходит сразу `result`, а если http запрос вернул `response.status` >= `400`, то произойдет выброс исключения.
140
+
141
+ ## Обработка ошибок:
142
+
143
+ ```typescript
144
+ import { ForbiddenError, HttpClient, NotFoundError, UnauthorizedError } from './http-client.errors';
145
+
146
+ try {
147
+ const result = httpClient.get<never, never, never>('/entrypoint', {
148
+ withAuthorization: true,
149
+ withCredentials: false,
150
+ });
151
+ } catch (error: unknown) {
152
+ if (error instanceof UnauthorizedError) {
153
+ // TODO: Если пользователь не авторизован...
154
+ }
155
+
156
+ if (error instanceof ForbiddenError) {
157
+ // TODO: Если пользователю запрещено выполнять запрос...
158
+ }
159
+
160
+ if (error instanceof NotFoundError) {
161
+ // TODO: Если сервер ничего не нашел...
162
+ }
163
+
164
+ // записываем ошибку в sentry
165
+ captureException(error);
166
+ }
167
+ ```
168
+
169
+ Каждая ошибка имеет свой класс:
170
+
171
+ #### Ошибки связанные с самим модулем:
172
+ `HttpClientError (extends Error)` - Класс описывает ошибку связанную с HttpClient
173
+
174
+ `InternalError (extends HttpClientError)` - Класс описывающий внутреннюю ошибку HttpClient
175
+
176
+ #### Ошибки связанные со всеми запросами:
177
+ `UnknownError (extends HttpClientError)` - Неизвестная ошибка HttpClient
178
+
179
+ #### Ошибки связанные с запросами:
180
+ `BadRequestError (extends HttpClientError)` - Класс описывающий ошибку "Плохой запрос"
181
+
182
+ `UnauthorizedError (extends HttpClientError)` - Класс описывающий ошибку "Не авторизовано"
183
+
184
+ `ForbiddenError (extends HttpClientError)` - Класс описывающий ошибку "Запрещено"
185
+
186
+ `NotFoundError (extends HttpClientError)` - Класс описывающий ошибку "Не найден"
187
+
188
+ `MethodNotAllowedError (extends HttpClientError)` - Класс описывающий ошибку "Метод не доступен"
189
+
190
+ `UnsupportedMediaTypeError (extends HttpClientError)` - Класс описывающий ошибку "Медиа формат не поддерживается сервером"
191
+
192
+ `UnprocessableEntityError (extends HttpClientError)` - Класс описывающий ошибку "Сервер не может обработать запрос"
193
+
194
+ #### Ошибки связанные с работой сервера:
195
+ `InternalServerError (extends HttpClientError)` - Класс описывающий ошибку "Внутренняя ошибка сервера"
196
+
197
+ `BadGatewayError (extends HttpClientError)` - Класс описывающий ошибку "Плохой шлюз"
198
+
199
+ `ServiceUnavailableError (extends HttpClientError)` - Класс описывающий ошибку "Сервис недоступен"
200
+
201
+ `GatewayTimeoutError (extends HttpClientError)` - Класс описывающий ошибку "Вышло время ожидания ответа"
@@ -0,0 +1,169 @@
1
+ import { Body, Config, CookieOptions, DeleteOptions, GetOptions, Introspect, Method, Options, Params, PatchOptions, PostOptions, PutOptions, Query, Response, Result } from './http-client.types';
2
+ export declare class HttpClient<I extends Introspect = Introspect> {
3
+ /**
4
+ * Имя куки в которой будет сохранена случайная строка для проверки ответа авторизации
5
+ */
6
+ private static STORIED_AUTHORIZATION_STATE_COOKIE_NAME;
7
+ /**
8
+ * Имя куки в которой будет сохранен текущий адрес страницы
9
+ */
10
+ private static STORIED_URL_COOKIE_NAME;
11
+ /**
12
+ * Параметры конфигурации
13
+ */
14
+ private readonly config;
15
+ /**
16
+ * Параметры куки
17
+ */
18
+ private readonly cookieOptions;
19
+ /**
20
+ * Токен доступа
21
+ */
22
+ private accessToken;
23
+ /**
24
+ * Токен обновления
25
+ */
26
+ private refreshToken;
27
+ /**
28
+ * Происходит ли сейчас обновление токенов
29
+ */
30
+ private isRefreshing;
31
+ /**
32
+ * Был ли установлен токен доступа вручную
33
+ */
34
+ private isAccessTokenSetManual;
35
+ constructor(config: Config, cookieOptions?: CookieOptions);
36
+ /**
37
+ * Разобрать ответ от сервера и вернуть результат выполнения запроса
38
+ *
39
+ * @param { Response<*> } response - ответ от сервера в формате Axios
40
+ */
41
+ static parseResponse<R extends Result>(response: Response<R>): R;
42
+ /**
43
+ * Проверить наличие авторизации
44
+ */
45
+ private guardAuthorization;
46
+ /**
47
+ * Обновить токены
48
+ * Для более подробной информации: RFC 6749 https://www.rfc-editor.org/rfc/rfc6749.html
49
+ *
50
+ * @param { UpdateTokensPairQuery } query - параметры запроса для обновления токенов
51
+ */
52
+ private updateTokensPair;
53
+ /**
54
+ * Обновить токены с помощью кода авторизации
55
+ * Для более подробной информации: RFC 6749 https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1.3
56
+ *
57
+ * @param { string } authorizationCode - код авторизации, полученный от сервера авторизации
58
+ */
59
+ private updateTokensPairByAuthorizationCode;
60
+ /**
61
+ * Обновить токены с помощью токена обновления
62
+ * Для более подробной информации: RFC 6749 https://www.rfc-editor.org/rfc/rfc6749.html#section-6
63
+ *
64
+ * @param { string } refreshToken - токен обновления, полученный от сервера авторизации
65
+ * @param { Introspect } introspect - содержимое токена доступа
66
+ */
67
+ private updateTokensPairByRefreshToken;
68
+ /**
69
+ * Инициализировать модуль, если передан authorizationCode и authorizationState будет выполнена попытка обновить токены
70
+ *
71
+ * @param { string } authorizationCode - код авторизации, полученный от сервера авторизации
72
+ * @param { string } authorizationState - уникальная строка, переданная клиентом серверу при авторизации, полученная от сервера авторизации
73
+ */
74
+ initialize(authorizationCode: string | null, authorizationState: string | null): Promise<I | null>;
75
+ /**
76
+ * Выполнить аутентификацию пользователя
77
+ */
78
+ login(): Promise<void>;
79
+ /**
80
+ * Завершить сессию
81
+ */
82
+ logout(): Promise<void>;
83
+ /**
84
+ * Временный костыль, чтобы установить токен доступа из сессии
85
+ */
86
+ setAccessToken(accessToken: string): void;
87
+ /**
88
+ * Получить токен доступа, если время жизни токена закончилось, будет осуществлена попытка обновить токен
89
+ */
90
+ getAccessToken(): Promise<string>;
91
+ /**
92
+ * Выполнить запрос на сервер
93
+ *
94
+ * Дженерик:
95
+ * Method - тип запроса
96
+ * Placeholders - параметры, которые будут подставлены в шаблон пути
97
+ * Query - параметры, которые будут преобразованы в строку запроса
98
+ * Body - тело запроса (never для GET)
99
+ * Result - ожидаемый результат выполнения запроса
100
+ *
101
+ * @param { string } url - адрес запроса
102
+ * @param { Options<Method, Params, Query, Body> } options - параметры запроса
103
+ */
104
+ request<M extends Method, P extends Params, Q extends Query, B extends Body, R extends Result>(url: string, options: Options<M, P, Q, B>): Promise<Response<R>>;
105
+ /**
106
+ * Выполнить GET запрос на сервер
107
+ *
108
+ * Дженерик:
109
+ * Params - параметры, которые будут подставлены в шаблон пути
110
+ * Query - параметры, которые будут преобразованы в строку запроса
111
+ * Result - ожидаемый результат выполнения запроса
112
+ *
113
+ * @param { string } url - адрес запроса
114
+ * @param { GetOptions<Params, Query> } options - параметры GET запроса
115
+ */
116
+ get<P extends Params, Q extends Query, R extends Result>(url: string, options: GetOptions<P, Q>): Promise<R>;
117
+ /**
118
+ * Выполнить POST запрос на сервер
119
+ *
120
+ * Дженерик:
121
+ * Params - параметры, которые будут подставлены в шаблон пути
122
+ * Query - параметры, которые будут преобразованы в строку запроса
123
+ * Body - тело запроса
124
+ * Result - ожидаемый результат выполнения запроса
125
+ *
126
+ * @param { string } url - адрес запроса
127
+ * @param { PostOptions<Params, Query, Body> } options - параметры POST запроса
128
+ */
129
+ post<P extends Params, Q extends Query, B extends Body, R extends Result>(url: string, options: PostOptions<P, Q, B>): Promise<R>;
130
+ /**
131
+ * Выполнить PUT запрос на сервер
132
+ *
133
+ * Дженерик:
134
+ * Params - параметры, которые будут подставлены в шаблон пути
135
+ * Query - параметры, которые будут преобразованы в строку запроса
136
+ * Body - тело запроса
137
+ * Result - ожидаемый результат выполнения запроса
138
+ *
139
+ * @param { string } url - адрес запроса
140
+ * @param { PutOptions<Params, Query, Body> } options - параметры PUT запроса
141
+ */
142
+ put<P extends Params, Q extends Query, B extends Body, R extends Result>(url: string, options: PutOptions<P, Q, B>): Promise<R>;
143
+ /**
144
+ * Выполнить PATCH запрос на сервер
145
+ *
146
+ * Дженерик:
147
+ * Params - параметры, которые будут подставлены в шаблон пути
148
+ * Query - параметры, которые будут преобразованы в строку запроса
149
+ * Body - тело запроса
150
+ * Result - ожидаемый результат выполнения запроса
151
+ *
152
+ * @param { string } url - адрес запроса
153
+ * @param { PatchOptions<Params, Query, Body> } options - параметры PATCH запроса
154
+ */
155
+ patch<P extends Params, Q extends Query, B extends Body, R extends Result>(url: string, options: PatchOptions<P, Q, B>): Promise<R>;
156
+ /**
157
+ * Выполнить DELETE запрос на сервер
158
+ *
159
+ * Дженерик:
160
+ * Params - параметры, которые будут подставлены в шаблон пути
161
+ * Query - параметры, которые будут преобразованы в строку запроса
162
+ * Body - тело запроса
163
+ * Result - ожидаемый результат выполнения запроса
164
+ *
165
+ * @param { string } url - адрес запроса
166
+ * @param { DeleteOptions<Params, Query, Body> } options - параметры DELETE запроса
167
+ */
168
+ delete<P extends Params, Q extends Query, B extends Body, R extends Result>(url: string, options: DeleteOptions<P, Q, B>): Promise<R>;
169
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Класс описывающий ошибку связанную с модулем HttpClient
3
+ */
4
+ export declare class HttpClientError extends Error {
5
+ readonly name: string;
6
+ /**
7
+ * Статус ошибки
8
+ */
9
+ readonly status: number;
10
+ /**
11
+ * Детали ошибки
12
+ */
13
+ readonly details: string | null;
14
+ constructor(message: string, status: number, details: string | null);
15
+ }
16
+ /**
17
+ * Класс описывающий внутреннюю ошибку связанную с модулем HttpClient
18
+ */
19
+ export declare class InternalError extends HttpClientError {
20
+ readonly name: string;
21
+ constructor(message: string, details: string | null);
22
+ }
23
+ /**
24
+ * Класс описывающий неизвестную ошибку
25
+ */
26
+ export declare class UnknownError extends HttpClientError {
27
+ constructor(code: number, details: string);
28
+ }
29
+ /**
30
+ * Класс описывающий ошибку "Плохой запрос"
31
+ * Это означает, что сервер не понимает запрос из-за неверного синтаксиса
32
+ */
33
+ export declare class BadRequestError extends HttpClientError {
34
+ constructor(details: string);
35
+ }
36
+ /**
37
+ * Класс описывающий ошибку "Не авторизовано"
38
+ * Это означает. что для получения запрашиваемого ответа нужна аутентификация
39
+ */
40
+ export declare class UnauthorizedError extends HttpClientError {
41
+ constructor(details: string);
42
+ }
43
+ /**
44
+ * Класс описывающий ошибку "Запрещено"
45
+ * Это означает, что у клиента нет прав доступа к содержимому
46
+ */
47
+ export declare class ForbiddenError extends HttpClientError {
48
+ constructor(details: string);
49
+ }
50
+ /**
51
+ * Класс описывающий ошибку "Не найден"
52
+ * Это означает, что сервер не может найти запрашиваемый ресурс
53
+ */
54
+ export declare class NotFoundError extends HttpClientError {
55
+ constructor(details: string);
56
+ }
57
+ /**
58
+ * Класс описывающий ошибку "Метод не доступен"
59
+ * Это означает, что серверу известен запрос, но он был отключен и не может быть использован
60
+ */
61
+ export declare class MethodNotAllowedError extends HttpClientError {
62
+ constructor(details: string);
63
+ }
64
+ /**
65
+ * Класс описывающий ошибку "Медиа формат не поддерживается сервером"
66
+ * Это означает, что сервер не поддерживает медиа формат запрашиваемых данных
67
+ */
68
+ export declare class UnsupportedMediaTypeError extends HttpClientError {
69
+ constructor(details: string);
70
+ }
71
+ /**
72
+ * Класс описывающий ошибку "Сервер не может обработать запрос"
73
+ * Это означает, что сервер понимает тип содержимого в теле запроса
74
+ * и синтаксис запроса является корректным, но серверу не удалось обработать инструкции содержимого
75
+ */
76
+ export declare class UnprocessableEntityError extends HttpClientError {
77
+ constructor(details: string);
78
+ }
79
+ /**
80
+ * Класс описывающий ошибку "Внутренняя ошибка сервера"
81
+ * Это означает, что сервер столкнулся с ситуацией, которую он не знает как обработать
82
+ */
83
+ export declare class InternalServerError extends HttpClientError {
84
+ constructor(details: string);
85
+ }
86
+ /**
87
+ * Класс описывающий ошибку "Плохой шлюз"
88
+ * Это означает, что сервер действует как шлюз шлюз и получил недопустимый ответ
89
+ */
90
+ export declare class BadGatewayError extends HttpClientError {
91
+ constructor(details: string);
92
+ }
93
+ /**
94
+ * Класс описывающий ошибку "Сервис недоступен"
95
+ * Это означает, что сервер не готов обрабатывать запрос
96
+ */
97
+ export declare class ServiceUnavailableError extends HttpClientError {
98
+ constructor(details: string);
99
+ }
100
+ /**
101
+ * Класс описывающий ошибку "Вышло время ожидания ответа"
102
+ * Это означает, что сервер действует как шлюз и не может получить ответ вовремя
103
+ */
104
+ export declare class GatewayTimeoutError extends HttpClientError {
105
+ constructor(details: string);
106
+ }
@@ -0,0 +1,71 @@
1
+ import { CookieAttributes } from 'js-cookie';
2
+ declare type Primitives = string | number | boolean | null | undefined;
3
+ export declare type Config = {
4
+ readonly clientId: string;
5
+ readonly urls: {
6
+ readonly authorize: string;
7
+ readonly token: string;
8
+ readonly logout: string;
9
+ };
10
+ };
11
+ export declare type CookieOptions = CookieAttributes;
12
+ export declare type GetMethod = 'GET';
13
+ export declare type PostMethod = 'POST';
14
+ export declare type PutMethod = 'PUT';
15
+ export declare type PatchMethod = 'PATCH';
16
+ export declare type DeleteMethod = 'DELETE';
17
+ export declare type Method = GetMethod | PutMethod | PostMethod | PatchMethod | DeleteMethod;
18
+ export declare type Params = Record<string, string | number> | never;
19
+ export declare type Query = Record<string, Primitives> | URLSearchParams | string | never;
20
+ export declare type Body = Record<string, unknown> | FormData | never;
21
+ export declare type Result = unknown | void;
22
+ declare type BaseRequestOptions<M extends Method> = {
23
+ readonly method: M;
24
+ readonly headers?: Record<string, string>;
25
+ readonly withAuthorization: boolean;
26
+ readonly withCredentials?: boolean;
27
+ };
28
+ export declare type Options<M extends Method, P extends Params, Q extends Query, B extends Body> = M extends GetMethod ? [P] extends [never] ? [Q] extends [never] ? BaseRequestOptions<GetMethod> : BaseRequestOptions<GetMethod> & {
29
+ query: Q;
30
+ } : [Q] extends [never] ? BaseRequestOptions<GetMethod> & {
31
+ params: P;
32
+ } : BaseRequestOptions<GetMethod> & {
33
+ params: P;
34
+ query: Q;
35
+ } : [P] extends [never] ? [Q] extends [never] ? [B] extends [never] ? BaseRequestOptions<Method> : BaseRequestOptions<Method> & {
36
+ body: B;
37
+ } : [B] extends [never] ? BaseRequestOptions<Method> & {
38
+ query: Q;
39
+ } : BaseRequestOptions<Method> & {
40
+ params: P;
41
+ body: B;
42
+ } : [Q] extends [never] ? [B] extends [never] ? BaseRequestOptions<Method> & {
43
+ params: P;
44
+ } : BaseRequestOptions<Method> & {
45
+ params: P;
46
+ body: B;
47
+ } : [B] extends [never] ? BaseRequestOptions<Method> & {
48
+ params: P;
49
+ query: Q;
50
+ } : BaseRequestOptions<Method> & {
51
+ params: P;
52
+ query: Q;
53
+ body: B;
54
+ };
55
+ export declare type GetOptions<P extends Params, Q extends Query> = Omit<Options<GetMethod, P, Q, never>, 'method'>;
56
+ export declare type PostOptions<P extends Params, Q extends Query, B extends Body> = Omit<Options<PostMethod, P, Q, B>, 'method'>;
57
+ export declare type PutOptions<P extends Params, Q extends Query, B extends Body> = Omit<Options<PutMethod, P, Q, B>, 'method'>;
58
+ export declare type PatchOptions<P extends Params, Q extends Query, B extends Body> = Omit<Options<PatchMethod, P, Q, B>, 'method'>;
59
+ export declare type DeleteOptions<P extends Params, Q extends Query, B extends Body> = Omit<Options<DeleteMethod, P, Q, B>, 'method'>;
60
+ export declare type Response<R extends Result> = {
61
+ readonly result: R;
62
+ readonly status: number;
63
+ readonly message: string;
64
+ };
65
+ export declare type Introspect = {
66
+ readonly user_id: string | number;
67
+ readonly username: string | number;
68
+ readonly account_id: string | number;
69
+ readonly expired_at: number;
70
+ };
71
+ export {};
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './http-client';
2
+ export * from './http-client.errors';
3
+ export * from './http-client.types';