@andrey4emk/npm-app-back-b24 3.8.2 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -13
- package/bitrix24/b24/config.ts +24 -0
- package/bitrix24/b24/facade.ts +371 -0
- package/bitrix24/b24/instance.ts +186 -0
- package/bitrix24/b24/proxy.ts +120 -0
- package/bitrix24/b24/retry.ts +490 -0
- package/bitrix24/b24/state.ts +39 -0
- package/bitrix24/b24/tokens.ts +280 -0
- package/bitrix24/b24/types.ts +93 -0
- package/bitrix24/b24.ts +97 -955
- package/bitrix24/errTaskB24.ts +8 -5
- package/bitrix24/eventB24.ts +301 -63
- package/index.ts +4 -1
- package/package.json +26 -7
- package/sendMessage/chatApp.ts +101 -24
- package/sendMessage/email.ts +233 -32
- package/sendMessage/smsgold.ts +11 -2
- package/sendMessage/wappi.ts +41 -15
- package/utils/fetchRetry.ts +70 -9
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { SdkError } from "@bitrix24/b24jssdk";
|
|
2
|
+
import type { AuthData } from "@bitrix24/b24jssdk";
|
|
3
|
+
import { logs } from "../../logs/logs.ts";
|
|
4
|
+
import { isPreConnectionError } from "../../utils/fetchRetry.ts";
|
|
5
|
+
import { RETRY_COUNT, RETRY_DELAY_MS, delay } from "./retry.ts";
|
|
6
|
+
import { APP_ENV, confAuthB24, cleanDomain } from "./config.ts";
|
|
7
|
+
import { $b24 } from "./state.ts";
|
|
8
|
+
import type { SaveResult } from "./types.ts";
|
|
9
|
+
|
|
10
|
+
// ==================== Константы ====================
|
|
11
|
+
|
|
12
|
+
/** 25 минут при времени жизни токена около часа */
|
|
13
|
+
export const PROACTIVE_REFRESH_INTERVAL_MS = 25 * 60 * 1000;
|
|
14
|
+
/** 2 минуты — ускоренный интервал при ошибке обновления */
|
|
15
|
+
export const PROACTIVE_RETRY_ON_ERROR_MS = 2 * 60 * 1000;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Бюджет одного обмена refresh-токена. По его истечении сторож освобождает мьютекс.
|
|
19
|
+
*
|
|
20
|
+
* Совпадает с PROACTIVE_RETRY_ON_ERROR_MS намеренно: сторож не отменяет обмен, а лишь
|
|
21
|
+
* возвращает управление, и следующую попытку делает проактивный таймер — то есть при
|
|
22
|
+
* залипании процесс пробует обновить токен примерно раз в четыре минуты. При времени
|
|
23
|
+
* жизни токена около часа этого с запасом хватает, чтобы дожить до восстановления сети.
|
|
24
|
+
*
|
|
25
|
+
* Собственный цикл повторов внутри обмена укладывается в бюджет целиком: пять попыток
|
|
26
|
+
* с линейно растущей задержкой дают около пяти секунд плюс сетевое время.
|
|
27
|
+
*
|
|
28
|
+
* Плата за то, что сторож обмен не отменяет: при недоступности oauth-хоста висящие
|
|
29
|
+
* запросы копятся примерно по одному на четыре минуты и рассасываются сами, когда
|
|
30
|
+
* сокеты добьют таймауты ОС. Это не утечка, а следствие отсутствия сигнала отмены
|
|
31
|
+
* внутри SDK — искать её в другом месте не нужно.
|
|
32
|
+
*
|
|
33
|
+
* Известный компромисс: если ротация на сервере всё же прошла, второй обмен пойдёт
|
|
34
|
+
* с уже отозванным refresh-токеном и вернёт отказ. Отказ виден в логах и лечится
|
|
35
|
+
* проактивным таймером, тогда как залипание лечилось только перезапуском процесса.
|
|
36
|
+
*
|
|
37
|
+
* Переменной окружения для бюджета нет намеренно: рычаг, которым можно выключить
|
|
38
|
+
* защиту, в аварийном пути опаснее отсутствия настройки.
|
|
39
|
+
*/
|
|
40
|
+
export const REFRESH_STUCK_TIMEOUT_MS = 2 * 60 * 1000;
|
|
41
|
+
|
|
42
|
+
// ==================== Работа с токенами ====================
|
|
43
|
+
|
|
44
|
+
/** Проверяет, что authData содержит все обязательные непустые поля */
|
|
45
|
+
function isValidAuthData(data: AuthData): boolean {
|
|
46
|
+
return !!(
|
|
47
|
+
data &&
|
|
48
|
+
typeof data.access_token === "string" && data.access_token.length > 0 &&
|
|
49
|
+
typeof data.refresh_token === "string" && data.refresh_token.length > 0 &&
|
|
50
|
+
typeof data.domain === "string" && data.domain.length > 0 &&
|
|
51
|
+
data.expires
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Сохраняет токены в authB24.json, нормализуя домен без протокола */
|
|
56
|
+
export function saveTokens(authData: AuthData): SaveResult {
|
|
57
|
+
try {
|
|
58
|
+
if (!isValidAuthData(authData)) {
|
|
59
|
+
const msg = "Попытка сохранить невалидные токены — операция отклонена";
|
|
60
|
+
logs.add(msg, "error");
|
|
61
|
+
return { error: true, message: msg };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
confAuthB24.set(APP_ENV, { ...authData, domain: cleanDomain(authData.domain) });
|
|
65
|
+
logs.add("Токены Bitrix24 сохранены", "debug");
|
|
66
|
+
return { error: false, message: "Токены сохранены." };
|
|
67
|
+
} catch (error: unknown) {
|
|
68
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
+
logs.add(`Ошибка сохранения токенов: ${message}`, "error");
|
|
70
|
+
return { error: true, message };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ==================== Мьютекс для refresh токена ====================
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Дедупликация refresh-запросов.
|
|
78
|
+
* Пока промис не завершён, все новые вызовы ждут его результат
|
|
79
|
+
* вместо параллельных запросов к oauth.bitrix.info.
|
|
80
|
+
*/
|
|
81
|
+
let refreshInProgress: Promise<AuthData> | null = null;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Владелец мьютекса. По нему поздно завершившийся обмен узнаёт, что мьютекс уже
|
|
85
|
+
* не его: без билета его завершение обнулило бы `refreshInProgress` нового обмена,
|
|
86
|
+
* и два запроса к oauth.bitrix.info пошли бы параллельно.
|
|
87
|
+
*
|
|
88
|
+
* Проверка появляется вместе со сторожем: до него новый обмен не мог начаться раньше,
|
|
89
|
+
* чем отработает `finally` предыдущего, и гонки не существовало.
|
|
90
|
+
*/
|
|
91
|
+
let refreshTicket: object | null = null;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Снимает мьютекс, только если он всё ещё принадлежит этому обмену.
|
|
95
|
+
*
|
|
96
|
+
* Возвращает `true`, когда мьютекс был снят, и `false`, когда он уже принадлежит
|
|
97
|
+
* другому обмену. По этому значению сторож выбирает формулировку: строка «мьютекс
|
|
98
|
+
* освобождён» после `reinitializeB24()` сообщала бы о том, чего не было, и уводила
|
|
99
|
+
* разбор аварии в сторону.
|
|
100
|
+
*/
|
|
101
|
+
function releaseRefreshMutex(ticket: object): boolean {
|
|
102
|
+
if (refreshTicket !== ticket) return false;
|
|
103
|
+
refreshInProgress = null;
|
|
104
|
+
refreshTicket = null;
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Отвязывает мьютекс от текущего обмена, не дожидаясь его.
|
|
110
|
+
*
|
|
111
|
+
* Нужен `reinitializeB24()`: `refreshInProgress` — переменная модуля, общая для всех
|
|
112
|
+
* экземпляров, и без сброса свежесозданный экземпляр приклеился бы к обмену старого,
|
|
113
|
+
* то есть к запросу со старым refresh-токеном. Билет прежнего обмена после сброса
|
|
114
|
+
* никому не принадлежит, поэтому его завершение мьютекс не тронет.
|
|
115
|
+
*
|
|
116
|
+
* Наружу пакета имя не уходит: `b24.ts` реэкспортирует явным списком.
|
|
117
|
+
*/
|
|
118
|
+
export function resetRefreshMutex(): void {
|
|
119
|
+
refreshInProgress = null;
|
|
120
|
+
refreshTicket = null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Сам обмен токена: пять попыток с линейно растущей задержкой.
|
|
125
|
+
* Мьютексом не распоряжается — это забота вызывающего.
|
|
126
|
+
*/
|
|
127
|
+
async function runRefreshAttempts(): Promise<AuthData> {
|
|
128
|
+
if (!$b24) throw new Error("$b24 не инициализирован");
|
|
129
|
+
|
|
130
|
+
let lastError: unknown;
|
|
131
|
+
for (let attempt = 1; attempt <= RETRY_COUNT; attempt++) {
|
|
132
|
+
try {
|
|
133
|
+
return await $b24.auth.refreshAuth();
|
|
134
|
+
} catch (error) {
|
|
135
|
+
lastError = error;
|
|
136
|
+
|
|
137
|
+
// Повторяем только когда соединение заведомо не состоялось.
|
|
138
|
+
// При таймауте или обрыве сервер мог уже провести ротацию: тогда
|
|
139
|
+
// локальный refresh-токен мёртв независимо от наших действий, и повтор
|
|
140
|
+
// не помогает, а лишь маскирует проблему серией одинаковых отказов.
|
|
141
|
+
// Следующая попытка всё равно будет — проактивный таймер повторит
|
|
142
|
+
// через 2 минуты при времени жизни токена около часа
|
|
143
|
+
if (!isPreConnectionError(error) || attempt === RETRY_COUNT) throw error;
|
|
144
|
+
|
|
145
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
146
|
+
const code = error instanceof SdkError ? ` [${error.code}]` : "";
|
|
147
|
+
const delayMs = RETRY_DELAY_MS * attempt;
|
|
148
|
+
logs.add(`refreshAuth${code}: попытка ${attempt}/${RETRY_COUNT} не удалась (${msg}), повтор через ${delayMs}мс`, "warn");
|
|
149
|
+
await delay(delayMs);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
throw lastError;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Обмен токена под мьютексом со сторожевым таймером.
|
|
157
|
+
*
|
|
158
|
+
* Обмен идёт отдельным экземпляром axios внутри SDK и таймаута не имеет вовсе,
|
|
159
|
+
* поэтому зависший сокет означал бы вечно занятый мьютекс: все дальнейшие обновления
|
|
160
|
+
* приклеились бы к мёртвому промису, а через час процесс начал бы получать 401
|
|
161
|
+
* на каждый вызов. Сторож это разрывает.
|
|
162
|
+
*
|
|
163
|
+
* Сторож освобождает ТОЛЬКО мьютекс: сам обмен не отменяется и его результат
|
|
164
|
+
* не отбрасывается. Отменить его нечем (axios-клиент создан внутри SDK, сигнала
|
|
165
|
+
* туда не передать), а гонка `Promise.race` потеряла бы токен, пришедший секундой
|
|
166
|
+
* позже. Поздний успех всё равно попадёт в authB24.json: колбэк сохранения SDK
|
|
167
|
+
* зовёт сам, внутри refreshAuth(), до возврата значения (oauth/auth.mjs:106–110).
|
|
168
|
+
*/
|
|
169
|
+
function refreshAuthWithMutex(): Promise<AuthData> {
|
|
170
|
+
if (refreshInProgress) {
|
|
171
|
+
logs.add("refreshAuth: ожидаем завершения текущего refresh", "debug");
|
|
172
|
+
return refreshInProgress;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const ticket = {};
|
|
176
|
+
refreshTicket = ticket;
|
|
177
|
+
|
|
178
|
+
// Промис живёт своей жизнью: сторож его не отменяет и результат не отбрасывает
|
|
179
|
+
const work = runRefreshAttempts();
|
|
180
|
+
|
|
181
|
+
const guarded = new Promise<AuthData>((resolve, reject) => {
|
|
182
|
+
const watchdog = setTimeout(() => {
|
|
183
|
+
const released = releaseRefreshMutex(ticket);
|
|
184
|
+
|
|
185
|
+
// Билет мог уже отобрать resetRefreshMutex() из reinitializeB24() —
|
|
186
|
+
// тогда мьютекс принадлежит другому обмену, и трогать его нечем
|
|
187
|
+
const mutexPart = released
|
|
188
|
+
? "мьютекс освобождён"
|
|
189
|
+
: "обмен признан зависшим, мьютекс уже принадлежит другому обмену";
|
|
190
|
+
|
|
191
|
+
// Уровень warn, а не error, намеренно: брошенную ниже ошибку поймает
|
|
192
|
+
// refreshAndSaveTokens и напишет свою строку уровня error, а error уходит
|
|
193
|
+
// в чат B24 — иначе на одно залипание пришло бы два сообщения
|
|
194
|
+
logs.add(
|
|
195
|
+
`refreshAuth: обмен токена не завершился за ${REFRESH_STUCK_TIMEOUT_MS / 60000} мин — ${mutexPart}, зависший запрос продолжает работу и сохранит токены сам, если ответ придёт`,
|
|
196
|
+
"warn"
|
|
197
|
+
);
|
|
198
|
+
reject(new Error(`обмен refresh-токена завис (не завершился за ${REFRESH_STUCK_TIMEOUT_MS / 60000} мин)`));
|
|
199
|
+
}, REFRESH_STUCK_TIMEOUT_MS);
|
|
200
|
+
|
|
201
|
+
// unref(), чтобы сторож не держал процесс живым: у потребителя это фоновая
|
|
202
|
+
// страховка, а не работа. Приведение типа нужно потому, что пакет публикуется
|
|
203
|
+
// исходниками — у потребителя с lib.dom в tsconfig setTimeout может резолвиться
|
|
204
|
+
// в браузерную перегрузку, возвращающую number
|
|
205
|
+
(watchdog as unknown as { unref?: () => void }).unref?.();
|
|
206
|
+
|
|
207
|
+
const settle = (finish: () => void) => {
|
|
208
|
+
clearTimeout(watchdog);
|
|
209
|
+
releaseRefreshMutex(ticket);
|
|
210
|
+
finish();
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Обработчика обязательно два: поздний отказ зависшего обмена без своего
|
|
214
|
+
// обработчика стал бы unhandled rejection и уронил процесс потребителя.
|
|
215
|
+
// По той же причине здесь нельзя .finally() — производный промис отклонится
|
|
216
|
+
work.then(
|
|
217
|
+
(authData) => settle(() => resolve(authData)),
|
|
218
|
+
(error: unknown) => settle(() => reject(error))
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
refreshInProgress = guarded;
|
|
223
|
+
return guarded;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Обновляет токены через мьютекс и сохраняет в файл.
|
|
228
|
+
* Используется проактивным таймером и может вызываться вручную.
|
|
229
|
+
*/
|
|
230
|
+
export async function refreshAndSaveTokens(): Promise<SaveResult> {
|
|
231
|
+
if (!$b24) {
|
|
232
|
+
return { error: true, message: "$b24 не инициализирован." };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const authData = await refreshAuthWithMutex();
|
|
237
|
+
return saveTokens(authData);
|
|
238
|
+
} catch (error: unknown) {
|
|
239
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
240
|
+
logs.add(`Ошибка обновления токенов: ${message}`, "error");
|
|
241
|
+
return { error: true, message };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ==================== Проактивное обновление токена ====================
|
|
246
|
+
|
|
247
|
+
let proactiveRefreshTimer: ReturnType<typeof setTimeout> | null = null;
|
|
248
|
+
|
|
249
|
+
/** Запускает проактивное обновление токена. При ошибке — повтор через 2 мин, при успехе — через 25 мин */
|
|
250
|
+
export function startProactiveRefresh(): void {
|
|
251
|
+
if (proactiveRefreshTimer) {
|
|
252
|
+
clearTimeout(proactiveRefreshTimer);
|
|
253
|
+
proactiveRefreshTimer = null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const scheduleNext = (delayMs: number) => {
|
|
257
|
+
proactiveRefreshTimer = setTimeout(async () => {
|
|
258
|
+
logs.add("Проактивное обновление токена Bitrix24", "debug");
|
|
259
|
+
const result = await refreshAndSaveTokens();
|
|
260
|
+
|
|
261
|
+
if (result.error) {
|
|
262
|
+
logs.add(`Ошибка проактивного обновления токена, повтор через ${PROACTIVE_RETRY_ON_ERROR_MS / 60000} мин`, "error");
|
|
263
|
+
scheduleNext(PROACTIVE_RETRY_ON_ERROR_MS);
|
|
264
|
+
} else {
|
|
265
|
+
scheduleNext(PROACTIVE_REFRESH_INTERVAL_MS);
|
|
266
|
+
}
|
|
267
|
+
}, delayMs);
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
scheduleNext(PROACTIVE_REFRESH_INTERVAL_MS);
|
|
271
|
+
logs.add(`Проактивное обновление токена запущено (каждые ${PROACTIVE_REFRESH_INTERVAL_MS / 60000} мин)`, "debug");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Останавливает проактивное обновление токена */
|
|
275
|
+
export function stopProactiveRefresh(): void {
|
|
276
|
+
if (!proactiveRefreshTimer) return;
|
|
277
|
+
clearTimeout(proactiveRefreshTimer);
|
|
278
|
+
proactiveRefreshTimer = null;
|
|
279
|
+
logs.add("Проактивное обновление токена остановлено", "debug");
|
|
280
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { B24OAuth, AjaxResult, Result, BatchCommandsArrayUniversal, BatchCommandsObjectUniversal, BatchNamedCommandsUniversal } from "@bitrix24/b24jssdk";
|
|
2
|
+
|
|
3
|
+
// ==================== Типы ====================
|
|
4
|
+
|
|
5
|
+
/** Результат операций с токенами */
|
|
6
|
+
export interface SaveResult {
|
|
7
|
+
error: boolean;
|
|
8
|
+
message: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Минимальный контракт HTTP-запроса, который читает `saveAuthB24Handler`.
|
|
13
|
+
*
|
|
14
|
+
* Своя структура вместо `Request` из `@types/express`: три потребителя
|
|
15
|
+
* (`b24_rnp`, `b24_ai`, `marketing_vk_ads`) `express` не ставят вовсе и жили
|
|
16
|
+
* на нашей копии типов, приезжавшей транзитивно. С 4.0.0 `@types/*` в
|
|
17
|
+
* `devDependencies`, поэтому такой копии больше нет.
|
|
18
|
+
*
|
|
19
|
+
* Express подставляет сюда свой `Request` без правок у потребителя:
|
|
20
|
+
* параметры контравариантны, и `Request.body: any` подходит под `body: unknown`.
|
|
21
|
+
*/
|
|
22
|
+
export interface AuthSaveRequest {
|
|
23
|
+
body: unknown;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Минимальный контракт HTTP-ответа, который пишет `saveAuthB24Handler` */
|
|
27
|
+
export interface AuthSaveResponse {
|
|
28
|
+
status(code: number): { json(body: unknown): unknown };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* `B24OAuth` плюс пять методов, которые пакет реализует сам поверх `actions.v2.*`.
|
|
33
|
+
*
|
|
34
|
+
* SDK 2.x помечает `callMethod`/`callListMethod`/`fetchListMethod`/`callBatch`/`callBatchByChunk`
|
|
35
|
+
* к удалению в следующем major. Пакет предоставляет их сам, поэтому сигнатуры для потребителя
|
|
36
|
+
* не меняются, а зависимости от удаляемого API у нас больше нет.
|
|
37
|
+
*
|
|
38
|
+
* Тип расширяет `B24OAuth`, поэтому `$b24` по-прежнему передаётся туда, где ждут `B24OAuth`
|
|
39
|
+
* (`new Smsgold(auth, $b24)`, `new Event($b24)`).
|
|
40
|
+
*
|
|
41
|
+
* Пять сигнатур ниже объявлены НАМЕРЕННО и сегодня дублируют то, что и так приходит
|
|
42
|
+
* по наследству от `B24OAuth`. Удалять их нельзя: в SDK 3.x этих методов в `AbstractB24`
|
|
43
|
+
* не будет, и вместе с ними из типа пропадёт вся поверхность фасада — а тайпчек на
|
|
44
|
+
* SDK 2.2.0 останется зелёным, то есть пропажа обнаружится только у потребителя.
|
|
45
|
+
* Страховка от «уборки дублей» — `tests/client-type.test.ts`: он читает текст этого
|
|
46
|
+
* файла и падает, если хоть одно объявление отсюда исчезло.
|
|
47
|
+
*
|
|
48
|
+
* `any` в сигнатурах оставлен НАМЕРЕННО и снятию по T-53 не подлежит: это дефолты
|
|
49
|
+
* дженериков публичной поверхности, дословно повторяющие `AbstractB24`. На них стоит
|
|
50
|
+
* код потребителей — `for await (const chunk of $b24.fetchListMethod(...))
|
|
51
|
+
* chunk.forEach((x) => x.ID)` и `callBatch([["crm.deal.get", { id }]])`. `unknown`
|
|
52
|
+
* в дефолте уронил бы каждое такое место, ничего не дав взамен: узкий тип задаётся
|
|
53
|
+
* параметром типа на месте вызова (`callMethod<Deal>(...)`).
|
|
54
|
+
*/
|
|
55
|
+
export interface B24Client extends B24OAuth {
|
|
56
|
+
callMethod<T = any>(method: string, params?: object, start?: number): Promise<AjaxResult<T>>;
|
|
57
|
+
callListMethod(method: string, params?: object, progress?: null | ((progress: number) => void), customKeyForResult?: string | null): Promise<Result>;
|
|
58
|
+
fetchListMethod(method: string, params?: any, idKey?: string, customKeyForResult?: string | null): AsyncGenerator<any[]>;
|
|
59
|
+
callBatch(calls: Array<any> | object, isHaltOnError?: boolean, returnAjaxResult?: boolean): Promise<Result>;
|
|
60
|
+
callBatchByChunk(calls: Array<any>, isHaltOnError: boolean): Promise<Result>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Минимальный контракт «умеет вызывать REST-методы B24».
|
|
65
|
+
*
|
|
66
|
+
* Нужен внутренним классам (`Event`, `Smsgold`), которые принимают экземпляр извне.
|
|
67
|
+
* Объявляет `callMethod` сам, поэтому переживёт удаление метода из `B24OAuth`.
|
|
68
|
+
*
|
|
69
|
+
* Объединение `B24OAuth | B24Client` эту задачу не решает: `B24Client` расширяет
|
|
70
|
+
* `B24OAuth`, union схлопывается в супертип, а вызов метода на union требует его
|
|
71
|
+
* наличия в обеих ветках — то есть после удаления из SDK тайпчек сломается всё равно.
|
|
72
|
+
*
|
|
73
|
+
* Структурный тип шире `B24OAuth`, поэтому публичные сигнатуры конструкторов
|
|
74
|
+
* не сужаются: и `B24OAuth`, и `$b24`, и любой свой объект с `callMethod` подходят.
|
|
75
|
+
*/
|
|
76
|
+
export interface B24MethodCaller {
|
|
77
|
+
// Дженерика здесь быть не должно: у SDK метод не generic, и `B24OAuth`
|
|
78
|
+
// потребителя перестал бы подходить под этот тип. Проверено тайпчеком
|
|
79
|
+
callMethod(method: string, params?: object, start?: number): Promise<AjaxResult>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Набор методов фасада — то, что Proxy отдаёт вместо реализаций SDK */
|
|
83
|
+
export type FacadeMethods = Pick<B24Client, "callMethod" | "callListMethod" | "fetchListMethod" | "callBatch" | "callBatchByChunk">;
|
|
84
|
+
|
|
85
|
+
/** Формы списка команд batch, которые принимает actions.v2 */
|
|
86
|
+
export type BatchCalls = BatchCommandsArrayUniversal | BatchCommandsObjectUniversal | BatchNamedCommandsUniversal;
|
|
87
|
+
|
|
88
|
+
/** Конверт ответа restApi:v2 — то, что прислал портал, до того как AjaxResult его урезал */
|
|
89
|
+
export interface V2Envelope {
|
|
90
|
+
result?: unknown;
|
|
91
|
+
next?: unknown;
|
|
92
|
+
total?: unknown;
|
|
93
|
+
}
|