bx-rest 1.5.0 → 1.6.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 +177 -0
- package/README.en.md +116 -5
- package/README.md +117 -5
- package/esm2022/functions/errors.mjs +17 -5
- package/esm2022/functions/mapResult.mjs +4 -2
- package/esm2022/functions/uniqueElementCode.mjs +24 -0
- package/esm2022/map/disk.mjs +13 -0
- package/esm2022/map/im/recent.mjs +1 -9
- package/esm2022/map/task.mjs +11 -5
- package/esm2022/map/tasks/task.mjs +3 -3
- package/esm2022/map/tasks.mjs +4 -3
- package/esm2022/map/user.mjs +5 -4
- package/esm2022/public-api.mjs +2 -2
- package/esm2022/request-navvy/bizproc.mjs +19 -10
- package/esm2022/request-navvy/calendar/event.mjs +1 -9
- package/esm2022/request-navvy/disk/operation.mjs +3 -10
- package/esm2022/request-navvy/pull/config.mjs +1 -3
- package/esm2022/request-navvy/socialnetwork/api/livefeed.mjs +1 -5
- package/esm2022/request-navvy/task/operation/elapseditem.mjs +14 -71
- package/esm2022/request-navvy/tasks/task.mjs +1 -119
- package/esm2022/rest/map.mjs +3 -3
- package/esm2022/rest/navvy.mjs +66 -64
- package/esm2022/services/base.mjs +7 -7
- package/esm2022/services/context.mjs +59 -0
- package/esm2022/services/http/HttpBX.mjs +55 -20
- package/esm2022/services/http/authRefresh.mjs +25 -23
- package/esm2022/services/http/http.mjs +14 -69
- package/esm2022/services/http/middleware.mjs +149 -0
- package/esm2022/services/http/sessionKey.mjs +16 -5
- package/esm2022/services/http/transport.mjs +5 -0
- package/esm2022/services/navvy/extends/navvy-pag-base.mjs +63 -9
- package/esm2022/services/navvy/navvy-alternative-pagination-navigation.mjs +21 -71
- package/esm2022/services/navvy/navvy-pag-nav-result-key-object.mjs +10 -49
- package/esm2022/services/navvy/navvy-pag-nav-result-key.mjs +14 -73
- package/esm2022/services/navvy/navvy-pag-nav-tasks.mjs +4 -76
- package/esm2022/services/navvy/navvy-pag-nav-with-useless-key.mjs +10 -76
- package/esm2022/services/navvy/navvy-pag.mjs +7 -65
- package/esm2022/services/navvy/navvy-support.mjs +2 -15
- package/esm2022/services/navvy.mjs +18 -31
- package/esm2022/typification/rest/calendar/event/get.mjs +1 -1
- package/esm2022/typification/rest/settings.mjs +1 -1
- package/esm2022/typification/rest/tasks/task/get.mjs +1 -1
- package/esm2022/typification/rest/user/user.mjs +1 -1
- package/esm2022/typification/settings.mjs +1 -1
- package/fesm2022/bx-rest.mjs +860 -2038
- package/fesm2022/bx-rest.mjs.map +1 -1
- package/functions/errors.d.ts +9 -0
- package/functions/uniqueElementCode.d.ts +12 -0
- package/map/disk.d.ts +10 -0
- package/map/task.d.ts +3 -3
- package/map/tasks/task.d.ts +1 -1
- package/map/tasks.d.ts +1 -1
- package/package.json +5 -2
- package/public-api.d.ts +7 -3
- package/request-navvy/pull/config.d.ts +0 -2
- package/rest/map.d.ts +2 -2
- package/rest/navvy.d.ts +9 -0
- package/services/context.d.ts +43 -0
- package/services/http/HttpBX.d.ts +10 -0
- package/services/http/authRefresh.d.ts +5 -1
- package/services/http/http.d.ts +12 -9
- package/services/http/middleware.d.ts +31 -0
- package/services/http/sessionKey.d.ts +10 -0
- package/services/http/transport.d.ts +28 -0
- package/services/navvy/extends/navvy-pag-base.d.ts +16 -2
- package/services/navvy/navvy-alternative-pagination-navigation.d.ts +5 -2
- package/services/navvy/navvy-pag-nav-result-key-object.d.ts +0 -2
- package/services/navvy/navvy-pag-nav-result-key.d.ts +1 -4
- package/services/navvy/navvy-pag-nav-tasks.d.ts +2 -9
- package/services/navvy/navvy-pag-nav-with-useless-key.d.ts +0 -2
- package/services/navvy/navvy-pag.d.ts +0 -2
- package/services/navvy/navvy-support.d.ts +0 -10
- package/services/navvy.d.ts +1 -0
- package/typification/rest/calendar/event/get.d.ts +1 -1
- package/typification/rest/settings.d.ts +1 -0
- package/typification/rest/user/user.d.ts +3 -3
- package/typification/settings.d.ts +34 -0
- package/esm2022/functions/errorHandler.mjs +0 -34
- package/esm2022/request/app.mjs +0 -13
- package/esm2022/request/bizproc/activity.mjs +0 -3
- package/esm2022/request/bizproc/event.mjs +0 -3
- package/esm2022/request/bizproc/robot.mjs +0 -3
- package/esm2022/request/bizproc/task.mjs +0 -3
- package/esm2022/request/bizproc/workflow/instance.mjs +0 -3
- package/esm2022/request/bizproc/workflow/template.mjs +0 -3
- package/esm2022/request/bizproc/workflow.mjs +0 -18
- package/esm2022/request/bizproc.mjs +0 -15
- package/esm2022/request/department.mjs +0 -26
- package/esm2022/request/disk/attachedObject.mjs +0 -14
- package/esm2022/request/disk/file.mjs +0 -18
- package/esm2022/request/disk/folder.mjs +0 -117
- package/esm2022/request/disk.mjs +0 -11
- package/esm2022/request/lists/element.mjs +0 -18
- package/esm2022/request/lists/field.mjs +0 -35
- package/esm2022/request/lists.mjs +0 -86
- package/esm2022/request/log/blogcomment.mjs +0 -3
- package/esm2022/request/log/blogpost.mjs +0 -18
- package/esm2022/request/log.mjs +0 -9
- package/esm2022/request/pull/application/config.mjs +0 -11
- package/esm2022/request/pull/channel/public.mjs +0 -18
- package/esm2022/request/pull/config.mjs +0 -13
- package/esm2022/request/pull.mjs +0 -11
- package/esm2022/request/server.mjs +0 -14
- package/esm2022/request/socialnetwork/api/contentview.mjs +0 -14
- package/esm2022/request/socialnetwork/api/livefeed/blogpost/important.mjs +0 -14
- package/esm2022/request/socialnetwork/api/livefeed/blogpost.mjs +0 -7
- package/esm2022/request/socialnetwork/api/livefeed.mjs +0 -7
- package/esm2022/request/socialnetwork/api.mjs +0 -9
- package/esm2022/request/socialnetwork.mjs +0 -8
- package/esm2022/request/sonet_group.mjs +0 -24
- package/esm2022/request/task/commentitem.mjs +0 -32
- package/esm2022/request/task/elapseditem.mjs +0 -40
- package/esm2022/request/task/item/userfield.mjs +0 -14
- package/esm2022/request/task/item.mjs +0 -7
- package/esm2022/request/task/planner.mjs +0 -11
- package/esm2022/request/task.mjs +0 -13
- package/esm2022/request/tasks/task/result.mjs +0 -22
- package/esm2022/request/tasks/task.mjs +0 -110
- package/esm2022/request/tasks.mjs +0 -7
- package/esm2022/request/user/userfield.mjs +0 -14
- package/esm2022/request/user.mjs +0 -40
- package/esm2022/rest/base.mjs +0 -39
- package/esm2022/services/http/HttpData.mjs +0 -77
- package/esm2022/services/http/base/base.mjs +0 -4
- package/esm2022/services/http/httpNavvy.mjs +0 -14
- package/esm2022/typification/base/answers.mjs +0 -438
- package/esm2022/typification/base/params.mjs +0 -525
- package/esm2022/typification/base/workpiece/methods-list.mjs +0 -2
- package/esm2022/typification/base/workpiece/methods.mjs +0 -2
- package/functions/errorHandler.d.ts +0 -10
- package/request/app.d.ts +0 -8
- package/request/bizproc/activity.d.ts +0 -2
- package/request/bizproc/event.d.ts +0 -2
- package/request/bizproc/robot.d.ts +0 -2
- package/request/bizproc/task.d.ts +0 -2
- package/request/bizproc/workflow/instance.d.ts +0 -2
- package/request/bizproc/workflow/template.d.ts +0 -2
- package/request/bizproc/workflow.d.ts +0 -13
- package/request/bizproc.d.ts +0 -12
- package/request/department.d.ts +0 -18
- package/request/disk/attachedObject.d.ts +0 -8
- package/request/disk/file.d.ts +0 -10
- package/request/disk/folder.d.ts +0 -96
- package/request/disk.d.ts +0 -8
- package/request/lists/element.d.ts +0 -11
- package/request/lists/field.d.ts +0 -11
- package/request/lists.d.ts +0 -31
- package/request/log/blogcomment.d.ts +0 -2
- package/request/log/blogpost.d.ts +0 -11
- package/request/log.d.ts +0 -6
- package/request/pull/application/config.d.ts +0 -5
- package/request/pull/channel/public.d.ts +0 -5
- package/request/pull/config.d.ts +0 -7
- package/request/pull.d.ts +0 -8
- package/request/server.d.ts +0 -7
- package/request/socialnetwork/api/contentview.d.ts +0 -6
- package/request/socialnetwork/api/livefeed/blogpost/important.d.ts +0 -6
- package/request/socialnetwork/api/livefeed/blogpost.d.ts +0 -4
- package/request/socialnetwork/api/livefeed.d.ts +0 -4
- package/request/socialnetwork/api.d.ts +0 -6
- package/request/socialnetwork.d.ts +0 -4
- package/request/sonet_group.d.ts +0 -8
- package/request/task/commentitem.d.ts +0 -23
- package/request/task/elapseditem.d.ts +0 -26
- package/request/task/item/userfield.d.ts +0 -6
- package/request/task/item.d.ts +0 -4
- package/request/task/planner.d.ts +0 -4
- package/request/task.d.ts +0 -10
- package/request/tasks/task/result.d.ts +0 -13
- package/request/tasks/task.d.ts +0 -37
- package/request/tasks.d.ts +0 -4
- package/request/user/userfield.d.ts +0 -8
- package/request/user.d.ts +0 -24
- package/rest/base.d.ts +0 -33
- package/services/http/HttpData.d.ts +0 -7
- package/services/http/base/base.d.ts +0 -7
- package/services/http/httpNavvy.d.ts +0 -10
- package/typification/base/answers.d.ts +0 -12
- package/typification/base/params.d.ts +0 -16
- package/typification/base/workpiece/methods-list.d.ts +0 -247
- package/typification/base/workpiece/methods.d.ts +0 -20
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,183 @@
|
|
|
7
7
|
История до версии 1.0.0 здесь не восстанавливалась — смотрите `git log` для
|
|
8
8
|
более ранних изменений.
|
|
9
9
|
|
|
10
|
+
## [1.6.0] - 2026-11-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Клиент со своими настройками: `new BXRestNavvy(settings)`. Все запросы
|
|
15
|
+
такого клиента идут на его портал с его токеном, продление авторизации у
|
|
16
|
+
него своё, а `BXRestSettings` на него не влияют. Так в одном процессе можно
|
|
17
|
+
работать с несколькими порталами. `new BXRestNavvy()` без аргумента
|
|
18
|
+
работает с глобальными `BXRestSettings`, как раньше. Подробнее — в разделе
|
|
19
|
+
README «Несколько порталов».
|
|
20
|
+
- Настройки `retryOnQueryLimit`, `timeout` и `throttle` — в `BXRestSettings`
|
|
21
|
+
или у клиента `new BXRestNavvy(settings)`. `retryOnQueryLimit` повторяет
|
|
22
|
+
запрос при `QUERY_LIMIT_EXCEEDED` с растущей паузой, `timeout` отменяет
|
|
23
|
+
запрос, который идёт дольше заданного времени, с ошибкой
|
|
24
|
+
`BXRestTimeoutError`, `throttle` пропускает не больше N запросов в секунду.
|
|
25
|
+
`true` включает значения по умолчанию: 3 попытки с паузой от 1000 мс,
|
|
26
|
+
60 000 мс и 2 запроса в секунду. Пока настройки не заданы, запросы идут как
|
|
27
|
+
раньше. Подробнее — в разделе README «Повтор, таймаут и очередь запросов».
|
|
28
|
+
- Настройка `middleware` — свои функции, через которые проходит каждый
|
|
29
|
+
запрос: логирование, подмена заголовков, свой транспорт вместо axios. Типы
|
|
30
|
+
`BXRestMiddleware`, `BXRestRequestConfig` и `BXRestSend` экспортируются из
|
|
31
|
+
`bx-rest`. Подробнее — в разделе README «Свои middleware».
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- Отписка до ответа теперь отменяет HTTP-запрос, как у `HttpClient` в
|
|
36
|
+
Angular. Раньше запрос доходил до конца, а ответ отбрасывался. На
|
|
37
|
+
`firstValueFrom` и обычную подписку это не влияет: они отписываются после
|
|
38
|
+
ответа. Отменяются запросы, которые обрывают `switchMap`, `takeUntil`,
|
|
39
|
+
`timeout` из RxJS или явный `unsubscribe()`. Отмена не откатывает то, что
|
|
40
|
+
портал уже успел сделать.
|
|
41
|
+
- Один экземпляр axios на клиента: он создаётся только при первом запросе.
|
|
42
|
+
Создание объекта запроса стало примерно в 100 раз быстрее (10,7 → 0,15 мкс),
|
|
43
|
+
при `new BXRestNavvy()` — вдвое.
|
|
44
|
+
- Ошибки HTTP библиотека узнаёт по полю `response: { status, data }`, а не
|
|
45
|
+
через `axios.isAxiosError`. Для ошибок axios ничего не меняется, а
|
|
46
|
+
`isBXRestAuthError` и `BXRestApiError` теперь работают и с ошибками своего
|
|
47
|
+
транспорта такой же формы.
|
|
48
|
+
- `bizProc.startProcFormNewsFeed()`: если портал вернул ошибку Bitrix при
|
|
49
|
+
создании элемента списка в ответе 2xx, вызов завершается ошибкой
|
|
50
|
+
`BXRestApiError` с кодом и описанием ошибки. Раньше в этом случае приходила
|
|
51
|
+
общая ошибка `Не удалось запустить бизнес процесс`. Ошибки со статусом не 2xx
|
|
52
|
+
и раньше приходили как `BXRestApiError`.
|
|
53
|
+
- Ошибки, которые библиотека создаёт сама, теперь на английском, как и
|
|
54
|
+
остальные её сообщения (`SessionKeyError`, `BXRestTimeoutError`, ошибки
|
|
55
|
+
разбора значений). Это ошибки `bizProc.startProcFormNewsFeed()`,
|
|
56
|
+
`disk.operation.*` и `task.elapsedItem.operation.*`. Код, который
|
|
57
|
+
сравнивает `err.message` с русским текстом, нужно обновить. Ошибки Bitrix
|
|
58
|
+
(`BXRestApiError`) приходят с текстом портала, как и раньше.
|
|
59
|
+
|
|
60
|
+
### Removed
|
|
61
|
+
|
|
62
|
+
- У `BXRestRequest` удалены публичные члены, которые библиотека не
|
|
63
|
+
использовала: `defSettings`, `getHttpParamsPost()`, `sliceArrayIntoChunks()`
|
|
64
|
+
и `removingMultipleSlashes()`. Они достались от внутреннего базового класса
|
|
65
|
+
и запросы не отправляли. `post()`, `get()`, `httpPost()` и `httpGet()` не
|
|
66
|
+
изменились. Код, который обращался к удалённым членам, перестанет
|
|
67
|
+
компилироваться.
|
|
68
|
+
- Из `bx-rest/services/navvy` удалён абстрактный класс `NavvySupport`:
|
|
69
|
+
библиотека его не использовала. Тип `ReturnTypeNavvy` остался.
|
|
70
|
+
- Из `bx-rest` удалён класс `HttpNavvyServices` вместе с его типами
|
|
71
|
+
`iBXRestParams` и `iBXRestAnswers`. Он отправлял запросы с типами для
|
|
72
|
+
небольшого набора методов, сама библиотека его не использовала, а в README
|
|
73
|
+
он не описан. Вместо него используйте `BXRestNavvy` или
|
|
74
|
+
`BXRestRequest.post()`.
|
|
75
|
+
- Из исходников удалены 63 модуля, недостижимых из точек входа пакета (старый
|
|
76
|
+
слой `request/*`, `rest/base`, `functions/errorHandler`, `isDate`, `isEqual`,
|
|
77
|
+
`isValidDate`, `serializeJavascript`, `services/permission` и другие), и
|
|
78
|
+
закомментированный код. Файлы 52 из них (`.d.ts` и `esm2022/*.mjs`) лежали в
|
|
79
|
+
пакете, но ни основной бандл, ни `exports` их не подключали, поэтому через
|
|
80
|
+
`import` из `bx-rest` и его подпутей они были недоступны. Из пакета удалено
|
|
81
|
+
104 файла. Затронуть это может только код, который импортировал типы по
|
|
82
|
+
внутреннему пути вроде `bx-rest/request/...` при `moduleResolution: "node"`.
|
|
83
|
+
|
|
84
|
+
### Fixed
|
|
85
|
+
|
|
86
|
+
- Проверка типов под `moduleResolution: "nodenext"` или `"node16"` с
|
|
87
|
+
`skipLibCheck: false` больше не падает с ошибкой TS1479 в
|
|
88
|
+
`typification/rest/calendar/event/get.d.ts`. Типы `countries-and-timezones`
|
|
89
|
+
теперь подключаются через `resolution-mode: 'import'`: ветка `require` этого
|
|
90
|
+
пакета указывает на несуществующий файл типов. Сами типы не изменились, код
|
|
91
|
+
пакета тоже. В режиме `bundler` (Angular, Vite, webpack) ошибки не было.
|
|
92
|
+
- `bizProc.startProcFormNewsFeed()` создаёт `ELEMENT_CODE` из префикса
|
|
93
|
+
`bx_rest_` и 128 случайных бит (`crypto.getRandomValues`). Раньше код
|
|
94
|
+
собирался из префикса другого приложения `trace_studio_`, времени и
|
|
95
|
+
`Math.random()`. Если портал отвечает, что такой код уже занят
|
|
96
|
+
(`ERROR_ELEMENT_ALREADY_EXISTS`), вызов создаёт новый код и повторяет
|
|
97
|
+
запрос, всего до трёх попыток.
|
|
98
|
+
- `disk.operation.getOfCreateRootFolderApp()` и
|
|
99
|
+
`getOfCreateRootContentFolderApp()`: если не удалось получить хранилище
|
|
100
|
+
приложения, ошибка теперь так и называется — `Could not get the app
|
|
101
|
+
storage`. Раньше в ней был чужой текст про учёт времени: `Отсутствуют права
|
|
102
|
+
добавление записи затраченного времени`.
|
|
103
|
+
|
|
104
|
+
## [1.5.1] - 2026-11-05
|
|
105
|
+
|
|
106
|
+
### Added
|
|
107
|
+
|
|
108
|
+
- Типы `iBXRestSettings` и `BXRestApiErrorDetails` экспортируются из `bx-rest`,
|
|
109
|
+
а `iBXRestSettings` — ещё и из `bx-rest/typification/settings`, где его раньше
|
|
110
|
+
не было. Новый подпуть `bx-rest/typification/batch` — типы `batch`.
|
|
111
|
+
|
|
112
|
+
### Changed
|
|
113
|
+
|
|
114
|
+
- `.resAll()` теперь завершается ошибкой `BXRestApiError`, если любая страница
|
|
115
|
+
внутри batch-запроса вернула ошибку (`result_error`) или сам batch-запрос
|
|
116
|
+
вернул ошибку в теле ответа 2xx. Раньше такие страницы молча пропускались, и
|
|
117
|
+
вызывающий код получал неполный список как полный. Код, который рассчитывал
|
|
118
|
+
на частичный результат, должен обрабатывать ошибку.
|
|
119
|
+
- `iBXRestUser.DATE_REGISTER` и `iBXRestUser.PERSONAL_BIRTHDAY` стали
|
|
120
|
+
необязательными: см. исправление `user.get` ниже. Код, который обращается к
|
|
121
|
+
этим полям без проверки, перестанет компилироваться в режиме `strict`.
|
|
122
|
+
- Отписка от `.resAll()` останавливает запрос оставшихся страниц. Уже
|
|
123
|
+
отправленный HTTP-запрос не отменяется. `.resAll()` по-прежнему кэширует
|
|
124
|
+
завершённый результат там, где делал это раньше.
|
|
125
|
+
- `.resAll()` у всех видов пагинации отправляет batch-запросы по 50 команд.
|
|
126
|
+
Раньше `pagNavResultKeyObject` отправлял отдельный batch на каждую страницу.
|
|
127
|
+
|
|
128
|
+
### Fixed
|
|
129
|
+
|
|
130
|
+
- `.resAll()` у `pagNavWithUselessKey` (`lists.field.get`) складывал страницы
|
|
131
|
+
после первой под числовые ключи вместо объединения полей, а без своего `map`
|
|
132
|
+
накапливал результат между запусками одного и того же объекта запроса.
|
|
133
|
+
- `.resAll()` падал с `RangeError: Maximum call stack size exceeded` на списках
|
|
134
|
+
примерно от 124 тысяч записей.
|
|
135
|
+
- `.resAll()` у `pagNav` при нескольких одновременных подписчиках мог отдавать
|
|
136
|
+
записи по два раза: подписчики делили один массив результата.
|
|
137
|
+
- `.resAll()` учитывает параметры запроса: при заданном `start` страница больше
|
|
138
|
+
не запрашивается повторно, а `alterPagNav` при `nPageSize` меньше 50 загружает
|
|
139
|
+
все страницы — раньше он отдавал только часть записей.
|
|
140
|
+
- `.resAll()` у `pagNavTasks`, `pagNavWithUselessKey` и `alterPagNav` не
|
|
141
|
+
запрашивает лишнюю пустую страницу, если число записей кратно размеру страницы.
|
|
142
|
+
- Ответ 2xx не от Bitrix (например, HTML-страница входа от прокси) больше не даёт
|
|
143
|
+
`TypeError: Cannot use 'in' operator...`: `.res()` и `.resAll()` завершаются
|
|
144
|
+
обычным `Error` с названием метода. Пустое тело `.res()` по-прежнему отдаёт как
|
|
145
|
+
`undefined`, а `.resAll()` завершается той же ошибкой.
|
|
146
|
+
- `isBXRestAnswerSuccess()` и `BXRestMapResult()` падали с `TypeError`, если им
|
|
147
|
+
передавали не объект.
|
|
148
|
+
- `BXRestRequest.get()`:
|
|
149
|
+
- вложенные параметры уходили как `filter=[object Object]`, а массивы — как
|
|
150
|
+
`select=ID,TITLE`. Теперь они сериализуются так же, как в POST;
|
|
151
|
+
- при `auth.key: 'OAuth2'` токен попадал в адрес запроса как `OAuth2=...`.
|
|
152
|
+
Теперь он не отправляется, как и в POST.
|
|
153
|
+
- `BXRestSettings.withCredentials` не действовал на запросы `.get()` и на
|
|
154
|
+
POST-запросы при `auth.key: 'sessid'`.
|
|
155
|
+
- `toDate()` превращал несуществующие даты в соседние (`31.02.2026` — в 3 марта).
|
|
156
|
+
Теперь это ошибка `Wrong date format`, как и у других неверных дат.
|
|
157
|
+
- `user.get`, `user.current` и другие методы с тем же маппером: пустой
|
|
158
|
+
`PERSONAL_BIRTHDAY` давал `Invalid Date`, а ответ без `DATE_REGISTER` или
|
|
159
|
+
`PERSONAL_BIRTHDAY` завершал весь запрос ошибкой. Теперь в этих случаях поле
|
|
160
|
+
равно `undefined`.
|
|
161
|
+
- `bizProc.startProcFormNewsFeed()` заменял текущей датой любую переданную строку
|
|
162
|
+
`DATE_CREATE` и падал с `TypeError`, если передать `Date`.
|
|
163
|
+
- `task.elapsedItem.operation.getListByIDTask([])` завершался без значения, и
|
|
164
|
+
`firstValueFrom` падал с `EmptyError`. Теперь пустой список задач даёт `[]` без
|
|
165
|
+
запроса. В ошибке `operation.add()` исправлен текст: «Отсутствуют права на
|
|
166
|
+
добавление записи затраченного времени».
|
|
167
|
+
- `new BXRestMap().disk` возвращал класс запросов вместо маппера, а
|
|
168
|
+
`.task.commentItem`, `.task.elapsedItem`, `.task.stages`, `.tasks.task` и
|
|
169
|
+
`.tasks.task.result` были недоступны.
|
|
170
|
+
- Импорт одного класса больше не тянет в бандл всю библиотеку: `BXRestFrame` без
|
|
171
|
+
остальной библиотеки весит около 8 КБ вместо 117 КБ. Мешали статический
|
|
172
|
+
инициализатор и декораторы, которые выполнялись при загрузке модуля. В
|
|
173
|
+
результате убрано статическое поле `BXRestNavvyPullConfig.public` — оно нигде не
|
|
174
|
+
использовалось, а сам класс не экспортируется.
|
|
175
|
+
|
|
176
|
+
### Security
|
|
177
|
+
|
|
178
|
+
- `.res()` и `.resAll()` больше не пишут ошибки запроса в `console.error`:
|
|
179
|
+
в `AxiosError` лежит конфигурация запроса с токеном авторизации, и он мог
|
|
180
|
+
попасть в журналы браузера и системы сбора ошибок. Ошибка по-прежнему приходит
|
|
181
|
+
подписчику. Токен остаётся в `cause.config` у `BXRestApiError` — не логируйте
|
|
182
|
+
и не отправляйте `cause` целиком.
|
|
183
|
+
- Минимальная версия `axios` поднята до 1.21.0: в версиях до 1.19.0 включительно
|
|
184
|
+
есть уязвимости высокой степени. В `package-lock.json` также обновлён
|
|
185
|
+
`follow-redirects` до 1.16.2.
|
|
186
|
+
|
|
10
187
|
## [1.5.0] - 2026-11-02
|
|
11
188
|
|
|
12
189
|
### Added
|
package/README.en.md
CHANGED
|
@@ -24,6 +24,9 @@ Also known as: Bitrix24 REST client, BX24 REST SDK, Bitrix24 API client, Bitrix
|
|
|
24
24
|
- [A token from the portal without BX24.js](#a-token-from-the-portal-without-bx24js)
|
|
25
25
|
- [Recipe: straight to the portal](#recipe-straight-to-the-portal)
|
|
26
26
|
- [Recipe: through a backend proxy](#recipe-through-a-backend-proxy)
|
|
27
|
+
- [Several portals](#several-portals)
|
|
28
|
+
- [Retry, timeout and request queue](#retry-timeout-and-request-queue)
|
|
29
|
+
- [Your own middleware](#your-own-middleware)
|
|
27
30
|
- [Working in an iframe without BX24.js](#working-in-an-iframe-without-bx24js)
|
|
28
31
|
- [Usage in Angular](#usage-in-angular)
|
|
29
32
|
- [SessionKeyServices](#sessionkeyservices)
|
|
@@ -93,7 +96,7 @@ Do not hardcode or commit a production access token. Pass it from your applicati
|
|
|
93
96
|
|
|
94
97
|
### Result modes
|
|
95
98
|
|
|
96
|
-
REST calls are lazy: the request starts when the returned `Observable` is subscribed to or passed to `firstValueFrom`.
|
|
99
|
+
REST calls are lazy: the request starts when the returned `Observable` is subscribed to or passed to `firstValueFrom`. Unsubscribing before the response aborts the HTTP request — this is what `switchMap`, `takeUntil` and the RxJS `timeout` operator rely on. Aborting does not undo what the portal has already done: if `crm.deal.add` reached the server, the deal may be created.
|
|
97
100
|
|
|
98
101
|
| Method | Result |
|
|
99
102
|
| --- | --- |
|
|
@@ -196,7 +199,9 @@ For cross-origin requests, the server — the portal or your proxy — must allo
|
|
|
196
199
|
| --- | --- |
|
|
197
200
|
| 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`. |
|
|
198
201
|
| 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. |
|
|
199
|
-
| Transport / configuration errors — network failure, missing auth,
|
|
202
|
+
| Transport / configuration errors — network failure, [`timeout`](#retry-timeout-and-request-queue) exceeded, missing auth, an empty base address (see below), a response that is not from Bitrix (e.g. a proxy's HTML error page) | The returned `Observable` errors with a plain `Error`. The original error is in its `cause`: for a response that is not from Bitrix it is the axios error, with the HTTP status in `cause.response.status`; for `timeout` it is `BXRestTimeoutError`. |
|
|
203
|
+
|
|
204
|
+
**Base address.** The request address is built from two parts: the portal address and `urls.additional_part`. The portal address is `urls.key`, or, with `urls.source: 'localStorage'`, the value stored in `localStorage` under the `urls.key` key. The `Base URL was not resolved` error happens only when both parts are empty. If only the portal address is empty and `additional_part` is `'rest'` (as in the default settings), the request goes to `rest/<method>` relative to the current page. This is meant for pages opened on the portal's own domain. But a request also goes there when nothing was found in `localStorage` under the key, so check the value beforehand.
|
|
200
205
|
|
|
201
206
|
`BXRestApiError` fields:
|
|
202
207
|
|
|
@@ -210,6 +215,8 @@ For cross-origin requests, the server — the portal or your proxy — must allo
|
|
|
210
215
|
|
|
211
216
|
For REST v3 methods, `error` holds the error `code` and `error_description` its `message`.
|
|
212
217
|
|
|
218
|
+
**The token in errors.** An axios error keeps the whole request that was sent: `config.url` holds the address, and with it the webhook secret, and `config.data` holds the body with the token. The library passes this error on as is — in `err.cause` for `.res()` and as the error itself for `.resVanilla()`. So `console.error(err)` or sending the error to an error tracker passes the token along too. If errors go to logs, pick the fields you need, for example `err.status`, `err.error` and `err.cause?.response?.data`.
|
|
219
|
+
|
|
213
220
|
**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:
|
|
214
221
|
|
|
215
222
|
```typescript
|
|
@@ -238,7 +245,7 @@ bxRest.crm.deal.list({
|
|
|
238
245
|
|
|
239
246
|
Without `throwOnApiError`, plain `.res()` can't tell "no records" apart from "an error in a 2xx response" — both resolve as `undefined`.
|
|
240
247
|
|
|
241
|
-
**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`. An error in the body of a 2xx response comes back as is from `.resVanilla()` and `.mapForVanilla()`, while `.resAll()` fails with a `BXRestApiError` — it has no `throwOnApiError` option.
|
|
248
|
+
**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`. An error in the body of a 2xx response comes back as is from `.resVanilla()` and `.mapForVanilla()`, while `.resAll()` fails with a `BXRestApiError` — it has no `throwOnApiError` option. It fails the same way when any page inside a batch request returns an error (`result_error`): `.resAll()` never gives a partial list.
|
|
242
249
|
|
|
243
250
|
```typescript
|
|
244
251
|
import { firstValueFrom } from 'rxjs'
|
|
@@ -305,7 +312,7 @@ How the retry works:
|
|
|
305
312
|
})
|
|
306
313
|
```
|
|
307
314
|
|
|
308
|
-
- **Where it works.** In every library request: `.res()`, `.resAll()` (the first page and every batch request), `.resVanilla()`, `.mapForVanilla()`, `BXRestRequest.post()`/`.get()
|
|
315
|
+
- **Where it works.** In every library request: `.res()`, `.resAll()` (the first page and every batch request), `.resVanilla()`, `.mapForVanilla()`, `BXRestRequest.post()`/`.get()`. The retry lives in the HTTP layer, so `.res()` already gets the result of the retry.
|
|
309
316
|
- **What is not retried:** errors in the body of a 2xx response (Bitrix sends `expired_token` with status 401) and errors of single commands inside a successful batch response (`result_error`) — the whole HTTP request is what gets retried.
|
|
310
317
|
|
|
311
318
|
### A token from the portal without BX24.js
|
|
@@ -402,7 +409,105 @@ BXRestSettings.update({
|
|
|
402
409
|
})
|
|
403
410
|
```
|
|
404
411
|
|
|
405
|
-
Errors of the refresh function end up in `authRefreshError` of the original error, and
|
|
412
|
+
Errors of the refresh function end up in `authRefreshError` of the original error, and your app may print that error to the console or send it to an error tracker — so keep tokens out of the error messages of the refresh function.
|
|
413
|
+
|
|
414
|
+
## Several portals
|
|
415
|
+
|
|
416
|
+
`BXRestSettings` are shared by the whole app. To work with several portals in one process, for example on a server that serves different customers, pass the settings to the `BXRestNavvy` constructor. Such a client has its own settings: all its requests go to its portal with its token, and `BXRestSettings` do not affect it.
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
import { BXRestNavvy } from 'bx-rest'
|
|
420
|
+
import { firstValueFrom } from 'rxjs'
|
|
421
|
+
|
|
422
|
+
const portalA = new BXRestNavvy({
|
|
423
|
+
auth: { source: () => tokenA, key: 'auth' },
|
|
424
|
+
urls: { source: 'string', key: 'https://a.bitrix24.com', additional_part: 'rest' }
|
|
425
|
+
})
|
|
426
|
+
const portalB = new BXRestNavvy({
|
|
427
|
+
auth: { source: () => tokenB, key: 'auth' },
|
|
428
|
+
urls: { source: 'string', key: 'https://b.bitrix24.com', additional_part: 'rest' }
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
const deals = await firstValueFrom(
|
|
432
|
+
portalA.crm.deal.list({ select: ['ID'], filter: {}, start: 0 }).resAll()
|
|
433
|
+
)
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
- **Without an argument** `new BXRestNavvy()` uses the global `BXRestSettings`, as before.
|
|
437
|
+
- **The format** is the same as for `BXRestSettings.update()`, but the settings are passed in full: `auth` and `urls` are required and are not merged with the global settings.
|
|
438
|
+
- **Changing the token of an existing client.** The library calls `auth.source` before every request, at subscribe time. To change the token, change the value the function returns. Requests created earlier but not started yet get the new token too:
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
let token = await getToken()
|
|
442
|
+
const portal = new BXRestNavvy({
|
|
443
|
+
auth: {
|
|
444
|
+
source: () => token,
|
|
445
|
+
key: 'auth',
|
|
446
|
+
refresh: async () => { token = await getNewToken() } // optional
|
|
447
|
+
},
|
|
448
|
+
urls: { source: 'string', key: 'https://a.bitrix24.com', additional_part: 'rest' }
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
token = newToken // the next request goes out with the new token
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
- **Another portal or other headers** — create a new client, it takes a fraction of a millisecond.
|
|
455
|
+
- **Authorization refresh** is per client: a client's `auth.refresh` is called only for its own requests.
|
|
456
|
+
- **`auth.source: 'cookies'` and `'localStorage'`** read the shared browser storage, so two clients with the same key get the same token. For several portals, set `source` to a function.
|
|
457
|
+
- **Objects created directly** — `new Navvy()`, `new BXRestRequest()`, `new SessionKeyServices()` — use the global `BXRestSettings`. Only `BXRestNavvy` and everything created through it get the client's own settings.
|
|
458
|
+
|
|
459
|
+
## Retry, timeout and request queue
|
|
460
|
+
|
|
461
|
+
Three optional settings protect against the Bitrix24 request rate limit and against hanging requests. Set them in `BXRestSettings.update()` for the whole app or in the settings of a `new BXRestNavvy(settings)` client. `true` turns on the defaults, a number or an object sets your own values. While a setting is not set, requests go as before.
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
BXRestSettings.update({
|
|
465
|
+
retryOnQueryLimit: true, // 3 attempts, pause from 1000 ms
|
|
466
|
+
timeout: 30_000, // ms
|
|
467
|
+
throttle: { perSecond: 2 },
|
|
468
|
+
})
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
| Setting | `true` means | What it does |
|
|
472
|
+
| --- | --- | --- |
|
|
473
|
+
| `retryOnQueryLimit` | `{ attempts: 3, delay: 1000 }` | Retries the request when the portal answers `QUERY_LIMIT_EXCEEDED` (HTTP 503, request rate limit exceeded). `attempts` is the total number of attempts including the first one, `delay` is the pause before the second attempt in ms, then it doubles: 1000, 2000, 4000... Other errors are not retried. When the attempts run out, the error of the last one goes out; in `.res()` it is a `BXRestApiError` with `error: 'QUERY_LIMIT_EXCEEDED'`. |
|
|
474
|
+
| `timeout` | `60000` | Time limit for one HTTP request in ms. A request that takes longer is aborted with `BXRestTimeoutError` (its `timeout` field is the limit). In `.res()` it is in `cause`; `.resVanilla()`, `.mapForVanilla()` and `.resAll()` error with it directly. |
|
|
475
|
+
| `throttle` | `{ perSecond: 2 }` | At most `perSecond` requests per second, the rest wait in a queue. 2 requests per second is the Bitrix24 limit on regular plans. |
|
|
476
|
+
|
|
477
|
+
An object may set only some fields: `retryOnQueryLimit: { attempts: 5 }` keeps `delay: 1000`.
|
|
478
|
+
|
|
479
|
+
- **Order:** retry, queue, timeout, [your own middleware](#your-own-middleware), axios. Every retry attempt queues again and gets its own `timeout`. Waiting in the queue and pauses before a retry do not count towards `timeout`.
|
|
480
|
+
- **The queue** belongs to each `new BXRestNavvy(settings)` client and is shared by everything that uses the global `BXRestSettings`. Every request goes through it, including every batch request of `.resAll()`.
|
|
481
|
+
- **`throttle` is stricter than the portal.** Bitrix24 lets short bursts above the limit through, while `throttle` spreads all requests evenly. If short bursts of requests need to be fast, `retryOnQueryLimit` alone is enough.
|
|
482
|
+
- **Unsubscribing** stops both the wait in the queue and the pause before a retry: such a request is never sent.
|
|
483
|
+
- **Settings are read at subscribe time**, like the rest.
|
|
484
|
+
- **An invalid value** — for example `throttle: { perSecond: 0 }` or a negative `timeout` — fails the request with a `TypeError` that describes it; nothing is sent to the portal.
|
|
485
|
+
|
|
486
|
+
### Your own middleware
|
|
487
|
+
|
|
488
|
+
A middleware is a function every request passes through before it is sent: logging, metrics, changing headers, your own transport instead of axios. Set it in `middleware`, globally or per client in the same way:
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
import { BXRestMiddleware, BXRestSettings } from 'bx-rest'
|
|
492
|
+
|
|
493
|
+
const logDuration: BXRestMiddleware = async (config, next) => {
|
|
494
|
+
const started = Date.now()
|
|
495
|
+
try {
|
|
496
|
+
return await next(config)
|
|
497
|
+
} finally {
|
|
498
|
+
console.debug(config.method, Date.now() - started, 'ms')
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
BXRestSettings.update({ middleware: [logDuration] })
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
- **`config`** is the request: `method` (`'GET'` or `'POST'`), `url` (with the parameters for GET), `data` (the POST body), `headers`, `withCredentials`, `signal`. `next(config)` sends it on and returns the response body.
|
|
506
|
+
- **Order:** the first in the list is the outermost one: it sees the request first and the response last. All your middleware run after `retryOnQueryLimit`, `throttle` and `timeout`, so they see every retry attempt. The retry after `auth.refresh` wraps all the steps: on an authorization refresh the request passes through them again.
|
|
507
|
+
- **To change the request**, pass a new object on: `next({ ...config, headers: { ...config.headers, 'X-Trace': id } })`.
|
|
508
|
+
- **Your own transport** is a middleware that does not call `next`: it sends the request itself and returns the response body. For a non-2xx response it must reject with an error that has a `response: { status, data }` field, as axios does. The library uses that field to recognize Bitrix errors (`BXRestApiError`) and authorization errors (`auth.refresh`).
|
|
509
|
+
- **Pass `config.signal`** to your transport: it aborts the request on unsubscribe and on `timeout`.
|
|
510
|
+
- **`config.url` and `config.data`** contain the token and the webhook secret — do not log them as they are.
|
|
406
511
|
|
|
407
512
|
## Working in an iframe without BX24.js
|
|
408
513
|
|
|
@@ -665,6 +770,12 @@ npm run demo:bootstrap
|
|
|
665
770
|
Then, in any of `projects/demo-angular|demo-vue|demo-react`: `npm run dev`
|
|
666
771
|
(for Angular, `npm start`). See each demo's own README for details.
|
|
667
772
|
|
|
773
|
+
`projects/demo-node` is a Node.js demo without a UI. It shows two portals in
|
|
774
|
+
one process, `retryOnQueryLimit`, `throttle`, `timeout`, your own `middleware`
|
|
775
|
+
for logging and handling `BXRestApiError`. `npm run mock` runs it against a
|
|
776
|
+
built-in mock portal, `npm start` against a real portal using the incoming
|
|
777
|
+
webhook address from `BX_WEBHOOK_A`. On a portal the demo only reads data.
|
|
778
|
+
|
|
668
779
|
## Custom REST methods
|
|
669
780
|
|
|
670
781
|
`Navvy` is exported from `bx-rest`. In Angular, register the `NavvyProvider` shown above and inject `Navvy` into your custom API service. A method URL is represented as an array and is joined with dots before the request is sent.
|
package/README.md
CHANGED
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
- [Токен от портала без BX24.js](#токен-от-портала-без-bx24js)
|
|
25
25
|
- [Рецепт: напрямую в портал](#рецепт-напрямую-в-портал)
|
|
26
26
|
- [Рецепт: через бэкенд-прокси](#рецепт-через-бэкенд-прокси)
|
|
27
|
+
- [Несколько порталов](#несколько-порталов)
|
|
28
|
+
- [Повтор, таймаут и очередь запросов](#повтор-таймаут-и-очередь-запросов)
|
|
29
|
+
- [Свои middleware](#свои-middleware)
|
|
27
30
|
- [Работа в iframe без BX24.js](#работа-в-iframe-без-bx24js)
|
|
28
31
|
- [Использование в Angular](#использование-в-angular)
|
|
29
32
|
- [SessionKeyServices](#sessionkeyservices)
|
|
@@ -103,7 +106,7 @@ console.log(deals)
|
|
|
103
106
|
|
|
104
107
|
### Режимы результата
|
|
105
108
|
|
|
106
|
-
REST-вызовы ленивые: запрос начинается, когда на возвращённый `Observable` подписываются или передают его в `firstValueFrom`.
|
|
109
|
+
REST-вызовы ленивые: запрос начинается, когда на возвращённый `Observable` подписываются или передают его в `firstValueFrom`. Отписка до ответа отменяет HTTP-запрос — так работают `switchMap`, `takeUntil` и оператор `timeout` из RxJS. Отмена не откатывает то, что портал уже успел сделать: если `crm.deal.add` дошёл до сервера, сделка может быть создана.
|
|
107
110
|
|
|
108
111
|
| Метод | Результат |
|
|
109
112
|
| --- | --- |
|
|
@@ -206,7 +209,9 @@ const deals = await firstValueFrom(
|
|
|
206
209
|
| --- | --- |
|
|
207
210
|
| Ошибка 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` и без него. |
|
|
208
211
|
| Ошибка API Bitrix в теле ответа со статусом 2xx | По умолчанию `.res()` резолвится обычным образом со значением `undefined` — **исключение не выбрасывается**. Передайте `{ throwOnApiError: true }`, чтобы вместо этого выбрасывалось `BXRestApiError`. |
|
|
209
|
-
| Ошибки транспорта / конфигурации — сбой сети, отсутствие авторизации,
|
|
212
|
+
| Ошибки транспорта / конфигурации — сбой сети, превышен [`timeout`](#повтор-таймаут-и-очередь-запросов), отсутствие авторизации, пустой базовый адрес (см. ниже), ответ не от Bitrix (например, HTML-страница ошибки прокси) | Возвращённый `Observable` завершается обычной ошибкой `Error`. Исходная ошибка — в её `cause`: для ответа не от Bitrix это ошибка axios, HTTP-статус — в `cause.response.status`; для `timeout` — `BXRestTimeoutError`. |
|
|
213
|
+
|
|
214
|
+
**Базовый адрес.** Адрес запроса собирается из двух частей: адреса портала и `urls.additional_part`. Адрес портала — это `urls.key`, а при `urls.source: 'localStorage'` — значение в `localStorage` под ключом `urls.key`. Ошибка `Base URL was not resolved` возникает, только если пусты обе части. Если пуст только адрес портала, а `additional_part` равен `'rest'` (так в настройках по умолчанию), запрос уходит на `rest/<метод>` относительно текущей страницы. Это нужно страницам, открытым на домене самого портала. Но так же уйдёт запрос, если в `localStorage` под ключом ничего не оказалось, — проверяйте значение заранее.
|
|
210
215
|
|
|
211
216
|
Поля `BXRestApiError`:
|
|
212
217
|
|
|
@@ -220,6 +225,8 @@ const deals = await firstValueFrom(
|
|
|
220
225
|
|
|
221
226
|
Для методов REST v3 в `error` попадает `code` ошибки, а в `error_description` — её `message`.
|
|
222
227
|
|
|
228
|
+
**Токен в ошибках.** Ошибка axios хранит весь отправленный запрос: в `config.url` — адрес, а с ним и секрет вебхука, в `config.data` — тело с токеном. Библиотека отдаёт эту ошибку как есть — в `err.cause` у `.res()` и саму ошибку у `.resVanilla()`. Поэтому `console.error(err)` или отправка ошибки в систему сбора ошибок передаёт и токен. Если ошибки уходят в логи, выбирайте из них нужные поля, например `err.status`, `err.error` и `err.cause?.response?.data`.
|
|
229
|
+
|
|
223
230
|
**Рекомендуемый вариант — `.res({ throwOnApiError: true })`.** Тогда любая ошибка Bitrix приходит как `BXRestApiError` независимо от HTTP-статуса, и её можно обработать вместе с транспортными ошибками в одном колбэке `catchError`/`error`:
|
|
224
231
|
|
|
225
232
|
```typescript
|
|
@@ -248,7 +255,7 @@ bxRest.crm.deal.list({
|
|
|
248
255
|
|
|
249
256
|
Без `throwOnApiError` по голому `.res()` нельзя отличить «нет записей» от «ошибки в ответе 2xx» — оба случая резолвятся как `undefined`.
|
|
250
257
|
|
|
251
|
-
**Альтернатива — проверять сырой ответ.** `.resVanilla()` возвращает сырое тело ответа Bitrix для ответов 2xx; проверяйте его экспортируемым type guard `isBXRestAnswerSuccess()`. Учтите, что `.resVanilla()`, `.mapForVanilla()` и `.resAll()` ошибки не преобразуют: на ошибку Bitrix со статусом не 2xx они завершаются исходной ошибкой axios, тело ответа — в `err.response.data`. Ошибку в теле ответа 2xx `.resVanilla()` и `.mapForVanilla()` отдают как есть, а `.resAll()` завершается ошибкой `BXRestApiError`: опции `throwOnApiError` у него нет.
|
|
258
|
+
**Альтернатива — проверять сырой ответ.** `.resVanilla()` возвращает сырое тело ответа Bitrix для ответов 2xx; проверяйте его экспортируемым type guard `isBXRestAnswerSuccess()`. Учтите, что `.resVanilla()`, `.mapForVanilla()` и `.resAll()` ошибки не преобразуют: на ошибку Bitrix со статусом не 2xx они завершаются исходной ошибкой axios, тело ответа — в `err.response.data`. Ошибку в теле ответа 2xx `.resVanilla()` и `.mapForVanilla()` отдают как есть, а `.resAll()` завершается ошибкой `BXRestApiError`: опции `throwOnApiError` у него нет. Так же он завершается, если ошибку вернула любая из страниц внутри batch-запроса (`result_error`): неполный список `.resAll()` не отдаёт.
|
|
252
259
|
|
|
253
260
|
```typescript
|
|
254
261
|
import { firstValueFrom } from 'rxjs'
|
|
@@ -315,7 +322,7 @@ BXRestSettings.update({
|
|
|
315
322
|
})
|
|
316
323
|
```
|
|
317
324
|
|
|
318
|
-
- **Где работает.** Во всех запросах библиотеки: `.res()`, `.resAll()` (и первая страница, и каждый batch-запрос), `.resVanilla()`, `.mapForVanilla()`, `BXRestRequest.post()`/`.get()
|
|
325
|
+
- **Где работает.** Во всех запросах библиотеки: `.res()`, `.resAll()` (и первая страница, и каждый batch-запрос), `.resVanilla()`, `.mapForVanilla()`, `BXRestRequest.post()`/`.get()`. Повтор встроен в HTTP-слой, поэтому `.res()` получает уже результат повтора.
|
|
319
326
|
- **Что не повторяется:** ошибки в теле ответа со статусом 2xx (Bitrix отдаёт `expired_token` со статусом 401) и ошибки отдельных команд внутри успешного batch-ответа (`result_error`) — повторяется HTTP-запрос целиком.
|
|
320
327
|
|
|
321
328
|
### Токен от портала без BX24.js
|
|
@@ -412,7 +419,105 @@ BXRestSettings.update({
|
|
|
412
419
|
})
|
|
413
420
|
```
|
|
414
421
|
|
|
415
|
-
Ошибки функции продления попадают в `authRefreshError` исходной ошибки, а
|
|
422
|
+
Ошибки функции продления попадают в `authRefreshError` исходной ошибки, а приложение может вывести эту ошибку в консоль или отправить в систему сбора ошибок — поэтому не кладите токены в текст ошибок функции продления.
|
|
423
|
+
|
|
424
|
+
## Несколько порталов
|
|
425
|
+
|
|
426
|
+
`BXRestSettings` — общие настройки на всё приложение. Если в одном процессе нужно работать с несколькими порталами, например на сервере, который обслуживает разных клиентов, передайте настройки в конструктор `BXRestNavvy`. У такого клиента настройки свои: все его запросы идут на его портал с его токеном, а `BXRestSettings` на него не влияют.
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
import { BXRestNavvy } from 'bx-rest'
|
|
430
|
+
import { firstValueFrom } from 'rxjs'
|
|
431
|
+
|
|
432
|
+
const portalA = new BXRestNavvy({
|
|
433
|
+
auth: { source: () => tokenA, key: 'auth' },
|
|
434
|
+
urls: { source: 'string', key: 'https://a.bitrix24.ru', additional_part: 'rest' }
|
|
435
|
+
})
|
|
436
|
+
const portalB = new BXRestNavvy({
|
|
437
|
+
auth: { source: () => tokenB, key: 'auth' },
|
|
438
|
+
urls: { source: 'string', key: 'https://b.bitrix24.ru', additional_part: 'rest' }
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
const deals = await firstValueFrom(
|
|
442
|
+
portalA.crm.deal.list({ select: ['ID'], filter: {}, start: 0 }).resAll()
|
|
443
|
+
)
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
- **Без аргумента** `new BXRestNavvy()` работает с глобальными `BXRestSettings`, как и раньше.
|
|
447
|
+
- **Формат** тот же, что у `BXRestSettings.update()`, но настройки передаются целиком: `auth` и `urls` обязательны, с глобальными настройками они не смешиваются.
|
|
448
|
+
- **Смена токена у готового клиента.** Функцию `auth.source` библиотека вызывает перед каждым запросом, в момент подписки. Чтобы сменить токен, поменяйте значение, которое она возвращает. Новый токен получат и запросы, созданные раньше, но ещё не запущенные:
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
let token = await getToken()
|
|
452
|
+
const portal = new BXRestNavvy({
|
|
453
|
+
auth: {
|
|
454
|
+
source: () => token,
|
|
455
|
+
key: 'auth',
|
|
456
|
+
refresh: async () => { token = await getNewToken() } // необязательно
|
|
457
|
+
},
|
|
458
|
+
urls: { source: 'string', key: 'https://a.bitrix24.ru', additional_part: 'rest' }
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
token = newToken // следующий запрос уйдёт с новым токеном
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
- **Другой портал или другие заголовки** — создайте новый клиент, это занимает доли миллисекунды.
|
|
465
|
+
- **Продление авторизации** у каждого клиента своё: `auth.refresh` клиента вызывается только для его запросов.
|
|
466
|
+
- **`auth.source: 'cookies'` и `'localStorage'`** читают общее хранилище браузера, поэтому два клиента с одним ключом получат один и тот же токен. Для нескольких порталов задавайте `source` функцией.
|
|
467
|
+
- **Объекты, созданные напрямую**, — `new Navvy()`, `new BXRestRequest()`, `new SessionKeyServices()` — работают с глобальными `BXRestSettings`. Свои настройки получает только `BXRestNavvy` и всё, что создаётся через него.
|
|
468
|
+
|
|
469
|
+
## Повтор, таймаут и очередь запросов
|
|
470
|
+
|
|
471
|
+
Три необязательные настройки защищают от лимита частоты запросов Bitrix24 и от зависших запросов. Задайте их в `BXRestSettings.update()` для всего приложения или в настройках клиента `new BXRestNavvy(settings)`. `true` включает значения по умолчанию, число или объект задают свои. Пока настройка не задана, запросы идут как раньше.
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
BXRestSettings.update({
|
|
475
|
+
retryOnQueryLimit: true, // 3 попытки, пауза от 1000 мс
|
|
476
|
+
timeout: 30_000, // мс
|
|
477
|
+
throttle: { perSecond: 2 },
|
|
478
|
+
})
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
| Настройка | `true` означает | Что делает |
|
|
482
|
+
| --- | --- | --- |
|
|
483
|
+
| `retryOnQueryLimit` | `{ attempts: 3, delay: 1000 }` | Повторяет запрос, если портал ответил `QUERY_LIMIT_EXCEEDED` (HTTP 503, превышен лимит частоты запросов). `attempts` — сколько всего попыток вместе с первой, `delay` — пауза перед второй попыткой в мс, дальше она растёт вдвое: 1000, 2000, 4000... Другие ошибки не повторяются. Попытки кончились — наружу уходит ошибка последней, в `.res()` это `BXRestApiError` с `error: 'QUERY_LIMIT_EXCEEDED'`. |
|
|
484
|
+
| `timeout` | `60000` | Предельное время одного HTTP-запроса в мс. Не уложился — запрос отменяется с ошибкой `BXRestTimeoutError` (поле `timeout` — заданное время). В `.res()` она лежит в `cause`, `.resVanilla()`, `.mapForVanilla()` и `.resAll()` завершаются ей самой. |
|
|
485
|
+
| `throttle` | `{ perSecond: 2 }` | Не больше `perSecond` запросов в секунду, остальные ждут своей очереди. 2 запроса в секунду — лимит Bitrix24 на обычных тарифах. |
|
|
486
|
+
|
|
487
|
+
В объекте можно задать только часть полей: `retryOnQueryLimit: { attempts: 5 }` оставит `delay: 1000`.
|
|
488
|
+
|
|
489
|
+
- **Порядок:** повтор, очередь, таймаут, [свои middleware](#свои-middleware), axios. Каждая попытка повтора заново встаёт в очередь и получает своё время `timeout`. Ожидание в очереди и паузы перед повтором в `timeout` не входят.
|
|
490
|
+
- **Очередь** своя у каждого клиента `new BXRestNavvy(settings)` и общая у всех, кто работает с глобальными `BXRestSettings`. Через неё проходит каждый запрос, в том числе каждый batch-запрос `.resAll()`.
|
|
491
|
+
- **`throttle` строже портала.** Bitrix24 пропускает короткие всплески сверх лимита, а `throttle` равномерно распределяет все запросы. Если важна скорость коротких пачек запросов, хватит одного `retryOnQueryLimit`.
|
|
492
|
+
- **Отписка** прерывает и ожидание в очереди, и паузу перед повтором: такой запрос уже не уйдёт.
|
|
493
|
+
- **Настройки читаются в момент подписки**, как и остальные.
|
|
494
|
+
- **Неверное значение** — например, `throttle: { perSecond: 0 }` или отрицательный `timeout` — завершает запрос ошибкой `TypeError` с описанием, на портал ничего не уходит.
|
|
495
|
+
|
|
496
|
+
### Свои middleware
|
|
497
|
+
|
|
498
|
+
Middleware — функция, через которую проходит каждый запрос перед отправкой: логирование, метрики, подмена заголовков, свой транспорт вместо axios. Задаётся в `middleware` — так же глобально или у клиента:
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
import { BXRestMiddleware, BXRestSettings } from 'bx-rest'
|
|
502
|
+
|
|
503
|
+
const logDuration: BXRestMiddleware = async (config, next) => {
|
|
504
|
+
const started = Date.now()
|
|
505
|
+
try {
|
|
506
|
+
return await next(config)
|
|
507
|
+
} finally {
|
|
508
|
+
console.debug(config.method, Date.now() - started, 'ms')
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
BXRestSettings.update({ middleware: [logDuration] })
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
- **`config`** — запрос: `method` (`'GET'` или `'POST'`), `url` (у GET вместе с параметрами), `data` (тело POST), `headers`, `withCredentials`, `signal`. `next(config)` отправляет его дальше и возвращает тело ответа.
|
|
516
|
+
- **Порядок:** первый в списке — внешний, он первым видит запрос и последним — ответ. Все свои middleware идут после `retryOnQueryLimit`, `throttle` и `timeout`, поэтому видят каждую попытку повтора. Повтор после `auth.refresh` идёт снаружи всех шагов: при продлении авторизации запрос пройдёт через них ещё раз.
|
|
517
|
+
- **Изменить запрос** — передать дальше новый объект: `next({ ...config, headers: { ...config.headers, 'X-Trace': id } })`.
|
|
518
|
+
- **Свой транспорт** — middleware, который не зовёт `next`: он сам отправляет запрос и возвращает тело ответа. На ответ не 2xx он должен отклонить промис ошибкой с полем `response: { status, data }`, как это делает axios. По этому полю библиотека узнаёт ошибки Bitrix (`BXRestApiError`) и ошибки авторизации (`auth.refresh`).
|
|
519
|
+
- **`config.signal`** передавайте в свой транспорт: по нему запрос отменяется при отписке и по `timeout`.
|
|
520
|
+
- **`config.url` и `config.data`** содержат токен и секрет вебхука — не логируйте их как есть.
|
|
416
521
|
|
|
417
522
|
## Работа в iframe без BX24.js
|
|
418
523
|
|
|
@@ -675,6 +780,13 @@ npm run demo:bootstrap
|
|
|
675
780
|
Дальше в любой из `projects/demo-angular|demo-vue|demo-react`: `npm run dev`
|
|
676
781
|
(для Angular — `npm start`). Подробности — в README каждого демо.
|
|
677
782
|
|
|
783
|
+
`projects/demo-node` — демо для Node.js, без интерфейса. Показывает два
|
|
784
|
+
портала в одном процессе, `retryOnQueryLimit`, `throttle`, `timeout`, свой
|
|
785
|
+
`middleware` для логирования и обработку `BXRestApiError`. `npm run mock`
|
|
786
|
+
запускает его со встроенной заглушкой портала, `npm start` — с настоящим
|
|
787
|
+
порталом по адресу входящего вебхука из `BX_WEBHOOK_A`. На портале демо только
|
|
788
|
+
читает данные.
|
|
789
|
+
|
|
678
790
|
## Кастомные REST-методы
|
|
679
791
|
|
|
680
792
|
`Navvy` экспортируется из `bx-rest`. В Angular зарегистрируйте показанный выше `NavvyProvider` и внедрите `Navvy` в ваш кастомный API-сервис. URL метода представлен массивом и перед отправкой запроса соединяется точками.
|