bx-rest 1.1.0 → 1.2.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/CHANGELOG.md CHANGED
@@ -7,6 +7,27 @@
7
7
  История до версии 1.0.0 здесь не восстанавливалась — смотрите `git log` для
8
8
  более ранних изменений.
9
9
 
10
+ ## [1.2.0] - 2026-10-28
11
+
12
+ ### Fixed
13
+
14
+ - `.res()` терял код и описание ошибки Bitrix, если портал отвечал со
15
+ статусом не 2xx — а так Bitrix отдаёт большинство ошибок (`expired_token`
16
+ — 401, `QUERY_LIMIT_EXCEEDED` — 503, `Not found` у `crm.*.get` — 400).
17
+ Вместо `Error("AxiosError: Request failed with status code 400")` теперь
18
+ приходит `BXRestApiError` с `error` и `error_description` — с
19
+ `throwOnApiError` и без него. Поддерживается и формат ошибок REST v3
20
+ (`{ error: { code, message } }`). Сетевые ошибки и ответы не от Bitrix
21
+ (например, HTML-страница прокси) по-прежнему приходят обычным `Error`.
22
+
23
+ ### Added
24
+
25
+ - У `BXRestApiError` появились поля `status` (HTTP-статус; `undefined`, если
26
+ ошибка пришла в теле ответа 2xx), `method` (REST-метод) и `cause` (исходная
27
+ ошибка axios с полным телом ответа в `cause.response.data`). Конструктор
28
+ принимает их третьим необязательным аргументом — существующие вызовы
29
+ `new BXRestApiError(error, error_description)` работают как раньше.
30
+
10
31
  ## [1.1.0] - 2026-10-09
11
32
 
12
33
  ### Fixed
package/README.en.md CHANGED
@@ -156,16 +156,27 @@ BXRestSettings.update({
156
156
 
157
157
  ## Error handling
158
158
 
159
- `bx-rest` surfaces two different kinds of failure, and `.res()` only reacts to one of them.
159
+ `.res()` can fail in three ways — which one depends on how the portal answered.
160
160
 
161
- | Failure | When it happens | How it surfaces on `.res()` |
162
- | --- | --- | --- |
163
- | Transport / configuration errors — network failure, missing auth, missing base URL | Before or during the HTTP call | The returned `Observable` errors. Handle it with RxJS `catchError` or the `error` callback of `subscribe()`. |
164
- | Bitrix API errors — `{ error, error_description }`, e.g. `INSUFFICIENT_SCOPE`, `expired_token` | HTTP 200 response whose body is not a success payload | By default, `.res()` resolves normally with `undefined` — **it does not throw**. Pass `{ throwOnApiError: true }` to make it throw a `BXRestApiError` instead. |
161
+ | Failure | How it surfaces on `.res()` |
162
+ | --- | --- |
163
+ | Bitrix API error with a non-2xx HTTP status — this is how Bitrix sends most errors: `expired_token` (401), `ERROR_METHOD_NOT_FOUND` (404), `QUERY_LIMIT_EXCEEDED` (503), `Not found` from `crm.*.get` (400) | The returned `Observable` errors with a `BXRestApiError`, with or without `throwOnApiError`. |
164
+ | Bitrix API error in the body of a 2xx response | By default, `.res()` resolves normally with `undefined` — **it does not throw**. Pass `{ throwOnApiError: true }` to make it throw a `BXRestApiError` instead. |
165
+ | Transport / configuration errors — network failure, missing auth, missing base URL, a response that is not from Bitrix (e.g. a proxy's HTML error page) | The returned `Observable` errors with a plain `Error`. |
166
+
167
+ `BXRestApiError` fields:
168
+
169
+ | Field | Value |
170
+ | --- | --- |
171
+ | `error` | Bitrix error code, e.g. `expired_token`. Can be an empty string: `crm.*.get` answers a missing ID with an empty code and `Not found` in the description. |
172
+ | `error_description` | Bitrix error description; also used as `message`. |
173
+ | `status` | HTTP status, e.g. `401`; `undefined` when the error came in the body of a 2xx response. |
174
+ | `method` | REST method, e.g. `crm.deal.get`. |
175
+ | `cause` | The original axios error when the status was non-2xx; the full response body is in `cause.response.data` (for REST v3 — including `validation`). |
165
176
 
166
- Because a Bitrix-level error resolves as `undefined` rather than as an `Observable` error by default, don't rely on plain `.res()` alone to tell "no records" apart from "the call failed". You have two options.
177
+ For REST v3 methods, `error` holds the error `code` and `error_description` its `message`.
167
178
 
168
- **Option 1 — opt in to throwing with `.res({ throwOnApiError: true })`.** The `Observable` errors with a `BXRestApiError`, exposing `error` and `error_description`, so it can be handled alongside transport errors in a single `catchError`/`error` callback:
179
+ **Recommended — `.res({ throwOnApiError: true })`.** Every Bitrix error then arrives as a `BXRestApiError` whatever the HTTP status, and can be handled alongside transport errors in a single `catchError`/`error` callback:
169
180
 
170
181
  ```typescript
171
182
  import { BXRestNavvy, BXRestApiError } from 'bx-rest'
@@ -180,7 +191,10 @@ bxRest.crm.deal.list({
180
191
  next: deals => console.log(deals),
181
192
  error: err => {
182
193
  if (err instanceof BXRestApiError) {
183
- console.error('Bitrix API error:', err.error, err.error_description)
194
+ console.error('Bitrix API error:', err.status, err.error, err.error_description)
195
+ if (err.error === 'expired_token') {
196
+ // refresh the token and retry the request
197
+ }
184
198
  } else {
185
199
  console.error('Request failed:', err.message)
186
200
  }
@@ -188,7 +202,9 @@ bxRest.crm.deal.list({
188
202
  })
189
203
  ```
190
204
 
191
- **Option 2 — keep the default and check the raw response.** Call `.resVanilla()` instead and check the response with the exported `isBXRestAnswerSuccess()` type guard:
205
+ Without `throwOnApiError`, plain `.res()` can't tell "no records" apart from "an error in a 2xx response" — both resolve as `undefined`.
206
+
207
+ **Alternative — check the raw response.** `.resVanilla()` returns the raw Bitrix body for 2xx responses; check it with the exported `isBXRestAnswerSuccess()` type guard. Note that `.resVanilla()`, `.mapForVanilla()` and `.resAll()` don't convert errors: on a Bitrix error with a non-2xx status they fail with the original axios error, with the response body in `err.response.data`.
192
208
 
193
209
  ```typescript
194
210
  import { firstValueFrom } from 'rxjs'
@@ -211,19 +227,6 @@ if (!answer || !isBXRestAnswerSuccess(answer)) {
211
227
  }
212
228
  ```
213
229
 
214
- Regardless of `throwOnApiError`, transport-level failures always propagate as `Observable` errors and can be caught the usual RxJS way:
215
-
216
- ```typescript
217
- bxRest.crm.deal.list({
218
- select: ['ID', 'TITLE'],
219
- filter: {},
220
- start: 0
221
- }).res().subscribe({
222
- next: deals => console.log(deals),
223
- error: err => console.error('Request failed:', err.message)
224
- })
225
- ```
226
-
227
230
  ## Usage in Angular
228
231
 
229
232
  Register the public clients and `Navvy` when you want to inject them into Angular services or components:
package/README.md CHANGED
@@ -166,16 +166,27 @@ BXRestSettings.update({
166
166
 
167
167
  ## Обработка ошибок
168
168
 
169
- `bx-rest` показывает два разных вида сбоев, и `.res()` реагирует только на один из них.
169
+ `.res()` может завершиться неудачей тремя способами — какой именно, зависит от того, как ответил портал.
170
170
 
171
- | Сбой | Когда происходит | Как проявляется в `.res()` |
172
- | --- | --- | --- |
173
- | Ошибки транспорта / конфигурации — сбой сети, отсутствие авторизации, отсутствие базового URL | До или во время HTTP-вызова | Возвращённый `Observable` завершается ошибкой. Обрабатывайте её через RxJS `catchError` или колбэк `error` у `subscribe()`. |
174
- | Ошибки API Bitrix — `{ error, error_description }`, например `INSUFFICIENT_SCOPE`, `expired_token` | HTTP-ответ 200, тело которого не является успешным payload'ом | По умолчанию `.res()` резолвится обычным образом со значением `undefined` — **исключение не выбрасывается**. Передайте `{ throwOnApiError: true }`, чтобы вместо этого выбрасывалось `BXRestApiError`. |
171
+ | Сбой | Как проявляется в `.res()` |
172
+ | --- | --- |
173
+ | Ошибка API Bitrix с HTTP-статусом не 2xx — так Bitrix отдаёт большинство ошибок: `expired_token` (401), `ERROR_METHOD_NOT_FOUND` (404), `QUERY_LIMIT_EXCEEDED` (503), `Not found` у `crm.*.get` (400) | Возвращённый `Observable` завершается ошибкой `BXRestApiError` — с `throwOnApiError` и без него. |
174
+ | Ошибка API Bitrix в теле ответа со статусом 2xx | По умолчанию `.res()` резолвится обычным образом со значением `undefined` — **исключение не выбрасывается**. Передайте `{ throwOnApiError: true }`, чтобы вместо этого выбрасывалось `BXRestApiError`. |
175
+ | Ошибки транспорта / конфигурации — сбой сети, отсутствие авторизации, отсутствие базового URL, ответ не от Bitrix (например, HTML-страница ошибки прокси) | Возвращённый `Observable` завершается обычной ошибкой `Error`. |
176
+
177
+ Поля `BXRestApiError`:
178
+
179
+ | Поле | Значение |
180
+ | --- | --- |
181
+ | `error` | Код ошибки Bitrix, например `expired_token`. Может быть пустой строкой: на несуществующий ID `crm.*.get` отвечает пустым кодом и `Not found` в описании. |
182
+ | `error_description` | Описание ошибки Bitrix; оно же используется как `message`. |
183
+ | `status` | HTTP-статус, например `401`; `undefined`, если ошибка пришла в теле ответа со статусом 2xx. |
184
+ | `method` | REST-метод, например `crm.deal.get`. |
185
+ | `cause` | Исходная ошибка axios, если статус был не 2xx; полное тело ответа — в `cause.response.data` (для REST v3 — в том числе `validation`). |
175
186
 
176
- Поскольку ошибка уровня Bitrix по умолчанию резолвится как `undefined`, а не как ошибка `Observable`, не полагайтесь только на голый `.res()`, чтобы отличить «нет записей» от «запрос не удался». Есть два варианта.
187
+ Для методов REST v3 в `error` попадает `code` ошибки, а в `error_description` — её `message`.
177
188
 
178
- **Вариант 1 — включить выброс исключения через `.res({ throwOnApiError: true })`.** `Observable` завершается ошибкой `BXRestApiError`, предоставляющей `error` и `error_description`, поэтому её можно обработать вместе с транспортными ошибками в одном колбэке `catchError`/`error`:
189
+ **Рекомендуемый вариант — `.res({ throwOnApiError: true })`.** Тогда любая ошибка Bitrix приходит как `BXRestApiError` независимо от HTTP-статуса, и её можно обработать вместе с транспортными ошибками в одном колбэке `catchError`/`error`:
179
190
 
180
191
  ```typescript
181
192
  import { BXRestNavvy, BXRestApiError } from 'bx-rest'
@@ -190,7 +201,10 @@ bxRest.crm.deal.list({
190
201
  next: deals => console.log(deals),
191
202
  error: err => {
192
203
  if (err instanceof BXRestApiError) {
193
- console.error('Bitrix API error:', err.error, err.error_description)
204
+ console.error('Bitrix API error:', err.status, err.error, err.error_description)
205
+ if (err.error === 'expired_token') {
206
+ // обновить токен и повторить запрос
207
+ }
194
208
  } else {
195
209
  console.error('Request failed:', err.message)
196
210
  }
@@ -198,7 +212,9 @@ bxRest.crm.deal.list({
198
212
  })
199
213
  ```
200
214
 
201
- **Вариант 2 — оставить поведение по умолчанию и проверять сырой ответ.** Вызовите `.resVanilla()` и проверьте ответ с помощью экспортируемого type guard `isBXRestAnswerSuccess()`:
215
+ Без `throwOnApiError` по голому `.res()` нельзя отличить «нет записей» от «ошибки в ответе 2xx» — оба случая резолвятся как `undefined`.
216
+
217
+ **Альтернатива — проверять сырой ответ.** `.resVanilla()` возвращает сырое тело ответа Bitrix для ответов 2xx; проверяйте его экспортируемым type guard `isBXRestAnswerSuccess()`. Учтите, что `.resVanilla()`, `.mapForVanilla()` и `.resAll()` ошибки не преобразуют: на ошибку Bitrix со статусом не 2xx они завершаются исходной ошибкой axios, тело ответа — в `err.response.data`.
202
218
 
203
219
  ```typescript
204
220
  import { firstValueFrom } from 'rxjs'
@@ -221,19 +237,6 @@ if (!answer || !isBXRestAnswerSuccess(answer)) {
221
237
  }
222
238
  ```
223
239
 
224
- Независимо от `throwOnApiError`, ошибки транспортного уровня всегда всплывают как ошибки `Observable` и могут быть перехвачены обычным способом RxJS:
225
-
226
- ```typescript
227
- bxRest.crm.deal.list({
228
- select: ['ID', 'TITLE'],
229
- filter: {},
230
- start: 0
231
- }).res().subscribe({
232
- next: deals => console.log(deals),
233
- error: err => console.error('Request failed:', err.message)
234
- })
235
- ```
236
-
237
240
  ## Использование в Angular
238
241
 
239
242
  Зарегистрируйте публичные клиенты и `Navvy`, когда нужно внедрять их в сервисы или компоненты Angular:
@@ -1,17 +1,73 @@
1
+ import axios from 'axios';
1
2
  /**
2
- * Thrown by `.res()` when called with `{ throwOnApiError: true }` and the
3
- * Bitrix REST call resolves as a Bitrix-level API error, e.g.
4
- * `INSUFFICIENT_SCOPE` or `expired_token`.
3
+ * A Bitrix-level API error, e.g. `INSUFFICIENT_SCOPE` or `expired_token`.
5
4
  *
6
- * By default `.res()` resolves such responses as `undefined` instead of
7
- * throwing — see the "Error handling" section of the README.
5
+ * `.res()` fails with it whenever the portal answers with a Bitrix error and
6
+ * a non-2xx HTTP status — which is how Bitrix sends most of its errors. When
7
+ * the error comes in the body of a 2xx response, `.res()` resolves as
8
+ * `undefined` by default and fails with it only with
9
+ * `{ throwOnApiError: true }` — see the "Error handling" section of the README.
8
10
  */
9
11
  export class BXRestApiError extends Error {
10
- constructor(error, error_description) {
11
- super(error_description || error || 'Bitrix API error');
12
+ constructor(error, error_description, details) {
13
+ super(error_description || error || 'Bitrix API error', details?.cause !== undefined ? { cause: details.cause } : undefined);
12
14
  this.error = error;
13
15
  this.error_description = error_description;
14
16
  this.name = 'BXRestApiError';
17
+ this.status = details?.status;
18
+ this.method = details?.method;
15
19
  }
16
20
  }
17
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvYngtcmVzdC9zcmMvZnVuY3Rpb25zL2Vycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxPQUFPLGNBQWUsU0FBUSxLQUFLO0lBQ3ZDLFlBQ1MsS0FBeUIsRUFDekIsaUJBQXFDO1FBRTVDLEtBQUssQ0FBQyxpQkFBaUIsSUFBSSxLQUFLLElBQUksa0JBQWtCLENBQUMsQ0FBQTtRQUhoRCxVQUFLLEdBQUwsS0FBSyxDQUFvQjtRQUN6QixzQkFBaUIsR0FBakIsaUJBQWlCLENBQW9CO1FBRzVDLElBQUksQ0FBQyxJQUFJLEdBQUcsZ0JBQWdCLENBQUE7SUFDOUIsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUaHJvd24gYnkgYC5yZXMoKWAgd2hlbiBjYWxsZWQgd2l0aCBgeyB0aHJvd09uQXBpRXJyb3I6IHRydWUgfWAgYW5kIHRoZVxuICogQml0cml4IFJFU1QgY2FsbCByZXNvbHZlcyBhcyBhIEJpdHJpeC1sZXZlbCBBUEkgZXJyb3IsIGUuZy5cbiAqIGBJTlNVRkZJQ0lFTlRfU0NPUEVgIG9yIGBleHBpcmVkX3Rva2VuYC5cbiAqXG4gKiBCeSBkZWZhdWx0IGAucmVzKClgIHJlc29sdmVzIHN1Y2ggcmVzcG9uc2VzIGFzIGB1bmRlZmluZWRgIGluc3RlYWQgb2ZcbiAqIHRocm93aW5nIOKAlCBzZWUgdGhlIFwiRXJyb3IgaGFuZGxpbmdcIiBzZWN0aW9uIG9mIHRoZSBSRUFETUUuXG4gKi9cbmV4cG9ydCBjbGFzcyBCWFJlc3RBcGlFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgY29uc3RydWN0b3IoXG4gICAgcHVibGljIGVycm9yOiBzdHJpbmcgfCB1bmRlZmluZWQsXG4gICAgcHVibGljIGVycm9yX2Rlc2NyaXB0aW9uOiBzdHJpbmcgfCB1bmRlZmluZWRcbiAgKSB7XG4gICAgc3VwZXIoZXJyb3JfZGVzY3JpcHRpb24gfHwgZXJyb3IgfHwgJ0JpdHJpeCBBUEkgZXJyb3InKVxuICAgIHRoaXMubmFtZSA9ICdCWFJlc3RBcGlFcnJvcidcbiAgfVxufVxuIl19
21
+ /**
22
+ * Recognises a Bitrix error body: REST v2 `{ error, error_description }` and
23
+ * REST v3 `{ error: { code, message } }`. Returns `undefined` for anything
24
+ * else — a success payload, an HTML page from a proxy, an empty body.
25
+ *
26
+ * An empty `error` still counts: Bitrix answers e.g. `crm.deal.get` for a
27
+ * missing ID with `{ error: '', error_description: 'Not found' }`.
28
+ */
29
+ export function parseBXRestErrorBody(body) {
30
+ if (!body || typeof body !== 'object' || !('error' in body)) {
31
+ return undefined;
32
+ }
33
+ const { error, error_description } = body;
34
+ if (typeof error === 'string') {
35
+ return {
36
+ error,
37
+ error_description: typeof error_description === 'string' ? error_description : undefined
38
+ };
39
+ }
40
+ if (error && typeof error === 'object' && 'code' in error) {
41
+ const { code, message } = error;
42
+ return {
43
+ error: String(code),
44
+ error_description: typeof message === 'string' ? message : undefined
45
+ };
46
+ }
47
+ return undefined;
48
+ }
49
+ /**
50
+ * Turns an HTTP failure into a `BXRestApiError` when the portal answered with
51
+ * a Bitrix error body. Bitrix sends most errors with a non-2xx status
52
+ * (401 `expired_token`, 404 `ERROR_METHOD_NOT_FOUND`, 503
53
+ * `QUERY_LIMIT_EXCEEDED`, 400 "Not found" from `crm.*.get`, ...), and axios
54
+ * rejects on those.
55
+ *
56
+ * Returns `undefined` for network errors and for responses that are not a
57
+ * Bitrix error body, so the caller can keep its own handling for them.
58
+ */
59
+ export function bxRestApiErrorFromHttp(err, method) {
60
+ if (!axios.isAxiosError(err) || !err.response) {
61
+ return undefined;
62
+ }
63
+ const parsed = parseBXRestErrorBody(err.response.data);
64
+ if (!parsed) {
65
+ return undefined;
66
+ }
67
+ return new BXRestApiError(parsed.error, parsed.error_description, {
68
+ status: err.response.status,
69
+ method,
70
+ cause: err
71
+ });
72
+ }
73
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3JzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vcHJvamVjdHMvYngtcmVzdC9zcmMvZnVuY3Rpb25zL2Vycm9ycy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssTUFBTSxPQUFPLENBQUE7QUFpQnpCOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxPQUFPLGNBQWUsU0FBUSxLQUFLO0lBSXZDLFlBQ1MsS0FBeUIsRUFDekIsaUJBQXFDLEVBQzVDLE9BQStCO1FBRS9CLEtBQUssQ0FDSCxpQkFBaUIsSUFBSSxLQUFLLElBQUksa0JBQWtCLEVBQ2hELE9BQU8sRUFBRSxLQUFLLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FDcEUsQ0FBQTtRQVBNLFVBQUssR0FBTCxLQUFLLENBQW9CO1FBQ3pCLHNCQUFpQixHQUFqQixpQkFBaUIsQ0FBb0I7UUFPNUMsSUFBSSxDQUFDLElBQUksR0FBRyxnQkFBZ0IsQ0FBQTtRQUM1QixJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sRUFBRSxNQUFNLENBQUE7UUFDN0IsSUFBSSxDQUFDLE1BQU0sR0FBRyxPQUFPLEVBQUUsTUFBTSxDQUFBO0lBQy9CLENBQUM7Q0FDRjtBQUVEOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsb0JBQW9CLENBQUMsSUFBYTtJQUVoRCxJQUFJLENBQUMsSUFBSSxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxDQUFDLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDNUQsT0FBTyxTQUFTLENBQUE7SUFDbEIsQ0FBQztJQUNELE1BQU0sRUFBRSxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsR0FBRyxJQUF1RCxDQUFBO0lBRTVGLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDOUIsT0FBTztZQUNMLEtBQUs7WUFDTCxpQkFBaUIsRUFBRSxPQUFPLGlCQUFpQixLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxDQUFDLFNBQVM7U0FDekYsQ0FBQTtJQUNILENBQUM7SUFFRCxJQUFJLEtBQUssSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksTUFBTSxJQUFJLEtBQUssRUFBRSxDQUFDO1FBQzFELE1BQU0sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEdBQUcsS0FBNkMsQ0FBQTtRQUN2RSxPQUFPO1lBQ0wsS0FBSyxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUM7WUFDbkIsaUJBQWlCLEVBQUUsT0FBTyxPQUFPLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFNBQVM7U0FDckUsQ0FBQTtJQUNILENBQUM7SUFFRCxPQUFPLFNBQVMsQ0FBQTtBQUNsQixDQUFDO0FBRUQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxVQUFVLHNCQUFzQixDQUFDLEdBQVksRUFBRSxNQUFjO0lBQ2pFLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQzlDLE9BQU8sU0FBUyxDQUFBO0lBQ2xCLENBQUM7SUFDRCxNQUFNLE1BQU0sR0FBRyxvQkFBb0IsQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFBO0lBQ3RELElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE9BQU8sU0FBUyxDQUFBO0lBQ2xCLENBQUM7SUFDRCxPQUFPLElBQUksY0FBYyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLGlCQUFpQixFQUFFO1FBQ2hFLE1BQU0sRUFBRSxHQUFHLENBQUMsUUFBUSxDQUFDLE1BQU07UUFDM0IsTUFBTTtRQUNOLEtBQUssRUFBRSxHQUFHO0tBQ1gsQ0FBQyxDQUFBO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBheGlvcyBmcm9tICdheGlvcydcblxuZXhwb3J0IGludGVyZmFjZSBCWFJlc3RBcGlFcnJvckRldGFpbHMge1xuICAvKipcbiAgICogSFRUUCBzdGF0dXMgb2YgdGhlIHBvcnRhbCByZXNwb25zZS4gYHVuZGVmaW5lZGAgd2hlbiB0aGUgZXJyb3IgY2FtZSBpblxuICAgKiB0aGUgYm9keSBvZiBhIHN1Y2Nlc3NmdWwgKDJ4eCkgcmVzcG9uc2UuXG4gICAqL1xuICBzdGF0dXM/OiBudW1iZXJcbiAgLyoqIFJFU1QgbWV0aG9kIHRoYXQgZmFpbGVkLCBlLmcuIGBjcm0uZGVhbC5nZXRgLiAqL1xuICBtZXRob2Q/OiBzdHJpbmdcbiAgLyoqXG4gICAqIFRoZSBvcmlnaW5hbCBlcnJvciwgZS5nLiB0aGUgYEF4aW9zRXJyb3JgIOKAlCBpdHMgYHJlc3BvbnNlLmRhdGFgIGhvbGRzXG4gICAqIHRoZSBmdWxsIHJlc3BvbnNlIGJvZHkgKGluY2x1ZGluZyBSRVNUIHYzIGB2YWxpZGF0aW9uYCkuXG4gICAqL1xuICBjYXVzZT86IHVua25vd25cbn1cblxuLyoqXG4gKiBBIEJpdHJpeC1sZXZlbCBBUEkgZXJyb3IsIGUuZy4gYElOU1VGRklDSUVOVF9TQ09QRWAgb3IgYGV4cGlyZWRfdG9rZW5gLlxuICpcbiAqIGAucmVzKClgIGZhaWxzIHdpdGggaXQgd2hlbmV2ZXIgdGhlIHBvcnRhbCBhbnN3ZXJzIHdpdGggYSBCaXRyaXggZXJyb3IgYW5kXG4gKiBhIG5vbi0yeHggSFRUUCBzdGF0dXMg4oCUIHdoaWNoIGlzIGhvdyBCaXRyaXggc2VuZHMgbW9zdCBvZiBpdHMgZXJyb3JzLiBXaGVuXG4gKiB0aGUgZXJyb3IgY29tZXMgaW4gdGhlIGJvZHkgb2YgYSAyeHggcmVzcG9uc2UsIGAucmVzKClgIHJlc29sdmVzIGFzXG4gKiBgdW5kZWZpbmVkYCBieSBkZWZhdWx0IGFuZCBmYWlscyB3aXRoIGl0IG9ubHkgd2l0aFxuICogYHsgdGhyb3dPbkFwaUVycm9yOiB0cnVlIH1gIOKAlCBzZWUgdGhlIFwiRXJyb3IgaGFuZGxpbmdcIiBzZWN0aW9uIG9mIHRoZSBSRUFETUUuXG4gKi9cbmV4cG9ydCBjbGFzcyBCWFJlc3RBcGlFcnJvciBleHRlbmRzIEVycm9yIHtcbiAgcmVhZG9ubHkgc3RhdHVzOiBudW1iZXIgfCB1bmRlZmluZWRcbiAgcmVhZG9ubHkgbWV0aG9kOiBzdHJpbmcgfCB1bmRlZmluZWRcblxuICBjb25zdHJ1Y3RvcihcbiAgICBwdWJsaWMgZXJyb3I6IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICBwdWJsaWMgZXJyb3JfZGVzY3JpcHRpb246IHN0cmluZyB8IHVuZGVmaW5lZCxcbiAgICBkZXRhaWxzPzogQlhSZXN0QXBpRXJyb3JEZXRhaWxzXG4gICkge1xuICAgIHN1cGVyKFxuICAgICAgZXJyb3JfZGVzY3JpcHRpb24gfHwgZXJyb3IgfHwgJ0JpdHJpeCBBUEkgZXJyb3InLFxuICAgICAgZGV0YWlscz8uY2F1c2UgIT09IHVuZGVmaW5lZCA/IHsgY2F1c2U6IGRldGFpbHMuY2F1c2UgfSA6IHVuZGVmaW5lZFxuICAgIClcbiAgICB0aGlzLm5hbWUgPSAnQlhSZXN0QXBpRXJyb3InXG4gICAgdGhpcy5zdGF0dXMgPSBkZXRhaWxzPy5zdGF0dXNcbiAgICB0aGlzLm1ldGhvZCA9IGRldGFpbHM/Lm1ldGhvZFxuICB9XG59XG5cbi8qKlxuICogUmVjb2duaXNlcyBhIEJpdHJpeCBlcnJvciBib2R5OiBSRVNUIHYyIGB7IGVycm9yLCBlcnJvcl9kZXNjcmlwdGlvbiB9YCBhbmRcbiAqIFJFU1QgdjMgYHsgZXJyb3I6IHsgY29kZSwgbWVzc2FnZSB9IH1gLiBSZXR1cm5zIGB1bmRlZmluZWRgIGZvciBhbnl0aGluZ1xuICogZWxzZSDigJQgYSBzdWNjZXNzIHBheWxvYWQsIGFuIEhUTUwgcGFnZSBmcm9tIGEgcHJveHksIGFuIGVtcHR5IGJvZHkuXG4gKlxuICogQW4gZW1wdHkgYGVycm9yYCBzdGlsbCBjb3VudHM6IEJpdHJpeCBhbnN3ZXJzIGUuZy4gYGNybS5kZWFsLmdldGAgZm9yIGFcbiAqIG1pc3NpbmcgSUQgd2l0aCBgeyBlcnJvcjogJycsIGVycm9yX2Rlc2NyaXB0aW9uOiAnTm90IGZvdW5kJyB9YC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlQlhSZXN0RXJyb3JCb2R5KGJvZHk6IHVua25vd24pXG4gIDogeyBlcnJvcjogc3RyaW5nLCBlcnJvcl9kZXNjcmlwdGlvbjogc3RyaW5nIHwgdW5kZWZpbmVkIH0gfCB1bmRlZmluZWQge1xuICBpZiAoIWJvZHkgfHwgdHlwZW9mIGJvZHkgIT09ICdvYmplY3QnIHx8ICEoJ2Vycm9yJyBpbiBib2R5KSkge1xuICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxuICBjb25zdCB7IGVycm9yLCBlcnJvcl9kZXNjcmlwdGlvbiB9ID0gYm9keSBhcyB7IGVycm9yOiB1bmtub3duLCBlcnJvcl9kZXNjcmlwdGlvbj86IHVua25vd24gfVxuXG4gIGlmICh0eXBlb2YgZXJyb3IgPT09ICdzdHJpbmcnKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGVycm9yLFxuICAgICAgZXJyb3JfZGVzY3JpcHRpb246IHR5cGVvZiBlcnJvcl9kZXNjcmlwdGlvbiA9PT0gJ3N0cmluZycgPyBlcnJvcl9kZXNjcmlwdGlvbiA6IHVuZGVmaW5lZFxuICAgIH1cbiAgfVxuXG4gIGlmIChlcnJvciAmJiB0eXBlb2YgZXJyb3IgPT09ICdvYmplY3QnICYmICdjb2RlJyBpbiBlcnJvcikge1xuICAgIGNvbnN0IHsgY29kZSwgbWVzc2FnZSB9ID0gZXJyb3IgYXMgeyBjb2RlOiB1bmtub3duLCBtZXNzYWdlPzogdW5rbm93biB9XG4gICAgcmV0dXJuIHtcbiAgICAgIGVycm9yOiBTdHJpbmcoY29kZSksXG4gICAgICBlcnJvcl9kZXNjcmlwdGlvbjogdHlwZW9mIG1lc3NhZ2UgPT09ICdzdHJpbmcnID8gbWVzc2FnZSA6IHVuZGVmaW5lZFxuICAgIH1cbiAgfVxuXG4gIHJldHVybiB1bmRlZmluZWRcbn1cblxuLyoqXG4gKiBUdXJucyBhbiBIVFRQIGZhaWx1cmUgaW50byBhIGBCWFJlc3RBcGlFcnJvcmAgd2hlbiB0aGUgcG9ydGFsIGFuc3dlcmVkIHdpdGhcbiAqIGEgQml0cml4IGVycm9yIGJvZHkuIEJpdHJpeCBzZW5kcyBtb3N0IGVycm9ycyB3aXRoIGEgbm9uLTJ4eCBzdGF0dXNcbiAqICg0MDEgYGV4cGlyZWRfdG9rZW5gLCA0MDQgYEVSUk9SX01FVEhPRF9OT1RfRk9VTkRgLCA1MDNcbiAqIGBRVUVSWV9MSU1JVF9FWENFRURFRGAsIDQwMCBcIk5vdCBmb3VuZFwiIGZyb20gYGNybS4qLmdldGAsIC4uLiksIGFuZCBheGlvc1xuICogcmVqZWN0cyBvbiB0aG9zZS5cbiAqXG4gKiBSZXR1cm5zIGB1bmRlZmluZWRgIGZvciBuZXR3b3JrIGVycm9ycyBhbmQgZm9yIHJlc3BvbnNlcyB0aGF0IGFyZSBub3QgYVxuICogQml0cml4IGVycm9yIGJvZHksIHNvIHRoZSBjYWxsZXIgY2FuIGtlZXAgaXRzIG93biBoYW5kbGluZyBmb3IgdGhlbS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ4UmVzdEFwaUVycm9yRnJvbUh0dHAoZXJyOiB1bmtub3duLCBtZXRob2Q6IHN0cmluZyk6IEJYUmVzdEFwaUVycm9yIHwgdW5kZWZpbmVkIHtcbiAgaWYgKCFheGlvcy5pc0F4aW9zRXJyb3IoZXJyKSB8fCAhZXJyLnJlc3BvbnNlKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZFxuICB9XG4gIGNvbnN0IHBhcnNlZCA9IHBhcnNlQlhSZXN0RXJyb3JCb2R5KGVyci5yZXNwb25zZS5kYXRhKVxuICBpZiAoIXBhcnNlZCkge1xuICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxuICByZXR1cm4gbmV3IEJYUmVzdEFwaUVycm9yKHBhcnNlZC5lcnJvciwgcGFyc2VkLmVycm9yX2Rlc2NyaXB0aW9uLCB7XG4gICAgc3RhdHVzOiBlcnIucmVzcG9uc2Uuc3RhdHVzLFxuICAgIG1ldGhvZCxcbiAgICBjYXVzZTogZXJyXG4gIH0pXG59XG4iXX0=
@@ -1,7 +1,7 @@
1
1
  import { catchError, map } from 'rxjs/operators';
2
2
  import { BXRestMapResult, instanceOfiBXRestAnswerSuccess } from '../../../functions/mapResult';
3
3
  import { HttpBXServices } from '../../http/HttpBX';
4
- import { BXRestApiError } from '../../../functions/errors';
4
+ import { BXRestApiError, bxRestApiErrorFromHttp, parseBXRestErrorBody } from '../../../functions/errors';
5
5
  export class NavvyPagBase {
6
6
  constructor(url, param, map = undefined, restApi = false) {
7
7
  this.url = url;
@@ -31,9 +31,11 @@ export class NavvyPagBase {
31
31
  : v));
32
32
  }
33
33
  mapAndError(request, options) {
34
+ const method = this.http.getNameMethod(this.url);
34
35
  return request.pipe(map(v => {
35
36
  if (options?.throwOnApiError && v && !instanceOfiBXRestAnswerSuccess(v)) {
36
- throw new BXRestApiError(v.error, v.error_description);
37
+ const parsed = parseBXRestErrorBody(v);
38
+ throw new BXRestApiError(parsed ? parsed.error : v.error, parsed ? parsed.error_description : v.error_description, { method });
37
39
  }
38
40
  return BXRestMapResult(v);
39
41
  }), catchError((err) => {
@@ -41,8 +43,15 @@ export class NavvyPagBase {
41
43
  if (err instanceof BXRestApiError) {
42
44
  throw err;
43
45
  }
46
+ // Большинство ошибок Bitrix приходит со статусом не 2xx, и axios
47
+ // отклоняет такой ответ — достаём из него код и описание ошибки,
48
+ // а не отдаём безликое "AxiosError: Request failed with status code 400".
49
+ const apiError = bxRestApiErrorFromHttp(err, method);
50
+ if (apiError) {
51
+ throw apiError;
52
+ }
44
53
  throw new Error((err.error) ? err.error : err);
45
54
  }));
46
55
  }
47
56
  }
48
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmF2dnktcGFnLWJhc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9ieC1yZXN0L3NyYy9zZXJ2aWNlcy9uYXZ2eS9leHRlbmRzL25hdnZ5LXBhZy1iYXNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFDaEQsT0FBTyxFQUFFLGVBQWUsRUFBRSw4QkFBOEIsRUFBRSxNQUFNLDhCQUE4QixDQUFBO0FBQzlGLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUVsRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sMkJBQTJCLENBQUE7QUFjMUQsTUFBTSxPQUFnQixZQUFZO0lBS2hDLFlBQ1ksR0FBYSxFQUNiLEtBQVEsRUFDUixNQUE0RCxTQUFTLEVBQ3JFLFVBQVUsS0FBSztRQUhmLFFBQUcsR0FBSCxHQUFHLENBQVU7UUFDYixVQUFLLEdBQUwsS0FBSyxDQUFHO1FBQ1IsUUFBRyxHQUFILEdBQUcsQ0FBa0U7UUFDckUsWUFBTyxHQUFQLE9BQU8sQ0FBUTtRQU4zQixhQUFRLEdBQUcsRUFBRSxDQUFBLENBQUMsOEJBQThCO1FBUTFDLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxjQUFjLEVBQUUsQ0FBQTtJQUNsQyxDQUFDO0lBRUQsVUFBVTtRQUNSLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUM5RCxDQUFDO0lBRUQsYUFBYTtRQUNYLE9BQU8sSUFBSSxDQUFDLFVBQVUsRUFBRTthQUNyQixJQUFJLENBQ0gsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQ04sSUFBSSxDQUFDLElBQUksOEJBQThCLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO2dCQUN2RCxvREFBb0Q7Z0JBQ3BELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE1BQVcsQ0FBMEIsQ0FBQTtnQkFDL0QsT0FBTyxFQUFFLEdBQUcsQ0FBQyxFQUFFLEdBQUcsRUFBQyxNQUFNLEVBQUUsTUFBTSxFQUFDLEVBQTBDLENBQUE7WUFDOUUsQ0FBQztZQUNELE9BQU8sQ0FBcUQsQ0FBQTtRQUM5RCxDQUFDLENBQUMsQ0FDSCxDQUFBO0lBQ0wsQ0FBQztJQUVELEdBQUcsQ0FBQyxPQUEwQjtRQUM1QixPQUFPLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxFQUFFLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FDdEQsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQztZQUN0QixDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDYixDQUFDLENBQUMsQ0FBQyxDQUNKLENBQytDLENBQUE7SUFDcEQsQ0FBQztJQUVELFdBQVcsQ0FDVCxPQUFpRCxFQUNqRCxPQUEwQjtRQUUxQixPQUFPLE9BQU8sQ0FBQyxJQUFJLENBQ2pCLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRTtZQUNOLElBQUksT0FBTyxFQUFFLGVBQWUsSUFBSSxDQUFDLElBQUksQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUN4RSxNQUFNLElBQUksY0FBYyxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLGlCQUFpQixDQUFDLENBQUE7WUFDeEQsQ0FBQztZQUNELE9BQU8sZUFBZSxDQUFDLENBQUMsQ0FBQyxDQUFBO1FBQzNCLENBQUMsQ0FBQyxFQUNGLFVBQVUsQ0FBQyxDQUFDLEdBQVEsRUFBRSxFQUFFO1lBQ3RCLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUE7WUFDbEIsSUFBSSxHQUFHLFlBQVksY0FBYyxFQUFFLENBQUM7Z0JBQ2xDLE1BQU0sR0FBRyxDQUFBO1lBQ1gsQ0FBQztZQUNELE1BQU0sSUFBSSxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1FBQ2hELENBQUMsQ0FBQyxDQUNILENBQUE7SUFDSCxDQUFDO0NBRUYiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcydcclxuaW1wb3J0IHsgaUJYUmVzdEFuc3dlciB9IGZyb20gJy4uLy4uLy4uL3R5cGlmaWNhdGlvbi9yZXN0L2Jhc2UvYW5zd2VyJ1xyXG5pbXBvcnQgeyBjYXRjaEVycm9yLCBtYXAgfSBmcm9tICdyeGpzL29wZXJhdG9ycydcclxuaW1wb3J0IHsgQlhSZXN0TWFwUmVzdWx0LCBpbnN0YW5jZU9maUJYUmVzdEFuc3dlclN1Y2Nlc3MgfSBmcm9tICcuLi8uLi8uLi9mdW5jdGlvbnMvbWFwUmVzdWx0J1xyXG5pbXBvcnQgeyBIdHRwQlhTZXJ2aWNlcyB9IGZyb20gJy4uLy4uL2h0dHAvSHR0cEJYJ1xyXG5pbXBvcnQgeyBSZXR1cm5UeXBlTmF2dnkgfSBmcm9tICcuLi9uYXZ2eS1zdXBwb3J0J1xyXG5pbXBvcnQgeyBCWFJlc3RBcGlFcnJvciB9IGZyb20gJy4uLy4uLy4uL2Z1bmN0aW9ucy9lcnJvcnMnXHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIEJYUmVzdFJlc09wdGlvbnMge1xyXG4gIC8qKlxyXG4gICAqIFdoZW4gYHRydWVgLCBhIEJpdHJpeC1sZXZlbCBBUEkgZXJyb3IgKGB7IGVycm9yLCBlcnJvcl9kZXNjcmlwdGlvbiB9YCxcclxuICAgKiBlLmcuIGBJTlNVRkZJQ0lFTlRfU0NPUEVgLCBgZXhwaXJlZF90b2tlbmApIG1ha2VzIHRoZSBgT2JzZXJ2YWJsZWBcclxuICAgKiByZXR1cm5lZCBieSBgLnJlcygpYCBlcnJvciBvdXQgd2l0aCBhIGBCWFJlc3RBcGlFcnJvcmAgaW5zdGVhZCBvZlxyXG4gICAqIHJlc29sdmluZyB3aXRoIGB1bmRlZmluZWRgLlxyXG4gICAqXHJcbiAgICogRGVmYXVsdDogYGZhbHNlYCAocHJlc2VydmVzIHRoZSBoaXN0b3JpY2FsIGAucmVzKClgIGJlaGF2aW91cikuXHJcbiAgICovXHJcbiAgdGhyb3dPbkFwaUVycm9yPzogYm9vbGVhblxyXG59XHJcblxyXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgTmF2dnlQYWdCYXNlPFQsIFIsIFA+IHtcclxuXHJcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGh0dHA6IEh0dHBCWFNlcnZpY2VzXHJcbiAgcGFnZVNpemUgPSA1MCAvLyDRgdGC0LDQvdC00LDRgNGC0L3Ri9C5INGA0LDQt9C80LXRgCDRgdGC0YDQsNC90LjRhtGLXHJcblxyXG4gIHB1YmxpYyBjb25zdHJ1Y3RvcihcclxuICAgIHByb3RlY3RlZCB1cmw6IHN0cmluZ1tdLFxyXG4gICAgcHJvdGVjdGVkIHBhcmFtOiBQLFxyXG4gICAgcHJvdGVjdGVkIG1hcDogKChwYXJhbTogVHwgdW5kZWZpbmVkKSA9PiBSIHwgdW5kZWZpbmVkKSB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZCxcclxuICAgIHByb3RlY3RlZCByZXN0QXBpID0gZmFsc2VcclxuICApIHtcclxuICAgIHRoaXMuaHR0cCA9IG5ldyBIdHRwQlhTZXJ2aWNlcygpXHJcbiAgfVxyXG5cclxuICByZXNWYW5pbGxhKCkge1xyXG4gICAgcmV0dXJuIHRoaXMuaHR0cC5wb3N0PFQ+KHRoaXMudXJsLCB0aGlzLnBhcmFtLCB0aGlzLnJlc3RBcGkpXHJcbiAgfVxyXG5cclxuICBtYXBGb3JWYW5pbGxhKCk6IE9ic2VydmFibGU8aUJYUmVzdEFuc3dlcjxSZXR1cm5UeXBlTmF2dnk8VCwgUj4+IHwgdW5kZWZpbmVkPiB7XHJcbiAgICByZXR1cm4gdGhpcy5yZXNWYW5pbGxhKClcclxuICAgICAgLnBpcGUoXHJcbiAgICAgICAgbWFwKHYgPT4ge1xyXG4gICAgICAgICAgaWYgKHYgJiYgaW5zdGFuY2VPZmlCWFJlc3RBbnN3ZXJTdWNjZXNzKHYpICYmIHRoaXMubWFwKSB7XHJcbiAgICAgICAgICAgIC8vINCf0YDQuNC80LXQvdGP0LXQvCB0aGlzLm1hcCDQuCDRj9Cy0L3QviDQv9GA0LjQstC+0LTQuNC8INGC0LjQvyDRgNC10LfRg9C70YzRgtCw0YLQsFxyXG4gICAgICAgICAgICBjb25zdCByZXN1bHQgPSB0aGlzLm1hcCh2LnJlc3VsdCBhcyBUKSBhcyBSZXR1cm5UeXBlTmF2dnk8VCwgUj5cclxuICAgICAgICAgICAgcmV0dXJuIHsgLi4udiwgLi4ue3Jlc3VsdDogcmVzdWx0fSB9IGFzIGlCWFJlc3RBbnN3ZXI8UmV0dXJuVHlwZU5hdnZ5PFQsIFI+PlxyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIHYgYXMgaUJYUmVzdEFuc3dlcjxSZXR1cm5UeXBlTmF2dnk8VCwgUj4+IHwgdW5kZWZpbmVkXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gIH1cclxuXHJcbiAgcmVzKG9wdGlvbnM/OiBCWFJlc3RSZXNPcHRpb25zKSB7XHJcbiAgICByZXR1cm4gdGhpcy5tYXBBbmRFcnJvcih0aGlzLnJlc1ZhbmlsbGEoKSwgb3B0aW9ucykucGlwZShcclxuICAgICAgbWFwKHYgPT4gKHYgJiYgdGhpcy5tYXApXHJcbiAgICAgICAgPyB0aGlzLm1hcCh2KVxyXG4gICAgICAgIDogdlxyXG4gICAgICApXHJcbiAgICApIGFzIE9ic2VydmFibGU8UmV0dXJuVHlwZU5hdnZ5PFQsIFI+IHwgdW5kZWZpbmVkPlxyXG4gIH1cclxuXHJcbiAgbWFwQW5kRXJyb3I8VD4oXHJcbiAgICByZXF1ZXN0OiBPYnNlcnZhYmxlPGlCWFJlc3RBbnN3ZXI8VD4gfCB1bmRlZmluZWQ+LFxyXG4gICAgb3B0aW9ucz86IEJYUmVzdFJlc09wdGlvbnNcclxuICApIHtcclxuICAgIHJldHVybiByZXF1ZXN0LnBpcGUoXHJcbiAgICAgIG1hcCh2ID0+IHtcclxuICAgICAgICBpZiAob3B0aW9ucz8udGhyb3dPbkFwaUVycm9yICYmIHYgJiYgIWluc3RhbmNlT2ZpQlhSZXN0QW5zd2VyU3VjY2Vzcyh2KSkge1xyXG4gICAgICAgICAgdGhyb3cgbmV3IEJYUmVzdEFwaUVycm9yKHYuZXJyb3IsIHYuZXJyb3JfZGVzY3JpcHRpb24pXHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBCWFJlc3RNYXBSZXN1bHQodilcclxuICAgICAgfSksXHJcbiAgICAgIGNhdGNoRXJyb3IoKGVycjogYW55KSA9PiB7XHJcbiAgICAgICAgY29uc29sZS5lcnJvcihlcnIpXHJcbiAgICAgICAgaWYgKGVyciBpbnN0YW5jZW9mIEJYUmVzdEFwaUVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJcclxuICAgICAgICB9XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKChlcnIuZXJyb3IpID8gZXJyLmVycm9yIDogZXJyKVxyXG4gICAgICB9KVxyXG4gICAgKVxyXG4gIH1cclxuXHJcbn1cclxuIl19
57
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmF2dnktcGFnLWJhc2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9ieC1yZXN0L3NyYy9zZXJ2aWNlcy9uYXZ2eS9leHRlbmRzL25hdnZ5LXBhZy1iYXNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFDaEQsT0FBTyxFQUFFLGVBQWUsRUFBRSw4QkFBOEIsRUFBRSxNQUFNLDhCQUE4QixDQUFBO0FBQzlGLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUVsRCxPQUFPLEVBQUUsY0FBYyxFQUFFLHNCQUFzQixFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkJBQTJCLENBQUE7QUFpQnhHLE1BQU0sT0FBZ0IsWUFBWTtJQUtoQyxZQUNZLEdBQWEsRUFDYixLQUFRLEVBQ1IsTUFBNEQsU0FBUyxFQUNyRSxVQUFVLEtBQUs7UUFIZixRQUFHLEdBQUgsR0FBRyxDQUFVO1FBQ2IsVUFBSyxHQUFMLEtBQUssQ0FBRztRQUNSLFFBQUcsR0FBSCxHQUFHLENBQWtFO1FBQ3JFLFlBQU8sR0FBUCxPQUFPLENBQVE7UUFOM0IsYUFBUSxHQUFHLEVBQUUsQ0FBQSxDQUFDLDhCQUE4QjtRQVExQyxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksY0FBYyxFQUFFLENBQUE7SUFDbEMsQ0FBQztJQUVELFVBQVU7UUFDUixPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDOUQsQ0FBQztJQUVELGFBQWE7UUFDWCxPQUFPLElBQUksQ0FBQyxVQUFVLEVBQUU7YUFDckIsSUFBSSxDQUNILEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRTtZQUNOLElBQUksQ0FBQyxJQUFJLDhCQUE4QixDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztnQkFDdkQsb0RBQW9EO2dCQUNwRCxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxNQUFXLENBQTBCLENBQUE7Z0JBQy9ELE9BQU8sRUFBRSxHQUFHLENBQUMsRUFBRSxHQUFHLEVBQUMsTUFBTSxFQUFFLE1BQU0sRUFBQyxFQUEwQyxDQUFBO1lBQzlFLENBQUM7WUFDRCxPQUFPLENBQXFELENBQUE7UUFDOUQsQ0FBQyxDQUFDLENBQ0gsQ0FBQTtJQUNMLENBQUM7SUFFRCxHQUFHLENBQUMsT0FBMEI7UUFDNUIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQ3RELEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUM7WUFDdEIsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQ2IsQ0FBQyxDQUFDLENBQUMsQ0FDSixDQUMrQyxDQUFBO0lBQ3BELENBQUM7SUFFRCxXQUFXLENBQ1QsT0FBaUQsRUFDakQsT0FBMEI7UUFFMUIsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFBO1FBQ2hELE9BQU8sT0FBTyxDQUFDLElBQUksQ0FDakIsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFO1lBQ04sSUFBSSxPQUFPLEVBQUUsZUFBZSxJQUFJLENBQUMsSUFBSSxDQUFDLDhCQUE4QixDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQ3hFLE1BQU0sTUFBTSxHQUFHLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxDQUFBO2dCQUN0QyxNQUFNLElBQUksY0FBYyxDQUN0QixNQUFNLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLEVBQy9CLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsaUJBQWlCLEVBQ3ZELEVBQUUsTUFBTSxFQUFFLENBQ1gsQ0FBQTtZQUNILENBQUM7WUFDRCxPQUFPLGVBQWUsQ0FBQyxDQUFDLENBQUMsQ0FBQTtRQUMzQixDQUFDLENBQUMsRUFDRixVQUFVLENBQUMsQ0FBQyxHQUFRLEVBQUUsRUFBRTtZQUN0QixPQUFPLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFBO1lBQ2xCLElBQUksR0FBRyxZQUFZLGNBQWMsRUFBRSxDQUFDO2dCQUNsQyxNQUFNLEdBQUcsQ0FBQTtZQUNYLENBQUM7WUFDRCxpRUFBaUU7WUFDakUsaUVBQWlFO1lBQ2pFLDBFQUEwRTtZQUMxRSxNQUFNLFFBQVEsR0FBRyxzQkFBc0IsQ0FBQyxHQUFHLEVBQUUsTUFBTSxDQUFDLENBQUE7WUFDcEQsSUFBSSxRQUFRLEVBQUUsQ0FBQztnQkFDYixNQUFNLFFBQVEsQ0FBQTtZQUNoQixDQUFDO1lBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUE7UUFDaEQsQ0FBQyxDQUFDLENBQ0gsQ0FBQTtJQUNILENBQUM7Q0FFRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IE9ic2VydmFibGUgfSBmcm9tICdyeGpzJ1xyXG5pbXBvcnQgeyBpQlhSZXN0QW5zd2VyIH0gZnJvbSAnLi4vLi4vLi4vdHlwaWZpY2F0aW9uL3Jlc3QvYmFzZS9hbnN3ZXInXHJcbmltcG9ydCB7IGNhdGNoRXJyb3IsIG1hcCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJ1xyXG5pbXBvcnQgeyBCWFJlc3RNYXBSZXN1bHQsIGluc3RhbmNlT2ZpQlhSZXN0QW5zd2VyU3VjY2VzcyB9IGZyb20gJy4uLy4uLy4uL2Z1bmN0aW9ucy9tYXBSZXN1bHQnXHJcbmltcG9ydCB7IEh0dHBCWFNlcnZpY2VzIH0gZnJvbSAnLi4vLi4vaHR0cC9IdHRwQlgnXHJcbmltcG9ydCB7IFJldHVyblR5cGVOYXZ2eSB9IGZyb20gJy4uL25hdnZ5LXN1cHBvcnQnXHJcbmltcG9ydCB7IEJYUmVzdEFwaUVycm9yLCBieFJlc3RBcGlFcnJvckZyb21IdHRwLCBwYXJzZUJYUmVzdEVycm9yQm9keSB9IGZyb20gJy4uLy4uLy4uL2Z1bmN0aW9ucy9lcnJvcnMnXHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIEJYUmVzdFJlc09wdGlvbnMge1xyXG4gIC8qKlxyXG4gICAqIFdoZW4gYHRydWVgLCBhIEJpdHJpeC1sZXZlbCBBUEkgZXJyb3IgdGhhdCBjb21lcyBpbiB0aGUgYm9keSBvZiBhIDJ4eFxyXG4gICAqIHJlc3BvbnNlIG1ha2VzIHRoZSBgT2JzZXJ2YWJsZWAgcmV0dXJuZWQgYnkgYC5yZXMoKWAgZXJyb3Igb3V0IHdpdGggYVxyXG4gICAqIGBCWFJlc3RBcGlFcnJvcmAgaW5zdGVhZCBvZiByZXNvbHZpbmcgd2l0aCBgdW5kZWZpbmVkYC5cclxuICAgKlxyXG4gICAqIEVycm9ycyB0aGF0IEJpdHJpeCBzZW5kcyB3aXRoIGEgbm9uLTJ4eCBIVFRQIHN0YXR1cyAobW9zdCBvZiB0aGVtLCBlLmcuXHJcbiAgICogYGV4cGlyZWRfdG9rZW5gLCBgSU5TVUZGSUNJRU5UX1NDT1BFYCkgZmFpbCB3aXRoIGBCWFJlc3RBcGlFcnJvcmBcclxuICAgKiByZWdhcmRsZXNzIG9mIHRoaXMgb3B0aW9uLlxyXG4gICAqXHJcbiAgICogRGVmYXVsdDogYGZhbHNlYCAocHJlc2VydmVzIHRoZSBoaXN0b3JpY2FsIGAucmVzKClgIGJlaGF2aW91cikuXHJcbiAgICovXHJcbiAgdGhyb3dPbkFwaUVycm9yPzogYm9vbGVhblxyXG59XHJcblxyXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgTmF2dnlQYWdCYXNlPFQsIFIsIFA+IHtcclxuXHJcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGh0dHA6IEh0dHBCWFNlcnZpY2VzXHJcbiAgcGFnZVNpemUgPSA1MCAvLyDRgdGC0LDQvdC00LDRgNGC0L3Ri9C5INGA0LDQt9C80LXRgCDRgdGC0YDQsNC90LjRhtGLXHJcblxyXG4gIHB1YmxpYyBjb25zdHJ1Y3RvcihcclxuICAgIHByb3RlY3RlZCB1cmw6IHN0cmluZ1tdLFxyXG4gICAgcHJvdGVjdGVkIHBhcmFtOiBQLFxyXG4gICAgcHJvdGVjdGVkIG1hcDogKChwYXJhbTogVHwgdW5kZWZpbmVkKSA9PiBSIHwgdW5kZWZpbmVkKSB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZCxcclxuICAgIHByb3RlY3RlZCByZXN0QXBpID0gZmFsc2VcclxuICApIHtcclxuICAgIHRoaXMuaHR0cCA9IG5ldyBIdHRwQlhTZXJ2aWNlcygpXHJcbiAgfVxyXG5cclxuICByZXNWYW5pbGxhKCkge1xyXG4gICAgcmV0dXJuIHRoaXMuaHR0cC5wb3N0PFQ+KHRoaXMudXJsLCB0aGlzLnBhcmFtLCB0aGlzLnJlc3RBcGkpXHJcbiAgfVxyXG5cclxuICBtYXBGb3JWYW5pbGxhKCk6IE9ic2VydmFibGU8aUJYUmVzdEFuc3dlcjxSZXR1cm5UeXBlTmF2dnk8VCwgUj4+IHwgdW5kZWZpbmVkPiB7XHJcbiAgICByZXR1cm4gdGhpcy5yZXNWYW5pbGxhKClcclxuICAgICAgLnBpcGUoXHJcbiAgICAgICAgbWFwKHYgPT4ge1xyXG4gICAgICAgICAgaWYgKHYgJiYgaW5zdGFuY2VPZmlCWFJlc3RBbnN3ZXJTdWNjZXNzKHYpICYmIHRoaXMubWFwKSB7XHJcbiAgICAgICAgICAgIC8vINCf0YDQuNC80LXQvdGP0LXQvCB0aGlzLm1hcCDQuCDRj9Cy0L3QviDQv9GA0LjQstC+0LTQuNC8INGC0LjQvyDRgNC10LfRg9C70YzRgtCw0YLQsFxyXG4gICAgICAgICAgICBjb25zdCByZXN1bHQgPSB0aGlzLm1hcCh2LnJlc3VsdCBhcyBUKSBhcyBSZXR1cm5UeXBlTmF2dnk8VCwgUj5cclxuICAgICAgICAgICAgcmV0dXJuIHsgLi4udiwgLi4ue3Jlc3VsdDogcmVzdWx0fSB9IGFzIGlCWFJlc3RBbnN3ZXI8UmV0dXJuVHlwZU5hdnZ5PFQsIFI+PlxyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgcmV0dXJuIHYgYXMgaUJYUmVzdEFuc3dlcjxSZXR1cm5UeXBlTmF2dnk8VCwgUj4+IHwgdW5kZWZpbmVkXHJcbiAgICAgICAgfSlcclxuICAgICAgKVxyXG4gIH1cclxuXHJcbiAgcmVzKG9wdGlvbnM/OiBCWFJlc3RSZXNPcHRpb25zKSB7XHJcbiAgICByZXR1cm4gdGhpcy5tYXBBbmRFcnJvcih0aGlzLnJlc1ZhbmlsbGEoKSwgb3B0aW9ucykucGlwZShcclxuICAgICAgbWFwKHYgPT4gKHYgJiYgdGhpcy5tYXApXHJcbiAgICAgICAgPyB0aGlzLm1hcCh2KVxyXG4gICAgICAgIDogdlxyXG4gICAgICApXHJcbiAgICApIGFzIE9ic2VydmFibGU8UmV0dXJuVHlwZU5hdnZ5PFQsIFI+IHwgdW5kZWZpbmVkPlxyXG4gIH1cclxuXHJcbiAgbWFwQW5kRXJyb3I8VD4oXHJcbiAgICByZXF1ZXN0OiBPYnNlcnZhYmxlPGlCWFJlc3RBbnN3ZXI8VD4gfCB1bmRlZmluZWQ+LFxyXG4gICAgb3B0aW9ucz86IEJYUmVzdFJlc09wdGlvbnNcclxuICApIHtcclxuICAgIGNvbnN0IG1ldGhvZCA9IHRoaXMuaHR0cC5nZXROYW1lTWV0aG9kKHRoaXMudXJsKVxyXG4gICAgcmV0dXJuIHJlcXVlc3QucGlwZShcclxuICAgICAgbWFwKHYgPT4ge1xyXG4gICAgICAgIGlmIChvcHRpb25zPy50aHJvd09uQXBpRXJyb3IgJiYgdiAmJiAhaW5zdGFuY2VPZmlCWFJlc3RBbnN3ZXJTdWNjZXNzKHYpKSB7XHJcbiAgICAgICAgICBjb25zdCBwYXJzZWQgPSBwYXJzZUJYUmVzdEVycm9yQm9keSh2KVxyXG4gICAgICAgICAgdGhyb3cgbmV3IEJYUmVzdEFwaUVycm9yKFxyXG4gICAgICAgICAgICBwYXJzZWQgPyBwYXJzZWQuZXJyb3IgOiB2LmVycm9yLFxyXG4gICAgICAgICAgICBwYXJzZWQgPyBwYXJzZWQuZXJyb3JfZGVzY3JpcHRpb24gOiB2LmVycm9yX2Rlc2NyaXB0aW9uLFxyXG4gICAgICAgICAgICB7IG1ldGhvZCB9XHJcbiAgICAgICAgICApXHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBCWFJlc3RNYXBSZXN1bHQodilcclxuICAgICAgfSksXHJcbiAgICAgIGNhdGNoRXJyb3IoKGVycjogYW55KSA9PiB7XHJcbiAgICAgICAgY29uc29sZS5lcnJvcihlcnIpXHJcbiAgICAgICAgaWYgKGVyciBpbnN0YW5jZW9mIEJYUmVzdEFwaUVycm9yKSB7XHJcbiAgICAgICAgICB0aHJvdyBlcnJcclxuICAgICAgICB9XHJcbiAgICAgICAgLy8g0JHQvtC70YzRiNC40L3RgdGC0LLQviDQvtGI0LjQsdC+0LogQml0cml4INC/0YDQuNGF0L7QtNC40YIg0YHQviDRgdGC0LDRgtGD0YHQvtC8INC90LUgMnh4LCDQuCBheGlvc1xyXG4gICAgICAgIC8vINC+0YLQutC70L7QvdGP0LXRgiDRgtCw0LrQvtC5INC+0YLQstC10YIg4oCUINC00L7RgdGC0LDRkdC8INC40Lcg0L3QtdCz0L4g0LrQvtC0INC4INC+0L/QuNGB0LDQvdC40LUg0L7RiNC40LHQutC4LFxyXG4gICAgICAgIC8vINCwINC90LUg0L7RgtC00LDRkdC8INCx0LXQt9C70LjQutC+0LUgXCJBeGlvc0Vycm9yOiBSZXF1ZXN0IGZhaWxlZCB3aXRoIHN0YXR1cyBjb2RlIDQwMFwiLlxyXG4gICAgICAgIGNvbnN0IGFwaUVycm9yID0gYnhSZXN0QXBpRXJyb3JGcm9tSHR0cChlcnIsIG1ldGhvZClcclxuICAgICAgICBpZiAoYXBpRXJyb3IpIHtcclxuICAgICAgICAgIHRocm93IGFwaUVycm9yXHJcbiAgICAgICAgfVxyXG4gICAgICAgIHRocm93IG5ldyBFcnJvcigoZXJyLmVycm9yKSA/IGVyci5lcnJvciA6IGVycilcclxuICAgICAgfSlcclxuICAgIClcclxuICB9XHJcblxyXG59XHJcbiJdfQ==
@@ -1059,21 +1059,76 @@ class HttpBXServices extends HttpServices {
1059
1059
  }
1060
1060
 
1061
1061
  /**
1062
- * Thrown by `.res()` when called with `{ throwOnApiError: true }` and the
1063
- * Bitrix REST call resolves as a Bitrix-level API error, e.g.
1064
- * `INSUFFICIENT_SCOPE` or `expired_token`.
1062
+ * A Bitrix-level API error, e.g. `INSUFFICIENT_SCOPE` or `expired_token`.
1065
1063
  *
1066
- * By default `.res()` resolves such responses as `undefined` instead of
1067
- * throwing — see the "Error handling" section of the README.
1064
+ * `.res()` fails with it whenever the portal answers with a Bitrix error and
1065
+ * a non-2xx HTTP status — which is how Bitrix sends most of its errors. When
1066
+ * the error comes in the body of a 2xx response, `.res()` resolves as
1067
+ * `undefined` by default and fails with it only with
1068
+ * `{ throwOnApiError: true }` — see the "Error handling" section of the README.
1068
1069
  */
1069
1070
  class BXRestApiError extends Error {
1070
- constructor(error, error_description) {
1071
- super(error_description || error || 'Bitrix API error');
1071
+ constructor(error, error_description, details) {
1072
+ super(error_description || error || 'Bitrix API error', details?.cause !== undefined ? { cause: details.cause } : undefined);
1072
1073
  this.error = error;
1073
1074
  this.error_description = error_description;
1074
1075
  this.name = 'BXRestApiError';
1076
+ this.status = details?.status;
1077
+ this.method = details?.method;
1075
1078
  }
1076
1079
  }
1080
+ /**
1081
+ * Recognises a Bitrix error body: REST v2 `{ error, error_description }` and
1082
+ * REST v3 `{ error: { code, message } }`. Returns `undefined` for anything
1083
+ * else — a success payload, an HTML page from a proxy, an empty body.
1084
+ *
1085
+ * An empty `error` still counts: Bitrix answers e.g. `crm.deal.get` for a
1086
+ * missing ID with `{ error: '', error_description: 'Not found' }`.
1087
+ */
1088
+ function parseBXRestErrorBody(body) {
1089
+ if (!body || typeof body !== 'object' || !('error' in body)) {
1090
+ return undefined;
1091
+ }
1092
+ const { error, error_description } = body;
1093
+ if (typeof error === 'string') {
1094
+ return {
1095
+ error,
1096
+ error_description: typeof error_description === 'string' ? error_description : undefined
1097
+ };
1098
+ }
1099
+ if (error && typeof error === 'object' && 'code' in error) {
1100
+ const { code, message } = error;
1101
+ return {
1102
+ error: String(code),
1103
+ error_description: typeof message === 'string' ? message : undefined
1104
+ };
1105
+ }
1106
+ return undefined;
1107
+ }
1108
+ /**
1109
+ * Turns an HTTP failure into a `BXRestApiError` when the portal answered with
1110
+ * a Bitrix error body. Bitrix sends most errors with a non-2xx status
1111
+ * (401 `expired_token`, 404 `ERROR_METHOD_NOT_FOUND`, 503
1112
+ * `QUERY_LIMIT_EXCEEDED`, 400 "Not found" from `crm.*.get`, ...), and axios
1113
+ * rejects on those.
1114
+ *
1115
+ * Returns `undefined` for network errors and for responses that are not a
1116
+ * Bitrix error body, so the caller can keep its own handling for them.
1117
+ */
1118
+ function bxRestApiErrorFromHttp(err, method) {
1119
+ if (!axios.isAxiosError(err) || !err.response) {
1120
+ return undefined;
1121
+ }
1122
+ const parsed = parseBXRestErrorBody(err.response.data);
1123
+ if (!parsed) {
1124
+ return undefined;
1125
+ }
1126
+ return new BXRestApiError(parsed.error, parsed.error_description, {
1127
+ status: err.response.status,
1128
+ method,
1129
+ cause: err
1130
+ });
1131
+ }
1077
1132
 
1078
1133
  class NavvyPagBase {
1079
1134
  constructor(url, param, map = undefined, restApi = false) {
@@ -1104,9 +1159,11 @@ class NavvyPagBase {
1104
1159
  : v));
1105
1160
  }
1106
1161
  mapAndError(request, options) {
1162
+ const method = this.http.getNameMethod(this.url);
1107
1163
  return request.pipe(map(v => {
1108
1164
  if (options?.throwOnApiError && v && !instanceOfiBXRestAnswerSuccess(v)) {
1109
- throw new BXRestApiError(v.error, v.error_description);
1165
+ const parsed = parseBXRestErrorBody(v);
1166
+ throw new BXRestApiError(parsed ? parsed.error : v.error, parsed ? parsed.error_description : v.error_description, { method });
1110
1167
  }
1111
1168
  return BXRestMapResult(v);
1112
1169
  }), catchError((err) => {
@@ -1114,6 +1171,13 @@ class NavvyPagBase {
1114
1171
  if (err instanceof BXRestApiError) {
1115
1172
  throw err;
1116
1173
  }
1174
+ // Большинство ошибок Bitrix приходит со статусом не 2xx, и axios
1175
+ // отклоняет такой ответ — достаём из него код и описание ошибки,
1176
+ // а не отдаём безликое "AxiosError: Request failed with status code 400".
1177
+ const apiError = bxRestApiErrorFromHttp(err, method);
1178
+ if (apiError) {
1179
+ throw apiError;
1180
+ }
1117
1181
  throw new Error((err.error) ? err.error : err);
1118
1182
  }));
1119
1183
  }