itd-api 0.0.7 → 0.0.9

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.
@@ -1,3900 +0,0 @@
1
- /** Метка билдера. Через `Symbol.for` — чтобы распознавание переживало смешивание ESM и CJS. */
2
- declare const BUILDER: unique symbol;
3
- /**
4
- * Билдер входных данных.
5
- *
6
- * Билдеры необязательны: любой метод, принимающий билдер, принимает и обычный объект.
7
- * Проверки одинаковы в обоих случаях.
8
- */
9
- interface ItdBuilder<T> {
10
- /** @internal */
11
- readonly [BUILDER]: true;
12
- /**
13
- * Собирает и проверяет результат.
14
- *
15
- * @throws {ItdConfigError} если нарушены требования к данным
16
- */
17
- build(): T;
18
- /** Чтобы билдер корректно вёл себя внутри `JSON.stringify`. */
19
- toJSON(): T;
20
- }
21
- /**
22
- * Три равноправные формы входа: обычный объект, готовый билдер или функция-настройщик.
23
- *
24
- * @example
25
- * ```ts
26
- * itd.posts.create({ content: 'привет' }); // объект
27
- * itd.posts.create(post().content('привет')); // билдер
28
- * itd.posts.create((p) => p.content('привет')); // функция
29
- * ```
30
- */
31
- type BuilderInput<T, B extends ItdBuilder<T>> = T | B | ((builder: B) => B | T);
32
- /** Является ли значение билдером. */
33
- declare function isBuilder<T>(value: unknown): value is ItdBuilder<T>;
34
-
35
- /**
36
- * Перечисления API итд.com.
37
- *
38
- * Здесь намеренно не используется `enum` из TypeScript. Вместо него — пара «замороженный
39
- * объект + одноимённый тип». Такой приём даёт всё, ради чего берут `enum`
40
- * (`FeedTab.Popular`, перебор значений в рантайме), и при этом:
41
- *
42
- * - **стирается без остатка** — `enum` порождает рантайм-код и отвергается средами,
43
- * которые просто срезают типы (`node --experimental-strip-types`);
44
- * - **не запрещает обычные строки** — `itd.posts.list({ tab: 'popular' })` остаётся валидным,
45
- * тогда как строковый `enum` считает это ошибкой типа и вынуждает всех импортировать себя;
46
- * - **позволяет открытые множества** — там, где документация перечисляет значения не полностью,
47
- * тип расширяется через {@link Loose}, а объект остаётся справочником известных значений.
48
- *
49
- * @example
50
- * ```ts
51
- * import { FeedTab } from 'itd-api';
52
- *
53
- * await itd.posts.list({ tab: FeedTab.Popular }); // без магических строк
54
- * await itd.posts.list({ tab: 'popular' }); // и так тоже можно
55
- *
56
- * Object.values(FeedTab); // ['popular', 'following', 'clan']
57
- * ```
58
- *
59
- * @packageDocumentation
60
- */
61
- /**
62
- * Открытое строковое перечисление.
63
- *
64
- * Даёт автодополнение известных значений, но не ломается, если сервер пришлёт новое.
65
- * Используется там, где документация API перечисляет значения не полностью («`everyone` и др.»).
66
- */
67
- type Loose<T extends string> = T | (string & {});
68
- /**
69
- * Вкладка ленты `GET /api/posts`.
70
- *
71
- * Множество закрытое: неизвестное значение сервер отвергнет.
72
- */
73
- declare const FeedTab: Readonly<{
74
- /** Популярное. Курсор здесь — номер страницы в виде строки (`"2"`, `"6"`…). */
75
- readonly Popular: "popular";
76
- /** Записи тех, на кого вы подписаны. Курсор — отметка времени последнего поста. */
77
- readonly Following: "following";
78
- /** Лента клана. Курсор, как и в подписках, — отметка времени. */
79
- readonly Clan: "clan";
80
- }>;
81
- type FeedTab = (typeof FeedTab)[keyof typeof FeedTab];
82
- /** Порядок комментариев к посту. */
83
- declare const CommentSort: Readonly<{
84
- /** Сначала новые. */
85
- readonly Newest: "newest";
86
- /** Сначала старые. */
87
- readonly Oldest: "oldest";
88
- /** Сначала популярные. */
89
- readonly Popular: "popular";
90
- }>;
91
- type CommentSort = (typeof CommentSort)[keyof typeof CommentSort];
92
- /** Тип вложения. */
93
- declare const AttachmentType: Readonly<{
94
- readonly Image: "image";
95
- readonly Video: "video";
96
- /** Голосовые комментарии: `audio/ogg`, с полем `duration`. */
97
- readonly Audio: "audio";
98
- }>;
99
- type AttachmentType = (typeof AttachmentType)[keyof typeof AttachmentType];
100
- /**
101
- * Тип фрагмента разметки в тексте поста или комментария.
102
- *
103
- * Первые два сервер расставляет сам при разборе текста, остальные приходят от редактора.
104
- * Тип открытый: набор может пополниться.
105
- *
106
- * @example
107
- * ```ts
108
- * await itd.posts.update(postId, {
109
- * content: 'жирное слово',
110
- * spans: [{ type: SpanType.Bold, offset: 0, length: 6 }],
111
- * });
112
- * ```
113
- */
114
- declare const SpanType: Readonly<{
115
- /** Хэштег. Название без решётки лежит в `tag`. */
116
- readonly Hashtag: "hashtag";
117
- /** Упоминание. Имя пользователя лежит в `tag`. */
118
- readonly Mention: "mention";
119
- /** Ссылка. Адрес лежит в `url`, а не в `tag`. */
120
- readonly Link: "link";
121
- readonly Bold: "bold";
122
- readonly Italic: "italic";
123
- readonly Underline: "underline";
124
- /** Зачёркнутый. */
125
- readonly Strike: "strike";
126
- /** Спойлер: текст скрыт до нажатия. */
127
- readonly Spoiler: "spoiler";
128
- /** Моноширинный. */
129
- readonly Monospace: "monospace";
130
- readonly Quote: "quote";
131
- }>;
132
- type SpanType = Loose<(typeof SpanType)[keyof typeof SpanType]>;
133
- /** На что подаётся жалоба. */
134
- declare const ReportTargetType: Readonly<{
135
- readonly Post: "post";
136
- readonly Comment: "comment";
137
- readonly User: "user";
138
- }>;
139
- type ReportTargetType = (typeof ReportTargetType)[keyof typeof ReportTargetType];
140
- /** Причина жалобы. Множество закрытое. */
141
- declare const ReportReason: Readonly<{
142
- readonly Spam: "spam";
143
- readonly Violence: "violence";
144
- readonly Hate: "hate";
145
- readonly Adult: "adult";
146
- readonly Fraud: "fraud";
147
- readonly Other: "other";
148
- }>;
149
- type ReportReason = (typeof ReportReason)[keyof typeof ReportReason];
150
- /** Состояние realtime-соединения. */
151
- declare const RealtimeStatus: Readonly<{
152
- readonly Connecting: "connecting";
153
- readonly Connected: "connected";
154
- readonly Error: "error";
155
- readonly Disconnected: "disconnected";
156
- }>;
157
- type RealtimeStatus = (typeof RealtimeStatus)[keyof typeof RealtimeStatus];
158
- /**
159
- * Уровень доступа к разделу профиля.
160
- *
161
- * Общий набор значений для полей `wallAccess` и `likesVisibility` настроек приватности.
162
- * Тип открытый: сервер может прислать значение вне этого перечня.
163
- */
164
- declare const AccessType: Readonly<{
165
- /** Никто. */
166
- readonly Nobody: "nobody";
167
- /** Только взаимные подписки. */
168
- readonly Mutual: "mutual";
169
- /** Подписчики. */
170
- readonly Followers: "followers";
171
- /** Все. */
172
- readonly Everyone: "everyone";
173
- }>;
174
- type AccessType = Loose<(typeof AccessType)[keyof typeof AccessType]>;
175
- /** Кто может писать на стену профиля. Псевдоним {@link AccessType}. */
176
- declare const WallAccess: Readonly<{
177
- /** Никто. */
178
- readonly Nobody: "nobody";
179
- /** Только взаимные подписки. */
180
- readonly Mutual: "mutual";
181
- /** Подписчики. */
182
- readonly Followers: "followers";
183
- /** Все. */
184
- readonly Everyone: "everyone";
185
- }>;
186
- type WallAccess = AccessType;
187
- /** Кто видит реакции пользователя. Псевдоним {@link AccessType}. */
188
- declare const LikesVisibility: Readonly<{
189
- /** Никто. */
190
- readonly Nobody: "nobody";
191
- /** Только взаимные подписки. */
192
- readonly Mutual: "mutual";
193
- /** Подписчики. */
194
- readonly Followers: "followers";
195
- /** Все. */
196
- readonly Everyone: "everyone";
197
- }>;
198
- type LikesVisibility = AccessType;
199
- /**
200
- * Канонический тип уведомления (новое поколение имён).
201
- *
202
- * REST-эндпоинт `/api/notifications/` отдаёт старые имена (`like`, `comment`, `reply`,
203
- * `repost`, `mention`), SSE-поток — новые. Библиотека приводит их к этому набору,
204
- * сохраняя исходное значение в поле `rawType`.
205
- */
206
- declare const NotificationType: Readonly<{
207
- /** Реакция на пост. Старое имя — `like`. */
208
- readonly PostReaction: "post_reaction";
209
- /** Комментарий к посту. Старое имя — `comment`. */
210
- readonly PostComment: "post_comment";
211
- /** Ответ на комментарий. Старое имя — `reply`. */
212
- readonly CommentReply: "comment_reply";
213
- /** Репост. Старое имя — `repost`. */
214
- readonly PostRepost: "post_repost";
215
- /** Упоминание в посте. Старое имя — `mention`. */
216
- readonly PostMention: "post_mention";
217
- /** Реакция на комментарий. */
218
- readonly CommentReaction: "comment_reaction";
219
- /** Упоминание в комментарии. */
220
- readonly CommentMention: "comment_mention";
221
- /** Запись на вашей стене. */
222
- readonly WallPost: "wall_post";
223
- /** На вас подписались. */
224
- readonly Follow: "follow";
225
- /** Заявка на подписку (закрытый профиль). */
226
- readonly FollowRequest: "follow_request";
227
- /** Заявка на подписку принята. */
228
- readonly FollowAccepted: "follow_accepted";
229
- /** Верификация одобрена. Приходит только по REST. */
230
- readonly VerificationApproved: "verification_approved";
231
- /** Верификация отклонена. Приходит только по REST. */
232
- readonly VerificationRejected: "verification_rejected";
233
- }>;
234
- type NotificationType = Loose<(typeof NotificationType)[keyof typeof NotificationType]>;
235
- /**
236
- * Тип взаимодействия с контентом в телеметрии (`POST /api/v1/x`, поле `t`).
237
- *
238
- * Кодируется числом.
239
- */
240
- declare const InteractionType: Readonly<{
241
- /** Открытие фотографии. */
242
- readonly PhotoOpen: 1;
243
- /** Прогресс просмотра видео. Несёт поля `pm`/`dm`. */
244
- readonly VideoProgress: 2;
245
- }>;
246
- type InteractionType = (typeof InteractionType)[keyof typeof InteractionType];
247
- /**
248
- * Источник показа поста в телеметрии (поле `s`).
249
- *
250
- * Кодируется числом. Поле применимо к источникам `PostPage` и `Link`; для лент источник
251
- * передаётся контекстом `sc`.
252
- */
253
- declare const ViewSource: Readonly<{
254
- readonly FeedGlobal: 1;
255
- readonly FeedFollowing: 2;
256
- readonly FeedClan: 3;
257
- readonly Profile: 4;
258
- readonly Hashtag: 5;
259
- readonly PostPage: 6;
260
- readonly Link: 7;
261
- readonly Search: 8;
262
- }>;
263
- type ViewSource = (typeof ViewSource)[keyof typeof ViewSource];
264
- /**
265
- * Причина завершения просмотра поста в телеметрии (`POST /api/v1/i`, поле `r`).
266
- *
267
- * Кодируется числом.
268
- */
269
- declare const ViewReason: Readonly<{
270
- /** Пост ушёл из зоны видимости при обычной прокрутке. */
271
- readonly Normal: 0;
272
- /** Потеря фокуса окна. */
273
- readonly Blur: 1;
274
- /** Вкладка скрыта. */
275
- readonly Hidden: 2;
276
- /** Уход со страницы (`pagehide`). */
277
- readonly PageHide: 3;
278
- /** Элемент перестал наблюдаться. */
279
- readonly Unobserve: 4;
280
- /** Достигнут порог времени просмотра. */
281
- readonly ThresholdMet: 5;
282
- }>;
283
- type ViewReason = (typeof ViewReason)[keyof typeof ViewReason];
284
- /**
285
- * Строковые коды ошибок из поля `code`.
286
- *
287
- * Ключи намеренно повторяют написание сервера: код из ответа API можно найти здесь
288
- * поиском один в один, без мысленного перевода регистра.
289
- *
290
- * Список открыт — сервер может добавить новый код, и это не должно ломать типизацию.
291
- *
292
- * @example
293
- * ```ts
294
- * if (err.hasCode(ItdErrorCode.OTP_INVALID)) await restartOtpFlow();
295
- * ```
296
- */
297
- declare const ItdErrorCode: Readonly<{
298
- readonly BAD_REQUEST: "BAD_REQUEST";
299
- readonly UNAUTHORIZED: "UNAUTHORIZED";
300
- readonly ACCESS_DENIED: "ACCESS_DENIED";
301
- readonly ENTITY_NOT_FOUND: "ENTITY_NOT_FOUND";
302
- readonly ENTITY_ALREADY_EXISTS: "ENTITY_ALREADY_EXISTS";
303
- readonly VALIDATION_ERROR: "VALIDATION_ERROR";
304
- readonly BUSINESS_RULE_VIOLATION: "BUSINESS_RULE_VIOLATION";
305
- readonly RATE_LIMIT_EXCEEDED: "RATE_LIMIT_EXCEEDED";
306
- readonly UNKNOWN_ERROR: "UNKNOWN_ERROR";
307
- /** Сервер отвечает так на `404`, `ENTITY_NOT_FOUND` в этом случае не приходит. */
308
- readonly NOT_FOUND: "NOT_FOUND";
309
- /** На практике не приходит: вместо него сервер шлёт `TURNSTILE_VERIFICATION_FAILED`. */
310
- readonly CAPTCHA_FAILED: "CAPTCHA_FAILED";
311
- /** Капча не пройдена: токен Turnstile недействителен, просрочен или уже использован. */
312
- readonly TURNSTILE_VERIFICATION_FAILED: "TURNSTILE_VERIFICATION_FAILED";
313
- readonly OTP_INVALID: "OTP_INVALID";
314
- /** `flowToken` неизвестен или просрочен — поток подтверждения нужно начинать заново. */
315
- readonly INVALID_FLOW_TOKEN: "INVALID_FLOW_TOKEN";
316
- readonly ACCOUNT_DEACTIVATED: "ACCOUNT_DEACTIVATED";
317
- readonly ACCOUNT_EMAIL_DOMAIN_NOT_ALLOWED: "ACCOUNT_EMAIL_DOMAIN_NOT_ALLOWED";
318
- readonly ACCOUNT_INVALID_CREDENTIALS: "ACCOUNT_INVALID_CREDENTIALS";
319
- readonly ACCOUNT_TEMPORARILY_LOCKED: "ACCOUNT_TEMPORARILY_LOCKED";
320
- readonly ACCOUNT_CURRENT_PASSWORD_INCORRECT: "ACCOUNT_CURRENT_PASSWORD_INCORRECT";
321
- readonly SESSION_EXPIRED: "SESSION_EXPIRED";
322
- readonly SESSION_REVOKED: "SESSION_REVOKED";
323
- readonly SESSION_INVALID_REFRESH_TOKEN: "SESSION_INVALID_REFRESH_TOKEN";
324
- /** Запрос обновления пришёл без cookie `refresh_token` — продлевать нечего. */
325
- readonly REFRESH_TOKEN_MISSING: "REFRESH_TOKEN_MISSING";
326
- /** Cookie `refresh_token` есть, но сессии за ней уже нет: отозвана или истекла. */
327
- readonly SESSION_NOT_FOUND: "SESSION_NOT_FOUND";
328
- readonly MISSING_FLOW_TOKEN: "MISSING_FLOW_TOKEN";
329
- readonly PROFILE_USERNAME_TAKEN: "PROFILE_USERNAME_TAKEN";
330
- readonly PROFILE_RESTRICTION_ACTIVE: "PROFILE_RESTRICTION_ACTIVE";
331
- readonly PROFILE_MODIFICATION_RESTRICTED: "PROFILE_MODIFICATION_RESTRICTED";
332
- readonly CONTENT_MODERATION_FAILED: "CONTENT_MODERATION_FAILED";
333
- readonly FILE_TOO_LARGE: "FILE_TOO_LARGE";
334
- readonly UNSUPPORTED_FILE_TYPE: "UNSUPPORTED_FILE_TYPE";
335
- readonly UPLOAD_FAILED: "UPLOAD_FAILED";
336
- readonly VIDEO_REQUIRES_VERIFICATION: "VIDEO_REQUIRES_VERIFICATION";
337
- readonly PHONE_VERIFICATION_REQUIRED: "PHONE_VERIFICATION_REQUIRED";
338
- readonly WRITE_ACCESS_RESTRICTED: "WRITE_ACCESS_RESTRICTED";
339
- }>;
340
- type ItdErrorCode = Loose<(typeof ItdErrorCode)[keyof typeof ItdErrorCode]>;
341
-
342
- /**
343
- * Дата и время в формате ISO-8601, например `2026-07-21T14:30:00.000Z`.
344
- *
345
- * Библиотека не превращает такие поля в `Date`: строку проще сравнивать, логировать
346
- * и передавать дальше без потерь. Для разбора есть {@link toDate}.
347
- */
348
- type IsoDate = string;
349
- /**
350
- * Идентификатор пользователя — **строго UUID**.
351
- *
352
- * Отличается от {@link UserRef} тем, что имя пользователя здесь не подойдёт. Так помечены
353
- * места, где API принимает только UUID: например `wallRecipientId` при постинге на чужую стену.
354
- */
355
- type UserId = string;
356
- /**
357
- * Ссылка на пользователя: **UUID либо имя пользователя**.
358
- *
359
- * Пути вида `/api/users/{id}` принимают оба варианта, поэтому `itd.users.get('durov')`
360
- * работает так же, как `itd.users.get('9f1c…')`.
361
- */
362
- type UserRef = string;
363
- /**
364
- * Разметка в тексте поста.
365
- *
366
- * Приходит от сервера и отправляется обратно как есть — библиотека разметку не генерирует
367
- * и не пересчитывает.
368
- *
369
- * Единицы `offset` и `length` в документации API не уточнены (UTF-16 или кодовые точки),
370
- * поэтому при работе с эмодзи проверяйте результат.
371
- */
372
- interface Span {
373
- /** Тип фрагмента — см. {@link SpanType}. */
374
- type: SpanType;
375
- /** Смещение от начала текста. */
376
- offset: number;
377
- /** Длина фрагмента. */
378
- length: number;
379
- /** Имя хэштега без решётки либо имя пользователя. */
380
- tag?: string;
381
- /** Адрес ссылки. Только у `link`: у него вместо `tag` отдельное поле. */
382
- url?: string;
383
- }
384
- /**
385
- * Значок-«пин» в профиле — награда или отметка платформы.
386
- */
387
- interface Pin {
388
- /** Постоянный идентификатор, например `epepuy_202605_59`. */
389
- slug: string;
390
- /** Отображаемое название. */
391
- name: string;
392
- /** Описание, за что выдан. */
393
- description: string;
394
- /** Адрес изображения. */
395
- url: string;
396
- /** Когда выдан. Приходит только в списке своих пинов. */
397
- grantedAt?: IsoDate;
398
- }
399
- /**
400
- * Автор поста или комментария.
401
- *
402
- * Встречается внутри `post.author` и `comment.author`.
403
- */
404
- interface Author {
405
- id: UserId;
406
- username: string;
407
- displayName: string;
408
- /**
409
- * **Эмодзи, а не картинка.**
410
- *
411
- * На итд.com аватар — это символ клана (`🩵`, `🦎`), а не адрес изображения.
412
- * Отрисовывать его нужно как текст.
413
- */
414
- avatar: string;
415
- /** Пройдена ли верификация. */
416
- verified: boolean;
417
- /** Активный значок профиля. Может отсутствовать. */
418
- pin?: Pin | null;
419
- /** Есть ли премиум-подписка (значок NUKSTA). */
420
- hasNuksta?: boolean;
421
- }
422
- /**
423
- * Участник события в уведомлении.
424
- *
425
- * Отличается от {@link Author} набором полей: вместо значков приходит связь с вами.
426
- */
427
- interface Actor {
428
- id: UserId;
429
- username: string;
430
- displayName: string;
431
- /** Эмодзи-аватар, см. {@link Author.avatar}. */
432
- avatar: string;
433
- /** Подписаны ли вы на этого пользователя. */
434
- isFollowing?: boolean;
435
- /** Подписан ли он на вас. */
436
- isFollowedBy?: boolean;
437
- }
438
- /**
439
- * Пользователь в списках.
440
- *
441
- * Набор полей зависит от эндпоинта: подписчики и подписки приносят `isFollowing`,
442
- * поиск и рекомендации — `followersCount` и `hasNuksta`. Необязательные поля отражают
443
- * это различие.
444
- */
445
- interface UserSummary {
446
- id: UserId;
447
- username: string;
448
- displayName: string;
449
- /** Эмодзи-аватар, см. {@link Author.avatar}. */
450
- avatar: string;
451
- verified: boolean;
452
- /** Подписаны ли вы. Приходит в списках подписчиков и подписок. */
453
- isFollowing?: boolean;
454
- /** Есть ли премиум. Приходит в поиске и рекомендациях. */
455
- hasNuksta?: boolean;
456
- /** Число подписчиков. Приходит в поиске и рекомендациях. */
457
- followersCount?: number;
458
- }
459
- /** Поля профиля, общие для своего и чужого. */
460
- interface ProfileBase {
461
- id: UserId;
462
- username: string;
463
- displayName: string;
464
- /** Эмодзи-аватар, см. {@link Author.avatar}. */
465
- avatar: string;
466
- /** Адрес изображения-шапки либо `null`. В отличие от аватара это настоящий URL. */
467
- banner: string | null;
468
- /** Описание профиля. */
469
- bio: string;
470
- verified: boolean;
471
- pin?: Pin | null;
472
- /** Кто может писать на стену. */
473
- wallAccess: WallAccess;
474
- /** Кто видит реакции. */
475
- likesVisibility: LikesVisibility;
476
- followersCount: number;
477
- followingCount: number;
478
- postsCount: number;
479
- createdAt: IsoDate;
480
- }
481
- /** Состояние подписки на премиум. */
482
- interface SubscriptionState {
483
- isActive: boolean;
484
- expiresAt: IsoDate | null;
485
- autoRenewal: boolean;
486
- }
487
- /**
488
- * Свой профиль — ответ `GET /api/users/me`.
489
- *
490
- * Отличается от чужого наличием {@link subscription} и {@link isPhoneVerified}
491
- * и отсутствием полей связи (`isFollowing`, `online`).
492
- */
493
- interface MyProfile extends ProfileBase {
494
- /** Закрыт ли профиль. */
495
- isPrivate: boolean;
496
- /** Подтверждён ли телефон. Без него часть действий недоступна. */
497
- isPhoneVerified: boolean;
498
- /** Своя премиум-подписка. */
499
- subscription: SubscriptionState;
500
- }
501
- /**
502
- * Чужой профиль — ответ `GET /api/users/{id|username}`.
503
- *
504
- * Вместо своей подписки содержит связь с вами и присутствие.
505
- */
506
- interface PublicProfile extends ProfileBase {
507
- hasNuksta?: boolean;
508
- /** Закреплённый пост, если он есть. */
509
- pinnedPostId: string | null;
510
- /** Подписаны ли вы на него. */
511
- isFollowing: boolean;
512
- /** Подписан ли он на вас. */
513
- isFollowedBy: boolean;
514
- /** Сейчас ли пользователь в сети. */
515
- online: boolean;
516
- /** Когда был в сети. `null`, если скрыто настройками приватности. */
517
- lastSeen: IsoDate | null;
518
- }
519
- /** Профиль: свой либо чужой. Различаются функцией {@link isMyProfile}. */
520
- type Profile = MyProfile | PublicProfile;
521
- /**
522
- * Свой ли это профиль.
523
- *
524
- * @example
525
- * ```ts
526
- * if (isMyProfile(profile)) console.log(profile.subscription.isActive);
527
- * ```
528
- */
529
- declare function isMyProfile(profile: Profile): profile is MyProfile;
530
- /** Вложение поста или комментария. */
531
- interface Attachment {
532
- id: string;
533
- type: AttachmentType;
534
- /** Адрес файла на CDN. */
535
- url: string;
536
- /** Ширина изображения или видео в пикселях. */
537
- width?: number;
538
- /** Высота изображения или видео в пикселях. */
539
- height?: number;
540
- mimeType: string;
541
- /** Исходное имя файла. Приходит не всегда. */
542
- filename?: string;
543
- /** Размер в байтах. Приходит не всегда. */
544
- size?: number;
545
- /** Длительность аудио или видео в секундах. */
546
- duration?: number | null;
547
- /** Порядковый номер во вложениях поста. */
548
- order?: number;
549
- }
550
- /** Вариант ответа в опросе. */
551
- interface PollOption {
552
- id: string;
553
- text: string;
554
- /** Сколько голосов отдано за этот вариант. */
555
- votesCount: number;
556
- /** Порядковый номер варианта, начиная с нуля. */
557
- position: number;
558
- }
559
- /** Опрос внутри поста. */
560
- interface Poll {
561
- id: string;
562
- /** Пост, которому принадлежит опрос. */
563
- postId: string;
564
- question: string;
565
- /** Можно ли выбрать несколько вариантов. */
566
- multipleChoice: boolean;
567
- options: PollOption[];
568
- totalVotes: number;
569
- /** Голосовали ли вы. */
570
- hasVoted: boolean;
571
- /** За что проголосовали вы. Пустой массив, если голоса не было. */
572
- votedOptionIds: string[];
573
- createdAt: IsoDate;
574
- }
575
- /** Пост ленты, стены или профиля. */
576
- interface Post {
577
- id: string;
578
- content: string;
579
- /** Разметка текста. Передаётся без изменений, см. {@link Span}. */
580
- spans: Span[];
581
- author: Author;
582
- attachments: Attachment[];
583
- likesCount: number;
584
- commentsCount: number;
585
- repostsCount: number;
586
- viewsCount: number;
587
- /** Чья это стена, если пост опубликован не у себя. */
588
- wallRecipientId: UserId | null;
589
- /** Владелец стены. Приходит не во всех ответах. */
590
- wallRecipient?: Author | null;
591
- /** Поставили ли вы реакцию. */
592
- isLiked: boolean;
593
- /** Делали ли вы репост. */
594
- isReposted: boolean;
595
- /** Засчитан ли просмотр. */
596
- isViewed: boolean;
597
- /** Ваш ли это пост. */
598
- isOwner: boolean;
599
- /** Исходный пост, если это репост. */
600
- originalPost?: Post | null;
601
- poll?: Poll | null;
602
- /** Преобладающая реакция — эмодзи либо `null`. */
603
- dominantEmoji?: string | null;
604
- /** Когда пост отредактировали. `null`, если не редактировали. */
605
- editedAt: IsoDate | null;
606
- createdAt: IsoDate;
607
- /**
608
- * Служебная метка показа для телеметрии.
609
- *
610
- * Нужна только эндпоинтам `itd.telemetry.*`. В остальных случаях игнорируйте.
611
- */
612
- vs?: string;
613
- /**
614
- * Топовые комментарии. Приходят только в ответе `GET /api/posts/{id}`.
615
- *
616
- * В списках постов поле отсутствует.
617
- */
618
- comments?: Comment[];
619
- }
620
- /** На чей комментарий дан ответ. */
621
- interface CommentReplyTo {
622
- id: string;
623
- username: string;
624
- displayName: string;
625
- }
626
- /** Комментарий к посту или ответ на комментарий. */
627
- interface Comment {
628
- id: string;
629
- /** Текст. У голосового комментария пустой. */
630
- content: string;
631
- author: Author;
632
- likesCount: number;
633
- repliesCount: number;
634
- isLiked: boolean;
635
- createdAt: IsoDate;
636
- /** Вложения. У голосового — одно аудио с `mimeType: 'audio/ogg'`. */
637
- attachments?: Attachment[];
638
- /** Вложенные ответы. В списках приходит превью, полный список — через `itd.comments.replies()`. */
639
- replies?: Comment[];
640
- /** Заполнено только у ответов. */
641
- replyTo?: CommentReplyTo;
642
- }
643
- /**
644
- * Уведомление в единой форме.
645
- *
646
- * REST-список и SSE-поток отдают уведомления по-разному — разные имена типов, разные имена
647
- * полей, один участник против массива. Библиотека приводит оба вида к этой структуре,
648
- * поэтому объекты из `itd.notifications.list()` и из потока можно складывать в один список.
649
- *
650
- * Исходные данные не теряются: сервeрное имя типа остаётся в {@link rawType},
651
- * а весь необработанный объект — в {@link raw}.
652
- */
653
- interface Notification {
654
- id: string;
655
- /** Канонический тип. Старые имена (`like`, `comment`) приведены к новым. */
656
- type: NotificationType;
657
- /** Имя типа в том виде, в каком его прислал сервер. */
658
- rawType: string;
659
- /** Объект события: пост, комментарий, пользователь. */
660
- entityId: string | null;
661
- /** Пост, которому принадлежит комментарий, если событие о комментарии. */
662
- parentEntityId: string | null;
663
- /** Прочитано ли уведомление. */
664
- isRead: boolean;
665
- /** Кто совершил действие. Для схлопнутых уведомлений — несколько человек. */
666
- actors: Actor[];
667
- /** Сколько участников схлопнуто в одно уведомление. Минимум 1. */
668
- count: number;
669
- /** Текст или заголовок объекта события. */
670
- preview: string | null;
671
- /** Ссылка перехода, предложенная сервером. Обычно точнее её `resolveNotificationUrl()`. */
672
- clickUrl?: string;
673
- createdAt: IsoDate;
674
- /** Когда уведомление изменилось — например было прочитано. */
675
- updatedAt: IsoDate;
676
- /** Исходный объект как он пришёл от сервера. */
677
- raw: unknown;
678
- }
679
- /** Настройки приватности профиля. */
680
- interface PrivacySettings {
681
- /** Закрыт ли профиль: подписка требует одобрения. */
682
- isPrivate: boolean;
683
- wallAccess: WallAccess;
684
- likesVisibility: LikesVisibility;
685
- /** Показывать ли время последнего посещения. */
686
- showLastSeen: boolean;
687
- }
688
- /**
689
- * Настройки уведомлений.
690
- *
691
- * Сервер отдаёт плоский объект, но исторически знает два набора имён для одних и тех же
692
- * настроек (`likes` и `reactions`, `comments` и `replies`). При сохранении библиотека
693
- * отправляет оба, при чтении принимает любой.
694
- */
695
- interface NotificationSettings {
696
- /** Общий выключатель доставки. */
697
- enabled: boolean;
698
- /** Звук уведомления. */
699
- sound: boolean;
700
- /** Новые подписчики. */
701
- follows: boolean;
702
- /** Записи на вашей стене. */
703
- wallPosts: boolean;
704
- /** Реакции на ваши записи. */
705
- likes: boolean;
706
- /** Комментарии и ответы. */
707
- comments: boolean;
708
- /** Упоминания. */
709
- mentions: boolean;
710
- }
711
- /** Активная сессия входа. */
712
- interface Session {
713
- id: string;
714
- /** Та ли это сессия, из которой выполнен запрос. */
715
- isCurrent: boolean;
716
- createdAt: IsoDate;
717
- lastUsedAt: IsoDate;
718
- expiresAt: IsoDate;
719
- ipAddress: string;
720
- /** Код страны по IP, например `RU`. */
721
- ipCountry: string | null;
722
- ipCity: string | null;
723
- deviceType: Loose<'desktop' | 'mobile'>;
724
- osName: string | null;
725
- osVersion: string | null;
726
- /** Название браузера или приложения. */
727
- clientName: string | null;
728
- clientVersion: string | null;
729
- deviceModel: string | null;
730
- }
731
- /** Состояние платной подписки и её цена. */
732
- interface Subscription {
733
- /** Активна ли подписка сейчас. */
734
- active: boolean;
735
- /** Включено ли автопродление. */
736
- recurringEnabled: boolean;
737
- /** Цена в рублях. */
738
- price: number;
739
- }
740
- /** Сохранённый способ оплаты. */
741
- interface PaymentMethod {
742
- id: string;
743
- /** Последние четыре цифры карты. */
744
- last4?: string;
745
- /** Платёжная система: `visa`, `mastercard`, `mir`. */
746
- brand?: string;
747
- /** Основной ли это способ оплаты. */
748
- isDefault?: boolean;
749
- expiresAt?: IsoDate | null;
750
- }
751
- /** Хэштег. */
752
- interface Hashtag {
753
- id: string;
754
- /** Название без решётки. */
755
- name: string;
756
- /** Сколько постов с этим хэштегом. */
757
- postsCount: number;
758
- }
759
- /** Клан в рейтинге. */
760
- interface Clan {
761
- /** Эмодзи клана — оно же аватар его участников. */
762
- avatar: string;
763
- memberCount: number;
764
- }
765
- /**
766
- * Результат подписки на пользователя.
767
- *
768
- * @example
769
- * ```ts
770
- * const result = await itd.users.follow('durov');
771
- * // { following: true, followersCount: 11 }
772
- * ```
773
- */
774
- interface FollowResult {
775
- /** Подписка оформлена. У закрытого профиля отправляется заявка, и здесь будет `false`. */
776
- following: boolean;
777
- /** Сколько подписчиков стало у пользователя после действия. */
778
- followersCount?: number;
779
- /** Статус заявки, если профиль закрыт. */
780
- status?: Loose<'following' | 'requested'>;
781
- }
782
- /** Запись журнала изменений платформы. */
783
- interface ChangelogEntry {
784
- version: string;
785
- date: string;
786
- changes: string[];
787
- }
788
- /** Кнопка в анонсе платформы. */
789
- interface AnnouncementButton {
790
- title: string;
791
- /** Оформление: `primary`, `secondary` и другие. */
792
- style: string;
793
- action: {
794
- type: string;
795
- [key: string]: unknown;
796
- };
797
- }
798
- /** Анонс на главной странице платформы. */
799
- interface Announcement {
800
- id: string;
801
- image: {
802
- url: string;
803
- width: number;
804
- height: number;
805
- };
806
- title: string;
807
- description: string;
808
- /** Дополнительный текст мелким шрифтом. */
809
- additional_text?: string;
810
- buttons: AnnouncementButton[];
811
- }
812
- /** Баннер текущего события — виджет «портал». */
813
- interface Portal {
814
- active: boolean;
815
- title: string;
816
- url: string;
817
- }
818
- /** Статус заявки на верификацию. `none` означает, что заявка не подавалась. */
819
- interface VerificationStatus {
820
- status: Loose<'none' | 'pending' | 'approved' | 'rejected'>;
821
- }
822
- /** Созданная жалоба. */
823
- interface Report {
824
- id: string;
825
- createdAt: IsoDate;
826
- }
827
- /** Счётчики поста из `itd.posts.stats()`. */
828
- interface PostStats {
829
- id: string;
830
- likesCount: number;
831
- commentsCount: number;
832
- repostsCount: number;
833
- viewsCount: number;
834
- /** Преобладающая реакция — эмодзи либо `null`. */
835
- dominantEmoji: string | null;
836
- }
837
- /** Результат реакции на пост. */
838
- interface LikeResult {
839
- liked: boolean;
840
- likesCount: number;
841
- }
842
- /** Результат закрепления поста в профиле. */
843
- interface PinPostResult {
844
- success: boolean;
845
- pinnedPostId: string | null;
846
- }
847
- /** Закреплённые значки профиля и выбранный из них. */
848
- interface PinsResult {
849
- pins: Pin[];
850
- /** Идентификатор активного значка — строка, а не объект. */
851
- activePin: string | null;
852
- }
853
- /**
854
- * Разбирает дату API в объект `Date`.
855
- *
856
- * @returns `null`, если строки нет или она не разбирается
857
- *
858
- * @example
859
- * ```ts
860
- * const created = toDate(post.createdAt);
861
- * ```
862
- */
863
- declare function toDate(value: IsoDate | null | undefined): Date | null;
864
-
865
- /**
866
- * Билдер опроса.
867
- *
868
- * Неизменяемый: каждый вызов возвращает новый экземпляр, поэтому заготовку можно
869
- * переиспользовать, не боясь её испортить. Создаётся функцией {@link poll}.
870
- */
871
- declare class PollBuilder implements ItdBuilder<CreatePollInput> {
872
- #private;
873
- /** @internal */
874
- readonly [BUILDER]: true;
875
- /** @internal Создавайте билдер функцией {@link poll}. */
876
- constructor(state: CreatePollInput);
877
- /** Задаёт вопрос. */
878
- question(text: string): PollBuilder;
879
- /** Добавляет один вариант ответа. */
880
- option(text: string): PollBuilder;
881
- /**
882
- * Добавляет несколько вариантов сразу.
883
- *
884
- * @example
885
- * ```ts
886
- * poll('ну как?').options('да', 'нет', 'не знаю');
887
- * ```
888
- */
889
- options(...texts: string[]): PollBuilder;
890
- /** Разрешает выбор нескольких вариантов. */
891
- multipleChoice(enabled?: boolean): PollBuilder;
892
- build(): CreatePollInput;
893
- toJSON(): CreatePollInput;
894
- }
895
- /**
896
- * Начинает сборку опроса.
897
- *
898
- * @param question вопрос; можно задать позже методом {@link PollBuilder.question}
899
- *
900
- * @example
901
- * ```ts
902
- * import { poll } from 'itd-api';
903
- *
904
- * const q = poll('Какой язык лучше?')
905
- * .options('TypeScript', 'JavaScript')
906
- * .multipleChoice();
907
- *
908
- * await itd.posts.create({ content: 'голосуем', poll: q });
909
- * ```
910
- */
911
- declare function poll(question?: string): PollBuilder;
912
- /** Что принимает параметр опроса: объект, билдер или функция-настройщик. */
913
- type PollInput = BuilderInput<CreatePollInput, PollBuilder>;
914
-
915
- /**
916
- * Файл для загрузки.
917
- *
918
- * Строка означает **путь на диске** и работает только в Node, Bun и Deno — для этого
919
- * подключите `itd-api/node`. В браузере и React Native передавайте `File` или `Blob`.
920
- */
921
- type FileInput = Blob | ArrayBuffer | Uint8Array | string | {
922
- /** Содержимое файла. */
923
- data: Blob | ArrayBuffer | Uint8Array;
924
- /** Имя файла. Влияет на определение типа, если `contentType` не задан. */
925
- filename?: string;
926
- /** MIME-тип. Если не указан, определяется по расширению или по самому `Blob`. */
927
- contentType?: string;
928
- };
929
- /** Данные для создания опроса. */
930
- interface CreatePollInput {
931
- /** Вопрос. Не может быть пустым. */
932
- question: string;
933
- /** Варианты ответа. Требуется минимум два. */
934
- options: {
935
- text: string;
936
- }[];
937
- /** Разрешить выбор нескольких вариантов. По умолчанию `false`. */
938
- multipleChoice?: boolean;
939
- }
940
- /** Данные для создания поста. */
941
- interface CreatePostInput {
942
- /** Текст поста. */
943
- content?: string;
944
- /** Разметка текста. Передаётся серверу без изменений — библиотека её не генерирует. */
945
- spans?: Span[];
946
- /**
947
- * Чья стена, если пост публикуется не у себя.
948
- *
949
- * Требуется **UUID**: имя пользователя здесь не работает, его можно получить
950
- * из профиля через `itd.users.get(username)`.
951
- */
952
- wallRecipientId?: UserId | null;
953
- /** Идентификаторы заранее загруженных вложений. */
954
- attachmentIds?: string[];
955
- /** Файлы, которые нужно загрузить перед публикацией. Порядок сохраняется. */
956
- files?: FileInput[];
957
- /** Опрос: обычный объект, {@link PollBuilder} или функция-настройщик. */
958
- poll?: PollInput;
959
- }
960
- /** Данные для создания комментария или ответа. */
961
- interface CreateCommentInput {
962
- /** Текст. У голосового комментария должен быть пустым. */
963
- content?: string;
964
- /** Идентификаторы заранее загруженных вложений. */
965
- attachmentIds?: string[];
966
- /** Файлы, которые нужно загрузить перед отправкой. */
967
- files?: FileInput[];
968
- /**
969
- * Кому адресован ответ.
970
- *
971
- * Применимо только в `itd.comments.reply()`; в комментарии к посту поле не имеет смысла.
972
- */
973
- replyToUserId?: UserId;
974
- }
975
- /** Данные для создания жалобы. */
976
- interface CreateReportInput {
977
- /** На что жалоба. */
978
- targetType: ReportTargetType;
979
- /** Идентификатор объекта жалобы. */
980
- targetId: string;
981
- /** Причина. */
982
- reason: ReportReason;
983
- /** Пояснение в свободной форме. */
984
- description?: string;
985
- }
986
-
987
- /** Внутреннее состояние {@link CommentBuilder}. */
988
- interface CommentState extends CreateCommentInput {
989
- content: string;
990
- attachmentIds: string[];
991
- files: FileInput[];
992
- /** Голосовой комментарий: текста быть не должно, вложение ровно одно. */
993
- voice: boolean;
994
- }
995
- /**
996
- * Билдер комментария и ответа на комментарий.
997
- *
998
- * Неизменяемый: каждый вызов возвращает новый экземпляр. Создаётся функцией {@link comment}.
999
- */
1000
- declare class CommentBuilder implements ItdBuilder<CreateCommentInput> {
1001
- #private;
1002
- /** @internal */
1003
- readonly [BUILDER]: true;
1004
- /** @internal Создавайте билдер функцией {@link comment}. */
1005
- constructor(state: CommentState);
1006
- /** Задаёт текст комментария. */
1007
- content(text: string): CommentBuilder;
1008
- /** Прикладывает файл — он будет загружен перед отправкой. */
1009
- attach(file: FileInput): CommentBuilder;
1010
- /** Прикладывает уже загруженное вложение. */
1011
- attachId(attachmentId: string): CommentBuilder;
1012
- /**
1013
- * Делает комментарий голосовым.
1014
- *
1015
- * Текста у такого комментария быть не должно, а вложение ровно одно — аудио в формате
1016
- * `audio/ogg`. Так его принимает API.
1017
- *
1018
- * @example
1019
- * ```ts
1020
- * await itd.posts.comment(postId, (c) => c.voice('./answer.ogg'));
1021
- * ```
1022
- */
1023
- voice(audio: FileInput): CommentBuilder;
1024
- /**
1025
- * Кому адресован ответ.
1026
- *
1027
- * Имеет смысл только в `itd.comments.reply()`; при отправке комментария к посту
1028
- * это поле вызовет ошибку.
1029
- */
1030
- replyTo(userId: UserId): CommentBuilder;
1031
- build(): CreateCommentInput;
1032
- toJSON(): CreateCommentInput;
1033
- }
1034
- /**
1035
- * Начинает сборку комментария.
1036
- *
1037
- * @param content текст; можно задать позже методом {@link CommentBuilder.content}
1038
- *
1039
- * @example
1040
- * ```ts
1041
- * import { comment } from 'itd-api';
1042
- *
1043
- * await itd.posts.comment(postId, comment('согласен').attach('./meme.png'));
1044
- * ```
1045
- */
1046
- declare function comment(content?: string): CommentBuilder;
1047
- /** Что принимает параметр комментария: объект, билдер или функция-настройщик. */
1048
- type CommentInput = BuilderInput<CreateCommentInput, CommentBuilder>;
1049
-
1050
- /** Внутреннее состояние {@link PostBuilder}. */
1051
- interface PostState extends CreatePostInput {
1052
- content: string;
1053
- attachmentIds: string[];
1054
- files: FileInput[];
1055
- }
1056
- /**
1057
- * Билдер поста.
1058
- *
1059
- * Неизменяемый: каждый вызов возвращает новый экземпляр, поэтому заготовку можно
1060
- * переиспользовать. Создаётся функцией {@link post}.
1061
- *
1062
- * @example Заготовка для нескольких постов
1063
- * ```ts
1064
- * const onWall = post().onWall(userId);
1065
- *
1066
- * await itd.posts.create(onWall.content('первый'));
1067
- * await itd.posts.create(onWall.content('второй')); // заготовка не испорчена
1068
- * ```
1069
- */
1070
- declare class PostBuilder implements ItdBuilder<CreatePostInput> {
1071
- #private;
1072
- /** @internal */
1073
- readonly [BUILDER]: true;
1074
- /** @internal Создавайте билдер функцией {@link post}. */
1075
- constructor(state: PostState);
1076
- /** Задаёт текст поста, заменяя прежний. */
1077
- content(text: string): PostBuilder;
1078
- /** Дописывает текст к уже заданному. */
1079
- append(text: string): PostBuilder;
1080
- /**
1081
- * Задаёт разметку текста.
1082
- *
1083
- * Библиотека разметку не генерирует: хэштеги и упоминания нужно размечать самостоятельно
1084
- * либо не размечать вовсе.
1085
- */
1086
- spans(spans: Span[]): PostBuilder;
1087
- /**
1088
- * Публикует пост на стене другого пользователя.
1089
- *
1090
- * @param userId **UUID** пользователя; имя пользователя не подойдёт
1091
- */
1092
- onWall(userId: UserId): PostBuilder;
1093
- /**
1094
- * Прикладывает файл — он будет загружен перед публикацией.
1095
- *
1096
- * Порядок вызовов сохраняется в порядке вложений.
1097
- */
1098
- attach(file: FileInput): PostBuilder;
1099
- /** Прикладывает уже загруженное вложение по его идентификатору. */
1100
- attachId(attachmentId: string): PostBuilder;
1101
- /**
1102
- * Добавляет опрос.
1103
- *
1104
- * Принимает объект, {@link PollBuilder} или функцию-настройщик.
1105
- *
1106
- * @example
1107
- * ```ts
1108
- * post('голосуем').poll((q) => q.question('ну как?').options('да', 'нет'));
1109
- * ```
1110
- */
1111
- poll(input: PollInput): PostBuilder;
1112
- build(): CreatePostInput;
1113
- toJSON(): CreatePostInput;
1114
- }
1115
- /**
1116
- * Начинает сборку поста.
1117
- *
1118
- * @param content текст; можно задать позже методом {@link PostBuilder.content}
1119
- *
1120
- * @example
1121
- * ```ts
1122
- * import { post } from 'itd-api';
1123
- *
1124
- * await itd.posts.create(
1125
- * post('смотрите что нашёл')
1126
- * .attach('./photo.jpg')
1127
- * .poll((q) => q.question('нравится?').options('да', 'нет')),
1128
- * );
1129
- * ```
1130
- */
1131
- declare function post(content?: string): PostBuilder;
1132
- /** Что принимает параметр поста: объект, билдер или функция-настройщик. */
1133
- type PostInput = BuilderInput<CreatePostInput, PostBuilder>;
1134
-
1135
- /**
1136
- * Билдер жалобы.
1137
- *
1138
- * Точка входа задаёт объект жалобы и его тип одновременно, поэтому рассогласовать
1139
- * `targetType` и `targetId` невозможно. Создаётся объектом {@link report}.
1140
- */
1141
- declare class ReportBuilder implements ItdBuilder<CreateReportInput> {
1142
- #private;
1143
- /** @internal */
1144
- readonly [BUILDER]: true;
1145
- /** @internal Создавайте билдер через {@link report}. */
1146
- constructor(state: Partial<CreateReportInput>);
1147
- /** Указывает причину жалобы. */
1148
- reason(reason: ReportReason): ReportBuilder;
1149
- /** Добавляет пояснение в свободной форме. */
1150
- description(text: string): ReportBuilder;
1151
- build(): CreateReportInput;
1152
- toJSON(): CreateReportInput;
1153
- }
1154
- /**
1155
- * Начинает сборку жалобы.
1156
- *
1157
- * Тип объекта выбирается точкой входа, так что указать идентификатор комментария
1158
- * с типом «пост» нельзя в принципе.
1159
- *
1160
- * @example
1161
- * ```ts
1162
- * import { report, ReportReason } from 'itd-api';
1163
- *
1164
- * await itd.reports.create(report.post(postId).reason(ReportReason.Spam));
1165
- * await itd.reports.create(report.user(userId).reason('fraud').description('пишет в личку'));
1166
- * ```
1167
- */
1168
- declare const report: Readonly<{
1169
- /** Жалоба на пост. */
1170
- post: (postId: string) => ReportBuilder;
1171
- /** Жалоба на комментарий. */
1172
- comment: (commentId: string) => ReportBuilder;
1173
- /** Жалоба на пользователя. */
1174
- user: (userId: string) => ReportBuilder;
1175
- }>;
1176
- /** Что принимает параметр жалобы: объект, билдер или функция-настройщик. */
1177
- type ReportInput = BuilderInput<CreateReportInput, ReportBuilder>;
1178
-
1179
- /**
1180
- * Как библиотека обращается с cookie.
1181
- *
1182
- * - `browser` — cookie ведёт браузер, запросы уходят с `credentials: 'include'`;
1183
- * - `server` — cookie ведёт встроенный jar, заголовок `Cookie` подставляется вручную;
1184
- * - `auto` — определяется по среде исполнения (значение по умолчанию).
1185
- */
1186
- declare const RuntimeMode: Readonly<{
1187
- /** Определяется по среде исполнения. Значение по умолчанию. */
1188
- readonly Auto: "auto";
1189
- /** Cookie ведёт браузер, запросы уходят с `credentials: 'include'`. */
1190
- readonly Browser: "browser";
1191
- /** Cookie ведёт встроенный jar, заголовок `Cookie` подставляется вручную. */
1192
- readonly Server: "server";
1193
- }>;
1194
- type RuntimeMode = (typeof RuntimeMode)[keyof typeof RuntimeMode];
1195
- /** Распознанная среда исполнения. */
1196
- declare const DetectedRuntime: Readonly<{
1197
- readonly Browser: "browser";
1198
- /** Есть `window`, но нет `document`; cookie ведёт нативный сетевой слой. */
1199
- readonly ReactNative: "react-native";
1200
- readonly Server: "server";
1201
- }>;
1202
- type DetectedRuntime = (typeof DetectedRuntime)[keyof typeof DetectedRuntime];
1203
-
1204
- /**
1205
- * Сохранённая сессия.
1206
- *
1207
- * Кроме токенов сюда попадают cookie: refresh-токен итд.com живёт именно в cookie, и без них
1208
- * восстановить сессию после перезапуска процесса невозможно.
1209
- */
1210
- interface ItdSession {
1211
- /** Токен доступа для заголовка `Authorization: Bearer`. */
1212
- accessToken?: string | undefined;
1213
- /**
1214
- * Refresh-токен, если удалось получить его явным значением.
1215
- *
1216
- * Обычно сервер держит его в httpOnly-cookie и наружу не отдаёт — тогда поле останется
1217
- * пустым, а обновление пойдёт через {@link ItdSession.cookies}.
1218
- */
1219
- refreshToken?: string | undefined;
1220
- /** Сырые cookie в форме `имя=значение`, привязанные к origin API. */
1221
- cookies?: string[] | undefined;
1222
- /**
1223
- * Идентификатор устройства для заголовка `X-Device-Id`.
1224
- *
1225
- * Сервер различает по нему записи в списке сессий, поэтому значение должно пережить
1226
- * перезапуск процесса — иначе каждый старт бота порождает новую сессию. Библиотека
1227
- * заводит его сама при первом запросе и хранит здесь.
1228
- */
1229
- deviceId?: string | undefined;
1230
- /** Когда сессия получена, мс с начала эпохи. Нужно для диагностики. */
1231
- obtainedAt?: number | undefined;
1232
- }
1233
- /**
1234
- * Хранилище сессии.
1235
- *
1236
- * Подключаемый компонент: библиотека не знает, где вы держите токены, и обращается к ним
1237
- * только через этот интерфейс. Все методы могут быть как синхронными, так и асинхронными.
1238
- *
1239
- * @example Своё хранилище поверх AsyncStorage в React Native
1240
- * ```ts
1241
- * const storage = createTokenStorage({
1242
- * get: async () => JSON.parse((await AsyncStorage.getItem('itd')) ?? 'null'),
1243
- * set: (session) => AsyncStorage.setItem('itd', JSON.stringify(session)),
1244
- * clear: () => AsyncStorage.removeItem('itd'),
1245
- * });
1246
- * ```
1247
- */
1248
- interface TokenStorage {
1249
- /** Прочитать сессию. `null`, если её нет. */
1250
- get(): ItdSession | null | Promise<ItdSession | null>;
1251
- /** Сохранить сессию целиком. */
1252
- set(session: ItdSession): void | Promise<void>;
1253
- /** Удалить сессию. Вызывается при выходе и при неудачном обновлении токена. */
1254
- clear(): void | Promise<void>;
1255
- }
1256
- /**
1257
- * Хранилище в памяти процесса — вариант по умолчанию.
1258
- *
1259
- * Сессия теряется при перезапуске. Для долгоживущих ботов возьмите `FileTokenStorage`
1260
- * из `itd-api/node`, для браузера — {@link LocalStorageTokenStorage}.
1261
- */
1262
- declare class MemoryTokenStorage implements TokenStorage {
1263
- #private;
1264
- constructor(initial?: ItdSession | null);
1265
- get(): ItdSession | null;
1266
- set(session: ItdSession): void;
1267
- clear(): void;
1268
- }
1269
- /**
1270
- * Хранилище поверх `localStorage` браузера.
1271
- *
1272
- * Если `localStorage` недоступен (приватный режим, серверный рендеринг), молча работает
1273
- * как хранилище в памяти — библиотека не должна падать из-за настроек браузера.
1274
- *
1275
- * Помните, что `localStorage` доступен любому скрипту на странице: не используйте его,
1276
- * если для вашего приложения это неприемлемый риск.
1277
- */
1278
- declare class LocalStorageTokenStorage implements TokenStorage {
1279
- #private;
1280
- /** @param key ключ в `localStorage`. По умолчанию `itd-api:session`. */
1281
- constructor(key?: string);
1282
- get(): ItdSession | null;
1283
- set(session: ItdSession): void;
1284
- clear(): void;
1285
- }
1286
- /**
1287
- * Собирает {@link TokenStorage} из трёх функций — когда заводить класс избыточно.
1288
- *
1289
- * @example
1290
- * ```ts
1291
- * const storage = createTokenStorage({
1292
- * get: () => db.getSession(userId),
1293
- * set: (session) => db.saveSession(userId, session),
1294
- * clear: () => db.deleteSession(userId),
1295
- * });
1296
- * ```
1297
- */
1298
- declare function createTokenStorage(handlers: TokenStorage): TokenStorage;
1299
-
1300
- /** Значение параметра запроса. `undefined` и `null` в строку не попадают. */
1301
- type QueryValue = string | number | boolean | null | undefined | readonly (string | number | boolean)[];
1302
- /** Параметры строки запроса. */
1303
- type QueryParams = Record<string, QueryValue>;
1304
-
1305
- /**
1306
- * Вход по логину и паролю.
1307
- *
1308
- * Вход требует токен капчи Cloudflare Turnstile, поэтому полностью автоматическим он быть
1309
- * не может: капчу должен решить кто-то снаружи. Токен одноразовый и живёт несколько минут,
1310
- * так что долгоживущему клиенту нужен `getTurnstileToken` — он спрашивается заново перед
1311
- * каждой попыткой входа. Одиночный `turnstileToken` годится для разового скрипта.
1312
- */
1313
- interface CredentialsAuth {
1314
- email: string;
1315
- password: string;
1316
- /** Разовый токен капчи. Для повторного входа после истечения сессии не подойдёт. */
1317
- turnstileToken?: string | undefined;
1318
- /** Источник свежего токена капчи. Спрашивается перед каждым входом. */
1319
- getTurnstileToken?: (() => string | Promise<string>) | undefined;
1320
- }
1321
- /**
1322
- * Как клиент получает доступ к API.
1323
- *
1324
- * Четыре формы — от разового вызова с готовым токеном до полноценной сессии, которую
1325
- * библиотека заводит и продлевает сама.
1326
- *
1327
- * Опция необязательна: если {@link ItdClientOptions.storage} уже содержит сессию, доступ
1328
- * берётся оттуда. Когда заданы обе, хранилище главнее — оно отражает текущее состояние
1329
- * сессии, — а недостающие поля берутся отсюда.
1330
- *
1331
- * @example
1332
- * ```ts
1333
- * new ItdClient({ auth: '<accessToken>' }); // разовый вызов
1334
- * new ItdClient({ auth: { accessToken, refreshToken } }); // восстановить сессию
1335
- * new ItdClient({ auth: { email, password, getTurnstileToken } }); // залогиниться самому
1336
- * new ItdClient({ auth: { getToken: () => vault.read() } }); // токен из внешнего источника
1337
- * ```
1338
- */
1339
- type AuthInput = string | {
1340
- accessToken: string;
1341
- refreshToken?: string | undefined;
1342
- } | CredentialsAuth | {
1343
- getToken: () => string | null | Promise<string | null>;
1344
- };
1345
- /** Куда библиотека пишет отладочные сообщения. Совместим с `console`. */
1346
- interface Logger {
1347
- debug(message: string, ...args: unknown[]): void;
1348
- info(message: string, ...args: unknown[]): void;
1349
- warn(message: string, ...args: unknown[]): void;
1350
- error(message: string, ...args: unknown[]): void;
1351
- }
1352
- /** Настройки повторных попыток. */
1353
- interface RetryOptions {
1354
- /** Сколько всего попыток, включая первую. По умолчанию 3. */
1355
- attempts?: number | undefined;
1356
- /** Базовая пауза в мс, удваивается с каждой попыткой. По умолчанию 500. */
1357
- baseDelay?: number | undefined;
1358
- /** Верхняя граница паузы в мс. По умолчанию 30000. */
1359
- maxDelay?: number | undefined;
1360
- /** Доля случайного разброса паузы, 0…1. По умолчанию 0.3. */
1361
- jitter?: number | undefined;
1362
- /**
1363
- * Повторять ли запись (`POST`, `PUT`, `PATCH`, `DELETE`) при сетевых сбоях и `5xx`.
1364
- *
1365
- * По умолчанию `false`: сервер мог успеть выполнить операцию до обрыва, и повтор
1366
- * создаст дубль поста или лишнюю жалобу. Ответ `429` повторяется всегда — он гарантирует,
1367
- * что запрос не был обработан.
1368
- */
1369
- retryWrites?: boolean | undefined;
1370
- /** Своя логика: вернуть `true`, чтобы повторить. Заменяет правила по умолчанию. */
1371
- shouldRetry?: ((error: unknown, attempt: number) => boolean) | undefined;
1372
- }
1373
- /** Настройки ограничения нагрузки на API. */
1374
- interface RateLimitOptions {
1375
- /** Сколько запросов выполняется одновременно. По умолчанию 6. */
1376
- concurrency?: number | undefined;
1377
- /** Верхняя граница запросов в секунду. По умолчанию без ограничения. */
1378
- rps?: number | undefined;
1379
- /**
1380
- * Паузы перед повторами при ответе `429`, мс.
1381
- * По умолчанию `[1000, 5000, 30000, 60000, 90000]`.
1382
- *
1383
- * Сервер не сообщает, когда сбросится окно лимита, поэтому паузу приходится подбирать.
1384
- * Лестница начинается с секунды: если окно почти истекло, работа продолжится почти
1385
- * сразу, а если лимит исчерпан всерьёз — паузы дорастут до полутора минут.
1386
- * Когда лестница закончилась, {@link ItdRateLimitError} пробрасывается вызывающему коду.
1387
- *
1388
- * Этот список не зависит от `retry.attempts`: тот управляет повторами при обрывах
1389
- * сети и ошибках сервера, где уместен совсем другой темп.
1390
- */
1391
- retryDelays?: readonly number[] | undefined;
1392
- /**
1393
- * Тормозить ли очередь по заголовкам ответа. По умолчанию `true`.
1394
- *
1395
- * Выключите, если управляете темпом сами.
1396
- */
1397
- respectHeaders?: boolean | undefined;
1398
- }
1399
- /** Данные о запросе, доступные хукам. */
1400
- interface RequestContext {
1401
- method: string;
1402
- /** Путь без базового URL, например `/api/posts`. */
1403
- path: string;
1404
- /** Итоговый URL со строкой запроса. */
1405
- url: string;
1406
- headers: Headers;
1407
- /** Номер попытки, начиная с 1. */
1408
- attempt: number;
1409
- }
1410
- /** Данные об успешном ответе. */
1411
- interface ResponseContext extends RequestContext {
1412
- status: number;
1413
- /** Длительность запроса в мс. */
1414
- duration: number;
1415
- response: Response;
1416
- }
1417
- /** Данные об ошибке запроса. */
1418
- interface ErrorContextHook extends RequestContext {
1419
- duration: number;
1420
- error: unknown;
1421
- }
1422
- /** Данные о предстоящем повторе. */
1423
- interface RetryContext extends RequestContext {
1424
- error: unknown;
1425
- /** Пауза перед следующей попыткой в мс. */
1426
- delay: number;
1427
- }
1428
- /**
1429
- * Перехватчики жизненного цикла запроса.
1430
- *
1431
- * Вызываются последовательно; исключение внутри хука прервёт запрос, поэтому свою логику
1432
- * лучше оборачивать в `try`.
1433
- */
1434
- interface ClientHooks {
1435
- /** Перед отправкой. Можно дописать заголовки — объект `headers` изменяемый. */
1436
- onRequest?(context: RequestContext): void | Promise<void>;
1437
- /** После успешного ответа, до разбора тела. */
1438
- onResponse?(context: ResponseContext): void | Promise<void>;
1439
- /** При любой ошибке запроса, включая те, что будут повторены. */
1440
- onError?(context: ErrorContextHook): void | Promise<void>;
1441
- /** Перед паузой между попытками. */
1442
- onRetry?(context: RetryContext): void | Promise<void>;
1443
- }
1444
- /**
1445
- * Опции конструктора `ItdClient`.
1446
- *
1447
- * Все поля допускают явный `undefined`, чтобы можно было передавать значения, которых
1448
- * может не быть, — например `new ItdClient({ auth: process.env.ITD_TOKEN })`.
1449
- */
1450
- interface ItdClientOptions {
1451
- /**
1452
- * Базовый URL API. По умолчанию `https://xn--d1ah4a.com`.
1453
- *
1454
- * Укажите здесь адрес своего прокси, если работаете из браузера: CORS для сторонних
1455
- * источников на итд.com, скорее всего, не настроен.
1456
- */
1457
- baseUrl?: string | undefined;
1458
- /** Авторизация. Без неё доступны только публичные эндпоинты. */
1459
- auth?: AuthInput | undefined;
1460
- /** Где хранить сессию. По умолчанию {@link MemoryTokenStorage}. */
1461
- storage?: TokenStorage | undefined;
1462
- /**
1463
- * Обновлять токен автоматически при ответе `401`. По умолчанию `true`.
1464
- *
1465
- * При `false` библиотека просто пробросит {@link ItdAuthError}, а обновлением
1466
- * вы управляете сами через `itd.auth.refresh()`.
1467
- */
1468
- autoRefresh?: boolean | undefined;
1469
- /**
1470
- * Пытаться ли войти заново, если обновление токена не удалось.
1471
- *
1472
- * Работает, только когда в `auth` переданы email и пароль. По умолчанию `true`.
1473
- */
1474
- reloginOnRefreshFailure?: boolean | undefined;
1475
- /** Своя реализация `fetch`: для Deno, React Native, тестов или прокси. */
1476
- fetch?: typeof fetch | undefined;
1477
- /** Таймаут запроса в мс. По умолчанию 30000 — столько же использует сайт итд.com. `0` снимает ограничение. */
1478
- timeout?: number | undefined;
1479
- /** Повторные попытки. `false` отключает их полностью. */
1480
- retry?: RetryOptions | false | undefined;
1481
- /** Ограничение нагрузки. `false` отключает очередь. */
1482
- rateLimit?: RateLimitOptions | false | undefined;
1483
- /** Перехватчики запросов. */
1484
- hooks?: ClientHooks | undefined;
1485
- /** Отладочный вывод. `true` — писать в `console`. */
1486
- logger?: Logger | boolean | undefined;
1487
- /** Заголовки, добавляемые ко всем запросам, — например `User-Agent` для бота. */
1488
- headers?: Record<string, string> | undefined;
1489
- /**
1490
- * Значение заголовка `X-Device-Id`, который уходит с каждым запросом.
1491
- *
1492
- * Сервер различает по нему записи в списке сессий, поэтому значение должно быть стабильным.
1493
- * Если не задать, библиотека заведёт идентификатор сама и сохранит его в {@link ItdSession},
1494
- * так что при постоянном хранилище он переживёт перезапуск процесса.
1495
- */
1496
- deviceId?: string | undefined;
1497
- /**
1498
- * Значение заголовка `User-Agent`. `false` — не отправлять его вовсе.
1499
- *
1500
- * По умолчанию `Mozilla/5.0 (compatible; itd-api/<версия>; …)`: `fetch` в Node не шлёт
1501
- * `User-Agent` сам, а сайт стоит за DDoS-Guard, который такие запросы может не пропустить.
1502
- * В браузере опция не действует — там заголовок менять запрещено.
1503
- */
1504
- userAgent?: string | false | undefined;
1505
- /** Как обращаться с cookie. По умолчанию определяется по среде исполнения. */
1506
- mode?: RuntimeMode | undefined;
1507
- }
1508
- /** Опции отдельного запроса. Доступны в каждом методе ресурсов. */
1509
- interface RequestOptions {
1510
- /** Отмена запроса извне. */
1511
- signal?: AbortSignal | undefined;
1512
- /** Таймаут только для этого запроса, мс. */
1513
- timeout?: number | undefined;
1514
- /** Дополнительные заголовки. */
1515
- headers?: Record<string, string> | undefined;
1516
- /** Повторы только для этого запроса. */
1517
- retry?: RetryOptions | false | undefined;
1518
- }
1519
- /** Полное описание запроса для низкоуровневого `itd.request()`. */
1520
- interface RawRequestOptions extends RequestOptions {
1521
- method: string;
1522
- /** Путь с ведущим слэшем, например `/api/posts`. Завершающий слэш значим. */
1523
- path: string;
1524
- query?: QueryParams | undefined;
1525
- /** Тело: будет отправлено как JSON. Для загрузки файлов передайте `FormData`. */
1526
- body?: unknown;
1527
- /** Не подставлять заголовок авторизации. */
1528
- skipAuth?: boolean | undefined;
1529
- /** Не пытаться обновить токен при `401` — используется самими эндпоинтами авторизации. */
1530
- skipAuthRefresh?: boolean | undefined;
1531
- /**
1532
- * Выполнить запрос мимо очереди.
1533
- *
1534
- * Нужно запросам, которые слой авторизации делает **изнутри** другого запроса: продление
1535
- * токена и отложенный вход. Такой запрос обязан стартовать, пока исходный держит место
1536
- * в очереди и ждёт его результата, — иначе оба ждут друг друга и не завершатся никогда.
1537
- */
1538
- skipQueue?: boolean | undefined;
1539
- /** Вернуть тело ответа без снятия обёртки `{ data: … }`. */
1540
- raw?: boolean | undefined;
1541
- }
1542
-
1543
- /** Базовый URL API итд.com. Домен записан в punycode: `итд.com`. */
1544
- declare const DEFAULT_BASE_URL = "https://xn--d1ah4a.com";
1545
- /** Таймаут запроса по умолчанию. Столько же использует официальный клиент итд.com. */
1546
- declare const DEFAULT_TIMEOUT = 30000;
1547
- /**
1548
- * Версия библиотеки — попадает в `User-Agent`.
1549
- */
1550
- declare const LIBRARY_VERSION = "0.0.7";
1551
- /**
1552
- * `User-Agent` по умолчанию.
1553
- *
1554
- * Сайт стоит за DDoS-Guard, и запросы вовсе без `User-Agent` (так делает `fetch` в Node)
1555
- * имеют шанс не пройти фильтр. Префикс `Mozilla/5.0` — дань традиции таких фильтров,
1556
- * дальше идёт честное имя библиотеки: подделываться под браузер она не должна.
1557
- *
1558
- * В браузере заголовок не выставляется — `User-Agent` там запрещён к изменению, и среда
1559
- * молча его игнорирует.
1560
- */
1561
- declare const DEFAULT_USER_AGENT = "Mozilla/5.0 (compatible; itd-api/0.0.7; +https://github.com/KiowDev/itd-api)";
1562
- /**
1563
- * Настройки повторов со всеми значениями по умолчанию.
1564
- *
1565
- * Поля перечислены явно, а не через `Required<RetryOptions>`: тот снимает необязательность,
1566
- * но оставляет `| undefined` в типе значения, раз оно указано в исходном интерфейсе.
1567
- */
1568
- interface ResolvedRetryOptions {
1569
- attempts: number;
1570
- baseDelay: number;
1571
- maxDelay: number;
1572
- jitter: number;
1573
- retryWrites: boolean;
1574
- shouldRetry: ((error: unknown, attempt: number) => boolean) | undefined;
1575
- }
1576
- /** Настройки очереди со всеми значениями по умолчанию. */
1577
- interface ResolvedRateLimitOptions {
1578
- concurrency: number;
1579
- rps: number | undefined;
1580
- retryDelays: readonly number[];
1581
- respectHeaders: boolean;
1582
- }
1583
- /** Конфигурация клиента после подстановки значений по умолчанию и проверок. */
1584
- interface ResolvedConfig {
1585
- baseUrl: string;
1586
- auth: AuthInput | undefined;
1587
- storage: TokenStorage;
1588
- autoRefresh: boolean;
1589
- reloginOnRefreshFailure: boolean;
1590
- fetch: typeof fetch;
1591
- timeout: number;
1592
- retry: ResolvedRetryOptions | undefined;
1593
- rateLimit: ResolvedRateLimitOptions | undefined;
1594
- hooks: ClientHooks;
1595
- logger: Logger | undefined;
1596
- headers: Record<string, string>;
1597
- /** Значение заголовка `X-Device-Id`, если задано вручную. Иначе заводится само. */
1598
- deviceId: string | undefined;
1599
- /** Значение заголовка `User-Agent`. `undefined` — заголовок не выставляется. */
1600
- userAgent: string | undefined;
1601
- mode: RuntimeMode;
1602
- /** Вести ли собственный cookie-jar (вне браузера и React Native). */
1603
- useCookieJar: boolean;
1604
- /** Отправлять ли `credentials: 'include'` (в браузере). */
1605
- sendCredentials: boolean;
1606
- }
1607
-
1608
- /** Имя cookie-флага «есть refresh-сессия». Ставится сайтом итд.com рядом с refresh-токеном. */
1609
- declare const AUTH_FLAG_COOKIE = "is_auth";
1610
- /**
1611
- * Имя cookie с refresh-токеном.
1612
- *
1613
- * `POST /api/v1/auth/refresh` читает токен **только отсюда** — тело запроса сервер игнорирует.
1614
- * Cookie помечена `HttpOnly`, поэтому в браузере её не прочитать и не выставить; вне браузера
1615
- * она проходит через jar, и туда же кладётся токен, переданный строкой.
1616
- */
1617
- declare const REFRESH_COOKIE = "refresh_token";
1618
- /** Путь, которым сервер ограничивает {@link REFRESH_COOKIE}. */
1619
- declare const REFRESH_COOKIE_PATH = "/api/v1/auth";
1620
- /**
1621
- * Минимальное хранилище cookie для сред без своего.
1622
- *
1623
- * Refresh-токен итд.com приходит в `Set-Cookie`, а `fetch` вне браузера cookie не хранит —
1624
- * без jar сессию не продлить. В браузере и React Native не используется: там cookie ведёт
1625
- * сама среда.
1626
- *
1627
- * Реализована практическая часть RFC 6265: origin, путь, срок жизни, флаг `Secure`.
1628
- * Доменные cookie для поддоменов намеренно не поддерживаются — API работает с одного хоста.
1629
- */
1630
- declare class CookieJar {
1631
- #private;
1632
- /**
1633
- * Забирает `Set-Cookie` из ответа.
1634
- *
1635
- * Использует `Headers.getSetCookie()`, где он есть (Node 20+, undici). В остальных средах
1636
- * заголовки склеены в одну строку, и её нельзя резать по запятой напрямую: запятая есть
1637
- * внутри `Expires=Wed, 09 Jun 2027 …`. Разделением занимается `set-cookie-parser`.
1638
- */
1639
- setFromResponse(url: string, response: Response): void;
1640
- /**
1641
- * Кладёт cookie напрямую, минуя `Set-Cookie`.
1642
- *
1643
- * Нужно ровно в одном случае: пользователь передал refresh-токен строкой, а сервер читает
1644
- * его только из cookie. Значение не кодируется — оно уходит в заголовок как есть.
1645
- */
1646
- set(url: string, name: string, value: string, path?: string): void;
1647
- /** Сохраняет cookie из готовых строк `Set-Cookie`. */
1648
- setFromStrings(url: string, setCookieStrings: string[]): void;
1649
- /**
1650
- * Собирает значение заголовка `Cookie` для запроса.
1651
- *
1652
- * @returns строка вида `a=1; b=2` либо `undefined`, если подходящих cookie нет
1653
- */
1654
- getHeader(url: string): string | undefined;
1655
- /**
1656
- * Значение действующей cookie.
1657
- *
1658
- * Нужно, чтобы забрать обновлённый refresh-токен: сервер ротирует его при каждом
1659
- * продлении сессии, и сохранять надо именно новое значение.
1660
- *
1661
- * @param url если указан, учитываются origin, путь и флаг `Secure`
1662
- */
1663
- getValue(name: string, url?: string): string | undefined;
1664
- /**
1665
- * Есть ли действующая cookie с таким именем.
1666
- *
1667
- * Используется для проверки флага {@link AUTH_FLAG_COOKIE} перед запросом обновления
1668
- * токена: у анонима refresh-сессии нет, и дёргать API незачем.
1669
- */
1670
- has(name: string, url?: string): boolean;
1671
- /** Сохраняет содержимое jar для записи в {@link TokenStorage}. */
1672
- serialize(): string[];
1673
- /** Восстанавливает jar из результата {@link serialize}. Некорректные записи молча пропускаются. */
1674
- deserialize(entries: readonly string[] | undefined): void;
1675
- /** Удаляет все cookie. */
1676
- clear(): void;
1677
- }
1678
-
1679
- /** Обработчик события. */
1680
- type Listener<T> = (payload: T) => void;
1681
- /** Функция отписки, которую возвращает {@link Emitter.on}. */
1682
- type Unsubscribe = () => void;
1683
- /**
1684
- * Минимальный типизированный источник событий.
1685
- *
1686
- * Своя реализация вместо `EventTarget` и `EventEmitter`: первый есть не везде и требует
1687
- * обёрток `CustomEvent`, второй существует только в Node. Нужны ровно подписка и рассылка.
1688
- *
1689
- * Исключение в обработчике не прерывает рассылку остальным и не роняет библиотеку.
1690
- *
1691
- * @typeParam Events карта «имя события → тип полезной нагрузки». Задаётся интерфейсом,
1692
- * поэтому ограничение на индексную сигнатуру намеренно не накладывается.
1693
- */
1694
- declare class Emitter<Events> {
1695
- #private;
1696
- constructor(onListenerError?: (error: unknown) => void);
1697
- /**
1698
- * Подписывается на событие.
1699
- *
1700
- * @returns функция отписки
1701
- *
1702
- * @example
1703
- * ```ts
1704
- * const off = realtime.on('notification', (event) => console.log(event));
1705
- * off();
1706
- * ```
1707
- */
1708
- on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): Unsubscribe;
1709
- /** Подписывается на одно срабатывание. */
1710
- once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): Unsubscribe;
1711
- /** Отписывается от события. */
1712
- off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void;
1713
- /** Рассылает событие подписчикам. */
1714
- emit<K extends keyof Events>(event: K, payload: Events[K]): void;
1715
- /** Сколько подписчиков у события. */
1716
- listenerCount(event: keyof Events): number;
1717
- /** Снимает все подписки. */
1718
- removeAllListeners(): void;
1719
- }
1720
-
1721
- /**
1722
- * Обёртка вокруг запроса.
1723
- *
1724
- * Получает описание запроса и продолжение цепочки. Может изменить запрос перед отправкой,
1725
- * посмотреть и подменить разобранный ответ или вовсе не вызывать `next` и вернуть своё.
1726
- *
1727
- * @param request что уходит на сервер; изменять сам объект не нужно — передайте копию в `next`
1728
- * @param next продолжение: либо следующая обёртка, либо настоящий запрос
1729
- * @returns тело ответа в том виде, в каком его получит вызывающий код
1730
- *
1731
- * @example Дописать заголовок ко всем запросам
1732
- * ```ts
1733
- * const transformer: Transformer = (request, next) =>
1734
- * next({ ...request, headers: { ...request.headers, 'X-Trace': trace() } });
1735
- * ```
1736
- */
1737
- type Transformer = (request: RawRequestOptions, next: (request: RawRequestOptions) => Promise<unknown>) => Promise<unknown>;
1738
- /** Что плагин получает при подключении. */
1739
- interface PluginContext {
1740
- /** Базовый URL клиента — например чтобы разобрать абсолютные ссылки из ответа. */
1741
- baseUrl: string;
1742
- /** Отладочный вывод клиента, если он включён. */
1743
- logger: Logger | undefined;
1744
- /** Добавляет обёртку запроса. Подключённые раньше оказываются снаружи. */
1745
- use(transformer: Transformer): void;
1746
- }
1747
- /**
1748
- * Плагин клиента.
1749
- *
1750
- * Подключается через `itd.use(plugin)` и работает на уровне транспорта: видит запрос
1751
- * до отправки и разобранный ответ. Библиотека не знает, что именно делает плагин, —
1752
- * ей достаточно списка обёрток и имён опций, которые он читает.
1753
- *
1754
- * @example
1755
- * ```ts
1756
- * const logging: ItdPlugin = {
1757
- * name: 'logging',
1758
- * install({ use, logger }) {
1759
- * use(async (request, next) => {
1760
- * logger?.info(`${request.method} ${request.path}`);
1761
- * return next(request);
1762
- * });
1763
- * },
1764
- * };
1765
- *
1766
- * itd.use(logging);
1767
- * ```
1768
- */
1769
- interface ItdPlugin {
1770
- /** Имя плагина. Должно быть уникальным: повторное подключение — ошибка. */
1771
- name: string;
1772
- /**
1773
- * Имена опций запроса, которые плагин читает у методов ресурсов.
1774
- *
1775
- * Библиотека этих опций не понимает и ничего с ними не делает — только доносит
1776
- * от вызова метода до обёртки нетронутыми. Без такого списка чужие поля отсеиваются,
1777
- * чтобы случайная опечатка в параметрах не уезжала на сервер.
1778
- *
1779
- * Имена полей самого запроса (`path`, `body`, `headers`, `signal` и прочие из
1780
- * `RawRequestOptions`) заявить нельзя: подключение такого плагина завершится ошибкой.
1781
- *
1782
- * Типы для них плагин объявляет сам, дополняя `RequestOptions`:
1783
- * ```ts
1784
- * declare module 'itd-api' {
1785
- * interface RequestOptions { encrypt?: string | undefined }
1786
- * }
1787
- * ```
1788
- */
1789
- optionKeys?: readonly string[];
1790
- /** Вызывается один раз при подключении. */
1791
- install(context: PluginContext): void;
1792
- }
1793
- /**
1794
- * Список подключённых плагинов и собранная из них цепочка обёрток.
1795
- *
1796
- * Живёт в клиенте, а работает в транспорте: {@link HttpClient} прогоняет через `run`
1797
- * каждый запрос, если плагины есть.
1798
- */
1799
- declare class PluginRegistry {
1800
- #private;
1801
- /** Сколько обёрток подключено. Ноль означает, что запрос идёт прежним путём. */
1802
- get size(): number;
1803
- /** Имена опций запроса, заявленные плагинами. */
1804
- get optionKeys(): ReadonlySet<string>;
1805
- /**
1806
- * Подключает плагин.
1807
- *
1808
- * @throws {ItdConfigError} если плагин задан неверно, уже подключён или заявил занятое
1809
- * имя опции
1810
- */
1811
- add(plugin: ItdPlugin, context: Omit<PluginContext, 'use'>): void;
1812
- /**
1813
- * Прогоняет запрос через цепочку обёрток.
1814
- *
1815
- * Цепочка собирается на каждый запрос заново: плагин можно подключить в любой момент,
1816
- * а обёрток единицы — экономить тут не на чем.
1817
- *
1818
- * @param execute настоящий запрос, вызывается самой внутренней обёрткой
1819
- */
1820
- run(request: RawRequestOptions, execute: (request: RawRequestOptions) => Promise<unknown>): Promise<unknown>;
1821
- }
1822
-
1823
- /**
1824
- * Подключаемые части конвейера.
1825
- *
1826
- * Авторизация, cookie, очередь и повторы живут в отдельных модулях и подставляются сюда.
1827
- * Благодаря этому транспорт тестируется изолированно, а `HttpClient` ничего не знает
1828
- * о том, как именно добывается токен.
1829
- */
1830
- interface HttpCollaborators {
1831
- /** Заголовки авторизации для запроса. Вызывается перед каждой попыткой. */
1832
- getAuthHeaders?(): Promise<Record<string, string>> | Record<string, string>;
1833
- /**
1834
- * Реакция на ответ `401`.
1835
- *
1836
- * Должна вернуть `true`, если токен обновлён и запрос имеет смысл повторить.
1837
- * Повтор выполняется ровно один раз.
1838
- */
1839
- onUnauthorized?(): Promise<boolean>;
1840
- /**
1841
- * Идентификатор устройства для заголовка `X-Device-Id`.
1842
- *
1843
- * Отдельно от {@link getAuthHeaders}, потому что нужен и на анонимных запросах —
1844
- * например на `sign-in`, где заголовка авторизации ещё нет.
1845
- */
1846
- getDeviceId?(): Promise<string> | string;
1847
- /** Значение заголовка `Cookie` для указанного URL. */
1848
- getCookieHeader?(url: string): string | undefined;
1849
- /** Приём `Set-Cookie` из ответа. */
1850
- saveCookies?(url: string, response: Response): void;
1851
- /** Очередь запросов: ограничение конкурентности и частоты. */
1852
- schedule?<T>(task: () => Promise<T>): Promise<T>;
1853
- /**
1854
- * Сообщает об остатке лимита из заголовков ответа.
1855
- *
1856
- * Вызывается после **каждого** ответа, включая ошибочные, — так очередь узнаёт
1857
- * об исчерпании лимита заранее и успевает притормозить до отказа сервера.
1858
- */
1859
- onRateLimit?(limit: number | undefined, remaining: number | undefined): void;
1860
- /**
1861
- * Планировщик повторов.
1862
- *
1863
- * Возвращает паузу в мс перед следующей попыткой либо `undefined`, если повторять не нужно.
1864
- */
1865
- nextRetryDelay?(error: unknown, attempt: number, method: string): number | undefined;
1866
- }
1867
- /**
1868
- * Транспортный слой: единственное место, откуда библиотека ходит в сеть.
1869
- *
1870
- * Отвечает за сборку URL, заголовки, таймауты, разбор ответа и превращение любой неудачи
1871
- * в типизированную ошибку. Авторизация, cookie, очередь и повторы подключаются извне
1872
- * через {@link HttpCollaborators}.
1873
- */
1874
- declare class HttpClient {
1875
- #private;
1876
- constructor(config: ResolvedConfig, collaborators?: HttpCollaborators);
1877
- /** Базовый URL, к которому обращается клиент. */
1878
- get baseUrl(): string;
1879
- /**
1880
- * Имена опций запроса, заявленные плагинами.
1881
- *
1882
- * Читается ресурсами: они переносят в транспорт только известные поля, а чужие,
1883
- * если их никто не заявил, отсеивают.
1884
- */
1885
- get pluginOptionKeys(): ReadonlySet<string>;
1886
- /** Подключает список плагинов. Реестр общий с клиентом и пополняется через `itd.use()`. */
1887
- usePlugins(plugins: PluginRegistry): void;
1888
- /**
1889
- * Подключает недостающие части конвейера.
1890
- *
1891
- * Нужно из-за кольцевой зависимости: слой авторизации сам выполняет запросы, поэтому
1892
- * не может быть передан в конструктор до создания транспорта.
1893
- */
1894
- setCollaborators(collaborators: HttpCollaborators): void;
1895
- /**
1896
- * Выполняет запрос к API.
1897
- *
1898
- * @typeParam T ожидаемая форма ответа после снятия обёртки `{ data: … }`
1899
- * @throws {ItdApiError} если сервер ответил статусом ≥ 400
1900
- * @throws {ItdTimeoutError} если истёк таймаут
1901
- * @throws {ItdAbortError} если запрос отменён через `signal`
1902
- * @throws {ItdNetworkError} если запрос не дошёл до сервера
1903
- */
1904
- request<T = unknown>(options: RawRequestOptions): Promise<T>;
1905
- }
1906
-
1907
- /** Пути авторизации. Вынесены, чтобы не расходиться между модулями. */
1908
- declare const AUTH_PATHS: {
1909
- readonly signIn: "/api/v1/auth/sign-in";
1910
- readonly refresh: "/api/v1/auth/refresh";
1911
- };
1912
- /**
1913
- * Публичный ключ Cloudflare Turnstile платформы итд.com.
1914
- *
1915
- * Нужен, чтобы отрисовать виджет капчи и получить токен для `signIn`, `signUp`
1916
- * и `forgotPassword`.
1917
- *
1918
- * @example
1919
- * ```ts
1920
- * turnstile.render('#captcha', {
1921
- * sitekey: TURNSTILE_SITE_KEY,
1922
- * callback: (turnstileToken) => itd.auth.signIn({ email, password, turnstileToken }),
1923
- * });
1924
- * ```
1925
- */
1926
- declare const TURNSTILE_SITE_KEY = "0x4AAAAAACHhxczw6fJGwPBg";
1927
- /** Заголовок с идентификатором устройства. Сервер связывает с ним запись в списке сессий. */
1928
- declare const DEVICE_ID_HEADER = "X-Device-Id";
1929
- /** События слоя авторизации. */
1930
- interface AuthEvents {
1931
- /** Токен получен или обновлён. */
1932
- tokens: {
1933
- accessToken: string;
1934
- };
1935
- /** Выполнен вход. */
1936
- signIn: {
1937
- accessToken: string;
1938
- };
1939
- /** Сессия очищена — вручную или из-за неудачного обновления. */
1940
- signOut: undefined;
1941
- /** Обновить сессию не удалось; дальнейшие запросы будут падать с 401. */
1942
- authError: {
1943
- error: unknown;
1944
- };
1945
- }
1946
- /**
1947
- * Хранит сессию и продлевает её.
1948
- *
1949
- * Главное здесь — **дедупликация обновления**. Когда десять параллельных запросов
1950
- * одновременно получают `401`, обновление должно произойти один раз, а остальные обязаны
1951
- * дождаться его результата. Иначе сервер увидит десять параллельных `refresh`, и все,
1952
- * кроме первого, скорее всего получат отказ по уже использованному токену.
1953
- */
1954
- declare class AuthManager {
1955
- #private;
1956
- constructor(config: ResolvedConfig, http: HttpClient, jar: CookieJar);
1957
- /** Подписка на события авторизации. */
1958
- get on(): Emitter<AuthEvents>['on'];
1959
- /** Подписка на одно срабатывание. */
1960
- get once(): Emitter<AuthEvents>['once'];
1961
- /**
1962
- * Есть ли признак живой refresh-сессии.
1963
- *
1964
- * Рядом с refresh-токеном сервер ставит незакрытую cookie `is_auth` — по ней видно,
1965
- * что продлевать сессию вообще есть смысл, и API не дёргается у анонимов.
1966
- * В браузере cookie ведёт сама среда, поэтому там ответ всегда `true`.
1967
- *
1968
- * Асинхронный, потому что признак может лежать в {@link TokenStorage}: до чтения оттуда
1969
- * ответ был бы `false` даже при полностью рабочей сохранённой сессии.
1970
- */
1971
- hasRefreshSession(): Promise<boolean>;
1972
- /** Заголовки авторизации для очередного запроса. Пустой объект, если токена нет. */
1973
- getAuthHeaders(): Promise<Record<string, string>>;
1974
- /**
1975
- * Идентификатор устройства для заголовка `X-Device-Id`.
1976
- *
1977
- * Заводится один раз и сохраняется в сессии, чтобы пережить перезапуск процесса:
1978
- * сервер связывает с ним запись в списке сессий, и плавающее значение плодило бы
1979
- * по новой сессии на каждый старт.
1980
- */
1981
- getDeviceId(): Promise<string>;
1982
- /**
1983
- * Текущий токен доступа.
1984
- *
1985
- * При необходимости выполняет отложенный вход: если в конфигурации переданы логин
1986
- * и пароль, первый же запрос сам заведёт сессию.
1987
- */
1988
- getAccessToken(): Promise<string | null>;
1989
- /**
1990
- * Реакция транспорта на ответ `401`.
1991
- *
1992
- * @returns `true`, если токен обновлён и запрос имеет смысл повторить
1993
- */
1994
- onUnauthorized(): Promise<boolean>;
1995
- /**
1996
- * Обновляет токен доступа.
1997
- *
1998
- * Параллельные вызовы объединяются в один сетевой запрос.
1999
- *
2000
- * @throws {ItdAuthError} если обновить сессию не удалось
2001
- */
2002
- refresh(): Promise<string>;
2003
- /** Сохраняет токен, полученный извне, — например после подтверждения OTP. */
2004
- setAccessToken(accessToken: string): Promise<void>;
2005
- /** Текущая сессия целиком. Полезно, чтобы сохранить её самому. */
2006
- getSession(): Promise<ItdSession | null>;
2007
- /** Заменяет сессию целиком. */
2008
- setSession(session: ItdSession): Promise<void>;
2009
- /**
2010
- * Забывает сессию и cookie. Сетевой запрос не выполняется.
2011
- *
2012
- * Идентификатор устройства выход переживает: иначе каждая пара «выход — вход» плодила бы
2013
- * новую запись в списке сессий.
2014
- */
2015
- clear(): Promise<void>;
2016
- }
2017
-
2018
- /** Событие потока уведомлений после разбора. */
2019
- interface NotificationEvent {
2020
- /** Само уведомление в единой форме. */
2021
- notification: Notification;
2022
- /**
2023
- * Актуальное число непрочитанных, если сервер его сообщил.
2024
- *
2025
- * Клиент не увеличивает счётчик сам: значение приходит с сервера.
2026
- */
2027
- unreadCount: number | undefined;
2028
- /** Нужно ли проиграть звук. */
2029
- sound: boolean;
2030
- }
2031
- /**
2032
- * Приводит уведомление к единой форме.
2033
- *
2034
- * Нужна потому, что REST-список и поток событий описывают одно и то же событие по-разному:
2035
- * различаются имена типов (`like` против `post_reaction`), имена полей
2036
- * (`targetId`/`entityId`, `read`/`isRead`, `preview`/`entityPreview`) и число участников
2037
- * (`actor` против массива `actors`). После приведения объекты из обоих источников
2038
- * можно складывать в один список.
2039
- *
2040
- * Исходные данные не теряются: имя типа с сервера остаётся в `rawType`,
2041
- * весь объект целиком — в `raw`.
2042
- *
2043
- * @param input уведомление из REST-ответа либо полезная нагрузка события потока
2044
- *
2045
- * @example
2046
- * ```ts
2047
- * const fromRest = normalizeNotification(restItem);
2048
- * const fromStream = normalizeNotification(event.payload);
2049
- * // одинаковая форма — можно объединять
2050
- * ```
2051
- */
2052
- declare function normalizeNotification(input: unknown): Notification;
2053
- /**
2054
- * Разбирает событие `notification` из потока.
2055
- *
2056
- * Кроме самого уведомления событие несёт служебные поля уровня конверта: актуальный
2057
- * счётчик непрочитанных и признак звука.
2058
- */
2059
- declare function readNotificationEvent(data: unknown): NotificationEvent;
2060
- /**
2061
- * Разбирает событие `unread_count` из потока.
2062
- *
2063
- * Возвращает `undefined`, если сервер прислал событие без вложенного `payload`.
2064
- * Официальный клиент в этом случае **обнуляет** счётчик — это ошибка, из-за которой
2065
- * непрочитанные пропадают из интерфейса.
2066
- */
2067
- declare function readUnreadCountEvent(data: unknown): number | undefined;
2068
-
2069
- /**
2070
- * Паузы перед попытками переподключения, мс.
2071
- *
2072
- * Значения совпадают с теми, что использует сайт итд.com, — поведение библиотеки
2073
- * не отличается от привычного пользователю.
2074
- */
2075
- declare const RECONNECT_BACKOFF: readonly number[];
2076
- /** Доля случайного разброса паузы. */
2077
- declare const RECONNECT_JITTER = 0.3;
2078
- /**
2079
- * Сколько раз пытаться переподключиться подряд.
2080
- *
2081
- * После исчерпания поток сообщает `giveup` и ждёт ручного `connect()`.
2082
- */
2083
- declare const MAX_RECONNECT_ATTEMPTS = 15;
2084
- /** Настройки переподключения. */
2085
- interface ReconnectOptions {
2086
- /** Таблица пауз. Последнее значение действует для всех дальнейших попыток. */
2087
- backoff?: readonly number[];
2088
- /** Доля разброса, 0…1. */
2089
- jitter?: number;
2090
- /** Предел числа попыток. */
2091
- maxAttempts?: number;
2092
- }
2093
-
2094
- /** Событие, пришедшее по каналу реального времени. */
2095
- interface TransportEvent {
2096
- /** Имя события: `notification`, `unread_count` и другие. */
2097
- name: string;
2098
- /** Полезная нагрузка, уже разобранная из JSON. */
2099
- data: unknown;
2100
- }
2101
- /** Что транспорт получает от клиента при подключении. */
2102
- interface TransportContext {
2103
- /** Базовый URL API. */
2104
- baseUrl: string;
2105
- /** Реализация `fetch`. */
2106
- fetch: typeof fetch;
2107
- /** Текущий токен доступа. */
2108
- getToken: () => Promise<string | null>;
2109
- /** Отмена подключения. */
2110
- signal: AbortSignal;
2111
- /** Сообщает о полученном событии. */
2112
- onEvent: (event: TransportEvent) => void;
2113
- /** Сообщает о разобранном, но некорректном сообщении. Соединение при этом живёт. */
2114
- onParseError: (error: unknown, raw: string) => void;
2115
- /** Вызывается, когда соединение установлено. */
2116
- onOpen: () => void;
2117
- }
2118
- /**
2119
- * Канал получения событий в реальном времени.
2120
- *
2121
- * Сейчас у платформы один такой канал — поток `text/event-stream`. Абстракция нужна
2122
- * на будущее: политика безопасности сайта уже разрешает `wss://*.xn--d1ah4a.com`,
2123
- * и когда появится WebSocket, достаточно будет добавить ещё одну реализацию этого
2124
- * интерфейса. Переподключение, обновление токена и разбор уведомлений от транспорта
2125
- * не зависят.
2126
- */
2127
- interface RealtimeTransport {
2128
- /** Понятное имя для логов и диагностики. */
2129
- readonly name: string;
2130
- /**
2131
- * Держит соединение, пока оно живо.
2132
- *
2133
- * Должен завершиться, когда поток закрылся, и бросить исключение при ошибке.
2134
- * Отмена через `context.signal` должна приводить к `AbortError`.
2135
- */
2136
- connect(context: TransportContext): Promise<void>;
2137
- }
2138
- /** Ошибка, по которой видно, что сервер отверг авторизацию потока. */
2139
- declare class UnauthorizedStreamError extends Error {
2140
- constructor();
2141
- }
2142
-
2143
- /** События потока уведомлений. */
2144
- interface RealtimeEvents {
2145
- /** Пришло новое уведомление. */
2146
- notification: NotificationEvent;
2147
- /**
2148
- * Сервер подтвердил подключение и назвал получателя событий.
2149
- *
2150
- * Приходит первым кадром сразу после установки соединения.
2151
- */
2152
- ready: {
2153
- userId: string | undefined;
2154
- };
2155
- /**
2156
- * Сервер сообщил актуальное число непрочитанных.
2157
- *
2158
- * На практике сервер этого не делает: за всё наблюдение он не прислал ни одного
2159
- * такого кадра, а в уведомлениях нет поля со счётчиком. Держите счётчик сами
2160
- * либо запрашивайте `itd.notifications.count()`.
2161
- */
2162
- unreadCount: number;
2163
- /** Изменилось состояние соединения. */
2164
- status: RealtimeStatus;
2165
- /** Соединение оборвалось; будет предпринята попытка переподключения. */
2166
- error: {
2167
- error: unknown;
2168
- willReconnect: boolean;
2169
- };
2170
- /** Сообщение не удалось разобрать. Соединение при этом продолжает работать. */
2171
- parseError: {
2172
- error: unknown;
2173
- raw: string;
2174
- };
2175
- /** Запланировано переподключение. */
2176
- reconnect: {
2177
- attempt: number;
2178
- delay: number;
2179
- };
2180
- /** Попытки исчерпаны — соединение восстановится только ручным `connect()`. */
2181
- giveup: undefined;
2182
- /** Любое событие потока в необработанном виде, включая неизвестные библиотеке. */
2183
- message: {
2184
- name: string;
2185
- data: unknown;
2186
- };
2187
- }
2188
- /** Способ получения событий. */
2189
- declare const RealtimeTransportKind: Readonly<{
2190
- /** Поток событий, если среда умеет читать тело по частям, иначе опрос. */
2191
- readonly Auto: "auto";
2192
- /** Поток `text/event-stream`. */
2193
- readonly Sse: "sse";
2194
- /** Периодический опрос REST. */
2195
- readonly Poll: "poll";
2196
- }>;
2197
- type RealtimeTransportKind = (typeof RealtimeTransportKind)[keyof typeof RealtimeTransportKind];
2198
- /** Настройки потока уведомлений. */
2199
- interface RealtimeOptions extends ReconnectOptions {
2200
- /**
2201
- * Транспорт. По умолчанию `auto`: поток событий, если среда умеет читать тело ответа
2202
- * по частям, иначе опрос.
2203
- *
2204
- * Можно передать и свою реализацию {@link RealtimeTransport} — это пригодится, если
2205
- * у платформы появится WebSocket либо нужен нестандартный способ доставки.
2206
- */
2207
- transport?: RealtimeTransportKind | RealtimeTransport;
2208
- /**
2209
- * Молчание сервера, после которого соединение считается мёртвым, мс. По умолчанию 90 000.
2210
- *
2211
- * Сервер не присылает keep-alive, поэтому без этой проверки оборванное соединение
2212
- * может незаметно «зависнуть».
2213
- */
2214
- idleTimeout?: number;
2215
- /** Как часто опрашивать сервер, если используется запасной транспорт. */
2216
- pollInterval?: number;
2217
- /**
2218
- * Запрашивать число непрочитанных при подключении. По умолчанию `true`.
2219
- *
2220
- * Так поступает сайт итд.com: поток присылает только новые события, а начальное
2221
- * значение счётчика нужно получить отдельно.
2222
- */
2223
- syncCount?: boolean;
2224
- /**
2225
- * Переподключаться, когда вкладка снова становится видимой. По умолчанию `true`.
2226
- *
2227
- * Только в браузере. У сайта итд.com такой обработки нет, из-за чего вкладка,
2228
- * пролежавшая в фоне, может остаться без соединения.
2229
- */
2230
- reconnectOnVisible?: boolean;
2231
- /** Переподключаться при восстановлении сети. По умолчанию `true`. Только в браузере. */
2232
- reconnectOnOnline?: boolean;
2233
- }
2234
- /** Что поток получает от клиента. */
2235
- interface RealtimeDeps {
2236
- baseUrl: string;
2237
- fetch: typeof fetch;
2238
- getToken: () => Promise<string | null>;
2239
- /** Обновляет токен после отказа авторизации. Возвращает `true`, если удалось. */
2240
- refresh: () => Promise<boolean>;
2241
- /** Загружает начальное число непрочитанных. */
2242
- fetchUnreadCount: () => Promise<number>;
2243
- logger?: Logger | undefined;
2244
- }
2245
- /**
2246
- * Поток уведомлений в реальном времени.
2247
- *
2248
- * Получается вызовом `itd.realtime()`. Соединение поднимается методом {@link connect}
2249
- * и держится само: обрывы, обновление токена и повторные попытки библиотека берёт на себя.
2250
- *
2251
- * @example
2252
- * ```ts
2253
- * const stream = itd.realtime();
2254
- *
2255
- * stream.on('notification', ({ notification, unreadCount }) => {
2256
- * console.log(formatNotificationText(notification), unreadCount);
2257
- * });
2258
- * stream.on('status', (status) => console.log('соединение:', status));
2259
- *
2260
- * await stream.connect();
2261
- * // …позже
2262
- * stream.disconnect();
2263
- * ```
2264
- */
2265
- declare class ItdRealtime {
2266
- #private;
2267
- constructor(deps: RealtimeDeps, options?: RealtimeOptions);
2268
- /** Текущее состояние соединения. */
2269
- get status(): RealtimeStatus;
2270
- /** Какой транспорт используется: `sse` или `poll`. */
2271
- get transport(): string;
2272
- /** Подписывается на событие потока. @returns функция отписки */
2273
- on<K extends keyof RealtimeEvents>(event: K, listener: Listener<RealtimeEvents[K]>): Unsubscribe;
2274
- /** Подписывается на одно срабатывание. */
2275
- once<K extends keyof RealtimeEvents>(event: K, listener: Listener<RealtimeEvents[K]>): Unsubscribe;
2276
- /**
2277
- * Поднимает соединение.
2278
- *
2279
- * Повторный вызов при уже живом соединении ничего не делает — это защита от двойного
2280
- * подключения при перерисовке интерфейса.
2281
- *
2282
- * Возвращает управление сразу после запуска: соединение живёт в фоне.
2283
- */
2284
- connect(): Promise<void>;
2285
- /** Закрывает соединение и отменяет запланированные попытки. */
2286
- disconnect(): void;
2287
- /** Снимает все подписки. Соединение при этом не закрывается. */
2288
- removeAllListeners(): void;
2289
- }
2290
-
2291
- /**
2292
- * Страница списка — единая форма для всех трёх схем пагинации API.
2293
- *
2294
- * Какие необязательные поля заполнены, зависит от эндпоинта: у ленты это `nextCursor`,
2295
- * у подписчиков — `page` и `total`, у уведомлений — `nextOffset`. Обычно они не нужны:
2296
- * перебор берёт на себя {@link Paginator}.
2297
- */
2298
- interface Page<T> {
2299
- /** Элементы страницы. */
2300
- items: T[];
2301
- /** Есть ли следующая страница. */
2302
- hasMore: boolean;
2303
- /**
2304
- * Курсор следующей страницы.
2305
- *
2306
- * Непрозрачен: у вкладки `popular` это номер страницы, у `following` — отметка времени.
2307
- * Передавайте его обратно как есть и не пытайтесь разобрать.
2308
- */
2309
- nextCursor?: string | null | undefined;
2310
- /** Номер текущей страницы при постраничной схеме. */
2311
- page?: number | undefined;
2312
- /** Запрошенный размер страницы. */
2313
- limit?: number | undefined;
2314
- /** Общее число элементов, если сервер его сообщил. */
2315
- total?: number | undefined;
2316
- /** Смещение для следующего запроса при схеме со смещением. */
2317
- nextOffset?: number | undefined;
2318
- /** Исходный ответ — на случай, если документация разошлась с реальностью. */
2319
- raw: unknown;
2320
- }
2321
- /** Схема пагинации эндпоинта. */
2322
- declare const PaginationMode: Readonly<{
2323
- /** Следующая страница запрашивается непрозрачным курсором. */
2324
- readonly Cursor: "cursor";
2325
- /** Следующая страница запрашивается номером. */
2326
- readonly Page: "page";
2327
- /** Следующая страница запрашивается смещением от начала списка. */
2328
- readonly Offset: "offset";
2329
- }>;
2330
- type PaginationMode = (typeof PaginationMode)[keyof typeof PaginationMode];
2331
- /** Позиция, с которой запрашивается очередная страница. */
2332
- interface PageState {
2333
- cursor?: string | undefined;
2334
- page?: number | undefined;
2335
- offset?: number | undefined;
2336
- }
2337
- /** Настройки перебора страниц. */
2338
- interface PaginatorOptions<T> {
2339
- mode: PaginationMode;
2340
- /** Загружает одну страницу для указанной позиции. */
2341
- load: (state: PageState) => Promise<Page<T>>;
2342
- /**
2343
- * С какой позиции начать. По умолчанию с начала списка.
2344
- *
2345
- * Нужно, чтобы перебор можно было продолжить с сохранённого курсора, а не только
2346
- * начать заново.
2347
- */
2348
- start?: PageState | undefined;
2349
- /**
2350
- * Предохранитель от бесконечного перебора. По умолчанию 1000.
2351
- *
2352
- * Сработает, только если сервер бесконечно сообщает `hasMore` — при нормальной работе
2353
- * перебор останавливается сам.
2354
- */
2355
- maxPages?: number | undefined;
2356
- /** Отмена перебора. */
2357
- signal?: AbortSignal | undefined;
2358
- }
2359
- /**
2360
- * Перебор страниц списка.
2361
- *
2362
- * Скрывает различия трёх схем пагинации: перебор элементов, страниц и сбор в массив
2363
- * выглядят одинаково независимо от эндпоинта.
2364
- *
2365
- * **Одноразовый.** Позиция хранится внутри, поэтому повторный перебор того же объекта
2366
- * ничего не вернёт: он продолжится с места, где закончился прошлый. Нужен второй проход —
2367
- * возьмите новый перебор у того же метода ресурса.
2368
- *
2369
- * @example Перебор элементов
2370
- * ```ts
2371
- * for await (const post of itd.posts.iterate({ tab: 'following' })) {
2372
- * console.log(post.content);
2373
- * }
2374
- * ```
2375
- *
2376
- * @example Первые сто элементов
2377
- * ```ts
2378
- * const posts = await itd.posts.iterate({ tab: 'popular' }).collect(100);
2379
- * ```
2380
- *
2381
- * @example Постранично
2382
- * ```ts
2383
- * for await (const page of itd.users.followers('durov').pages()) {
2384
- * console.log(page.items.length, 'из', page.total);
2385
- * }
2386
- * ```
2387
- */
2388
- declare class Paginator<T> implements AsyncIterable<T> {
2389
- #private;
2390
- constructor(options: PaginatorOptions<T>);
2391
- /**
2392
- * Загружает следующую страницу.
2393
- *
2394
- * @returns страница либо `null`, если перебор закончен
2395
- */
2396
- next(): Promise<Page<T> | null>;
2397
- /**
2398
- * Перебирает страницы целиком.
2399
- *
2400
- * Полезно, когда нужны сведения о самой странице — например `total`.
2401
- */
2402
- pages(): AsyncGenerator<Page<T>, void, undefined>;
2403
- /** Перебирает элементы всех страниц подряд. */
2404
- [Symbol.asyncIterator](): AsyncGenerator<T, void, undefined>;
2405
- /**
2406
- * Собирает элементы в массив.
2407
- *
2408
- * @param max сколько элементов достаточно; без него перебираются все страницы
2409
- */
2410
- collect(max?: number): Promise<T[]>;
2411
- }
2412
-
2413
- /** Общая основа всех групп методов клиента. */
2414
- declare class BaseResource {
2415
- /** @internal */
2416
- protected readonly http: HttpClient;
2417
- constructor(http: HttpClient);
2418
- /**
2419
- * Переносит общие поля опций запроса в параметры транспорта.
2420
- *
2421
- * Поля перечислены поимённо, а не скопированы целиком: параметры методов наследуют
2422
- * {@link RequestOptions} и приносят с собой `limit`, `cursor` и прочее, чему в описании
2423
- * запроса делать нечего. Исключение — опции, заявленные плагинами: их библиотека
2424
- * не понимает, но обязана донести до обёрток нетронутыми.
2425
- */
2426
- protected requestOptions(options: RequestOptions | undefined): Partial<RequestOptions>;
2427
- /**
2428
- * Собирает перебор страниц.
2429
- *
2430
- * @param mode схема пагинации эндпоинта
2431
- * @param load загружает одну страницу для указанной позиции
2432
- * @param options `maxPages` и `signal`, а также `start` — позиция, с которой продолжить
2433
- */
2434
- protected paginate<T>(mode: PaginationMode, load: (state: PageState) => Promise<Page<T>>, options?: RequestOptions & {
2435
- maxPages?: number;
2436
- start?: PageState;
2437
- }): Paginator<T>;
2438
- }
2439
-
2440
- /** Учётные данные для входа. */
2441
- interface Credentials {
2442
- email: string;
2443
- password: string;
2444
- }
2445
- /**
2446
- * Учётные данные вместе с токеном капчи.
2447
- *
2448
- * `turnstileToken` обязателен: без него сервер отвечает `VALIDATION_ERROR`, с недействительным —
2449
- * `TURNSTILE_VERIFICATION_FAILED`. Токен даёт виджет Cloudflare Turnstile с ключом
2450
- * {@link TURNSTILE_SITE_KEY}; он одноразовый и живёт несколько минут.
2451
- */
2452
- interface CaptchaCredentials extends Credentials {
2453
- turnstileToken: string;
2454
- }
2455
- /** Запрос письма для сброса пароля. Капча обязательна, как и при входе. */
2456
- interface ForgotPasswordInput {
2457
- email: string;
2458
- turnstileToken: string;
2459
- }
2460
- /**
2461
- * Установка нового пароля.
2462
- *
2463
- * Сброс идёт тем же потоком с кодом, что и вход: {@link AuthResource.forgotPassword} выдаёт
2464
- * `flowToken`, письмо приносит `otp`, и всё это вместе с новым паролем уходит сюда.
2465
- */
2466
- interface ResetPasswordInput {
2467
- email: string;
2468
- otp: string;
2469
- flowToken: string;
2470
- newPassword: string;
2471
- }
2472
- /** Провайдер внешнего входа. */
2473
- declare const OAuthProvider: Readonly<{
2474
- readonly Yandex: "yandex";
2475
- readonly Google: "google";
2476
- }>;
2477
- type OAuthProvider = (typeof OAuthProvider)[keyof typeof OAuthProvider];
2478
- /** Чем закончился вход: сразу токеном или запросом кода подтверждения. */
2479
- declare const SignInStatus: Readonly<{
2480
- readonly Authenticated: "authenticated";
2481
- readonly OtpRequired: "otp_required";
2482
- }>;
2483
- type SignInStatus = (typeof SignInStatus)[keyof typeof SignInStatus];
2484
- /**
2485
- * Результат входа.
2486
- *
2487
- * Сервер может как сразу выдать токен, так и потребовать код подтверждения — размеченное
2488
- * объединение делает оба случая явными.
2489
- */
2490
- type SignInResult = {
2491
- status: typeof SignInStatus.Authenticated;
2492
- accessToken: string;
2493
- } | {
2494
- status: typeof SignInStatus.OtpRequired;
2495
- flowToken: string | undefined;
2496
- };
2497
- /**
2498
- * Авторизация, сессии и пароли.
2499
- *
2500
- * Доступна как `itd.auth`.
2501
- */
2502
- declare class AuthResource extends BaseResource {
2503
- #private;
2504
- constructor(http: HttpClient, deps: {
2505
- auth: AuthManager;
2506
- });
2507
- /**
2508
- * Регистрирует аккаунт и запускает подтверждение по коду.
2509
- *
2510
- * @returns `flowToken`, который нужно передать в {@link verifyOtp}
2511
- */
2512
- signUp(credentials: CaptchaCredentials, options?: RequestOptions): Promise<string>;
2513
- /**
2514
- * Выполняет вход.
2515
- *
2516
- * Если сервер потребовал код подтверждения, вернётся `status: 'otp_required'` —
2517
- * тогда продолжайте через {@link verifyOtp} либо воспользуйтесь {@link signInWithOtp}.
2518
- *
2519
- * При успешном входе токен сохраняется в клиенте автоматически.
2520
- *
2521
- * @param credentials email, пароль и обязательный токен капчи — см. {@link CaptchaCredentials}
2522
- */
2523
- signIn(credentials: CaptchaCredentials, options?: RequestOptions): Promise<SignInResult>;
2524
- /**
2525
- * Подтверждает вход кодом из письма.
2526
- *
2527
- * Полученный токен сохраняется в клиенте автоматически.
2528
- */
2529
- verifyOtp(input: Credentials & {
2530
- otp: string;
2531
- flowToken: string;
2532
- }, options?: RequestOptions): Promise<string>;
2533
- /** Отправляет код подтверждения повторно. */
2534
- resendOtp(input: {
2535
- email: string;
2536
- flowToken: string;
2537
- }, options?: RequestOptions): Promise<void>;
2538
- /**
2539
- * Полный вход с подтверждением по коду.
2540
- *
2541
- * Удобно для скриптов и ботов: код запрашивается функцией `getOtp`, а всё остальное
2542
- * библиотека делает сама.
2543
- *
2544
- * @example
2545
- * ```ts
2546
- * import { createInterface } from 'node:readline/promises';
2547
- *
2548
- * const rl = createInterface({ input: process.stdin, output: process.stdout });
2549
- *
2550
- * const token = await itd.auth.signInWithOtp({
2551
- * email, password,
2552
- * getOtp: () => rl.question('Код из письма: '),
2553
- * });
2554
- * ```
2555
- */
2556
- signInWithOtp(input: CaptchaCredentials & {
2557
- getOtp: () => string | Promise<string>;
2558
- }, options?: RequestOptions): Promise<string>;
2559
- /**
2560
- * Обновляет токен доступа.
2561
- *
2562
- * Параллельные вызовы объединяются в один сетевой запрос. При включённом `autoRefresh`
2563
- * вызывать вручную обычно не нужно.
2564
- */
2565
- refresh(): Promise<string>;
2566
- /**
2567
- * Есть ли признак живой сессии обновления.
2568
- *
2569
- * Проверяет cookie `is_auth`, которую сервер ставит рядом с refresh-токеном, а также
2570
- * refresh-токен, переданный строкой. Позволяет не дёргать API у неавторизованного
2571
- * пользователя. В браузере всегда `true`: cookie ведёт сама среда, и прочитать её
2572
- * из JS нельзя.
2573
- *
2574
- * Читает {@link TokenStorage}, поэтому результат верен и до первого запроса.
2575
- *
2576
- * @example
2577
- * ```ts
2578
- * if (await itd.auth.hasRefreshSession()) await itd.auth.refresh();
2579
- * else redirectToLogin();
2580
- * ```
2581
- */
2582
- hasRefreshSession(): Promise<boolean>;
2583
- /** Завершает текущую сессию на сервере и очищает локальную. */
2584
- logout(options?: RequestOptions): Promise<void>;
2585
- /**
2586
- * Завершает все сессии пользователя и очищает локальную.
2587
- *
2588
- * Собран из двух запросов, потому что единого эндпоинта на сервере нет:
2589
- * `POST /api/v1/auth/logout-all` отвечает `404`. Сначала отзываются все прочие сессии
2590
- * (`DELETE /api/v1/auth/sessions`), затем завершается текущая — в обратном порядке
2591
- * отзывать было бы уже нечем.
2592
- */
2593
- logoutAll(options?: RequestOptions): Promise<void>;
2594
- /** Забывает сессию локально, не обращаясь к серверу. */
2595
- signOut(): Promise<void>;
2596
- /**
2597
- * Запрашивает письмо с кодом для сброса пароля.
2598
- *
2599
- * @returns `flowToken`, который нужно передать в {@link resetPassword}
2600
- */
2601
- forgotPassword(input: ForgotPasswordInput, options?: RequestOptions): Promise<string>;
2602
- /**
2603
- * Устанавливает новый пароль по коду из письма.
2604
- *
2605
- * Сервер ждёт все четыре поля сразу — `email`, `otp`, `flowToken` и `newPassword`;
2606
- * при нехватке любого отвечает `422`.
2607
- */
2608
- resetPassword(input: ResetPasswordInput, options?: RequestOptions): Promise<void>;
2609
- /**
2610
- * Полный сброс пароля с кодом из письма.
2611
- *
2612
- * Тот же приём, что и {@link signInWithOtp}: код запрашивается функцией `getOtp`,
2613
- * остальное библиотека делает сама.
2614
- *
2615
- * @example
2616
- * ```ts
2617
- * await itd.auth.resetPasswordWithOtp({
2618
- * email,
2619
- * turnstileToken,
2620
- * newPassword,
2621
- * getOtp: () => rl.question('Код из письма: '),
2622
- * });
2623
- * ```
2624
- */
2625
- resetPasswordWithOtp(input: ForgotPasswordInput & {
2626
- newPassword: string;
2627
- getOtp: () => string | Promise<string>;
2628
- }, options?: RequestOptions): Promise<void>;
2629
- /**
2630
- * Меняет пароль. Требует действующей сессии.
2631
- *
2632
- * При неверном текущем пароле сервер отвечает `ACCOUNT_CURRENT_PASSWORD_INCORRECT`.
2633
- */
2634
- changePassword(input: {
2635
- oldPassword: string;
2636
- newPassword: string;
2637
- }, options?: RequestOptions): Promise<void>;
2638
- /**
2639
- * Возвращает адрес для входа через внешнего провайдера.
2640
- *
2641
- * Сам переход выполняет приложение: в браузере — редиректом, в приложении — открытием
2642
- * системного браузера.
2643
- *
2644
- * @example
2645
- * ```ts
2646
- * window.location.href = itd.auth.oauthUrl('yandex');
2647
- * ```
2648
- */
2649
- oauthUrl(provider: OAuthProvider): string;
2650
- /** Загружает список активных сессий. У текущей поле `isCurrent` равно `true`. */
2651
- sessions(options?: RequestOptions): Promise<Session[]>;
2652
- /** Завершает указанную сессию. */
2653
- revokeSession(sessionId: string, options?: RequestOptions): Promise<void>;
2654
- /** Завершает все сессии, кроме текущей. */
2655
- revokeOtherSessions(options?: RequestOptions): Promise<void>;
2656
- }
2657
-
2658
- /** Параметры запроса ответов на комментарий. */
2659
- interface RepliesParams extends RequestOptions {
2660
- limit?: number;
2661
- page?: number;
2662
- maxPages?: number;
2663
- }
2664
- /**
2665
- * Комментарии и ответы на них.
2666
- *
2667
- * Доступна как `itd.comments`. Комментарии **к посту** живут в `itd.posts`:
2668
- * `itd.posts.comments()` и `itd.posts.comment()`.
2669
- */
2670
- declare class CommentsResource extends BaseResource {
2671
- #private;
2672
- constructor(http: HttpClient, deps: {
2673
- uploadFiles: (files: FileInput[], options?: RequestOptions) => Promise<string[]>;
2674
- });
2675
- /**
2676
- * Загружает страницу ответов на комментарий.
2677
- *
2678
- * Здесь пагинация **постраничная**, в отличие от комментариев к посту, где курсорная.
2679
- */
2680
- replies(commentId: string, params?: RepliesParams): Promise<Page<Comment>>;
2681
- /** Перебирает ответы на комментарий. */
2682
- iterateReplies(commentId: string, params?: RepliesParams): Paginator<Comment>;
2683
- /**
2684
- * Отвечает на комментарий.
2685
- *
2686
- * @example
2687
- * ```ts
2688
- * await itd.comments.reply(commentId, 'согласен');
2689
- * await itd.comments.reply(commentId, (c) => c.content('и вот почему').replyTo(userId));
2690
- * ```
2691
- */
2692
- reply(commentId: string, input: CommentInput | string, options?: RequestOptions): Promise<Comment>;
2693
- /** Редактирует текст комментария. */
2694
- update(commentId: string, content: string, options?: RequestOptions): Promise<Comment>;
2695
- /** Удаляет комментарий. Восстановить его можно через {@link restore}. */
2696
- remove(commentId: string, options?: RequestOptions): Promise<void>;
2697
- /** Восстанавливает удалённый комментарий. */
2698
- restore(commentId: string, options?: RequestOptions): Promise<Comment>;
2699
- /** Ставит реакцию на комментарий. */
2700
- like(commentId: string, options?: RequestOptions): Promise<LikeResult>;
2701
- /** Убирает реакцию с комментария. */
2702
- unlike(commentId: string, options?: RequestOptions): Promise<LikeResult>;
2703
- }
2704
-
2705
- /** Ответ загрузки файла. */
2706
- interface UploadedFile {
2707
- /** Идентификатор вложения — его передают в `attachmentIds`. */
2708
- id: string;
2709
- /** Адрес файла на CDN. */
2710
- url: string;
2711
- }
2712
- /** Настройки загрузки. */
2713
- interface UploadOptions extends RequestOptions {
2714
- /** Имя файла. По нему определяется тип, если он не задан явно. */
2715
- filename?: string;
2716
- /** MIME-тип. Если не указан, определяется по имени файла или по самому `Blob`. */
2717
- contentType?: string;
2718
- /**
2719
- * Проверять тип файла до отправки. По умолчанию `true`.
2720
- *
2721
- * Отключайте, если API начал принимать формат, которого ещё нет в списке библиотеки.
2722
- */
2723
- validateMime?: boolean;
2724
- }
2725
- /** Чтение файла по пути — подставляется точкой входа `itd-api/node`. */
2726
- type FileReader = (path: string) => Promise<{
2727
- data: Uint8Array;
2728
- filename: string;
2729
- }>;
2730
- /**
2731
- * Файлы и медиа.
2732
- *
2733
- * Доступна как `itd.files`. Обычно вызывать её напрямую не нужно: `itd.posts.create()`
2734
- * и `itd.posts.comment()` загружают файлы сами.
2735
- */
2736
- declare class FilesResource extends BaseResource {
2737
- #private;
2738
- constructor(http: HttpClient, deps?: {
2739
- readFile?: FileReader;
2740
- });
2741
- /**
2742
- * Подключает чтение файлов с диска.
2743
- *
2744
- * Вызывается точкой входа `itd-api/node`; в основном бандле работы с файловой
2745
- * системой нет, чтобы браузерные сборщики не пытались разрешить `node:fs`.
2746
- */
2747
- setFileReader(readFile: FileReader): void;
2748
- /**
2749
- * Загружает файл и возвращает его идентификатор.
2750
- *
2751
- * @remarks
2752
- * Кроме типа сервер проверяет и само изображение: слишком маленькие картинки
2753
- * он отклоняет сообщением «Не удалось проверить изображение». Точный порог
2754
- * неизвестен, но 64×64 проходит.
2755
- *
2756
- * @example
2757
- * ```ts
2758
- * const file = await itd.files.upload(blob, { filename: 'photo.jpg' });
2759
- * await itd.posts.create({ content: 'смотрите', attachmentIds: [file.id] });
2760
- * ```
2761
- */
2762
- upload(input: FileInput, options?: UploadOptions): Promise<UploadedFile>;
2763
- /**
2764
- * Загружает несколько файлов, сохраняя порядок.
2765
- *
2766
- * Файлы отправляются последовательно: параллельная загрузка нескольких видео легко
2767
- * упирается в ограничение частоты, а порядок вложений в посте важен.
2768
- *
2769
- * @returns идентификаторы вложений в том же порядке, что и входные файлы
2770
- */
2771
- uploadMany(files: FileInput[], options?: UploadOptions): Promise<string[]>;
2772
- /**
2773
- * Загружает сведения о файле.
2774
- *
2775
- * @remarks
2776
- * Сервер отвечает `404` даже на только что загруженный файл, который ещё никуда
2777
- * не прикреплён, — проверено на боевом API. Практической пользы у метода пока нет,
2778
- * он оставлен для полноты.
2779
- */
2780
- get(fileId: string, options?: RequestOptions): Promise<unknown>;
2781
- /** Удаляет загруженный файл. */
2782
- remove(fileId: string, options?: RequestOptions): Promise<void>;
2783
- }
2784
-
2785
- /** Параметры запроса постов по хэштегу. */
2786
- interface HashtagPostsParams extends RequestOptions {
2787
- limit?: number;
2788
- cursor?: string;
2789
- maxPages?: number;
2790
- }
2791
- /** Результат глобального поиска. */
2792
- interface SearchResult {
2793
- users: UserSummary[];
2794
- hashtags: Hashtag[];
2795
- }
2796
- /**
2797
- * Хэштеги.
2798
- *
2799
- * Доступна как `itd.hashtags`.
2800
- */
2801
- declare class HashtagsResource extends BaseResource {
2802
- /**
2803
- * Ищет хэштеги.
2804
- *
2805
- * Без строки запроса возвращает общий список.
2806
- */
2807
- search(query?: string, params?: {
2808
- limit?: number;
2809
- } & RequestOptions): Promise<Hashtag[]>;
2810
- /** Загружает трендовые хэштеги. */
2811
- trending(params?: {
2812
- limit?: number;
2813
- } & RequestOptions): Promise<Hashtag[]>;
2814
- /**
2815
- * Загружает страницу постов по хэштегу.
2816
- *
2817
- * @param tag название без решётки; кодируется автоматически, поэтому кириллица
2818
- * и пробелы допустимы
2819
- */
2820
- posts(tag: string, params?: HashtagPostsParams): Promise<Page<Post>>;
2821
- /** Перебирает посты по хэштегу. */
2822
- iteratePosts(tag: string, params?: HashtagPostsParams): Paginator<Post>;
2823
- }
2824
- /**
2825
- * Глобальный поиск.
2826
- *
2827
- * Доступна как `itd.search`.
2828
- */
2829
- declare class SearchResource extends BaseResource {
2830
- /**
2831
- * Ищет пользователей и хэштеги одним запросом.
2832
- *
2833
- * @example
2834
- * ```ts
2835
- * const { users, hashtags } = await itd.search.all('арт');
2836
- * ```
2837
- */
2838
- all(query: string, options?: RequestOptions): Promise<SearchResult>;
2839
- }
2840
- /**
2841
- * Жалобы на контент и пользователей.
2842
- *
2843
- * Доступна как `itd.reports`.
2844
- */
2845
- declare class ReportsResource extends BaseResource {
2846
- /**
2847
- * Отправляет жалобу.
2848
- *
2849
- * Повторная жалоба на тот же объект отклоняется сервером с сообщением
2850
- * «Вы уже отправляли жалобу на этот контент».
2851
- *
2852
- * @example
2853
- * ```ts
2854
- * await itd.reports.create(report.post(postId).reason('spam'));
2855
- * await itd.reports.create({ targetType: 'user', targetId, reason: 'fraud' });
2856
- * ```
2857
- */
2858
- create(input: ReportInput, options?: RequestOptions): Promise<Report>;
2859
- }
2860
- /**
2861
- * Верификация профиля.
2862
- *
2863
- * Доступна как `itd.verification`.
2864
- */
2865
- declare class VerificationResource extends BaseResource {
2866
- /** Загружает статус заявки. Значение `none` означает, что заявка не подавалась. */
2867
- status(options?: RequestOptions): Promise<VerificationStatus>;
2868
- /** Подаёт заявку на верификацию с видео. */
2869
- submit(videoUrl: string, options?: RequestOptions): Promise<unknown>;
2870
- }
2871
- /**
2872
- * Подписка и способы оплаты.
2873
- *
2874
- * Доступна как `itd.subscription`.
2875
- */
2876
- declare class SubscriptionResource extends BaseResource {
2877
- /** Загружает состояние подписки и её цену. */
2878
- status(options?: RequestOptions): Promise<Subscription>;
2879
- /**
2880
- * Запускает оплату подписки.
2881
- *
2882
- * Форма ответа в документации API не описана, поэтому тип результата не уточняется.
2883
- */
2884
- pay(options?: RequestOptions): Promise<unknown>;
2885
- /** Включает или отключает автопродление. */
2886
- setAutoRenewal(enabled: boolean, options?: RequestOptions): Promise<unknown>;
2887
- /** Запускает привязку карты. */
2888
- bindCard(options?: RequestOptions): Promise<unknown>;
2889
- /** Загружает список способов оплаты. Пустой массив, если карт нет. */
2890
- methods(options?: RequestOptions): Promise<PaymentMethod[]>;
2891
- /** Делает способ оплаты основным. */
2892
- setDefaultMethod(methodId: string, options?: RequestOptions): Promise<unknown>;
2893
- /** Удаляет способ оплаты. */
2894
- removeMethod(methodId: string, options?: RequestOptions): Promise<void>;
2895
- }
2896
- /**
2897
- * Сведения о платформе: изменения, анонсы, баннер события.
2898
- *
2899
- * Доступна как `itd.platform`.
2900
- */
2901
- declare class PlatformResource extends BaseResource {
2902
- /** Загружает журнал изменений. */
2903
- changelog(options?: RequestOptions): Promise<ChangelogEntry[]>;
2904
- /** Загружает анонсы платформы. */
2905
- announcements(options?: RequestOptions): Promise<Announcement[]>;
2906
- /** Загружает баннер текущего события — виджет «портал». */
2907
- portal(options?: RequestOptions): Promise<Portal>;
2908
- }
2909
-
2910
- /** Параметры запроса списка уведомлений. */
2911
- interface NotificationListParams extends RequestOptions {
2912
- limit?: number;
2913
- /** Смещение от начала списка. */
2914
- offset?: number;
2915
- maxPages?: number;
2916
- }
2917
- /** Изменяемые настройки уведомлений. */
2918
- type UpdateNotificationSettingsInput = Partial<NotificationSettings>;
2919
- /**
2920
- * Уведомления: список, счётчик, отметки о прочтении, настройки.
2921
- *
2922
- * Доступна как `itd.notifications`. Все уведомления приведены к единой форме, поэтому
2923
- * объекты отсюда и из потока событий можно складывать в один список.
2924
- */
2925
- declare class NotificationsResource extends BaseResource {
2926
- #private;
2927
- /**
2928
- * Загружает страницу уведомлений.
2929
- *
2930
- * Пагинация здесь основана на смещении. Сайт итд.com оборачивает смещение в строку
2931
- * и притворяется, что это курсор; библиотека отдаёт честное число.
2932
- *
2933
- * @example
2934
- * ```ts
2935
- * const page = await itd.notifications.list({ limit: 20 });
2936
- * const next = await itd.notifications.list({ limit: 20, offset: page.nextOffset });
2937
- * ```
2938
- */
2939
- list(params?: NotificationListParams): Promise<Page<Notification>>;
2940
- /**
2941
- * Перебирает уведомления.
2942
- *
2943
- * @example
2944
- * ```ts
2945
- * for await (const notification of itd.notifications.iterate()) {
2946
- * console.log(formatNotificationText(notification));
2947
- * }
2948
- * ```
2949
- */
2950
- iterate(params?: NotificationListParams): Paginator<Notification>;
2951
- /** Загружает число непрочитанных уведомлений. */
2952
- count(options?: RequestOptions): Promise<number>;
2953
- /**
2954
- * Отмечает уведомление прочитанным.
2955
- *
2956
- * @returns сколько записей отметил сервер
2957
- */
2958
- markRead(notificationId: string, options?: RequestOptions): Promise<number>;
2959
- /**
2960
- * Отмечает прочитанными сразу несколько уведомлений.
2961
- *
2962
- * Список автоматически режется на части по 20 идентификаторов — столько же отправляет
2963
- * сайт итд.com, поэтому на сервере вероятен предел. Части уходят последовательно,
2964
- * результат суммируется.
2965
- *
2966
- * @returns сколько записей отметил сервер суммарно
2967
- */
2968
- markReadBatch(ids: string[], options?: RequestOptions): Promise<number>;
2969
- /** Отмечает прочитанными все уведомления. */
2970
- markAllRead(options?: RequestOptions): Promise<number>;
2971
- /** Загружает настройки уведомлений. */
2972
- getSettings(options?: RequestOptions): Promise<NotificationSettings>;
2973
- /**
2974
- * Обновляет настройки уведомлений.
2975
- *
2976
- * Отправляются только изменяемые поля, в том же виде, в каком сервер их возвращает.
2977
- */
2978
- updateSettings(input: UpdateNotificationSettingsInput, options?: RequestOptions): Promise<NotificationSettings>;
2979
- }
2980
-
2981
- /** Параметры запроса ленты. */
2982
- interface FeedParams extends RequestOptions {
2983
- /** Вкладка ленты. По умолчанию сервер отдаёт популярное. */
2984
- tab?: FeedTab;
2985
- /** Сколько постов на страницу. */
2986
- limit?: number;
2987
- /**
2988
- * Курсор следующей страницы из предыдущего ответа.
2989
- *
2990
- * Передавайте значение как есть: его формат зависит от вкладки и может измениться.
2991
- */
2992
- cursor?: string;
2993
- /** Ограничение числа страниц при переборе. */
2994
- maxPages?: number;
2995
- }
2996
- /** Параметры запроса постов пользователя. */
2997
- interface UserPostsParams extends RequestOptions {
2998
- limit?: number;
2999
- cursor?: string;
3000
- /** Порядок сортировки. */
3001
- sort?: string;
3002
- /** Закреплённый пост, чтобы сервер поднял его наверх. */
3003
- pinnedPostId?: string;
3004
- maxPages?: number;
3005
- }
3006
- /** Параметры запроса комментариев к посту. */
3007
- interface CommentsParams extends RequestOptions {
3008
- limit?: number;
3009
- /**
3010
- * Курсор следующей страницы: идентификатор последнего полученного комментария.
3011
- *
3012
- * Передавайте значение из `nextCursor` предыдущего ответа как есть.
3013
- */
3014
- cursor?: string;
3015
- sort?: CommentSort;
3016
- maxPages?: number;
3017
- }
3018
- /**
3019
- * Посты: лента, публикация, реакции, репосты, комментарии.
3020
- *
3021
- * Доступна как `itd.posts`.
3022
- */
3023
- declare class PostsResource extends BaseResource {
3024
- #private;
3025
- constructor(http: HttpClient, deps: {
3026
- uploadFiles: (files: FileInput[], options?: RequestOptions) => Promise<string[]>;
3027
- });
3028
- /**
3029
- * Загружает страницу ленты.
3030
- *
3031
- * @example
3032
- * ```ts
3033
- * const page = await itd.posts.list({ tab: FeedTab.Following, limit: 20 });
3034
- * const next = await itd.posts.list({ tab: FeedTab.Following, cursor: page.nextCursor ?? undefined });
3035
- * ```
3036
- */
3037
- list(params?: FeedParams): Promise<Page<Post>>;
3038
- /**
3039
- * Перебирает ленту, сама подставляя курсоры.
3040
- *
3041
- * @example
3042
- * ```ts
3043
- * for await (const post of itd.posts.iterate({ tab: 'following' })) {
3044
- * console.log(post.author.username, post.content);
3045
- * }
3046
- * ```
3047
- */
3048
- iterate(params?: FeedParams): Paginator<Post>;
3049
- /**
3050
- * Публикует пост.
3051
- *
3052
- * Принимает обычный объект, {@link PostBuilder} или функцию-настройщик. Файлы из поля
3053
- * `files` загружаются автоматически, порядок вложений сохраняется.
3054
- *
3055
- * @example
3056
- * ```ts
3057
- * await itd.posts.create({ content: 'привет' });
3058
- * await itd.posts.create((p) => p.content('привет').attach('./photo.jpg'));
3059
- * ```
3060
- */
3061
- create(input: PostInput, options?: RequestOptions): Promise<Post>;
3062
- /**
3063
- * Загружает один пост вместе с топовыми комментариями.
3064
- *
3065
- * В отличие от списков, здесь у поста заполнено поле `comments`.
3066
- */
3067
- get(postId: string, options?: RequestOptions): Promise<Post>;
3068
- /** Редактирует текст поста. */
3069
- update(postId: string, input: Pick<CreatePostInput, 'content' | 'spans'>, options?: RequestOptions): Promise<Post>;
3070
- /** Удаляет пост. Восстановить его можно через {@link restore}. */
3071
- remove(postId: string, options?: RequestOptions): Promise<void>;
3072
- /** Восстанавливает удалённый пост. */
3073
- restore(postId: string, options?: RequestOptions): Promise<Post>;
3074
- /** Ставит реакцию на пост. */
3075
- like(postId: string, options?: RequestOptions): Promise<LikeResult>;
3076
- /** Убирает реакцию с поста. */
3077
- unlike(postId: string, options?: RequestOptions): Promise<LikeResult>;
3078
- /**
3079
- * Делает репост с необязательным комментарием.
3080
- *
3081
- * Вложения к репосту не поддерживаются: сервер их игнорирует, поэтому параметров
3082
- * для файлов здесь нет.
3083
- */
3084
- repost(postId: string, content?: string, options?: RequestOptions): Promise<Post>;
3085
- /** Отменяет репост. */
3086
- unrepost(postId: string, options?: RequestOptions): Promise<void>;
3087
- /** Закрепляет пост в профиле. */
3088
- pin(postId: string, options?: RequestOptions): Promise<PinPostResult>;
3089
- /** Открепляет пост. */
3090
- unpin(postId: string, options?: RequestOptions): Promise<PinPostResult>;
3091
- /**
3092
- * Голосует в опросе.
3093
- *
3094
- * @param optionIds выбранные варианты; несколько допустимы только при `multipleChoice`
3095
- */
3096
- vote(postId: string, optionIds: string[], options?: RequestOptions): Promise<Poll>;
3097
- /** Запрашивает счётчики сразу для нескольких постов. */
3098
- stats(ids: string[], options?: RequestOptions): Promise<PostStats[]>;
3099
- /**
3100
- * Загружает страницу стены пользователя.
3101
- *
3102
- * Это **не только его собственные посты**: сюда попадают и записи, которые другие
3103
- * оставили на его стене — у них `author` чужой, а `wallRecipient` указывает на владельца
3104
- * стены. Поэтому число записей обычно больше, чем `postsCount` из профиля; чтобы
3105
- * получить только авторские посты, отфильтруйте по `post.author.id`.
3106
- *
3107
- * Принимает и UUID, и имя пользователя.
3108
- */
3109
- byUser(user: UserRef, params?: UserPostsParams): Promise<Page<Post>>;
3110
- /** Перебирает стену пользователя. Что именно в неё входит — см. {@link byUser}. */
3111
- iterateByUser(user: UserRef, params?: UserPostsParams): Paginator<Post>;
3112
- /** Загружает страницу постов, которые пользователь отметил реакцией. */
3113
- likedByUser(user: UserRef, params?: UserPostsParams): Promise<Page<Post>>;
3114
- /** Перебирает посты, которые пользователь отметил реакцией. */
3115
- iterateLikedByUser(user: UserRef, params?: UserPostsParams): Paginator<Post>;
3116
- /**
3117
- * Загружает страницу комментариев к посту.
3118
- *
3119
- * У этого эндпоинта курсор и признак продолжения лежат рядом со списком, а не внутри
3120
- * объекта `pagination`, как у остальных, — разница скрыта внутри.
3121
- */
3122
- comments(postId: string, params?: CommentsParams): Promise<Page<Comment>>;
3123
- /** Перебирает комментарии к посту. */
3124
- iterateComments(postId: string, params?: CommentsParams): Paginator<Comment>;
3125
- /**
3126
- * Комментирует пост.
3127
- *
3128
- * @example
3129
- * ```ts
3130
- * await itd.posts.comment(postId, 'согласен');
3131
- * await itd.posts.comment(postId, (c) => c.content('смотри').attach('./meme.png'));
3132
- * ```
3133
- */
3134
- comment(postId: string, input: CommentInput | string, options?: RequestOptions): Promise<Comment>;
3135
- /**
3136
- * Отправляет голосовой комментарий.
3137
- *
3138
- * Текста у такого комментария нет: сервер ждёт пустой `content` и одно аудиовложение
3139
- * в формате `audio/ogg`.
3140
- *
3141
- * @example
3142
- * ```ts
3143
- * await itd.posts.voiceComment(postId, './answer.ogg');
3144
- * ```
3145
- */
3146
- voiceComment(postId: string, audio: FileInput, options?: RequestOptions): Promise<Comment>;
3147
- }
3148
-
3149
- /**
3150
- * Общие опции запроса телеметрии.
3151
- *
3152
- * Помимо {@link RequestOptions} позволяет задать `sid` — идентификатор сессии телеметрии.
3153
- * По умолчанию он заводится один на объект {@link TelemetryResource}.
3154
- */
3155
- interface TelemetryOptions extends RequestOptions {
3156
- /** Переопределяет идентификатор сессии телеметрии (`sid`) для этого запроса. */
3157
- sid?: string;
3158
- }
3159
- /**
3160
- * Событие просмотра поста для {@link TelemetryResource.dwell}.
3161
- *
3162
- * Пост определяется меткой показа `vs` и, при наличии, контекстом источника `sc`; поля
3163
- * `postId` эндпоинт `/api/v1/i` не принимает.
3164
- */
3165
- interface DwellEntry {
3166
- /** Метка показа — поле `vs` объекта поста. Уходит в поле `v`. */
3167
- vs: string;
3168
- /** Время появления поста в зоне видимости, epoch-мс. Поле `et`. */
3169
- enterAt: number;
3170
- /** Время ухода из зоны видимости, epoch-мс. Поле `xt`. */
3171
- exitAt: number;
3172
- /** Причина завершения просмотра. Поле `r`. */
3173
- reason: ViewReason;
3174
- /**
3175
- * Длительность просмотра в мс. Поле `md`.
3176
- *
3177
- * Если не задано, вычисляется как `exitAt − enterAt`.
3178
- */
3179
- durationMs?: number;
3180
- /** Контекст источника показа. Поле `sc`. */
3181
- sourceContext?: string;
3182
- /** Источник показа. Применим к `PostPage`/`Link`. Поле `s`. */
3183
- source?: ViewSource;
3184
- /** Повторный просмотр: пост уже встречался в этой сессии. Уходит как `b: 1`. */
3185
- repeat?: boolean;
3186
- }
3187
- /** Событие взаимодействия с контентом для {@link TelemetryResource.interaction}. */
3188
- interface InteractionEntry {
3189
- /** Тип взаимодействия. Поле `t`. */
3190
- type: InteractionType;
3191
- /** Метка показа — поле `vs` объекта поста. Поле `v`. */
3192
- vs: string;
3193
- /** Идентификатор поста. Поле `ai`. */
3194
- postId: string;
3195
- /** Индекс вложения (с нуля) — для {@link InteractionType.PhotoOpen}. Поле `mi`. */
3196
- mediaIndex?: number;
3197
- /** Источник показа. Поле `s`. */
3198
- source?: ViewSource;
3199
- /** Просмотрено мс — для {@link InteractionType.VideoProgress}. Поле `pm`. */
3200
- positionMs?: number;
3201
- /** Длительность видео в мс — для {@link InteractionType.VideoProgress}. Поле `dm`. */
3202
- durationMs?: number;
3203
- }
3204
- /**
3205
- * Телеметрия просмотров.
3206
- *
3207
- * @experimental Недокументированные эндпоинты `/api/v1/i` (просмотры) и `/api/v1/x`
3208
- * (взаимодействия); формат полей может измениться без предупреждения.
3209
- *
3210
- * Методы не вызываются автоматически — телеметрия отправляется только явным вызовом.
3211
- *
3212
- * Оба эндпоинта принимают конверт `{ sid, e }`, где `sid` — идентификатор сессии
3213
- * телеметрии: по умолчанию один на объект, переопределяется опцией `sid`.
3214
- *
3215
- * Доступна как `itd.telemetry`.
3216
- */
3217
- declare class TelemetryResource extends BaseResource {
3218
- #private;
3219
- /**
3220
- * Идентификатор сессии телеметрии (`sid`).
3221
- *
3222
- * Создаётся лениво при первом обращении и далее неизменен.
3223
- */
3224
- get sessionId(): string;
3225
- /**
3226
- * Отправляет события просмотра постов (`POST /api/v1/i`).
3227
- *
3228
- * @experimental См. предупреждение у {@link TelemetryResource}.
3229
- */
3230
- dwell(entries: DwellEntry[], options?: TelemetryOptions): Promise<unknown>;
3231
- /**
3232
- * Отправляет события взаимодействия с контентом (`POST /api/v1/x`).
3233
- *
3234
- * @experimental См. предупреждение у {@link TelemetryResource}.
3235
- */
3236
- interaction(entries: InteractionEntry[], options?: TelemetryOptions): Promise<unknown>;
3237
- }
3238
-
3239
- /**
3240
- * Параметры списков пользователей.
3241
- *
3242
- * ⚠️ Списки подписчиков, подписок и заблокированных на сервере **не листаются**:
3243
- * `page` он игнорирует, а `limit` зажимает на 20. Подробности — в {@link UsersResource.followers}.
3244
- */
3245
- interface UserListParams extends RequestOptions {
3246
- /** Сколько записей вернуть. Значения больше 20 сервер молча уменьшает до 20. */
3247
- limit?: number;
3248
- /** Номер страницы. Сервер его игнорирует — оставлен на случай, если пагинацию починят. */
3249
- page?: number;
3250
- maxPages?: number;
3251
- }
3252
- /** Изменяемые поля своего профиля. */
3253
- interface UpdateProfileInput {
3254
- displayName?: string;
3255
- username?: string;
3256
- /** Эмодзи-аватар: символ клана, а не адрес картинки. */
3257
- avatar?: string;
3258
- bio?: string;
3259
- /** Адрес изображения-шапки. */
3260
- banner?: string;
3261
- }
3262
- /** Изменяемые настройки приватности. */
3263
- type UpdatePrivacyInput = Partial<PrivacySettings>;
3264
- /**
3265
- * Пользователи: профили, подписки, блокировки, приватность.
3266
- *
3267
- * Доступна как `itd.users`.
3268
- */
3269
- declare class UsersResource extends BaseResource {
3270
- #private;
3271
- /** Загружает свой профиль — с подпиской и признаком подтверждённого телефона. */
3272
- me(options?: RequestOptions): Promise<MyProfile>;
3273
- /** Обновляет свой профиль. Передавайте только изменяемые поля. */
3274
- updateMe(input: UpdateProfileInput, options?: RequestOptions): Promise<MyProfile>;
3275
- /** Деактивирует аккаунт. Вернуть его можно через {@link restore}. */
3276
- deactivate(options?: RequestOptions): Promise<void>;
3277
- /** Восстанавливает деактивированный аккаунт. */
3278
- restore(options?: RequestOptions): Promise<void>;
3279
- /** Создаёт профиль после регистрации. */
3280
- createProfile(input: {
3281
- username: string;
3282
- displayName: string;
3283
- avatar?: string;
3284
- }, options?: RequestOptions): Promise<MyProfile>;
3285
- /**
3286
- * Загружает профиль пользователя.
3287
- *
3288
- * @param user UUID **или** имя пользователя — подходит и то, и другое
3289
- *
3290
- * @example
3291
- * ```ts
3292
- * const profile = await itd.users.get('durov');
3293
- * await itd.posts.create({ content: 'привет', wallRecipientId: profile.id });
3294
- * ```
3295
- */
3296
- get(user: UserRef, options?: RequestOptions): Promise<PublicProfile>;
3297
- /** Проверяет, свободно ли имя пользователя. */
3298
- checkUsername(username: string, options?: RequestOptions): Promise<boolean>;
3299
- /** Ищет пользователей по строке запроса. */
3300
- search(query: string, params?: {
3301
- limit?: number;
3302
- } & RequestOptions): Promise<UserSummary[]>;
3303
- /** Загружает рекомендации, на кого подписаться. */
3304
- whoToFollow(options?: RequestOptions): Promise<UserSummary[]>;
3305
- /** Загружает рейтинг кланов. */
3306
- topClans(options?: RequestOptions): Promise<Clan[]>;
3307
- /**
3308
- * Подписывается на пользователя.
3309
- *
3310
- * У закрытого профиля вместо подписки отправляется заявка — это видно по полю `status`.
3311
- */
3312
- follow(user: UserRef, options?: RequestOptions): Promise<FollowResult>;
3313
- /** Отписывается от пользователя. */
3314
- unfollow(user: UserRef, options?: RequestOptions): Promise<void>;
3315
- /**
3316
- * Загружает подписчиков пользователя.
3317
- *
3318
- * ⚠️ **Сервер этот список не листает.** Возвращаются первые 20 записей и только они:
3319
- * параметр `page` игнорируется (любая страница отдаёт те же записи и `pagination.page: 1`),
3320
- * `limit` больше 20 молча уменьшается, а `hasMore` всегда `false`. Последнее честно —
3321
- * получить продолжение нечем.
3322
- *
3323
- * Числу `total` доверять тоже не стоит: оно расходится с `followersCount` из профиля —
3324
- * на проверенных аккаунтах занижено примерно на 1–4%.
3325
- */
3326
- followers(user: UserRef, params?: UserListParams): Promise<Page<UserSummary>>;
3327
- /**
3328
- * Перебирает подписчиков.
3329
- *
3330
- * ⚠️ Перебор закончится после первых 20 записей: сервер список не листает —
3331
- * см. {@link followers}. Метод оставлен на случай, если пагинацию починят.
3332
- */
3333
- iterateFollowers(user: UserRef, params?: UserListParams): Paginator<UserSummary>;
3334
- /** Загружает подписки пользователя. Ограничения те же, что у {@link followers}. */
3335
- following(user: UserRef, params?: UserListParams): Promise<Page<UserSummary>>;
3336
- /** Перебирает подписки. Закончится после первых 20 записей — см. {@link followers}. */
3337
- iterateFollowing(user: UserRef, params?: UserListParams): Paginator<UserSummary>;
3338
- /**
3339
- * Проверяет, подписаны ли вы, сразу для нескольких пользователей.
3340
- *
3341
- * @returns объект «идентификатор пользователя → подписаны ли вы»
3342
- *
3343
- * @example
3344
- * ```ts
3345
- * const statuses = await itd.users.followStatus([userA, userB]);
3346
- * // { 'b89dee4f-…': true, '35ea3059-…': false }
3347
- * ```
3348
- */
3349
- followStatus(userIds: UserId[], options?: RequestOptions): Promise<Record<string, boolean>>;
3350
- /** Блокирует пользователя. */
3351
- block(user: UserRef, options?: RequestOptions): Promise<void>;
3352
- /** Снимает блокировку. */
3353
- unblock(user: UserRef, options?: RequestOptions): Promise<void>;
3354
- /** Загружает заблокированных пользователей. Ограничения те же, что у {@link followers}. */
3355
- blocked(params?: UserListParams): Promise<Page<UserSummary>>;
3356
- /** Перебирает заблокированных. Закончится после первых 20 записей — см. {@link followers}. */
3357
- iterateBlocked(params?: UserListParams): Paginator<UserSummary>;
3358
- /** Загружает настройки приватности. */
3359
- getPrivacy(options?: RequestOptions): Promise<PrivacySettings>;
3360
- /** Обновляет настройки приватности. Передавайте только изменяемые поля. */
3361
- updatePrivacy(input: UpdatePrivacyInput, options?: RequestOptions): Promise<PrivacySettings>;
3362
- /**
3363
- * Загружает значки профиля и выбранный из них.
3364
- *
3365
- * `activePin` — строка-идентификатор, а не объект.
3366
- */
3367
- pins(options?: RequestOptions): Promise<PinsResult>;
3368
- /** Выбирает активный значок профиля. */
3369
- setPin(slug: string, options?: RequestOptions): Promise<void>;
3370
- /** Снимает активный значок. */
3371
- removePin(options?: RequestOptions): Promise<void>;
3372
- }
3373
-
3374
- /**
3375
- * Клиент API итд.com.
3376
- *
3377
- * Методы сгруппированы по разделам: `itd.posts`, `itd.users`, `itd.comments`, `itd.auth`,
3378
- * `itd.files`. Авторизация, обновление токена, повторы и очередь запросов работают сами.
3379
- *
3380
- * @example Готовый токен — для разового вызова
3381
- * ```ts
3382
- * const itd = new ItdClient({ auth: '<accessToken>' });
3383
- * const me = await itd.users.me();
3384
- * ```
3385
- *
3386
- * @example Полноценная сессия для бота
3387
- * ```ts
3388
- * import { ItdClient } from 'itd-api';
3389
- * import { FileTokenStorage } from 'itd-api/node';
3390
- *
3391
- * const itd = new ItdClient({
3392
- * // `auth` не обязателен: когда хранилище уже содержит сессию, токен берётся оттуда,
3393
- * // а истёкший продлевается сам. Здесь он нужен на первый запуск.
3394
- * // Вход по паролю требует токена капчи — см. AuthInput и TURNSTILE_SITE_KEY.
3395
- * auth: { email, password, getTurnstileToken },
3396
- * storage: new FileTokenStorage('./.itd-session.json'),
3397
- * rateLimit: { concurrency: 4, rps: 8 },
3398
- * });
3399
- *
3400
- * for await (const post of itd.posts.iterate({ tab: 'following' })) {
3401
- * if (!post.isLiked) await itd.posts.like(post.id);
3402
- * }
3403
- * ```
3404
- */
3405
- declare class ItdClient {
3406
- #private;
3407
- /** Авторизация, сессии и пароли. */
3408
- readonly auth: AuthResource;
3409
- /** Профили, подписки, блокировки, приватность. */
3410
- readonly users: UsersResource;
3411
- /** Лента, публикация, реакции, репосты, комментарии к постам. */
3412
- readonly posts: PostsResource;
3413
- /** Ответы на комментарии и действия над ними. */
3414
- readonly comments: CommentsResource;
3415
- /** Загрузка файлов и медиа. */
3416
- readonly files: FilesResource;
3417
- /** Уведомления: список, счётчик, отметки о прочтении, настройки. */
3418
- readonly notifications: NotificationsResource;
3419
- /** Хэштеги и посты по ним. */
3420
- readonly hashtags: HashtagsResource;
3421
- /** Глобальный поиск по пользователям и хэштегам. */
3422
- readonly search: SearchResource;
3423
- /** Жалобы на контент и пользователей. */
3424
- readonly reports: ReportsResource;
3425
- /** Верификация профиля. */
3426
- readonly verification: VerificationResource;
3427
- /** Подписка и способы оплаты. */
3428
- readonly subscription: SubscriptionResource;
3429
- /** Сведения о платформе: изменения, анонсы, баннер события. */
3430
- readonly platform: PlatformResource;
3431
- /**
3432
- * Телеметрия просмотров.
3433
- *
3434
- * @experimental Недокументированные эндпоинты. Библиотека никогда не отправляет их сама.
3435
- */
3436
- readonly telemetry: TelemetryResource;
3437
- constructor(options?: ItdClientOptions);
3438
- /** Базовый URL, к которому обращается клиент. */
3439
- get baseUrl(): string;
3440
- /**
3441
- * Выполняет произвольный запрос к API.
3442
- *
3443
- * Запасной путь для случаев, когда нужного метода ещё нет или ответ сервера разошёлся
3444
- * с документацией. Проходит через ту же авторизацию, очередь и обработку ошибок.
3445
- *
3446
- * @example
3447
- * ```ts
3448
- * const raw = await itd.request({ method: 'GET', path: '/api/posts', raw: true });
3449
- * ```
3450
- */
3451
- request<T = unknown>(options: RawRequestOptions): Promise<T>;
3452
- /**
3453
- * Подключает плагин.
3454
- *
3455
- * Плагин работает на уровне транспорта: видит запрос до отправки и разобранный ответ,
3456
- * поэтому одна обёртка охватывает сразу все методы клиента. Подключать можно в любой
3457
- * момент, но обычно это делают сразу после создания клиента.
3458
- *
3459
- * @throws {ItdConfigError} если плагин задан неверно или уже подключён
3460
- *
3461
- * @example
3462
- * ```ts
3463
- * import { crypt } from 'itd-api-crypto';
3464
- *
3465
- * itd.use(crypt());
3466
- * await itd.posts.create({ content: 'секрет' }, { encrypt: 'invis' });
3467
- * ```
3468
- */
3469
- use(plugin: ItdPlugin): this;
3470
- /**
3471
- * Подписывается на события авторизации.
3472
- *
3473
- * Полезно, чтобы сохранять сессию во внешнее хранилище или узнавать, что вход
3474
- * окончательно потерян.
3475
- *
3476
- * @returns функция отписки
3477
- *
3478
- * @example
3479
- * ```ts
3480
- * itd.on('tokens', ({ accessToken }) => cache.set('itd', accessToken));
3481
- * itd.on('authError', () => notifyUser('Сессия истекла, войдите заново'));
3482
- * ```
3483
- */
3484
- on<K extends keyof AuthEvents>(event: K, listener: Listener<AuthEvents[K]>): Unsubscribe;
3485
- /**
3486
- * Создаёт поток уведомлений в реальном времени.
3487
- *
3488
- * Каждый вызов даёт новый независимый поток; обычно он нужен один на приложение.
3489
- * Соединение поднимается методом `connect()` и держится само.
3490
- *
3491
- * @example
3492
- * ```ts
3493
- * const stream = itd.realtime();
3494
- *
3495
- * stream.on('notification', ({ notification }) => {
3496
- * console.log(formatNotificationText(notification));
3497
- * });
3498
- * stream.on('unreadCount', (count) => setBadge(count));
3499
- *
3500
- * await stream.connect();
3501
- * ```
3502
- */
3503
- realtime(options?: RealtimeOptions): ItdRealtime;
3504
- /** Текущая сессия целиком — чтобы сохранить её самостоятельно. */
3505
- getSession(): Promise<ItdSession | null>;
3506
- /** Восстанавливает сохранённую сессию, включая cookie. */
3507
- setSession(session: ItdSession): Promise<void>;
3508
- /**
3509
- * Подключает чтение файлов с диска.
3510
- *
3511
- * Вызывается из `itd-api/node`; напрямую обычно не нужно.
3512
- *
3513
- * @internal
3514
- */
3515
- setFileReader(readFile: FileReader): void;
3516
- }
3517
- /**
3518
- * Создаёт клиент API итд.com.
3519
- *
3520
- * То же, что `new ItdClient(options)`, — для тех, кому привычнее фабрика.
3521
- *
3522
- * @example
3523
- * ```ts
3524
- * const itd = createClient({ auth: token });
3525
- * ```
3526
- */
3527
- declare function createClient(options?: ItdClientOptions): ItdClient;
3528
-
3529
- /** Бренд, по которому ошибки библиотеки распознаются надёжнее, чем через `instanceof`. */
3530
- declare const ITD_ERROR: unique symbol;
3531
- /**
3532
- * Категория ошибки. Определяет, какие поля у неё есть.
3533
- *
3534
- * Значение, а не только тип: категорию нужно с чем-то сравнивать в рантайме.
3535
- */
3536
- declare const ItdErrorKind: Readonly<{
3537
- /** Сервер ответил статусом ≥ 400. */
3538
- readonly Api: "api";
3539
- /** Запрос не дошёл до сервера. */
3540
- readonly Network: "network";
3541
- /** Истёк таймаут запроса. */
3542
- readonly Timeout: "timeout";
3543
- /** Запрос отменён через `AbortSignal`. */
3544
- readonly Abort: "abort";
3545
- /** Некорректная конфигурация или аргументы — обнаружено до обращения к сети. */
3546
- readonly Config: "config";
3547
- }>;
3548
- type ItdErrorKind = (typeof ItdErrorKind)[keyof typeof ItdErrorKind];
3549
- /**
3550
- * Разновидность ошибки API — то же, что класс ошибки, но в виде данных.
3551
- *
3552
- * Существует потому, что `instanceof` подводит, когда в дереве зависимостей оказались
3553
- * две копии пакета или смешаны сборки ESM и CJS: классы тогда разные, хотя ошибка та же.
3554
- * Проверки {@link isItdValidationError} и соседние опираются на это поле, а не на класс.
3555
- */
3556
- declare const ItdApiErrorKind: Readonly<{
3557
- /** Ни одна из специализаций не подошла. */
3558
- readonly Generic: "generic";
3559
- readonly Validation: "validation";
3560
- readonly Auth: "auth";
3561
- readonly Forbidden: "forbidden";
3562
- readonly NotFound: "not_found";
3563
- readonly Conflict: "conflict";
3564
- readonly RateLimit: "rate_limit";
3565
- readonly PhoneVerification: "phone_verification";
3566
- readonly Server: "server";
3567
- }>;
3568
- type ItdApiErrorKind = (typeof ItdApiErrorKind)[keyof typeof ItdApiErrorKind];
3569
- /** Ошибки по полям формы: `{ email: ['уже занят'] }`. */
3570
- type ItdFieldErrors = Record<string, string[]>;
3571
- /**
3572
- * Базовый класс всех ошибок библиотеки.
3573
- *
3574
- * Ловить его имеет смысл, чтобы отделить проблемы обращения к итд.com от прочих исключений.
3575
- * Для разбора конкретной причины используйте {@link isItdApiError} и поле {@link ItdApiError.code}.
3576
- *
3577
- * @example
3578
- * ```ts
3579
- * try {
3580
- * await itd.posts.like(id);
3581
- * } catch (e) {
3582
- * if (isItdError(e)) console.error('итд.com:', e.message);
3583
- * else throw e;
3584
- * }
3585
- * ```
3586
- */
3587
- declare class ItdError extends Error {
3588
- /** @internal */
3589
- readonly [ITD_ERROR]: true;
3590
- /** Категория ошибки. */
3591
- readonly kind: ItdErrorKind;
3592
- constructor(kind: ItdErrorKind, message: string, options?: {
3593
- cause?: unknown;
3594
- });
3595
- }
3596
- /** Параметры конструктора {@link ItdApiError}. */
3597
- interface ItdApiErrorInit {
3598
- /** HTTP-статус ответа. */
3599
- status: number;
3600
- /** Строковый код ошибки из тела ответа. */
3601
- code: ItdErrorCode;
3602
- /** Человекочитаемое сообщение. */
3603
- message: string;
3604
- /** Расширенное описание, если сервер его прислал. */
3605
- detail?: string | undefined;
3606
- /** Заголовок ошибки, если сервер его прислал. */
3607
- title?: string | undefined;
3608
- /** Ошибки по конкретным полям (сведены из `errors` и `violations`). */
3609
- fieldErrors?: ItdFieldErrors | undefined;
3610
- /** Идентификатор запроса из заголовков ответа, если есть. */
3611
- requestId?: string | undefined;
3612
- /** HTTP-метод запроса. */
3613
- method: string;
3614
- /** Путь запроса без базового URL. */
3615
- path: string;
3616
- /** Тело ответа как оно пришло — на случай, если документация разошлась с реальностью. */
3617
- raw: unknown;
3618
- /** Сам объект ответа. Тело уже прочитано. */
3619
- response?: Response | undefined;
3620
- /** Значение `Retry-After` в миллисекундах, если заголовок был. */
3621
- retryAfter?: number | undefined;
3622
- /** Сколько запросов разрешено в окне (`x-ratelimit-limit`). */
3623
- rateLimit?: number | undefined;
3624
- /** Сколько запросов осталось в окне (`x-ratelimit-remaining`). */
3625
- rateLimitRemaining?: number | undefined;
3626
- }
3627
- /**
3628
- * Ошибка, возвращённая сервером итд.com (HTTP-статус ≥ 400).
3629
- *
3630
- * API отдаёт ошибки в двух разных формах — `{ error: { … } }` и `{ code, message, violations }`.
3631
- * Библиотека сводит обе к этому классу, поэтому разбирать форму ответа вручную не нужно.
3632
- *
3633
- * @example
3634
- * ```ts
3635
- * try {
3636
- * await itd.users.updateMe({ username: 'занятое_имя' });
3637
- * } catch (e) {
3638
- * if (e instanceof ItdValidationError) {
3639
- * console.log(e.fieldErrors.username); // ['Имя уже занято']
3640
- * }
3641
- * }
3642
- * ```
3643
- */
3644
- declare class ItdApiError extends ItdError {
3645
- /**
3646
- * Разновидность ошибки: та же информация, что и класс, но пригодная для сравнения.
3647
- *
3648
- * Позволяет разбирать ошибку через `switch`, а проверкам вроде {@link isItdAuthError} —
3649
- * работать даже когда в проекте оказались две копии библиотеки.
3650
- */
3651
- readonly apiKind: ItdApiErrorKind;
3652
- /** HTTP-статус ответа. */
3653
- readonly status: number;
3654
- /** Строковый код ошибки, например `VALIDATION_ERROR`. */
3655
- readonly code: ItdErrorCode;
3656
- /** Расширенное описание, если сервер его прислал. */
3657
- readonly detail: string | undefined;
3658
- /** Заголовок ошибки, если сервер его прислал. */
3659
- readonly title: string | undefined;
3660
- /** Ошибки по полям. Пустой объект, если сервер их не прислал. */
3661
- readonly fieldErrors: ItdFieldErrors;
3662
- /** Идентификатор запроса из заголовков ответа. */
3663
- readonly requestId: string | undefined;
3664
- /** HTTP-метод запроса. */
3665
- readonly method: string;
3666
- /** Путь запроса без базового URL. */
3667
- readonly path: string;
3668
- /** Тело ответа как оно пришло. */
3669
- readonly raw: unknown;
3670
- /** Объект ответа. Тело уже прочитано и повторно прочитано быть не может. */
3671
- readonly response: Response | undefined;
3672
- /** Пауза из заголовка `Retry-After` в миллисекундах. Сервер итд.com его не присылает. */
3673
- readonly retryAfter: number | undefined;
3674
- /**
3675
- * Сколько запросов разрешено в окне — заголовок `x-ratelimit-limit`.
3676
- *
3677
- * Времени сброса окна сервер не сообщает, поэтому точный момент повтора неизвестен.
3678
- */
3679
- readonly rateLimit: number | undefined;
3680
- /** Сколько запросов осталось в окне — заголовок `x-ratelimit-remaining`. */
3681
- readonly rateLimitRemaining: number | undefined;
3682
- /**
3683
- * @param apiKind разновидность; подставляется подклассами, снаружи задавать не нужно
3684
- */
3685
- constructor(init: ItdApiErrorInit, apiKind?: ItdApiErrorKind);
3686
- /**
3687
- * Проверяет код ошибки. Удобнее, чем сравнивать строки вручную.
3688
- *
3689
- * @example
3690
- * ```ts
3691
- * if (err.hasCode('OTP_INVALID', 'MISSING_FLOW_TOKEN')) await restartOtpFlow();
3692
- * ```
3693
- */
3694
- hasCode(...codes: ItdErrorCode[]): boolean;
3695
- /** Имеет ли смысл повторить запрос: `429` и серверные ошибки `5xx`. */
3696
- get isRetryable(): boolean;
3697
- }
3698
- /** `400` / `422` — данные не прошли валидацию. Подробности в {@link ItdApiError.fieldErrors}. */
3699
- declare class ItdValidationError extends ItdApiError {
3700
- constructor(init: ItdApiErrorInit);
3701
- }
3702
- /** `401` — токен отсутствует, истёк или отозван. */
3703
- declare class ItdAuthError extends ItdApiError {
3704
- constructor(init: ItdApiErrorInit);
3705
- }
3706
- /** `403` — доступ запрещён либо действие ограничено настройками приватности. */
3707
- declare class ItdForbiddenError extends ItdApiError {
3708
- constructor(init: ItdApiErrorInit);
3709
- }
3710
- /** `404` — сущность не найдена. */
3711
- declare class ItdNotFoundError extends ItdApiError {
3712
- constructor(init: ItdApiErrorInit);
3713
- }
3714
- /** `409` — сущность уже существует. */
3715
- declare class ItdConflictError extends ItdApiError {
3716
- constructor(init: ItdApiErrorInit);
3717
- }
3718
- /**
3719
- * `429` — превышен лимит запросов.
3720
- *
3721
- * Если сервер прислал `Retry-After`, пауза доступна в {@link ItdApiError.retryAfter}
3722
- * (в миллисекундах). При включённых ретраях библиотека выдерживает её автоматически.
3723
- */
3724
- declare class ItdRateLimitError extends ItdApiError {
3725
- constructor(init: ItdApiErrorInit);
3726
- }
3727
- /**
3728
- * Действие требует подтверждённого телефона (`PHONE_VERIFICATION_REQUIRED`).
3729
- *
3730
- * Подтверждение проходит через Telegram-бота: ссылка лежит в {@link verificationUrl}.
3731
- */
3732
- declare class ItdPhoneVerificationError extends ItdApiError {
3733
- /** Ссылка на бота подтверждения, если удалось определить идентификатор пользователя. */
3734
- readonly verificationUrl: string | undefined;
3735
- constructor(init: ItdApiErrorInit & {
3736
- userId?: string | undefined;
3737
- });
3738
- }
3739
- /** `5xx` — ошибка на стороне сервера. */
3740
- declare class ItdServerError extends ItdApiError {
3741
- constructor(init: ItdApiErrorInit);
3742
- }
3743
- /** Запрос не дошёл до сервера: DNS, обрыв соединения, отсутствие сети. */
3744
- declare class ItdNetworkError extends ItdError {
3745
- /** HTTP-метод запроса. */
3746
- readonly method: string;
3747
- /** Путь запроса без базового URL. */
3748
- readonly path: string;
3749
- constructor(message: string, init: {
3750
- method: string;
3751
- path: string;
3752
- cause?: unknown;
3753
- });
3754
- }
3755
- /** Истёк таймаут запроса, заданный опцией `timeout`. */
3756
- declare class ItdTimeoutError extends ItdError {
3757
- /** Значение таймаута в миллисекундах. */
3758
- readonly timeout: number;
3759
- /** HTTP-метод запроса. */
3760
- readonly method: string;
3761
- /** Путь запроса без базового URL. */
3762
- readonly path: string;
3763
- constructor(init: {
3764
- timeout: number;
3765
- method: string;
3766
- path: string;
3767
- });
3768
- }
3769
- /** Запрос отменён через переданный `AbortSignal`. */
3770
- declare class ItdAbortError extends ItdError {
3771
- constructor(message?: string);
3772
- }
3773
- /**
3774
- * Некорректная конфигурация или аргументы — обнаружено до обращения к сети.
3775
- *
3776
- * Этим же классом сообщают о нарушенных инвариантах билдеры: например, опрос
3777
- * с одним вариантом ответа.
3778
- */
3779
- declare class ItdConfigError extends ItdError {
3780
- constructor(message: string);
3781
- }
3782
- /** Любая ошибка, порождённая этой библиотекой. */
3783
- declare function isItdError(value: unknown): value is ItdError;
3784
- /** Ошибка, пришедшая от сервера итд.com (статус ≥ 400). */
3785
- declare function isItdApiError(value: unknown): value is ItdApiError;
3786
- /** Ошибка валидации: `VALIDATION_ERROR` либо статус `400`/`422`. */
3787
- declare function isItdValidationError(value: unknown): value is ItdValidationError;
3788
- /** Ошибка авторизации: истёкший или отозванный токен. */
3789
- declare function isItdAuthError(value: unknown): value is ItdAuthError;
3790
- /** Доступ запрещён либо действие ограничено настройками приватности. */
3791
- declare function isItdForbiddenError(value: unknown): value is ItdForbiddenError;
3792
- /** Сущность не найдена. */
3793
- declare function isItdNotFoundError(value: unknown): value is ItdNotFoundError;
3794
- /** Сущность уже существует. */
3795
- declare function isItdConflictError(value: unknown): value is ItdConflictError;
3796
- /** Превышен лимит запросов. */
3797
- declare function isItdRateLimitError(value: unknown): value is ItdRateLimitError;
3798
- /** Действие требует подтверждённого телефона. Ссылка — в `verificationUrl`. */
3799
- declare function isItdPhoneVerificationError(value: unknown): value is ItdPhoneVerificationError;
3800
- /** Ошибка на стороне сервера (`5xx`). */
3801
- declare function isItdServerError(value: unknown): value is ItdServerError;
3802
-
3803
- /** Изображения, которые принимает `POST /api/files/upload`. */
3804
- declare const IMAGE_MIME_TYPES: readonly ["image/jpeg", "image/png", "image/gif", "image/webp", "image/avif", "image/heic", "image/heif"];
3805
- type ImageMimeType = (typeof IMAGE_MIME_TYPES)[number];
3806
- /** Видео, которые принимает `POST /api/files/upload`. */
3807
- declare const VIDEO_MIME_TYPES: readonly ["video/mp4", "video/webm", "video/quicktime"];
3808
- type VideoMimeType = (typeof VIDEO_MIME_TYPES)[number];
3809
- /** Аудио для голосовых комментариев. */
3810
- declare const AUDIO_MIME_TYPES: readonly ["audio/ogg"];
3811
- type AudioMimeType = (typeof AUDIO_MIME_TYPES)[number];
3812
- /** Все типы, которые принимает загрузка. */
3813
- declare const ALLOWED_MIME_TYPES: readonly ["image/jpeg", "image/png", "image/gif", "image/webp", "image/avif", "image/heic", "image/heif", "video/mp4", "video/webm", "video/quicktime", "audio/ogg"];
3814
- type AllowedMimeType = (typeof ALLOWED_MIME_TYPES)[number];
3815
-
3816
- /**
3817
- * Собирает текст уведомления на русском.
3818
- *
3819
- * Повторяет формулировки сайта итд.com. Для неизвестного типа возвращает
3820
- * «Новое уведомление» — библиотека не выдумывает текст, которого нет.
3821
- *
3822
- * @example
3823
- * ```ts
3824
- * formatNotificationText(notification);
3825
- * // 'Аня и ещё 2 оценили ваш пост'
3826
- * ```
3827
- */
3828
- declare function formatNotificationText(notification: Notification): string;
3829
-
3830
- /**
3831
- * Соответствие коротких имён типов уведомлений развёрнутым.
3832
- *
3833
- * Сервер — и в списке, и в потоке событий — присылает короткие имена: `like`, `comment`,
3834
- * `reply`, `repost`, `comment_like`. Развёрнутые (`post_reaction`, `post_comment`)
3835
- * встречаются в оформлении интерфейса, поэтому библиотека приводит типы к ним:
3836
- * они однозначно называют и объект, и действие.
3837
- *
3838
- * Пришедшее значение всегда остаётся в поле `rawType`.
3839
- */
3840
- declare const NOTIFICATION_TYPE_ALIASES: Readonly<Record<string, NotificationType>>;
3841
- /**
3842
- * Приводит имя типа к каноническому.
3843
- *
3844
- * Неизвестное значение возвращается без изменений. Официальный клиент в этом случае
3845
- * подставляет `follow`, из-за чего новое уведомление выглядит как подписка, — здесь
3846
- * такого не происходит.
3847
- *
3848
- * @example
3849
- * ```ts
3850
- * canonicalNotificationType('like'); // 'post_reaction'
3851
- * canonicalNotificationType('post_reaction'); // 'post_reaction'
3852
- * canonicalNotificationType('новое_событие'); // 'новое_событие'
3853
- * ```
3854
- */
3855
- declare function canonicalNotificationType(rawType: string): NotificationType;
3856
- /**
3857
- * Известен ли библиотеке этот тип уведомления.
3858
- *
3859
- * Полезно, чтобы решить, показывать ли уведомление, для которого нет своего оформления.
3860
- */
3861
- declare function isKnownNotificationType(type: string): boolean;
3862
-
3863
- /**
3864
- * Вычисляет адрес, на который ведёт уведомление.
3865
- *
3866
- * Возвращает путь внутри сайта — без домена, чтобы его можно было передать роутеру
3867
- * приложения. Поле `clickUrl` от сервера используется только как запасной вариант:
3868
- * вычисленный путь точнее, поскольку учитывает родительский пост у комментариев.
3869
- *
3870
- * @example
3871
- * ```ts
3872
- * const url = resolveNotificationUrl(notification);
3873
- * // '/@durov/post/9f1c…?comment=2b7e…'
3874
- * ```
3875
- */
3876
- declare function resolveNotificationUrl(notification: Notification): string;
3877
-
3878
- /** Настройки опроса. */
3879
- interface PollTransportOptions {
3880
- /** Как часто опрашивать сервер, мс. По умолчанию 15 000. */
3881
- interval?: number;
3882
- /** Сколько уведомлений запрашивать за раз. По умолчанию 20. */
3883
- limit?: number;
3884
- }
3885
-
3886
- /** Путь потока уведомлений. */
3887
- declare const STREAM_PATH = "/api/notifications/stream";
3888
- /** Настройки SSE-транспорта. */
3889
- interface SseTransportOptions {
3890
- /**
3891
- * Сколько миллисекунд ждать данных, прежде чем считать соединение мёртвым.
3892
- *
3893
- * Сервер не присылает keep-alive, а оборванное TCP-соединение может не закрыться
3894
- * само — без этой проверки поток «тихо умирает» и новых уведомлений не приходит.
3895
- * По умолчанию 90 000. `0` отключает проверку.
3896
- */
3897
- idleTimeout?: number;
3898
- }
3899
-
3900
- export { HashtagsResource as $, ALLOWED_MIME_TYPES as A, type BuilderInput as B, type CaptchaCredentials as C, type CreateCommentInput as D, type CreatePollInput as E, type FileReader as F, type CreatePostInput as G, type CreateReportInput as H, type ItdSession as I, type Credentials as J, type CredentialsAuth as K, DEFAULT_BASE_URL as L, DEFAULT_TIMEOUT as M, DEFAULT_USER_AGENT as N, DEVICE_ID_HEADER as O, DetectedRuntime as P, type DwellEntry as Q, type ErrorContextHook as R, type FeedParams as S, type TokenStorage as T, FeedTab as U, type FileInput as V, FilesResource as W, type FollowResult as X, type ForgotPasswordInput as Y, type Hashtag as Z, type HashtagPostsParams as _, ItdClient as a, type PostInput as a$, IMAGE_MIME_TYPES as a0, type ImageMimeType as a1, type InteractionEntry as a2, InteractionType as a3, type IsoDate as a4, ItdAbortError as a5, ItdApiError as a6, type ItdApiErrorInit as a7, ItdApiErrorKind as a8, ItdAuthError as a9, type MyProfile as aA, NOTIFICATION_TYPE_ALIASES as aB, type Notification as aC, type NotificationEvent as aD, type NotificationListParams as aE, type NotificationSettings as aF, NotificationType as aG, NotificationsResource as aH, OAuthProvider as aI, type Page as aJ, type PageState as aK, PaginationMode as aL, Paginator as aM, type PaymentMethod as aN, type Pin as aO, type PinPostResult as aP, type PinsResult as aQ, PlatformResource as aR, type PluginContext as aS, type Poll as aT, PollBuilder as aU, type PollInput as aV, type PollOption as aW, type PollTransportOptions as aX, type Portal as aY, type Post as aZ, PostBuilder as a_, type ItdBuilder as aa, ItdConfigError as ab, ItdConflictError as ac, ItdError as ad, ItdErrorCode as ae, ItdErrorKind as af, type ItdFieldErrors as ag, ItdForbiddenError as ah, ItdNetworkError as ai, ItdNotFoundError as aj, ItdPhoneVerificationError as ak, type ItdPlugin as al, ItdRateLimitError as am, ItdRealtime as an, ItdServerError as ao, ItdTimeoutError as ap, ItdValidationError as aq, LIBRARY_VERSION as ar, type LikeResult as as, LikesVisibility as at, type Listener as au, LocalStorageTokenStorage as av, type Logger as aw, type Loose as ax, MAX_RECONNECT_ATTEMPTS as ay, MemoryTokenStorage as az, type ItdClientOptions as b, VerificationResource as b$, type PostStats as b0, PostsResource as b1, type PrivacySettings as b2, type Profile as b3, type PublicProfile as b4, RECONNECT_BACKOFF as b5, RECONNECT_JITTER as b6, REFRESH_COOKIE as b7, REFRESH_COOKIE_PATH as b8, type RateLimitOptions as b9, SignInStatus as bA, type Span as bB, SpanType as bC, type SseTransportOptions as bD, type Subscription as bE, SubscriptionResource as bF, type SubscriptionState as bG, TURNSTILE_SITE_KEY as bH, type TelemetryOptions as bI, TelemetryResource as bJ, type Transformer as bK, type TransportContext as bL, type TransportEvent as bM, UnauthorizedStreamError as bN, type Unsubscribe as bO, type UpdateNotificationSettingsInput as bP, type UpdatePrivacyInput as bQ, type UpdateProfileInput as bR, type UploadOptions as bS, type UploadedFile as bT, type UserId as bU, type UserListParams as bV, type UserPostsParams as bW, type UserRef as bX, type UserSummary as bY, UsersResource as bZ, VIDEO_MIME_TYPES as b_, type RawRequestOptions as ba, type RealtimeEvents as bb, type RealtimeOptions as bc, RealtimeStatus as bd, type RealtimeTransport as be, RealtimeTransportKind as bf, type ReconnectOptions as bg, type RepliesParams as bh, type Report as bi, ReportBuilder as bj, type ReportInput as bk, ReportReason as bl, ReportTargetType as bm, ReportsResource as bn, type RequestContext as bo, type RequestOptions as bp, type ResetPasswordInput as bq, type ResponseContext as br, type RetryContext as bs, type RetryOptions as bt, RuntimeMode as bu, STREAM_PATH as bv, SearchResource as bw, type SearchResult as bx, type Session as by, type SignInResult as bz, AUDIO_MIME_TYPES as c, type VerificationStatus as c0, type VideoMimeType as c1, ViewReason as c2, ViewSource as c3, WallAccess as c4, canonicalNotificationType as c5, comment as c6, createTokenStorage as c7, formatNotificationText as c8, isBuilder as c9, isItdApiError as ca, isItdAuthError as cb, isItdConflictError as cc, isItdError as cd, isItdForbiddenError as ce, isItdNotFoundError as cf, isItdPhoneVerificationError as cg, isItdRateLimitError as ch, isItdServerError as ci, isItdValidationError as cj, isKnownNotificationType as ck, isMyProfile as cl, normalizeNotification as cm, poll as cn, post as co, readNotificationEvent as cp, readUnreadCountEvent as cq, report as cr, resolveNotificationUrl as cs, toDate as ct, createClient as cu, AUTH_FLAG_COOKIE as d, AUTH_PATHS as e, AccessType as f, type Actor as g, type AllowedMimeType as h, type Announcement as i, type AnnouncementButton as j, type Attachment as k, AttachmentType as l, type AudioMimeType as m, type AuthInput as n, AuthResource as o, type Author as p, type ChangelogEntry as q, type Clan as r, type ClientHooks as s, type Comment as t, CommentBuilder as u, type CommentInput as v, type CommentReplyTo as w, CommentSort as x, type CommentsParams as y, CommentsResource as z };