@andrey4emk/npm-app-back-b24 3.8.0 → 3.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -474,6 +474,8 @@ const sent = await fetchWithTimeout("https://api.example.com/send", { method: "P
474
474
  processId: "12345",
475
475
  message: [
476
476
  {
477
+ messageId: "7cf5d29bee90cc37382a9fff07fc2a43", // MESSAGE_ID записи очереди — ключ для clear()
478
+ eventId: "4821", // ID записи очереди, информационное; в clear() не передавать
477
479
  connectorId: "...",
478
480
  lineId: "...",
479
481
  chatId: "...",
@@ -501,6 +503,8 @@ const sent = await fetchWithTimeout("https://api.example.com/send", { method: "P
501
503
  - Автоматически фильтрует и удаляет события от системного пользователя (`user_id: '138'`).
502
504
  - Для коннекторных событий конвертирует файлы в формат с типами `image`, `video`, `document`.
503
505
  - Если событий нет, возвращает `data` с пустыми массивами.
506
+ - **Гранулярность очистки — запись очереди, а не сообщение.** Одна запись коннектора (один `MESSAGE_ID`) может нести несколько сообщений, и они приходят отдельными элементами `message` с одинаковым `messageId`. Очистка по этому ключу удаляет запись целиком: если из двух сообщений одной записи ушло одно, `clear()` заберёт и неотправленное. Строить поэлементную очистку с точностью до сообщения на `messageId` нельзя. Для дедупликации отдельных сообщений он не годится по той же причине — у сообщений одной записи он общий, для этого есть `im.id`. Обратная сторона: запись, из которой не разобрано ни одного сообщения, в `data.message` не попадёт вовсе, и очистка по собранным `messageId` её не заберёт. Безопасный режим — `clear(processId)` целиком.
507
+ - Типы `ConnectorMessage`, `ConnectorEvents` и `EntityEvents` экспортируются из пакета.
504
508
 
505
509
  ```js
506
510
  const { error, data } = await event.get("ONCRMDYNAMICITEMUPDATE_149");
@@ -514,7 +518,7 @@ const sent = await fetchWithTimeout("https://api.example.com/send", { method: "P
514
518
  - **Параметры:**
515
519
 
516
520
  - `processId` (string) — ID процесса (получен из `get()`).
517
- - `messageId` (string|string[]) — опциональный ID или массив ID сообщений для очистки.
521
+ - `messageId` (string|string[]) — опциональный `MESSAGE_ID` или массив `MESSAGE_ID` записей очереди для очистки. Без него очищается весь пакет `processId`. Пустой массив означает «очищать нечего»: вызов до API не доходит и возвращает `error: false` (раньше уезжало `message_id: []`). «Мягкая» ошибка портала, например `ERROR_ENTITY_NOT_FOUND` на протухшем `processId`, возвращается как `error: true`.
518
522
 
519
523
  - **Возвращает:** промис с объектом `{ error, message, data }`.
520
524
 
@@ -3,7 +3,18 @@ import { $b24, getResultData, type B24MethodCaller } from "./b24.ts";
3
3
  // ==================== Типы ====================
4
4
 
5
5
  /** Сообщение из коннектора */
6
- interface ConnectorMessage {
6
+ export interface ConnectorMessage {
7
+ /**
8
+ * `MESSAGE_ID` записи очереди офлайн-событий — ключ для `clear()`.
9
+ * Идентифицирует запись, а не отдельное сообщение: у сообщений одной записи
10
+ * он общий, поэтому для дедупликации сообщений не годится (для этого `im.id`)
11
+ */
12
+ messageId: string;
13
+ /**
14
+ * `ID` записи очереди офлайн-событий. Поле информационное, для сверки
15
+ * с `event.offline.list`. В `clear()` не передавать — метод принимает `MESSAGE_ID`
16
+ */
17
+ eventId: string;
7
18
  connectorId: string;
8
19
  lineId: string;
9
20
  chatId: string;
@@ -14,7 +25,7 @@ interface ConnectorMessage {
14
25
  }
15
26
 
16
27
  /** События коннектора */
17
- interface ConnectorEvents {
28
+ export interface ConnectorEvents {
18
29
  processId: string | null;
19
30
  message: ConnectorMessage[];
20
31
  }
@@ -26,7 +37,7 @@ interface MessageEntityLink {
26
37
  }
27
38
 
28
39
  /** События сущностей (сделки, смарт-процессы) */
29
- interface EntityEvents {
40
+ export interface EntityEvents {
30
41
  processId: string | null;
31
42
  entitysId: string[];
32
43
  arrMessageIdAndEntityId: MessageEntityLink[];
@@ -41,6 +52,7 @@ interface StandardResult {
41
52
 
42
53
  /** Офлайн-событие Bitrix24 */
43
54
  interface OfflineEvent {
55
+ ID: string;
44
56
  MESSAGE_ID: string;
45
57
  EVENT_NAME: string;
46
58
  EVENT_DATA: any;
@@ -148,7 +160,19 @@ export class Event {
148
160
  }
149
161
 
150
162
  /**
151
- * Получает офлайн-события по имени
163
+ * Получает офлайн-события по имени.
164
+ *
165
+ * Гранулярность очистки — запись очереди, а не сообщение. Для коннектора одна
166
+ * запись (один `MESSAGE_ID`) может нести несколько сообщений, и `get()` раскладывает
167
+ * их через `flatMap` — у таких сообщений `messageId` совпадает. Очистка по этому
168
+ * ключу удалит запись целиком: если из двух сообщений одной записи ушло одно,
169
+ * `clear()` заберёт и неотправленное. Поэлементную очистку с точностью до
170
+ * сообщения на этом ключе построить нельзя.
171
+ *
172
+ * Обратная сторона: запись, из которой не разобрано ни одного сообщения
173
+ * (пустой или битый `MESSAGES`), в `data.message` не попадает вовсе — очистка
174
+ * по собранным `messageId` её не заберёт, и она останется зарезервированной
175
+ * под тем же `process_id`. Безопасный режим — `clear(processId)` целиком.
152
176
  */
153
177
  async get(eventName: string): Promise<StandardResult> {
154
178
  try {
@@ -184,6 +208,10 @@ export class Event {
184
208
  return normalizeMessages(eventData.MESSAGES)
185
209
  .filter((messageData) => messageData?.chat && messageData?.message)
186
210
  .map((messageData) => ({
211
+ // Портал отдаёт идентификаторы то строкой, то числом —
212
+ // приводим, чтобы тип `string` не врал
213
+ messageId: String(event.MESSAGE_ID ?? ""),
214
+ eventId: String(event.ID ?? ""),
187
215
  connectorId: eventData.CONNECTOR,
188
216
  lineId: eventData.LINE,
189
217
  chatId: messageData.chat.id,
@@ -210,6 +238,9 @@ export class Event {
210
238
 
211
239
  if (arrEventsToClear.length > 0) {
212
240
  const arrMessageIdToClear = arrEventsToClear.map((event) => event.MESSAGE_ID);
241
+ // Результат намеренно не проверяется: отказ здесь не фатален — те же
242
+ // записи придут следующим опросом, а бросок лишил бы потребителя всей
243
+ // выборки активных событий
213
244
  await this.b24.callMethod("event.offline.clear", {
214
245
  process_id: arrOfflineEvents.process_id,
215
246
  message_id: arrMessageIdToClear,
@@ -243,11 +274,29 @@ export class Event {
243
274
  */
244
275
  async clear(processId: string, messageId?: string | string[]): Promise<StandardResult> {
245
276
  try {
277
+ // Пустой массив в JavaScript истинен и уезжал в теле запроса как `message_id: []` —
278
+ // это не документированный режим «очистить весь пакет» (что портал делает с
279
+ // пустым списком, не проверялось). Считаем пустой список за «очищать нечего»
280
+ // и до API не доходим: молча сносить весь пакет по пустому списку опасно —
281
+ // зарезервированный пакет портал повторно не выдаёт
282
+ if (Array.isArray(messageId) && messageId.length === 0) {
283
+ return { error: false, message: "Список записей пуст, очищать нечего", data: null };
284
+ }
285
+
246
286
  const params: any = { process_id: processId };
247
287
  if (messageId) {
248
288
  params.message_id = messageId;
249
289
  }
250
- await this.b24.callMethod("event.offline.clear", params);
290
+ const response = await this.b24.callMethod("event.offline.clear", params);
291
+ // «Мягкую» ошибку (например, ERROR_ENTITY_NOT_FOUND на протухшем process_id)
292
+ // SDK не бросает, а возвращает AjaxResult с isSuccess: false. Без проверки
293
+ // метод рапортовал бы «События очищены» при неочищенной очереди.
294
+ // Именно isSuccess, а не getResultData(): та бросает ещё и на result == null,
295
+ // а что портал кладёт в result этого метода, не проверено
296
+ if (!response.isSuccess) {
297
+ const messages = response.getErrorMessages().join("; ") || "неизвестная ошибка";
298
+ throw new Error(`event.offline.clear: Bitrix24 вернул ошибку — ${messages}`);
299
+ }
251
300
 
252
301
  return { error: false, message: "События очищены", data: null };
253
302
  } catch (error: any) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrey4emk/npm-app-back-b24",
3
- "version": "3.8.0",
3
+ "version": "3.8.1",
4
4
  "description": "Bitrix24 OAuth helpers for Node.js projects",
5
5
  "main": "index.ts",
6
6
  "type": "module",