itd-api 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +6 -4
  2. package/dist/index.cjs +3030 -1579
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +950 -568
  5. package/dist/index.d.ts +950 -568
  6. package/dist/index.js +2961 -1503
  7. package/dist/index.js.map +1 -1
  8. package/dist/{multi-storage-D4h6QA-w.js → multi-storage-BUZaLAPO.js} +132 -84
  9. package/dist/multi-storage-BUZaLAPO.js.map +1 -0
  10. package/dist/{multi-storage-DwaIh24p.d.ts → multi-storage-BUoEoW8f.d.cts} +13 -43
  11. package/dist/{multi-storage-C8R5Crwo.cjs → multi-storage-Bf84xiO8.cjs} +169 -91
  12. package/dist/multi-storage-Bf84xiO8.cjs.map +1 -0
  13. package/dist/{multi-storage-DP_ujJw1.d.cts → multi-storage-CQSlI_kn.d.ts} +13 -43
  14. package/dist/node.cjs +62 -53
  15. package/dist/node.cjs.map +1 -1
  16. package/dist/node.d.cts +19 -5
  17. package/dist/node.d.ts +19 -5
  18. package/dist/node.js +62 -54
  19. package/dist/node.js.map +1 -1
  20. package/dist/storage-BqMxs76Y.d.cts +152 -0
  21. package/dist/storage-BqMxs76Y.d.ts +152 -0
  22. package/dist/{storage-ycBqLBRB.cjs → storage-DnzZPS_9.cjs} +263 -17
  23. package/dist/storage-DnzZPS_9.cjs.map +1 -0
  24. package/dist/{storage-D9tfHx7Z.js → storage-IHdXw52v.js} +210 -18
  25. package/dist/storage-IHdXw52v.js.map +1 -0
  26. package/dist/web.cjs +143 -40
  27. package/dist/web.cjs.map +1 -1
  28. package/dist/web.d.cts +40 -3
  29. package/dist/web.d.ts +40 -3
  30. package/dist/web.js +141 -41
  31. package/dist/web.js.map +1 -1
  32. package/package.json +3 -3
  33. package/dist/multi-storage-C8R5Crwo.cjs.map +0 -1
  34. package/dist/multi-storage-D4h6QA-w.js.map +0 -1
  35. package/dist/runtime-CFEsf-jD.cjs +0 -185
  36. package/dist/runtime-CFEsf-jD.cjs.map +0 -1
  37. package/dist/runtime-DHxDn8gf.js +0 -126
  38. package/dist/runtime-DHxDn8gf.js.map +0 -1
  39. package/dist/storage-BjNRlkbE.d.cts +0 -82
  40. package/dist/storage-BjNRlkbE.d.ts +0 -82
  41. package/dist/storage-D9tfHx7Z.js.map +0 -1
  42. package/dist/storage-ycBqLBRB.cjs.map +0 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#listeners","#onError","#config","#send","#jar","#emitter","#hooks","#authScope","#loadSession","#identityForToken","#readExternalToken","#session","#sessionFromConfig","#externalIdentity","#authEpoch","#externalToken","#rotateAuthScope","#hasRefreshSession","#deviceId","#deviceIdLoading","#resolveDeviceId","#saveSession","#signInWithCredentials","#refreshDeduplicated","#noRefreshSessionError","#invalidateInFlight","#transitionAuth","#seedRefreshCookie","#loading","#performLoad","#refreshing","#performRefresh","#reloginOrNull","#signingIn","#performSignIn","#resolveTurnstileToken","isRecord","#handler","#plugins","#baseUrl","#entries","#optionKeys","#removing","#cleanups","#ordered","#rebuildOptionKeys","#trackCleanup","#waitForDrain","#runHook","#concurrency","#minGap","#waiting","#clock","#active","#drain","#cancelTimer","#nextSlot","#options","#main","#byService","#services","#primaryHost","ItdApiErrorClass","#config","#deps","#buildHeaders","#prepareBody","#toTransportError","#readBodyOrFail","#options","#hooks","#middleware","#handlers","#queue","#activeKeys","#drainWaiters","#keysFor","#pump","#resolveDrain","#active","#run","#interval","#limit","#clock","#readCount","#wait","#idleTimeout","#handshakeTimeout","#clock","#lastEventId","#handshake","#read","#deps","#options","#emitter","#dispatcher","#transport","#maxAttempts","#createTransport","#deliver","#reportDispatchError","#status","#wanted","#attachEnvironmentListeners","#dispatch","#run","#cancelTimer","#detachEnvironment","#controller","#attempt","#setStatus","#handleEvent","#handleFailure","#refreshAndReconnect","#scheduleReconnect","#giveUp","#options","#maxPages","#state","#finished","#pagesLoaded","#advance","#auth","#state","#uploadFiles","#replies","#fetch","#createBodyFactory","#isStreamInput","#prepareStream","#prepareBuffer","#resolveBuffer","#nameFromMime","#posts","#list","#content","#spans","#formatted","#state","#state","#input","#uploadFiles","#feed","#wall","#liked","#comments","#collectAttachments","#state","#clock","#input","#submit","#result","#resource","#options","#maxBatchSize","#onClose","#dwell","#interactions","#state","#assertOpen","#flushPromise","#flushAll","#closePromise","#batches","#sessionId","#uploadFile","#userList","#userPage","#userPaginator","#config","#http","#transport","#authManager","#jar","#queues","#ownsQueues","#plugins","#services","#streams","#throttleByHeaders","#disconnectStreams","#base","#storage","#clients","#removing","#storageControls","#eventUnsubscribers","#plugins","#removingPlugins","#queues","#rateLimitScope","#emitter","#logger","#mergeOptions","#reportPluginCleanup","#forwardEvents","#selector","#routes","#fallback","#registration","#captureMiddleware"],"sources":["../src/core/emitter.ts","../src/core/jwt.ts","../src/core/auth.ts","../src/core/clock.ts","../src/core/url.ts","../src/core/version.ts","../src/core/config.ts","../src/core/http.ts","../src/core/pipeline.ts","../src/core/plugins/hooks.ts","../src/core/retry.ts","../src/core/middleware.ts","../src/core/plugins/order.ts","../src/core/plugins/registry.ts","../src/core/rate-limit.ts","../src/core/services.ts","../src/core/redact.ts","../src/core/unwrap.ts","../src/core/error-factory.ts","../src/core/transport.ts","../src/types/enums.ts","../src/realtime/middleware.ts","../src/realtime/transport.ts","../src/realtime/poll.ts","../src/realtime/reconnect.ts","../node_modules/eventsource-parser/dist/index.js","../src/realtime/sse.ts","../src/notifications/type-map.ts","../src/notifications/normalize.ts","../src/realtime/updates.ts","../src/realtime/stream.ts","../src/core/pagination.ts","../src/types/options.ts","../src/resources/base.ts","../src/resources/auth.ts","../src/builders/base.ts","../src/builders/comment.ts","../src/resources/comments.ts","../src/core/attachments/url-source.ts","../src/core/mime.ts","../src/core/multipart.ts","../src/resources/files.ts","../src/resources/hashtags.ts","../src/resources/notifications.ts","../src/core/time.ts","../src/resources/platform.ts","../src/spans/validate.ts","../src/spans/parse.ts","../src/builders/markup.ts","../src/builders/poll.ts","../src/builders/post.ts","../src/resources/posts.ts","../src/builders/report.ts","../src/resources/reports.ts","../src/resources/search.ts","../src/resources/subscription.ts","../src/resources/telemetry.ts","../src/resources/users.ts","../src/resources/verification.ts","../src/client.ts","../src/accounts.ts","../src/core/attachments/factories.ts","../src/models/guards.ts","../src/models/status-helpers.ts","../src/notifications/text.ts","../src/notifications/url.ts","../src/realtime/router.ts","../src/spans/render.ts"],"sourcesContent":["import type { Logger } from '../types/options.js';\n\n/** Обработчик события. */\nexport type Listener<T> = (payload: T) => void;\n\n/** Функция отписки, которую возвращает подписка на событие. */\nexport type Unsubscribe = () => void;\n\n/**\n * Минимальный типизированный источник событий.\n *\n * Своя реализация вместо `EventTarget` и `EventEmitter`: первый есть не везде и требует\n * обёрток `CustomEvent`, второй существует только в Node. Нужны ровно подписка и рассылка.\n *\n * Исключение в обработчике не прерывает рассылку остальным и не роняет библиотеку.\n *\n * @typeParam Events карта «имя события → тип полезной нагрузки». Задаётся интерфейсом,\n * поэтому ограничение на индексную сигнатуру намеренно не накладывается.\n */\nexport class Emitter<Events> {\n readonly #listeners = new Map<keyof Events, Set<Listener<never>>>();\n readonly #onError: ((error: unknown) => void) | undefined;\n\n constructor(onListenerError?: (error: unknown) => void) {\n this.#onError = onListenerError;\n }\n\n /**\n * Подписывается на событие.\n *\n * @returns функция отписки\n *\n * @example\n * ```ts\n * const off = realtime.on('notification', (event) => console.log(event));\n * off();\n * ```\n */\n on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): Unsubscribe {\n const set = this.#listeners.get(event) ?? new Set();\n set.add(listener as Listener<never>);\n this.#listeners.set(event, set);\n\n return () => this.off(event, listener);\n }\n\n /** Подписывается на одно срабатывание. */\n once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): Unsubscribe {\n const off = this.on(event, (payload) => {\n off();\n listener(payload);\n });\n return off;\n }\n\n /** Отписывается от события. */\n off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void {\n this.#listeners.get(event)?.delete(listener as Listener<never>);\n }\n\n /** Рассылает событие подписчикам. */\n emit<K extends keyof Events>(event: K, payload: Events[K]): void {\n const set = this.#listeners.get(event);\n if (!set) return;\n\n // Копия нужна: обработчик вправе отписаться прямо во время рассылки.\n for (const listener of [...set]) {\n try {\n (listener as Listener<Events[K]>)(payload);\n } catch (error) {\n this.#onError?.(error);\n }\n }\n }\n\n /** Сколько подписчиков у события. */\n listenerCount(event: keyof Events): number {\n return this.#listeners.get(event)?.size ?? 0;\n }\n\n /** Снимает все подписки. */\n removeAllListeners(): void {\n this.#listeners.clear();\n }\n}\n\n/**\n * Сообщает об исключении из пользовательского обработчика события: пишет в логгер,\n * при его отсутствии — в консоль.\n *\n * Ошибка чужого обработчика не должна ронять библиотеку, но и пропадать молча ей нельзя:\n * иначе подписка, падающая на каждом событии, выглядит как её отсутствие.\n *\n * @param scope о чьих событиях речь — попадает в текст сообщения\n *\n * @internal\n */\nexport function reportListenerError(\n logger: Logger | undefined,\n scope: string,\n error: unknown,\n): void {\n const message = `Ошибка в обработчике события ${scope}`;\n if (logger) logger.error(message, error);\n else console.error(`[itd-api] ${message}`, error);\n}\n","/**\n * Разбирает строку base64url.\n *\n * `atob` понимает только обычный base64, поэтому алфавит приводится к нему, а недостающее\n * выравнивание добивается. Результат декодируется как UTF-8: в полезной нагрузке токена\n * может оказаться кириллица, и «бинарная» строка из `atob` сломала бы `JSON.parse`.\n */\nfunction decodeBase64Url(segment: string): string {\n const base64 = segment.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');\n\n const binary = atob(padded);\n const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));\n\n return new TextDecoder().decode(bytes);\n}\n\n/** Идентификаторы владельца и сессии, прочитанные из JWT без проверки подписи. */\nexport interface TokenIdentity {\n subject?: string | undefined;\n sessionId?: string | undefined;\n}\n\n/**\n * Читает `sub` и `sid` из полезной нагрузки JWT.\n *\n * Подпись не проверяется — ключа для проверки у клиента нет. Прочитанные значения служат\n * лишь метками для разделения локального состояния и не дают доступа: его определяет сервер.\n */\nexport function readTokenIdentity(token: string): TokenIdentity {\n try {\n const payload = token.split('.')[1];\n if (!payload) return {};\n\n const parsed: unknown = JSON.parse(decodeBase64Url(payload));\n if (typeof parsed !== 'object' || parsed === null) return {};\n\n const { sub, sid } = parsed as { sub?: unknown; sid?: unknown };\n return {\n ...(typeof sub === 'string' && sub.length > 0 ? { subject: sub } : {}),\n ...(typeof sid === 'string' && sid.length > 0 ? { sessionId: sid } : {}),\n };\n } catch {\n // Токен другого формата — не повод падать: поля просто останутся пустыми.\n return {};\n }\n}\n\n/**\n * Читает `sub` из полезной нагрузки JWT.\n *\n * @returns идентификатор владельца токена; `undefined`, если токен не JWT, повреждён\n * или поля `sub` в нём нет\n */\nexport function readTokenSubject(token: string): string | undefined {\n return readTokenIdentity(token).subject;\n}\n","import type { UserId } from '../models/common.js';\nimport type { AuthInput, CredentialsAuth } from '../types/options.js';\nimport type { AuthConfig } from './config.js';\nimport {\n AUTH_FLAG_COOKIE,\n type CookieJar,\n REFRESH_COOKIE,\n REFRESH_COOKIE_PATH,\n} from './cookies.js';\nimport { Emitter, reportListenerError } from './emitter.js';\nimport { ItdApiError, ItdAuthError, ItdConfigError } from './errors.js';\nimport { readTokenIdentity, readTokenSubject } from './jwt.js';\nimport type { RequestHandler } from './pipeline.js';\nimport { createDeviceId } from './runtime.js';\nimport { copySession, type ItdSession } from './storage.js';\n\n/** Пути эндпоинтов авторизации. */\nexport const AUTH_PATHS = {\n signUp: '/api/v1/auth/sign-up',\n signIn: '/api/v1/auth/sign-in',\n verifyOtp: '/api/v1/auth/verify-otp',\n resendOtp: '/api/v1/auth/resend-otp',\n refresh: '/api/v1/auth/refresh',\n logout: '/api/v1/auth/logout',\n forgotPassword: '/api/v1/auth/forgot-password',\n resetPassword: '/api/v1/auth/reset-password',\n changePassword: '/api/v1/auth/change-password',\n sessions: '/api/v1/auth/sessions',\n} as const;\n\n/**\n * Публичный ключ Cloudflare Turnstile платформы итд.com.\n *\n * Нужен, чтобы отрисовать виджет капчи и получить токен для `signIn`, `signUp`\n * и `forgotPassword`.\n *\n * @example\n * ```ts\n * turnstile.render('#captcha', {\n * sitekey: TURNSTILE_SITE_KEY,\n * callback: (turnstileToken) => itd.auth.signIn({ email, password, turnstileToken }),\n * });\n * ```\n */\nexport const TURNSTILE_SITE_KEY = '0x4AAAAAACHhxczw6fJGwPBg';\n\n/** Заголовок с идентификатором устройства. Сервер связывает с ним запись в списке сессий. */\nexport const DEVICE_ID_HEADER = 'X-Device-Id';\n\n/** События слоя авторизации. */\nexport interface AuthEvents {\n /** Токен получен или обновлён. */\n tokens: { accessToken: string };\n /** Выполнен вход. */\n signIn: { accessToken: string };\n /** Сессия очищена — вручную или из-за неудачного обновления. */\n signOut: undefined;\n /** Обновить сессию не удалось; дальнейшие запросы будут падать с 401. */\n authError: { error: unknown };\n}\n\n/** Ответ эндпоинтов, выдающих токен. */\ninterface TokenResponse {\n accessToken?: unknown;\n}\n\nfunction readAccessToken(payload: unknown): string | undefined {\n if (typeof payload !== 'object' || payload === null) return undefined;\n const token = (payload as TokenResponse).accessToken;\n return typeof token === 'string' && token.length > 0 ? token : undefined;\n}\n\n/** Убирает поле, которое сохраняли промежуточные версии с поддержкой `getUserId()`. */\nfunction withoutLegacyUserId(session: ItdSession): ItdSession {\n const clean = { ...session };\n delete (clean as ItdSession & { userId?: unknown }).userId;\n return clean;\n}\n\nlet authScopeSequence = 0;\n\nfunction nextAuthScope(): string {\n authScopeSequence += 1;\n return String(authScopeSequence);\n}\n\n/** Области аккаунта и конкретной сессии для локального состояния плагинов. */\nexport interface AuthIdentity {\n /** Идентификатор пользователя; отсутствует у непрозрачного или повреждённого токена. */\n userId?: UserId | undefined;\n /** Идентификатор серверной сессии; отсутствует у непрозрачного или повреждённого токена. */\n sessionId?: string | undefined;\n}\n\ninterface AuthManagerHooks {\n /** Вызывается синхронно перед заменой владельца авторизации. */\n onAccountChange?: (() => void) | undefined;\n}\n\n/**\n * Хранит сессию и продлевает её.\n *\n * Главное здесь — **дедупликация обновления**. Когда десять параллельных запросов\n * одновременно получают `401`, обновление должно произойти один раз, а остальные обязаны\n * дождаться его результата. Иначе сервер увидит десять параллельных `refresh`, и все,\n * кроме первого, скорее всего получат отказ по уже использованному токену.\n */\nexport class AuthManager {\n readonly #config: AuthConfig;\n readonly #send: RequestHandler;\n readonly #jar: CookieJar;\n readonly #emitter: Emitter<AuthEvents>;\n readonly #hooks: AuthManagerHooks;\n\n /** `undefined` — сессия ещё не читалась из хранилища. */\n #session: ItdSession | null | undefined;\n /**\n * Общий промис чтения сессии из хранилища. Дедупликация: параллельные запросы на холодном\n * старте читают хранилище один раз и не заводят каждый свой `deviceId`.\n */\n #loading: Promise<ItdSession | null> | null = null;\n /** Общий промис обновления: к нему присоединяются все, кто получил 401. */\n #refreshing: Promise<string | null> | null = null;\n /** Общий промис входа по логину и паролю. */\n #signingIn: Promise<string> | null = null;\n /** Fallback-область для изоляции состояния плагинов, когда JWT-идентичность недоступна. */\n #authScope = nextAuthScope();\n /** Последний токен внешнего источника; `undefined` означает, что источник ещё не читался. */\n #externalToken: string | null | undefined;\n /** Идентификаторы последнего токена внешнего источника для синхронных потребителей. */\n #externalIdentity: AuthIdentity | undefined;\n /**\n * Идентификатор устройства.\n *\n * Держится отдельно от сессии намеренно: выход из аккаунта не меняет устройство,\n * поэтому `clear()` его не трогает.\n */\n #deviceId: string | undefined;\n /** Общий промис первичной выдачи `deviceId` — чтобы параллельные запросы получили один. */\n #deviceIdLoading: Promise<string> | null = null;\n /**\n * Счётчик смен владельца авторизации.\n *\n * Растёт при каждой операции, которая заменяет или очищает сессию извне: `clear`,\n * `setSession`, `setAccessToken`, вход. `#performRefresh` снимает его значение перед\n * сетевым запросом и сверяет перед записью — иначе запоздавший ответ обновления мог бы\n * воскресить уже очищенную сессию поверх `signOut`.\n */\n #authEpoch = 0;\n\n constructor(\n config: AuthConfig,\n send: RequestHandler,\n jar: CookieJar,\n hooks: AuthManagerHooks = {},\n ) {\n this.#config = config;\n this.#send = send;\n this.#jar = jar;\n this.#hooks = hooks;\n this.#emitter = new Emitter<AuthEvents>((error) =>\n reportListenerError(config.logger, 'авторизации', error),\n );\n }\n\n /** Подписка на события авторизации. */\n get on(): Emitter<AuthEvents>['on'] {\n return this.#emitter.on.bind(this.#emitter);\n }\n\n /** Подписка на одно срабатывание. */\n get once(): Emitter<AuthEvents>['once'] {\n return this.#emitter.once.bind(this.#emitter);\n }\n\n /**\n * Непрозрачная fallback-область авторизации.\n *\n * Изолирует состояние плагинов, когда идентичность аккаунта из JWT недоступна (непрозрачный\n * токен): значение уникально для владельца и сменяется при замене или очистке сессии.\n */\n getAuthScope(): string {\n return this.#authScope;\n }\n\n /** Загружает сессию и возвращает идентификаторы аккаунта и сессии для плагинов. */\n async getAuthIdentity(): Promise<AuthIdentity> {\n const session = await this.#loadSession();\n if (session?.accessToken) return this.#identityForToken(session.accessToken);\n\n const auth = this.#config.auth;\n if (typeof auth === 'object' && auth !== null && 'getToken' in auth) {\n const token = await this.#readExternalToken(() => auth.getToken());\n return this.#identityForToken(token ?? undefined);\n }\n\n return {};\n }\n\n /** Идентификаторы аккаунта и сессии без чтения хранилища. @internal */\n getCurrentAuthIdentity(): AuthIdentity {\n const session =\n this.#session === undefined ? this.#sessionFromConfig(this.#config.auth) : this.#session;\n return session?.accessToken\n ? this.#identityForToken(session.accessToken)\n : (this.#externalIdentity ?? {});\n }\n\n #rotateAuthScope(): void {\n this.#authScope = nextAuthScope();\n }\n\n /** Отмечает смену владельца авторизации — обесценивает результат идущего обновления. */\n #invalidateInFlight(): void {\n this.#authEpoch += 1;\n }\n\n #identityForToken(accessToken: string | undefined): AuthIdentity {\n const token = accessToken ? readTokenIdentity(accessToken) : {};\n return {\n ...(token.subject ? { userId: token.subject as UserId } : {}),\n ...(token.sessionId ? { sessionId: token.sessionId } : {}),\n };\n }\n\n async #readExternalToken(\n getToken: () => string | null | Promise<string | null>,\n ): Promise<string | null> {\n const token = (await getToken()) ?? null;\n const current = this.#identityForToken(token ?? undefined);\n\n if (this.#externalToken !== undefined && this.#externalToken !== token) {\n this.#rotateAuthScope();\n const previous = this.#externalIdentity ?? {};\n const changed =\n previous.userId !== undefined && current.userId !== undefined\n ? previous.userId !== current.userId\n : true;\n if (changed) this.#hooks.onAccountChange?.();\n }\n\n this.#externalToken = token;\n this.#externalIdentity = current;\n return token;\n }\n\n /** Меняет fallback и завершает realtime, только если фактически сменился аккаунт. */\n #transitionAuth(accessToken: string | undefined, rotateFallback = true): void {\n const knownPrevious = this.#session !== undefined;\n const previous = this.#identityForToken(this.#session?.accessToken);\n const current = this.#identityForToken(accessToken);\n const changed =\n previous.userId !== undefined && current.userId !== undefined\n ? previous.userId !== current.userId\n : previous.userId !== current.userId || rotateFallback;\n\n if (rotateFallback || changed) this.#rotateAuthScope();\n if (knownPrevious && changed) this.#hooks.onAccountChange?.();\n }\n\n /**\n * Есть ли признак живой refresh-сессии.\n *\n * Рядом с refresh-токеном сервер ставит незакрытую cookie `is_auth` — по ней видно,\n * что продлевать сессию вообще есть смысл, и API не дёргается у анонимов.\n * В браузере cookie ведёт сама среда, поэтому там ответ всегда `true`.\n *\n * Асинхронный, потому что признак может лежать в {@link TokenStorage}: до чтения оттуда\n * ответ был бы `false` даже при полностью рабочей сохранённой сессии.\n */\n async hasRefreshSession(): Promise<boolean> {\n await this.#loadSession();\n return this.#hasRefreshSession();\n }\n\n /** То же самое, но без чтения хранилища — для вызовов, где сессия уже загружена. */\n #hasRefreshSession(): boolean {\n if (!this.#config.useCookieJar) return true;\n // Флаг должен принадлежать основному API, а не другому сервису.\n if (this.#jar.has(AUTH_FLAG_COOKIE, this.#config.baseUrl)) return true;\n\n // Явно переданный refresh-токен — тоже основание пробовать.\n return Boolean(this.#session?.refreshToken);\n }\n\n /** Заголовки авторизации для очередного запроса. Пустой объект, если токена нет. */\n async getAuthHeaders(): Promise<Record<string, string>> {\n const token = await this.getAccessToken();\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n\n /**\n * Идентификатор устройства для заголовка `X-Device-Id`.\n *\n * Заводится один раз и сохраняется в сессии, чтобы пережить перезапуск процесса:\n * сервер связывает с ним запись в списке сессий, и плавающее значение плодило бы\n * по новой сессии на каждый старт.\n */\n getDeviceId(): Promise<string> {\n if (this.#deviceId) return Promise.resolve(this.#deviceId);\n\n // Дедупликация: параллельные вызовы на холодном клиенте получают один `X-Device-Id`.\n this.#deviceIdLoading ??= this.#resolveDeviceId().finally(() => {\n this.#deviceIdLoading = null;\n });\n\n return this.#deviceIdLoading;\n }\n\n async #resolveDeviceId(): Promise<string> {\n const session = await this.#loadSession();\n const deviceId = this.#config.deviceId ?? session?.deviceId ?? createDeviceId();\n\n this.#deviceId = deviceId;\n\n // Записываем, только если значение действительно новое, — иначе каждый первый запрос\n // дёргал бы хранилище без всякой пользы.\n if (session?.deviceId !== deviceId) {\n await this.#saveSession({ ...(session ?? {}), deviceId });\n }\n\n return deviceId;\n }\n\n /**\n * Текущий токен доступа.\n *\n * При необходимости выполняет отложенный вход: если в конфигурации переданы логин\n * и пароль, первый же запрос сам заведёт сессию.\n */\n async getAccessToken(): Promise<string | null> {\n const session = await this.#loadSession();\n if (session?.accessToken) return session.accessToken;\n\n const auth = this.#config.auth;\n if (!auth) return null;\n\n // Внешний источник токена спрашиваем каждый раз: он сам решает, когда обновлять.\n if (typeof auth === 'object' && 'getToken' in auth) {\n return this.#readExternalToken(() => auth.getToken());\n }\n\n if (typeof auth === 'object' && 'email' in auth) {\n return this.#signInWithCredentials(auth);\n }\n\n return null;\n }\n\n /**\n * Реакция транспорта на ответ `401`.\n *\n * @returns `true`, если токен обновлён и запрос имеет смысл повторить\n */\n async onUnauthorized(): Promise<boolean> {\n try {\n const token = await this.#refreshDeduplicated();\n if (token !== null) return true;\n\n // Обновлять нечем — это тоже отказ авторизации, и подписчик должен о нём узнать.\n this.#emitter.emit('authError', { error: this.#noRefreshSessionError() });\n return false;\n } catch (error) {\n this.#emitter.emit('authError', { error });\n return false;\n }\n }\n\n /**\n * Ошибка «сессию продлить нечем».\n *\n * Возникает, только когда обновление даже не начиналось: нет ни cookie `is_auth`,\n * ни refresh-токена. Если сервер ответил отказом, наружу уходит **его** ошибка —\n * подменять её этой значило бы прятать причину (`REFRESH_TOKEN_MISSING`,\n * `SESSION_NOT_FOUND`, `SESSION_REVOKED` — разные поводы и разные действия).\n */\n #noRefreshSessionError(): ItdAuthError {\n return new ItdAuthError({\n status: 401,\n code: 'SESSION_EXPIRED',\n message:\n 'Не удалось обновить сессию: нет ни cookie is_auth, ни refresh-токена. ' +\n 'Войдите заново либо передайте refreshToken в auth.',\n method: 'POST',\n path: AUTH_PATHS.refresh,\n raw: undefined,\n });\n }\n\n /**\n * Обновляет токен доступа.\n *\n * Параллельные вызовы объединяются в один сетевой запрос.\n *\n * @throws {ItdAuthError} если обновить сессию не удалось\n */\n async refresh(): Promise<string> {\n const token = await this.#refreshDeduplicated();\n if (token === null) throw this.#noRefreshSessionError();\n return token;\n }\n\n /** Сохраняет токен, полученный извне, — например после подтверждения OTP. */\n async setAccessToken(accessToken: string): Promise<void> {\n await this.#loadSession();\n this.#invalidateInFlight();\n this.#transitionAuth(accessToken);\n await this.#saveSession({\n ...(this.#session ?? {}),\n accessToken,\n obtainedAt: this.#config.clock.now(),\n });\n this.#emitter.emit('tokens', { accessToken });\n }\n\n /** Текущая сессия целиком. Полезно, чтобы сохранить её самому. */\n async getSession(): Promise<ItdSession | null> {\n const session = await this.#loadSession();\n return session ? copySession(session) : null;\n }\n\n /**\n * Идентификатор владельца сессии.\n *\n * Считается непосредственно из текущего токена и отдельно не сохраняется: после замены\n * токена идентификатор прежнего владельца остаться не может.\n */\n async getUserId(): Promise<UserId | undefined> {\n const session = await this.#loadSession();\n return session?.accessToken ? readTokenSubject(session.accessToken) : undefined;\n }\n\n /** Заменяет сессию и связанные с ней cookie целиком. */\n async setSession(session: ItdSession): Promise<void> {\n await this.#loadSession();\n this.#invalidateInFlight();\n this.#transitionAuth(session.accessToken);\n this.#jar.clear();\n this.#jar.deserialize(session.cookies);\n\n if (session.deviceId) this.#deviceId = session.deviceId;\n\n // Refresh-cookie добавляется до сериализации сессии.\n this.#session = session;\n this.#seedRefreshCookie();\n\n await this.#saveSession(session);\n }\n\n /**\n * Забывает сессию и cookie. Сетевой запрос не выполняется.\n *\n * Идентификатор устройства выход переживает: иначе каждая пара «выход — вход» плодила бы\n * новую запись в списке сессий.\n */\n async clear(): Promise<void> {\n await this.#loadSession();\n this.#invalidateInFlight();\n this.#transitionAuth(undefined);\n this.#session = null;\n this.#jar.clear();\n await this.#config.storage.clear();\n\n if (this.#deviceId) await this.#saveSession({ deviceId: this.#deviceId });\n\n this.#emitter.emit('signOut', undefined);\n }\n\n #loadSession(): Promise<ItdSession | null> {\n if (this.#session !== undefined) return Promise.resolve(this.#session);\n\n this.#loading ??= this.#performLoad().finally(() => {\n this.#loading = null;\n });\n\n return this.#loading;\n }\n\n async #performLoad(): Promise<ItdSession | null> {\n const loaded = (await this.#config.storage.get()) ?? null;\n const stored = loaded ? withoutLegacyUserId(loaded) : null;\n\n // Восстанавливаем cookie: без них не выйдет обновить токен после перезапуска процесса.\n if (stored?.cookies) this.#jar.deserialize(stored.cookies);\n\n const fromConfig = this.#sessionFromConfig(this.#config.auth);\n\n // Хранилище отвечает за состояние сессии и всегда важнее, но недостающие поля\n // берутся из конфигурации: типичный случай — сохранён только accessToken,\n // а refresh-токен приходит из настроек приложения.\n this.#session =\n stored && fromConfig\n ? {\n ...stored,\n accessToken: stored.accessToken ?? fromConfig.accessToken,\n refreshToken: stored.refreshToken ?? fromConfig.refreshToken,\n }\n : (stored ?? fromConfig);\n\n this.#seedRefreshCookie();\n\n return this.#session;\n }\n\n /**\n * Кладёт refresh-токен в jar как cookie `refresh_token`.\n *\n * `POST /api/v1/auth/refresh` читает токен только из cookie, поэтому переданный строкой\n * приходится превращать в неё. В браузере это невозможно — cookie помечена `HttpOnly`,\n * и там обновление работает только на той, что поставил сам сервер.\n */\n #seedRefreshCookie(): void {\n if (!this.#config.useCookieJar) return;\n\n const refreshToken = this.#session?.refreshToken;\n if (!refreshToken) return;\n if (this.#jar.has(REFRESH_COOKIE)) return;\n\n this.#jar.set(this.#config.baseUrl, REFRESH_COOKIE, refreshToken, REFRESH_COOKIE_PATH);\n }\n\n #sessionFromConfig(auth: AuthInput | undefined): ItdSession | null {\n if (!auth) return null;\n if (typeof auth === 'string') {\n return { accessToken: auth, obtainedAt: this.#config.clock.now() };\n }\n\n if ('accessToken' in auth) {\n return {\n accessToken: auth.accessToken,\n refreshToken: auth.refreshToken,\n obtainedAt: this.#config.clock.now(),\n };\n }\n\n return null;\n }\n\n async #saveSession(session: ItdSession): Promise<void> {\n const cookies = this.#config.useCookieJar ? this.#jar.serialize() : undefined;\n const deviceId = session.deviceId ?? this.#deviceId;\n\n const next = withoutLegacyUserId({\n ...session,\n ...(cookies?.length ? { cookies } : {}),\n ...(deviceId ? { deviceId } : {}),\n });\n\n this.#session = next;\n await this.#config.storage.set(next);\n }\n\n /**\n * Обновление с дедупликацией.\n *\n * Все, кто пришёл, пока обновление уже идёт, получают его результат, а не запускают своё.\n */\n #refreshDeduplicated(): Promise<string | null> {\n if (this.#refreshing) return this.#refreshing;\n\n const promise = this.#performRefresh().finally(() => {\n this.#refreshing = null;\n });\n\n this.#refreshing = promise;\n return promise;\n }\n\n async #performRefresh(): Promise<string | null> {\n await this.#loadSession();\n\n if (!this.#hasRefreshSession()) {\n // Нет признаков сессии — обновлять нечего. При наличии логина и пароля\n // пробуем войти заново.\n return this.#reloginOrNull();\n }\n\n // Снимок владельца авторизации: если он сменится, пока идёт сетевой запрос,\n // результат обновления записывать нельзя.\n const epoch = this.#authEpoch;\n\n try {\n // Служебный запрос идёт через плагины и повторы, но мимо очереди и слоя авторизации:\n // обновление порождается изнутри запроса, который держит слот в очереди и ждёт его результата.\n const payload = await this.#send({\n method: 'POST',\n path: AUTH_PATHS.refresh,\n skipQueue: true,\n skipAuth: true,\n skipAuthRefresh: true,\n // Тела нет намеренно: сервер читает refresh-токен только из cookie — см.\n // #seedRefreshCookie. По той же причине не нужен и устаревший Bearer.\n });\n\n const accessToken = readAccessToken(payload);\n if (!accessToken) return this.#reloginOrNull();\n\n if (this.#authEpoch !== epoch) {\n // Пока шёл refresh, владельца сменили (signOut / setSession / вход): его результат\n // устарел. Не воскрешаем сохранённую сессию, отдаём актуальный токен как есть.\n return this.#session?.accessToken ?? null;\n }\n\n // Сервер выдаёт при обновлении **новый** refresh-токен (`Set-Cookie: refresh_token=…;\n // Max-Age=2592000`) и тут же гасит прежний. Забрать его из jar обязательно: иначе\n // сохранённая строка протухнет и восстановление сессии из хранилища перестанет работать.\n const rotated = this.#jar.getValue(\n REFRESH_COOKIE,\n this.#config.baseUrl + REFRESH_COOKIE_PATH,\n );\n\n // Refresh продолжает ту же серверную сессию. Для непрозрачного токена это единственная\n // надёжная возможность сохранить scope; смена пользователя в JWT всё равно будет обнаружена.\n this.#transitionAuth(accessToken, false);\n await this.#saveSession({\n ...(this.#session ?? {}),\n accessToken,\n ...(rotated ? { refreshToken: rotated } : {}),\n obtainedAt: this.#config.clock.now(),\n });\n\n this.#emitter.emit('tokens', { accessToken });\n return accessToken;\n } catch (error) {\n if (error instanceof ItdApiError) {\n if (this.#authEpoch !== epoch) {\n // Владельца сменили, пока шёл refresh: ошибка относится к уже забытой сессии —\n // не трогаем актуальное состояние.\n return this.#session?.accessToken ?? null;\n }\n\n // Сессия недействительна — чистим её, иначе будем биться в стену на каждом запросе.\n this.#transitionAuth(undefined);\n this.#session = null;\n this.#jar.clear();\n await this.#config.storage.clear();\n\n const relogged = await this.#reloginOrNull();\n if (relogged !== null) return relogged;\n\n // Войти заново нечем — отдаём ошибку сервера как есть. Именно она объясняет,\n // что произошло: REFRESH_TOKEN_MISSING, SESSION_NOT_FOUND, SESSION_REVOKED.\n throw error;\n }\n throw error;\n }\n }\n\n /** Повторный вход, если разрешён настройкой и есть логин с паролем. */\n async #reloginOrNull(): Promise<string | null> {\n const auth = this.#config.auth;\n\n if (\n !this.#config.reloginOnRefreshFailure ||\n !auth ||\n typeof auth !== 'object' ||\n !('email' in auth)\n ) {\n return null;\n }\n\n try {\n return await this.#signInWithCredentials(auth);\n } catch {\n return null;\n }\n }\n\n /**\n * Вход по логину и паролю.\n *\n * Параллельные вызовы объединяются: одновременный старт нескольких запросов не должен\n * приводить к нескольким попыткам входа и блокировке аккаунта.\n */\n #signInWithCredentials(credentials: CredentialsAuth): Promise<string> {\n if (this.#signingIn) return this.#signingIn;\n\n const promise = this.#performSignIn(credentials).finally(() => {\n this.#signingIn = null;\n });\n\n this.#signingIn = promise;\n return promise;\n }\n\n /**\n * Берёт токен капчи для входа.\n *\n * `getTurnstileToken` приоритетнее готовой строки: токен Turnstile одноразовый и живёт\n * несколько минут, поэтому при повторном входе через сутки годится только свежий.\n */\n async #resolveTurnstileToken(credentials: CredentialsAuth): Promise<string> {\n if (credentials.getTurnstileToken) {\n const token = await credentials.getTurnstileToken();\n if (token) return token;\n }\n\n if (credentials.turnstileToken) return credentials.turnstileToken;\n\n throw new ItdConfigError(\n 'Вход по email и паролю требует токен капчи Cloudflare Turnstile: без него сервер ' +\n 'отвечает 422. Передайте auth.getTurnstileToken (источник свежего токена) либо ' +\n 'разовый auth.turnstileToken. Ключ виджета — TURNSTILE_SITE_KEY. В Node токен умеет ' +\n 'добывать отдельный пакет: npm i @itd-api/turnstile, затем ' +\n 'getTurnstileToken: createTurnstileSolver().',\n );\n }\n\n async #performSignIn(credentials: CredentialsAuth): Promise<string> {\n const turnstileToken = await this.#resolveTurnstileToken(credentials);\n\n // Через служебный auth-конвейер: плагины и повторы сохраняются, очередь и слой\n // авторизации обходятся, чтобы не зависнуть на запросе, который ждёт токен.\n const payload = await this.#send({\n method: 'POST',\n path: AUTH_PATHS.signIn,\n body: { email: credentials.email, password: credentials.password, turnstileToken },\n skipQueue: true,\n skipAuth: true,\n skipAuthRefresh: true,\n });\n\n const accessToken = readAccessToken(payload);\n\n if (!accessToken) {\n // Сервер запросил подтверждение по коду — автоматически это не пройти.\n throw new ItdConfigError(\n 'Вход по email и паролю требует подтверждения кодом из письма. Автоматический вход ' +\n 'невозможен: воспользуйтесь itd.auth.signInWithOtp() и передайте полученный ' +\n 'accessToken в конфигурацию клиента.',\n );\n }\n\n // Прежний refresh-токен и cookie намеренно не переносятся: вход выдал новую сессию,\n // и держаться за старую было бы ошибкой. Идентификатор устройства добавит #saveSession.\n this.#invalidateInFlight();\n this.#transitionAuth(accessToken);\n await this.#saveSession({ accessToken, obtainedAt: this.#config.clock.now() });\n this.#emitter.emit('tokens', { accessToken });\n this.#emitter.emit('signIn', { accessToken });\n\n return accessToken;\n }\n}\n","/**\n * Часы, которыми клиент измеряет время и планирует отложенную работу.\n *\n * Своя реализация нужна прежде всего в тестах: она позволяет проверять тайм-ауты,\n * повторы и переподключение без ожидания в реальном времени.\n */\nexport interface ItdClock {\n /** Текущее время в миллисекундах с начала эпохи Unix. */\n now(): number;\n /** Планирует вызов после завершения текущего стека и возвращает функцию отмены. */\n schedule(callback: () => void, delay: number): () => void;\n}\n\n/** Системные часы, используемые клиентом по умолчанию. */\nexport const systemClock: ItdClock = Object.freeze({\n now: () => Date.now(),\n schedule(callback: () => void, delay: number): () => void {\n const timer = setTimeout(callback, delay);\n return () => clearTimeout(timer);\n },\n});\n","import { ItdConfigError } from './errors.js';\n\n/** Значение параметра запроса. `undefined` и `null` в строку не попадают. */\nexport type QueryValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | readonly (string | number | boolean)[];\n\n/** Параметры строки запроса. */\nexport type QueryParams = Record<string, QueryValue>;\n\n/**\n * Собирает строку запроса.\n *\n * Правила:\n * - `undefined` и `null` пропускаются целиком — не нужно чистить объект перед вызовом;\n * - `boolean` превращается в `true` / `false`;\n * - массив повторяет ключ (`ids=1&ids=2`).\n *\n * @returns строка вида `?a=1&b=2` либо пустая строка, если параметров нет\n *\n * @example\n * ```ts\n * buildQuery({ tab: 'popular', limit: 20, cursor: undefined });\n * // '?tab=popular&limit=20'\n * ```\n */\nexport function buildQuery(params?: QueryParams): string {\n if (!params) return '';\n\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item === undefined || item === null) continue;\n search.append(key, String(item));\n }\n continue;\n }\n\n search.append(key, String(value));\n }\n\n const query = search.toString();\n return query ? `?${query}` : '';\n}\n\n/**\n * Кодирует значение для подстановки в путь.\n *\n * Нужно для сегментов, которые могут содержать что угодно, — прежде всего хэштегов\n * в `/api/hashtags/{tag}/posts`.\n *\n * @throws {ItdConfigError} если значение пустое\n */\nexport function encodePathSegment(value: string, name = 'параметр пути'): string {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new ItdConfigError(\n `${name} должен быть непустой строкой, получено: ${JSON.stringify(value)}`,\n );\n }\n return encodeURIComponent(value);\n}\n\n/**\n * Склеивает базовый URL и путь.\n *\n * Завершающий слэш пути сохраняется: он значим для `/api/notifications/`\n * и `/api/v1/subscription/`, без него сервер отвечает ошибкой.\n *\n * @example\n * ```ts\n * joinUrl('https://xn--d1ah4a.com/', '/api/notifications/');\n * // 'https://xn--d1ah4a.com/api/notifications/'\n * ```\n */\nexport function joinUrl(baseUrl: string, path: string): string {\n const base = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n const suffix = path.startsWith('/') ? path : `/${path}`;\n return `${base}${suffix}`;\n}\n\n/** Приводит базовый URL к каноничному виду и проверяет, что он вообще похож на URL. */\nexport function normalizeBaseUrl(baseUrl: string): string {\n if (typeof baseUrl !== 'string' || baseUrl.trim() === '') {\n throw new ItdConfigError(\n `baseUrl должен быть непустой строкой с абсолютным URL, получено: ${JSON.stringify(baseUrl)}`,\n );\n }\n\n let parsed: URL;\n try {\n parsed = new URL(baseUrl);\n } catch {\n throw new ItdConfigError(\n `baseUrl должен быть абсолютным URL, получено: ${JSON.stringify(baseUrl)}`,\n );\n }\n\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new ItdConfigError(\n `baseUrl должен использовать http или https, получено: ${parsed.protocol}`,\n );\n }\n\n if (parsed.username || parsed.password || parsed.search || parsed.hash) {\n throw new ItdConfigError(\n 'baseUrl не должен содержать логин, пароль, query-параметры или fragment',\n );\n }\n\n return parsed.origin + (parsed.pathname === '/' ? '' : parsed.pathname.replace(/\\/$/, ''));\n}\n","// Этот файл создаётся автоматически: scripts/sync-version.mjs.\n// Менять вручную не нужно — значение берётся из package.json при `npm version` и сборке.\n\n/** Версия библиотеки. Попадает в `User-Agent`. */\nexport const LIBRARY_VERSION = '0.4.0';\n","import type { AuthInput, ClientHooks, ItdClientOptions, Logger } from '../types/options.js';\nimport { type ItdClock, systemClock } from './clock.js';\nimport { ItdConfigError } from './errors.js';\nimport { RuntimeMode, resolveFetch, shouldSendCredentials, shouldUseCookieJar } from './runtime.js';\nimport type { ServiceDefinition } from './services.js';\nimport { MemoryTokenStorage, type TokenStorage } from './storage.js';\nimport { normalizeBaseUrl } from './url.js';\nimport { LIBRARY_VERSION } from './version.js';\n\n/** Базовый URL API итд.com. Домен записан в punycode: `итд.com`. */\nexport const DEFAULT_BASE_URL = 'https://xn--d1ah4a.com';\n\n/** Базовый URL страницы статуса. Домен записан в punycode: `статус.итд.com`. */\nexport const DEFAULT_STATUS_BASE_URL = 'https://xn--80a7abcbg.xn--d1ah4a.com';\n\n/** Имя встроенного сервиса статуса. */\nexport const STATUS_SERVICE = 'status';\n\n/** Сервисы, зарегистрированные у любого клиента. */\nexport const BUILT_IN_SERVICES: readonly ServiceDefinition[] = Object.freeze([\n Object.freeze({ name: STATUS_SERVICE, baseUrl: DEFAULT_STATUS_BASE_URL, auth: false }),\n]);\n\n/** Таймаут запроса по умолчанию — 30 секунд. */\nexport const DEFAULT_TIMEOUT = 30_000;\n\n// Значение живёт в отдельном модуле, который порождается из package.json скриптом\n// scripts/sync-version.mjs: две записанные вручную версии рано или поздно разъезжаются.\nexport { LIBRARY_VERSION } from './version.js';\n\n/**\n * `User-Agent` по умолчанию.\n *\n * Сайт стоит за DDoS-Guard, и запросы вовсе без `User-Agent` (так делает `fetch` в Node)\n * имеют шанс не пройти фильтр. Префикс `Mozilla/5.0` — дань традиции таких фильтров,\n * дальше идёт честное имя библиотеки: подделываться под браузер она не должна.\n *\n * В браузере заголовок не выставляется — `User-Agent` там запрещён к изменению, и среда\n * молча его игнорирует.\n */\nexport const DEFAULT_USER_AGENT = `Mozilla/5.0 (compatible; itd-api/${LIBRARY_VERSION}; +https://github.com/KiowDev/itd-api)`;\n\n/**\n * Настройки повторов со всеми значениями по умолчанию.\n *\n * Поля перечислены явно, а не через `Required<RetryOptions>`: тот снимает необязательность,\n * но оставляет `| undefined` в типе значения, раз оно указано в исходном интерфейсе.\n */\nexport interface ResolvedRetryOptions {\n attempts: number;\n baseDelay: number;\n maxDelay: number;\n jitter: number;\n retryWrites: boolean;\n shouldRetry: ((error: unknown, attempt: number) => boolean) | undefined;\n}\n\n/**\n * Паузы перед повторами при ответе `429`.\n *\n * Сервер итд.com не присылает `Retry-After` и не сообщает время сброса окна, поэтому\n * паузу приходится подбирать лестницей: от секунды, если окно почти истекло,\n * до полутора минут, если лимит исчерпан всерьёз.\n */\nexport const DEFAULT_RATE_LIMIT_DELAYS = Object.freeze([1000, 5000, 30_000, 60_000, 90_000]);\n\n/** Настройки очереди со всеми значениями по умолчанию. */\nexport interface ResolvedRateLimitOptions {\n concurrency: number;\n rps: number | undefined;\n retryDelays: readonly number[];\n respectHeaders: boolean;\n}\n\n/**\n * Срез конфигурации, нужный слою авторизации.\n *\n * Выделен явно, чтобы {@link AuthManager} не получал целиком `ResolvedConfig` со всеми\n * настройками транспорта, повторов и очереди, к которым он отношения не имеет.\n * `ResolvedConfig` содержит все эти поля, поэтому подходит везде, где ждут `AuthConfig`.\n */\nexport interface AuthConfig {\n baseUrl: string;\n clock: ItdClock;\n auth: AuthInput | undefined;\n storage: TokenStorage;\n useCookieJar: boolean;\n deviceId: string | undefined;\n reloginOnRefreshFailure: boolean;\n logger: Logger | undefined;\n}\n\n/** Конфигурация клиента после подстановки значений по умолчанию и проверок. */\nexport interface ResolvedConfig extends AuthConfig {\n /** Сервисы из опций клиента. Встроенные сюда не входят. */\n services: ServiceDefinition[];\n autoRefresh: boolean;\n fetch: typeof fetch;\n timeout: number;\n retry: ResolvedRetryOptions | undefined;\n rateLimit: ResolvedRateLimitOptions | undefined;\n hooks: ClientHooks;\n headers: Record<string, string>;\n /** Значение заголовка `User-Agent`. `undefined` — заголовок не выставляется. */\n userAgent: string | undefined;\n mode: RuntimeMode;\n /** Отправлять ли `credentials: 'include'` (в браузере). */\n sendCredentials: boolean;\n}\n\nfunction requirePositive(value: number, name: string): number {\n if (!Number.isFinite(value) || value < 0) {\n throw new ItdConfigError(`${name} должен быть неотрицательным числом, получено: ${value}`);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): boolean {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction requireOptionalBoolean(value: unknown, name: string): void {\n if (value !== undefined && typeof value !== 'boolean') {\n throw new ItdConfigError(`${name} должен быть boolean`);\n }\n}\n\nfunction resolveStorage(storage: TokenStorage | undefined): TokenStorage {\n if (storage === undefined) return new MemoryTokenStorage();\n if (!isRecord(storage)) throw new ItdConfigError('storage должен быть объектом TokenStorage');\n\n for (const method of ['get', 'set', 'clear'] as const) {\n if (typeof storage[method] !== 'function') {\n throw new ItdConfigError(`storage.${method} должен быть функцией`);\n }\n }\n return storage;\n}\n\nfunction resolveHeaders(headers: Record<string, string> | undefined): Record<string, string> {\n if (headers === undefined) return {};\n if (!isRecord(headers)) throw new ItdConfigError('headers должен быть объектом строк');\n\n for (const [name, value] of Object.entries(headers)) {\n if (typeof value !== 'string') {\n throw new ItdConfigError(`headers.${name} должен быть строкой`);\n }\n }\n return { ...headers };\n}\n\nfunction resolveHooks(hooks: ClientHooks | undefined): ClientHooks {\n if (hooks === undefined) return {};\n if (!isRecord(hooks)) throw new ItdConfigError('hooks должен быть объектом');\n\n for (const name of ['onRequest', 'onResponse', 'onError', 'onRetry'] as const) {\n if (hooks[name] !== undefined && typeof hooks[name] !== 'function') {\n throw new ItdConfigError(`hooks.${name} должен быть функцией`);\n }\n }\n return { ...hooks };\n}\n\nfunction resolveLogger(logger: ItdClientOptions['logger']): Logger | undefined {\n if (logger === undefined || logger === false) return undefined;\n if (logger === true) return consoleLogger();\n if (!isRecord(logger)) throw new ItdConfigError('logger должен быть boolean или объектом Logger');\n\n for (const method of ['debug', 'info', 'warn', 'error'] as const) {\n if (typeof logger[method] !== 'function') {\n throw new ItdConfigError(`logger.${method} должен быть функцией`);\n }\n }\n return logger;\n}\n\n/** Логгер поверх `console` — включается опцией `logger: true`. */\nfunction consoleLogger(): Logger {\n return {\n debug: (message, ...args) => console.debug(`[itd-api] ${message}`, ...args),\n info: (message, ...args) => console.info(`[itd-api] ${message}`, ...args),\n warn: (message, ...args) => console.warn(`[itd-api] ${message}`, ...args),\n error: (message, ...args) => console.error(`[itd-api] ${message}`, ...args),\n };\n}\n\n/**\n * Приводит настройки повторов к полному виду.\n *\n * Вызывается и при создании клиента (для глобальной настройки), и на каждый запрос,\n * у которого задан свой `retry`. Возвращает `undefined`, когда повторять не нужно\n * (`retry: false` или единственная попытка).\n *\n * @throws {ItdConfigError} при некорректных значениях\n */\nexport function resolveRetry(retry: ItdClientOptions['retry']): ResolvedRetryOptions | undefined {\n if (retry === false) return undefined;\n if (retry !== undefined && !isRecord(retry)) {\n throw new ItdConfigError('retry должен быть объектом или false');\n }\n\n const options = retry ?? {};\n const attempts = options.attempts ?? 3;\n\n if (!Number.isInteger(attempts) || attempts < 1) {\n throw new ItdConfigError(`retry.attempts должен быть целым числом от 1, получено: ${attempts}`);\n }\n\n const jitter = options.jitter ?? 0.3;\n if (!Number.isFinite(jitter) || jitter < 0 || jitter > 1) {\n throw new ItdConfigError(`retry.jitter должен быть в диапазоне 0…1, получено: ${jitter}`);\n }\n requireOptionalBoolean(options.retryWrites, 'retry.retryWrites');\n if (options.shouldRetry !== undefined && typeof options.shouldRetry !== 'function') {\n throw new ItdConfigError('retry.shouldRetry должен быть функцией');\n }\n\n const baseDelay = requirePositive(options.baseDelay ?? 500, 'retry.baseDelay');\n const maxDelay = requirePositive(options.maxDelay ?? 30_000, 'retry.maxDelay');\n\n // Одна попытка означает отсутствие повторов — очередь ретраев можно не поднимать.\n if (attempts === 1) return undefined;\n\n return {\n attempts,\n baseDelay,\n maxDelay,\n jitter,\n retryWrites: options.retryWrites ?? false,\n shouldRetry: options.shouldRetry,\n };\n}\n\n/**\n * Приводит настройки очереди к полному виду. `undefined` — очередь не нужна.\n *\n * Кроме создания клиента вызывается ещё из {@link ItdAccounts}: общая на всех аккаунтов\n * очередь заводится из тех же опций и с теми же проверками.\n *\n * @throws {ItdConfigError} при некорректных значениях\n *\n * @internal\n */\nexport function resolveRateLimit(\n rateLimit: ItdClientOptions['rateLimit'],\n): ResolvedRateLimitOptions | undefined {\n if (rateLimit === false) return undefined;\n if (rateLimit !== undefined && !isRecord(rateLimit)) {\n throw new ItdConfigError('rateLimit должен быть объектом или false');\n }\n\n const defaults = {\n concurrency: 6,\n rps: undefined,\n retryDelays: DEFAULT_RATE_LIMIT_DELAYS,\n respectHeaders: true,\n };\n if (!rateLimit) return defaults;\n\n const concurrency = rateLimit.concurrency ?? 6;\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new ItdConfigError(\n `rateLimit.concurrency должен быть целым числом от 1, получено: ${concurrency}`,\n );\n }\n\n if (rateLimit.rps !== undefined && (!Number.isFinite(rateLimit.rps) || rateLimit.rps <= 0)) {\n throw new ItdConfigError(\n `rateLimit.rps должен быть положительным числом, получено: ${rateLimit.rps}`,\n );\n }\n\n const retryDelays = rateLimit.retryDelays ?? defaults.retryDelays;\n if (!Array.isArray(retryDelays)) {\n throw new ItdConfigError('rateLimit.retryDelays должен быть массивом чисел');\n }\n for (const delay of retryDelays) requirePositive(delay, 'rateLimit.retryDelays');\n requireOptionalBoolean(rateLimit.respectHeaders, 'rateLimit.respectHeaders');\n\n return {\n concurrency,\n rps: rateLimit.rps,\n retryDelays: [...retryDelays],\n respectHeaders: rateLimit.respectHeaders ?? true,\n };\n}\n\n/**\n * Проверяет форму `auth` и сообщает о типичных ошибках понятным текстом.\n *\n * Молчаливое игнорирование неверной формы приводит к загадочным `401`, поэтому\n * ошибка возникает сразу при создании клиента.\n */\nfunction validateAuth(auth: AuthInput | undefined): AuthInput | undefined {\n if (auth === undefined) return undefined;\n\n if (typeof auth === 'string') {\n if (auth.trim() === '') {\n throw new ItdConfigError('auth: передана пустая строка вместо accessToken');\n }\n return auth;\n }\n\n if (typeof auth !== 'object' || auth === null) {\n throw new ItdConfigError(\n `auth должен быть строкой с токеном или объектом, получено: ${typeof auth}`,\n );\n }\n\n if ('getToken' in auth) {\n if (typeof auth.getToken !== 'function') {\n throw new ItdConfigError('auth.getToken должен быть функцией');\n }\n return { ...auth };\n }\n\n if ('accessToken' in auth) {\n if (typeof auth.accessToken !== 'string' || auth.accessToken.trim() === '') {\n throw new ItdConfigError('auth.accessToken должен быть непустой строкой');\n }\n return { ...auth };\n }\n\n if ('email' in auth || 'password' in auth) {\n const { email, password, turnstileToken, getTurnstileToken } = auth as {\n email?: unknown;\n password?: unknown;\n turnstileToken?: unknown;\n getTurnstileToken?: unknown;\n };\n if (typeof email !== 'string' || email.trim() === '') {\n throw new ItdConfigError('auth.email должен быть непустой строкой');\n }\n if (typeof password !== 'string' || password === '') {\n throw new ItdConfigError('auth.password должен быть непустой строкой');\n }\n if (getTurnstileToken !== undefined && typeof getTurnstileToken !== 'function') {\n throw new ItdConfigError('auth.getTurnstileToken должен быть функцией');\n }\n if (\n turnstileToken !== undefined &&\n (typeof turnstileToken !== 'string' || turnstileToken.trim() === '')\n ) {\n throw new ItdConfigError('auth.turnstileToken должен быть непустой строкой');\n }\n\n // Отсутствие капчи — не ошибка конфигурации: сессия может быть восстановлена из\n // хранилища, и до входа по паролю дело вообще не дойдёт. Ошибка возникнет в момент\n // входа, где её текст может объяснить, что именно нужно сделать.\n return { ...auth };\n }\n\n throw new ItdConfigError(\n 'auth не распознан. Ожидается строка с accessToken либо объект ' +\n '{ accessToken }, { email, password } или { getToken }',\n );\n}\n\n/**\n * Разворачивает запись сервисов из опций в определения: имя берётся из ключа, строка\n * означает один только базовый URL. URL проверяется при регистрации сервиса.\n */\nfunction resolveServices(services: ItdClientOptions['services']): ServiceDefinition[] {\n if (services === undefined) return [];\n if (!isRecord(services)) throw new ItdConfigError('services должен быть объектом');\n\n return Object.entries(services).map(([name, value]) => {\n if (typeof value === 'string') return { name, baseUrl: value };\n if (!isRecord(value)) {\n throw new ItdConfigError(`services.${name} должен быть URL или объектом сервиса`);\n }\n return { ...value, name } as ServiceDefinition;\n });\n}\n\n/**\n * Приводит пользовательские опции к полной конфигурации.\n *\n * Все проверки выполняются здесь, до единого сетевого запроса: неверная настройка должна\n * проявляться при создании клиента, а не через полчаса работы бота.\n *\n * @throws {ItdConfigError} при некорректных значениях\n */\nexport function resolveConfig(options: ItdClientOptions = {}): ResolvedConfig {\n if (!isRecord(options)) throw new ItdConfigError('опции клиента должны быть объектом');\n\n const mode: RuntimeMode = options.mode ?? RuntimeMode.Auto;\n\n if (!Object.values(RuntimeMode).includes(mode)) {\n throw new ItdConfigError(\n `mode должен быть одним из ${Object.values(RuntimeMode).join(', ')}, получено: ${mode}`,\n );\n }\n\n const timeout = requirePositive(options.timeout ?? DEFAULT_TIMEOUT, 'timeout');\n\n if (\n options.clock !== undefined &&\n (typeof options.clock !== 'object' ||\n options.clock === null ||\n typeof options.clock.now !== 'function' ||\n typeof options.clock.schedule !== 'function')\n ) {\n throw new ItdConfigError('clock должен предоставлять методы now() и schedule()');\n }\n requireOptionalBoolean(options.autoRefresh, 'autoRefresh');\n requireOptionalBoolean(options.reloginOnRefreshFailure, 'reloginOnRefreshFailure');\n\n if (\n options.userAgent !== undefined &&\n options.userAgent !== false &&\n typeof options.userAgent !== 'string'\n ) {\n throw new ItdConfigError('userAgent должен быть строкой или false');\n }\n\n if (\n options.deviceId !== undefined &&\n (typeof options.deviceId !== 'string' || options.deviceId.trim() === '')\n ) {\n throw new ItdConfigError('deviceId должен быть непустой строкой');\n }\n\n return {\n baseUrl: normalizeBaseUrl(options.baseUrl ?? DEFAULT_BASE_URL),\n services: resolveServices(options.services),\n auth: validateAuth(options.auth),\n storage: resolveStorage(options.storage),\n autoRefresh: options.autoRefresh ?? true,\n reloginOnRefreshFailure: options.reloginOnRefreshFailure ?? true,\n fetch: resolveFetch(options.fetch),\n clock: options.clock ?? systemClock,\n timeout,\n retry: resolveRetry(options.retry),\n rateLimit: resolveRateLimit(options.rateLimit),\n hooks: resolveHooks(options.hooks),\n logger: resolveLogger(options.logger),\n headers: resolveHeaders(options.headers),\n deviceId: options.deviceId,\n // `false` — способ не слать заголовок вовсе; строка заменяет умолчание.\n userAgent: options.userAgent === false ? undefined : (options.userAgent ?? DEFAULT_USER_AGENT),\n mode,\n useCookieJar: shouldUseCookieJar(mode),\n sendCredentials: shouldSendCredentials(mode),\n };\n}\n","import type { PipelineRequest, RequestHandler } from './pipeline.js';\nimport type { PluginRegistry } from './plugins/registry.js';\n\n/** Что нужно фасаду для работы. */\nexport interface HttpClientDeps {\n /** Готовый обработчик — вся цепочка слоёв поверх транспорта. */\n handler: RequestHandler;\n /** Реестр плагинов: у него ресурсы спрашивают имена заявленных опций. */\n plugins: PluginRegistry;\n baseUrl: string;\n}\n\n/**\n * Точка входа ресурсов в конвейер запросов.\n *\n * Принимает готовый обработчик — цепочку слоёв поверх транспорта, собранную\n * в {@link ItdClient}, — и отдаёт ресурсам метод `request` и имена опций плагинов.\n * О слоях и их порядке ресурсы не знают.\n */\nexport class HttpClient {\n readonly #handler: RequestHandler;\n readonly #plugins: PluginRegistry;\n readonly #baseUrl: string;\n\n constructor(deps: HttpClientDeps) {\n this.#handler = deps.handler;\n this.#plugins = deps.plugins;\n this.#baseUrl = deps.baseUrl;\n }\n\n /** Базовый URL, к которому обращается клиент. */\n get baseUrl(): string {\n return this.#baseUrl;\n }\n\n /**\n * Имена опций запроса, заявленные плагинами.\n *\n * Читается ресурсами: они переносят в транспорт только известные поля, а чужие,\n * если их никто не заявил, отсеивают.\n */\n get pluginOptionKeys(): ReadonlySet<string> {\n return this.#plugins.optionKeys;\n }\n\n /**\n * Выполняет запрос к API через собранный конвейер.\n *\n * @typeParam T ожидаемая форма ответа после снятия обёртки `{ data: … }`\n * @throws {ItdApiError} если сервер ответил статусом ≥ 400\n * @throws {ItdTimeoutError} если истёк таймаут\n * @throws {ItdAbortError} если запрос отменён через `signal`\n * @throws {ItdNetworkError} если запрос не дошёл до сервера\n */\n request<T = unknown>(options: PipelineRequest): Promise<T> {\n return this.#handler(options) as Promise<T>;\n }\n}\n","import type { RawRequestOptions } from '../types/options.js';\n\n/** Тело, заново подготовленное для одной транспортной попытки. */\nexport interface PreparedRequestBody {\n body: BodyInit;\n /** Заголовки тела, например multipart boundary. Пользовательские заголовки важнее. */\n headers?: Record<string, string> | undefined;\n /** Освобождает открытый файл или входящий HTTP-поток. */\n cleanup?: (() => void | Promise<void>) | undefined;\n}\n\n/** Контекст подготовки повторяемого тела. */\nexport interface RequestBodyContext {\n signal: AbortSignal;\n attempt: number;\n}\n\n/** Создаёт новое тело для каждой транспортной попытки. */\nexport type RequestBodyFactory = (\n context: RequestBodyContext,\n) => PreparedRequestBody | Promise<PreparedRequestBody>;\n\n/**\n * Описание запроса внутри конвейера.\n *\n * Отличается от публичного {@link RawRequestOptions} одним служебным полем: слои конвейера\n * должны уметь дописать заголовки так, чтобы пользовательские `headers` всё равно остались\n * важнее. Смешивать их в одном объекте нельзя — тогда слой авторизации перебивал бы\n * `Authorization`, заданный вызывающим кодом вручную.\n */\nexport interface PipelineRequest extends RawRequestOptions {\n /** Повторяемое тело. Используется внутренними ресурсами вместо `body`. @internal */\n bodyFactory?: RequestBodyFactory | undefined;\n /**\n * Разрешает повтор записи после сетевого сбоя. Тело должно быть повторяемым.\n *\n * @internal\n */\n retryNetworkWrite?: boolean | undefined;\n /**\n * Заголовки, добавленные слоями конвейера.\n *\n * Ставятся до пользовательских `headers` и потому могут быть ими переопределены.\n *\n * @internal\n */\n layerHeaders?: Record<string, string> | undefined;\n /**\n * Номер попытки, начиная с 1. Проставляет слой повторов, читают хуки.\n *\n * @internal\n */\n attempt?: number | undefined;\n}\n\n/** Обработчик запроса. Самый внутренний в цепочке — транспорт. */\nexport type RequestHandler = (request: PipelineRequest) => Promise<unknown>;\n\n/**\n * Слой конвейера запросов.\n *\n * Получает запрос и продолжение цепочки. Может изменить запрос, обработать ошибку\n * продолжения или вовсе не вызывать `next`.\n */\nexport type RequestMiddleware = (\n request: PipelineRequest,\n next: RequestHandler,\n) => Promise<unknown>;\n\n/**\n * Собирает слои в один обработчик.\n *\n * Первый слой оказывается самым внешним. Порядок задаётся в {@link ItdClient}.\n *\n * @example\n * ```ts\n * const handler = composePipeline([queue, plugins, retries, auth], transport.send);\n * ```\n */\nexport function composePipeline(\n middlewares: readonly RequestMiddleware[],\n final: RequestHandler,\n): RequestHandler {\n return middlewares.reduceRight<RequestHandler>(\n (next, middleware) => (request) => middleware(request, next),\n final,\n );\n}\n\n/** Добавляет заголовки слоя, не трогая пользовательские. */\nexport function withLayerHeaders(\n request: PipelineRequest,\n headers: Record<string, string>,\n): PipelineRequest {\n return { ...request, layerHeaders: { ...request.layerHeaders, ...headers } };\n}\n","import type { ClientHooks, RawRequestOptions } from '../../types/options.js';\n\nexport type HookName = keyof ClientHooks;\nexport type HookContext<K extends HookName> = Parameters<NonNullable<ClientHooks[K]>>[0];\nexport type HookDispatcher = <K extends HookName>(\n field: K,\n context: HookContext<K>,\n request: RawRequestOptions,\n) => Promise<void>;\n\nconst REQUEST_HOOK_DISPATCHERS = new WeakMap<ClientHooks, HookDispatcher>();\nconst PLUGIN_HOOK_SCOPE: unique symbol = Symbol('itd-api.plugin-hooks');\ntype ScopedRequest = RawRequestOptions & {\n [PLUGIN_HOOK_SCOPE]?: readonly ClientHooks[];\n};\n\n/** Создаёт динамический набор hooks, связанный с диспетчером registry. @internal */\nexport function createRequestHooks(dispatcher: HookDispatcher): ClientHooks {\n const hooks: ClientHooks = {};\n REQUEST_HOOK_DISPATCHERS.set(hooks, dispatcher);\n return hooks;\n}\n\n/** Привязывает к запросу неизменяемый снимок plugin hooks. @internal */\nexport function withRequestHookScope(\n request: RawRequestOptions,\n hooks: readonly ClientHooks[],\n): RawRequestOptions {\n const scoped = request as ScopedRequest;\n return scoped[PLUGIN_HOOK_SCOPE] === hooks\n ? scoped\n : ({ ...request, [PLUGIN_HOOK_SCOPE]: hooks } as ScopedRequest);\n}\n\n/** Читает снимок plugin hooks, привязанный к логическому запросу. @internal */\nexport function requestHookScope(request: RawRequestOptions): readonly ClientHooks[] {\n return (request as ScopedRequest)[PLUGIN_HOOK_SCOPE] ?? [];\n}\n\n/**\n * Вызывает публичный хук, сохраняя привязанный к логическому запросу снимок плагинов.\n *\n * Обычные наборы хуков по-прежнему получают только публичный контекст. Дополнительный\n * аргумент используется исключительно внутренним составным набором PluginRegistry.\n *\n * @internal\n */\nexport async function dispatchRequestHook<K extends HookName>(\n hooks: ClientHooks,\n field: K,\n context: HookContext<K>,\n request: RawRequestOptions,\n): Promise<void> {\n const dispatcher = REQUEST_HOOK_DISPATCHERS.get(hooks);\n if (dispatcher) {\n await dispatcher(field, context, request);\n return;\n }\n const hook = hooks[field] as ((value: HookContext<K>) => void | Promise<void>) | undefined;\n await hook?.(context);\n}\n","import type { ResolvedRetryOptions } from './config.js';\nimport {\n ItdAbortError,\n ItdApiError,\n ItdFileError,\n ItdNetworkError,\n ItdTimeoutError,\n} from './errors.js';\n\n/**\n * Решает, повторять ли запрос.\n *\n * @returns пауза в миллисекундах перед следующей попыткой либо `undefined`, если повторять не нужно\n */\nexport type RetryScheduler = (\n error: unknown,\n attempt: number,\n method: string,\n retryNetworkWrite?: boolean,\n) => number | undefined;\n\n/**\n * Методы, повтор которых безопасен по определению.\n *\n * `DELETE` формально тоже идемпотентен, но его повтор после успеха вернёт `404`\n * и собьёт с толку — в список он не входит.\n */\nconst IDEMPOTENT_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);\n\n/**\n * Стоит ли повторять запрос после этой ошибки.\n *\n * Отдельно разобран `429`: он повторяется даже для запросов на запись, потому что\n * гарантирует, что запрос **не был обработан**. Обрыв сети и `5xx` такой гарантии не дают —\n * сервер мог успеть создать пост, — поэтому запись по умолчанию не повторяется.\n */\nfunction isRetryable(\n error: unknown,\n method: string,\n retryWrites: boolean,\n retryNetworkWrite: boolean,\n): boolean {\n // Отмену повторять нельзя ни при каких условиях: её попросил пользователь.\n if (error instanceof ItdAbortError) return false;\n\n const safeToRepeat = retryWrites || IDEMPOTENT_METHODS.has(method);\n\n if (error instanceof ItdApiError) {\n if (error.status === 429) return true;\n if (error.status >= 500) return safeToRepeat;\n return false;\n }\n\n if (error instanceof ItdNetworkError || error instanceof ItdTimeoutError) {\n return safeToRepeat || retryNetworkWrite;\n }\n\n if (error instanceof ItdFileError) {\n return error.retryable && (safeToRepeat || retryNetworkWrite);\n }\n\n return false;\n}\n\n/** Экспоненциальная пауза со случайным разбросом. */\nfunction backoffDelay(\n attempt: number,\n options: ResolvedRetryOptions,\n random: () => number,\n): number {\n const exponential = options.baseDelay * 2 ** (attempt - 1);\n const capped = Math.min(exponential, options.maxDelay);\n const spread = capped * options.jitter * (random() * 2 - 1);\n\n return Math.max(0, Math.round(capped + spread));\n}\n\n/**\n * Собирает планировщик повторов для транспорта.\n *\n * Поведение при `Retry-After`: пауза, названная сервером, соблюдается точно — она\n * авторитетнее нашего расчёта. Но если сервер просит ждать дольше, чем `maxDelay`,\n * повтор **не выполняется вовсе**: молча спать десять минут внутри вызова библиотека\n * не должна, лучше отдать {@link ItdRateLimitError} и дать решить вызывающему коду.\n *\n * @param options настройки повторов после подстановки значений по умолчанию\n * @param random источник случайности; подменяется в тестах ради предсказуемости\n *\n * @example\n * ```ts\n * const scheduler = createRetryScheduler(config.retry);\n * const delay = scheduler(error, 1, 'GET'); // 500 мс ± 30%\n * ```\n */\nexport function createRetryScheduler(\n options: ResolvedRetryOptions,\n random: () => number = Math.random,\n): RetryScheduler {\n return (error, attempt, method, retryNetworkWrite = false) => {\n if (attempt >= options.attempts) return undefined;\n\n if (options.shouldRetry) {\n return options.shouldRetry(error, attempt)\n ? backoffDelay(attempt, options, random)\n : undefined;\n }\n\n if (!isRetryable(error, method, options.retryWrites, retryNetworkWrite)) return undefined;\n\n if (error instanceof ItdApiError && error.retryAfter !== undefined) {\n return error.retryAfter > options.maxDelay ? undefined : error.retryAfter;\n }\n\n return backoffDelay(attempt, options, random);\n };\n}\n","import type { ClientHooks, Logger, RawRequestOptions, RequestOptions } from '../types/options.js';\nimport { type ItdClock, systemClock } from './clock.js';\nimport { type ResolvedRetryOptions, resolveRetry } from './config.js';\nimport { ItdAbortError, isItdApiError, isItdRateLimitError } from './errors.js';\nimport {\n type PipelineRequest,\n type RequestHandler,\n type RequestMiddleware,\n withLayerHeaders,\n} from './pipeline.js';\nimport { dispatchRequestHook } from './plugins/hooks.js';\nimport type { PluginRegistry } from './plugins/registry.js';\nimport { createRetryScheduler, type RetryScheduler } from './retry.js';\nimport type { ServiceRegistry } from './services.js';\nimport { normalizeBaseUrl } from './url.js';\n\n/** Ожидание повтора, которое уважает отмену запроса. */\nfunction sleep(clock: ItdClock, ms: number, signal?: AbortSignal): Promise<void> {\n if (!signal) return new Promise((resolve) => clock.schedule(resolve, ms));\n if (signal.aborted) {\n return Promise.reject(new ItdAbortError('Запрос отменён во время ожидания повтора'));\n }\n\n return new Promise((resolve, reject) => {\n const cancel = clock.schedule(() => {\n signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n cancel();\n reject(new ItdAbortError('Запрос отменён во время ожидания повтора'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/**\n * Слой очереди: ограничение конкурентности и частоты.\n *\n * `skipQueue` пропускает запрос мимо очереди — так поступают служебные запросы, которые\n * порождены изнутри другого запроса и не могут ждать освободившегося слота.\n *\n * Очередь выбирается по запросу: у каждого сервиса платформы свой хост и свой лимит.\n */\nexport function createQueueMiddleware(\n schedule: <T>(request: PipelineRequest, task: () => Promise<T>) => Promise<T>,\n): RequestMiddleware {\n return (request, next) =>\n request.skipQueue ? next(request) : schedule(request, () => next(request));\n}\n\n/**\n * Слой плагинов.\n *\n * Стоит снаружи повторов и внутри очереди: плагин должен увидеть запрос и ответ по одному\n * разу, независимо от числа попыток, — иначе, например, текст поста зашифруется дважды.\n */\nexport function createPluginsMiddleware(plugins: PluginRegistry): RequestMiddleware {\n return (request, next) =>\n plugins.run(request, next as (request: RawRequestOptions) => Promise<unknown>);\n}\n\n/**\n * Слой сервисов.\n *\n * Запросу с полем `service` подставляет хост сервиса, его заголовки и `skipAuth`, если\n * сервис объявлен публичным. Заданный у запроса `baseUrl` не трогает.\n *\n * Стоит снаружи повторов и авторизации, чтобы выставленный здесь `skipAuth` был ей виден.\n */\nexport function createServicesMiddleware(registry: ServiceRegistry): RequestMiddleware {\n return async (request, next) => {\n const service = request.service === undefined ? undefined : registry.require(request.service);\n let prepared = request;\n\n if (request.baseUrl !== undefined) {\n const baseUrl = normalizeBaseUrl(request.baseUrl);\n if (baseUrl !== request.baseUrl) prepared = { ...prepared, baseUrl };\n\n // Разовый хост не наследует разрешение авторизации от сервиса с другим URL.\n // Явное `skipAuth: false` остаётся способом осознанно отправить токен наружу.\n const matchesService = service?.baseUrl === baseUrl;\n const mayAuthorize = matchesService\n ? service.auth !== false\n : registry.isPrimarySite(baseUrl);\n if (!mayAuthorize && prepared.skipAuth === undefined) {\n prepared = { ...prepared, skipAuth: true };\n }\n }\n\n if (!service) return next(prepared);\n\n if (prepared.baseUrl === undefined) prepared = { ...prepared, baseUrl: service.baseUrl };\n\n if (service.headers) prepared = withLayerHeaders(prepared, service.headers);\n if (service.auth === false && prepared.skipAuth === undefined) {\n prepared = { ...prepared, skipAuth: true };\n }\n\n return next(prepared);\n };\n}\n\n/** Что нужно слою авторизации. */\nexport interface AuthMiddlewareDeps {\n /** Заголовки авторизации для очередного запроса. Пустой объект, если токена нет. */\n getAuthHeaders: () => Promise<Record<string, string>> | Record<string, string>;\n /** Реакция на `401`. Возвращает `true`, если токен обновлён и повтор имеет смысл. */\n onUnauthorized: () => Promise<boolean>;\n /** Обновлять ли токен при `401` автоматически. */\n autoRefresh: boolean;\n}\n\nasync function applyAuth(\n request: PipelineRequest,\n deps: AuthMiddlewareDeps,\n): Promise<PipelineRequest> {\n if (request.skipAuth) return request;\n\n const headers = await deps.getAuthHeaders();\n return Object.keys(headers).length > 0 ? withLayerHeaders(request, headers) : request;\n}\n\n/**\n * Слой авторизации.\n *\n * Подставляет заголовок `Authorization` и обрабатывает `401`: обновляет токен и повторяет\n * запрос ровно один раз. Стоит внутри повторов, поэтому обычным попыткам он не виден —\n * они уже работают со свежим токеном.\n */\nexport function createAuthMiddleware(deps: AuthMiddlewareDeps): RequestMiddleware {\n return async (request, next) => {\n const authorized = await applyAuth(request, deps);\n\n try {\n return await next(authorized);\n } catch (error) {\n // Обновляем и повторяем ровно один раз, чтобы не зациклиться, если сервер\n // отдаёт 401 и на свежем токене.\n if (\n request.skipAuthRefresh ||\n !deps.autoRefresh ||\n !isItdApiError(error) ||\n error.status !== 401\n ) {\n throw error;\n }\n\n const refreshed = await deps.onUnauthorized();\n if (!refreshed) throw error;\n\n const retried = await applyAuth({ ...request, skipAuthRefresh: true }, deps);\n return next(retried);\n }\n };\n}\n\n/** Что нужно слою повторов. */\nexport interface RetryMiddlewareDeps {\n clock?: ItdClock;\n /** Глобальные настройки повторов. `undefined` — по умолчанию не повторять. */\n retry: ResolvedRetryOptions | undefined;\n /**\n * Паузы перед повторами при ответе `429`.\n *\n * Живут отдельно от `retry`: сервер не присылает `Retry-After`, и экспоненциальный откат\n * тут бесполезен — окно измеряется десятками секунд. Не зависят от `retry.attempts`.\n */\n rateLimitDelays: readonly number[];\n /**\n * Придерживает очередь запроса на паузу `429`. `undefined`, если очереди нет.\n *\n * Тормозится очередь того хоста, который ответил отказом: лимит у каждого свой.\n */\n pauseQueue: ((ms: number, request: PipelineRequest) => void) | undefined;\n hooks: ClientHooks;\n logger: Logger | undefined;\n buildUrl: (request: PipelineRequest) => string;\n}\n\n/**\n * Выбирает планировщик отката для конкретного запроса.\n *\n * `retry` у запроса переопределяет глобальную настройку: `false` выключает повторы,\n * объект задаёт свои. Обработка `429` от этого не зависит — она общая.\n */\nfunction resolveBackoff(\n retry: RequestOptions['retry'],\n global: RetryScheduler | undefined,\n): RetryScheduler | undefined {\n if (retry === undefined) return global;\n if (retry === false) return undefined;\n\n const resolved = resolveRetry(retry);\n return resolved ? createRetryScheduler(resolved) : undefined;\n}\n\n/**\n * Слой повторов.\n *\n * Ответ `429` обрабатывается отдельно от прочих ошибок лестницей пауз и с придержанием\n * всей очереди; сетевые сбои и `5xx` — экспоненциальным откатом. Настройка `retry`\n * у отдельного запроса имеет приоритет над глобальной.\n */\nexport function createRetryMiddleware(deps: RetryMiddlewareDeps): RequestMiddleware {\n const globalScheduler = deps.retry ? createRetryScheduler(deps.retry) : undefined;\n\n const nextDelay = (\n error: unknown,\n attempt: number,\n request: PipelineRequest,\n method: string,\n backoff: RetryScheduler | undefined,\n ): number | undefined => {\n if (isItdRateLimitError(error)) {\n // Пауза, названную сервером, соблюдаем точно; иначе берём очередной шаг лестницы.\n const wait = error.retryAfter ?? deps.rateLimitDelays[attempt - 1];\n if (wait === undefined) return undefined;\n\n deps.pauseQueue?.(wait, request);\n deps.logger?.debug(`лимит частоты, попытка ${attempt + 1} через ${wait} мс`);\n return wait;\n }\n\n return backoff?.(error, attempt, method, request.retryNetworkWrite ?? false);\n };\n\n return async (request, next) => {\n const method = request.method.toUpperCase();\n const backoff = resolveBackoff(request.retry, globalScheduler);\n\n for (let attempt = 1; ; attempt++) {\n try {\n return await next({ ...request, attempt });\n } catch (error) {\n const delay = nextDelay(error, attempt, request, method, backoff);\n if (delay === undefined) throw error;\n\n await dispatchRequestHook(\n deps.hooks,\n 'onRetry',\n {\n method,\n path: request.path,\n url: deps.buildUrl(request),\n // Умолчания транспорта добавляются после слоя повторов и сюда не входят.\n headers: new Headers({ ...request.layerHeaders, ...request.headers }),\n attempt,\n error,\n delay,\n },\n request,\n );\n\n deps.logger?.debug(\n `повтор ${method} ${request.path}, попытка ${attempt + 1} через ${delay} мс`,\n );\n\n await sleep(deps.clock ?? systemClock, delay, request.signal);\n }\n }\n };\n}\n\n/** Собирает обработчик из слоёв. Реэкспорт для удобства сборки в одном месте. */\nexport { composePipeline } from './pipeline.js';\nexport type { RequestHandler, RequestMiddleware };\n","import type { ClientHooks } from '../../types/options.js';\nimport { ItdConfigError } from '../errors.js';\nimport type { ItdPlugin } from './contracts.js';\n\nconst RESERVED_OPTION_KEYS: ReadonlySet<string> = new Set([\n 'signal',\n 'timeout',\n 'headers',\n 'retry',\n 'method',\n 'path',\n 'service',\n 'baseUrl',\n 'query',\n 'body',\n 'skipAuth',\n 'skipAuthRefresh',\n 'skipQueue',\n 'raw',\n]);\n\nconst RELATION_FIELDS = ['requires', 'conflicts', 'before', 'after'] as const;\nconst HOOK_FIELDS = ['onRequest', 'onResponse', 'onError', 'onRetry'] as const;\n\nfunction validateNameList(plugin: ItdPlugin, field: (typeof RELATION_FIELDS)[number]): void {\n const values = plugin[field];\n if (values === undefined) return;\n if (!Array.isArray(values)) {\n throw new ItdConfigError(`плагин «${plugin.name}»: ${field} должен быть массивом`);\n }\n\n const seen = new Set<string>();\n for (const value of values as readonly unknown[]) {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new ItdConfigError(`плагин «${plugin.name}»: ${field} содержит пустое имя плагина`);\n }\n if (value === plugin.name) {\n throw new ItdConfigError(`плагин «${plugin.name}» не может указать себя в ${field}`);\n }\n if (seen.has(value)) {\n throw new ItdConfigError(`плагин «${plugin.name}»: ${field} повторяет имя «${value}»`);\n }\n seen.add(value);\n }\n}\n\n/** Проверяет набор hooks, переданный плагином. @internal */\nexport function validatePluginHooks(plugin: string, hooks: ClientHooks): void {\n if (typeof hooks !== 'object' || hooks === null) {\n throw new ItdConfigError(`плагин «${plugin}» передал в useHooks() не объект`);\n }\n for (const field of HOOK_FIELDS) {\n if (hooks[field] !== undefined && typeof hooks[field] !== 'function') {\n throw new ItdConfigError(`плагин «${plugin}»: useHooks().${field} должен быть функцией`);\n }\n }\n}\n\n/**\n * Проверяет описание плагина без его установки.\n *\n * Нужна не только {@link PluginRegistry}: контейнер аккаунтов обязан отклонять сломанный\n * плагин сразу, даже когда внутри ещё нет ни одного клиента, которому можно поручить\n * полноценную установку.\n *\n * @internal\n */\nexport function validatePluginDefinition(plugin: ItdPlugin): void {\n if (typeof plugin?.install !== 'function') {\n throw new ItdConfigError('Плагин должен быть объектом с методом install()');\n }\n\n const name = plugin.name;\n if (typeof name !== 'string' || name.trim() === '') {\n throw new ItdConfigError('У плагина должно быть непустое имя');\n }\n\n const keys = plugin.optionKeys ?? [];\n if (!Array.isArray(keys)) {\n throw new ItdConfigError(`плагин «${name}»: optionKeys должен быть массивом`);\n }\n for (const key of keys as readonly unknown[]) {\n if (typeof key !== 'string' || key.trim() === '') {\n throw new ItdConfigError(`Плагин «${name}» заявил пустое имя опции`);\n }\n if (RESERVED_OPTION_KEYS.has(key)) {\n throw new ItdConfigError(\n `Плагин «${name}» заявил опцию «${key}»: это поле запроса, имя занято. ` +\n `Занятые имена: ${[...RESERVED_OPTION_KEYS].join(', ')}`,\n );\n }\n }\n\n for (const field of RELATION_FIELDS) validateNameList(plugin, field);\n}\n\ninterface OrderedPlugin {\n plugin: ItdPlugin;\n sequence: number;\n}\n\nfunction addEdge(\n from: string,\n to: string,\n edges: Map<string, Set<string>>,\n indegree: Map<string, number>,\n): void {\n const targets = edges.get(from);\n if (!targets || targets.has(to)) return;\n targets.add(to);\n indegree.set(to, (indegree.get(to) ?? 0) + 1);\n}\n\n/**\n * Проверяет зависимости и возвращает плагины в порядке обёрток: внешний идёт раньше.\n *\n * @internal\n */\nexport function orderPluginDefinitions(plugins: readonly ItdPlugin[]): ItdPlugin[] {\n const entries: OrderedPlugin[] = [];\n const byName = new Map<string, OrderedPlugin>();\n\n for (const [sequence, plugin] of plugins.entries()) {\n validatePluginDefinition(plugin);\n if (byName.has(plugin.name)) {\n throw new ItdConfigError(`плагин «${plugin.name}» уже подключён`);\n }\n const entry = { plugin, sequence };\n entries.push(entry);\n byName.set(plugin.name, entry);\n }\n\n for (const { plugin } of entries) {\n for (const required of plugin.requires ?? []) {\n if (!byName.has(required)) {\n throw new ItdConfigError(`плагину «${plugin.name}» требуется плагин «${required}»`);\n }\n }\n for (const conflict of plugin.conflicts ?? []) {\n if (byName.has(conflict)) {\n throw new ItdConfigError(`плагин «${plugin.name}» несовместим с плагином «${conflict}»`);\n }\n }\n for (const { plugin: other } of entries) {\n if (other.conflicts?.includes(plugin.name)) {\n throw new ItdConfigError(`плагин «${plugin.name}» несовместим с плагином «${other.name}»`);\n }\n }\n }\n\n const edges = new Map<string, Set<string>>();\n const indegree = new Map<string, number>();\n for (const { plugin } of entries) {\n edges.set(plugin.name, new Set());\n indegree.set(plugin.name, 0);\n }\n for (const { plugin } of entries) {\n for (const required of plugin.requires ?? []) addEdge(required, plugin.name, edges, indegree);\n for (const target of plugin.before ?? []) {\n if (byName.has(target)) addEdge(plugin.name, target, edges, indegree);\n }\n for (const target of plugin.after ?? []) {\n if (byName.has(target)) addEdge(target, plugin.name, edges, indegree);\n }\n }\n\n const ready = entries.filter(({ plugin }) => indegree.get(plugin.name) === 0);\n ready.sort((a, b) => a.sequence - b.sequence);\n const ordered: ItdPlugin[] = [];\n while (ready.length > 0) {\n const current = ready.shift();\n if (!current) break;\n ordered.push(current.plugin);\n for (const target of edges.get(current.plugin.name) ?? []) {\n const next = (indegree.get(target) ?? 0) - 1;\n indegree.set(target, next);\n if (next === 0) {\n const entry = byName.get(target);\n if (entry) {\n ready.push(entry);\n ready.sort((a, b) => a.sequence - b.sequence);\n }\n }\n }\n }\n\n if (ordered.length !== entries.length) {\n const cycle = entries\n .filter(({ plugin }) => (indegree.get(plugin.name) ?? 0) > 0)\n .map(({ plugin }) => plugin.name);\n throw new ItdConfigError(`циклический порядок плагинов: ${cycle.join(' → ')}`);\n }\n return ordered;\n}\n\n/** Проверяет, можно ли удалить плагин без нарушения обязательных зависимостей. @internal */\nexport function assertPluginRemovable(plugins: readonly ItdPlugin[], name: string): void {\n const dependent = plugins.find((plugin) => plugin.requires?.includes(name));\n if (dependent) {\n throw new ItdConfigError(\n `нельзя отключить плагин «${name}»: от него зависит «${dependent.name}»`,\n );\n }\n}\n","import type { ClientHooks, RawRequestOptions } from '../../types/options.js';\nimport { ItdConfigError } from '../errors.js';\nimport type { ItdPlugin, PluginContext, PluginTeardown, Transformer } from './contracts.js';\nimport {\n createRequestHooks,\n type HookContext,\n type HookName,\n requestHookScope,\n withRequestHookScope,\n} from './hooks.js';\nimport {\n assertPluginRemovable,\n orderPluginDefinitions,\n validatePluginDefinition,\n validatePluginHooks,\n} from './order.js';\n\nconst NO_KEYS: ReadonlySet<string> = new Set<string>();\n\ninterface InstalledPlugin {\n plugin: ItdPlugin;\n transformers: Transformer[];\n hooks: ClientHooks[];\n teardown: PluginTeardown | undefined;\n activeRequests: number;\n drain: Promise<void> | undefined;\n finishDrain: (() => void) | undefined;\n}\n\n/**\n * Список подключённых плагинов и собранная из них цепочка обёрток.\n *\n * Живёт в клиенте, а работает в транспорте: {@link HttpClient} прогоняет через `run`\n * каждый запрос, если плагины есть.\n */\nexport class PluginRegistry {\n readonly #entries = new Map<string, InstalledPlugin>();\n readonly #optionKeys = new Set<string>();\n readonly #removing = new Set<string>();\n readonly #cleanups = new Set<Promise<void>>();\n #ordered: InstalledPlugin[] = [];\n\n /** Сколько плагинов подключено. */\n get size(): number {\n return this.#entries.size;\n }\n\n /** Имена опций активных плагинов. */\n get optionKeys(): ReadonlySet<string> {\n return this.#optionKeys.size === 0 ? NO_KEYS : this.#optionKeys;\n }\n\n /** Имена плагинов в фактическом порядке выполнения. */\n names(): string[] {\n return this.#ordered.map(({ plugin }) => plugin.name);\n }\n\n /** Подключён ли плагин с таким именем. */\n has(name: string): boolean {\n return this.#entries.has(name);\n }\n\n /** Проверяет добавление без вызова `install()`. @internal */\n assertCanAdd(plugin: ItdPlugin): void {\n validatePluginDefinition(plugin);\n if (this.#removing.has(plugin.name)) {\n throw new ItdConfigError(\n `плагин «${plugin.name}» ещё отключается; дождитесь завершения unuse() или dispose()`,\n );\n }\n orderPluginDefinitions([...this.#ordered.map((entry) => entry.plugin), plugin]);\n }\n\n /** Проверяет удаление без изменения реестра. @internal */\n assertCanRemove(name: string): void {\n const entry = this.#entries.get(name);\n if (!entry) return;\n assertPluginRemovable(\n this.#ordered.map((current) => current.plugin),\n name,\n );\n }\n\n /**\n * Подключает плагин.\n *\n * @throws {ItdConfigError} если плагин задан неверно, уже подключён, нарушает зависимости\n * или заявил занятое имя опции\n */\n add(plugin: ItdPlugin, context: Omit<PluginContext, 'use' | 'useHooks'>): void {\n this.assertCanAdd(plugin);\n const ordered = orderPluginDefinitions([...this.#ordered.map((entry) => entry.plugin), plugin]);\n const transformers: Transformer[] = [];\n const hooks: ClientHooks[] = [];\n const installed = plugin.install({\n ...context,\n use: (transformer) => {\n if (typeof transformer !== 'function') {\n throw new ItdConfigError(`плагин «${plugin.name}» передал в use() не функцию`);\n }\n transformers.push(transformer);\n },\n useHooks: (value) => {\n validatePluginHooks(plugin.name, value);\n hooks.push({ ...value });\n },\n });\n const teardown = typeof installed === 'function' ? (installed as PluginTeardown) : undefined;\n\n this.#entries.set(plugin.name, {\n plugin,\n transformers,\n hooks,\n teardown,\n activeRequests: 0,\n drain: undefined,\n finishDrain: undefined,\n });\n this.#ordered = ordered.map((definition) => {\n const entry = this.#entries.get(definition.name);\n if (!entry) throw new ItdConfigError(`плагин «${definition.name}» не установлен`);\n return entry;\n });\n this.#rebuildOptionKeys();\n }\n\n /**\n * Отключает плагин и вызывает его функцию очистки.\n *\n * Новые запросы перестают видеть плагин сразу. Если его обёртка уже выполняется,\n * очистка дождётся завершения этого логического запроса.\n *\n * @returns `false`, если такого плагина не было\n */\n async remove(name: string): Promise<boolean> {\n const entry = this.#entries.get(name);\n if (!entry) return false;\n this.assertCanRemove(name);\n this.#entries.delete(name);\n this.#ordered = this.#ordered.filter((current) => current !== entry);\n this.#rebuildOptionKeys();\n this.#removing.add(name);\n\n const cleanup = this.#trackCleanup(\n (async () => {\n await this.#waitForDrain(entry);\n await entry.teardown?.();\n })(),\n );\n try {\n await cleanup;\n return true;\n } finally {\n this.#removing.delete(name);\n }\n }\n\n /**\n * Отключает все плагины окончательно.\n *\n * Очистка идёт изнутри наружу — в порядке, обратном выполнению обёрток.\n */\n async dispose(): Promise<void> {\n const entries = [...this.#ordered].reverse();\n const previousCleanups = [...this.#cleanups];\n this.#entries.clear();\n this.#ordered = [];\n this.#optionKeys.clear();\n for (const { plugin } of entries) this.#removing.add(plugin.name);\n\n const cleanup = this.#trackCleanup(\n (async () => {\n const errors: unknown[] = [];\n const previous = await Promise.allSettled(previousCleanups);\n for (const result of previous) {\n if (result.status === 'rejected') errors.push(result.reason);\n }\n for (const entry of entries) {\n try {\n await this.#waitForDrain(entry);\n await entry.teardown?.();\n } catch (error) {\n errors.push(error);\n } finally {\n this.#removing.delete(entry.plugin.name);\n }\n }\n if (errors.length > 0) {\n throw new AggregateError(errors, 'Не удалось освободить ресурсы плагинов');\n }\n })(),\n );\n await cleanup;\n }\n\n /**\n * Объединяет конструкторские хуки с хуками подключаемых плагинов.\n *\n * Возвращённый объект динамический: подключение и отключение плагина начинает действовать\n * со следующего логического запроса без пересоздания транспорта.\n */\n hooks(base: ClientHooks): ClientHooks {\n return createRequestHooks((field, context, request) =>\n this.#runHook(field, context, request, base),\n );\n }\n\n /**\n * Прогоняет запрос через цепочку обёрток.\n *\n * Снимок цепочки берётся в начале: `unuse()` влияет на новые запросы, но не обрывает\n * уже выполняющийся посередине.\n *\n * @param execute настоящий запрос, вызывается самой внутренней обёрткой\n */\n async run(\n request: RawRequestOptions,\n execute: (request: RawRequestOptions) => Promise<unknown>,\n ): Promise<unknown> {\n const entries = [...this.#ordered];\n for (const entry of entries) entry.activeRequests += 1;\n const hookScope = entries.flatMap((entry) => entry.hooks);\n const scoped = (current: RawRequestOptions): RawRequestOptions =>\n withRequestHookScope(current, hookScope);\n const chain = entries\n .flatMap((entry) => entry.transformers)\n .reduceRight<(request: RawRequestOptions) => Promise<unknown>>(\n (next, transformer) => (current) =>\n transformer(scoped(current), (prepared) => next(scoped(prepared))),\n (current) => execute(scoped(current)),\n );\n\n try {\n return await chain(scoped(request));\n } finally {\n for (const entry of entries) {\n entry.activeRequests -= 1;\n if (entry.activeRequests === 0) {\n entry.finishDrain?.();\n entry.finishDrain = undefined;\n entry.drain = undefined;\n }\n }\n }\n }\n\n #rebuildOptionKeys(): void {\n this.#optionKeys.clear();\n for (const { plugin } of this.#ordered) {\n for (const key of plugin.optionKeys ?? []) this.#optionKeys.add(key);\n }\n }\n\n #waitForDrain(entry: InstalledPlugin): Promise<void> {\n if (entry.activeRequests === 0) return Promise.resolve();\n entry.drain ??= new Promise<void>((resolve) => {\n entry.finishDrain = resolve;\n });\n return entry.drain;\n }\n\n #trackCleanup(cleanup: Promise<void>): Promise<void> {\n this.#cleanups.add(cleanup);\n void cleanup.then(\n () => this.#cleanups.delete(cleanup),\n () => this.#cleanups.delete(cleanup),\n );\n return cleanup;\n }\n\n async #runHook<K extends HookName>(\n field: K,\n context: HookContext<K>,\n request: RawRequestOptions,\n base: ClientHooks,\n ): Promise<void> {\n const baseHook = base[field] as ((value: HookContext<K>) => void | Promise<void>) | undefined;\n await baseHook?.(context);\n for (const hooks of requestHookScope(request)) {\n const hook = hooks[field] as ((value: HookContext<K>) => void | Promise<void>) | undefined;\n await hook?.(context);\n }\n }\n}\n","import { type ItdClock, systemClock } from './clock.js';\nimport type { ResolvedRateLimitOptions } from './config.js';\nimport { ItdAbortError } from './errors.js';\n\n/** Задача, ожидающая своей очереди. */\ninterface QueuedTask {\n run: () => void;\n /** Снимает задачу, ещё не начавшую выполняться, — используется при остановке очереди. */\n cancel: (reason: unknown) => void;\n}\n\n/** Ошибка отмены запроса, который ещё не дошёл до транспорта. */\nfunction queueAbortError(): ItdAbortError {\n return new ItdAbortError('Запрос отменён во время ожидания очереди');\n}\n\n/**\n * Очередь запросов: ограничивает одновременность и частоту.\n *\n * Нужна прежде всего ботам: без неё цикл по сотне постов уходит в API одним залпом\n * и упирается в `RATE_LIMIT_EXCEEDED`.\n *\n * Частота выдерживается равномерным разносом стартов (`1000 / rps` между запросами),\n * а не окном со счётчиком: так нагрузка ровная, без всплеска в начале каждой секунды.\n *\n * @internal\n */\nexport class RequestQueue {\n readonly #concurrency: number;\n /** Минимальный промежуток между стартами, мс. `0` — без ограничения частоты. */\n readonly #minGap: number;\n\n readonly #waiting: QueuedTask[] = [];\n #active = 0;\n /** Момент, раньше которого следующий запрос стартовать не должен. */\n #nextSlot = 0;\n readonly #clock: ItdClock;\n #cancelTimer: (() => void) | undefined;\n\n constructor(options: ResolvedRateLimitOptions, clock: ItdClock = systemClock) {\n this.#concurrency = options.concurrency;\n this.#minGap = options.rps ? 1000 / options.rps : 0;\n this.#clock = clock;\n }\n\n /** Сколько задач выполняется прямо сейчас. */\n get active(): number {\n return this.#active;\n }\n\n /** Сколько задач ждёт очереди. */\n get pending(): number {\n return this.#waiting.length;\n }\n\n /**\n * Ставит задачу в очередь.\n *\n * @returns результат задачи; ошибка задачи пробрасывается без изменений\n */\n schedule<T>(task: () => Promise<T>, signal?: AbortSignal): Promise<T> {\n if (signal?.aborted) return Promise.reject(queueAbortError());\n\n return new Promise<T>((resolve, reject) => {\n let queued: QueuedTask;\n\n const detach = () => signal?.removeEventListener('abort', onAbort);\n const onAbort = () => {\n const index = this.#waiting.indexOf(queued);\n if (index < 0) return;\n\n this.#waiting.splice(index, 1);\n queued.cancel(queueAbortError());\n this.#drain();\n };\n\n queued = {\n run: () => {\n detach();\n this.#active += 1;\n\n // `task` обычно возвращает промис, но пользовательский middleware может бросить\n // синхронно. Нормализуем оба пути, чтобы слот всегда освободился.\n Promise.resolve()\n .then(task)\n .then(resolve, reject)\n .finally(() => {\n this.#active -= 1;\n this.#drain();\n });\n },\n cancel: (reason) => {\n detach();\n reject(reason);\n },\n };\n\n this.#waiting.push(queued);\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Защищает и от нестандартной реализации AbortSignal, которая могла перейти\n // в aborted между первой проверкой и установкой обработчика.\n if (signal?.aborted) onAbort();\n else this.#drain();\n });\n }\n\n /**\n * Останавливает очередь: снимает отложенную паузу и отклоняет ещё не начатые задачи\n * ошибкой `ItdAbortError`. Уже выполняющиеся задачи доводятся до конца.\n */\n stop(): void {\n if (this.#cancelTimer) {\n this.#cancelTimer();\n this.#cancelTimer = undefined;\n }\n this.#nextSlot = 0;\n\n const pending = this.#waiting.splice(0, this.#waiting.length);\n for (const task of pending) {\n task.cancel(new ItdAbortError('Клиент закрыт, запрос отменён'));\n }\n }\n\n /**\n * Придерживает всю очередь на заданное время.\n *\n * Вызывается при получении `429` с заголовком `Retry-After`: тормозить нужно все запросы,\n * а не только тот, который наткнулся на лимит, — иначе остальные продолжат добивать API.\n */\n pause(ms: number): void {\n if (ms <= 0) return;\n this.#nextSlot = Math.max(this.#nextSlot, this.#clock.now() + ms);\n }\n\n /** Запускает столько ожидающих задач, сколько позволяют ограничения. */\n #drain(): void {\n if (this.#waiting.length === 0) {\n // Последний ожидающий запрос мог быть отменён во время длинной паузы. Таймер больше\n // не нужен и не должен удерживать event loop процесса.\n if (this.#cancelTimer) {\n this.#cancelTimer();\n this.#cancelTimer = undefined;\n }\n return;\n }\n if (this.#active >= this.#concurrency) return;\n // Ждём уже запланированного пробуждения, чтобы не плодить таймеры.\n if (this.#cancelTimer) return;\n\n const now = this.#clock.now();\n\n if (this.#nextSlot > now) {\n this.#cancelTimer = this.#clock.schedule(() => {\n this.#cancelTimer = undefined;\n this.#drain();\n }, this.#nextSlot - now);\n return;\n }\n\n const next = this.#waiting.shift();\n if (!next) return;\n\n if (this.#minGap > 0) this.#nextSlot = now + this.#minGap;\n\n next.run();\n\n // Следующая задача может стартовать сразу, если позволяет конкурентность.\n this.#drain();\n }\n}\n\n/**\n * Очереди по хостам: основная и по одной на каждый сервис платформы.\n *\n * @internal\n */\nexport class RequestQueuePool {\n readonly #options: ResolvedRateLimitOptions;\n readonly #clock: ItdClock;\n readonly #main: RequestQueue;\n /** Очереди сервисов заводятся при первом запросе — обычно не нужна ни одна. */\n readonly #byService = new Map<string, RequestQueue>();\n\n constructor(options: ResolvedRateLimitOptions, clock: ItdClock = systemClock) {\n this.#options = options;\n this.#clock = clock;\n this.#main = new RequestQueue(options, clock);\n }\n\n /** Очередь хоста. */\n for(service: string | undefined): RequestQueue {\n if (service === undefined) return this.#main;\n\n let queue = this.#byService.get(service);\n if (!queue) {\n queue = new RequestQueue(this.#options, this.#clock);\n this.#byService.set(service, queue);\n }\n return queue;\n }\n\n /** Останавливает все очереди. */\n stop(): void {\n this.#main.stop();\n for (const queue of this.#byService.values()) queue.stop();\n }\n}\n","import { ItdConfigError } from './errors.js';\nimport { normalizeBaseUrl } from './url.js';\n\n/** Сервис платформы на отдельном домене. */\nexport interface ServiceDefinition {\n /** Имя, по которому запрос выбирает сервис: `{ service: 'status' }`. */\n name: string;\n /** Базовый URL сервиса. */\n baseUrl: string;\n /** Заголовки, добавляемые к каждому запросу сервиса. Заголовки вызова важнее. */\n headers?: Record<string, string> | undefined;\n /**\n * Слать ли заголовок авторизации.\n *\n * По умолчанию включено для основного хоста и его поддоменов. Для остальных хостов\n * авторизацию нужно разрешить явно: `auth: true`.\n */\n auth?: boolean | undefined;\n}\n\n/** Хост из URL. Пустая строка, если разобрать не удалось. */\nfunction hostOf(url: string): string {\n try {\n return new URL(url).hostname.toLowerCase();\n } catch {\n return '';\n }\n}\n\n/** Тот же хост либо его поддомен. */\nfunction isSameSite(primaryHost: string, host: string): boolean {\n if (!primaryHost || !host) return false;\n return host === primaryHost || host.endsWith(`.${primaryHost}`);\n}\n\n/**\n * Именованные сервисы клиента.\n *\n * @internal\n */\nexport class ServiceRegistry {\n readonly #services = new Map<string, ServiceDefinition>();\n /** Хост основного API. */\n readonly #primaryHost: string;\n\n /** @param primaryBaseUrl базовый URL клиента */\n constructor(primaryBaseUrl?: string) {\n this.#primaryHost = primaryBaseUrl ? hostOf(primaryBaseUrl) : '';\n }\n\n /**\n * Регистрирует сервис. Имя очищается от краевых пробелов, базовый URL приводится\n * к каноничному виду, а незаданный `auth` выводится из хоста.\n *\n * @throws {ItdConfigError} если имя пустое, имя занято или `baseUrl` не абсолютный URL\n */\n define(definition: ServiceDefinition): void {\n const raw = definition?.name;\n if (typeof raw !== 'string' || raw.trim() === '') {\n throw new ItdConfigError('У сервиса должно быть непустое имя');\n }\n\n const name = raw.trim();\n if (this.#services.has(name)) {\n throw new ItdConfigError(`Сервис «${name}» уже зарегистрирован и не может быть заменён`);\n }\n\n const baseUrl = normalizeBaseUrl(definition.baseUrl);\n if (definition.auth !== undefined && typeof definition.auth !== 'boolean') {\n throw new ItdConfigError(`services.${name}.auth должен быть boolean`);\n }\n\n let headers: Readonly<Record<string, string>> | undefined;\n if (definition.headers !== undefined) {\n if (\n typeof definition.headers !== 'object' ||\n definition.headers === null ||\n Array.isArray(definition.headers)\n ) {\n throw new ItdConfigError(`services.${name}.headers должен быть объектом строк`);\n }\n for (const [header, value] of Object.entries(definition.headers)) {\n if (typeof value !== 'string') {\n throw new ItdConfigError(`services.${name}.headers.${header} должен быть строкой`);\n }\n }\n headers = Object.freeze({ ...definition.headers });\n }\n\n const normalized = {\n ...definition,\n name,\n baseUrl,\n auth: definition.auth ?? isSameSite(this.#primaryHost, hostOf(baseUrl)),\n ...(headers ? { headers } : {}),\n } satisfies ServiceDefinition;\n\n this.#services.set(name, Object.freeze(normalized));\n }\n\n /** Определение сервиса либо `undefined`, если такого нет. */\n get(name: string): ServiceDefinition | undefined {\n return this.#services.get(name);\n }\n\n /** Зарегистрирован ли сервис с таким именем. */\n has(name: string): boolean {\n return this.#services.has(name);\n }\n\n /**\n * Определение сервиса.\n *\n * @throws {ItdConfigError} если сервис не зарегистрирован\n */\n require(name: string): ServiceDefinition {\n const service = this.#services.get(name);\n if (!service) {\n const known = [...this.#services.keys()];\n throw new ItdConfigError(\n `Сервис «${name}» не зарегистрирован. ` +\n (known.length > 0\n ? `Известны: ${known.join(', ')}`\n : 'Зарегистрируйте его через itd.defineService() или опцию services'),\n );\n }\n return service;\n }\n\n /**\n * Базовый URL сервиса.\n *\n * @throws {ItdConfigError} если сервис не зарегистрирован\n */\n resolveBaseUrl(name: string): string {\n return this.require(name).baseUrl;\n }\n\n /**\n * Принадлежит ли URL основному хосту клиента или его поддомену.\n *\n * Используется для безопасного значения по умолчанию у разового `baseUrl`: Bearer-токен\n * не должен уходить на посторонний хост без явного `skipAuth: false`.\n *\n * @internal\n */\n isPrimarySite(baseUrl: string): boolean {\n return isSameSite(this.#primaryHost, hostOf(baseUrl));\n }\n}\n","import { isBlob } from './runtime.js';\n\n/** Заголовки, значение которых нельзя писать в лог целиком. */\nconst SECRET_HEADERS = new Set(['authorization', 'cookie', 'set-cookie', 'x-api-key']);\n\n/** Поля тела запроса, значение которых нельзя писать в лог. */\nconst SECRET_FIELDS = new Set([\n 'password',\n 'oldpassword',\n 'newpassword',\n 'accesstoken',\n 'refreshtoken',\n 'currentpassword',\n 'flowtoken',\n 'token',\n 'turnstiletoken',\n 'otp',\n]);\n\n/**\n * Прячет середину секрета, оставляя концы для сопоставления.\n *\n * @example\n * ```ts\n * maskSecret('eyJhbGciOiJIUzI1NiJ9.abc'); // 'eyJh…(24)…abc'\n * ```\n */\nexport function maskSecret(value: string): string {\n if (value.length <= 8) return '…';\n return `${value.slice(0, 4)}…(${value.length})…${value.slice(-3)}`;\n}\n\n/**\n * Готовит заголовки к записи в лог.\n *\n * `Authorization` и `Cookie` маскируются: логи часто уезжают в системы сбора,\n * и токен доступа в них попадать не должен.\n */\nexport function redactHeaders(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n\n headers.forEach((value, name) => {\n if (SECRET_HEADERS.has(name.toLowerCase())) {\n // У Authorization маскируем только сам токен, схему («Bearer») оставляем видимой.\n const spaceAt = value.indexOf(' ');\n result[name] =\n spaceAt > 0\n ? `${value.slice(0, spaceAt)} ${maskSecret(value.slice(spaceAt + 1))}`\n : maskSecret(value);\n return;\n }\n result[name] = value;\n });\n\n return result;\n}\n\n/**\n * Готовит тело запроса к записи в лог: пароли, токены и коды OTP заменяются заглушкой.\n *\n * Обходит вложенные объекты и массивы. `FormData` и бинарные тела не раскрываются вовсе.\n */\nexport function redactBody(body: unknown): unknown {\n if (body === null || body === undefined) return body;\n\n if (typeof FormData !== 'undefined' && body instanceof FormData) return '[FormData]';\n if (isBlob(body)) return '[Blob]';\n if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return '[binary]';\n\n if (Array.isArray(body)) return body.map(redactBody);\n\n if (typeof body === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(body)) {\n result[key] = SECRET_FIELDS.has(key.toLowerCase()) ? '[скрыто]' : redactBody(value);\n }\n return result;\n }\n\n return body;\n}\n","/**\n * Снимает внешнюю обёртку `{ data: … }`.\n *\n * Это единственное преобразование ответа, которое делает транспортный слой. Формы ответов у\n * итд.com непоследовательны: одни эндпоинты заворачивают полезную нагрузку в `data`, другие\n * отдают её напрямую (`GET /api/notifications/` → `{ notifications, hasMore }`), третьи кладут\n * список в поле по имени сущности. Угадывать имя поля опасно, поэтому его разбирает каждый\n * метод ресурса явно, а здесь снимается только однозначная обёртка.\n *\n * Обёртка считается обёрткой, лишь когда `data` — **единственный** ключ объекта. Ответ\n * `{ data: …, meta: … }` вернётся как есть.\n *\n * @example\n * ```ts\n * unwrapData({ data: { posts: [] } }); // { posts: [] }\n * unwrapData({ notifications: [], hasMore }); // без изменений\n * unwrapData({ data: [], hasMore: true }); // без изменений — ключей больше одного\n * ```\n */\nexport function unwrapData(body: unknown): unknown {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return body;\n\n const keys = Object.keys(body);\n if (keys.length !== 1 || keys[0] !== 'data') return body;\n\n return (body as { data: unknown }).data;\n}\n\n/**\n * Обычный объект — не `null`, не массив.\n *\n * Живёт здесь, а не в каждом разборщике ответа: одна и та же проверка нужна и фабрике\n * ошибок, и приведению уведомлений.\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Непустая строка либо `undefined`. Отличается от {@link pickString} тем, что берёт значение, а не поле. */\nexport function asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * Достаёт поле-список из ответа, уже прошедшего {@link unwrapData}.\n *\n * Если поля нет или оно не массив, возвращается пустой массив: сервер иногда опускает\n * пустые коллекции, и падать из-за этого библиотека не должна.\n */\nexport function pickArray<T>(source: unknown, field: string): T[] {\n if (typeof source !== 'object' || source === null) return [];\n const value = (source as Record<string, unknown>)[field];\n return Array.isArray(value) ? (value as T[]) : [];\n}\n\n/** Достаёт объект по имени поля. Возвращает `undefined`, если это не объект. */\nexport function pickObject(source: unknown, field: string): Record<string, unknown> | undefined {\n if (typeof source !== 'object' || source === null) return undefined;\n const value = (source as Record<string, unknown>)[field];\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\n/** Достаёт булево поле. Возвращает `fallback`, если поля нет или тип не тот. */\nexport function pickBoolean(source: unknown, field: string, fallback = false): boolean {\n if (typeof source !== 'object' || source === null) return fallback;\n const value = (source as Record<string, unknown>)[field];\n return typeof value === 'boolean' ? value : fallback;\n}\n\n/** Достаёт числовое поле. Возвращает `fallback`, если поля нет или значение не конечное число. */\nexport function pickNumber(source: unknown, field: string, fallback: number): number {\n if (typeof source !== 'object' || source === null) return fallback;\n const value = (source as Record<string, unknown>)[field];\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n\n/** Достаёт строковое поле. Возвращает `undefined`, если поля нет, оно пустое или не строка. */\nexport function pickString(source: unknown, field: string): string | undefined {\n if (typeof source !== 'object' || source === null) return undefined;\n const value = (source as Record<string, unknown>)[field];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n","import type { ItdErrorCode } from '../types/enums.js';\nimport {\n type ItdApiError,\n ItdApiError as ItdApiErrorClass,\n ItdAuthError,\n ItdConflictError,\n type ItdFieldErrors,\n ItdForbiddenError,\n ItdNotFoundError,\n ItdPhoneVerificationError,\n ItdRateLimitError,\n ItdServerError,\n ItdValidationError,\n} from './errors.js';\nimport { redactBody } from './redact.js';\nimport { asString, isRecord } from './unwrap.js';\n\n/** Разобранное тело ошибки, приведённое к одной форме. */\nexport interface ParsedErrorBody {\n code: ItdErrorCode;\n message: string;\n detail: string | undefined;\n title: string | undefined;\n fieldErrors: ItdFieldErrors;\n /** Идентификатор пользователя, если сервер его вернул (нужен для ссылки подтверждения телефона). */\n userId: string | undefined;\n}\n\n/**\n * Сводит ошибки по полям к единой форме `{ поле: [сообщения] }`.\n *\n * Понимает две документированные формы:\n * - `errors: { email: ['…'] }` либо `errors: { email: '…' }`\n * - `violations: [{ field: 'email', message: '…' }]`\n */\nfunction collectFieldErrors(source: Record<string, unknown>): ItdFieldErrors {\n const result: ItdFieldErrors = {};\n\n const errors = source.errors;\n if (isRecord(errors)) {\n for (const [field, value] of Object.entries(errors)) {\n if (Array.isArray(value)) {\n const messages = value.filter((item): item is string => typeof item === 'string');\n if (messages.length > 0) result[field] = messages;\n } else if (typeof value === 'string') {\n result[field] = [value];\n }\n }\n }\n\n const violations = source.violations;\n if (Array.isArray(violations)) {\n for (const violation of violations) {\n if (!isRecord(violation)) continue;\n const field = asString(violation.field) ?? asString(violation.property);\n const message = asString(violation.message);\n if (!field || !message) continue;\n const existing = result[field];\n if (existing) existing.push(message);\n else result[field] = [message];\n }\n }\n\n return result;\n}\n\n/**\n * Разбирает тело ответа с ошибкой.\n *\n * API отдаёт ошибки в двух формах — с обёрткой `{ error: … }` и без неё, — а при сбое на\n * уровне прокси может вернуть вообще не JSON. Все три случая сводятся к одной структуре.\n *\n * @param body тело ответа: разобранный JSON, строка или `undefined`\n * @param status HTTP-статус, используется для сообщения по умолчанию\n * @param statusText текст статуса из ответа\n */\nexport function parseErrorBody(body: unknown, status: number, statusText = ''): ParsedErrorBody {\n const fallbackMessage = statusText ? `HTTP ${status} ${statusText}` : `HTTP ${status}`;\n\n // Не-JSON: HTML от прокси, простой текст, пустое тело.\n if (typeof body === 'string') {\n return {\n code: 'UNKNOWN_ERROR',\n message: asString(body.trim()) ?? fallbackMessage,\n detail: undefined,\n title: undefined,\n fieldErrors: {},\n userId: undefined,\n };\n }\n\n if (!isRecord(body)) {\n return {\n code: 'UNKNOWN_ERROR',\n message: fallbackMessage,\n detail: undefined,\n title: undefined,\n fieldErrors: {},\n userId: undefined,\n };\n }\n\n // Ответ вида `{ type: \"validation\", on: \"query\", found: {…} }` — так сервер сообщает\n // о непрошедшей проверке параметров. Ни кода, ни текста в нём нет.\n if (body.type === 'validation') {\n const target = asString(body.on);\n\n return {\n code: 'VALIDATION_ERROR',\n message: target\n ? `Проверка не пройдена: некорректные данные в «${target}»`\n : 'Проверка входных данных не пройдена',\n detail: undefined,\n title: undefined,\n fieldErrors: {},\n userId: undefined,\n };\n }\n\n // Форма `{ error: { code, message, … } }` — работаем с вложенным объектом,\n // но поля верхнего уровня остаются запасным источником.\n const inner = isRecord(body.error) ? body.error : body;\n\n const message =\n asString(inner.message) ??\n asString(inner.detail) ??\n asString(inner.title) ??\n // `{ \"error\": \"Invalid token\" }` — так отвечает сервер на недействительный токен.\n asString(body.error) ??\n fallbackMessage;\n\n return {\n code: (asString(inner.code) ?? asString(body.code) ?? 'UNKNOWN_ERROR') as ItdErrorCode,\n message,\n detail: asString(inner.detail),\n title: asString(inner.title),\n fieldErrors: { ...collectFieldErrors(body), ...collectFieldErrors(inner) },\n userId: asString(inner.userId) ?? asString(body.userId),\n };\n}\n\n/**\n * Переводит заголовок `Retry-After` в миллисекунды.\n *\n * Поддерживает обе формы из спецификации HTTP: число секунд и дату.\n * Возвращает `undefined`, если заголовка нет или он не разбирается.\n */\nexport function parseRetryAfter(\n header: string | null | undefined,\n now = Date.now(),\n): number | undefined {\n if (!header) return undefined;\n\n const seconds = Number(header);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n\n const date = Date.parse(header);\n if (Number.isFinite(date)) return Math.max(0, date - now);\n\n return undefined;\n}\n\n/** Читает целое число из заголовка. */\nfunction readIntHeader(headers: Headers | undefined, name: string): number | undefined {\n const raw = headers?.get(name);\n if (raw === null || raw === undefined) return undefined;\n\n const value = Number.parseInt(raw, 10);\n return Number.isFinite(value) ? value : undefined;\n}\n\n/**\n * Читает сведения об ограничении частоты.\n *\n * Сервер сообщает размер окна и остаток, но **не сообщает время сброса** — поэтому\n * точный момент, когда можно повторить, из ответа не вывести.\n */\nexport function readRateLimit(headers: Headers | undefined): {\n limit: number | undefined;\n remaining: number | undefined;\n} {\n return {\n limit: readIntHeader(headers, 'x-ratelimit-limit'),\n remaining: readIntHeader(headers, 'x-ratelimit-remaining'),\n };\n}\n\nconst REQUEST_ID_HEADERS = ['x-request-id', 'x-requestid', 'request-id', 'x-correlation-id'];\n\n/** Достаёт идентификатор запроса из заголовков ответа — пригодится при обращении в поддержку. */\nexport function getRequestId(headers: Headers | undefined): string | undefined {\n if (!headers) return undefined;\n for (const name of REQUEST_ID_HEADERS) {\n const value = headers.get(name);\n if (value) return value;\n }\n return undefined;\n}\n\n/** Соответствие кода ошибки конкретному классу. Приоритетнее, чем HTTP-статус. */\nconst CODE_TO_CLASS: Record<string, new (init: never) => ItdApiError> = {\n VALIDATION_ERROR: ItdValidationError,\n RATE_LIMIT_EXCEEDED: ItdRateLimitError,\n UNAUTHORIZED: ItdAuthError,\n SESSION_EXPIRED: ItdAuthError,\n SESSION_REVOKED: ItdAuthError,\n SESSION_INVALID_REFRESH_TOKEN: ItdAuthError,\n // Оба приходят с `/auth/refresh`: первый — когда cookie refresh_token не долетела,\n // второй — когда она есть, но сессия за ней уже мертва.\n REFRESH_TOKEN_MISSING: ItdAuthError,\n SESSION_NOT_FOUND: ItdAuthError,\n ACCOUNT_INVALID_CREDENTIALS: ItdAuthError,\n ACCESS_DENIED: ItdForbiddenError,\n ENTITY_NOT_FOUND: ItdNotFoundError,\n // Сервер отвечает именно так: `{ error: { code: 'NOT_FOUND', message: 'Post not found' } }`.\n NOT_FOUND: ItdNotFoundError,\n ENTITY_ALREADY_EXISTS: ItdConflictError,\n};\n\n/** Соответствие HTTP-статуса классу — запасной вариант, когда код ничего не говорит. */\nfunction classByStatus(status: number): new (init: never) => ItdApiError {\n if (status === 401) return ItdAuthError;\n if (status === 403) return ItdForbiddenError;\n if (status === 404) return ItdNotFoundError;\n if (status === 409) return ItdConflictError;\n if (status === 422) return ItdValidationError;\n if (status === 429) return ItdRateLimitError;\n if (status >= 500) return ItdServerError;\n return ItdApiErrorClass;\n}\n\n/**\n * Готовит тело ответа к сохранению в `ItdApiError.raw`.\n *\n * При `422` сервер возвращает присланное тело эхом: `{ type: 'validation', on: 'body',\n * found: { email: '…', password: '…' } }`. Ошибка обычно доезжает до логов и систем сбора\n * вроде Sentry — пароль в ней оказаться не должен. Имена полей для диагностики остаются,\n * значения секретов заменяются заглушкой.\n */\nfunction safeRawBody(body: unknown): unknown {\n if (!isRecord(body) || body.type !== 'validation' || !isRecord(body.found)) return body;\n\n return { ...body, found: redactBody(body.found) };\n}\n\n/** Что нужно знать о запросе, чтобы построить ошибку. */\nexport interface ErrorContext {\n method: string;\n path: string;\n status: number;\n /** Текущее время в миллисекундах с начала эпохи Unix. */\n now?: number | undefined;\n statusText?: string | undefined;\n headers?: Headers | undefined;\n response?: Response | undefined;\n /** Тело ответа: разобранный JSON либо строка. */\n body: unknown;\n}\n\n/**\n * Строит типизированную ошибку из ответа сервера.\n *\n * Класс выбирается сначала по коду ошибки, затем по HTTP-статусу — так `VALIDATION_ERROR`\n * со статусом `400` всё равно станет {@link ItdValidationError}.\n */\nexport function createApiError(context: ErrorContext): ItdApiError {\n const parsed = parseErrorBody(context.body, context.status, context.statusText);\n const rateLimit = readRateLimit(context.headers);\n\n const init = {\n rateLimit: rateLimit.limit,\n rateLimitRemaining: rateLimit.remaining,\n status: context.status,\n code: parsed.code,\n message: parsed.message,\n detail: parsed.detail,\n title: parsed.title,\n fieldErrors: parsed.fieldErrors,\n requestId: getRequestId(context.headers),\n method: context.method,\n path: context.path,\n raw: safeRawBody(context.body),\n response: context.response,\n retryAfter: parseRetryAfter(context.headers?.get('retry-after'), context.now),\n };\n\n if (parsed.code === 'PHONE_VERIFICATION_REQUIRED') {\n return new ItdPhoneVerificationError({ ...init, userId: parsed.userId });\n }\n\n const Ctor = CODE_TO_CLASS[parsed.code] ?? classByStatus(context.status);\n return new Ctor(init as never);\n}\n","import type { ClientHooks, Logger } from '../types/options.js';\nimport type { ItdClock } from './clock.js';\nimport type { CookieJar } from './cookies.js';\nimport { createApiError, readRateLimit } from './error-factory.js';\nimport {\n ItdAbortError,\n ItdConfigError,\n ItdError,\n ItdNetworkError,\n ItdTimeoutError,\n} from './errors.js';\nimport type { PipelineRequest, PreparedRequestBody } from './pipeline.js';\nimport { dispatchRequestHook } from './plugins/hooks.js';\nimport { redactBody, redactHeaders } from './redact.js';\nimport { isBlob } from './runtime.js';\nimport { unwrapData } from './unwrap.js';\nimport { buildQuery, joinUrl } from './url.js';\n\n/**\n * Настройки, нужные транспорту.\n *\n * Узкий срез общей конфигурации: авторизация, хранилище, повторы и очередь транспорта\n * не касаются, и он не должен иметь к ним доступ.\n */\nexport interface TransportConfig {\n baseUrl: string;\n fetch: typeof fetch;\n timeout: number;\n clock: ItdClock;\n headers: Record<string, string>;\n /** Значение заголовка `User-Agent`. `undefined` — заголовок не выставляется. */\n userAgent: string | undefined;\n useCookieJar: boolean;\n sendCredentials: boolean;\n hooks: ClientHooks;\n logger: Logger | undefined;\n}\n\n/**\n * Внешние части, которыми пользуется транспорт.\n *\n * Все три обязательны к передаче и все три могут отсутствовать по существу: cookie-jar\n * не нужен в браузере, идентификатор устройства — при выключенной авторизации, а сведения\n * об ограничении частоты интересны только тогда, когда есть очередь.\n */\nexport interface TransportDeps {\n /** Хранилище cookie. `undefined` — cookie ведёт сама среда. */\n cookies: CookieJar | undefined;\n /**\n * Идентификатор устройства для заголовка `X-Device-Id`. Отправляется на всех запросах,\n * включая анонимные (`sign-in`).\n */\n getDeviceId: (() => Promise<string>) | undefined;\n /**\n * Сообщает об остатке лимита из заголовков ответа.\n *\n * Вызывается после **каждого** ответа, включая ошибочные, — так очередь узнаёт\n * об исчерпании лимита заранее и успевает притормозить до отказа сервера.\n */\n onRateLimit:\n | ((limit: number | undefined, remaining: number | undefined, request: PipelineRequest) => void)\n | undefined;\n}\n\n/**\n * Ставит заголовок, превращая ограничение HTTP в понятное сообщение.\n *\n * Значения заголовков ограничены latin1, поэтому кириллица в них невозможна. Среда\n * сообщает об этом невнятным `Cannot convert argument to a ByteString`, что при русских\n * названиях приложений сбивает с толку.\n */\nfunction setHeader(headers: Headers, name: string, value: string): void {\n try {\n headers.set(name, value);\n } catch {\n throw new ItdConfigError(\n `Значение заголовка ${name} содержит символы вне latin1. HTTP-заголовки не могут ` +\n 'содержать кириллицу — закодируйте значение, например через encodeURIComponent.',\n );\n }\n}\n\n/** Тело, которое отправляется как есть, без сериализации в JSON. */\nfunction isRawBody(body: unknown): body is BodyInit {\n if (typeof body !== 'object' || body === null) return typeof body === 'string';\n return (\n (typeof FormData !== 'undefined' && body instanceof FormData) ||\n isBlob(body) ||\n (typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams) ||\n (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) ||\n body instanceof ArrayBuffer ||\n ArrayBuffer.isView(body)\n );\n}\n\n/**\n * Читает тело ответа один раз.\n *\n * Ответ можно прочитать только однократно, а тело нужно и при успехе, и при ошибке,\n * поэтому чтение происходит здесь, до ветвления по статусу.\n */\nasync function readBody(response: Response): Promise<unknown> {\n if (response.status === 204 || response.status === 205) return undefined;\n if (response.headers.get('content-length') === '0') return undefined;\n\n const contentType = response.headers.get('content-type') ?? '';\n\n if (contentType.includes('json')) {\n const text = await response.text();\n if (text === '') return undefined;\n try {\n return JSON.parse(text) as unknown;\n } catch {\n // Заголовок обещал JSON, но тело им не является — отдаём как текст,\n // чтобы сообщение об ошибке осталось информативным.\n return text;\n }\n }\n\n const text = await response.text();\n return text === '' ? undefined : text;\n}\n\n/** Ошибка отмены в формате `fetch`. */\nfunction createAbortError(): Error {\n const error = new Error('Операция прервана');\n error.name = 'AbortError';\n return error;\n}\n\n/** Прерывает ожидание промиса при срабатывании сигнала. */\nfunction abortable<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) {\n // Готовый промис может завершиться до отложенной ошибки отмены.\n return Promise.race([\n promise,\n new Promise<never>((_resolve, reject) => {\n setTimeout(() => reject(createAbortError()), 0);\n }),\n ]);\n }\n\n let onAbort: (() => void) | undefined;\n\n const interrupted = new Promise<never>((_resolve, reject) => {\n onAbort = () => reject(createAbortError());\n signal.addEventListener('abort', onAbort, { once: true });\n });\n\n return Promise.race([promise, interrupted]).finally(() => {\n if (onAbort) signal.removeEventListener('abort', onAbort);\n });\n}\n\n/** Результат объединения пользовательской отмены и таймаута. */\ninterface AbortBundle {\n signal: AbortSignal;\n /** Сработал ли именно таймаут — от этого зависит класс ошибки. */\n timedOut: () => boolean;\n cleanup: () => void;\n}\n\n/**\n * Объединяет пользовательский `AbortSignal` с таймаутом.\n *\n * Реализовано вручную, а не через `AbortSignal.any`: последний появился только в Node 20,\n * а библиотека поддерживает Node 18.\n */\nfunction createAbortBundle(\n userSignal: AbortSignal | undefined,\n timeout: number,\n clock: ItdClock,\n): AbortBundle {\n const controller = new AbortController();\n let timedOut = false;\n\n const onUserAbort = () => controller.abort(userSignal?.reason);\n\n if (userSignal) {\n if (userSignal.aborted) controller.abort(userSignal.reason);\n else userSignal.addEventListener('abort', onUserAbort, { once: true });\n }\n\n const cancelTimer =\n timeout > 0\n ? clock.schedule(() => {\n timedOut = true;\n controller.abort();\n }, timeout)\n : undefined;\n\n return {\n signal: controller.signal,\n timedOut: () => timedOut,\n cleanup: () => {\n cancelTimer?.();\n userSignal?.removeEventListener('abort', onUserAbort);\n },\n };\n}\n\n/**\n * Единственное место, откуда библиотека ходит в сеть.\n *\n * Отвечает за сборку URL, заголовки, cookie, таймауты, разбор ответа и превращение любой\n * неудачи в типизированную ошибку. Авторизация, повторы, очередь и плагины — отдельные\n * слои конвейера, и транспорт о них не знает.\n */\nexport class Transport {\n readonly #config: TransportConfig;\n readonly #deps: TransportDeps;\n\n constructor(config: TransportConfig, deps: TransportDeps) {\n this.#config = config;\n this.#deps = deps;\n }\n\n /** Базовый URL, к которому обращается транспорт. */\n get baseUrl(): string {\n return this.#config.baseUrl;\n }\n\n /**\n * Выполняет один сетевой запрос.\n *\n * @throws {ItdApiError} если сервер ответил статусом ≥ 400\n * @throws {ItdTimeoutError} если истёк таймаут\n * @throws {ItdAbortError} если запрос отменён через `signal`\n * @throws {ItdNetworkError} если запрос не дошёл до сервера\n */\n send = async (request: PipelineRequest): Promise<unknown> => {\n const method = request.method.toUpperCase();\n const url = this.buildUrl(request);\n const headers = await this.#buildHeaders(request, url);\n const attempt = request.attempt ?? 1;\n\n const timeout = request.timeout ?? this.#config.timeout;\n const abort = createAbortBundle(request.signal, timeout, this.#config.clock);\n const startedAt = this.#config.clock.now();\n let cleanupBody: (() => void | Promise<void>) | undefined;\n\n try {\n let body: BodyInit | undefined;\n try {\n const prepared = await this.#prepareBody(request, headers, abort.signal, attempt);\n body = prepared.body;\n cleanupBody = prepared.cleanup;\n } catch (error) {\n const failure = this.#toTransportError(error, abort, request, method, timeout);\n const context = { method, path: request.path, url, headers, attempt };\n await dispatchRequestHook(\n this.#config.hooks,\n 'onError',\n { ...context, duration: this.#config.clock.now() - startedAt, error: failure },\n request,\n );\n throw failure;\n }\n\n const context = { method, path: request.path, url, headers, attempt };\n await dispatchRequestHook(this.#config.hooks, 'onRequest', context, request);\n\n this.#config.logger?.debug(`→ ${method} ${request.path}`, {\n headers: redactHeaders(headers),\n body: request.bodyFactory ? '[повторяемое тело]' : redactBody(request.body),\n });\n\n let response: Response;\n try {\n const init: RequestInit & { duplex?: 'half' } = {\n method,\n headers,\n signal: abort.signal,\n ...(body !== undefined ? { body } : {}),\n ...(this.#config.sendCredentials ? { credentials: 'include' as const } : {}),\n };\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n init.duplex = 'half';\n }\n response = await this.#config.fetch(url, init);\n } catch (error) {\n const duration = this.#config.clock.now() - startedAt;\n const failure = this.#toTransportError(error, abort, request, method, timeout);\n\n await dispatchRequestHook(\n this.#config.hooks,\n 'onError',\n { ...context, duration, error: failure },\n request,\n );\n this.#config.logger?.warn(\n `× ${method} ${request.path} (${duration} мс): ${failure.message}`,\n );\n\n throw failure;\n }\n\n if (this.#deps.onRateLimit) {\n const { limit, remaining } = readRateLimit(response.headers);\n this.#deps.onRateLimit(limit, remaining, request);\n }\n\n if (this.#config.useCookieJar) this.#deps.cookies?.setFromResponse(url, response);\n\n // Хук получает непрочитанный ответ.\n if (response.ok) {\n await dispatchRequestHook(\n this.#config.hooks,\n 'onResponse',\n {\n ...context,\n status: response.status,\n duration: this.#config.clock.now() - startedAt,\n response,\n },\n request,\n );\n }\n\n const payload = await this.#readBodyOrFail(\n response,\n context,\n request,\n method,\n abort,\n timeout,\n );\n const duration = this.#config.clock.now() - startedAt;\n\n if (!response.ok) {\n const error = createApiError({\n method,\n path: request.path,\n status: response.status,\n now: this.#config.clock.now(),\n statusText: response.statusText,\n headers: response.headers,\n response,\n body: payload,\n });\n\n await dispatchRequestHook(\n this.#config.hooks,\n 'onError',\n { ...context, duration, error },\n request,\n );\n this.#config.logger?.warn(\n `← ${response.status} ${method} ${request.path} (${duration} мс): ${error.message}`,\n );\n\n throw error;\n }\n\n this.#config.logger?.debug(`← ${response.status} ${method} ${request.path} (${duration} мс)`);\n\n return request.raw ? payload : unwrapData(payload);\n } finally {\n try {\n await cleanupBody?.();\n } catch (error) {\n this.#config.logger?.warn(`не удалось закрыть тело ${method} ${request.path}`, error);\n } finally {\n abort.cleanup();\n }\n }\n };\n\n /** Подготавливает тело внутри попытки, чтобы поток можно было открыть заново при retry. */\n async #prepareBody(\n request: PipelineRequest,\n headers: Headers,\n signal: AbortSignal,\n attempt: number,\n ): Promise<{ body: BodyInit | undefined; cleanup: (() => void | Promise<void>) | undefined }> {\n if (request.bodyFactory) {\n if (request.body !== undefined && request.body !== null) {\n throw new ItdConfigError('body и bodyFactory нельзя задавать одновременно');\n }\n\n const pending = Promise.resolve(request.bodyFactory({ signal, attempt }));\n let prepared: PreparedRequestBody;\n try {\n prepared = await abortable(pending, signal);\n } catch (error) {\n if (signal.aborted) {\n void pending\n .then(async (late) => {\n try {\n await late.cleanup?.();\n } catch (cleanupError) {\n this.#config.logger?.warn(\n `не удалось закрыть отложенное тело ${request.method} ${request.path}`,\n cleanupError,\n );\n }\n })\n .catch(() => {});\n }\n throw error;\n }\n for (const [name, value] of Object.entries(prepared.headers ?? {})) {\n if (!headers.has(name)) setHeader(headers, name, value);\n }\n return { body: prepared.body, cleanup: prepared.cleanup };\n }\n\n if (request.body === undefined || request.body === null) {\n return { body: undefined, cleanup: undefined };\n }\n if (isRawBody(request.body)) {\n return { body: request.body, cleanup: undefined };\n }\n\n if (!headers.has('Content-Type')) headers.set('Content-Type', 'application/json');\n return { body: JSON.stringify(request.body), cleanup: undefined };\n }\n\n /** Читает тело и преобразует ошибку чтения в транспортную ошибку библиотеки. */\n async #readBodyOrFail(\n response: Response,\n context: { method: string; path: string; url: string; headers: Headers; attempt: number },\n request: PipelineRequest,\n method: string,\n abort: AbortBundle,\n timeout: number,\n ): Promise<unknown> {\n const startedAt = this.#config.clock.now();\n\n try {\n return await abortable(readBody(response), abort.signal);\n } catch (error) {\n await response.body?.cancel().catch(() => {});\n\n const failure = this.#toTransportError(error, abort, request, method, timeout);\n\n await dispatchRequestHook(\n this.#config.hooks,\n 'onError',\n {\n ...context,\n duration: this.#config.clock.now() - startedAt,\n error: failure,\n },\n request,\n );\n this.#config.logger?.warn(\n `× ${method} ${request.path}: не удалось прочитать тело ответа — ${failure.message}`,\n );\n\n throw failure;\n }\n }\n\n /**\n * Итоговый URL со строкой запроса. Нужен и слою повторов — для хука `onRetry`.\n *\n * Хост берётся из самого запроса, если он там задан: у сервисов платформы свои домены.\n */\n buildUrl(request: PipelineRequest): string {\n const base = request.baseUrl ?? this.#config.baseUrl;\n return joinUrl(base, request.path) + buildQuery(request.query);\n }\n\n /**\n * Собирает общие заголовки клиента: `User-Agent`, идентификатор устройства,\n * заголовки конфигурации и cookie для указанного адреса.\n */\n async platformHeaders(url: string): Promise<Headers> {\n const headers = new Headers();\n\n // Сервер этот заголовок не требует, но ожидает: дешевле отправить, чем разбираться,\n // почему часть запросов не проходит фильтры.\n headers.set('X-Requested-With', 'XMLHttpRequest');\n\n // В браузере это запрещённый заголовок: среда молча его игнорирует, ошибки не будет.\n if (this.#config.userAgent) setHeader(headers, 'User-Agent', this.#config.userAgent);\n\n if (this.#deps.getDeviceId) {\n setHeader(headers, 'X-Device-Id', await this.#deps.getDeviceId());\n }\n\n for (const [name, value] of Object.entries(this.#config.headers))\n setHeader(headers, name, value);\n\n if (this.#config.useCookieJar && this.#deps.cookies) {\n const cookie = this.#deps.cookies.getHeader(url);\n if (cookie) setHeader(headers, 'Cookie', cookie);\n }\n\n return headers;\n }\n\n /**\n * Дополняет общие заголовки значением `Accept`, заголовками конвейера и вызова.\n * Заголовки вызова применяются последними.\n */\n async #buildHeaders(request: PipelineRequest, url: string): Promise<Headers> {\n const headers = await this.platformHeaders(url);\n\n // Заголовок из конфигурации имеет приоритет над значением по умолчанию.\n if (!headers.has('Accept')) headers.set('Accept', 'application/json');\n\n for (const [name, value] of Object.entries(request.layerHeaders ?? {}))\n setHeader(headers, name, value);\n\n for (const [name, value] of Object.entries(request.headers ?? {})) {\n setHeader(headers, name, value);\n }\n\n return headers;\n }\n\n /** Превращает исключение `fetch` в понятную ошибку библиотеки. */\n #toTransportError(\n error: unknown,\n abort: AbortBundle,\n request: PipelineRequest,\n method: string,\n timeout: number,\n ): ItdError {\n // Пользовательская отмена с собственным `reason` реджектит `fetch` этим значением, а не\n // `AbortError`, — поэтому опираемся на состояние сигнала, а не только на имя ошибки.\n const aborted = abort.signal.aborted || (error instanceof Error && error.name === 'AbortError');\n\n if (aborted && abort.timedOut()) {\n return new ItdTimeoutError({ timeout, method, path: request.path });\n }\n\n if (aborted) {\n // Сохраняем причину отмены: пользователь мог передать её в `abort(reason)`.\n const reason = request.signal?.reason;\n return new ItdAbortError(\n `Запрос ${method} ${request.path} отменён`,\n reason !== undefined ? { cause: reason } : undefined,\n );\n }\n\n if (error instanceof ItdError) return error;\n\n return new ItdNetworkError(\n `Не удалось выполнить ${method} ${request.path}: ${error instanceof Error ? error.message : String(error)}`,\n { method, path: request.path, cause: error },\n );\n }\n}\n","/**\n * Перечисления API итд.com.\n *\n * Здесь намеренно не используется `enum` из TypeScript. Вместо него — пара «замороженный\n * объект + одноимённый тип». Такой приём даёт всё, ради чего берут `enum`\n * (`FeedTab.Popular`, перебор значений в рантайме), и при этом:\n *\n * - **стирается без остатка** — `enum` порождает рантайм-код и отвергается средами,\n * которые просто срезают типы (`node --experimental-strip-types`);\n * - **не запрещает обычные строки** — `itd.posts.list({ tab: 'popular' })` остаётся валидным,\n * тогда как строковый `enum` считает это ошибкой типа и вынуждает всех импортировать себя;\n * - **позволяет открытые множества** — там, где документация перечисляет значения не полностью,\n * тип расширяется через {@link Loose}, а объект остаётся справочником известных значений.\n *\n * @example\n * ```ts\n * import { FeedTab } from 'itd-api';\n *\n * await itd.posts.list({ tab: FeedTab.Popular }); // без магических строк\n * await itd.posts.list({ tab: 'popular' }); // и так тоже можно\n *\n * Object.values(FeedTab); // ['popular', 'following', 'clan']\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * Открытое строковое перечисление.\n *\n * Даёт автодополнение известных значений, но не ломается, если сервер пришлёт новое.\n * Используется там, где документация API перечисляет значения не полностью («`everyone` и др.»).\n */\nexport type Loose<T extends string> = T | (string & {});\n\n/**\n * Вкладка ленты `GET /api/posts`.\n *\n * Множество закрытое: неизвестное значение сервер отвергнет.\n */\nexport const FeedTab = Object.freeze({\n /** Популярное. Курсор здесь — номер страницы в виде строки (`\"2\"`, `\"6\"`…). */\n Popular: 'popular',\n /** Записи тех, на кого вы подписаны. Курсор — отметка времени последнего поста. */\n Following: 'following',\n /** Лента клана. Курсор, как и в подписках, — отметка времени. */\n Clan: 'clan',\n} as const);\nexport type FeedTab = (typeof FeedTab)[keyof typeof FeedTab];\n\n/** Порядок комментариев к посту. */\nexport const CommentSort = Object.freeze({\n /** Сначала новые. */\n Newest: 'newest',\n /** Сначала старые. */\n Oldest: 'oldest',\n /** Сначала популярные. */\n Popular: 'popular',\n} as const);\nexport type CommentSort = (typeof CommentSort)[keyof typeof CommentSort];\n\n/** Тип вложения. */\nexport const AttachmentType = Object.freeze({\n Image: 'image',\n Video: 'video',\n /** Голосовые комментарии: `audio/ogg`, с полем `duration`. */\n Audio: 'audio',\n} as const);\nexport type AttachmentType = (typeof AttachmentType)[keyof typeof AttachmentType];\n\n/**\n * Тип фрагмента разметки в тексте поста или комментария.\n *\n * Первые два сервер расставляет сам при разборе текста, остальные приходят от редактора.\n * Тип открытый: набор может пополниться.\n *\n * @example\n * ```ts\n * await itd.posts.update(postId, {\n * content: 'жирное слово',\n * spans: [{ type: SpanType.Bold, offset: 0, length: 6 }],\n * });\n * ```\n */\nexport const SpanType = Object.freeze({\n /** Хэштег. Название без решётки лежит в `tag`. */\n Hashtag: 'hashtag',\n /** Упоминание. Имя пользователя лежит в `tag`. */\n Mention: 'mention',\n /** Ссылка. Адрес лежит в `url`, а не в `tag`. */\n Link: 'link',\n Bold: 'bold',\n Italic: 'italic',\n Underline: 'underline',\n /** Зачёркнутый. */\n Strike: 'strike',\n /** Спойлер: текст скрыт до нажатия. */\n Spoiler: 'spoiler',\n /** Моноширинный. */\n Monospace: 'monospace',\n Quote: 'quote',\n} as const);\nexport type SpanType = Loose<(typeof SpanType)[keyof typeof SpanType]>;\n\n/** На что подаётся жалоба. */\nexport const ReportTargetType = Object.freeze({\n Post: 'post',\n Comment: 'comment',\n User: 'user',\n} as const);\nexport type ReportTargetType = (typeof ReportTargetType)[keyof typeof ReportTargetType];\n\n/** Причина жалобы. Множество закрытое. */\nexport const ReportReason = Object.freeze({\n Spam: 'spam',\n Violence: 'violence',\n Hate: 'hate',\n Adult: 'adult',\n Fraud: 'fraud',\n Other: 'other',\n} as const);\nexport type ReportReason = (typeof ReportReason)[keyof typeof ReportReason];\n\n/** Состояние realtime-соединения. */\nexport const RealtimeStatus = Object.freeze({\n Connecting: 'connecting',\n Connected: 'connected',\n Error: 'error',\n Disconnected: 'disconnected',\n} as const);\nexport type RealtimeStatus = (typeof RealtimeStatus)[keyof typeof RealtimeStatus];\n\n/** Состояние сервиса платформы. Тип открытый. */\nexport const ServiceState = Object.freeze({\n /** Работает штатно. */\n Operational: 'operational',\n /** Работает с деградацией. */\n Degraded: 'degraded',\n /** Недоступен. */\n Downtime: 'downtime',\n} as const);\nexport type ServiceState = Loose<(typeof ServiceState)[keyof typeof ServiceState]>;\n\n/** Вид происшествия в истории сервиса. Тип открытый. */\nexport const IncidentKind = Object.freeze({\n /** Недоступен. */\n Down: 'down',\n /** Деградация. */\n Degraded: 'deg',\n} as const);\nexport type IncidentKind = Loose<(typeof IncidentKind)[keyof typeof IncidentKind]>;\n\n/**\n * Уровень доступа к разделу профиля.\n *\n * Общий набор значений для полей `wallAccess` и `likesVisibility` настроек приватности.\n * Тип открытый: сервер может прислать значение вне этого перечня.\n */\nexport const AccessType = Object.freeze({\n /** Никто. */\n Nobody: 'nobody',\n /** Только взаимные подписки. */\n Mutual: 'mutual',\n /** Подписчики. */\n Followers: 'followers',\n /** Все. */\n Everyone: 'everyone',\n} as const);\nexport type AccessType = Loose<(typeof AccessType)[keyof typeof AccessType]>;\n\n/** Кто может писать на стену профиля. Псевдоним {@link AccessType}. */\nexport const WallAccess = AccessType;\nexport type WallAccess = AccessType;\n\n/** Кто видит реакции пользователя. Псевдоним {@link AccessType}. */\nexport const LikesVisibility = AccessType;\nexport type LikesVisibility = AccessType;\n\n/**\n * Канонический тип уведомления (новое поколение имён).\n *\n * REST-эндпоинт `/api/notifications/` отдаёт старые имена (`like`, `comment`, `reply`,\n * `repost`, `mention`), SSE-поток — новые. Библиотека приводит их к этому набору,\n * сохраняя исходное значение в поле `rawType`.\n */\nexport const NotificationType = Object.freeze({\n /** Реакция на пост. Старое имя — `like`. */\n PostReaction: 'post_reaction',\n /** Комментарий к посту. Старое имя — `comment`. */\n PostComment: 'post_comment',\n /** Ответ на комментарий. Старое имя — `reply`. */\n CommentReply: 'comment_reply',\n /** Репост. Старое имя — `repost`. */\n PostRepost: 'post_repost',\n /** Упоминание в посте. Старое имя — `mention`. */\n PostMention: 'post_mention',\n /** Реакция на комментарий. */\n CommentReaction: 'comment_reaction',\n /** Упоминание в комментарии. */\n CommentMention: 'comment_mention',\n /** Запись на вашей стене. */\n WallPost: 'wall_post',\n /** На вас подписались. */\n Follow: 'follow',\n /** Заявка на подписку (закрытый профиль). */\n FollowRequest: 'follow_request',\n /** Заявка на подписку принята. */\n FollowAccepted: 'follow_accepted',\n /** Верификация одобрена. Приходит только по REST. */\n VerificationApproved: 'verification_approved',\n /** Верификация отклонена. Приходит только по REST. */\n VerificationRejected: 'verification_rejected',\n} as const);\nexport type NotificationType = Loose<(typeof NotificationType)[keyof typeof NotificationType]>;\n\n/**\n * Тип взаимодействия с контентом в телеметрии (`POST /api/v1/x`, поле `t`).\n *\n * Кодируется числом.\n */\nexport const InteractionType = Object.freeze({\n /** Открытие фотографии. */\n PhotoOpen: 1,\n /** Прогресс просмотра видео. Несёт поля `pm`/`dm`. */\n VideoProgress: 2,\n} as const);\nexport type InteractionType = (typeof InteractionType)[keyof typeof InteractionType];\n\n/**\n * Источник показа поста в телеметрии (поле `s`).\n *\n * Кодируется числом. Поле применимо к источникам `PostPage` и `Link`; для лент источник\n * передаётся контекстом `sc`.\n */\nexport const ViewSource = Object.freeze({\n FeedGlobal: 1,\n FeedFollowing: 2,\n FeedClan: 3,\n Profile: 4,\n Hashtag: 5,\n PostPage: 6,\n Link: 7,\n Search: 8,\n} as const);\nexport type ViewSource = (typeof ViewSource)[keyof typeof ViewSource];\n\n/**\n * Причина завершения просмотра поста в телеметрии (`POST /api/v1/i`, поле `r`).\n *\n * Кодируется числом.\n */\nexport const ViewReason = Object.freeze({\n /** Пост ушёл из зоны видимости при обычной прокрутке. */\n Normal: 0,\n /** Потеря фокуса окна. */\n Blur: 1,\n /** Вкладка скрыта. */\n Hidden: 2,\n /** Уход со страницы (`pagehide`). */\n PageHide: 3,\n /** Элемент перестал наблюдаться. */\n Unobserve: 4,\n /** Достигнут порог времени просмотра. */\n ThresholdMet: 5,\n} as const);\nexport type ViewReason = (typeof ViewReason)[keyof typeof ViewReason];\n\n/**\n * Строковые коды ошибок из поля `code`.\n *\n * Ключи намеренно повторяют написание сервера: код из ответа API можно найти здесь\n * поиском один в один, без мысленного перевода регистра.\n *\n * Список открыт — сервер может добавить новый код, и это не должно ломать типизацию.\n *\n * @example\n * ```ts\n * if (err.hasCode(ItdErrorCode.OTP_INVALID)) await restartOtpFlow();\n * ```\n */\nexport const ItdErrorCode = Object.freeze({\n BAD_REQUEST: 'BAD_REQUEST',\n UNAUTHORIZED: 'UNAUTHORIZED',\n ACCESS_DENIED: 'ACCESS_DENIED',\n ENTITY_NOT_FOUND: 'ENTITY_NOT_FOUND',\n ENTITY_ALREADY_EXISTS: 'ENTITY_ALREADY_EXISTS',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n BUSINESS_RULE_VIOLATION: 'BUSINESS_RULE_VIOLATION',\n RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n /** Сервер отвечает так на `404`, `ENTITY_NOT_FOUND` в этом случае не приходит. */\n NOT_FOUND: 'NOT_FOUND',\n /** На практике не приходит: вместо него сервер шлёт `TURNSTILE_VERIFICATION_FAILED`. */\n CAPTCHA_FAILED: 'CAPTCHA_FAILED',\n /** Капча не пройдена: токен Turnstile недействителен, просрочен или уже использован. */\n TURNSTILE_VERIFICATION_FAILED: 'TURNSTILE_VERIFICATION_FAILED',\n OTP_INVALID: 'OTP_INVALID',\n /** `flowToken` неизвестен или просрочен — поток подтверждения нужно начинать заново. */\n INVALID_FLOW_TOKEN: 'INVALID_FLOW_TOKEN',\n ACCOUNT_DEACTIVATED: 'ACCOUNT_DEACTIVATED',\n ACCOUNT_EMAIL_DOMAIN_NOT_ALLOWED: 'ACCOUNT_EMAIL_DOMAIN_NOT_ALLOWED',\n ACCOUNT_INVALID_CREDENTIALS: 'ACCOUNT_INVALID_CREDENTIALS',\n ACCOUNT_TEMPORARILY_LOCKED: 'ACCOUNT_TEMPORARILY_LOCKED',\n ACCOUNT_CURRENT_PASSWORD_INCORRECT: 'ACCOUNT_CURRENT_PASSWORD_INCORRECT',\n SESSION_EXPIRED: 'SESSION_EXPIRED',\n SESSION_REVOKED: 'SESSION_REVOKED',\n SESSION_INVALID_REFRESH_TOKEN: 'SESSION_INVALID_REFRESH_TOKEN',\n /** Запрос обновления пришёл без cookie `refresh_token` — продлевать нечего. */\n REFRESH_TOKEN_MISSING: 'REFRESH_TOKEN_MISSING',\n /** Cookie `refresh_token` есть, но сессии за ней уже нет: отозвана или истекла. */\n SESSION_NOT_FOUND: 'SESSION_NOT_FOUND',\n MISSING_FLOW_TOKEN: 'MISSING_FLOW_TOKEN',\n PROFILE_USERNAME_TAKEN: 'PROFILE_USERNAME_TAKEN',\n PROFILE_RESTRICTION_ACTIVE: 'PROFILE_RESTRICTION_ACTIVE',\n PROFILE_MODIFICATION_RESTRICTED: 'PROFILE_MODIFICATION_RESTRICTED',\n CONTENT_MODERATION_FAILED: 'CONTENT_MODERATION_FAILED',\n FILE_TOO_LARGE: 'FILE_TOO_LARGE',\n UNSUPPORTED_FILE_TYPE: 'UNSUPPORTED_FILE_TYPE',\n UPLOAD_FAILED: 'UPLOAD_FAILED',\n VIDEO_REQUIRES_VERIFICATION: 'VIDEO_REQUIRES_VERIFICATION',\n PHONE_VERIFICATION_REQUIRED: 'PHONE_VERIFICATION_REQUIRED',\n WRITE_ACCESS_RESTRICTED: 'WRITE_ACCESS_RESTRICTED',\n} as const);\nexport type ItdErrorCode = Loose<(typeof ItdErrorCode)[keyof typeof ItdErrorCode]>;\n","import type { Unsubscribe } from '../core/emitter.js';\nimport type { RealtimeContext } from './updates.js';\n\n/** Продолжает цепочку промежуточных обработчиков потока. */\nexport type RealtimeNext = () => Promise<void>;\n\n/** Обрабатывает обновление потока до его передачи подписчикам. */\nexport type RealtimeMiddleware<C extends RealtimeContext = RealtimeContext> = (\n context: C,\n next: RealtimeNext,\n) => void | Promise<void>;\n\n/** Асинхронный обработчик нормализованного обновления потока. */\nexport type RealtimeHandler<C extends RealtimeContext = RealtimeContext> = (\n context: C,\n) => unknown | Promise<unknown>;\n\n/** Условие отбора контекста потока. */\nexport type RealtimePredicate = (context: RealtimeContext) => boolean;\n\n/** Проверка, сужающая тип контекста потока. */\nexport type RealtimeTypeGuard<C extends RealtimeContext> = (\n context: RealtimeContext,\n) => context is C;\n\n/** Ключи, по которым обновления нельзя обрабатывать одновременно. */\nexport type RealtimeSequentializer = (\n context: RealtimeContext,\n) => PropertyKey | readonly PropertyKey[] | undefined;\n\nconst REALTIME_MIDDLEWARE_SNAPSHOT = Symbol('itd-api.realtime.middlewareSnapshot');\n\ntype SnapshottingRealtimeMiddleware<C extends RealtimeContext> = RealtimeMiddleware<C> & {\n [REALTIME_MIDDLEWARE_SNAPSHOT]?: () => RealtimeMiddleware<C>;\n};\n\n/** @internal */\nexport function captureRealtimeMiddleware<C extends RealtimeContext>(\n middleware: RealtimeMiddleware<C>,\n): RealtimeMiddleware<C> {\n const capture = (middleware as SnapshottingRealtimeMiddleware<C>)[REALTIME_MIDDLEWARE_SNAPSHOT];\n if (!capture) return middleware;\n\n const snapshot = capture();\n if (typeof snapshot !== 'function') {\n throw new TypeError('Снимок промежуточного обработчика должен быть функцией');\n }\n return snapshot;\n}\n\n/** @internal */\nexport function withRealtimeMiddlewareSnapshot<C extends RealtimeContext>(\n middleware: RealtimeMiddleware<C>,\n capture: () => RealtimeMiddleware<C>,\n): RealtimeMiddleware<C> {\n Object.defineProperty(middleware, REALTIME_MIDDLEWARE_SNAPSHOT, { value: capture });\n return middleware;\n}\n\ninterface HandlerRegistration {\n readonly predicate: RealtimePredicate;\n readonly handler: RealtimeHandler;\n}\n\ninterface DispatchWork {\n readonly context: RealtimeContext;\n readonly middleware: readonly RealtimeMiddleware[];\n readonly handlers: readonly HandlerRegistration[];\n readonly keys: readonly PropertyKey[];\n}\n\nexport interface RealtimeDispatcherOptions {\n concurrency: number;\n sequentialize?: RealtimeSequentializer | undefined;\n}\n\nexport interface RealtimeDispatcherHooks {\n deliver: (context: RealtimeContext) => void;\n middlewareError: (error: unknown, context: RealtimeContext) => void;\n handlerError: (error: unknown, context: RealtimeContext) => void;\n}\n\n/** Выполняет промежуточные обработчики по порядку и запрещает повторный вызов `next()`. */\nexport async function runRealtimeMiddleware<C extends RealtimeContext>(\n middleware: readonly RealtimeMiddleware<C>[],\n context: C,\n terminal: RealtimeNext,\n): Promise<void> {\n let lastIndex = -1;\n\n const dispatch = async (index: number): Promise<void> => {\n if (index <= lastIndex) throw new Error('next() в промежуточном обработчике вызван повторно');\n lastIndex = index;\n\n const current = middleware[index];\n if (!current) {\n await terminal();\n return;\n }\n\n let downstream: Promise<void> | undefined;\n let duplicateCalls: Promise<void> | undefined;\n let failure: { error: unknown } | undefined;\n\n const next: RealtimeNext = () => {\n if (!downstream) {\n downstream = dispatch(index + 1);\n return downstream;\n }\n\n const duplicate = Promise.reject(\n new Error('next() в одном промежуточном обработчике вызван повторно'),\n );\n duplicateCalls = Promise.all(duplicateCalls ? [duplicateCalls, duplicate] : [duplicate]).then(\n () => undefined,\n );\n return duplicate;\n };\n\n try {\n await current(context, next);\n } catch (error) {\n failure = { error };\n }\n\n try {\n await downstream;\n await duplicateCalls;\n } catch (error) {\n failure ??= { error };\n }\n\n if (failure) throw failure.error;\n };\n\n await dispatch(0);\n}\n\n/** Планирует нормализованные обновления и отслеживает незавершённые обработчики. */\nexport class RealtimeDispatcher {\n readonly #options: RealtimeDispatcherOptions;\n readonly #hooks: RealtimeDispatcherHooks;\n readonly #middleware: RealtimeMiddleware[] = [];\n readonly #handlers: HandlerRegistration[] = [];\n readonly #queue: DispatchWork[] = [];\n readonly #activeKeys = new Set<PropertyKey>();\n readonly #drainWaiters = new Set<() => void>();\n\n #active = 0;\n\n constructor(options: RealtimeDispatcherOptions, hooks: RealtimeDispatcherHooks) {\n this.#options = options;\n this.#hooks = hooks;\n }\n\n use(middleware: RealtimeMiddleware): Unsubscribe {\n this.#middleware.push(middleware);\n return () => {\n const index = this.#middleware.indexOf(middleware);\n if (index >= 0) this.#middleware.splice(index, 1);\n };\n }\n\n on(predicate: RealtimePredicate, handler: RealtimeHandler): Unsubscribe {\n const registration = { predicate, handler };\n this.#handlers.push(registration);\n\n return () => {\n const index = this.#handlers.indexOf(registration);\n if (index >= 0) this.#handlers.splice(index, 1);\n };\n }\n\n dispatch(context: RealtimeContext): void {\n let keys: readonly PropertyKey[];\n let middleware: readonly RealtimeMiddleware[];\n try {\n keys = this.#keysFor(context);\n middleware = this.#middleware.map(captureRealtimeMiddleware);\n } catch (error) {\n this.#hooks.middlewareError(error, context);\n return;\n }\n\n this.#queue.push({\n context,\n middleware,\n handlers: [...this.#handlers],\n keys,\n });\n this.#pump();\n }\n\n /** Отбрасывает обновления, обработка которых ещё не началась. */\n clearPending(): void {\n this.#queue.length = 0;\n this.#resolveDrain();\n }\n\n /** Ждёт завершения активных и поставленных в очередь обновлений. */\n drain(): Promise<void> {\n if (this.#active === 0 && this.#queue.length === 0) return Promise.resolve();\n return new Promise<void>((resolve) => this.#drainWaiters.add(resolve));\n }\n\n #keysFor(context: RealtimeContext): readonly PropertyKey[] {\n const value = this.#options.sequentialize?.(context);\n if (value === undefined) return [];\n\n const values = Array.isArray(value) ? value : [value];\n for (const key of values) {\n if (typeof key !== 'string' && typeof key !== 'number' && typeof key !== 'symbol') {\n throw new TypeError('sequentialize() должен возвращать PropertyKey или их список');\n }\n }\n return [...new Set(values)];\n }\n\n #pump(): void {\n while (this.#active < this.#options.concurrency) {\n const blockedKeys = new Set(this.#activeKeys);\n const index = this.#queue.findIndex(({ keys }) => {\n const runnable = keys.every((key) => !blockedKeys.has(key));\n if (!runnable) {\n for (const key of keys) blockedKeys.add(key);\n }\n return runnable;\n });\n if (index < 0) break;\n\n const [work] = this.#queue.splice(index, 1);\n if (!work) break;\n\n this.#active += 1;\n for (const key of work.keys) this.#activeKeys.add(key);\n void this.#run(work);\n }\n\n this.#resolveDrain();\n }\n\n async #run(work: DispatchWork): Promise<void> {\n try {\n await runRealtimeMiddleware(work.middleware, work.context, async () => {\n for (const { predicate, handler } of work.handlers) {\n try {\n if (predicate(work.context)) await handler(work.context);\n } catch (error) {\n this.#hooks.handlerError(error, work.context);\n }\n }\n\n this.#hooks.deliver(work.context);\n });\n } catch (error) {\n this.#hooks.middlewareError(error, work.context);\n } finally {\n this.#active -= 1;\n for (const key of work.keys) this.#activeKeys.delete(key);\n this.#pump();\n }\n }\n\n #resolveDrain(): void {\n if (this.#active !== 0 || this.#queue.length !== 0) return;\n for (const resolve of this.#drainWaiters) resolve();\n this.#drainWaiters.clear();\n }\n}\n","/** Событие, пришедшее по каналу реального времени. */\nexport interface TransportEvent {\n /** Имя события: `notification`, `unread_count` и другие. */\n name: string;\n /** Полезная нагрузка, уже разобранная из JSON. */\n data: unknown;\n}\n\n/** Что транспорт получает от клиента при подключении. */\nexport interface TransportContext {\n /** Базовый URL API. */\n baseUrl: string;\n /** Реализация `fetch`. */\n fetch: typeof fetch;\n /**\n * Общие заголовки клиента: `User-Agent`, `X-Device-Id`, заголовки конфигурации\n * и cookie для указанного адреса.\n */\n baseHeaders: (url: string) => Promise<Headers>;\n /** Текущий токен доступа. */\n getToken: () => Promise<string | null>;\n /** Отмена подключения. */\n signal: AbortSignal;\n /** Сообщает о полученном событии. */\n onEvent: (event: TransportEvent) => void;\n /** Сообщает о разобранном, но некорректном сообщении. Соединение при этом живёт. */\n onParseError: (error: unknown, raw: string) => void;\n /** Вызывается, когда соединение установлено. */\n onOpen: () => void;\n}\n\n/**\n * Канал получения событий в реальном времени.\n *\n * Сейчас у платформы один такой канал — поток `text/event-stream`. Абстракция нужна\n * на будущее: политика безопасности сайта уже разрешает `wss://*.xn--d1ah4a.com`,\n * и когда появится WebSocket, достаточно будет добавить ещё одну реализацию этого\n * интерфейса. Переподключение, обновление токена и разбор уведомлений от транспорта\n * не зависят.\n */\nexport interface RealtimeTransport {\n /** Понятное имя для логов и диагностики. */\n readonly name: string;\n\n /**\n * Держит соединение, пока оно живо.\n *\n * Должен завершиться, когда поток закрылся, и бросить исключение при ошибке.\n * Отмена через `context.signal` должна приводить к `AbortError`.\n */\n connect(context: TransportContext): Promise<void>;\n}\n\n/** Ошибка, по которой видно, что сервер отверг авторизацию потока. */\nexport class UnauthorizedStreamError extends Error {\n constructor() {\n super('Поток уведомлений отверг токен доступа');\n this.name = 'UnauthorizedStreamError';\n }\n}\n","import { type ItdClock, systemClock } from '../core/clock.js';\nimport { pickArray, pickNumber } from '../core/unwrap.js';\nimport { joinUrl } from '../core/url.js';\nimport type { RealtimeTransport, TransportContext } from './transport.js';\nimport { UnauthorizedStreamError } from './transport.js';\n\n/** Настройки опроса. */\nexport interface PollTransportOptions {\n /** Часы опроса. Обычно подменяются только в тестах. */\n clock?: ItdClock;\n /** Как часто опрашивать сервер, мс. По умолчанию 15 000. */\n interval?: number;\n /** Сколько уведомлений запрашивать за раз. По умолчанию 20. */\n limit?: number;\n}\n\n/**\n * Запасной транспорт: обычный опрос REST вместо потока.\n *\n * Нужен там, где `fetch` не умеет отдавать тело по частям, — например в части сборок\n * React Native. Наружу выдаёт те же события, что и {@link SseTransport}, поэтому\n * вызывающий код разницы не замечает.\n *\n * Новыми считаются уведомления, которых не было в предыдущем ответе; чтобы список\n * известных не рос бесконечно, он ограничен последними двумя страницами.\n */\nexport class PollTransport implements RealtimeTransport {\n readonly name = 'poll';\n\n readonly #interval: number;\n readonly #limit: number;\n readonly #clock: ItdClock;\n\n constructor(options: PollTransportOptions = {}) {\n this.#clock = options.clock ?? systemClock;\n this.#interval = options.interval ?? 15_000;\n this.#limit = options.limit ?? 20;\n }\n\n async connect(context: TransportContext): Promise<void> {\n const seen = new Set<string>();\n let firstRun = true;\n let lastUnreadCount: number | undefined;\n\n while (!context.signal.aborted) {\n const token = await context.getToken();\n if (!token) throw new UnauthorizedStreamError();\n\n const url = `${joinUrl(context.baseUrl, '/api/notifications/')}?limit=${this.#limit}&offset=0`;\n\n const headers = await context.baseHeaders(url);\n headers.set('Accept', 'application/json');\n headers.set('Authorization', `Bearer ${token}`);\n\n const response = await context.fetch(url, {\n method: 'GET',\n headers,\n signal: context.signal,\n });\n\n if (response.status === 401) throw new UnauthorizedStreamError();\n if (!response.ok) throw new Error(`Опрос уведомлений вернул статус ${response.status}`);\n\n // Соединение считается установленным только после первого успешного ответа: иначе\n // при постоянно недоступной сети каждая попытка обнуляла бы счётчик и maxAttempts\n // не срабатывал бы никогда.\n context.onOpen();\n\n const body: unknown = await response.json();\n const payload =\n typeof body === 'object' && body !== null && 'data' in body\n ? (body as { data: unknown }).data\n : body;\n\n const items = pickArray<Record<string, unknown>>(payload, 'notifications');\n\n for (const item of [...items].reverse()) {\n const id = typeof item.id === 'string' ? item.id : undefined;\n if (!id || seen.has(id)) continue;\n\n seen.add(id);\n // На первом проходе список уже существующих уведомлений не считается новыми\n // событиями: иначе подключение сразу выдало бы всю историю.\n if (!firstRun) context.onEvent({ name: 'notification', data: { payload: item } });\n }\n\n // Ограничиваем память: держим только последние две страницы идентификаторов.\n if (seen.size > this.#limit * 2) {\n const excess = [...seen].slice(0, seen.size - this.#limit * 2);\n for (const id of excess) seen.delete(id);\n }\n\n const count = await this.#readCount(context, headers);\n if (count !== undefined && count !== lastUnreadCount) {\n lastUnreadCount = count;\n context.onEvent({ name: 'unread_count', data: { payload: { count } } });\n }\n\n firstRun = false;\n await this.#wait(context.signal);\n }\n }\n\n async #readCount(context: TransportContext, headers: Headers): Promise<number | undefined> {\n try {\n const response = await context.fetch(joinUrl(context.baseUrl, '/api/notifications/count'), {\n method: 'GET',\n headers,\n signal: context.signal,\n });\n\n if (!response.ok) return undefined;\n\n const body: unknown = await response.json();\n const payload =\n typeof body === 'object' && body !== null && 'data' in body\n ? (body as { data: unknown }).data\n : body;\n\n return pickNumber(payload, 'count', 0);\n } catch {\n // Счётчик — вспомогательная величина: его недоступность не должна рвать опрос.\n return undefined;\n }\n }\n\n /** Ждёт следующего опроса, прерываясь при отмене. */\n #wait(signal: AbortSignal): Promise<void> {\n // Подписка на уже сработавший сигнал не вызывается никогда — без этой проверки\n // отмена, пришедшая перед самым ожиданием, стоила бы лишнего интервала.\n if (signal.aborted) return Promise.resolve();\n\n return new Promise<void>((resolve) => {\n const cancel = this.#clock.schedule(finish, this.#interval);\n\n function finish() {\n cancel();\n signal.removeEventListener('abort', finish);\n resolve();\n }\n\n signal.addEventListener('abort', finish, { once: true });\n });\n }\n}\n","/**\n * Паузы перед попытками переподключения, мс.\n *\n * Значения совпадают с теми, что использует сайт итд.com, — поведение библиотеки\n * не отличается от привычного пользователю.\n */\nexport const RECONNECT_BACKOFF = Object.freeze([1000, 2000, 4000, 8000, 16000, 30000]);\n\n/** Доля случайного разброса паузы. */\nexport const RECONNECT_JITTER = 0.3;\n\n/**\n * Сколько раз пытаться переподключиться подряд.\n *\n * После исчерпания поток сообщает `giveup` и ждёт ручного `connect()`.\n */\nexport const MAX_RECONNECT_ATTEMPTS = 15;\n\n/** Настройки переподключения. */\nexport interface ReconnectOptions {\n /** Таблица пауз. Последнее значение действует для всех дальнейших попыток. */\n backoff?: readonly number[];\n /** Доля разброса, 0…1. */\n jitter?: number;\n /** Предел числа попыток. */\n maxAttempts?: number;\n}\n\n/**\n * Вычисляет паузу перед попыткой переподключения.\n *\n * Разброс нужен, чтобы клиенты, отключившиеся одновременно из-за перезапуска сервера,\n * не вернулись тоже одновременно.\n *\n * @param attempt номер попытки, начиная с нуля\n * @param random источник случайности; подменяется в тестах\n */\nexport function reconnectDelay(\n attempt: number,\n options: ReconnectOptions = {},\n random: () => number = Math.random,\n): number {\n const backoff = options.backoff ?? RECONNECT_BACKOFF;\n const jitter = options.jitter ?? RECONNECT_JITTER;\n\n const base = backoff[Math.min(attempt, backoff.length - 1)] ?? 30_000;\n const spread = base * jitter * (random() * 2 - 1);\n\n return Math.max(0, Math.round(base + spread));\n}\n","class ParseError extends Error {\n constructor(message, options) {\n super(message), this.name = \"ParseError\", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;\n }\n}\nconst LF = 10, CR = 13, SPACE = 32;\nfunction noop(_arg) {\n}\nfunction createParser(config) {\n if (typeof config == \"function\")\n throw new TypeError(\n \"`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?\"\n );\n const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config, pendingFragments = [];\n let pendingFragmentsLength = 0, isFirstChunk = !0, id, data = \"\", dataLines = 0, eventType, terminated = !1;\n function feed(chunk) {\n if (terminated)\n throw new Error(\n \"Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.\"\n );\n if (isFirstChunk && (isFirstChunk = !1, chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191 && (chunk = chunk.slice(3))), pendingFragments.length === 0) {\n const trailing2 = processLines(chunk);\n trailing2 !== \"\" && (pendingFragments.push(trailing2), pendingFragmentsLength = trailing2.length), checkBufferSize();\n return;\n }\n if (chunk.indexOf(`\n`) === -1 && chunk.indexOf(\"\\r\") === -1) {\n pendingFragments.push(chunk), pendingFragmentsLength += chunk.length, checkBufferSize();\n return;\n }\n pendingFragments.push(chunk);\n const input = pendingFragments.join(\"\");\n pendingFragments.length = 0, pendingFragmentsLength = 0;\n const trailing = processLines(input);\n trailing !== \"\" && (pendingFragments.push(trailing), pendingFragmentsLength = trailing.length), checkBufferSize();\n }\n function checkBufferSize() {\n maxBufferSize !== void 0 && (pendingFragmentsLength + data.length <= maxBufferSize || (terminated = !0, pendingFragments.length = 0, pendingFragmentsLength = 0, id = void 0, data = \"\", dataLines = 0, eventType = void 0, onError(\n new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {\n type: \"max-buffer-size-exceeded\"\n })\n )));\n }\n function processLines(chunk) {\n let searchIndex = 0;\n if (chunk.indexOf(\"\\r\") === -1) {\n let lfIndex = chunk.indexOf(`\n`, searchIndex);\n for (; lfIndex !== -1; ) {\n if (searchIndex === lfIndex) {\n dataLines > 0 && onEvent({ id, event: eventType, data }), id = void 0, data = \"\", dataLines = 0, eventType = void 0, searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`\n`, searchIndex);\n continue;\n }\n const firstCharCode = chunk.charCodeAt(searchIndex);\n if (isDataPrefix(chunk, searchIndex, firstCharCode)) {\n const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5, value = chunk.slice(valueStart, lfIndex);\n if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {\n onEvent({ id, event: eventType, data: value }), id = void 0, data = \"\", eventType = void 0, searchIndex = lfIndex + 2, lfIndex = chunk.indexOf(`\n`, searchIndex);\n continue;\n }\n data = dataLines === 0 ? value : `${data}\n${value}`, dataLines++;\n } else isEventPrefix(chunk, searchIndex, firstCharCode) ? eventType = chunk.slice(\n chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,\n lfIndex\n ) || void 0 : parseLine(chunk, searchIndex, lfIndex);\n searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`\n`, searchIndex);\n }\n return chunk.slice(searchIndex);\n }\n for (; searchIndex < chunk.length; ) {\n const crIndex = chunk.indexOf(\"\\r\", searchIndex), lfIndex = chunk.indexOf(`\n`, searchIndex);\n let lineEnd = -1;\n if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = crIndex < lfIndex ? crIndex : lfIndex : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1)\n break;\n parseLine(chunk, searchIndex, lineEnd), searchIndex = lineEnd + 1, chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF && searchIndex++;\n }\n return chunk.slice(searchIndex);\n }\n function parseLine(chunk, start, end) {\n if (start === end) {\n dispatchEvent();\n return;\n }\n const firstCharCode = chunk.charCodeAt(start);\n if (isDataPrefix(chunk, start, firstCharCode)) {\n const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5, value2 = chunk.slice(valueStart, end);\n data = dataLines === 0 ? value2 : `${data}\n${value2}`, dataLines++;\n return;\n }\n if (isEventPrefix(chunk, start, firstCharCode)) {\n eventType = chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || void 0;\n return;\n }\n if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {\n const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);\n id = value2.includes(\"\\0\") ? void 0 : value2;\n return;\n }\n if (firstCharCode === 58) {\n if (onComment) {\n const line2 = chunk.slice(start, end);\n onComment(line2.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));\n }\n return;\n }\n const line = chunk.slice(start, end), fieldSeparatorIndex = line.indexOf(\":\");\n if (fieldSeparatorIndex === -1) {\n processField(line, \"\", line);\n return;\n }\n const field = line.slice(0, fieldSeparatorIndex), offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);\n processField(field, value, line);\n }\n function processField(field, value, line) {\n switch (field) {\n case \"event\":\n eventType = value || void 0;\n break;\n case \"data\":\n data = dataLines === 0 ? value : `${data}\n${value}`, dataLines++;\n break;\n case \"id\":\n id = value.includes(\"\\0\") ? void 0 : value;\n break;\n case \"retry\":\n /^\\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(\n new ParseError(`Invalid \\`retry\\` value: \"${value}\"`, {\n type: \"invalid-retry\",\n value,\n line\n })\n );\n break;\n default:\n onError(\n new ParseError(\n `Unknown field \"${field.length > 20 ? `${field.slice(0, 20)}\\u2026` : field}\"`,\n { type: \"unknown-field\", field, value, line }\n )\n );\n break;\n }\n }\n function dispatchEvent() {\n dataLines > 0 && onEvent({\n id,\n event: eventType,\n data\n }), id = void 0, data = \"\", dataLines = 0, eventType = void 0;\n }\n function reset(options = {}) {\n if (options.consume && pendingFragments.length > 0) {\n const incompleteLine = pendingFragments.join(\"\");\n parseLine(incompleteLine, 0, incompleteLine.length);\n }\n isFirstChunk = !0, id = void 0, data = \"\", dataLines = 0, eventType = void 0, pendingFragments.length = 0, pendingFragmentsLength = 0, terminated = !1;\n }\n return { feed, reset };\n}\nfunction isDataPrefix(chunk, i, firstCharCode) {\n return firstCharCode === 100 && chunk.charCodeAt(i + 1) === 97 && chunk.charCodeAt(i + 2) === 116 && chunk.charCodeAt(i + 3) === 97 && chunk.charCodeAt(i + 4) === 58;\n}\nfunction isEventPrefix(chunk, i, firstCharCode) {\n return firstCharCode === 101 && chunk.charCodeAt(i + 1) === 118 && chunk.charCodeAt(i + 2) === 101 && chunk.charCodeAt(i + 3) === 110 && chunk.charCodeAt(i + 4) === 116 && chunk.charCodeAt(i + 5) === 58;\n}\nexport {\n ParseError,\n createParser\n};\n//# sourceMappingURL=index.js.map\n","import { createParser } from 'eventsource-parser';\nimport { type ItdClock, systemClock } from '../core/clock.js';\nimport { joinUrl } from '../core/url.js';\nimport {\n type RealtimeTransport,\n type TransportContext,\n UnauthorizedStreamError,\n} from './transport.js';\n\n/** Путь потока уведомлений. */\nexport const STREAM_PATH = '/api/notifications/stream';\n\n/** Настройки SSE-транспорта. */\nexport interface SseTransportOptions {\n /** Часы потока. Обычно подменяются только в тестах. */\n clock?: ItdClock;\n /**\n * Сколько миллисекунд ждать данных, прежде чем считать соединение мёртвым.\n *\n * Сервер не присылает keep-alive, а оборванное TCP-соединение может не закрыться\n * само — без этой проверки поток «тихо умирает» и новых уведомлений не приходит.\n * По умолчанию 90 000. `0` отключает проверку.\n */\n idleTimeout?: number;\n /**\n * Сколько миллисекунд ждать ответа на запрос потока, прежде чем оборвать попытку.\n *\n * Проверка молчания ({@link idleTimeout}) начинается только после получения тела ответа.\n * Если `fetch` завис на установке соединения, без этого таймаута переподключение не\n * запустится до системного сетевого таймаута. По умолчанию 20 000. `0` отключает проверку.\n */\n handshakeTimeout?: number;\n}\n\n/**\n * Транспорт поверх Server-Sent Events.\n *\n * Нативный `EventSource` не подходит: он не умеет отправлять заголовок `Authorization`,\n * а поток требует Bearer-токен. Поэтому используется `fetch` с чтением тела по частям.\n *\n * Разбор кадров делегирован `eventsource-parser`, который корректно обрабатывает\n * многострочный `data:`, перевод строки `\\r\\n`, `data:` без пробела и кадр, разорванный\n * между сетевыми чанками. В разборе на сайте итд.com каждый из этих случаев обрабатывается\n * неверно.\n */\nexport class SseTransport implements RealtimeTransport {\n readonly name = 'sse';\n\n readonly #idleTimeout: number;\n readonly #handshakeTimeout: number;\n readonly #clock: ItdClock;\n /** Идентификатор последнего события — отправляется при переподключении. */\n #lastEventId: string | undefined;\n\n constructor(options: SseTransportOptions = {}) {\n this.#clock = options.clock ?? systemClock;\n this.#idleTimeout = options.idleTimeout ?? 90_000;\n this.#handshakeTimeout = options.handshakeTimeout ?? 20_000;\n }\n\n async connect(context: TransportContext): Promise<void> {\n const token = await context.getToken();\n if (!token) throw new UnauthorizedStreamError();\n\n const url = joinUrl(context.baseUrl, STREAM_PATH);\n\n const headers = await context.baseHeaders(url);\n headers.set('Accept', 'text/event-stream');\n headers.set('Authorization', `Bearer ${token}`);\n headers.set('Cache-Control', 'no-cache');\n\n // Сервер пока не присылает id, но поддержка не мешает и пригодится, если начнёт.\n if (this.#lastEventId) headers.set('Last-Event-ID', this.#lastEventId);\n\n // Отдельный контроллер, связанный с сигналом потока: помимо внешней отмены он позволяет\n // оборвать зависшее рукопожатие своим таймаутом. Живёт до конца чтения тела.\n const controller = new AbortController();\n const relayAbort = () => controller.abort(context.signal.reason);\n if (context.signal.aborted) controller.abort(context.signal.reason);\n else context.signal.addEventListener('abort', relayAbort, { once: true });\n\n try {\n const response = await this.#handshake(url, headers, context, controller);\n\n if (response.status === 401) throw new UnauthorizedStreamError();\n if (!response.ok) throw new Error(`Поток уведомлений вернул статус ${response.status}`);\n if (!response.body) throw new Error('Ответ потока уведомлений пуст');\n\n context.onOpen();\n\n await this.#read(response.body, context);\n } finally {\n context.signal.removeEventListener('abort', relayAbort);\n }\n }\n\n /** Выполняет запрос потока, обрывая его, если ответ не пришёл за отведённое время. */\n async #handshake(\n url: string,\n headers: Headers,\n context: TransportContext,\n controller: AbortController,\n ): Promise<Response> {\n let expired = false;\n const cancelTimer =\n this.#handshakeTimeout > 0\n ? this.#clock.schedule(() => {\n expired = true;\n // Абортим связанный контроллер: `fetch` реджектит, и внешний код переподключится.\n controller.abort(new Error('Истёк таймаут рукопожатия SSE'));\n }, this.#handshakeTimeout)\n : undefined;\n\n try {\n return await context.fetch(url, { method: 'GET', headers, signal: controller.signal });\n } catch (error) {\n if (expired) throw new Error('Поток уведомлений не ответил: истёк таймаут рукопожатия');\n throw error;\n } finally {\n cancelTimer?.();\n }\n }\n\n async #read(body: ReadableStream<Uint8Array>, context: TransportContext): Promise<void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n\n const parser = createParser({\n onEvent: (message) => {\n if (message.id) this.#lastEventId = message.id;\n\n let data: unknown;\n try {\n data = JSON.parse(message.data);\n } catch (error) {\n // Одно битое сообщение не повод рвать соединение.\n context.onParseError(error, message.data);\n return;\n }\n\n // Имя события может отсутствовать — тогда тип лежит внутри полезной нагрузки.\n const name =\n message.event ??\n (typeof data === 'object' && data !== null && 'type' in data\n ? String((data as { type: unknown }).type)\n : 'message');\n\n context.onEvent({ name, data });\n },\n });\n\n let cancelIdleTimer: (() => void) | undefined;\n\n const armIdleTimer = () => {\n if (this.#idleTimeout <= 0) return;\n cancelIdleTimer?.();\n\n cancelIdleTimer = this.#clock.schedule(() => {\n // Отмена чтения завершит цикл, и внешний код переподключится.\n void reader.cancel(new Error('Поток молчит дольше допустимого')).catch(() => {});\n }, this.#idleTimeout);\n };\n\n armIdleTimer();\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n\n armIdleTimer();\n parser.feed(decoder.decode(value, { stream: true }));\n }\n } finally {\n cancelIdleTimer?.();\n reader.releaseLock?.();\n }\n }\n}\n","import { NotificationType } from '../types/enums.js';\n\n/**\n * Соответствие коротких имён типов уведомлений развёрнутым.\n *\n * Сервер — и в списке, и в потоке событий — присылает короткие имена: `like`, `comment`,\n * `reply`, `repost`, `comment_like`. Развёрнутые (`post_reaction`, `post_comment`)\n * встречаются в оформлении интерфейса, поэтому библиотека приводит типы к ним:\n * они однозначно называют и объект, и действие.\n *\n * Пришедшее значение всегда остаётся в поле `rawType`.\n */\nexport const NOTIFICATION_TYPE_ALIASES: Readonly<Record<string, NotificationType>> = Object.freeze({\n like: NotificationType.PostReaction,\n comment: NotificationType.PostComment,\n comment_like: NotificationType.CommentReaction,\n reply: NotificationType.CommentReply,\n repost: NotificationType.PostRepost,\n mention: NotificationType.PostMention,\n});\n\nconst KNOWN_TYPES = new Set<string>(Object.values(NotificationType));\n\n/**\n * Приводит имя типа к каноническому.\n *\n * Неизвестное значение возвращается без изменений, чтобы не менять смысл нового типа\n * уведомления на другой.\n *\n * @example\n * ```ts\n * canonicalNotificationType('like'); // 'post_reaction'\n * canonicalNotificationType('post_reaction'); // 'post_reaction'\n * canonicalNotificationType('новое_событие'); // 'новое_событие'\n * ```\n */\nexport function canonicalNotificationType(rawType: string): NotificationType {\n return NOTIFICATION_TYPE_ALIASES[rawType] ?? rawType;\n}\n\n/**\n * Известен ли библиотеке этот тип уведомления.\n *\n * Полезно, чтобы решить, показывать ли уведомление, для которого нет своего оформления.\n */\nexport function isKnownNotificationType(type: string): boolean {\n return KNOWN_TYPES.has(canonicalNotificationType(type));\n}\n","import { asString, isRecord } from '../core/unwrap.js';\nimport type { Notification } from '../models/notifications.js';\nimport type { Actor } from '../models/users.js';\nimport { canonicalNotificationType } from './type-map.js';\n\n/** Событие потока уведомлений после разбора. */\nexport interface NotificationEvent {\n /** Само уведомление в единой форме. */\n notification: Notification;\n /**\n * Актуальное число непрочитанных, если сервер его сообщил.\n *\n * Клиент не увеличивает счётчик сам: значение приходит с сервера.\n */\n unreadCount: number | undefined;\n /** Нужно ли проиграть звук. */\n sound: boolean;\n}\n\nfunction asActor(value: unknown): Actor | undefined {\n if (!isRecord(value)) return undefined;\n const id = asString(value.id);\n if (!id) return undefined;\n\n return {\n id,\n username: asString(value.username) ?? '',\n displayName: asString(value.displayName) ?? '',\n avatar: asString(value.avatar) ?? '',\n ...(typeof value.isFollowing === 'boolean' ? { isFollowing: value.isFollowing } : {}),\n ...(typeof value.isFollowedBy === 'boolean' ? { isFollowedBy: value.isFollowedBy } : {}),\n };\n}\n\n/** Собирает участников: сервер присылает либо одного `actor`, либо массив `actors`. */\nfunction readActors(source: Record<string, unknown>): Actor[] {\n if (Array.isArray(source.actors)) {\n return source.actors.map(asActor).filter((actor): actor is Actor => actor !== undefined);\n }\n\n const single = asActor(source.actor);\n return single ? [single] : [];\n}\n\n/**\n * Приводит уведомление к единой форме.\n *\n * Нужна потому, что REST-список и поток событий описывают одно и то же событие по-разному:\n * различаются имена типов (`like` против `post_reaction`), имена полей\n * (`targetId`/`entityId`, `read`/`isRead`, `preview`/`entityPreview`) и число участников\n * (`actor` против массива `actors`). После приведения объекты из обоих источников\n * можно складывать в один список.\n *\n * Исходные данные не теряются: имя типа с сервера остаётся в `rawType`,\n * весь объект целиком — в `raw`.\n *\n * @param input уведомление из REST-ответа либо полезная нагрузка события потока\n *\n * @example\n * ```ts\n * const fromRest = normalizeNotification(restItem);\n * const fromStream = normalizeNotification(event.payload);\n * // одинаковая форма — можно объединять\n * ```\n */\nexport function normalizeNotification(input: unknown): Notification {\n const source = isRecord(input) ? input : {};\n\n // Поток кладёт полезную нагрузку то в payload, то прямо в корень события.\n const payload = isRecord(source.payload) ? source.payload : source;\n\n const rawType = asString(payload.type) ?? asString(source.type) ?? '';\n const createdAt = asString(payload.createdAt) ?? asString(source.createdAt) ?? '';\n const readAt = asString(payload.readAt) ?? asString(source.readAt);\n\n const isRead =\n typeof payload.isRead === 'boolean'\n ? payload.isRead\n : typeof payload.read === 'boolean'\n ? payload.read\n : Boolean(readAt);\n\n // Сервер описывает событие парой «цель — предмет», и смысл пары зависит от типа:\n //\n // комментарий, реакция на него — цель = пост, предмет = комментарий\n // репост — цель = сам репост, предмет = исходный пост\n // реакция на пост, подписка — предмета нет\n //\n // Событие относится к предмету, только когда тот является комментарием; иначе объект\n // события — цель. Без этого различия ссылка на репост вела бы на чужой исходный пост.\n const subjectId = asString(payload.subjectId);\n const targetId = asString(payload.targetId);\n const subjectIsComment = payload.subjectType === 'comment';\n const clickUrl = asString(payload.clickUrl);\n\n return {\n id: asString(payload.id) ?? asString(source.id) ?? '',\n type: canonicalNotificationType(rawType),\n rawType,\n entityId:\n asString(payload.entityId) ?? (subjectIsComment ? (subjectId ?? targetId) : targetId) ?? null,\n parentEntityId:\n asString(payload.parentEntityId) ?? (subjectIsComment ? (targetId ?? null) : null),\n isRead,\n actors: readActors(payload),\n count: typeof payload.count === 'number' && payload.count > 0 ? payload.count : 1,\n preview: asString(payload.entityPreview) ?? asString(payload.preview) ?? null,\n ...(clickUrl ? { clickUrl } : {}),\n createdAt,\n updatedAt: asString(payload.updatedAt) ?? readAt ?? createdAt,\n raw: input,\n };\n}\n\n/**\n * Разбирает событие `notification` из потока.\n *\n * Кроме самого уведомления событие несёт служебные поля уровня конверта: актуальный\n * счётчик непрочитанных и признак звука.\n */\nexport function readNotificationEvent(data: unknown): NotificationEvent {\n const source = isRecord(data) ? data : {};\n\n return {\n notification: normalizeNotification(data),\n unreadCount: typeof source.unreadCount === 'number' ? source.unreadCount : undefined,\n sound: source.sound === true,\n };\n}\n\n/**\n * Разбирает событие `unread_count` из потока.\n *\n * Возвращает `undefined`, если сервер прислал событие без вложенного `payload`.\n */\nexport function readUnreadCountEvent(data: unknown): number | undefined {\n if (!isRecord(data)) return undefined;\n\n const payload = isRecord(data.payload) ? data.payload : undefined;\n if (!payload) return undefined;\n\n return typeof payload.count === 'number' ? payload.count : undefined;\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { Notification } from '../models/notifications.js';\nimport type { NotificationEvent } from '../notifications/normalize.js';\nimport { readNotificationEvent, readUnreadCountEvent } from '../notifications/normalize.js';\nimport type { NotificationType } from '../types/enums.js';\nimport type { ItdRealtime } from './stream.js';\nimport type { TransportEvent } from './transport.js';\n\n/** Типы нормализованных обновлений потока. */\nexport const RealtimeUpdateType = Object.freeze({\n Notification: 'notification',\n UnreadCount: 'unreadCount',\n Unknown: 'unknown',\n} as const);\n\n/** Источники нормализованных обновлений потока. */\nexport const RealtimeUpdateOrigin = Object.freeze({\n Stream: 'stream',\n Sync: 'sync',\n} as const);\nexport type RealtimeUpdateOrigin = (typeof RealtimeUpdateOrigin)[keyof typeof RealtimeUpdateOrigin];\n\n/** Уведомление с типом, суженным фильтром потока. */\nexport type NotificationOfType<T extends NotificationType> = Omit<Notification, 'type'> & {\n type: T;\n};\n\n/** Конверт уведомления с типом, суженным фильтром потока. */\nexport type NotificationEventOfType<T extends NotificationType> = Omit<\n NotificationEvent,\n 'notification'\n> & {\n notification: NotificationOfType<T>;\n};\n\n/** Нормализованное уведомление из потока. */\nexport interface RealtimeNotificationUpdate<T extends NotificationType = NotificationType> {\n readonly type: typeof RealtimeUpdateType.Notification;\n readonly data: NotificationEventOfType<T>;\n}\n\n/** Актуальное число непрочитанных уведомлений. */\nexport interface RealtimeUnreadCountUpdate {\n readonly type: typeof RealtimeUpdateType.UnreadCount;\n readonly data: number;\n}\n\n/** Неизвестное библиотеке событие потока. */\nexport interface RealtimeUnknownUpdate {\n readonly type: typeof RealtimeUpdateType.Unknown;\n readonly name: string;\n readonly data: unknown;\n}\n\n/** Данные, проходящие через промежуточные обработчики потока. */\nexport type RealtimeUpdate =\n | RealtimeNotificationUpdate\n | RealtimeUnreadCountUpdate\n | RealtimeUnknownUpdate;\n\n/** Тип нормализованного обновления потока. */\nexport type RealtimeUpdateType = RealtimeUpdate['type'];\n\n/** Обновление потока указанного типа. */\nexport type RealtimeUpdateOfType<T extends RealtimeUpdateType> = Extract<\n RealtimeUpdate,\n { type: T }\n>;\n\n/** Контекст обработки одного обновления потока. */\nexport interface RealtimeContext<U extends RealtimeUpdate = RealtimeUpdate> {\n /** Нормализованные данные обновления. */\n readonly update: U;\n /** Поток, который получил обновление. */\n readonly stream: ItdRealtime;\n /** Исходный кадр транспорта. Для начальной REST-синхронизации равен `undefined`. */\n readonly raw: TransportEvent | undefined;\n /** Откуда получены данные. */\n readonly origin: RealtimeUpdateOrigin;\n}\n\n/** Контекст уведомления с типом, суженным фильтром. */\nexport type RealtimeNotificationContext<T extends NotificationType = NotificationType> =\n RealtimeContext<RealtimeNotificationUpdate<T>>;\n\n/** Условия отбора уведомлений. Все указанные поля объединяются через логическое И. */\nexport interface RealtimeNotificationFilter<T extends NotificationType = NotificationType> {\n /** Один или несколько канонических типов уведомления. */\n type?: T | readonly T[];\n /** Идентификатор хотя бы одного участника уведомления. */\n actorId?: string;\n /** Идентификатор объекта события. */\n entityId?: string | null;\n /** Идентификатор родительского объекта. */\n parentEntityId?: string | null;\n /** Дополнительная проверка после сопоставления полей. */\n predicate?: (context: RealtimeNotificationContext<T>) => boolean;\n}\n\n/** Краткая или объектная форма фильтра уведомлений. */\nexport type RealtimeNotificationSelector<T extends NotificationType = NotificationType> =\n | T\n | readonly T[]\n | RealtimeNotificationFilter<T>;\n\n/** Проверяет форму фильтра уведомлений. */\nexport function validateNotificationSelector(selector: unknown): void {\n if (typeof selector === 'string') {\n if (selector.length === 0) throw new ItdConfigError('Тип уведомления не должен быть пустым');\n return;\n }\n\n if (Array.isArray(selector)) {\n if (selector.length === 0 || selector.some((type) => typeof type !== 'string' || !type)) {\n throw new ItdConfigError('Список типов уведомлений должен содержать непустые строки');\n }\n return;\n }\n\n if (typeof selector !== 'object' || selector === null) {\n throw new ItdConfigError('Фильтр уведомлений должен быть строкой, списком или объектом');\n }\n\n const filter = selector as Record<string, unknown>;\n if (filter.type !== undefined) validateNotificationSelector(filter.type);\n if (filter.actorId !== undefined && typeof filter.actorId !== 'string') {\n throw new ItdConfigError('Фильтр уведомлений: actorId должен быть строкой');\n }\n for (const field of ['entityId', 'parentEntityId'] as const) {\n const value = filter[field];\n if (value !== undefined && value !== null && typeof value !== 'string') {\n throw new ItdConfigError(`Фильтр уведомлений: ${field} должен быть строкой или null`);\n }\n }\n if (filter.predicate !== undefined && typeof filter.predicate !== 'function') {\n throw new ItdConfigError('Фильтр уведомлений: predicate должен быть функцией');\n }\n}\n\n/** Преобразует транспортный кадр в одно логическое обновление. */\nexport function readRealtimeUpdate(event: TransportEvent): RealtimeUpdate | undefined {\n if (event.name === 'notification') {\n return {\n type: RealtimeUpdateType.Notification,\n data: readNotificationEvent(event.data),\n };\n }\n\n if (event.name === 'unread_count') {\n const count = readUnreadCountEvent(event.data);\n return count === undefined ? undefined : { type: RealtimeUpdateType.UnreadCount, data: count };\n }\n\n return { type: RealtimeUpdateType.Unknown, name: event.name, data: event.data };\n}\n\n/** Проверяет объектный или краткий фильтр уведомления. */\nexport function matchesNotification<T extends NotificationType>(\n context: RealtimeNotificationContext,\n selector: RealtimeNotificationSelector<T>,\n): context is RealtimeNotificationContext<T> {\n const notification = context.update.data.notification;\n\n if (typeof selector === 'string') return notification.type === selector;\n if (Array.isArray(selector)) return selector.includes(notification.type as T);\n\n const filter = selector as RealtimeNotificationFilter<T>;\n const types = filter.type === undefined ? undefined : [filter.type].flat();\n\n if (types && !types.includes(notification.type as T)) return false;\n if (\n filter.actorId !== undefined &&\n !notification.actors.some(({ id }) => id === filter.actorId)\n ) {\n return false;\n }\n if (filter.entityId !== undefined && notification.entityId !== filter.entityId) return false;\n if (\n filter.parentEntityId !== undefined &&\n notification.parentEntityId !== filter.parentEntityId\n ) {\n return false;\n }\n\n return filter.predicate?.(context as RealtimeNotificationContext<T>) ?? true;\n}\n\n/** Сужает произвольный контекст потока до контекста уведомления. */\nexport function isNotificationContext(\n context: RealtimeContext,\n): context is RealtimeNotificationContext {\n return context.update.type === RealtimeUpdateType.Notification;\n}\n","import type { AuthIdentity } from '../core/auth.js';\nimport { type ItdClock, systemClock } from '../core/clock.js';\nimport { Emitter, type Listener, type Unsubscribe } from '../core/emitter.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport { supportsStreamingBody } from '../core/runtime.js';\nimport { pickString } from '../core/unwrap.js';\nimport type { NotificationEvent } from '../notifications/normalize.js';\nimport { type NotificationType, RealtimeStatus } from '../types/enums.js';\nimport type { Logger } from '../types/options.js';\nimport {\n RealtimeDispatcher,\n type RealtimeHandler,\n type RealtimeMiddleware,\n type RealtimePredicate,\n type RealtimeSequentializer,\n type RealtimeTypeGuard,\n} from './middleware.js';\nimport { PollTransport } from './poll.js';\nimport { MAX_RECONNECT_ATTEMPTS, type ReconnectOptions, reconnectDelay } from './reconnect.js';\nimport { SseTransport } from './sse.js';\nimport {\n type RealtimeTransport,\n type TransportContext,\n type TransportEvent,\n UnauthorizedStreamError,\n} from './transport.js';\nimport {\n isNotificationContext,\n matchesNotification,\n type RealtimeContext,\n type RealtimeNotificationContext,\n type RealtimeNotificationSelector,\n type RealtimeUpdate,\n type RealtimeUpdateOfType,\n RealtimeUpdateOrigin,\n RealtimeUpdateType,\n readRealtimeUpdate,\n validateNotificationSelector,\n} from './updates.js';\n\n/** События потока уведомлений. */\nexport interface RealtimeEvents {\n /** Пришло новое уведомление. */\n notification: NotificationEvent;\n /**\n * Сервер подтвердил подключение и назвал получателя событий.\n *\n * Приходит первым кадром сразу после установки соединения.\n */\n ready: { userId: string | undefined };\n /**\n * Получено актуальное число непрочитанных.\n *\n * При подключении клиент может запросить начальное значение через REST. Затем событие\n * возникает, только если счётчик пришёл в потоке. В остальных случаях обновляйте его\n * в приложении либо запрашивайте `itd.notifications.count()`.\n */\n unreadCount: number;\n /** Изменилось состояние соединения. */\n status: RealtimeStatus;\n /** Соединение оборвалось; будет предпринята попытка переподключения. */\n error: { error: unknown; willReconnect: boolean };\n /** Сообщение не удалось разобрать. Соединение при этом продолжает работать. */\n parseError: { error: unknown; raw: string };\n /** Запланировано переподключение. */\n reconnect: { attempt: number; delay: number };\n /** Попытки исчерпаны — соединение восстановится только ручным `connect()`. */\n giveup: undefined;\n /** Любой исходный кадр транспорта. Отправляется до нормализации и промежуточных обработчиков. */\n message: TransportEvent;\n /** Промежуточный обработчик потока завершился исключением. */\n middlewareError: { error: unknown; context: RealtimeContext };\n /** Обработчик `onUpdate` завершился исключением. */\n handlerError: { error: unknown; context: RealtimeContext };\n}\n\n/** Способ получения событий. */\nexport const RealtimeTransportKind = Object.freeze({\n /** Поток событий, если среда умеет читать тело по частям, иначе опрос. */\n Auto: 'auto',\n /** Поток `text/event-stream`. */\n Sse: 'sse',\n /** Периодический опрос REST. */\n Poll: 'poll',\n} as const);\nexport type RealtimeTransportKind =\n (typeof RealtimeTransportKind)[keyof typeof RealtimeTransportKind];\n\n/** Настройки потока уведомлений. */\nexport interface RealtimeOptions extends ReconnectOptions {\n /**\n * Транспорт. По умолчанию `auto`: поток событий, если среда умеет читать тело ответа\n * по частям, иначе опрос.\n *\n * Можно передать и свою реализацию {@link RealtimeTransport} — это пригодится, если\n * у платформы появится WebSocket либо нужен нестандартный способ доставки.\n */\n transport?: RealtimeTransportKind | RealtimeTransport;\n /**\n * Молчание сервера, после которого соединение считается мёртвым, мс. По умолчанию 90 000.\n *\n * Сервер не присылает keep-alive, поэтому без этой проверки оборванное соединение\n * может незаметно «зависнуть».\n */\n idleTimeout?: number;\n /**\n * Сколько ждать ответа на запрос потока, прежде чем оборвать попытку, мс. По умолчанию\n * 20 000. Защищает от зависания на установке соединения, когда {@link idleTimeout} ещё\n * не действует. `0` отключает проверку. Только для потокового транспорта.\n */\n handshakeTimeout?: number;\n /** Как часто опрашивать сервер, если используется запасной транспорт. */\n pollInterval?: number;\n /**\n * Запрашивать число непрочитанных при подключении. По умолчанию `true`.\n *\n * Так поступает сайт итд.com: поток присылает только новые события, а начальное\n * значение счётчика нужно получить отдельно.\n */\n syncCount?: boolean;\n /**\n * Переподключаться, когда вкладка снова становится видимой. По умолчанию `true`.\n *\n * Только в браузере. У сайта итд.com такой обработки нет, из-за чего вкладка,\n * пролежавшая в фоне, может остаться без соединения.\n */\n reconnectOnVisible?: boolean;\n /** Переподключаться при восстановлении сети. По умолчанию `true`. Только в браузере. */\n reconnectOnOnline?: boolean;\n /** Максимальное число одновременно обрабатываемых обновлений. По умолчанию 1. */\n concurrency?: number;\n /** Возвращает ключи обновлений, которые нельзя обрабатывать одновременно. */\n sequentialize?: RealtimeSequentializer;\n}\n\n/** Проверяет настройки потока. @throws {ItdConfigError} при некорректных значениях */\nfunction validateRealtimeOptions(options: RealtimeOptions): void {\n const positiveInteger = (value: number | undefined, name: string): void => {\n if (value === undefined) return;\n if (!Number.isInteger(value) || value < 0) {\n throw new ItdConfigError(\n `realtime.${name} должен быть целым неотрицательным числом, получено: ${value}`,\n );\n }\n };\n\n const duration = (value: number | undefined, name: string, min: number): void => {\n if (value === undefined) return;\n if (!Number.isFinite(value) || value < min) {\n throw new ItdConfigError(\n `realtime.${name} должен быть числом не меньше ${min}, получено: ${value}`,\n );\n }\n };\n\n positiveInteger(options.maxAttempts, 'maxAttempts');\n positiveInteger(options.concurrency, 'concurrency');\n if (options.concurrency === 0) {\n throw new ItdConfigError('realtime.concurrency должен быть больше нуля');\n }\n duration(options.pollInterval, 'pollInterval', 1);\n duration(options.idleTimeout, 'idleTimeout', 0);\n duration(options.handshakeTimeout, 'handshakeTimeout', 0);\n\n if (options.jitter !== undefined && !(options.jitter >= 0 && options.jitter <= 1)) {\n throw new ItdConfigError(\n `realtime.jitter должен быть в диапазоне 0…1, получено: ${options.jitter}`,\n );\n }\n\n if (options.backoff !== undefined) {\n if (!Array.isArray(options.backoff) || options.backoff.length === 0) {\n throw new ItdConfigError('realtime.backoff должен быть непустым списком пауз');\n }\n for (const delay of options.backoff) duration(delay, 'backoff', 0);\n }\n\n if (options.sequentialize !== undefined && typeof options.sequentialize !== 'function') {\n throw new ItdConfigError('realtime.sequentialize должен быть функцией');\n }\n}\n\n/** Что поток получает от клиента. */\nexport interface RealtimeDeps {\n baseUrl: string;\n fetch: typeof fetch;\n clock?: ItdClock;\n /** Общие заголовки клиента для адреса — см. {@link TransportContext.baseHeaders}. */\n baseHeaders: (url: string) => Promise<Headers>;\n /** Идентификаторы аккаунта и сессии создавшего поток клиента. */\n getAuthIdentity?: (() => AuthIdentity) | undefined;\n /** Непрозрачная область авторизации создавшего поток клиента. */\n getAuthScope?: (() => string) | undefined;\n getToken: () => Promise<string | null>;\n /** Обновляет токен после отказа авторизации. Возвращает `true`, если удалось. */\n refresh: () => Promise<boolean>;\n /** Загружает начальное число непрочитанных. */\n fetchUnreadCount: () => Promise<number>;\n /** Вызывается при явном закрытии потока. */\n onClose?: (() => void) | undefined;\n /** Вызывается при запуске ранее закрытого потока. */\n onConnect?: (() => void) | undefined;\n logger?: Logger | undefined;\n}\n\n/**\n * Поток уведомлений в реальном времени.\n *\n * Получается вызовом `itd.realtime()`. Соединение поднимается методом {@link connect}\n * и держится само: обрывы, обновление токена и повторные попытки библиотека берёт на себя.\n *\n * @example\n * ```ts\n * import { NotificationType } from 'itd-api';\n *\n * const stream = itd.realtime();\n *\n * stream.onNotification(NotificationType.PostComment, async ({ update }) => {\n * await saveCommentNotification(update.data.notification);\n * });\n * stream.on('status', (status) => console.log('соединение:', status));\n *\n * await stream.connect();\n * // …позже\n * stream.disconnect();\n * await stream.drain();\n * ```\n */\nexport class ItdRealtime {\n readonly #deps: RealtimeDeps;\n readonly #options: RealtimeOptions;\n readonly #emitter: Emitter<RealtimeEvents>;\n readonly #dispatcher: RealtimeDispatcher;\n readonly #transport: RealtimeTransport;\n readonly #maxAttempts: number;\n\n #controller: AbortController | undefined;\n /**\n * Хочет ли вызывающий код, чтобы соединение было живо.\n *\n * Отдельно от `#controller`, потому что тот появляется только после `await` внутри\n * {@link connect}. Без этого флага два вызова подряд проскочили бы проверку оба\n * и подняли два соединения, а `disconnect()` во время ожидания счётчика не был бы\n * замечен и соединение поднялось бы уже после отмены.\n */\n #wanted = false;\n #status: RealtimeStatus = RealtimeStatus.Disconnected;\n #attempt = 0;\n #cancelTimer: (() => void) | undefined;\n #detachEnvironment: (() => void) | undefined;\n\n constructor(deps: RealtimeDeps, options: RealtimeOptions = {}) {\n validateRealtimeOptions(options);\n\n this.#deps = deps;\n this.#options = options;\n this.#maxAttempts = options.maxAttempts ?? MAX_RECONNECT_ATTEMPTS;\n this.#transport = this.#createTransport();\n // Исключение из пользовательского обработчика — в логгер, при его отсутствии в консоль.\n this.#emitter = new Emitter<RealtimeEvents>((error) => {\n const message = 'Ошибка в обработчике события realtime';\n if (deps.logger) deps.logger.error(message, error);\n else console.error(`[itd-api] ${message}`, error);\n });\n this.#dispatcher = new RealtimeDispatcher(\n {\n concurrency: options.concurrency ?? 1,\n ...(options.sequentialize ? { sequentialize: options.sequentialize } : {}),\n },\n {\n deliver: (context) => this.#deliver(context.update),\n middlewareError: (error, context) =>\n this.#reportDispatchError('middlewareError', error, context),\n handlerError: (error, context) => this.#reportDispatchError('handlerError', error, context),\n },\n );\n }\n\n /** Текущее состояние соединения. */\n get status(): RealtimeStatus {\n return this.#status;\n }\n\n /** Какой транспорт используется: `sse` или `poll`. */\n get transport(): string {\n return this.#transport.name;\n }\n\n /** Базовый URL клиента, создавшего поток. @internal */\n get baseUrl(): string {\n return this.#deps.baseUrl;\n }\n\n /** Идентификаторы аккаунта и сессии клиента, создавшего поток. @internal */\n getAuthIdentity(): AuthIdentity | undefined {\n return this.#deps.getAuthIdentity?.();\n }\n\n /** Непрозрачная область авторизации создавшего поток клиента. @internal */\n getAuthScope(): string | undefined {\n return this.#deps.getAuthScope?.();\n }\n\n /** Подписывается на событие потока. @returns функция отписки */\n on<K extends keyof RealtimeEvents>(event: K, listener: Listener<RealtimeEvents[K]>): Unsubscribe {\n return this.#emitter.on(event, listener);\n }\n\n /** Подписывается на одно срабатывание. */\n once<K extends keyof RealtimeEvents>(\n event: K,\n listener: Listener<RealtimeEvents[K]>,\n ): Unsubscribe {\n return this.#emitter.once(event, listener);\n }\n\n /**\n * Добавляет промежуточный обработчик нормализованных обновлений.\n *\n * Обработчики выполняются в порядке регистрации. Если `next()` не вызван, обновление не\n * передаётся дальше по цепочке, асинхронным обработчикам и слушателям событий.\n *\n * @returns функция удаления обработчика\n */\n use(middleware: RealtimeMiddleware): Unsubscribe {\n if (typeof middleware !== 'function') {\n throw new ItdConfigError('realtime.use() принимает функцию обработки');\n }\n return this.#dispatcher.use(middleware);\n }\n\n /** Подписывает асинхронный обработчик на все нормализованные обновления. */\n onUpdate(handler: RealtimeHandler): Unsubscribe;\n /** Подписывает асинхронный обработчик на обновление указанного типа. */\n onUpdate<T extends RealtimeUpdateType>(\n type: T,\n handler: RealtimeHandler<RealtimeContext<RealtimeUpdateOfType<T>>>,\n ): Unsubscribe;\n /** Подписывает асинхронный обработчик по функции сужения типа. */\n onUpdate<C extends RealtimeContext>(\n guard: RealtimeTypeGuard<C>,\n handler: RealtimeHandler<C>,\n ): Unsubscribe;\n /** Подписывает асинхронный обработчик по пользовательскому условию. */\n onUpdate(predicate: RealtimePredicate, handler: RealtimeHandler): Unsubscribe;\n onUpdate(\n selectorOrHandler: RealtimeUpdateType | RealtimePredicate | RealtimeHandler,\n selectedHandler?: RealtimeHandler,\n ): Unsubscribe {\n const selectAll = selectedHandler === undefined;\n const handler = selectAll ? (selectorOrHandler as RealtimeHandler) : selectedHandler;\n if (typeof handler !== 'function') {\n throw new ItdConfigError('realtime.onUpdate() принимает функцию обработчика');\n }\n if (\n !selectAll &&\n typeof selectorOrHandler !== 'function' &&\n !Object.values(RealtimeUpdateType).includes(selectorOrHandler)\n ) {\n throw new ItdConfigError(`Неизвестный тип обновления потока: ${String(selectorOrHandler)}`);\n }\n\n let predicate: RealtimePredicate;\n if (selectAll) {\n predicate = () => true;\n } else {\n const selector = selectorOrHandler as RealtimeUpdateType | RealtimePredicate;\n predicate =\n typeof selector === 'function' ? selector : (context) => context.update.type === selector;\n }\n\n return this.#dispatcher.on(predicate, handler);\n }\n\n /** Подписывает асинхронный обработчик на уведомления, подходящие под фильтр. */\n onNotification<T extends NotificationType>(\n selector: RealtimeNotificationSelector<T>,\n handler: RealtimeHandler<RealtimeNotificationContext<T>>,\n ): Unsubscribe;\n /** Подписывает асинхронный обработчик по функции сужения типа уведомления. */\n onNotification<C extends RealtimeNotificationContext>(\n guard: (context: RealtimeNotificationContext) => context is C,\n handler: RealtimeHandler<C>,\n ): Unsubscribe;\n /** Подписывает асинхронный обработчик по пользовательскому условию. */\n onNotification(\n predicate: (context: RealtimeNotificationContext) => boolean,\n handler: RealtimeHandler<RealtimeNotificationContext>,\n ): Unsubscribe;\n onNotification(\n selector: RealtimeNotificationSelector | ((context: RealtimeNotificationContext) => boolean),\n handler: (context: never) => unknown | Promise<unknown>,\n ): Unsubscribe {\n if (typeof handler !== 'function') {\n throw new ItdConfigError('realtime.onNotification() принимает функцию обработчика');\n }\n if (typeof selector !== 'function') validateNotificationSelector(selector);\n\n const predicate: RealtimePredicate = (context) => {\n if (!isNotificationContext(context)) return false;\n return typeof selector === 'function'\n ? selector(context)\n : matchesNotification(context, selector);\n };\n\n return this.#dispatcher.on(predicate, handler as RealtimeHandler);\n }\n\n /**\n * Поднимает соединение.\n *\n * Повторный вызов при уже живом соединении ничего не делает — это защита от двойного\n * подключения при перерисовке интерфейса.\n *\n * Возвращает управление сразу после запуска: соединение живёт в фоне.\n */\n async connect(): Promise<void> {\n if (this.#wanted) return;\n this.#wanted = true;\n this.#deps.onConnect?.();\n\n this.#attachEnvironmentListeners();\n\n if (this.#options.syncCount !== false) {\n try {\n const count = await this.#deps.fetchUnreadCount();\n if (this.#wanted) {\n this.#dispatch(\n { type: RealtimeUpdateType.UnreadCount, data: count },\n undefined,\n RealtimeUpdateOrigin.Sync,\n );\n }\n } catch (error) {\n // Начальный счётчик — вспомогательная величина, из-за неё поток не отменяется.\n this.#deps.logger?.debug('не удалось получить число непрочитанных', error);\n }\n }\n\n // Пока ждали счётчик, могли успеть вызвать disconnect().\n if (this.#wanted) this.#run();\n }\n\n /** Закрывает соединение и отменяет запланированные попытки. */\n disconnect(): void {\n this.#wanted = false;\n\n if (this.#cancelTimer) {\n this.#cancelTimer();\n this.#cancelTimer = undefined;\n }\n\n this.#detachEnvironment?.();\n this.#detachEnvironment = undefined;\n\n this.#controller?.abort();\n this.#controller = undefined;\n this.#attempt = 0;\n this.#dispatcher.clearPending();\n\n this.#setStatus(RealtimeStatus.Disconnected);\n this.#deps.onClose?.();\n }\n\n /** Ждёт завершения всех принятых обновлений. */\n drain(): Promise<void> {\n return this.#dispatcher.drain();\n }\n\n /** Снимает подписки `on()` и `once()`. Остальные обработчики остаются. */\n removeAllListeners(): void {\n this.#emitter.removeAllListeners();\n }\n\n #createTransport(): RealtimeTransport {\n const kind = this.#options.transport ?? RealtimeTransportKind.Auto;\n\n if (typeof kind === 'object') return kind;\n\n if (\n kind === RealtimeTransportKind.Poll ||\n (kind === RealtimeTransportKind.Auto && !supportsStreamingBody())\n ) {\n return new PollTransport({\n clock: this.#deps.clock ?? systemClock,\n ...(this.#options.pollInterval !== undefined\n ? { interval: this.#options.pollInterval }\n : {}),\n });\n }\n\n return new SseTransport({\n clock: this.#deps.clock ?? systemClock,\n ...(this.#options.idleTimeout !== undefined\n ? { idleTimeout: this.#options.idleTimeout }\n : {}),\n ...(this.#options.handshakeTimeout !== undefined\n ? { handshakeTimeout: this.#options.handshakeTimeout }\n : {}),\n });\n }\n\n /** Запускает попытку подключения; повторы планирует сам. */\n #run(): void {\n if (!this.#wanted) return;\n\n // Страховка от потерянного соединения: если предыдущее ещё живо, закрываем его,\n // иначе его AbortController остался бы недостижимым и поток — незакрытым.\n this.#controller?.abort();\n\n const controller = new AbortController();\n this.#controller = controller;\n this.#setStatus(RealtimeStatus.Connecting);\n\n void this.#transport\n .connect({\n baseUrl: this.#deps.baseUrl,\n fetch: this.#deps.fetch,\n baseHeaders: this.#deps.baseHeaders,\n getToken: this.#deps.getToken,\n signal: controller.signal,\n onOpen: () => {\n this.#attempt = 0;\n this.#setStatus(RealtimeStatus.Connected);\n },\n onEvent: (event) => this.#handleEvent(event),\n onParseError: (error, raw) => this.#emitter.emit('parseError', { error, raw }),\n })\n .then(\n () => {\n // Штатное закрытие потока — тоже повод переподключиться.\n if (!controller.signal.aborted) {\n this.#handleFailure(new Error('Соединение с потоком уведомлений закрыто'));\n }\n },\n (error: unknown) => {\n if (controller.signal.aborted) return;\n this.#handleFailure(error);\n },\n );\n }\n\n #handleEvent(event: TransportEvent): void {\n if (!this.#wanted) return;\n this.#emitter.emit('message', event);\n\n if (event.name === 'connected') {\n // Строго строка: `String(null)` дал бы подписчику осмысленно выглядящее «null».\n this.#emitter.emit('ready', { userId: pickString(event.data, 'userId') });\n return;\n }\n\n const update = readRealtimeUpdate(event);\n if (update) this.#dispatch(update, event, RealtimeUpdateOrigin.Stream);\n }\n\n #dispatch(\n update: RealtimeUpdate,\n raw: TransportEvent | undefined,\n origin: RealtimeUpdateOrigin,\n ): void {\n this.#dispatcher.dispatch({ update, stream: this, raw, origin });\n }\n\n #deliver(update: RealtimeUpdate): void {\n if (update.type === RealtimeUpdateType.Notification) {\n this.#emitter.emit('notification', update.data);\n if (update.data.unreadCount !== undefined) {\n this.#emitter.emit('unreadCount', update.data.unreadCount);\n }\n return;\n }\n\n if (update.type === RealtimeUpdateType.UnreadCount) {\n this.#emitter.emit('unreadCount', update.data);\n return;\n }\n\n if (update.type === RealtimeUpdateType.Unknown) return;\n\n assertNeverUpdate(update);\n }\n\n #reportDispatchError(\n event: 'middlewareError' | 'handlerError',\n error: unknown,\n context: RealtimeContext,\n ): void {\n if (this.#emitter.listenerCount(event) > 0) {\n this.#emitter.emit(event, { error, context });\n return;\n }\n\n const message =\n event === 'middlewareError'\n ? 'Ошибка в промежуточном обработчике потока'\n : 'Ошибка в обработчике обновления потока';\n if (this.#deps.logger) this.#deps.logger.error(message, error);\n else console.error(`[itd-api] ${message}`, error);\n }\n\n #handleFailure(error: unknown): void {\n this.#controller = undefined;\n\n if (error instanceof UnauthorizedStreamError) {\n void this.#refreshAndReconnect(error);\n return;\n }\n\n this.#setStatus(RealtimeStatus.Error);\n this.#scheduleReconnect(error);\n }\n\n /** Обновляет токен и переподключается; при неудаче прекращает попытки. */\n async #refreshAndReconnect(error: unknown): Promise<void> {\n this.#setStatus(RealtimeStatus.Error);\n\n const refreshed = await this.#deps.refresh().catch(() => false);\n\n if (!this.#wanted) return;\n\n if (!refreshed) {\n this.#giveUp(error);\n return;\n }\n\n this.#scheduleReconnect(error);\n }\n\n #scheduleReconnect(error: unknown): void {\n if (!this.#wanted) return;\n\n if (this.#attempt >= this.#maxAttempts) {\n this.#giveUp(error);\n return;\n }\n\n const delay = reconnectDelay(this.#attempt, this.#options);\n this.#attempt += 1;\n\n this.#emitter.emit('error', { error, willReconnect: true });\n this.#emitter.emit('reconnect', { attempt: this.#attempt, delay });\n\n this.#cancelTimer = (this.#deps.clock ?? systemClock).schedule(() => {\n this.#cancelTimer = undefined;\n this.#run();\n }, delay);\n }\n\n /** Завершает автоматические попытки переподключения. */\n #giveUp(error: unknown): void {\n this.#wanted = false;\n this.#attempt = 0;\n\n this.#detachEnvironment?.();\n this.#detachEnvironment = undefined;\n\n this.#emitter.emit('error', { error, willReconnect: false });\n this.#emitter.emit('giveup', undefined);\n }\n\n /**\n * Подписывается на события среды.\n *\n * Возврат вкладки из фона и восстановление сети — самые частые причины «мёртвого»\n * соединения. У сайта итд.com такой обработки нет.\n */\n #attachEnvironmentListeners(): void {\n if (this.#detachEnvironment) return;\n\n const target = globalThis as unknown as {\n addEventListener?: (type: string, listener: () => void) => void;\n removeEventListener?: (type: string, listener: () => void) => void;\n document?: { visibilityState?: string };\n };\n\n if (typeof target.addEventListener !== 'function') return;\n\n const wake = () => {\n // Реагируем, только если соединения сейчас нет и попытка не запланирована.\n if (this.#controller || this.#cancelTimer) return;\n if (this.#status === RealtimeStatus.Disconnected) return;\n\n this.#attempt = 0;\n this.#run();\n };\n\n const onVisibility = () => {\n if (target.document?.visibilityState === 'visible') wake();\n };\n\n const listeners: [string, () => void][] = [];\n\n if (this.#options.reconnectOnVisible !== false && target.document) {\n listeners.push(['visibilitychange', onVisibility]);\n }\n if (this.#options.reconnectOnOnline !== false) {\n listeners.push(['online', wake]);\n }\n\n for (const [type, listener] of listeners) target.addEventListener(type, listener);\n\n this.#detachEnvironment = () => {\n for (const [type, listener] of listeners) target.removeEventListener?.(type, listener);\n };\n }\n\n #setStatus(status: RealtimeStatus): void {\n if (this.#status === status) return;\n this.#status = status;\n this.#emitter.emit('status', status);\n }\n}\n\nfunction assertNeverUpdate(update: never): never {\n throw new TypeError(`Необработанное обновление потока: ${String(update)}`);\n}\n","import { ItdConfigError } from './errors.js';\nimport { pickArray, pickBoolean, pickNumber, pickObject, pickString } from './unwrap.js';\n\n/**\n * Страница списка — единая форма для всех трёх схем пагинации API.\n *\n * Какие необязательные поля заполнены, зависит от эндпоинта: у ленты это `nextCursor`,\n * у подписчиков — `page` и `total`, у уведомлений — `nextOffset`. Обычно они не нужны:\n * перебор берёт на себя {@link Paginator}.\n */\nexport interface Page<T> {\n /** Элементы страницы. */\n items: T[];\n /** Есть ли следующая страница. */\n hasMore: boolean;\n /**\n * Курсор следующей страницы.\n *\n * Непрозрачен: у вкладки `popular` это номер страницы, у `following` — отметка времени.\n * Передавайте его обратно как есть и не пытайтесь разобрать.\n */\n nextCursor?: string | null | undefined;\n /** Номер текущей страницы при постраничной схеме. */\n page?: number | undefined;\n /** Запрошенный размер страницы. */\n limit?: number | undefined;\n /** Общее число элементов, если сервер его сообщил. */\n total?: number | undefined;\n /** Смещение для следующего запроса при схеме со смещением. */\n nextOffset?: number | undefined;\n /** Исходный ответ — на случай, если документация разошлась с реальностью. */\n raw: unknown;\n}\n\n/** Схема пагинации эндпоинта. */\nexport const PaginationMode = Object.freeze({\n /** Следующая страница запрашивается непрозрачным курсором. */\n Cursor: 'cursor',\n /** Следующая страница запрашивается номером. */\n Page: 'page',\n /** Следующая страница запрашивается смещением от начала списка. */\n Offset: 'offset',\n} as const);\nexport type PaginationMode = (typeof PaginationMode)[keyof typeof PaginationMode];\n\n/** Применяет преобразование к элементам страницы, сохраняя сведения о пагинации. */\nexport function mapPage<T, R>(page: Page<T>, map: (item: T) => R): Page<R> {\n return { ...page, items: page.items.map(map) };\n}\n\n/** Позиция, с которой запрашивается очередная страница. */\nexport interface PageState {\n cursor?: string | undefined;\n page?: number | undefined;\n offset?: number | undefined;\n}\n\n/** Настройки перебора страниц. */\nexport interface PaginatorOptions<T> {\n mode: PaginationMode;\n /** Загружает одну страницу для указанной позиции. */\n load: (state: PageState) => Promise<Page<T>>;\n /**\n * С какой позиции начать. По умолчанию с начала списка.\n *\n * Нужно, чтобы перебор можно было продолжить с сохранённого курсора, а не только\n * начать заново.\n */\n start?: PageState | undefined;\n /**\n * Предохранитель от бесконечного перебора. По умолчанию 1000.\n *\n * Сработает, только если сервер бесконечно сообщает `hasMore` — при нормальной работе\n * перебор останавливается сам.\n */\n maxPages?: number | undefined;\n /** Отмена перебора. */\n signal?: AbortSignal | undefined;\n}\n\n/**\n * Достаёт список из ответа, перебирая все формы, которые встречаются у сервера.\n *\n * Список может прийти под именем сущности, под её альтернативным именем\n * (`followers` вместо `users`) либо голым массивом.\n *\n * @param fields имена полей в порядке предпочтения\n */\nfunction readItems<T>(body: unknown, fields: readonly string[]): T[] {\n if (Array.isArray(body)) return body as T[];\n\n for (const field of fields) {\n const items = pickArray<T>(body, field);\n if (items.length > 0) return items;\n }\n\n // Пустой список тоже валиден: возвращаем по первому известному имени.\n const primary = fields[0];\n return primary === undefined ? [] : pickArray<T>(body, primary);\n}\n\n/**\n * Достаёт курсор следующей страницы.\n *\n * Проверяются все известные места: внутри `pagination`, рядом со списком и во вложенном\n * объекте `meta.cursor.next`.\n */\nfunction readCursor(body: unknown): string | null {\n const pagination = pickObject(body, 'pagination');\n const meta = pickObject(body, 'meta');\n const metaCursor = pickObject(meta, 'cursor');\n\n return (\n pickString(pagination, 'nextCursor') ??\n pickString(body, 'nextCursor') ??\n pickString(body, 'cursor') ??\n pickString(metaCursor, 'next') ??\n null\n );\n}\n\n/**\n * Читает страницу курсорной схемы.\n *\n * Используется лентой, постами пользователя и постами по хэштегу.\n *\n * @param fields имена возможных полей со списком; первое — основное\n */\nexport function readCursorPage<T>(body: unknown, ...fields: string[]): Page<T> {\n const pagination = pickObject(body, 'pagination');\n const items = readItems<T>(body, fields);\n const nextCursor = readCursor(body);\n\n return {\n items,\n // Если признака продолжения нет, ориентируемся на наличие курсора.\n hasMore: pickBoolean(pagination, 'hasMore', pickBoolean(body, 'hasMore', nextCursor !== null)),\n nextCursor,\n limit: pickNumber(pagination, 'limit', 0) || undefined,\n raw: body,\n };\n}\n\n/**\n * Читает страницу курсорной схемы с курсором рядом со списком.\n *\n * Так устроены комментарии к посту и ответы на комментарий: `nextCursor` и `hasMore`\n * лежат на одном уровне со списком, а не внутри объекта `pagination`.\n */\nexport function readFlatCursorPage<T>(body: unknown, ...fields: string[]): Page<T> {\n const items = readItems<T>(body, fields);\n const nextCursor = readCursor(body);\n const total = pickNumber(body, 'total', -1);\n\n return {\n items,\n hasMore: pickBoolean(body, 'hasMore', nextCursor !== null),\n nextCursor,\n ...(total >= 0 ? { total } : {}),\n raw: body,\n };\n}\n\n/**\n * Читает страницу постраничной схемы: `data.pagination.{page,limit,total,hasMore}`.\n *\n * Если сервер вместо этого прислал курсор, он тоже подхватывается — некоторые списки\n * отвечают в курсорной форме, хотя документация описывает постраничную.\n */\nexport function readPagedPage<T>(body: unknown, ...fields: string[]): Page<T> {\n const pagination = pickObject(body, 'pagination');\n const items = readItems<T>(body, fields);\n const nextCursor = readCursor(body);\n\n return {\n items,\n hasMore: pickBoolean(pagination, 'hasMore', pickBoolean(body, 'hasMore', nextCursor !== null)),\n page: pickNumber(pagination, 'page', 1),\n limit: pickNumber(pagination, 'limit', 0) || undefined,\n total: pickNumber(pagination, 'total', 0),\n ...(nextCursor !== null ? { nextCursor } : {}),\n raw: body,\n };\n}\n\n/**\n * Читает страницу схемы со смещением.\n *\n * Используется для списков со смещением, включая уведомления.\n */\nexport function readOffsetPage<T>(body: unknown, field: string, offset: number): Page<T> {\n const items = pickArray<T>(body, field);\n\n return {\n items,\n hasMore: pickBoolean(body, 'hasMore'),\n nextOffset: offset + items.length,\n raw: body,\n };\n}\n\n/**\n * Перебор страниц списка.\n *\n * Скрывает различия трёх схем пагинации: перебор элементов, страниц и сбор в массив\n * выглядят одинаково независимо от эндпоинта.\n *\n * **Одноразовый.** Позиция хранится внутри, поэтому повторный перебор того же объекта\n * ничего не вернёт: он продолжится с места, где закончился прошлый. Нужен второй проход —\n * возьмите новый перебор у того же метода ресурса.\n *\n * @example Перебор элементов\n * ```ts\n * for await (const post of itd.posts.iterate({ tab: 'following' })) {\n * console.log(post.content);\n * }\n * ```\n *\n * @example Первые сто элементов\n * ```ts\n * const posts = await itd.posts.iterate({ tab: 'popular' }).collect(100);\n * ```\n *\n * @example Постранично\n * ```ts\n * for await (const page of itd.users.followers('nowkie').pages()) {\n * console.log(page.items.length, 'из', page.total);\n * }\n * ```\n */\nexport class Paginator<T> implements AsyncIterable<T> {\n readonly #options: PaginatorOptions<T>;\n readonly #maxPages: number;\n\n #state: PageState;\n #finished = false;\n #pagesLoaded = 0;\n\n constructor(options: PaginatorOptions<T>) {\n if (\n options.maxPages !== undefined &&\n (!Number.isInteger(options.maxPages) || options.maxPages < 1)\n ) {\n throw new ItdConfigError(\n `maxPages должен быть целым числом не меньше 1, получено: ${options.maxPages}`,\n );\n }\n\n this.#options = options;\n this.#maxPages = options.maxPages ?? 1000;\n this.#state = options.start ?? {};\n }\n\n /**\n * Загружает следующую страницу.\n *\n * @returns страница либо `null`, если перебор закончен\n */\n async next(): Promise<Page<T> | null> {\n if (this.#finished) return null;\n if (this.#options.signal?.aborted) return null;\n\n if (this.#pagesLoaded >= this.#maxPages) {\n this.#finished = true;\n return null;\n }\n\n const previous = this.#state;\n const page = await this.#options.load(previous);\n this.#pagesLoaded += 1;\n\n this.#state = this.#advance(previous, page);\n return page;\n }\n\n /**\n * Перебирает страницы целиком.\n *\n * Полезно, когда нужны сведения о самой странице — например `total`.\n */\n async *pages(): AsyncGenerator<Page<T>, void, undefined> {\n for (;;) {\n const page = await this.next();\n if (!page) return;\n yield page;\n }\n }\n\n /** Перебирает элементы всех страниц подряд. */\n async *[Symbol.asyncIterator](): AsyncGenerator<T, void, undefined> {\n for await (const page of this.pages()) {\n for (const item of page.items) {\n if (this.#options.signal?.aborted) return;\n yield item;\n }\n }\n }\n\n /**\n * Собирает элементы в массив.\n *\n * @param max сколько элементов достаточно; без него перебираются все страницы\n */\n async collect(max?: number): Promise<T[]> {\n // Ноль и отрицательное число — «ничего не нужно»: не делаем ни одного запроса.\n if (max !== undefined && max <= 0) return [];\n\n const result: T[] = [];\n\n for await (const item of this) {\n result.push(item);\n if (max !== undefined && result.length >= max) break;\n }\n\n return result;\n }\n\n /**\n * Вычисляет позицию следующей страницы и решает, продолжать ли.\n *\n * Здесь же стоят предохранители: пустая страница при `hasMore`, неизменившийся курсор\n * и отсутствие курсора останавливают перебор. Без них ошибка на сервере превратилась бы\n * в бесконечный цикл запросов.\n */\n #advance(previous: PageState, page: Page<T>): PageState {\n if (!page.hasMore || page.items.length === 0) {\n this.#finished = true;\n return previous;\n }\n\n if (this.#options.mode === PaginationMode.Cursor) {\n const cursor = page.nextCursor ?? undefined;\n\n if (!cursor || cursor === previous.cursor) {\n this.#finished = true;\n return previous;\n }\n\n return { cursor };\n }\n\n if (this.#options.mode === PaginationMode.Page) {\n return { page: (previous.page ?? 1) + 1 };\n }\n\n return { offset: page.nextOffset ?? (previous.offset ?? 0) + page.items.length };\n }\n}\n","import type { ItdClock } from '../core/clock.js';\nimport type { RuntimeMode } from '../core/runtime.js';\nimport type { ServiceDefinition } from '../core/services.js';\nimport type { TokenStorage } from '../core/storage.js';\nimport type { QueryParams } from '../core/url.js';\n\n/**\n * Вход по логину и паролю.\n *\n * Вход требует токен капчи Cloudflare Turnstile, поэтому полностью автоматическим он быть\n * не может: капчу должен решить кто-то снаружи. Токен одноразовый и живёт несколько минут,\n * так что долгоживущему клиенту нужен `getTurnstileToken` — он спрашивается заново перед\n * каждой попыткой входа. Одиночный `turnstileToken` годится для разового скрипта.\n */\nexport interface CredentialsAuth {\n email: string;\n password: string;\n /** Разовый токен капчи. Для повторного входа после истечения сессии не подойдёт. */\n turnstileToken?: string | undefined;\n /** Источник свежего токена капчи. Спрашивается перед каждым входом. */\n getTurnstileToken?: (() => string | Promise<string>) | undefined;\n}\n\n/**\n * Как клиент получает доступ к API.\n *\n * Четыре формы — от разового вызова с готовым токеном до полноценной сессии, которую\n * библиотека заводит и продлевает сама.\n *\n * Опция необязательна: если {@link ItdClientOptions.storage} уже содержит сессию, доступ\n * берётся оттуда. Когда заданы обе, хранилище главнее — оно отражает текущее состояние\n * сессии, — а недостающие поля берутся отсюда.\n *\n * @example\n * ```ts\n * new ItdClient({ auth: '<accessToken>' }); // разовый вызов\n * new ItdClient({ auth: { accessToken, refreshToken } }); // восстановить сессию\n * new ItdClient({ auth: { email, password, getTurnstileToken } }); // залогиниться самому\n * new ItdClient({ auth: { getToken: () => vault.read() } }); // токен из внешнего источника\n * ```\n */\nexport type AuthInput =\n | string\n | { accessToken: string; refreshToken?: string | undefined }\n | CredentialsAuth\n | { getToken: () => string | null | Promise<string | null> };\n\n/** Куда библиотека пишет отладочные сообщения. Совместим с `console`. */\nexport interface Logger {\n debug(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n}\n\n/** Настройки повторных попыток. */\nexport interface RetryOptions {\n /** Сколько всего попыток, включая первую. По умолчанию 3. */\n attempts?: number | undefined;\n /** Базовая пауза в мс, удваивается с каждой попыткой. По умолчанию 500. */\n baseDelay?: number | undefined;\n /** Верхняя граница паузы в мс. По умолчанию 30000. */\n maxDelay?: number | undefined;\n /** Доля случайного разброса паузы, 0…1. По умолчанию 0.3. */\n jitter?: number | undefined;\n /**\n * Повторять ли запись (`POST`, `PUT`, `PATCH`, `DELETE`) при сетевых сбоях и `5xx`.\n *\n * По умолчанию `false`: сервер мог успеть выполнить операцию до обрыва, и повтор\n * создаст дубль поста или лишнюю жалобу. Ответ `429` повторяется всегда — он гарантирует,\n * что запрос не был обработан.\n */\n retryWrites?: boolean | undefined;\n /** Своя логика: вернуть `true`, чтобы повторить. Заменяет правила по умолчанию. */\n shouldRetry?: ((error: unknown, attempt: number) => boolean) | undefined;\n}\n\n/** Настройки ограничения нагрузки на API. */\nexport interface RateLimitOptions {\n /** Сколько запросов выполняется одновременно. По умолчанию 6. */\n concurrency?: number | undefined;\n /** Верхняя граница запросов в секунду. По умолчанию без ограничения. */\n rps?: number | undefined;\n /**\n * Паузы перед повторами при ответе `429`, мс.\n * По умолчанию `[1000, 5000, 30000, 60000, 90000]`.\n *\n * Сервер не сообщает, когда сбросится окно лимита, поэтому паузу приходится подбирать.\n * Лестница начинается с секунды: если окно почти истекло, работа продолжится почти\n * сразу, а если лимит исчерпан всерьёз — паузы дорастут до полутора минут.\n * Когда лестница закончилась, {@link ItdRateLimitError} пробрасывается вызывающему коду.\n *\n * Этот список не зависит от `retry.attempts`: тот управляет повторами при обрывах\n * сети и ошибках сервера, где уместен совсем другой темп.\n */\n retryDelays?: readonly number[] | undefined;\n /**\n * Тормозить ли очередь по заголовкам ответа. По умолчанию `true`.\n *\n * Выключите, если управляете темпом сами.\n */\n respectHeaders?: boolean | undefined;\n}\n\n/** Данные о запросе, доступные хукам. */\nexport interface RequestContext {\n method: string;\n /** Путь без базового URL, например `/api/posts`. */\n path: string;\n /** Итоговый URL со строкой запроса. */\n url: string;\n headers: Headers;\n /** Номер попытки, начиная с 1. */\n attempt: number;\n}\n\n/** Данные об успешном ответе. */\nexport interface ResponseContext extends RequestContext {\n status: number;\n /** Длительность запроса в мс. */\n duration: number;\n response: Response;\n}\n\n/** Данные об ошибке запроса. */\nexport interface ErrorContextHook extends RequestContext {\n duration: number;\n error: unknown;\n}\n\n/** Данные о предстоящем повторе. */\nexport interface RetryContext extends RequestContext {\n error: unknown;\n /** Пауза перед следующей попыткой в мс. */\n delay: number;\n}\n\n/**\n * Перехватчики жизненного цикла запроса.\n *\n * Вызываются последовательно; исключение внутри хука прервёт запрос, поэтому свою логику\n * лучше оборачивать в `try`.\n */\nexport interface ClientHooks {\n /** Перед отправкой. Можно дописать заголовки — объект `headers` изменяемый. */\n onRequest?(context: RequestContext): void | Promise<void>;\n /** После успешного ответа, до разбора тела. */\n onResponse?(context: ResponseContext): void | Promise<void>;\n /** При любой ошибке запроса, включая те, что будут повторены. */\n onError?(context: ErrorContextHook): void | Promise<void>;\n /** Перед паузой между попытками. */\n onRetry?(context: RetryContext): void | Promise<void>;\n}\n\n/**\n * Опции конструктора `ItdClient`.\n *\n * Все поля допускают явный `undefined`, чтобы можно было передавать значения, которых\n * может не быть, — например `new ItdClient({ auth: process.env.ITD_TOKEN })`.\n */\nexport interface ItdClientOptions {\n /**\n * Базовый URL API. По умолчанию `https://xn--d1ah4a.com`.\n *\n * Укажите здесь адрес своего прокси, если работаете из браузера: CORS для сторонних\n * источников на итд.com, скорее всего, не настроен.\n */\n baseUrl?: string | undefined;\n /**\n * Сервисы платформы на отдельных доменах.\n *\n * Ключ — имя сервиса, значение — базовый URL или определение целиком. Имя встроенного\n * сервиса задаёт его хост. Встроен один: `status` — хост `itd.platform.status()`.\n *\n * @example\n * ```ts\n * const itd = new ItdClient({\n * services: {\n * status: 'https://my-proxy.example/status',\n * pb: { baseUrl: 'https://pbapi.xn--d1ah4a.com', headers: { Referer: 'https://pixel.xn--d1ah4a.com/' } },\n * },\n * });\n * ```\n */\n services?: Record<string, string | Omit<ServiceDefinition, 'name'>> | undefined;\n /** Авторизация. Без неё доступны только публичные эндпоинты. */\n auth?: AuthInput | undefined;\n /** Где хранить сессию. По умолчанию {@link MemoryTokenStorage}. */\n storage?: TokenStorage | undefined;\n /**\n * Обновлять токен автоматически при ответе `401`. По умолчанию `true`.\n *\n * При `false` библиотека просто пробросит {@link ItdAuthError}, а обновлением\n * вы управляете сами через `itd.auth.refresh()`.\n */\n autoRefresh?: boolean | undefined;\n /**\n * Пытаться ли войти заново, если обновление токена не удалось.\n *\n * Работает, только когда в `auth` переданы email и пароль. По умолчанию `true`.\n */\n reloginOnRefreshFailure?: boolean | undefined;\n /** Своя реализация `fetch`: для Deno, React Native, тестов или прокси. */\n fetch?: typeof fetch | undefined;\n /** Часы для тайм-аутов, повторов и очередей. Обычно подменяются только в тестах. */\n clock?: ItdClock | undefined;\n /** Таймаут запроса в мс. По умолчанию 30000 — столько же использует сайт итд.com. `0` снимает ограничение. */\n timeout?: number | undefined;\n /** Повторные попытки. `false` отключает их полностью. */\n retry?: RetryOptions | false | undefined;\n /** Ограничение нагрузки. `false` отключает очередь. */\n rateLimit?: RateLimitOptions | false | undefined;\n /** Перехватчики запросов. */\n hooks?: ClientHooks | undefined;\n /** Отладочный вывод. `true` — писать в `console`. */\n logger?: Logger | boolean | undefined;\n /** Заголовки, добавляемые ко всем запросам, — например `User-Agent` для бота. */\n headers?: Record<string, string> | undefined;\n /**\n * Значение заголовка `X-Device-Id`, который уходит с каждым запросом.\n *\n * Сервер различает по нему записи в списке сессий, поэтому значение должно быть стабильным.\n * Если не задать, библиотека заведёт идентификатор сама и сохранит его в {@link ItdSession},\n * так что при постоянном хранилище он переживёт перезапуск процесса.\n */\n deviceId?: string | undefined;\n /**\n * Значение заголовка `User-Agent`. `false` — не отправлять его вовсе.\n *\n * По умолчанию `Mozilla/5.0 (compatible; itd-api/<версия>; …)`: `fetch` в Node не шлёт\n * `User-Agent` сам, а сайт стоит за DDoS-Guard, который такие запросы может не пропустить.\n * В браузере опция не действует — там заголовок менять запрещено.\n */\n userAgent?: string | false | undefined;\n /** Как обращаться с cookie. По умолчанию определяется по среде исполнения. */\n mode?: RuntimeMode | undefined;\n}\n\n/** Опции отдельного запроса. Доступны в каждом методе ресурсов. */\nexport interface RequestOptions {\n /** Отмена запроса извне. */\n signal?: AbortSignal | undefined;\n /** Таймаут только для этого запроса, мс. */\n timeout?: number | undefined;\n /** Дополнительные заголовки. */\n headers?: Record<string, string> | undefined;\n /** Повторы только для этого запроса. Переопределяют глобальную настройку `retry`. */\n retry?: RetryOptions | false | undefined;\n}\n\n/**\n * Имена полей {@link RequestOptions} — единственный источник истины.\n *\n * Ресурсы переносят в описание запроса только эти поля (плюс заявленные плагинами),\n * потому что параметры методов подмешивают к ним `limit`, `cursor` и прочее, чему\n * в транспорте делать нечего. Список стоит рядом с интерфейсом, чтобы новое поле нельзя\n * было забыть.\n *\n * `satisfies` гарантирует, что каждое имя в списке — действительно поле `RequestOptions`.\n * Обратную полноту (не забыто ли новое поле) проверяет тип {@link RequestOptionKeysComplete}\n * в тесте: здесь её проверять нельзя — плагины расширяют `RequestOptions` своими опциями\n * (`encrypt`, `decrypt` и подобными), которых в этом списке быть и не должно.\n */\nexport const REQUEST_OPTION_KEYS = [\n 'signal',\n 'timeout',\n 'headers',\n 'retry',\n] as const satisfies readonly (keyof RequestOptions)[];\n\n/**\n * Тип-страж полноты {@link REQUEST_OPTION_KEYS}: `true`, только когда список покрывает\n * все ключи `RequestOptions`. Проверяется в тесте, а не здесь, — в сборке плагина\n * интерфейс расширен, и strict-проверка на месте давала бы ложную ошибку.\n */\nexport type RequestOptionKeysComplete =\n keyof RequestOptions extends (typeof REQUEST_OPTION_KEYS)[number] ? true : never;\n\n/** Полное описание запроса для низкоуровневого `itd.request()`. */\nexport interface RawRequestOptions extends RequestOptions {\n method: string;\n /** Путь с ведущим слэшем, например `/api/posts`. Завершающий слэш значим. */\n path: string;\n /**\n * Имя сервиса, на хост которого уйдёт запрос. Без него запрос идёт на основной `baseUrl`\n * клиента. Сервисы задаются опцией {@link ItdClientOptions.services}.\n */\n service?: string | undefined;\n /**\n * Хост этого запроса. Важнее, чем {@link RawRequestOptions.service}.\n *\n * На посторонний основному API хост Bearer-токен по умолчанию не отправляется.\n * Для осознанного разрешения укажите `skipAuth: false`.\n */\n baseUrl?: string | undefined;\n query?: QueryParams | undefined;\n /** Тело: будет отправлено как JSON. Для загрузки файлов передайте `FormData`. */\n body?: unknown;\n /**\n * Не подставлять заголовок авторизации.\n *\n * Явное `false` разрешает авторизацию и для разового внешнего `baseUrl`; без него\n * токен автоматически отправляется только основному хосту и его поддоменам.\n */\n skipAuth?: boolean | undefined;\n /** Не пытаться обновить токен при `401` — используется самими эндпоинтами авторизации. */\n skipAuthRefresh?: boolean | undefined;\n /**\n * Выполнить запрос мимо очереди.\n *\n * Нужно запросам, которые слой авторизации делает **изнутри** другого запроса: продление\n * токена и отложенный вход. Такой запрос обязан стартовать, пока исходный держит место\n * в очереди и ждёт его результата, — иначе оба ждут друг друга и не завершатся никогда.\n */\n skipQueue?: boolean | undefined;\n /** Вернуть тело ответа без снятия обёртки `{ data: … }`. */\n raw?: boolean | undefined;\n}\n","import type { HttpClient } from '../core/http.js';\nimport type { Page, PageState, PaginationMode } from '../core/pagination.js';\nimport { Paginator } from '../core/pagination.js';\nimport type { QueryParams } from '../core/url.js';\nimport { REQUEST_OPTION_KEYS, type RequestOptions } from '../types/options.js';\n\n/** Параметры перебираемого списка: опции запроса плюс предел числа страниц. */\nexport interface ListParams extends RequestOptions {\n /** Ограничение числа страниц при переборе. */\n maxPages?: number | undefined;\n}\n\n/**\n * Описание перебираемого эндпоинта.\n *\n * Одно место, где заданы путь, параметры запроса, чтение страницы и схема пагинации.\n * {@link BaseResource.paginated} строит из него и разовую загрузку, и перебор.\n *\n * @typeParam T тип элемента списка\n * @typeParam P тип параметров метода\n */\nexport interface ListingSpec<T, P extends ListParams> {\n /** Путь эндпоинта. */\n path: (params: P) => string;\n /** Параметры запроса без полей пагинации — их добавит перебор. */\n query: (params: P) => QueryParams;\n /** Читает страницу из ответа. Получает позицию — она нужна схеме со смещением. */\n read: (body: unknown, state: PageState) => Page<T>;\n /** Схема пагинации эндпоинта. */\n mode: PaginationMode;\n /** Начальная позиция, вычисленная из параметров (курсор, номер или смещение). */\n start: (params: P) => PageState;\n}\n\n/** Пара методов, собранная из {@link ListingSpec}: разовая загрузка и перебор. */\nexport interface Listing<T, P extends ListParams> {\n /** Загружает одну страницу с позиции, заданной параметрами. */\n list(params: P): Promise<Page<T>>;\n /** Перебирает страницы, сама подставляя позиции. */\n iterate(params: P): Paginator<T>;\n}\n\n/** Общая основа всех групп методов клиента. */\nexport class BaseResource {\n /** @internal */\n protected readonly http: HttpClient;\n\n constructor(http: HttpClient) {\n this.http = http;\n }\n\n /**\n * Переносит опции запроса в описание транспорта.\n *\n * Копируются только поля {@link REQUEST_OPTION_KEYS} и опции, заявленные плагинами:\n * параметры методов наследуют {@link RequestOptions} и приносят с собой `limit`, `cursor`\n * и прочее, чему в описании запроса делать нечего. Чужие опции плагинов библиотека\n * не понимает, но обязана донести до обёрток нетронутыми.\n */\n protected requestOptions(options: RequestOptions | undefined): Partial<RequestOptions> {\n if (!options) return {};\n\n const source = options as Record<string, unknown>;\n const result: Record<string, unknown> = {};\n\n for (const key of REQUEST_OPTION_KEYS) {\n const value = source[key];\n if (value !== undefined) result[key] = value;\n }\n\n const pluginKeys = this.http.pluginOptionKeys;\n for (const key of pluginKeys) {\n const value = source[key];\n if (value !== undefined) result[key] = value;\n }\n\n return result as Partial<RequestOptions>;\n }\n\n /**\n * Собирает перебор страниц.\n *\n * @param mode схема пагинации эндпоинта\n * @param load загружает одну страницу для указанной позиции\n * @param options `maxPages` и `signal`, а также `start` — позиция, с которой продолжить\n */\n protected paginate<T>(\n mode: PaginationMode,\n load: (state: PageState) => Promise<Page<T>>,\n options?: RequestOptions & { maxPages?: number; start?: PageState },\n ): Paginator<T> {\n return new Paginator<T>({\n mode,\n load,\n ...(options?.maxPages !== undefined ? { maxPages: options.maxPages } : {}),\n ...(options?.signal !== undefined ? { signal: options.signal } : {}),\n ...(options?.start !== undefined ? { start: options.start } : {}),\n });\n }\n\n /**\n * Собирает пару «загрузка страницы + перебор» из одного описания.\n *\n * Путь, параметры запроса и разбор ответа задаются один раз; `list` и `iterate`\n * строятся из них.\n *\n * @example\n * ```ts\n * #feed = this.paginated<Post, FeedParams>({\n * path: () => '/api/posts',\n * query: (p) => ({ tab: p.tab, limit: p.limit }),\n * start: (p) => (p.cursor ? { cursor: p.cursor } : {}),\n * read: (body) => readCursorPage<Post>(body, 'posts'),\n * mode: PaginationMode.Cursor,\n * });\n * ```\n */\n protected paginated<T, P extends ListParams>(spec: ListingSpec<T, P>): Listing<T, P> {\n const load = async (params: P, state: PageState): Promise<Page<T>> => {\n const body = await this.http.request({\n method: 'GET',\n path: spec.path(params),\n query: withPageState(spec.query(params), state),\n ...this.requestOptions(params),\n });\n return spec.read(body, state);\n };\n\n return {\n list: (params) => load(params, spec.start(params)),\n iterate: (params) =>\n this.paginate<T>(spec.mode, (state) => load(params, state), {\n ...(params.maxPages !== undefined ? { maxPages: params.maxPages } : {}),\n ...(params.signal !== undefined ? { signal: params.signal } : {}),\n start: spec.start(params),\n }),\n };\n }\n}\n\n/** Добавляет позицию страницы в параметры запроса. */\nexport function withPageState(query: QueryParams, state: PageState): QueryParams {\n return {\n ...query,\n ...(state.cursor !== undefined ? { cursor: state.cursor } : {}),\n ...(state.page !== undefined ? { page: state.page } : {}),\n ...(state.offset !== undefined ? { offset: state.offset } : {}),\n };\n}\n","import { AUTH_PATHS, type AuthManager, type TURNSTILE_SITE_KEY } from '../core/auth.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport type { HttpClient } from '../core/http.js';\nimport { pickArray, pickString } from '../core/unwrap.js';\nimport type { Session } from '../models/account.js';\nimport type { AuthState } from '../models/users.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Учётные данные для входа. */\nexport interface Credentials {\n email: string;\n password: string;\n}\n\n/**\n * Учётные данные вместе с токеном капчи.\n *\n * `turnstileToken` обязателен: без него сервер отвечает `VALIDATION_ERROR`, с недействительным —\n * `TURNSTILE_VERIFICATION_FAILED`. Токен даёт виджет Cloudflare Turnstile с ключом\n * {@link TURNSTILE_SITE_KEY}; он одноразовый и живёт несколько минут.\n */\nexport interface CaptchaCredentials extends Credentials {\n turnstileToken: string;\n}\n\n/** Запрос письма для сброса пароля. Капча обязательна, как и при входе. */\nexport interface ForgotPasswordInput {\n email: string;\n turnstileToken: string;\n}\n\n/**\n * Установка нового пароля.\n *\n * Сброс идёт тем же потоком с кодом, что и вход: {@link AuthResource.forgotPassword} выдаёт\n * `flowToken`, письмо приносит `otp`, и всё это вместе с новым паролем уходит сюда.\n */\nexport interface ResetPasswordInput {\n email: string;\n otp: string;\n flowToken: string;\n newPassword: string;\n}\n\n/** Чем закончился вход: сразу токеном или запросом кода подтверждения. */\nexport const SignInStatus = Object.freeze({\n Authenticated: 'authenticated',\n OtpRequired: 'otp_required',\n} as const);\nexport type SignInStatus = (typeof SignInStatus)[keyof typeof SignInStatus];\n\n/**\n * Результат входа.\n *\n * Сервер может как сразу выдать токен, так и потребовать код подтверждения — размеченное\n * объединение делает оба случая явными.\n */\nexport type SignInResult =\n | { status: 'authenticated'; accessToken: string }\n | { status: 'otp_required'; flowToken: string | undefined };\n\n/**\n * Авторизация, сессии и пароли.\n *\n * Доступна как `itd.auth`.\n */\nexport class AuthResource extends BaseResource {\n readonly #auth: AuthManager;\n\n constructor(http: HttpClient, deps: { auth: AuthManager }) {\n super(http);\n this.#auth = deps.auth;\n }\n\n /**\n * Проверяет состояние авторизации и возвращает текущего пользователя.\n *\n * Без авторизации возвращает `authenticated: false` и `user: null`.\n */\n check(options: RequestOptions = {}): Promise<AuthState> {\n return this.http.request<AuthState>({\n method: 'GET',\n path: '/api/profile',\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Регистрирует аккаунт и запускает подтверждение по коду.\n *\n * @returns `flowToken`, который нужно передать в {@link verifyOtp}\n */\n async signUp(credentials: CaptchaCredentials, options: RequestOptions = {}): Promise<string> {\n const body = await this.http.request({\n method: 'POST',\n path: AUTH_PATHS.signUp,\n body: credentials,\n skipAuth: true,\n skipAuthRefresh: true,\n ...this.requestOptions(options),\n });\n\n const flowToken = pickString(body, 'flowToken');\n if (!flowToken) {\n throw new ItdConfigError('Сервер не вернул flowToken при регистрации');\n }\n\n return flowToken;\n }\n\n /**\n * Выполняет вход.\n *\n * Если сервер потребовал код подтверждения, вернётся `status: 'otp_required'` —\n * тогда продолжайте через {@link verifyOtp} либо воспользуйтесь {@link signInWithOtp}.\n *\n * При успешном входе токен сохраняется в клиенте автоматически.\n *\n * @param credentials email, пароль и обязательный токен капчи — см. {@link CaptchaCredentials}\n */\n async signIn(\n credentials: CaptchaCredentials,\n options: RequestOptions = {},\n ): Promise<SignInResult> {\n const body = await this.http.request({\n method: 'POST',\n path: AUTH_PATHS.signIn,\n body: credentials,\n skipAuth: true,\n skipAuthRefresh: true,\n ...this.requestOptions(options),\n });\n\n const accessToken = pickString(body, 'accessToken');\n\n if (accessToken) {\n await this.#auth.setAccessToken(accessToken);\n return { status: SignInStatus.Authenticated, accessToken };\n }\n\n return { status: SignInStatus.OtpRequired, flowToken: pickString(body, 'flowToken') };\n }\n\n /**\n * Подтверждает вход кодом из письма.\n *\n * Полученный токен сохраняется в клиенте автоматически.\n */\n async verifyOtp(\n input: Credentials & { otp: string; flowToken: string },\n options: RequestOptions = {},\n ): Promise<string> {\n const body = await this.http.request({\n method: 'POST',\n path: AUTH_PATHS.verifyOtp,\n body: input,\n skipAuth: true,\n skipAuthRefresh: true,\n ...this.requestOptions(options),\n });\n\n const accessToken = pickString(body, 'accessToken');\n if (!accessToken) {\n throw new ItdConfigError('Сервер не вернул accessToken после подтверждения кода');\n }\n\n await this.#auth.setAccessToken(accessToken);\n return accessToken;\n }\n\n /** Отправляет код подтверждения повторно. */\n resendOtp(\n input: { email: string; flowToken: string },\n options: RequestOptions = {},\n ): Promise<void> {\n return this.http.request<void>({\n method: 'POST',\n path: AUTH_PATHS.resendOtp,\n body: input,\n skipAuth: true,\n skipAuthRefresh: true,\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Полный вход с подтверждением по коду.\n *\n * Удобно для скриптов и ботов: код запрашивается функцией `getOtp`, а всё остальное\n * библиотека делает сама.\n *\n * @example\n * ```ts\n * import { createInterface } from 'node:readline/promises';\n *\n * const rl = createInterface({ input: process.stdin, output: process.stdout });\n *\n * const token = await itd.auth.signInWithOtp({\n * email, password,\n * getOtp: () => rl.question('Код из письма: '),\n * });\n * ```\n */\n async signInWithOtp(\n input: CaptchaCredentials & { getOtp: () => string | Promise<string> },\n options: RequestOptions = {},\n ): Promise<string> {\n const { getOtp, ...credentials } = input;\n const result = await this.signIn(credentials, options);\n\n if (result.status === SignInStatus.Authenticated) return result.accessToken;\n\n if (!result.flowToken) {\n throw new ItdConfigError(\n 'Сервер запросил код подтверждения, но не вернул flowToken — продолжить вход невозможно',\n );\n }\n\n const otp = await getOtp();\n\n // Токен капчи сюда не передаётся: он одноразовый и уже потрачен на sign-in,\n // а verify-otp капчу не требует.\n return this.verifyOtp(\n {\n email: credentials.email,\n password: credentials.password,\n otp,\n flowToken: result.flowToken,\n },\n options,\n );\n }\n\n /**\n * Обновляет токен доступа.\n *\n * Параллельные вызовы объединяются в один сетевой запрос. При включённом `autoRefresh`\n * вызывать вручную обычно не нужно.\n */\n refresh(): Promise<string> {\n return this.#auth.refresh();\n }\n\n /**\n * Есть ли признак живой сессии обновления.\n *\n * Проверяет cookie `is_auth`, которую сервер ставит рядом с refresh-токеном, а также\n * refresh-токен, переданный строкой. Позволяет не дёргать API у неавторизованного\n * пользователя. В браузере всегда `true`: cookie ведёт сама среда, и прочитать её\n * из JS нельзя.\n *\n * Читает {@link TokenStorage}, поэтому результат верен и до первого запроса.\n *\n * @example\n * ```ts\n * if (await itd.auth.hasRefreshSession()) await itd.auth.refresh();\n * else redirectToLogin();\n * ```\n */\n hasRefreshSession(): Promise<boolean> {\n return this.#auth.hasRefreshSession();\n }\n\n /** Завершает текущую сессию на сервере и очищает локальную. */\n async logout(options: RequestOptions = {}): Promise<void> {\n await this.http.request<void>({\n method: 'POST',\n path: AUTH_PATHS.logout,\n skipAuthRefresh: true,\n ...this.requestOptions(options),\n });\n\n await this.#auth.clear();\n }\n\n /**\n * Завершает все сессии пользователя и очищает локальную.\n *\n * Собран из двух запросов, потому что единого эндпоинта на сервере нет:\n * `POST /api/v1/auth/logout-all` отвечает `404`. Сначала отзываются все прочие сессии\n * (`DELETE /api/v1/auth/sessions`), затем завершается текущая — в обратном порядке\n * отзывать было бы уже нечем.\n */\n async logoutAll(options: RequestOptions = {}): Promise<void> {\n await this.revokeOtherSessions(options);\n await this.logout(options);\n }\n\n /** Забывает сессию локально, не обращаясь к серверу. */\n signOut(): Promise<void> {\n return this.#auth.clear();\n }\n\n /**\n * Запрашивает письмо с кодом для сброса пароля.\n *\n * @returns `flowToken`, который нужно передать в {@link resetPassword}\n */\n async forgotPassword(input: ForgotPasswordInput, options: RequestOptions = {}): Promise<string> {\n const body = await this.http.request({\n method: 'POST',\n path: AUTH_PATHS.forgotPassword,\n body: input,\n skipAuth: true,\n skipAuthRefresh: true,\n ...this.requestOptions(options),\n });\n\n const flowToken = pickString(body, 'flowToken');\n if (!flowToken) {\n throw new ItdConfigError('Сервер не вернул flowToken при запросе сброса пароля');\n }\n\n return flowToken;\n }\n\n /**\n * Устанавливает новый пароль по коду из письма.\n *\n * Сервер ждёт все четыре поля сразу — `email`, `otp`, `flowToken` и `newPassword`;\n * при нехватке любого отвечает `422`.\n */\n resetPassword(input: ResetPasswordInput, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'POST',\n path: AUTH_PATHS.resetPassword,\n body: input,\n skipAuth: true,\n skipAuthRefresh: true,\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Полный сброс пароля с кодом из письма.\n *\n * Тот же приём, что и {@link signInWithOtp}: код запрашивается функцией `getOtp`,\n * остальное библиотека делает сама.\n *\n * @example\n * ```ts\n * await itd.auth.resetPasswordWithOtp({\n * email,\n * turnstileToken,\n * newPassword,\n * getOtp: () => rl.question('Код из письма: '),\n * });\n * ```\n */\n async resetPasswordWithOtp(\n input: ForgotPasswordInput & {\n newPassword: string;\n getOtp: () => string | Promise<string>;\n },\n options: RequestOptions = {},\n ): Promise<void> {\n const flowToken = await this.forgotPassword(\n { email: input.email, turnstileToken: input.turnstileToken },\n options,\n );\n\n const otp = await input.getOtp();\n\n await this.resetPassword(\n { email: input.email, otp, flowToken, newPassword: input.newPassword },\n options,\n );\n }\n\n /**\n * Меняет пароль. Требует действующей сессии.\n *\n * При неверном текущем пароле сервер отвечает `ACCOUNT_CURRENT_PASSWORD_INCORRECT`.\n *\n * @example\n * ```ts\n * await itd.auth.changePassword({ currentPassword, newPassword });\n * ```\n */\n changePassword(\n input: { currentPassword: string; newPassword: string },\n options: RequestOptions = {},\n ): Promise<void> {\n return this.http.request<void>({\n method: 'POST',\n path: AUTH_PATHS.changePassword,\n body: { currentPassword: input.currentPassword, newPassword: input.newPassword },\n ...this.requestOptions(options),\n });\n }\n\n /** Загружает список активных сессий. У текущей поле `isCurrent` равно `true`. */\n async sessions(options: RequestOptions = {}): Promise<Session[]> {\n const body = await this.http.request({\n method: 'GET',\n path: AUTH_PATHS.sessions,\n ...this.requestOptions(options),\n });\n\n return pickArray<Session>(body, 'sessions');\n }\n\n /** Завершает указанную сессию. */\n revokeSession(sessionId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: `${AUTH_PATHS.sessions}/${encodeURIComponent(sessionId)}`,\n ...this.requestOptions(options),\n });\n }\n\n /** Завершает все сессии, кроме текущей. */\n revokeOtherSessions(options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: AUTH_PATHS.sessions,\n ...this.requestOptions(options),\n });\n }\n}\n","/** Метка билдера. Через `Symbol.for` — чтобы распознавание переживало смешивание ESM и CJS. */\nexport const BUILDER = Symbol.for('itd.builder');\n\n/**\n * Билдер входных данных.\n *\n * Билдеры необязательны: любой метод, принимающий билдер, принимает и обычный объект.\n * Проверки одинаковы в обоих случаях.\n */\nexport interface ItdBuilder<T> {\n /** @internal */\n readonly [BUILDER]: true;\n /**\n * Собирает и проверяет результат.\n *\n * @throws {ItdConfigError} если нарушены требования к данным\n */\n build(): T;\n /** Чтобы билдер корректно вёл себя внутри `JSON.stringify`. */\n toJSON(): T;\n}\n\n/**\n * Три равноправные формы входа: обычный объект, готовый билдер или функция-настройщик.\n *\n * @example\n * ```ts\n * itd.posts.create({ content: 'привет' }); // объект\n * itd.posts.create(post().content('привет')); // билдер\n * itd.posts.create((p) => p.content('привет')); // функция\n * ```\n */\nexport type BuilderInput<T, B extends ItdBuilder<T>> = T | B | ((builder: B) => B | T);\n\n/** Является ли значение билдером. */\nexport function isBuilder<T>(value: unknown): value is ItdBuilder<T> {\n return typeof value === 'object' && value !== null && BUILDER in value;\n}\n\n/**\n * Приводит любую из трёх форм входа к готовому объекту.\n *\n * Проверка выполняется всегда, независимо от формы: обычный объект проходит ровно те же\n * правила, что и результат билдера.\n *\n * @param input то, что передал пользователь\n * @param factory создаёт пустой билдер для формы с функцией\n * @param validate проверяет собранный объект\n */\nexport function resolveInput<T, B extends ItdBuilder<T>>(\n input: BuilderInput<T, B>,\n factory: () => B,\n validate: (value: T) => T,\n): T {\n if (typeof input === 'function') {\n const result = (input as (builder: B) => B | T)(factory());\n return isBuilder<T>(result) ? result.build() : validate(result as T);\n }\n\n if (isBuilder<T>(input)) return input.build();\n\n return validate(input as T);\n}\n","import type { FileInput } from '../core/attachments/contracts.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport type { UserId } from '../models/common.js';\nimport type { CreateCommentInput } from '../types/params.js';\nimport { BUILDER, type BuilderInput, type ItdBuilder, resolveInput } from './base.js';\n\n/** Внутреннее состояние {@link CommentBuilder}. */\ninterface CommentState extends CreateCommentInput {\n content: string;\n attachmentIds: string[];\n files: FileInput[];\n /** Голосовой комментарий: текста быть не должно, вложение ровно одно. */\n voice: boolean;\n}\n\n/**\n * Проверяет данные комментария.\n *\n * @throws {ItdConfigError} если комментарий пуст или голосовой собран неверно\n */\nexport function validateComment(input: CreateCommentInput): CreateCommentInput {\n const content = typeof input?.content === 'string' ? input.content : '';\n const attachmentIds = input?.attachmentIds ?? [];\n const files = input?.files ?? [];\n\n const hasContent = content.trim() !== '';\n const hasAttachments = attachmentIds.length > 0 || files.length > 0;\n\n if (!hasContent && !hasAttachments) {\n throw new ItdConfigError('Комментарий пуст: нужен текст или вложение');\n }\n\n return input;\n}\n\n/** Дополнительная проверка голосового комментария. */\nfunction validateVoice(state: CommentState): void {\n if (state.content.trim() !== '') {\n throw new ItdConfigError(\n 'У голосового комментария не может быть текста: API принимает либо текст, либо аудио',\n );\n }\n\n const total = state.files.length + state.attachmentIds.length;\n if (total !== 1) {\n throw new ItdConfigError(\n `Голосовой комментарий требует ровно одно аудиовложение, передано: ${total}`,\n );\n }\n}\n\n/**\n * Билдер комментария и ответа на комментарий.\n *\n * Неизменяемый: каждый вызов возвращает новый экземпляр. Создаётся функцией {@link comment}.\n */\nexport class CommentBuilder implements ItdBuilder<CreateCommentInput> {\n /** @internal */\n readonly [BUILDER] = true as const;\n\n readonly #state: CommentState;\n\n /** @internal Создавайте билдер функцией {@link comment}. */\n constructor(state: CommentState) {\n this.#state = state;\n }\n\n /** Задаёт текст комментария. */\n content(text: string): CommentBuilder {\n return new CommentBuilder({ ...this.#state, content: text });\n }\n\n /** Прикладывает файл — он будет загружен перед отправкой. */\n attach(file: FileInput): CommentBuilder {\n return new CommentBuilder({ ...this.#state, files: [...this.#state.files, file] });\n }\n\n /** Прикладывает уже загруженное вложение. */\n attachId(attachmentId: string): CommentBuilder {\n return new CommentBuilder({\n ...this.#state,\n attachmentIds: [...this.#state.attachmentIds, attachmentId],\n });\n }\n\n /**\n * Делает комментарий голосовым.\n *\n * Текста у такого комментария быть не должно, а вложение ровно одно — аудио в формате\n * `audio/ogg`. Так его принимает API.\n *\n * @example\n * ```ts\n * import { fromPath } from 'itd-api/node';\n *\n * await itd.posts.comment(postId, (c) => c.voice(fromPath('./answer.ogg')));\n * ```\n */\n voice(audio: FileInput): CommentBuilder {\n return new CommentBuilder({ ...this.#state, files: [audio], voice: true });\n }\n\n /**\n * Кому адресован ответ.\n *\n * Имеет смысл только в `itd.comments.reply()`; при отправке комментария к посту\n * это поле вызовет ошибку.\n */\n replyTo(userId: UserId): CommentBuilder {\n return new CommentBuilder({ ...this.#state, replyToUserId: userId });\n }\n\n build(): CreateCommentInput {\n const { content, attachmentIds, files, voice, ...rest } = this.#state;\n\n if (voice) validateVoice(this.#state);\n\n return validateComment({\n ...rest,\n content,\n ...(attachmentIds.length > 0 ? { attachmentIds } : {}),\n ...(files.length > 0 ? { files } : {}),\n });\n }\n\n toJSON(): CreateCommentInput {\n return this.build();\n }\n}\n\n/**\n * Начинает сборку комментария.\n *\n * @param content текст; можно задать позже методом {@link CommentBuilder.content}\n *\n * @example\n * ```ts\n * import { comment } from 'itd-api';\n *\n * await itd.posts.comment(postId, comment('согласен').attach({ url: memeUrl }));\n * ```\n */\nexport function comment(content = ''): CommentBuilder {\n return new CommentBuilder({ content, attachmentIds: [], files: [], voice: false });\n}\n\n/** Что принимает параметр комментария: объект, билдер или функция-настройщик. */\nexport type CommentInput = BuilderInput<CreateCommentInput, CommentBuilder>;\n\n/**\n * Приводит любую форму входа к готовым данным комментария.\n *\n * @param allowReplyTo разрешено ли поле `replyToUserId`; в комментарии к посту — нет\n */\nexport function resolveComment(input: CommentInput, allowReplyTo = false): CreateCommentInput {\n const resolved = resolveInput(input, () => comment(), validateComment);\n\n if (!allowReplyTo && resolved.replyToUserId !== undefined) {\n throw new ItdConfigError(\n 'replyTo применим только к ответу на комментарий (itd.comments.reply). ' +\n 'В комментарии к посту адресат не указывается.',\n );\n }\n\n return resolved;\n}\n","import { type CommentInput, resolveComment } from '../builders/comment.js';\nimport type { FileInput } from '../core/attachments/contracts.js';\nimport type { HttpClient } from '../core/http.js';\nimport { type Page, PaginationMode, type Paginator, readPagedPage } from '../core/pagination.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { Comment, LikeResult } from '../models/content.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Параметры запроса ответов на комментарий. */\nexport interface RepliesParams extends RequestOptions {\n limit?: number;\n page?: number;\n maxPages?: number;\n}\n\n/**\n * Комментарии и ответы на них.\n *\n * Доступна как `itd.comments`. Комментарии **к посту** живут в `itd.posts`:\n * `itd.posts.comments()` и `itd.posts.comment()`.\n */\nexport class CommentsResource extends BaseResource {\n readonly #uploadFiles: (files: FileInput[], options?: RequestOptions) => Promise<string[]>;\n\n /** Ответы на комментарий: `/api/comments/{id}/replies`, постраничная пагинация. */\n readonly #replies = this.paginated<Comment, RepliesParams & { commentId: string }>({\n path: (p) => `/api/comments/${encodePathSegment(p.commentId, 'commentId')}/replies`,\n query: (p) => ({ limit: p.limit }),\n start: (p) => (p.page !== undefined ? { page: p.page } : {}),\n read: (body) => readPagedPage<Comment>(body, 'replies'),\n mode: PaginationMode.Page,\n });\n\n constructor(\n http: HttpClient,\n deps: { uploadFiles: (files: FileInput[], options?: RequestOptions) => Promise<string[]> },\n ) {\n super(http);\n this.#uploadFiles = deps.uploadFiles;\n }\n\n /**\n * Загружает страницу ответов на комментарий.\n *\n * Здесь пагинация **постраничная**, в отличие от комментариев к посту, где курсорная.\n */\n replies(commentId: string, params: RepliesParams = {}): Promise<Page<Comment>> {\n return this.#replies.list({ ...params, commentId });\n }\n\n /** Перебирает ответы на комментарий. */\n iterateReplies(commentId: string, params: RepliesParams = {}): Paginator<Comment> {\n return this.#replies.iterate({ ...params, commentId });\n }\n\n /**\n * Отвечает на комментарий.\n *\n * @example\n * ```ts\n * await itd.comments.reply(commentId, 'согласен');\n * await itd.comments.reply(commentId, (c) => c.content('и вот почему').replyTo(userId));\n * ```\n */\n async reply(\n commentId: string,\n input: CommentInput | string,\n options: RequestOptions = {},\n ): Promise<Comment> {\n const data = resolveComment(typeof input === 'string' ? { content: input } : input, true);\n\n const existing = data.attachmentIds ?? [];\n const files = data.files ?? [];\n const attachmentIds =\n files.length > 0 ? [...existing, ...(await this.#uploadFiles(files, options))] : existing;\n\n return this.http.request<Comment>({\n method: 'POST',\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}/replies`,\n body: {\n content: data.content ?? '',\n attachmentIds,\n ...(data.replyToUserId ? { replyToUserId: data.replyToUserId } : {}),\n },\n ...this.requestOptions(options),\n });\n }\n\n /** Редактирует текст комментария. */\n update(commentId: string, content: string, options: RequestOptions = {}): Promise<Comment> {\n return this.http.request<Comment>({\n method: 'PATCH',\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}`,\n body: { content },\n ...this.requestOptions(options),\n });\n }\n\n /** Удаляет комментарий. Восстановить его можно через {@link restore}. */\n remove(commentId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}`,\n ...this.requestOptions(options),\n });\n }\n\n /** Восстанавливает удалённый комментарий. */\n restore(commentId: string, options: RequestOptions = {}): Promise<Comment> {\n return this.http.request<Comment>({\n method: 'POST',\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}/restore`,\n ...this.requestOptions(options),\n });\n }\n\n /** Ставит реакцию на комментарий. */\n like(commentId: string, options: RequestOptions = {}): Promise<LikeResult> {\n return this.http.request<LikeResult>({\n method: 'POST',\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}/like`,\n ...this.requestOptions(options),\n });\n }\n\n /** Убирает реакцию с комментария. */\n unlike(commentId: string, options: RequestOptions = {}): Promise<LikeResult> {\n return this.http.request<LikeResult>({\n method: 'DELETE',\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}/like`,\n ...this.requestOptions(options),\n });\n }\n}\n","import { ItdConfigError, ItdFileError, ItdFileErrorReason } from '../errors.js';\nimport { boundedFileStream } from './bounded-stream.js';\nimport {\n DEFAULT_URL_FILE_MAX_BYTES,\n type FileContent,\n type FileContext,\n type FileStreamContent,\n type UrlFileOptions,\n} from './contracts.js';\nimport { fileTooLarge } from './limits.js';\nimport { resolveFileStreamOptions } from './options.js';\n\n/** Убирает параметры MIME и приводит его к форме для сравнения. */\nexport function normalizeMimeType(contentType: string | undefined): string | undefined {\n const normalized = contentType?.split(';', 1)[0]?.trim().toLowerCase();\n return normalized || undefined;\n}\n\n/** Достаёт имя файла из пути URL. */\nfunction filenameFromUrl(url: URL): string | undefined {\n const last = url.pathname.split('/').pop();\n if (!last) return undefined;\n\n try {\n return decodeURIComponent(last) || undefined;\n } catch {\n return last;\n }\n}\n\n/** Проверяет объявленный размер и возвращает его, если заголовок корректен. */\nfunction declaredSize(\n response: Response,\n maxBytes: number | undefined,\n url: string,\n): number | undefined {\n const header = response.headers.get('content-length');\n if (header === null) return undefined;\n\n const size = Number(header);\n if (!Number.isFinite(size) || size < 0 || !Number.isInteger(size)) return undefined;\n if (maxBytes !== undefined && size > maxBytes) throw fileTooLarge(url, maxBytes, size);\n return size;\n}\n\n/** Получает HTTP-ответ источника и проверяет его статус. */\nasync function fetchFile(\n target: string,\n options: UrlFileOptions,\n context: FileContext,\n): Promise<{ response: Response; url: URL; size: number | undefined }> {\n let requested: URL;\n try {\n requested = new URL(target);\n } catch {\n throw new ItdConfigError(`«${target}» не разбирается как адрес`);\n }\n\n if (requested.protocol !== 'http:' && requested.protocol !== 'https:') {\n throw new ItdConfigError(\n `вложение по адресу поддерживает только http и https, получено: ${requested.protocol}`,\n );\n }\n\n let response: Response;\n try {\n response = await context.fetch(requested, {\n ...(context.signal ? { signal: context.signal } : {}),\n });\n } catch (error) {\n if (context.signal?.aborted || (error instanceof Error && error.name === 'AbortError')) {\n throw error;\n }\n\n throw new ItdFileError(`не удалось получить файл по адресу ${requested.href}`, {\n reason: ItdFileErrorReason.Network,\n url: requested.href,\n retryable: true,\n cause: error,\n });\n }\n\n const finalUrl = response.url ? new URL(response.url) : requested;\n if (!response.ok) {\n await response.body?.cancel().catch(() => {});\n throw new ItdFileError(`источник ${finalUrl.href} ответил статусом ${response.status}`, {\n reason: ItdFileErrorReason.Http,\n url: finalUrl.href,\n status: response.status,\n retryable: response.status === 408 || response.status === 429 || response.status >= 500,\n });\n }\n\n const { maxBytes } = resolveFileStreamOptions(options, DEFAULT_URL_FILE_MAX_BYTES);\n let size: number | undefined;\n try {\n size = declaredSize(response, maxBytes, finalUrl.href);\n } catch (error) {\n await response.body?.cancel().catch(() => {});\n throw error;\n }\n return {\n response,\n url: finalUrl,\n size,\n };\n}\n\n/** Читает ответ с контролем размера до создания итогового `Blob`. */\nasync function responseBlob(\n response: Response,\n url: string,\n maxBytes: number | undefined,\n streamBufferBytes: number,\n signal: AbortSignal | undefined,\n): Promise<Blob> {\n if (!response.body) {\n const blob = await response.blob();\n if (maxBytes !== undefined && blob.size > maxBytes) {\n throw fileTooLarge(url, maxBytes, blob.size);\n }\n return blob;\n }\n\n const chunks: Uint8Array[] = [];\n const stream = boundedFileStream(response.body, {\n ...(maxBytes !== undefined ? { maxBytes } : {}),\n streamBufferBytes,\n ...(signal ? { signal } : {}),\n url,\n retryableRead: true,\n });\n const reader = stream.getReader();\n\n try {\n for (;;) {\n const next = await reader.read();\n if (next.done) break;\n chunks.push(next.value);\n }\n } finally {\n reader.releaseLock();\n }\n\n return new Blob(chunks.map((chunk) => Uint8Array.from(chunk).buffer));\n}\n\n/** Скачивает файл целиком с ограничением размера. @internal */\nexport async function downloadFile(\n target: string,\n options: UrlFileOptions,\n context: FileContext,\n): Promise<FileContent> {\n const resolved = resolveFileStreamOptions(options, DEFAULT_URL_FILE_MAX_BYTES);\n const { response, url } = await fetchFile(target, options, context);\n const blob = await responseBlob(\n response,\n url.href,\n resolved.maxBytes,\n resolved.streamBufferBytes,\n context.signal,\n );\n const contentType = normalizeMimeType(\n options.contentType ?? response.headers.get('content-type') ?? undefined,\n );\n const filename = options.filename ?? filenameFromUrl(url);\n\n return {\n file: new Blob([blob], { type: contentType ?? '' }),\n ...(filename ? { filename } : {}),\n ...(contentType ? { contentType } : {}),\n };\n}\n\n/** Открывает HTTP-ответ как ограниченный поток. @internal */\nexport async function openUrlFile(\n target: string,\n options: UrlFileOptions,\n context: FileContext,\n): Promise<FileStreamContent> {\n const resolved = resolveFileStreamOptions(options, DEFAULT_URL_FILE_MAX_BYTES);\n const { response, url, size } = await fetchFile(target, options, context);\n if (!response.body) {\n throw new ItdFileError(`источник ${url.href} не предоставил потоковое тело`, {\n reason: ItdFileErrorReason.StreamUnavailable,\n url: url.href,\n });\n }\n\n const stream = boundedFileStream(response.body, {\n ...(resolved.maxBytes !== undefined ? { maxBytes: resolved.maxBytes } : {}),\n streamBufferBytes: resolved.streamBufferBytes,\n ...(context.signal ? { signal: context.signal } : {}),\n url: url.href,\n retryableRead: true,\n });\n const filename = options.filename ?? filenameFromUrl(url);\n const contentType = normalizeMimeType(\n options.contentType ?? response.headers.get('content-type') ?? undefined,\n );\n\n return {\n stream,\n ...(filename ? { filename } : {}),\n ...(contentType ? { contentType } : {}),\n ...(size !== undefined ? { size } : {}),\n close: () => stream.cancel().catch(() => {}),\n };\n}\n","import { ItdConfigError } from './errors.js';\n\n/** Изображения, которые принимает `POST /api/files/upload`. */\nexport const IMAGE_MIME_TYPES = Object.freeze([\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/avif',\n 'image/heic',\n 'image/heif',\n] as const);\nexport type ImageMimeType = (typeof IMAGE_MIME_TYPES)[number];\n\n/** Видео, которые принимает `POST /api/files/upload`. */\nexport const VIDEO_MIME_TYPES = Object.freeze([\n 'video/mp4',\n 'video/webm',\n 'video/quicktime',\n] as const);\nexport type VideoMimeType = (typeof VIDEO_MIME_TYPES)[number];\n\n/** Аудио для голосовых комментариев. */\nexport const AUDIO_MIME_TYPES = Object.freeze(['audio/ogg'] as const);\nexport type AudioMimeType = (typeof AUDIO_MIME_TYPES)[number];\n\n/** Все типы, которые принимает загрузка. */\nexport const ALLOWED_MIME_TYPES = Object.freeze([\n ...IMAGE_MIME_TYPES,\n ...VIDEO_MIME_TYPES,\n ...AUDIO_MIME_TYPES,\n] as const);\nexport type AllowedMimeType = (typeof ALLOWED_MIME_TYPES)[number];\n\n/**\n * Соответствие расширения и MIME-типа.\n *\n * Своя маленькая таблица вместо зависимости: библиотеке нужны ровно те типы,\n * которые принимает API.\n */\nconst EXTENSION_TO_MIME: Readonly<Record<string, string>> = Object.freeze({\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n jfif: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n avif: 'image/avif',\n heic: 'image/heic',\n heif: 'image/heif',\n mp4: 'video/mp4',\n m4v: 'video/mp4',\n webm: 'video/webm',\n mov: 'video/quicktime',\n qt: 'video/quicktime',\n ogg: 'audio/ogg',\n oga: 'audio/ogg',\n opus: 'audio/ogg',\n});\n\n/** Определяет MIME-тип по расширению имени файла. */\nexport function mimeFromFilename(filename: string): string | undefined {\n const dot = filename.lastIndexOf('.');\n if (dot < 0) return undefined;\n\n return EXTENSION_TO_MIME[filename.slice(dot + 1).toLowerCase()];\n}\n\n/** Разрешён ли тип к загрузке. */\nexport function isAllowedMime(mimeType: string): mimeType is AllowedMimeType {\n return (ALLOWED_MIME_TYPES as readonly string[]).includes(mimeType.toLowerCase());\n}\n\n/**\n * Проверяет тип файла до отправки.\n *\n * Отказ на стороне клиента экономит и время, и трафик: тот же `UNSUPPORTED_FILE_TYPE`\n * пришёл бы с сервера, но уже после загрузки всего файла.\n *\n * @throws {ItdConfigError} если тип не поддерживается\n */\nexport function assertAllowedMime(mimeType: string | undefined, filename?: string): void {\n if (!mimeType) {\n throw new ItdConfigError(\n `Не удалось определить тип файла${filename ? ` «${filename}»` : ''}. ` +\n 'Укажите его явно: { file, filename, contentType }.',\n );\n }\n\n if (!isAllowedMime(mimeType)) {\n throw new ItdConfigError(\n `Тип «${mimeType}» не поддерживается загрузкой. Допустимые: ${ALLOWED_MIME_TYPES.join(', ')}.`,\n );\n }\n}\n","/** Один потоковый файл в multipart/form-data. */\nexport interface MultipartFile {\n stream: ReadableStream<Uint8Array>;\n filename: string;\n contentType: string;\n}\n\n/** Результат сборки потокового multipart-тела. */\nexport interface MultipartBody {\n body: ReadableStream<Uint8Array>;\n contentType: string;\n cancel: (reason?: unknown) => Promise<void>;\n}\n\nconst encoder = new TextEncoder();\n\n/** Убирает возможность внедрить дополнительный multipart-заголовок через имя файла. */\nfunction quotedFilename(filename: string): string {\n return filename\n .replace(/[\\r\\n]/g, '')\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"');\n}\n\n/** Удаляет разделители строк, чтобы значение не создало новый multipart-заголовок. */\nfunction headerValue(value: string): string {\n return value.replace(/[\\r\\n]/g, '');\n}\n\n/** Создаёт уникальную границу без зависимости от Web Crypto. */\nfunction boundary(): string {\n return `----itd-api-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Кодирует один файл в multipart без полной буферизации.\n *\n * Читает следующий чанк только когда потребитель запросил данные.\n */\nexport function createMultipartFileBody(file: MultipartFile): MultipartBody {\n const marker = boundary();\n const prefix = encoder.encode(\n `--${marker}\\r\\n` +\n `Content-Disposition: form-data; name=\"file\"; filename=\"${quotedFilename(file.filename)}\"\\r\\n` +\n `Content-Type: ${headerValue(file.contentType)}\\r\\n\\r\\n`,\n );\n const suffix = encoder.encode(`\\r\\n--${marker}--\\r\\n`);\n const reader = file.stream.getReader();\n let stage: 'prefix' | 'file' | 'suffix' | 'done' = 'prefix';\n let cancelled = false;\n\n const cancel = async (reason?: unknown) => {\n if (cancelled) return;\n cancelled = true;\n await reader.cancel(reason).catch(() => {});\n };\n\n const body = new ReadableStream<Uint8Array>({\n async pull(controller) {\n if (stage === 'prefix') {\n stage = 'file';\n controller.enqueue(prefix);\n return;\n }\n\n if (stage === 'file') {\n try {\n const next = await reader.read();\n if (!next.done) {\n controller.enqueue(next.value);\n return;\n }\n stage = 'suffix';\n } catch (error) {\n stage = 'done';\n controller.error(error);\n return;\n }\n }\n\n if (stage === 'suffix') {\n stage = 'done';\n controller.enqueue(suffix);\n controller.close();\n }\n },\n cancel,\n });\n\n return {\n body,\n contentType: `multipart/form-data; boundary=${marker}`,\n cancel,\n };\n}\n","import { boundedFileStream, isBoundedFileStream } from '../core/attachments/bounded-stream.js';\nimport {\n type FileContent,\n type FileContext,\n type FileInput,\n type FileStreamContent,\n FileTransferMode,\n type LazyFile,\n type StreamFile,\n type UrlFile,\n} from '../core/attachments/contracts.js';\nimport { resolveFileStreamOptions } from '../core/attachments/options.js';\nimport { downloadFile, normalizeMimeType, openUrlFile } from '../core/attachments/url-source.js';\nimport { ItdConfigError, ItdError, ItdFileError, ItdFileErrorReason } from '../core/errors.js';\nimport type { HttpClient } from '../core/http.js';\nimport { assertAllowedMime, mimeFromFilename } from '../core/mime.js';\nimport { createMultipartFileBody } from '../core/multipart.js';\nimport type { RequestBodyFactory } from '../core/pipeline.js';\nimport { isBlob, isFile } from '../core/runtime.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Ответ загрузки файла. */\nexport interface UploadedFile {\n /** Идентификатор вложения — его передают в `attachmentIds`. */\n id: string;\n /** Адрес файла на CDN. */\n url: string;\n}\n\n/** Настройки загрузки. */\nexport interface UploadOptions extends RequestOptions {\n /** Имя файла. Используется для определения MIME, если тип не задан. */\n filename?: string;\n /** MIME-тип. По умолчанию определяется по имени или `Blob`. */\n contentType?: string;\n /** Проверять тип до отправки. По умолчанию `true`. */\n validateMime?: boolean;\n /** Дополнительный предел размера для любого вида источника. */\n maxBytes?: number | undefined;\n /** Размер очереди библиотеки при потоковой передаче. */\n streamBufferBytes?: number | undefined;\n}\n\n/** Таймаут одной попытки загрузки файла — 5 минут. */\nexport const DEFAULT_UPLOAD_TIMEOUT = 300_000;\n\ninterface PreparedBuffer {\n mode: 'buffer';\n blob: Blob;\n filename: string;\n}\n\ninterface PreparedStream {\n mode: 'stream';\n stream: ReadableStream<Uint8Array>;\n filename: string;\n contentType: string;\n close?: (() => void | Promise<void>) | undefined;\n}\n\ntype PreparedFile = PreparedBuffer | PreparedStream;\n\n/** Файлы и медиа. */\nexport class FilesResource extends BaseResource {\n readonly #fetch: typeof fetch;\n\n constructor(http: HttpClient, deps: { fetch: typeof fetch }) {\n super(http);\n this.#fetch = deps.fetch;\n }\n\n /**\n * Загружает файл и возвращает его идентификатор.\n *\n * Потоковый источник открывается заново при каждой повторной попытке. Буферный источник\n * после успешного чтения переиспользуется.\n */\n upload(input: FileInput, options: UploadOptions = {}): Promise<UploadedFile> {\n const bodyFactory = this.#createBodyFactory(input, options);\n\n return this.http.request<UploadedFile>({\n method: 'POST',\n path: '/api/files/upload',\n bodyFactory,\n retryNetworkWrite: true,\n timeout: options.timeout ?? DEFAULT_UPLOAD_TIMEOUT,\n ...this.requestOptions(options),\n });\n }\n\n /** Загружает несколько файлов последовательно, сохраняя порядок. */\n async uploadMany(files: FileInput[], options: UploadOptions = {}): Promise<string[]> {\n const ids: string[] = [];\n for (const file of files) ids.push((await this.upload(file, options)).id);\n return ids;\n }\n\n /**\n * Загружает сведения о файле.\n *\n * Для ещё не прикреплённого файла сервер может ответить `404`.\n */\n get(fileId: string, options: RequestOptions = {}): Promise<unknown> {\n return this.http.request({\n method: 'GET',\n path: `/api/files/${encodePathSegment(fileId, 'fileId')}`,\n ...this.requestOptions(options),\n });\n }\n\n /** Удаляет загруженный файл. */\n remove(fileId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: `/api/files/${encodePathSegment(fileId, 'fileId')}`,\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Создаёт фабрику тела. Успешно подготовленный буфер кешируется, а поток открывается\n * заново, поэтому каждая транспортная попытка получает непрочитанное тело.\n */\n #createBodyFactory(input: FileInput, options: UploadOptions): RequestBodyFactory {\n const streamMode = this.#isStreamInput(input);\n let buffered: Promise<PreparedBuffer> | undefined;\n\n return async ({ signal, attempt }) => {\n const context: FileContext = { fetch: this.#fetch, signal, attempt };\n let prepared: PreparedFile;\n\n try {\n if (streamMode) {\n prepared = await this.#prepareStream(input, options, context);\n } else {\n buffered ??= this.#prepareBuffer(input, options, context).catch((error: unknown) => {\n buffered = undefined;\n throw error;\n });\n prepared = await buffered;\n }\n } catch (error) {\n if (error instanceof ItdError || signal.aborted) throw error;\n throw new ItdFileError('не удалось получить содержимое вложения', {\n reason: ItdFileErrorReason.Read,\n retryable: true,\n cause: error,\n });\n }\n\n if (prepared.mode === 'buffer') {\n const form = new FormData();\n form.set('file', prepared.blob, prepared.filename);\n return { body: form };\n }\n\n const multipart = createMultipartFileBody(prepared);\n return {\n body: multipart.body,\n headers: { 'Content-Type': multipart.contentType },\n cleanup: async () => {\n await multipart.cancel();\n await prepared.close?.();\n },\n };\n };\n }\n\n /** Определяет режим без чтения источника. */\n #isStreamInput(input: FileInput): boolean {\n if (typeof input !== 'object' || input === null) return false;\n if ('open' in input) return true;\n if ('url' in input) {\n return resolveFileStreamOptions(input).mode === FileTransferMode.Stream;\n }\n return false;\n }\n\n /** Получает и проверяет буферный источник. */\n async #prepareBuffer(\n input: FileInput,\n options: UploadOptions,\n context: FileContext,\n ): Promise<PreparedBuffer> {\n const content = await this.#resolveBuffer(input, context);\n const filename =\n options.filename ??\n content.filename ??\n this.#nameFromMime(options.contentType ?? content.contentType);\n const contentType =\n normalizeMimeType(options.contentType ?? content.contentType) ??\n normalizeMimeType(isBlob(content.file) ? content.file.type : undefined) ??\n mimeFromFilename(filename);\n\n if (options.validateMime !== false) assertAllowedMime(contentType, filename);\n\n const blob =\n isBlob(content.file) && (!contentType || content.file.type === contentType)\n ? content.file\n : new Blob([content.file as BlobPart], { type: contentType ?? '' });\n\n const limits = resolveFileStreamOptions(options);\n if (limits.maxBytes !== undefined && blob.size > limits.maxBytes) {\n throw new ItdFileError(`файл больше предела в ${limits.maxBytes} байт: ${blob.size}`, {\n reason: ItdFileErrorReason.TooLarge,\n limit: limits.maxBytes,\n actual: blob.size,\n });\n }\n\n return { mode: 'buffer', blob, filename };\n }\n\n /** Открывает и проверяет потоковый источник. */\n async #prepareStream(\n input: FileInput,\n options: UploadOptions,\n context: FileContext,\n ): Promise<PreparedStream> {\n let opened: FileStreamContent;\n if (typeof input === 'object' && input !== null && 'open' in input) {\n opened = await (input as StreamFile).open(context);\n } else if (typeof input === 'object' && input !== null && 'url' in input) {\n const { url, ...urlOptions } = input as UrlFile;\n opened = await openUrlFile(url, urlOptions, context);\n } else {\n throw new ItdConfigError('потоковое вложение должно иметь форму { open } или { url, mode }');\n }\n\n if (\n !opened ||\n typeof ReadableStream === 'undefined' ||\n !(opened.stream instanceof ReadableStream)\n ) {\n await opened?.close?.();\n throw new ItdConfigError('потоковый источник должен вернуть { stream: ReadableStream }');\n }\n\n try {\n const filename =\n options.filename ??\n opened.filename ??\n this.#nameFromMime(options.contentType ?? opened.contentType);\n const contentType =\n normalizeMimeType(options.contentType ?? opened.contentType) ?? mimeFromFilename(filename);\n if (options.validateMime !== false) assertAllowedMime(contentType, filename);\n\n const limits = resolveFileStreamOptions({\n mode: FileTransferMode.Stream,\n ...(options.maxBytes !== undefined ? { maxBytes: options.maxBytes } : {}),\n ...(options.streamBufferBytes !== undefined\n ? { streamBufferBytes: options.streamBufferBytes }\n : {}),\n });\n if (\n limits.maxBytes !== undefined &&\n opened.size !== undefined &&\n opened.size > limits.maxBytes\n ) {\n throw new ItdFileError(`файл больше предела в ${limits.maxBytes} байт: ${opened.size}`, {\n reason: ItdFileErrorReason.TooLarge,\n limit: limits.maxBytes,\n actual: opened.size,\n });\n }\n\n const keepExistingLimits =\n isBoundedFileStream(opened.stream) &&\n options.maxBytes === undefined &&\n options.streamBufferBytes === undefined;\n\n return {\n mode: 'stream',\n stream: keepExistingLimits\n ? opened.stream\n : boundedFileStream(opened.stream, {\n ...(limits.maxBytes !== undefined ? { maxBytes: limits.maxBytes } : {}),\n streamBufferBytes: limits.streamBufferBytes,\n signal: context.signal,\n }),\n filename,\n contentType: contentType ?? '',\n ...(opened.close ? { close: opened.close } : {}),\n };\n } catch (error) {\n await opened.close?.();\n throw error;\n }\n }\n\n /** Разрешает только буферные формы. */\n #resolveBuffer(input: FileInput, context: FileContext): FileContent | Promise<FileContent> {\n if (input instanceof ArrayBuffer || ArrayBuffer.isView(input) || isBlob(input)) {\n return {\n file: input as Blob | ArrayBuffer | Uint8Array,\n ...(isFile(input) ? { filename: input.name } : {}),\n };\n }\n\n if (typeof input !== 'object' || input === null) {\n throw new ItdConfigError(\n `вложение задано значением типа ${typeof input}; ожидается бинарное значение ` +\n 'или объект { file }, { url }, { load }, { open }. ' +\n \"Для файла на диске используйте fromPath('./photo.jpg') из itd-api/node\",\n );\n }\n\n if ('load' in input) return (input as LazyFile).load(context);\n if ('url' in input) {\n const { url, ...urlOptions } = input as UrlFile;\n return downloadFile(url, urlOptions, context);\n }\n if ('file' in input) return input as FileContent;\n if ('open' in input) {\n throw new ItdConfigError('потоковый источник нельзя использовать как буферный');\n }\n\n throw new ItdConfigError(\n 'вложение не распознано: ожидается { file }, { url }, { load } или { open }',\n );\n }\n\n /** Подбирает непустое имя, обязательное для multipart. */\n #nameFromMime(contentType: string | undefined): string {\n const extension = normalizeMimeType(contentType)?.split('/')[1];\n return extension ? `file.${extension}` : 'file';\n }\n}\n","import { type Page, PaginationMode, type Paginator, readCursorPage } from '../core/pagination.js';\nimport { pickArray } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { Hashtag, Post } from '../models/content.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Параметры запроса постов по хэштегу. */\nexport interface HashtagPostsParams extends RequestOptions {\n limit?: number;\n cursor?: string;\n maxPages?: number;\n}\n\n/**\n * Хэштеги.\n *\n * Доступна как `itd.hashtags`.\n */\nexport class HashtagsResource extends BaseResource {\n /** Посты по хэштегу: `/api/hashtags/{tag}/posts`, курсорная пагинация. */\n readonly #posts = this.paginated<Post, HashtagPostsParams & { tag: string }>({\n path: (p) => `/api/hashtags/${encodePathSegment(p.tag, 'tag')}/posts`,\n query: (p) => ({ limit: p.limit }),\n start: (p) => (p.cursor ? { cursor: p.cursor } : {}),\n read: (body) => readCursorPage<Post>(body, 'posts'),\n mode: PaginationMode.Cursor,\n });\n\n /**\n * Ищет хэштеги.\n *\n * Без строки запроса возвращает общий список.\n */\n async search(\n query?: string,\n params: { limit?: number } & RequestOptions = {},\n ): Promise<Hashtag[]> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/hashtags',\n query: { q: query, limit: params.limit },\n ...this.requestOptions(params),\n });\n\n return pickArray<Hashtag>(body, 'hashtags');\n }\n\n /** Загружает трендовые хэштеги. */\n async trending(params: { limit?: number } & RequestOptions = {}): Promise<Hashtag[]> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/hashtags/trending',\n query: { limit: params.limit },\n ...this.requestOptions(params),\n });\n\n return pickArray<Hashtag>(body, 'hashtags');\n }\n\n /**\n * Загружает страницу постов по хэштегу.\n *\n * @param tag название без решётки; кодируется автоматически, поэтому кириллица\n * и пробелы допустимы\n */\n posts(tag: string, params: HashtagPostsParams = {}): Promise<Page<Post>> {\n return this.#posts.list({ ...params, tag });\n }\n\n /** Перебирает посты по хэштегу. */\n iteratePosts(tag: string, params: HashtagPostsParams = {}): Paginator<Post> {\n return this.#posts.iterate({ ...params, tag });\n }\n}\n","import { type Page, PaginationMode, type Paginator, readOffsetPage } from '../core/pagination.js';\nimport { pickBoolean, pickNumber } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { Notification, NotificationSettings } from '../models/notifications.js';\nimport { normalizeNotification } from '../notifications/normalize.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\nconst NOTIFICATION_SETTING_KEYS = [\n 'enabled',\n 'sound',\n 'follows',\n 'wallPosts',\n 'likes',\n 'comments',\n 'mentions',\n] as const;\n\n/**\n * Сколько идентификаторов уходит в одном запросе на отметку прочтения.\n *\n * Столько же отправляет сайт итд.com — значит на сервере, скорее всего, есть ограничение.\n */\nconst READ_BATCH_SIZE = 20;\n\n/** Параметры запроса списка уведомлений. */\nexport interface NotificationListParams extends RequestOptions {\n limit?: number;\n /** Смещение от начала списка. */\n offset?: number;\n maxPages?: number;\n}\n\n/** Изменяемые настройки уведомлений. */\nexport type UpdateNotificationSettingsInput = Partial<NotificationSettings>;\n\n/**\n * Читает настройки уведомлений.\n *\n * Сервер отдаёт плоский объект: `enabled`, `sound`, `follows`, `wallPosts`, `likes`,\n * `comments`, `mentions`. Отсутствующая настройка считается включённой — так же\n * ведёт себя сайт итд.com.\n */\nfunction readSettings(body: unknown): NotificationSettings {\n const settings = {} as NotificationSettings;\n for (const key of NOTIFICATION_SETTING_KEYS) settings[key] = pickBoolean(body, key, true);\n return settings;\n}\n\n/**\n * Уведомления: список, счётчик, отметки о прочтении, настройки.\n *\n * Доступна как `itd.notifications`. Все уведомления приведены к единой форме, поэтому\n * объекты отсюда и из потока событий можно складывать в один список.\n */\nexport class NotificationsResource extends BaseResource {\n /** Уведомления: `/api/notifications/`, пагинация по смещению. */\n readonly #list = this.paginated<Notification, NotificationListParams>({\n // Завершающий слэш обязателен: без него сервер отвечает ошибкой.\n path: () => '/api/notifications/',\n query: (p) => ({ limit: p.limit }),\n start: (p) => ({ offset: p.offset ?? 0 }),\n read: (body, state) => {\n const page = readOffsetPage<unknown>(body, 'notifications', state.offset ?? 0);\n // Сайт итд.com оборачивает смещение в строку и притворяется, что это курсор;\n // библиотека отдаёт честное число, а сами уведомления — в единой форме.\n return { ...page, items: page.items.map(normalizeNotification) };\n },\n mode: PaginationMode.Offset,\n });\n\n /**\n * Загружает страницу уведомлений.\n *\n * Пагинация здесь основана на смещении.\n *\n * @example\n * ```ts\n * const page = await itd.notifications.list({ limit: 20 });\n * const next = await itd.notifications.list({ limit: 20, offset: page.nextOffset });\n * ```\n */\n list(params: NotificationListParams = {}): Promise<Page<Notification>> {\n return this.#list.list(params);\n }\n\n /**\n * Перебирает уведомления.\n *\n * @example\n * ```ts\n * for await (const notification of itd.notifications.iterate()) {\n * console.log(formatNotificationText(notification));\n * }\n * ```\n */\n iterate(params: NotificationListParams = {}): Paginator<Notification> {\n return this.#list.iterate(params);\n }\n\n /** Загружает число непрочитанных уведомлений. */\n async count(options: RequestOptions = {}): Promise<number> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/notifications/count',\n ...this.requestOptions(options),\n });\n\n return pickNumber(body, 'count', 0);\n }\n\n /**\n * Отмечает уведомление прочитанным.\n *\n * @returns сколько записей отметил сервер\n */\n async markRead(notificationId: string, options: RequestOptions = {}): Promise<number> {\n const body = await this.http.request({\n method: 'POST',\n path: `/api/notifications/${encodePathSegment(notificationId, 'notificationId')}/read`,\n ...this.requestOptions(options),\n });\n\n return pickNumber(body, 'markedCount', 0);\n }\n\n /**\n * Отмечает прочитанными сразу несколько уведомлений.\n *\n * Список автоматически режется на части по 20 идентификаторов — столько же отправляет\n * сайт итд.com, поэтому на сервере вероятен предел. Части уходят последовательно,\n * результат суммируется.\n *\n * @returns сколько записей отметил сервер суммарно\n */\n async markReadBatch(ids: string[], options: RequestOptions = {}): Promise<number> {\n let marked = 0;\n\n for (let index = 0; index < ids.length; index += READ_BATCH_SIZE) {\n const chunk = ids.slice(index, index + READ_BATCH_SIZE);\n\n const body = await this.http.request({\n method: 'POST',\n path: '/api/notifications/read-batch',\n body: { ids: chunk },\n ...this.requestOptions(options),\n });\n\n marked += pickNumber(body, 'markedCount', 0);\n }\n\n return marked;\n }\n\n /** Отмечает прочитанными все уведомления. */\n async markAllRead(options: RequestOptions = {}): Promise<number> {\n const body = await this.http.request({\n method: 'POST',\n path: '/api/notifications/read-all',\n ...this.requestOptions(options),\n });\n\n return pickNumber(body, 'markedCount', 0);\n }\n\n /** Загружает настройки уведомлений. */\n async getSettings(options: RequestOptions = {}): Promise<NotificationSettings> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/notifications/settings',\n ...this.requestOptions(options),\n });\n\n return readSettings(body);\n }\n\n /**\n * Обновляет настройки уведомлений.\n *\n * Отправляются только изменяемые поля, в том же виде, в каком сервер их возвращает.\n */\n async updateSettings(\n input: UpdateNotificationSettingsInput,\n options: RequestOptions = {},\n ): Promise<NotificationSettings> {\n const payload: Record<string, boolean> = {};\n\n for (const key of NOTIFICATION_SETTING_KEYS) {\n const value = input[key];\n if (value !== undefined) payload[key] = value;\n }\n\n const body = await this.http.request({\n method: 'PUT',\n path: '/api/notifications/settings',\n body: payload,\n ...this.requestOptions(options),\n });\n\n return readSettings(body);\n }\n}\n","/** Отметка времени без часового пояса: `2026-07-23 23:14:25`, возможно с долями секунды. */\nimport type { IsoDate } from '../models/common.js';\n\nconst NAIVE_STAMP = /^(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)$/;\n\n/**\n * Приводит отметку времени без часового пояса к ISO-8601, считая её временем UTC.\n *\n * Строку другого вида возвращает нетронутой.\n *\n * @example\n * ```ts\n * utcStampToIso('2026-07-23 23:14:25'); // '2026-07-23T23:14:25Z'\n * utcStampToIso('2026-07-23T23:14:25Z'); // без изменений\n * ```\n */\nexport function utcStampToIso(value: string): string {\n const match = typeof value === 'string' ? NAIVE_STAMP.exec(value) : null;\n if (!match) return value;\n\n const iso = `${match[1]}T${match[2]}Z`;\n return Number.isFinite(Date.parse(iso)) ? iso : value;\n}\n\n/**\n * Разбирает дату API в объект `Date`.\n *\n * @returns `null`, если строки нет или она не разбирается\n *\n * @example\n * ```ts\n * const created = toDate(post.createdAt);\n * ```\n */\nexport function toDate(value: IsoDate | null | undefined): Date | null {\n if (!value) return null;\n const date = new Date(value);\n return Number.isFinite(date.getTime()) ? date : null;\n}\n","import { STATUS_SERVICE } from '../core/config.js';\nimport { utcStampToIso } from '../core/time.js';\nimport { isRecord, pickArray } from '../core/unwrap.js';\nimport type { Announcement, ChangelogEntry, Portal } from '../models/platform.js';\nimport type { PlatformStatus, ServiceStatus } from '../models/status.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Требования к версии одного приложения платформы. */\nexport interface PlatformClientVersion {\n /** Минимальная поддерживаемая версия приложения. */\n minVersion: string;\n /** Последняя доступная версия приложения. */\n latestVersion: string;\n /** Адрес страницы обновления приложения. */\n updateUrl: string;\n}\n\n/** Версии клиентских приложений платформы. */\nexport interface PlatformVersions {\n android: PlatformClientVersion;\n ios: PlatformClientVersion;\n [client: string]: PlatformClientVersion;\n}\n\n/** Приводит `last_checked` каждого сервиса к ISO. Остальное остаётся как прислал сервер. */\nfunction normalizeStatus(body: PlatformStatus): PlatformStatus {\n if (!isRecord(body) || !Array.isArray(body.services)) return body;\n\n return {\n ...body,\n services: body.services.map((service: ServiceStatus) =>\n typeof service?.last_checked === 'string'\n ? { ...service, last_checked: utcStampToIso(service.last_checked) }\n : service,\n ),\n };\n}\n\n/**\n * Сведения о платформе: версии приложений, изменения, анонсы, баннер события.\n *\n * Доступна как `itd.platform`.\n */\nexport class PlatformResource extends BaseResource {\n /**\n * Загружает минимальные и актуальные версии клиентских приложений.\n *\n * Endpoint публичный: автоматическая авторизация в запрос не добавляется.\n *\n * @example\n * ```ts\n * const versions = await itd.platform.version();\n * console.log(versions.android.latestVersion);\n * ```\n */\n version(options: RequestOptions = {}): Promise<PlatformVersions> {\n return this.http.request<PlatformVersions>({\n method: 'GET',\n path: '/api/platform/version',\n skipAuth: true,\n ...this.requestOptions(options),\n });\n }\n\n /** Загружает журнал изменений. */\n async changelog(options: RequestOptions = {}): Promise<ChangelogEntry[]> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/platform/changelog',\n ...this.requestOptions(options),\n });\n\n return Array.isArray(body) ? (body as ChangelogEntry[]) : [];\n }\n\n /** Загружает анонсы платформы. */\n async announcements(options: RequestOptions = {}): Promise<Announcement[]> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/platform/announcements',\n ...this.requestOptions(options),\n });\n\n return pickArray<Announcement>(body, 'announcements');\n }\n\n /** Загружает баннер текущего события — виджет «портал». */\n portal(options: RequestOptions = {}): Promise<Portal> {\n return this.http.request<Portal>({\n method: 'GET',\n path: '/api/v1/portal',\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Загружает состояние сервисов платформы за последние 90 суток.\n *\n * Идёт на хост `статус.итд.com` без авторизации. Ответ кэшируется сервером на минуту.\n * История по суткам приходит разреженной, ровный массив даёт `statusDays`.\n *\n * @example\n * ```ts\n * const status = await itd.platform.status();\n *\n * if (status.overall_status !== 'operational') {\n * const broken = status.services.filter((s) => s.current_status !== 'operational');\n * console.log('лежит:', broken.map((s) => s.name).join(', '));\n * }\n * ```\n */\n async status(options: RequestOptions = {}): Promise<PlatformStatus> {\n const body = await this.http.request<PlatformStatus>({\n method: 'GET',\n service: STATUS_SERVICE,\n path: '/api/status',\n ...this.requestOptions(options),\n });\n\n return normalizeStatus(body);\n }\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { Span } from '../models/common.js';\n\n/**\n * Проверяет, что spans целиком лежат внутри текста.\n *\n * Возвращает копии, чтобы последующая мутация входного массива не меняла уже собранные данные.\n */\nexport function validateSpans(content: string, spans: unknown): Span[] {\n if (!Array.isArray(spans)) {\n throw new ItdConfigError('spans должен быть массивом');\n }\n if (content.length === 0 && spans.length > 0) {\n throw new ItdConfigError('Нельзя задать spans без текста: сначала укажите непустой content');\n }\n\n return spans.map((span, index) => {\n if (!span || typeof span !== 'object') {\n throw new ItdConfigError(`Некорректный span №${index + 1}: ожидался объект`);\n }\n\n const candidate = span as Partial<Span>;\n if (\n typeof candidate.type !== 'string' ||\n !Number.isInteger(candidate.offset) ||\n !Number.isInteger(candidate.length) ||\n (candidate.offset ?? -1) < 0 ||\n (candidate.length ?? 0) <= 0 ||\n (candidate.offset ?? 0) + (candidate.length ?? 0) > content.length\n ) {\n throw new ItdConfigError(\n `Некорректный span №${index + 1}: offset и length должны указывать на непустой фрагмент content`,\n );\n }\n\n return { ...candidate } as Span;\n });\n}\n","import type { TextMarkup } from '../builders/markup.js';\nimport type { Span } from '../models/common.js';\nimport { SpanType } from '../types/enums.js';\nimport { validateSpans } from './validate.js';\n\n/** Настройки безопасного импорта разметки. */\nexport interface ParseMarkupOptions {\n /**\n * Разрешённые схемы ссылок без завершающего двоеточия.\n *\n * По умолчанию разрешены только `http` и `https`. Относительные ссылки не превращаются\n * в spans: wire-формату нужен самостоятельный адрес.\n */\n allowedLinkProtocols?: readonly string[];\n}\n\ninterface MutableMarkup {\n content: string;\n spans: Span[];\n}\n\ninterface OpenHtmlSpan {\n tag: string;\n start: number;\n span: Omit<Span, 'offset' | 'length'>;\n}\n\nconst HTML_BLOCK_TAGS = new Set([\n 'address',\n 'article',\n 'aside',\n 'blockquote',\n 'div',\n 'footer',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'header',\n 'li',\n 'main',\n 'nav',\n 'ol',\n 'p',\n 'pre',\n 'section',\n 'ul',\n]);\n\nconst HTML_DROPPED_TAGS = new Set([\n 'applet',\n 'canvas',\n 'embed',\n 'iframe',\n 'math',\n 'noscript',\n 'object',\n 'script',\n 'style',\n 'svg',\n 'template',\n]);\n\nconst HTML_ENTITIES: Readonly<Record<string, string>> = Object.freeze({\n amp: '&',\n apos: \"'\",\n gt: '>',\n lt: '<',\n nbsp: '\\u00a0',\n quot: '\"',\n});\n\nfunction append(output: MutableMarkup, value: string): void {\n output.content += value;\n}\n\nfunction addSpan(\n output: MutableMarkup,\n start: number,\n span: Omit<Span, 'offset' | 'length'>,\n): void {\n const length = output.content.length - start;\n if (length > 0) output.spans.push({ ...span, offset: start, length });\n}\n\nfunction finish(output: MutableMarkup): TextMarkup {\n const unique = new Map<string, Span>();\n for (const span of output.spans) {\n const key = [\n span.type,\n span.offset,\n span.length,\n span.url ?? '',\n span.tag ?? '',\n span.username ?? '',\n span.id ?? '',\n ].join('\\u0000');\n if (!unique.has(key)) unique.set(key, span);\n }\n\n return {\n content: output.content,\n spans: validateSpans(output.content, [...unique.values()]).sort(\n (left, right) => left.offset - right.offset || right.length - left.length,\n ),\n };\n}\n\nfunction allowedProtocols(options: ParseMarkupOptions): Set<string> {\n const values = options.allowedLinkProtocols ?? ['http', 'https'];\n return new Set(\n values\n .map((value) => `${value.toLowerCase().replace(/:$/u, '')}:`)\n .filter((value) => value !== ':'),\n );\n}\n\nfunction safeLink(value: string, protocols: ReadonlySet<string>): string | null {\n try {\n const url = new URL(value);\n return url.hostname !== '' && protocols.has(url.protocol.toLowerCase()) ? value : null;\n } catch {\n return null;\n }\n}\n\nfunction isEscaped(value: string, index: number): boolean {\n let slashes = 0;\n for (let cursor = index - 1; cursor >= 0 && value[cursor] === '\\\\'; cursor -= 1) slashes += 1;\n return slashes % 2 === 1;\n}\n\nfunction findClosing(value: string, marker: string, from: number): number {\n let cursor = from;\n while (cursor < value.length) {\n const found = value.indexOf(marker, cursor);\n if (found < 0) return -1;\n if (!isEscaped(value, found)) return found;\n cursor = found + marker.length;\n }\n return -1;\n}\n\nfunction findBracketEnd(value: string, from: number, open: string, close: string): number {\n let depth = 1;\n for (let cursor = from; cursor < value.length; cursor += 1) {\n const character = value[cursor];\n if (character === '\\\\') {\n cursor += 1;\n continue;\n }\n if (character === open) depth += 1;\n if (character === close) {\n depth -= 1;\n if (depth === 0) return cursor;\n }\n }\n return -1;\n}\n\nfunction decodeEntities(value: string): string {\n return value.replace(/&(#(?:x[\\da-f]+|\\d+)|[a-z][a-z\\d]+);/giu, (raw, entity: string) => {\n if (entity.startsWith('#x') || entity.startsWith('#X')) {\n const code = Number.parseInt(entity.slice(2), 16);\n return Number.isFinite(code) && code <= 0x10ffff ? String.fromCodePoint(code) : raw;\n }\n if (entity.startsWith('#')) {\n const code = Number.parseInt(entity.slice(1), 10);\n return Number.isFinite(code) && code <= 0x10ffff ? String.fromCodePoint(code) : raw;\n }\n return HTML_ENTITIES[entity.toLowerCase()] ?? raw;\n });\n}\n\nfunction normalizeCodeSpan(value: string): string {\n const normalized = value.replace(/\\r\\n?|\\n/gu, ' ');\n if (\n normalized.length >= 2 &&\n normalized.startsWith(' ') &&\n normalized.endsWith(' ') &&\n !/^ +$/u.test(normalized)\n ) {\n return normalized.slice(1, -1);\n }\n return normalized;\n}\n\nfunction markdownStyle(\n source: string,\n index: number,\n): { marker: string; type: Span['type'] } | undefined {\n const styles = [\n { marker: '**', type: SpanType.Bold },\n { marker: '__', type: SpanType.Bold },\n { marker: '~~', type: SpanType.Strike },\n { marker: '||', type: SpanType.Spoiler },\n { marker: '*', type: SpanType.Italic },\n { marker: '_', type: SpanType.Italic },\n ] as const;\n\n for (const style of styles) {\n if (!source.startsWith(style.marker, index)) continue;\n if (style.marker.includes('_')) {\n const before = source[index - 1] ?? '';\n const after = source[index + style.marker.length] ?? '';\n if (/[\\p{L}\\p{N}]/u.test(before) && /[\\p{L}\\p{N}]/u.test(after)) continue;\n }\n return style;\n }\n return undefined;\n}\n\nfunction parseMarkdownInline(\n source: string,\n output: MutableMarkup,\n protocols: ReadonlySet<string>,\n): void {\n let index = 0;\n while (index < source.length) {\n const character = source[index] ?? '';\n\n if (character === '\\\\' && index + 1 < source.length) {\n append(output, source[index + 1] ?? '');\n index += 2;\n continue;\n }\n\n if (character === '`') {\n const run = source.slice(index).match(/^`+/u)?.[0] ?? '`';\n const close = findClosing(source, run, index + run.length);\n if (close > index + run.length) {\n const start = output.content.length;\n append(output, normalizeCodeSpan(source.slice(index + run.length, close)));\n addSpan(output, start, { type: SpanType.Monospace });\n index = close + run.length;\n continue;\n }\n }\n\n const image = source.startsWith('![', index);\n if ((image || character === '[') && !isEscaped(source, index)) {\n const labelStart = index + (image ? 2 : 1);\n const labelEnd = findBracketEnd(source, labelStart, '[', ']');\n if (labelEnd >= 0 && source[labelEnd + 1] === '(') {\n const destinationEnd = findBracketEnd(source, labelEnd + 2, '(', ')');\n if (destinationEnd >= 0) {\n const label = source.slice(labelStart, labelEnd);\n const rawDestination = source.slice(labelEnd + 2, destinationEnd).trim();\n const destination = rawDestination.replace(/\\\\([\\\\)])/gu, '$1');\n const start = output.content.length;\n parseMarkdownInline(label, output, protocols);\n if (!image) {\n const url = safeLink(destination, protocols);\n if (url) addSpan(output, start, { type: SpanType.Link, url });\n }\n index = destinationEnd + 1;\n continue;\n }\n }\n }\n\n if (source.slice(index, index + 3).toLowerCase() === '<u>') {\n const close = source.toLowerCase().indexOf('</u>', index + 3);\n if (close > index + 3) {\n const start = output.content.length;\n parseMarkdownInline(source.slice(index + 3, close), output, protocols);\n addSpan(output, start, { type: SpanType.Underline });\n index = close + 4;\n continue;\n }\n }\n\n if (character === '<') {\n const close = source.indexOf('>', index + 1);\n if (close > index + 1) {\n const candidate = decodeEntities(source.slice(index + 1, close));\n const url = safeLink(candidate, protocols);\n if (url) {\n const start = output.content.length;\n append(output, candidate);\n addSpan(output, start, { type: SpanType.Link, url });\n index = close + 1;\n continue;\n }\n }\n }\n\n const style = markdownStyle(source, index);\n if (style) {\n const close = findClosing(source, style.marker, index + style.marker.length);\n if (close > index + style.marker.length) {\n const start = output.content.length;\n parseMarkdownInline(source.slice(index + style.marker.length, close), output, protocols);\n addSpan(output, start, { type: style.type });\n index = close + style.marker.length;\n continue;\n }\n }\n\n if (character === '&') {\n const entity = source.slice(index).match(/^&(?:#(?:x[\\da-f]+|\\d+)|[a-z][a-z\\d]+);/iu)?.[0];\n if (entity) {\n append(output, decodeEntities(entity));\n index += entity.length;\n continue;\n }\n }\n\n append(output, character);\n index += 1;\n }\n}\n\nfunction lineEnd(source: string, from: number): number {\n const found = source.indexOf('\\n', from);\n return found < 0 ? source.length : found;\n}\n\n/**\n * Преобразует безопасное подмножество Markdown в текст и wire-spans.\n *\n * Поддерживаются bold, italic, strike, spoiler, inline/fenced code, ссылки, underline\n * через `<u>` и цитаты `>`. Неподдерживаемая и незакрытая разметка остаётся текстом.\n * У изображений сохраняется alt-текст без URL.\n */\nexport function parseMarkdown(source: string, options: ParseMarkupOptions = {}): TextMarkup {\n const output: MutableMarkup = { content: '', spans: [] };\n const protocols = allowedProtocols(options);\n let position = 0;\n\n while (position < source.length) {\n const end = lineEnd(source, position);\n const line = source.slice(position, end);\n const hasNewline = end < source.length;\n const fence = line.match(/^ {0,3}(`{3,}|~{3,})[^\\n]*$/u)?.[1];\n\n if (fence) {\n let closeStart = hasNewline ? end + 1 : source.length;\n let closeEnd = closeStart;\n let found = false;\n while (closeStart < source.length) {\n closeEnd = lineEnd(source, closeStart);\n const closeLine = source.slice(closeStart, closeEnd);\n const marker = closeLine.match(/^ {0,3}(`{3,}|~{3,})\\s*$/u)?.[1];\n if (marker !== undefined && marker[0] === fence[0] && marker.length >= fence.length) {\n found = true;\n break;\n }\n closeStart = closeEnd < source.length ? closeEnd + 1 : source.length;\n }\n\n if (found) {\n const bodyStart = end + 1;\n let body = source.slice(bodyStart, closeStart);\n if (body.endsWith('\\n')) body = body.slice(0, -1);\n const start = output.content.length;\n append(output, body);\n addSpan(output, start, { type: SpanType.Monospace });\n if (closeEnd < source.length) append(output, '\\n');\n position = closeEnd < source.length ? closeEnd + 1 : source.length;\n continue;\n }\n }\n\n const quote = line.match(/^ {0,3}> ?(.*)$/u);\n if (quote) {\n const start = output.content.length;\n parseMarkdownInline(quote[1] ?? '', output, protocols);\n addSpan(output, start, { type: SpanType.Quote });\n } else {\n parseMarkdownInline(line, output, protocols);\n }\n\n if (hasNewline) append(output, '\\n');\n position = hasNewline ? end + 1 : source.length;\n }\n\n return finish(output);\n}\n\nfunction readAttribute(source: string, name: string): string | undefined {\n const pattern = new RegExp(\n `(?:^|\\\\s)${name}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s\"'=<>\\\\x60]+))`,\n 'iu',\n );\n const match = source.match(pattern);\n const value = match?.[1] ?? match?.[2] ?? match?.[3];\n return value === undefined ? undefined : decodeEntities(value);\n}\n\nfunction htmlSpan(\n tag: string,\n attributes: string,\n protocols: ReadonlySet<string>,\n): Omit<Span, 'offset' | 'length'> | undefined {\n switch (tag) {\n case 'b':\n case 'strong':\n return { type: SpanType.Bold };\n case 'em':\n case 'i':\n return { type: SpanType.Italic };\n case 'u':\n return { type: SpanType.Underline };\n case 'del':\n case 's':\n case 'strike':\n return { type: SpanType.Strike };\n case 'code':\n case 'pre':\n return { type: SpanType.Monospace };\n case 'blockquote':\n return { type: SpanType.Quote };\n case 'a': {\n const href = readAttribute(attributes, 'href');\n const url = href ? safeLink(href, protocols) : null;\n return url ? { type: SpanType.Link, url } : undefined;\n }\n case 'span': {\n const classes = readAttribute(attributes, 'class')?.split(/\\s+/u) ?? [];\n if (\n readAttribute(attributes, 'data-spoiler') === 'true' ||\n classes.some((value) => value.endsWith('-spoiler'))\n ) {\n return { type: SpanType.Spoiler };\n }\n if (classes.some((value) => value.endsWith('-quote'))) {\n return { type: SpanType.Quote };\n }\n return undefined;\n }\n default:\n return undefined;\n }\n}\n\nfunction ensureBlockBreak(output: MutableMarkup): void {\n if (output.content !== '' && !output.content.endsWith('\\n')) append(output, '\\n');\n}\n\nfunction findHtmlTagEnd(source: string, from: number): number {\n let quote: '\"' | \"'\" | undefined;\n\n for (let index = from; index < source.length; index += 1) {\n const character = source[index];\n if (quote) {\n if (character === quote) quote = undefined;\n continue;\n }\n if (character === '\"' || character === \"'\") {\n quote = character;\n continue;\n }\n if (character === '>') return index;\n }\n\n return -1;\n}\n\n/**\n * Преобразует ограниченный HTML в обычный текст и wire-spans.\n *\n * Разрешены только семантические теги форматирования. Атрибуты событий игнорируются,\n * содержимое `script`, `style`, `iframe`, `object`, SVG и подобных активных элементов\n * удаляется. Опасный `href` сохраняет текст ссылки, но не создаёт span.\n */\nexport function parseHtml(source: string, options: ParseMarkupOptions = {}): TextMarkup {\n const output: MutableMarkup = { content: '', spans: [] };\n const protocols = allowedProtocols(options);\n const stack: OpenHtmlSpan[] = [];\n let position = 0;\n\n const closeTag = (tag: string): void => {\n for (let index = stack.length - 1; index >= 0; index -= 1) {\n const open = stack[index];\n if (open?.tag !== tag) continue;\n stack.splice(index, 1);\n addSpan(output, open.start, open.span);\n return;\n }\n };\n\n while (position < source.length) {\n if (source.startsWith('<!--', position)) {\n const end = source.indexOf('-->', position + 4);\n position = end < 0 ? source.length : end + 3;\n continue;\n }\n\n if (source[position] === '<') {\n const end = findHtmlTagEnd(source, position + 1);\n if (end >= 0) {\n const raw = source.slice(position, end + 1);\n const parsed = raw.match(/^<\\s*(\\/?)\\s*([a-z][\\w-]*)([\\s\\S]*?)\\/?\\s*>$/iu);\n if (parsed) {\n const closing = parsed[1] === '/';\n const tag = (parsed[2] ?? '').toLowerCase();\n const attributes = parsed[3] ?? '';\n\n if (!closing && HTML_DROPPED_TAGS.has(tag)) {\n const closingPattern = new RegExp(`<\\\\s*\\\\/\\\\s*${tag}\\\\s*>`, 'igu');\n closingPattern.lastIndex = end + 1;\n const match = closingPattern.exec(source);\n position = match ? match.index + match[0].length : source.length;\n continue;\n }\n\n if (tag === 'br' && !closing) {\n append(output, '\\n');\n position = end + 1;\n continue;\n }\n\n if (tag === 'img' && !closing) {\n append(output, readAttribute(attributes, 'alt') ?? '');\n position = end + 1;\n continue;\n }\n\n if (closing) {\n closeTag(tag);\n if (HTML_BLOCK_TAGS.has(tag)) ensureBlockBreak(output);\n } else {\n if (HTML_BLOCK_TAGS.has(tag)) ensureBlockBreak(output);\n const span = htmlSpan(tag, attributes, protocols);\n if (span) stack.push({ tag, start: output.content.length, span });\n }\n\n position = end + 1;\n continue;\n }\n }\n }\n\n const next = source.indexOf('<', position + 1);\n const end = next < 0 ? source.length : next;\n append(output, decodeEntities(source.slice(position, end)));\n position = end;\n }\n\n while (stack.length > 0) {\n const open = stack.pop();\n if (open) addSpan(output, open.start, open.span);\n }\n\n while (output.content.endsWith('\\n')) output.content = output.content.slice(0, -1);\n return finish(output);\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { Span } from '../models/common.js';\nimport { validateSpans } from '../spans/validate.js';\nimport { SpanType } from '../types/enums.js';\nimport { BUILDER, type BuilderInput, type ItdBuilder, resolveInput } from './base.js';\n\n/** Текст вместе с рассчитанной разметкой. */\nexport interface TextMarkup {\n content: string;\n spans: Span[];\n}\n\n/** Описание фрагмента без смещения: его вычисляет {@link MarkupBuilder}. */\nexport type MarkupSpan = Omit<Span, 'offset' | 'length'>;\n\n/** Что принимает метод разметки: результат, билдер или функция-настройщик. */\nexport type MarkupInput = BuilderInput<TextMarkup, MarkupBuilder>;\n\n/**\n * Содержимое форматированного фрагмента.\n *\n * Строка создаёт простой фрагмент. Билдер, готовая разметка или функция позволяют вложить\n * одни spans в другие, как при последовательном форматировании выделения в редакторе сайта.\n */\nexport type MarkupContent = string | MarkupInput;\n\n/** Какие сущности искать в {@link autoSpans}. */\nexport interface AutoSpansOptions {\n /** Находить `#хэштеги`. По умолчанию `true`. */\n hashtags?: boolean;\n /** Находить `@упоминания`. По умолчанию `true`. */\n mentions?: boolean;\n /** Находить абсолютные HTTP(S)-ссылки. По умолчанию `true`. */\n links?: boolean;\n}\n\nfunction validateMarkup(value: TextMarkup): TextMarkup {\n if (!value || typeof value.content !== 'string' || !Array.isArray(value.spans)) {\n throw new ItdConfigError('Разметка должна содержать строку content и массив spans');\n }\n\n return {\n content: value.content,\n spans: validateSpans(value.content, value.spans).sort(\n (left, right) => left.offset - right.offset || right.length - left.length,\n ),\n };\n}\n\nfunction entityName(value: string, prefix: '#' | '@', label: string): string {\n const name = value.startsWith(prefix) ? value.slice(prefix.length) : value;\n if (name === '' || /\\s/u.test(name)) {\n throw new ItdConfigError(`${label} должен быть непустым и не содержать пробелов`);\n }\n return name;\n}\n\n/**\n * Неизменяемый билдер текста с разметкой.\n *\n * Каждый метод дописывает фрагмент и сам считает `offset` и `length` в единицах UTF-16 —\n * именно такие индексы использует JavaScript-редактор сайта.\n */\nexport class MarkupBuilder implements ItdBuilder<TextMarkup> {\n /** @internal */\n readonly [BUILDER] = true as const;\n\n readonly #content: string;\n readonly #spans: Span[];\n\n /** @internal Создавайте билдер функцией {@link markup}. */\n constructor(content: string, spans: Span[]) {\n this.#content = content;\n this.#spans = spans;\n }\n\n /** Дописывает обычный текст без разметки. */\n text(value: string): MarkupBuilder {\n return new MarkupBuilder(this.#content + value, this.#spans);\n }\n\n /** Дописывает переводы строк. */\n newline(count = 1): MarkupBuilder {\n if (!Number.isInteger(count) || count < 0) {\n throw new ItdConfigError('Количество переводов строк должно быть целым неотрицательным');\n }\n return this.text('\\n'.repeat(count));\n }\n\n /** @internal Добавляет готовый фрагмент и сдвигает его spans к текущему концу текста. */\n #formatted(value: MarkupContent, outerSpans: readonly MarkupSpan[]): MarkupBuilder {\n const fragment =\n typeof value === 'string' ? { content: value, spans: [] } : resolveMarkup(value);\n if (fragment.content.length === 0) {\n throw new ItdConfigError('Размечаемый фрагмент не может быть пустым');\n }\n\n const offset = this.#content.length;\n const shifted = fragment.spans.map((span) => ({\n ...span,\n offset: span.offset + offset,\n }));\n const outer = outerSpans.map((span) => ({\n ...span,\n offset,\n length: fragment.content.length,\n }));\n\n return new MarkupBuilder(this.#content + fragment.content, [\n ...this.#spans,\n ...shifted,\n ...outer,\n ]);\n }\n\n /**\n * Дописывает фрагмент с произвольным типом разметки.\n *\n * Вложенный билдер позволяет форматировать часть фрагмента дополнительным стилем.\n * Для нескольких стилей на всём фрагменте используйте {@link styled}.\n */\n span(value: MarkupContent, span: MarkupSpan): MarkupBuilder {\n return this.#formatted(value, [span]);\n }\n\n /**\n * Дописывает фрагмент с несколькими стилями на одном диапазоне.\n *\n * Для `link`, которому нужен `url`, используйте {@link link}; произвольные spans с\n * метаданными можно объединять через вложенные вызовы {@link span}.\n */\n styled(value: MarkupContent, ...types: Span['type'][]): MarkupBuilder {\n if (types.length === 0) return this.#formatted(value, []);\n return this.#formatted(\n value,\n types.map((type) => ({ type })),\n );\n }\n\n /** Дописывает `#хэштег` и сохраняет имя без решётки в `tag`. */\n hashtag(tag: string): MarkupBuilder {\n const name = entityName(tag, '#', 'Хэштег');\n return this.span(`#${name}`, { type: SpanType.Hashtag, tag: name });\n }\n\n /** Дописывает `@username` и сохраняет имя пользователя в `username`. */\n mention(username: string): MarkupBuilder {\n const name = entityName(username, '@', 'Упоминание');\n return this.span(`@${name}`, { type: SpanType.Mention, username: name });\n }\n\n /**\n * Дописывает ссылку.\n *\n * Для строки адрес по умолчанию становится и текстом ссылки. Вложенному форматированному\n * фрагменту URL нужно передать явно.\n */\n link(content: MarkupContent, url?: string): MarkupBuilder {\n const resolvedUrl = url ?? (typeof content === 'string' ? content : undefined);\n if (resolvedUrl === undefined) {\n throw new ItdConfigError('Для форматированного текста ссылки нужно явно передать URL');\n }\n return this.span(content, { type: SpanType.Link, url: resolvedUrl });\n }\n\n bold(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Bold });\n }\n\n italic(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Italic });\n }\n\n underline(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Underline });\n }\n\n strike(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Strike });\n }\n\n spoiler(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Spoiler });\n }\n\n monospace(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Monospace });\n }\n\n quote(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Quote });\n }\n\n build(): TextMarkup {\n return validateMarkup({ content: this.#content, spans: this.#spans });\n }\n\n toJSON(): TextMarkup {\n return this.build();\n }\n}\n\n/** Начинает сборку текста с автоматически вычисляемыми смещениями. */\nexport function markup(content = ''): MarkupBuilder {\n return new MarkupBuilder(content, []);\n}\n\n/** Приводит любую форму разметки к тексту и массиву spans. */\nexport function resolveMarkup(input: MarkupInput): TextMarkup {\n return resolveInput(input, () => markup(), validateMarkup);\n}\n\nfunction trimUrlEnd(value: string): string {\n let result = value;\n const pairs = [\n ['(', ')'],\n ['[', ']'],\n ['{', '}'],\n ] as const;\n\n let changed: boolean;\n do {\n const before = result;\n while (/[.,!?;:…'\"»”]$/u.test(result)) result = result.slice(0, -1);\n\n for (const [open, close] of pairs) {\n while (result.endsWith(close) && result.split(close).length > result.split(open).length) {\n result = result.slice(0, -1);\n }\n }\n changed = result !== before;\n } while (changed);\n\n return result;\n}\n\nfunction overlaps(span: Span, start: number, end: number): boolean {\n return span.offset < end && start < span.offset + span.length;\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return (url.protocol === 'http:' || url.protocol === 'https:') && url.hostname !== '';\n } catch {\n return false;\n }\n}\n\nfunction previousCodePoint(text: string, index: number): string {\n if (index <= 0) return '';\n const last = text.charCodeAt(index - 1);\n if (last >= 0xdc00 && last <= 0xdfff && index >= 2) {\n const first = text.charCodeAt(index - 2);\n if (first >= 0xd800 && first <= 0xdbff) return text.slice(index - 2, index);\n }\n return text[index - 1] ?? '';\n}\n\n/**\n * Находит в тексте те сущности, которые сайт получает после серверного разбора:\n * HTTP(S)-ссылки, `#хэштеги` и `@упоминания`.\n *\n * Смещения выражены в UTF-16 code units, поэтому совпадают с `String#slice`,\n * `substring`, DOM Selection и wire-форматом сайта даже при наличии эмодзи.\n */\nexport function autoSpans(text: string, options: AutoSpansOptions = {}): Span[] {\n const includeLinks = options.links ?? true;\n const includeHashtags = options.hashtags ?? true;\n const includeMentions = options.mentions ?? true;\n const spans: Span[] = [];\n const links: Span[] = [];\n\n const urlPattern = /https?:\\/\\/[^\\s<>\"'`]+/giu;\n for (const match of text.matchAll(urlPattern)) {\n const raw = trimUrlEnd(match[0]);\n if (raw.length === 0 || match.index === undefined || !isHttpUrl(raw)) continue;\n links.push({\n type: SpanType.Link,\n offset: match.index,\n length: raw.length,\n url: raw,\n });\n }\n if (includeLinks) spans.push(...links);\n\n if (includeHashtags || includeMentions) {\n const entityPattern = /#([\\p{L}\\p{M}\\p{N}_]+)|@([a-z\\d][a-z\\d._-]*)/giu;\n\n for (const match of text.matchAll(entityPattern)) {\n if (match.index === undefined) continue;\n const offset = match.index;\n const previous = previousCodePoint(text, offset);\n if (/[\\p{L}\\p{M}\\p{N}_#@.]/u.test(previous)) continue;\n\n const end = offset + match[0].length;\n if (links.some((span) => overlaps(span, offset, end))) continue;\n\n const hashtag = match[1];\n const username = match[2]?.replace(/[._-]+$/u, '');\n if (hashtag !== undefined && includeHashtags) {\n spans.push({\n type: SpanType.Hashtag,\n offset,\n length: match[0].length,\n tag: hashtag,\n });\n } else if (username !== undefined && includeMentions) {\n spans.push({\n type: SpanType.Mention,\n offset,\n length: username.length + 1,\n username,\n });\n }\n }\n }\n\n return spans.sort((left, right) => left.offset - right.offset || right.length - left.length);\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { CreatePollInput } from '../types/params.js';\nimport { BUILDER, type BuilderInput, type ItdBuilder, resolveInput } from './base.js';\n\n/** Минимальное число вариантов ответа. */\nconst MIN_OPTIONS = 2;\n\n/** Ограничения опроса — те же, что действуют в интерфейсе итд.com. */\nconst MAX_OPTIONS = 10;\nconst MAX_QUESTION_LENGTH = 200;\nconst MAX_OPTION_LENGTH = 100;\n\n/**\n * Проверяет данные опроса.\n *\n * Применяется и к билдеру, и к обычному объекту — правила одни и те же.\n *\n * @throws {ItdConfigError} если вопрос пуст, вариантов меньше двух или есть дубли\n */\nexport function validatePoll(input: CreatePollInput): CreatePollInput {\n const question = input?.question;\n\n if (typeof question !== 'string' || question.trim() === '') {\n throw new ItdConfigError('Опрос требует непустого вопроса');\n }\n\n if (question.trim().length > MAX_QUESTION_LENGTH) {\n throw new ItdConfigError(\n `Вопрос длиннее ${MAX_QUESTION_LENGTH} символов (передано ${question.trim().length})`,\n );\n }\n\n const options = Array.isArray(input.options) ? input.options : [];\n\n const texts = options.map((option, index) => {\n const text = typeof option?.text === 'string' ? option.text.trim() : '';\n if (text === '') {\n throw new ItdConfigError(`Вариант ответа №${index + 1} пуст — у каждого должен быть текст`);\n }\n if (text.length > MAX_OPTION_LENGTH) {\n throw new ItdConfigError(\n `Вариант ответа №${index + 1} длиннее ${MAX_OPTION_LENGTH} символов (передано ${text.length})`,\n );\n }\n return text;\n });\n\n if (texts.length < MIN_OPTIONS) {\n throw new ItdConfigError(\n `Опрос требует минимум ${MIN_OPTIONS} варианта, передано: ${texts.length}`,\n );\n }\n\n if (texts.length > MAX_OPTIONS) {\n throw new ItdConfigError(\n `Опрос допускает не больше ${MAX_OPTIONS} вариантов, передано: ${texts.length}`,\n );\n }\n\n const seen = new Set<string>();\n for (const text of texts) {\n if (seen.has(text)) {\n throw new ItdConfigError(`Вариант «${text}» повторяется — варианты должны различаться`);\n }\n seen.add(text);\n }\n\n return {\n question: question.trim(),\n options: texts.map((text) => ({ text })),\n // Поле обязательно: без него сервер отвергает создание поста с опросом,\n // даже когда выбор одиночный.\n multipleChoice: input.multipleChoice ?? false,\n };\n}\n\n/**\n * Билдер опроса.\n *\n * Неизменяемый: каждый вызов возвращает новый экземпляр, поэтому заготовку можно\n * переиспользовать, не боясь её испортить. Создаётся функцией {@link poll}.\n */\nexport class PollBuilder implements ItdBuilder<CreatePollInput> {\n /** @internal */\n readonly [BUILDER] = true as const;\n\n readonly #state: CreatePollInput;\n\n /** @internal Создавайте билдер функцией {@link poll}. */\n constructor(state: CreatePollInput) {\n this.#state = state;\n }\n\n /** Задаёт вопрос. */\n question(text: string): PollBuilder {\n return new PollBuilder({ ...this.#state, question: text });\n }\n\n /** Добавляет один вариант ответа. */\n option(text: string): PollBuilder {\n return new PollBuilder({ ...this.#state, options: [...this.#state.options, { text }] });\n }\n\n /**\n * Добавляет несколько вариантов сразу.\n *\n * @example\n * ```ts\n * poll('ну как?').options('да', 'нет', 'не знаю');\n * ```\n */\n options(...texts: string[]): PollBuilder {\n return new PollBuilder({\n ...this.#state,\n options: [...this.#state.options, ...texts.map((text) => ({ text }))],\n });\n }\n\n /** Разрешает выбор нескольких вариантов. */\n multipleChoice(enabled = true): PollBuilder {\n return new PollBuilder({ ...this.#state, multipleChoice: enabled });\n }\n\n build(): CreatePollInput {\n return validatePoll(this.#state);\n }\n\n toJSON(): CreatePollInput {\n return this.build();\n }\n}\n\n/**\n * Начинает сборку опроса.\n *\n * @param question вопрос; можно задать позже методом {@link PollBuilder.question}\n *\n * @example\n * ```ts\n * import { poll } from 'itd-api';\n *\n * const q = poll('Какой язык лучше?')\n * .options('TypeScript', 'JavaScript')\n * .multipleChoice();\n *\n * await itd.posts.create({ content: 'голосуем', poll: q });\n * ```\n */\nexport function poll(question = ''): PollBuilder {\n return new PollBuilder({ question, options: [] });\n}\n\n/** Что принимает параметр опроса: объект, билдер или функция-настройщик. */\nexport type PollInput = BuilderInput<CreatePollInput, PollBuilder>;\n\n/** Приводит любую форму входа к готовым данным опроса. */\nexport function resolvePoll(input: PollInput): CreatePollInput {\n return resolveInput(input, () => poll(), validatePoll);\n}\n","import type { FileInput } from '../core/attachments/contracts.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport type { Span, UserId } from '../models/common.js';\nimport { type ParseMarkupOptions, parseHtml, parseMarkdown } from '../spans/parse.js';\nimport { validateSpans } from '../spans/validate.js';\nimport { SpanType } from '../types/enums.js';\nimport type { CreatePostData, UpdatePostInput } from '../types/params.js';\nimport { BUILDER, type BuilderInput, type ItdBuilder, isBuilder } from './base.js';\nimport { type AutoSpansOptions, autoSpans, type MarkupInput, resolveMarkup } from './markup.js';\nimport { type PollInput, resolvePoll } from './poll.js';\n\n/** UUID любой версии. */\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst BUILD_UPDATE = Symbol.for('itd.postBuilder.update');\n\n/** Данные для создания поста, включая поддерживаемые builder-формы вложенного опроса. */\nexport interface CreatePostInput extends Omit<CreatePostData, 'poll'> {\n /** Опрос: обычный объект, {@link PollBuilder} или функция-настройщик. */\n poll?: PollInput;\n}\n\n/**\n * Проверяет и нормализует данные поста.\n *\n * Отдельного внимания заслуживает `wallRecipientId`: API принимает там **только UUID**,\n * а имя пользователя молча приводит к ошибке на сервере. Проверка здесь превращает\n * это в понятное сообщение до отправки запроса.\n *\n * @throws {ItdConfigError} если пост пуст или получатель стены задан именем пользователя\n */\nexport function validatePost(input: CreatePostInput): CreatePostData {\n const content = typeof input?.content === 'string' ? input.content : '';\n const attachmentIds = input?.attachmentIds ?? [];\n const files = input?.files ?? [];\n\n const hasContent = content.trim() !== '';\n const hasAttachments = attachmentIds.length > 0 || files.length > 0;\n const hasPoll = Boolean(input?.poll);\n\n if (!hasContent && !hasAttachments && !hasPoll) {\n throw new ItdConfigError('Пост пуст: нужен текст, вложение или опрос');\n }\n\n const wallRecipientId = input.wallRecipientId;\n if (wallRecipientId !== undefined && wallRecipientId !== null) {\n if (!UUID_PATTERN.test(wallRecipientId)) {\n throw new ItdConfigError(\n `wallRecipientId должен быть UUID, а не именем пользователя (получено: «${wallRecipientId}»). ` +\n 'Идентификатор можно взять из профиля: (await itd.users.get(username)).id',\n );\n }\n }\n\n // Опрос внутри обычного объекта тоже может быть билдером или функцией — приводим его\n // здесь, чтобы форма записи не влияла на результат и на проверки.\n const { poll: inputPoll, ...data } = input;\n return {\n ...data,\n ...(input.spans !== undefined ? { spans: validateSpans(content, input.spans) } : {}),\n ...(inputPoll !== undefined ? { poll: resolvePoll(inputPoll) } : {}),\n };\n}\n\n/** Внутреннее состояние {@link PostBuilder}. */\ninterface PostState extends CreatePostInput {\n content: string;\n contentSet: boolean;\n attachmentIds: string[];\n files: FileInput[];\n}\n\n/**\n * Билдер поста.\n *\n * Неизменяемый: каждый вызов возвращает новый экземпляр, поэтому заготовку можно\n * переиспользовать. Создаётся функцией {@link post}.\n *\n * @example Заготовка для нескольких постов\n * ```ts\n * const onWall = post().onWall(userId);\n *\n * await itd.posts.create(onWall.content('первый'));\n * await itd.posts.create(onWall.content('второй')); // заготовка не испорчена\n * ```\n */\nexport class PostBuilder implements ItdBuilder<CreatePostData> {\n /** @internal */\n readonly [BUILDER] = true as const;\n\n readonly #state: PostState;\n\n /** @internal Создавайте билдер функцией {@link post}. */\n constructor(state: PostState) {\n this.#state = state;\n }\n\n /**\n * Задаёт текст поста, заменяя прежний вместе с его разметкой.\n *\n * Spans привязаны к конкретному тексту, поэтому после замены их нужно задать заново\n * через {@link spans}, {@link markup} или {@link autoSpans}.\n */\n content(text: string): PostBuilder {\n const state: PostState = { ...this.#state, content: text, contentSet: true };\n delete state.spans;\n return new PostBuilder(state);\n }\n\n /** Дописывает текст к уже заданному. */\n append(text: string): PostBuilder {\n const separator = this.#state.content === '' ? '' : '\\n';\n return new PostBuilder({\n ...this.#state,\n content: this.#state.content + separator + text,\n contentSet: true,\n });\n }\n\n /**\n * Задаёт готовую разметку текста. Смещения проверяются при {@link build}.\n *\n * Для автоматического поиска сущностей есть {@link autoSpans}, а для вычисления смещений\n * при сборке текста — {@link markup}.\n */\n spans(spans: Span[]): PostBuilder {\n return new PostBuilder({ ...this.#state, spans });\n }\n\n /**\n * Заменяет текст и разметку результатом {@link MarkupBuilder}.\n *\n * @example\n * ```ts\n * post().markup((m) => m.text('смотрите ').hashtag('котики').text(' от ').mention('nowkie'));\n * ```\n */\n markup(input: MarkupInput): PostBuilder {\n const result = resolveMarkup(input);\n return new PostBuilder({\n ...this.#state,\n content: result.content,\n contentSet: true,\n spans: result.spans,\n });\n }\n\n /** Заменяет текст и spans результатом безопасного разбора Markdown. */\n markdown(source: string, options: ParseMarkupOptions = {}): PostBuilder {\n return this.markup(parseMarkdown(source, options));\n }\n\n /** Заменяет текст и spans результатом безопасного разбора ограниченного HTML. */\n html(source: string, options: ParseMarkupOptions = {}): PostBuilder {\n return this.markup(parseHtml(source, options));\n }\n\n /**\n * Находит HTTP(S)-ссылки, хэштеги и упоминания в уже заданном тексте.\n *\n * Ручные стили сохраняются. Повторный вызов не дублирует уже найденные сущности.\n */\n autoSpans(options: AutoSpansOptions = {}): PostBuilder {\n const current = this.#state.spans ?? [];\n const entityTypes = new Set<string>([SpanType.Hashtag, SpanType.Mention, SpanType.Link]);\n const detected = autoSpans(this.#state.content, options).filter((candidate) => {\n return !current.some(\n (span) =>\n entityTypes.has(span.type) &&\n span.offset < candidate.offset + candidate.length &&\n candidate.offset < span.offset + span.length,\n );\n });\n\n return new PostBuilder({\n ...this.#state,\n spans: [...current, ...detected].sort(\n (left, right) => left.offset - right.offset || right.length - left.length,\n ),\n });\n }\n\n /**\n * Публикует пост на стене другого пользователя.\n *\n * @param userId **UUID** пользователя; имя пользователя не подойдёт\n */\n onWall(userId: UserId): PostBuilder {\n return new PostBuilder({ ...this.#state, wallRecipientId: userId });\n }\n\n /**\n * Прикладывает файл — он будет загружен перед публикацией.\n *\n * Порядок вызовов сохраняется в порядке вложений.\n */\n attach(file: FileInput): PostBuilder {\n return new PostBuilder({ ...this.#state, files: [...this.#state.files, file] });\n }\n\n /** Прикладывает уже загруженное вложение по его идентификатору. */\n attachId(attachmentId: string): PostBuilder {\n return new PostBuilder({\n ...this.#state,\n attachmentIds: [...this.#state.attachmentIds, attachmentId],\n });\n }\n\n /**\n * Добавляет опрос.\n *\n * Принимает объект, {@link PollBuilder} или функцию-настройщик.\n *\n * @example\n * ```ts\n * post('голосуем').poll((q) => q.question('ну как?').options('да', 'нет'));\n * ```\n */\n poll(input: PollInput): PostBuilder {\n return new PostBuilder({ ...this.#state, poll: resolvePoll(input) });\n }\n\n #input(includeEmptyContent = false): CreatePostInput {\n const { content, contentSet: _contentSet, attachmentIds, files, ...rest } = this.#state;\n return {\n ...rest,\n ...(content !== '' || includeEmptyContent ? { content } : {}),\n ...(attachmentIds.length > 0 ? { attachmentIds } : {}),\n ...(files.length > 0 ? { files } : {}),\n };\n }\n\n build(): CreatePostData {\n return validatePost(this.#input());\n }\n\n /** @internal Собирает данные по правилам `posts.update`, не применяя правила создания. */\n [BUILD_UPDATE](): UpdatePostInput {\n return validatePostUpdate(this.#input(this.#state.contentSet));\n }\n\n toJSON(): CreatePostData {\n return this.build();\n }\n}\n\n/**\n * Начинает сборку поста.\n *\n * @param content текст; можно задать позже методом {@link PostBuilder.content}\n *\n * @example\n * ```ts\n * import { post } from 'itd-api';\n *\n * await itd.posts.create(\n * post('смотрите что нашёл')\n * .attach({ url: 'https://example.com/photo.jpg' })\n * .poll((q) => q.question('нравится?').options('да', 'нет')),\n * );\n * ```\n */\nexport function post(content?: string): PostBuilder {\n return new PostBuilder({\n content: content ?? '',\n contentSet: content !== undefined,\n attachmentIds: [],\n files: [],\n });\n}\n\n/** Что принимает параметр поста: объект, билдер или функция-настройщик. */\nexport type PostInput = BuilderInput<CreatePostInput, PostBuilder>;\n\n/** Приводит любую форму входа к готовым данным поста. */\nexport function resolvePost(input: PostInput): CreatePostData {\n const resolved = typeof input === 'function' ? input(post()) : input;\n return isBuilder<CreatePostData>(resolved)\n ? validatePost(resolved.build())\n : validatePost(resolved);\n}\n\n/** Что принимает `posts.update`: объект, билдер поста или функция-настройщик. */\nexport type PostUpdateInput =\n | UpdatePostInput\n | PostBuilder\n | ((builder: PostBuilder) => PostBuilder | UpdatePostInput);\n\nfunction validatePostUpdate(input: CreatePostInput | UpdatePostInput): UpdatePostInput {\n if (!input || typeof input !== 'object') {\n throw new ItdConfigError('Для обновления поста нужен объект с явно заданным content');\n }\n\n const unsupported = ['wallRecipientId', 'attachmentIds', 'files', 'poll'].filter(\n (key) => (input as CreatePostInput)[key as keyof CreatePostInput] !== undefined,\n );\n if (unsupported.length > 0) {\n throw new ItdConfigError(\n `posts.update изменяет только content и spans; не поддерживаются: ${unsupported.join(', ')}`,\n );\n }\n\n if (input.content === undefined) {\n throw new ItdConfigError(\n 'posts.update требует явно заданный content; обновление одних spans могло бы стереть текст поста',\n );\n }\n if (typeof input.content !== 'string') {\n throw new ItdConfigError('content обновляемого поста должен быть строкой');\n }\n if (input.spans !== undefined && !Array.isArray(input.spans)) {\n throw new ItdConfigError('spans обновляемого поста должен быть массивом');\n }\n\n return {\n content: input.content,\n ...(input.spans !== undefined ? { spans: validateSpans(input.content, input.spans) } : {}),\n };\n}\n\n/** Приводит вход `posts.update` к поддерживаемым данным поста. */\nexport function resolvePostUpdate(input: PostUpdateInput): UpdatePostInput {\n if (typeof input === 'function') {\n const result = input(post());\n return isBuilder<CreatePostInput>(result)\n ? (result as PostBuilder)[BUILD_UPDATE]()\n : validatePostUpdate(result);\n }\n\n return isBuilder<CreatePostInput>(input)\n ? (input as PostBuilder)[BUILD_UPDATE]()\n : validatePostUpdate(input);\n}\n","import { type CommentInput, resolveComment } from '../builders/comment.js';\nimport {\n type PostInput,\n type PostUpdateInput,\n resolvePost,\n resolvePostUpdate,\n} from '../builders/post.js';\nimport type { FileInput } from '../core/attachments/contracts.js';\nimport type { HttpClient } from '../core/http.js';\nimport {\n type Page,\n PaginationMode,\n type Paginator,\n readCursorPage,\n readFlatCursorPage,\n} from '../core/pagination.js';\nimport { pickArray } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { UserRef } from '../models/common.js';\nimport type {\n Comment,\n LikeResult,\n PinPostResult,\n Poll,\n Post,\n PostStats,\n} from '../models/content.js';\nimport type { CommentSort, FeedTab } from '../types/enums.js';\nimport type { RequestOptions } from '../types/options.js';\nimport type { CreateCommentInput, CreatePostData } from '../types/params.js';\nimport { BaseResource } from './base.js';\n\n/** Курсорная позиция из параметров: если курсор задан — с него, иначе с начала. */\nfunction cursorStart(params: { cursor?: string | undefined }): { cursor?: string } {\n return params.cursor ? { cursor: params.cursor } : {};\n}\n\n/** Параметры запроса ленты. */\nexport interface FeedParams extends RequestOptions {\n /** Вкладка ленты. По умолчанию сервер отдаёт популярное. */\n tab?: FeedTab;\n /** Сколько постов на страницу. */\n limit?: number;\n /**\n * Курсор следующей страницы из предыдущего ответа.\n *\n * Передавайте значение как есть: его формат зависит от вкладки и может измениться.\n */\n cursor?: string;\n /** Ограничение числа страниц при переборе. */\n maxPages?: number;\n}\n\n/** Параметры запроса постов пользователя. */\nexport interface UserPostsParams extends RequestOptions {\n limit?: number;\n cursor?: string;\n /** Порядок сортировки. */\n sort?: string;\n /** Закреплённый пост, чтобы сервер поднял его наверх. */\n pinnedPostId?: string;\n maxPages?: number;\n}\n\n/** Параметры запроса комментариев к посту. */\nexport interface CommentsParams extends RequestOptions {\n limit?: number;\n /**\n * Курсор следующей страницы: идентификатор последнего полученного комментария.\n *\n * Передавайте значение из `nextCursor` предыдущего ответа как есть.\n */\n cursor?: string;\n sort?: CommentSort;\n maxPages?: number;\n}\n\n/**\n * Посты: лента, публикация, реакции, репосты, комментарии.\n *\n * Доступна как `itd.posts`.\n */\nexport class PostsResource extends BaseResource {\n readonly #uploadFiles: (files: FileInput[], options?: RequestOptions) => Promise<string[]>;\n\n /** Лента: `/api/posts`, курсорная пагинация. */\n readonly #feed = this.paginated<Post, FeedParams>({\n path: () => '/api/posts',\n query: (p) => ({ tab: p.tab, limit: p.limit }),\n start: cursorStart,\n read: (body) => readCursorPage<Post>(body, 'posts'),\n mode: PaginationMode.Cursor,\n });\n\n /** Стена пользователя: `/api/posts/user/{user}`. */\n readonly #wall = this.paginated<Post, UserPostsParams & { user: UserRef }>({\n path: (p) => `/api/posts/user/${encodePathSegment(p.user, 'user')}`,\n query: (p) => ({ limit: p.limit, sort: p.sort, pinnedPostId: p.pinnedPostId }),\n start: cursorStart,\n read: (body) => readCursorPage<Post>(body, 'posts'),\n mode: PaginationMode.Cursor,\n });\n\n /** Понравившиеся посты пользователя: `/api/posts/user/{user}/liked`. */\n readonly #liked = this.paginated<Post, UserPostsParams & { user: UserRef }>({\n path: (p) => `/api/posts/user/${encodePathSegment(p.user, 'user')}/liked`,\n query: (p) => ({ limit: p.limit }),\n start: cursorStart,\n read: (body) => readCursorPage<Post>(body, 'posts'),\n mode: PaginationMode.Cursor,\n });\n\n /** Комментарии к посту: курсор лежит рядом со списком, поэтому свой reader. */\n readonly #comments = this.paginated<Comment, CommentsParams & { postId: string }>({\n path: (p) => `/api/posts/${encodePathSegment(p.postId, 'postId')}/comments`,\n query: (p) => ({ limit: p.limit, sort: p.sort }),\n start: cursorStart,\n read: (body) => readFlatCursorPage<Comment>(body, 'comments'),\n mode: PaginationMode.Cursor,\n });\n\n constructor(\n http: HttpClient,\n deps: { uploadFiles: (files: FileInput[], options?: RequestOptions) => Promise<string[]> },\n ) {\n super(http);\n this.#uploadFiles = deps.uploadFiles;\n }\n\n /**\n * Загружает страницу ленты.\n *\n * @example\n * ```ts\n * const page = await itd.posts.list({ tab: FeedTab.Following, limit: 20 });\n * const next = await itd.posts.list({ tab: FeedTab.Following, cursor: page.nextCursor ?? undefined });\n * ```\n */\n list(params: FeedParams = {}): Promise<Page<Post>> {\n return this.#feed.list(params);\n }\n\n /**\n * Перебирает ленту, сама подставляя курсоры.\n *\n * @example\n * ```ts\n * for await (const post of itd.posts.iterate({ tab: 'following' })) {\n * console.log(post.author.username, post.content);\n * }\n * ```\n */\n iterate(params: FeedParams = {}): Paginator<Post> {\n return this.#feed.iterate(params);\n }\n\n /**\n * Публикует пост.\n *\n * Принимает обычный объект, {@link PostBuilder} или функцию-настройщик. Файлы из поля\n * `files` загружаются автоматически, порядок вложений сохраняется.\n *\n * @example\n * ```ts\n * await itd.posts.create({ content: 'привет' });\n * await itd.posts.create((p) => p.content('привет').attach({ url: 'https://example.com/photo.jpg' }));\n * ```\n */\n async create(input: PostInput, options: RequestOptions = {}): Promise<Post> {\n const data = resolvePost(input);\n const attachmentIds = await this.#collectAttachments(data, options);\n\n return this.http.request<Post>({\n method: 'POST',\n path: '/api/posts',\n body: {\n content: data.content ?? '',\n ...(data.spans ? { spans: data.spans } : {}),\n ...(data.wallRecipientId ? { wallRecipientId: data.wallRecipientId } : {}),\n ...(attachmentIds.length > 0 ? { attachmentIds } : {}),\n ...(data.poll ? { poll: data.poll } : {}),\n },\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Загружает один пост вместе с топовыми комментариями.\n *\n * В отличие от списков, здесь у поста заполнено поле `comments`.\n */\n get(postId: string, options: RequestOptions = {}): Promise<Post> {\n return this.http.request<Post>({\n method: 'GET',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}`,\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Редактирует текст и разметку поста.\n *\n * Как и {@link create}, принимает объект, готовый {@link PostBuilder} или\n * функцию-настройщик. Поля создания поста, которые update endpoint не поддерживает\n * (вложения, опрос и стена), отвергаются до запроса.\n */\n update(postId: string, input: PostUpdateInput, options: RequestOptions = {}): Promise<Post> {\n const data = resolvePostUpdate(input);\n return this.http.request<Post>({\n method: 'PUT',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}`,\n body: { content: data.content, ...(data.spans ? { spans: data.spans } : {}) },\n ...this.requestOptions(options),\n });\n }\n\n /** Удаляет пост. Восстановить его можно через {@link restore}. */\n remove(postId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}`,\n ...this.requestOptions(options),\n });\n }\n\n /** Восстанавливает удалённый пост. */\n restore(postId: string, options: RequestOptions = {}): Promise<Post> {\n return this.http.request<Post>({\n method: 'POST',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/restore`,\n ...this.requestOptions(options),\n });\n }\n\n /** Ставит реакцию на пост. */\n like(postId: string, options: RequestOptions = {}): Promise<LikeResult> {\n return this.http.request<LikeResult>({\n method: 'POST',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/like`,\n ...this.requestOptions(options),\n });\n }\n\n /** Убирает реакцию с поста. */\n unlike(postId: string, options: RequestOptions = {}): Promise<LikeResult> {\n return this.http.request<LikeResult>({\n method: 'DELETE',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/like`,\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Делает репост с необязательным комментарием.\n *\n * Вложения к репосту не поддерживаются: сервер их игнорирует, поэтому параметров\n * для файлов здесь нет.\n */\n repost(postId: string, content = '', options: RequestOptions = {}): Promise<Post> {\n return this.http.request<Post>({\n method: 'POST',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/repost`,\n body: { content },\n ...this.requestOptions(options),\n });\n }\n\n /** Отменяет репост. */\n unrepost(postId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/repost`,\n ...this.requestOptions(options),\n });\n }\n\n /** Закрепляет пост в профиле. */\n pin(postId: string, options: RequestOptions = {}): Promise<PinPostResult> {\n return this.http.request<PinPostResult>({\n method: 'POST',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/pin`,\n ...this.requestOptions(options),\n });\n }\n\n /** Открепляет пост. */\n unpin(postId: string, options: RequestOptions = {}): Promise<PinPostResult> {\n return this.http.request<PinPostResult>({\n method: 'DELETE',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/pin`,\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Голосует в опросе.\n *\n * @param optionIds выбранные варианты; несколько допустимы только при `multipleChoice`\n */\n vote(postId: string, optionIds: string[], options: RequestOptions = {}): Promise<Poll> {\n return this.http.request<Poll>({\n method: 'POST',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/poll/vote`,\n body: { optionIds },\n ...this.requestOptions(options),\n });\n }\n\n /** Запрашивает счётчики сразу для нескольких постов. */\n async stats(ids: string[], options: RequestOptions = {}): Promise<PostStats[]> {\n const body = await this.http.request({\n method: 'POST',\n path: '/api/posts/stats',\n body: { ids },\n ...this.requestOptions(options),\n });\n\n return pickArray<PostStats>(body, 'posts');\n }\n\n /**\n * Загружает страницу стены пользователя.\n *\n * Это **не только его собственные посты**: сюда попадают и записи, которые другие\n * оставили на его стене — у них `author` чужой, а `wallRecipient` указывает на владельца\n * стены. Поэтому число записей обычно больше, чем `postsCount` из профиля; чтобы\n * получить только авторские посты, отфильтруйте по `post.author.id`.\n *\n * Принимает и UUID, и имя пользователя.\n */\n byUser(user: UserRef, params: UserPostsParams = {}): Promise<Page<Post>> {\n return this.#wall.list({ ...params, user });\n }\n\n /** Перебирает стену пользователя. Что именно в неё входит — см. {@link byUser}. */\n iterateByUser(user: UserRef, params: UserPostsParams = {}): Paginator<Post> {\n return this.#wall.iterate({ ...params, user });\n }\n\n /** Загружает страницу постов, которые пользователь отметил реакцией. */\n likedByUser(user: UserRef, params: UserPostsParams = {}): Promise<Page<Post>> {\n return this.#liked.list({ ...params, user });\n }\n\n /** Перебирает посты, которые пользователь отметил реакцией. */\n iterateLikedByUser(user: UserRef, params: UserPostsParams = {}): Paginator<Post> {\n return this.#liked.iterate({ ...params, user });\n }\n\n /**\n * Загружает страницу комментариев к посту.\n *\n * У этого эндпоинта курсор и признак продолжения лежат рядом со списком, а не внутри\n * объекта `pagination`, как у остальных, — разница скрыта внутри.\n */\n comments(postId: string, params: CommentsParams = {}): Promise<Page<Comment>> {\n return this.#comments.list({ ...params, postId });\n }\n\n /** Перебирает комментарии к посту. */\n iterateComments(postId: string, params: CommentsParams = {}): Paginator<Comment> {\n return this.#comments.iterate({ ...params, postId });\n }\n\n /**\n * Комментирует пост.\n *\n * @example\n * ```ts\n * await itd.posts.comment(postId, 'согласен');\n * await itd.posts.comment(postId, (c) => c.content('смотри').attach(blob));\n * ```\n */\n async comment(\n postId: string,\n input: CommentInput | string,\n options: RequestOptions = {},\n ): Promise<Comment> {\n const data = resolveComment(typeof input === 'string' ? { content: input } : input);\n const attachmentIds = await this.#collectAttachments(data, options);\n\n return this.http.request<Comment>({\n method: 'POST',\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/comments`,\n body: { content: data.content ?? '', attachmentIds },\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Отправляет голосовой комментарий.\n *\n * Текста у такого комментария нет: сервер ждёт пустой `content` и одно аудиовложение\n * в формате `audio/ogg`.\n *\n * @example\n * ```ts\n * import { fromPath } from 'itd-api/node';\n *\n * await itd.posts.voiceComment(postId, fromPath('./answer.ogg'));\n * ```\n */\n voiceComment(postId: string, audio: FileInput, options: RequestOptions = {}): Promise<Comment> {\n return this.comment(postId, { content: '', files: [audio] }, options);\n }\n\n /** Загружает файлы из входных данных и объединяет их с уже готовыми идентификаторами. */\n async #collectAttachments(\n data: CreatePostData | CreateCommentInput,\n options: RequestOptions,\n ): Promise<string[]> {\n const existing = data.attachmentIds ?? [];\n const files = data.files ?? [];\n\n if (files.length === 0) return existing;\n\n const uploaded = await this.#uploadFiles(files, options);\n return [...existing, ...uploaded];\n }\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport { ReportReason, ReportTargetType } from '../types/enums.js';\nimport type { CreateReportInput } from '../types/params.js';\nimport { BUILDER, type BuilderInput, type ItdBuilder, resolveInput } from './base.js';\n\nconst REASONS = new Set<string>(Object.values(ReportReason));\nconst TARGET_TYPES = new Set<string>(Object.values(ReportTargetType));\n\n/**\n * Проверяет данные жалобы.\n *\n * @throws {ItdConfigError} если объект жалобы или причина заданы неверно\n */\nexport function validateReport(input: CreateReportInput): CreateReportInput {\n if (!input?.targetId || typeof input.targetId !== 'string') {\n throw new ItdConfigError('Жалоба требует идентификатор объекта (targetId)');\n }\n\n if (!TARGET_TYPES.has(input.targetType)) {\n throw new ItdConfigError(\n `targetType должен быть одним из ${[...TARGET_TYPES].join(', ')}, ` +\n `получено: ${String(input.targetType)}`,\n );\n }\n\n if (!REASONS.has(input.reason)) {\n throw new ItdConfigError(\n `Неизвестная причина жалобы «${String(input.reason)}». Допустимые: ${[...REASONS].join(', ')}`,\n );\n }\n\n return input;\n}\n\n/**\n * Билдер жалобы.\n *\n * Точка входа задаёт объект жалобы и его тип одновременно, поэтому рассогласовать\n * `targetType` и `targetId` невозможно. Создаётся объектом {@link report}.\n */\nexport class ReportBuilder implements ItdBuilder<CreateReportInput> {\n /** @internal */\n readonly [BUILDER] = true as const;\n\n readonly #state: Partial<CreateReportInput>;\n\n /** @internal Создавайте билдер через {@link report}. */\n constructor(state: Partial<CreateReportInput>) {\n this.#state = state;\n }\n\n /** Указывает причину жалобы. */\n reason(reason: ReportReason): ReportBuilder {\n return new ReportBuilder({ ...this.#state, reason });\n }\n\n /** Добавляет пояснение в свободной форме. */\n description(text: string): ReportBuilder {\n return new ReportBuilder({ ...this.#state, description: text });\n }\n\n build(): CreateReportInput {\n return validateReport(this.#state as CreateReportInput);\n }\n\n toJSON(): CreateReportInput {\n return this.build();\n }\n}\n\nfunction start(targetType: ReportTargetType, targetId: string): ReportBuilder {\n return new ReportBuilder({ targetType, targetId });\n}\n\n/**\n * Начинает сборку жалобы.\n *\n * Тип объекта выбирается точкой входа, так что указать идентификатор комментария\n * с типом «пост» нельзя в принципе.\n *\n * @example\n * ```ts\n * import { report, ReportReason } from 'itd-api';\n *\n * await itd.reports.create(report.post(postId).reason(ReportReason.Spam));\n * await itd.reports.create(report.user(userId).reason('fraud').description('пишет в личку'));\n * ```\n */\nexport const report = Object.freeze({\n /** Жалоба на пост. */\n post: (postId: string) => start(ReportTargetType.Post, postId),\n /** Жалоба на комментарий. */\n comment: (commentId: string) => start(ReportTargetType.Comment, commentId),\n /** Жалоба на пользователя. */\n user: (userId: string) => start(ReportTargetType.User, userId),\n});\n\n/** Что принимает параметр жалобы: объект, билдер или функция-настройщик. */\nexport type ReportInput = BuilderInput<CreateReportInput, ReportBuilder>;\n\n/** Приводит любую форму входа к готовым данным жалобы. */\nexport function resolveReport(input: ReportInput): CreateReportInput {\n return resolveInput(input, () => new ReportBuilder({}), validateReport);\n}\n","import { type ReportInput, resolveReport } from '../builders/report.js';\nimport type { Report } from '../models/platform.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/**\n * Жалобы на контент и пользователей.\n *\n * Доступна как `itd.reports`.\n */\nexport class ReportsResource extends BaseResource {\n /**\n * Отправляет жалобу.\n *\n * Повторная жалоба на тот же объект отклоняется сервером с сообщением\n * «Вы уже отправляли жалобу на этот контент».\n *\n * @example\n * ```ts\n * await itd.reports.create(report.post(postId).reason('spam'));\n * await itd.reports.create({ targetType: 'user', targetId, reason: 'fraud' });\n * ```\n */\n create(input: ReportInput, options: RequestOptions = {}): Promise<Report> {\n const data = resolveReport(input);\n\n return this.http.request<Report>({\n method: 'POST',\n path: '/api/reports',\n body: data,\n ...this.requestOptions(options),\n });\n }\n}\n","import { pickArray } from '../core/unwrap.js';\nimport type { Hashtag } from '../models/content.js';\nimport type { UserSummary } from '../models/users.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Результат глобального поиска. */\nexport interface SearchResult {\n users: UserSummary[];\n hashtags: Hashtag[];\n}\n\n/**\n * Глобальный поиск.\n *\n * Доступна как `itd.search`.\n */\nexport class SearchResource extends BaseResource {\n /**\n * Ищет пользователей и хэштеги одним запросом.\n *\n * @example\n * ```ts\n * const { users, hashtags } = await itd.search.all('арт');\n * ```\n */\n async all(query: string, options: RequestOptions = {}): Promise<SearchResult> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/search',\n query: { q: query },\n ...this.requestOptions(options),\n });\n\n return {\n users: pickArray<UserSummary>(body, 'users'),\n hashtags: pickArray<Hashtag>(body, 'hashtags'),\n };\n }\n}\n","import { encodePathSegment } from '../core/url.js';\nimport type { PaymentMethod, Subscription } from '../models/account.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/**\n * Подписка и способы оплаты.\n *\n * Доступна как `itd.subscription`.\n */\nexport class SubscriptionResource extends BaseResource {\n /** Загружает состояние подписки и её цену. */\n status(options: RequestOptions = {}): Promise<Subscription> {\n return this.http.request<Subscription>({\n method: 'GET',\n // Завершающий слэш обязателен.\n path: '/api/v1/subscription/',\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Запускает оплату подписки.\n *\n * Форма ответа в документации API не описана, поэтому тип результата не уточняется.\n */\n pay(options: RequestOptions = {}): Promise<unknown> {\n return this.http.request({\n method: 'POST',\n path: '/api/v1/subscription/pay',\n ...this.requestOptions(options),\n });\n }\n\n /** Включает или отключает автопродление. */\n setAutoRenewal(enabled: boolean, options: RequestOptions = {}): Promise<unknown> {\n return this.http.request({\n method: 'POST',\n path: '/api/v1/subscription/auto-renewal',\n body: { enabled },\n ...this.requestOptions(options),\n });\n }\n\n /** Запускает привязку карты. */\n bindCard(options: RequestOptions = {}): Promise<unknown> {\n return this.http.request({\n method: 'POST',\n path: '/api/v1/subscription/bind-card',\n ...this.requestOptions(options),\n });\n }\n\n /** Загружает список способов оплаты. Пустой массив, если карт нет. */\n async methods(options: RequestOptions = {}): Promise<PaymentMethod[]> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/v1/subscription/methods',\n ...this.requestOptions(options),\n });\n\n return Array.isArray(body) ? (body as PaymentMethod[]) : [];\n }\n\n /** Делает способ оплаты основным. */\n setDefaultMethod(methodId: string, options: RequestOptions = {}): Promise<unknown> {\n return this.http.request({\n method: 'POST',\n path: `/api/v1/subscription/methods/${encodePathSegment(methodId, 'methodId')}/default`,\n ...this.requestOptions(options),\n });\n }\n\n /** Удаляет способ оплаты. */\n removeMethod(methodId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: `/api/v1/subscription/methods/${encodePathSegment(methodId, 'methodId')}`,\n ...this.requestOptions(options),\n });\n }\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport { createDeviceId } from '../core/runtime.js';\nimport { InteractionType, type ViewReason, type ViewSource } from '../types/enums.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Общие опции запроса телеметрии. */\nexport interface TelemetryOptions extends RequestOptions {\n /** Переопределяет идентификатор сессии телеметрии (`sid`) для этого запроса. */\n sid?: string;\n}\n\n/** Часы, используемые tracker-ом для измерения времени просмотра. */\nexport interface TelemetryClock {\n /** Возвращает текущее время в миллисекундах. */\n now(): number;\n}\n\n/** Опции tracker-а просмотра. */\nexport interface ViewTrackerOptions extends TelemetryOptions {\n /** Часы для измерения времени. По умолчанию используется `Date.now()`. */\n clock?: TelemetryClock;\n}\n\n/** Опции накопителя телеметрии. */\nexport interface TelemetryBatchOptions extends TelemetryOptions {\n /**\n * Максимальное число событий в одном запросе.\n *\n * Значение по умолчанию — 50. Это размер клиентской пачки, а не заявленный лимит API.\n */\n maxBatchSize?: number;\n /** Часы для tracker-ов, созданных через накопитель. */\n clock?: TelemetryClock;\n}\n\n/** Событие просмотра поста для {@link TelemetryResource.dwell}. */\nexport interface DwellEntry {\n /** Метка показа — поле `vs` объекта поста. */\n vs: string;\n /** Время появления поста в зоне видимости, epoch-мс. */\n enterAt: number;\n /** Время ухода из зоны видимости, epoch-мс. */\n exitAt: number;\n /** Причина завершения просмотра. */\n reason: ViewReason;\n /** Длительность просмотра в мс. По умолчанию `exitAt - enterAt`. */\n durationMs?: number;\n /** Контекст источника показа. */\n sourceContext?: string;\n /** Источник показа. */\n source?: ViewSource;\n /** Пост уже встречался в этой сессии. */\n repeat?: boolean;\n}\n\n/** Событие взаимодействия с контентом для {@link TelemetryResource.interaction}. */\nexport interface InteractionEntry {\n /** Тип взаимодействия. */\n type: InteractionType;\n /** Метка показа — поле `vs` объекта поста. */\n vs: string;\n /** Идентификатор поста. */\n postId: string;\n /** Индекс вложения, начиная с нуля. */\n mediaIndex?: number;\n /** Источник показа. */\n source?: ViewSource;\n /** Просмотренная позиция видео в мс. */\n positionMs?: number;\n /** Длительность видео в мс. */\n durationMs?: number;\n}\n\n/** Данные для {@link TelemetryResource.startView}. */\nexport interface ViewTrackerInput {\n /** Метка показа — поле `vs` объекта поста. */\n vs: string;\n /** Контекст источника показа. */\n sourceContext?: string;\n /** Источник показа. */\n source?: ViewSource;\n /** Пост уже встречался в этой сессии. */\n repeat?: boolean;\n}\n\n/** Данные открытия фотографии. */\nexport interface PhotoOpenInput {\n /** Метка показа — поле `vs` объекта поста. */\n vs: string;\n /** Идентификатор поста. */\n postId: string;\n /** Индекс открытого вложения, начиная с нуля. */\n mediaIndex: number;\n /** Источник показа. */\n source?: ViewSource;\n}\n\n/** Данные о прогрессе просмотра видео. */\nexport interface VideoProgressInput {\n /** Метка показа — поле `vs` объекта поста. */\n vs: string;\n /** Идентификатор поста. */\n postId: string;\n /** Просмотренная позиция видео в мс. */\n positionMs: number;\n /** Длительность видео в мс. */\n durationMs: number;\n /** Источник показа. */\n source?: ViewSource;\n}\n\n/** Активное измерение времени просмотра. */\nexport interface ViewTracker {\n /** Время начала измерения в миллисекундах. */\n readonly enteredAt: number;\n /** Был ли уже вызван {@link finish}. */\n readonly finished: boolean;\n /**\n * Завершает измерение и передаёт событие.\n *\n * Повторный вызов возвращает тот же Promise и не создаёт второе событие.\n */\n finish(reason: ViewReason): Promise<void>;\n}\n\n/** Явно управляемый накопитель событий телеметрии. */\nexport interface TelemetryBatch {\n /** Число ожидающих событий просмотра. */\n readonly pendingDwell: number;\n /** Число ожидающих событий взаимодействия. */\n readonly pendingInteractions: number;\n /** Закрыт ли накопитель. */\n readonly closed: boolean;\n /** Добавляет одно или несколько событий просмотра. */\n dwell(entry: DwellEntry | readonly DwellEntry[]): this;\n /** Добавляет одно или несколько событий взаимодействия. */\n interaction(entry: InteractionEntry | readonly InteractionEntry[]): this;\n /** Добавляет событие открытия фотографии. */\n photoOpen(input: PhotoOpenInput): this;\n /** Добавляет событие прогресса просмотра видео. */\n videoProgress(input: VideoProgressInput): this;\n /** Начинает измерение просмотра, которое после `finish()` попадёт в этот накопитель. */\n startView(input: ViewTrackerInput): ViewTracker;\n /**\n * Отправляет накопленные события.\n *\n * Опции позволяют заменить, например, отменённый `signal` при повторной попытке.\n */\n flush(options?: TelemetryOptions): Promise<void>;\n /** Отправляет накопленные события и закрывает накопитель. */\n close(): Promise<void>;\n}\n\nconst DEFAULT_BATCH_SIZE = 50;\nconst SYSTEM_CLOCK: TelemetryClock = Object.freeze({ now: () => Date.now() });\n\nfunction assertNonEmpty(value: string, name: string): void {\n if (value.trim().length === 0) throw new ItdConfigError(`${name} должен быть непустой строкой`);\n}\n\nfunction assertFinite(value: number, name: string): void {\n if (!Number.isFinite(value)) throw new ItdConfigError(`${name} должен быть конечным числом`);\n}\n\nfunction assertNonNegative(value: number, name: string): void {\n assertFinite(value, name);\n if (value < 0) throw new ItdConfigError(`${name} должен быть неотрицательным числом`);\n}\n\nfunction validateDwell(entry: DwellEntry): DwellEntry {\n assertNonEmpty(entry.vs, 'vs');\n assertFinite(entry.enterAt, 'enterAt');\n assertFinite(entry.exitAt, 'exitAt');\n if (entry.exitAt < entry.enterAt) {\n throw new ItdConfigError('exitAt не может быть раньше enterAt');\n }\n if (entry.durationMs !== undefined) assertNonNegative(entry.durationMs, 'durationMs');\n return { ...entry };\n}\n\nfunction validateInteraction(entry: InteractionEntry): InteractionEntry {\n assertNonEmpty(entry.vs, 'vs');\n assertNonEmpty(entry.postId, 'postId');\n if (entry.mediaIndex !== undefined) {\n assertNonNegative(entry.mediaIndex, 'mediaIndex');\n if (!Number.isInteger(entry.mediaIndex)) {\n throw new ItdConfigError('mediaIndex должен быть целым числом');\n }\n }\n if (entry.positionMs !== undefined) assertNonNegative(entry.positionMs, 'positionMs');\n if (entry.durationMs !== undefined) assertNonNegative(entry.durationMs, 'durationMs');\n return { ...entry };\n}\n\nfunction photoOpenEntry(input: PhotoOpenInput): InteractionEntry {\n return validateInteraction({ type: InteractionType.PhotoOpen, ...input });\n}\n\nfunction videoProgressEntry(input: VideoProgressInput): InteractionEntry {\n return validateInteraction({ type: InteractionType.VideoProgress, ...input });\n}\n\nclass ViewTrackerImpl implements ViewTracker {\n readonly enteredAt: number;\n readonly #clock: TelemetryClock;\n readonly #input: ViewTrackerInput;\n readonly #submit: (entry: DwellEntry) => Promise<void> | void;\n #result: Promise<void> | undefined;\n\n constructor(\n input: ViewTrackerInput,\n clock: TelemetryClock,\n submit: (entry: DwellEntry) => Promise<void> | void,\n ) {\n assertNonEmpty(input.vs, 'vs');\n this.enteredAt = clock.now();\n assertFinite(this.enteredAt, 'clock.now()');\n this.#input = { ...input };\n this.#clock = clock;\n this.#submit = submit;\n }\n\n get finished(): boolean {\n return this.#result !== undefined;\n }\n\n finish(reason: ViewReason): Promise<void> {\n if (this.#result) return this.#result;\n\n try {\n const exitAt = this.#clock.now();\n assertFinite(exitAt, 'clock.now()');\n if (exitAt < this.enteredAt) {\n throw new ItdConfigError('часы не могут вернуться назад во время измерения просмотра');\n }\n\n this.#result = Promise.resolve(\n this.#submit({\n ...this.#input,\n enterAt: this.enteredAt,\n exitAt,\n reason,\n }),\n );\n } catch (error) {\n this.#result = Promise.reject(error);\n }\n\n return this.#result;\n }\n}\n\nclass TelemetryBatchImpl implements TelemetryBatch {\n readonly #resource: TelemetryResource;\n readonly #options: TelemetryOptions;\n readonly #clock: TelemetryClock;\n readonly #maxBatchSize: number;\n readonly #onClose: () => void;\n readonly #dwell: DwellEntry[] = [];\n readonly #interactions: InteractionEntry[] = [];\n #state: 'open' | 'closing' | 'closed' = 'open';\n #flushPromise: Promise<void> | undefined;\n #closePromise: Promise<void> | undefined;\n\n constructor(resource: TelemetryResource, options: TelemetryBatchOptions, onClose: () => void) {\n const { maxBatchSize = DEFAULT_BATCH_SIZE, clock = SYSTEM_CLOCK, ...requestOptions } = options;\n if (!Number.isInteger(maxBatchSize) || maxBatchSize < 1) {\n throw new ItdConfigError('maxBatchSize должен быть целым числом от 1');\n }\n this.#resource = resource;\n this.#options = requestOptions;\n this.#clock = clock;\n this.#maxBatchSize = maxBatchSize;\n this.#onClose = onClose;\n }\n\n get pendingDwell(): number {\n return this.#dwell.length;\n }\n\n get pendingInteractions(): number {\n return this.#interactions.length;\n }\n\n get closed(): boolean {\n return this.#state === 'closed';\n }\n\n dwell(entry: DwellEntry | readonly DwellEntry[]): this {\n this.#assertOpen();\n const entries = Array.isArray(entry) ? entry : [entry];\n this.#dwell.push(...entries.map(validateDwell));\n return this;\n }\n\n interaction(entry: InteractionEntry | readonly InteractionEntry[]): this {\n this.#assertOpen();\n const entries = Array.isArray(entry) ? entry : [entry];\n this.#interactions.push(...entries.map(validateInteraction));\n return this;\n }\n\n photoOpen(input: PhotoOpenInput): this {\n return this.interaction(photoOpenEntry(input));\n }\n\n videoProgress(input: VideoProgressInput): this {\n return this.interaction(videoProgressEntry(input));\n }\n\n startView(input: ViewTrackerInput): ViewTracker {\n this.#assertOpen();\n return new ViewTrackerImpl(input, this.#clock, (entry) => {\n this.dwell(entry);\n });\n }\n\n flush(options: TelemetryOptions = {}): Promise<void> {\n if (this.#state === 'closed') return Promise.resolve();\n if (this.#flushPromise) return this.#flushPromise;\n\n const running = this.#flushAll({ ...this.#options, ...options });\n const tracked = running.finally(() => {\n if (this.#flushPromise === tracked) this.#flushPromise = undefined;\n });\n this.#flushPromise = tracked;\n return tracked;\n }\n\n close(): Promise<void> {\n if (this.#state === 'closed') return Promise.resolve();\n if (this.#closePromise) return this.#closePromise;\n\n this.#state = 'closing';\n const running = this.flush()\n .then(() => {\n this.#state = 'closed';\n this.#onClose();\n })\n .catch((error: unknown) => {\n this.#state = 'open';\n throw error;\n });\n const tracked = running.finally(() => {\n if (this.#closePromise === tracked) this.#closePromise = undefined;\n });\n this.#closePromise = tracked;\n return tracked;\n }\n\n async #flushAll(options: TelemetryOptions): Promise<void> {\n while (this.#dwell.length > 0) {\n const chunk = this.#dwell.splice(0, this.#maxBatchSize);\n try {\n await this.#resource.dwell(chunk, options);\n } catch (error) {\n this.#dwell.unshift(...chunk);\n throw error;\n }\n }\n\n while (this.#interactions.length > 0) {\n const chunk = this.#interactions.splice(0, this.#maxBatchSize);\n try {\n await this.#resource.interaction(chunk, options);\n } catch (error) {\n this.#interactions.unshift(...chunk);\n throw error;\n }\n }\n }\n\n #assertOpen(): void {\n if (this.#state !== 'open') {\n throw new ItdConfigError('накопитель телеметрии уже закрывается или закрыт');\n }\n }\n}\n\n/**\n * Телеметрия просмотров и взаимодействий.\n *\n * Ничего не отправляет автоматически: каждый запрос, tracker или накопитель создаётся\n * явным вызовом пользователя. Доступна как `itd.telemetry`.\n */\nexport class TelemetryResource extends BaseResource {\n #sessionId: string | undefined;\n readonly #batches = new Set<TelemetryBatchImpl>();\n\n /** Идентификатор сессии телеметрии, общий для всех событий этого ресурса. */\n get sessionId(): string {\n this.#sessionId ??= createDeviceId();\n return this.#sessionId;\n }\n\n /** Отправляет события просмотра постов (`POST /api/v1/i`). */\n dwell(entries: readonly DwellEntry[], options: TelemetryOptions = {}): Promise<unknown> {\n const validated = entries.map(validateDwell);\n return this.http.request({\n method: 'POST',\n path: '/api/v1/i',\n body: {\n sid: options.sid ?? this.sessionId,\n e: validated.map((entry) => ({\n md: entry.durationMs ?? entry.exitAt - entry.enterAt,\n et: entry.enterAt,\n xt: entry.exitAt,\n r: entry.reason,\n v: entry.vs,\n ...(entry.sourceContext !== undefined ? { sc: entry.sourceContext } : {}),\n ...(entry.source !== undefined ? { s: entry.source } : {}),\n ...(entry.repeat ? { b: 1 } : {}),\n })),\n },\n ...this.requestOptions(options),\n });\n }\n\n /** Отправляет события взаимодействия с контентом (`POST /api/v1/x`). */\n interaction(\n entries: readonly InteractionEntry[],\n options: TelemetryOptions = {},\n ): Promise<unknown> {\n const validated = entries.map(validateInteraction);\n return this.http.request({\n method: 'POST',\n path: '/api/v1/x',\n body: {\n sid: options.sid ?? this.sessionId,\n e: validated.map((entry) => ({\n t: entry.type,\n v: entry.vs,\n ai: entry.postId,\n ...(entry.mediaIndex !== undefined ? { mi: entry.mediaIndex } : {}),\n ...(entry.source !== undefined ? { s: entry.source } : {}),\n ...(entry.positionMs !== undefined ? { pm: Math.round(entry.positionMs) } : {}),\n ...(entry.durationMs !== undefined ? { dm: Math.round(entry.durationMs) } : {}),\n })),\n },\n ...this.requestOptions(options),\n });\n }\n\n /** Начинает измерять время просмотра и отправляет результат после `finish()`. */\n startView(input: ViewTrackerInput, options: ViewTrackerOptions = {}): ViewTracker {\n const { clock = SYSTEM_CLOCK, ...requestOptions } = options;\n return new ViewTrackerImpl(input, clock, async (entry) => {\n await this.dwell([entry], requestOptions);\n });\n }\n\n /** Отправляет событие открытия фотографии. */\n photoOpen(input: PhotoOpenInput, options: TelemetryOptions = {}): Promise<unknown> {\n return this.interaction([photoOpenEntry(input)], options);\n }\n\n /** Отправляет событие прогресса просмотра видео. */\n videoProgress(input: VideoProgressInput, options: TelemetryOptions = {}): Promise<unknown> {\n return this.interaction([videoProgressEntry(input)], options);\n }\n\n /**\n * Создаёт накопитель с явными `flush()` и `close()`.\n *\n * Создание и добавление записей не выполняют сетевых запросов.\n */\n batch(options: TelemetryBatchOptions = {}): TelemetryBatch {\n let batch: TelemetryBatchImpl;\n batch = new TelemetryBatchImpl(this, options, () => {\n this.#batches.delete(batch);\n });\n this.#batches.add(batch);\n return batch;\n }\n\n /** Закрывает все созданные накопители, отправляя оставшиеся записи. */\n async close(): Promise<void> {\n const results = await Promise.allSettled([...this.#batches].map((batch) => batch.close()));\n const errors = results\n .filter((result): result is PromiseRejectedResult => result.status === 'rejected')\n .map((result) => result.reason);\n if (errors.length > 0) {\n throw new AggregateError(errors, 'Не удалось отправить накопленную телеметрию');\n }\n }\n}\n","import type { FileInput } from '../core/attachments/contracts.js';\nimport type { HttpClient } from '../core/http.js';\nimport { type Page, PaginationMode, type Paginator, readPagedPage } from '../core/pagination.js';\nimport { pickArray, pickBoolean, pickString } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { UserId, UserRef } from '../models/common.js';\nimport type { Clan } from '../models/platform.js';\nimport type {\n FollowResult,\n MyProfile,\n PinsResult,\n PrivacySettings,\n PublicProfile,\n UserSummary,\n} from '../models/users.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\nimport type { UploadedFile, UploadOptions } from './files.js';\n\n/**\n * Параметры списков пользователей.\n *\n * ⚠️ Списки подписчиков, подписок и заблокированных на сервере **не листаются**:\n * `page` он игнорирует, а `limit` зажимает на 20. Подробности — в {@link UsersResource.followers}.\n */\nexport interface UserListParams extends RequestOptions {\n /** Сколько записей вернуть. Значения больше 20 сервер молча уменьшает до 20. */\n limit?: number;\n /** Номер страницы. Сервер его игнорирует — оставлен на случай, если пагинацию починят. */\n page?: number;\n maxPages?: number;\n}\n\n/** Изменяемые поля своего профиля. */\nexport interface UpdateProfileInput {\n displayName?: string;\n username?: string;\n /** Эмодзи-аватар: символ клана, а не адрес картинки. */\n avatar?: string;\n bio?: string;\n /** Идентификатор загруженного файла баннера. `null` удаляет текущий баннер. */\n bannerId?: string | null;\n}\n\n/** Изменяемые настройки приватности. */\nexport type UpdatePrivacyInput = Partial<PrivacySettings>;\n\n/**\n * Пользователи: профили, подписки, блокировки, приватность.\n *\n * Доступна как `itd.users`.\n */\nexport class UsersResource extends BaseResource {\n readonly #uploadFile: (file: FileInput, options?: UploadOptions) => Promise<UploadedFile>;\n\n constructor(\n http: HttpClient,\n deps: {\n uploadFile: (file: FileInput, options?: UploadOptions) => Promise<UploadedFile>;\n },\n ) {\n super(http);\n this.#uploadFile = deps.uploadFile;\n }\n\n /**\n * Списки пользователей: подписчики, подписки, заблокированные.\n *\n * Путь приходит в параметрах — так один описатель обслуживает все три эндпоинта. Имена\n * полей перечислены с запасом: списки приходят под `users`, но альтернативное имя ничего\n * не стоит и спасает, если эндпоинт назовёт список по-своему. `page` уходит в запрос, хотя\n * сервер его сейчас не читает (см. {@link followers}): когда починят — заработает само.\n */\n readonly #userList = this.paginated<UserSummary, UserListParams & { path: string }>({\n path: (p) => p.path,\n query: (p) => ({ limit: p.limit }),\n start: (p) => (p.page !== undefined ? { page: p.page } : {}),\n read: (body) => readPagedPage<UserSummary>(body, 'users', 'followers', 'following', 'blocked'),\n mode: PaginationMode.Page,\n });\n\n /** Загружает свой профиль — с подпиской и признаком подтверждённого телефона. */\n me(options: RequestOptions = {}): Promise<MyProfile> {\n return this.http.request<MyProfile>({\n method: 'GET',\n path: '/api/users/me',\n ...this.requestOptions(options),\n });\n }\n\n /** Обновляет свой профиль. Передавайте только изменяемые поля. */\n updateMe(input: UpdateProfileInput, options: RequestOptions = {}): Promise<MyProfile> {\n return this.http.request<MyProfile>({\n method: 'PUT',\n path: '/api/users/me',\n body: input,\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Загружает изображение и устанавливает его баннером профиля.\n *\n * Для установки используется идентификатор, полученный от `/api/files/upload`.\n * Если файл уже загружен, используйте {@link updateMe}: `{ bannerId: file.id }`.\n *\n * @example\n * ```ts\n * await itd.users.setBanner(file, { filename: 'banner.webp' });\n * ```\n */\n async setBanner(file: FileInput, options: UploadOptions = {}): Promise<MyProfile> {\n const uploaded = await this.#uploadFile(file, options);\n return this.updateMe({ bannerId: uploaded.id }, options);\n }\n\n /** Удаляет баннер профиля, устанавливая `bannerId` в `null`. */\n removeBanner(options: RequestOptions = {}): Promise<MyProfile> {\n return this.updateMe({ bannerId: null }, options);\n }\n\n /** Деактивирует аккаунт. Вернуть его можно через {@link restore}. */\n deactivate(options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: '/api/users/me',\n ...this.requestOptions(options),\n });\n }\n\n /** Восстанавливает деактивированный аккаунт. */\n restore(options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'POST',\n path: '/api/users/me/restore',\n ...this.requestOptions(options),\n });\n }\n\n /** Создаёт профиль после регистрации. */\n createProfile(\n input: { username: string; displayName: string; avatar?: string },\n options: RequestOptions = {},\n ): Promise<MyProfile> {\n return this.http.request<MyProfile>({\n method: 'POST',\n path: '/api/users/profile',\n body: input,\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Загружает профиль пользователя.\n *\n * @param user UUID **или** имя пользователя — подходит и то, и другое\n *\n * @example\n * ```ts\n * const profile = await itd.users.get('nowkie');\n * await itd.posts.create({ content: 'привет', wallRecipientId: profile.id });\n * ```\n */\n get(user: UserRef, options: RequestOptions = {}): Promise<PublicProfile> {\n return this.http.request<PublicProfile>({\n method: 'GET',\n path: `/api/users/${encodePathSegment(user, 'user')}`,\n ...this.requestOptions(options),\n });\n }\n\n /** Проверяет, свободно ли имя пользователя. */\n async checkUsername(username: string, options: RequestOptions = {}): Promise<boolean> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/users/check-username',\n query: { username },\n ...this.requestOptions(options),\n });\n\n return pickBoolean(body, 'available');\n }\n\n /** Ищет пользователей по строке запроса. */\n async search(\n query: string,\n params: { limit?: number } & RequestOptions = {},\n ): Promise<UserSummary[]> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/users/search',\n query: { q: query, limit: params.limit },\n ...this.requestOptions(params),\n });\n\n return pickArray<UserSummary>(body, 'users');\n }\n\n /** Загружает рекомендации, на кого подписаться. */\n async whoToFollow(options: RequestOptions = {}): Promise<UserSummary[]> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/users/suggestions/who-to-follow',\n ...this.requestOptions(options),\n });\n\n return pickArray<UserSummary>(body, 'users');\n }\n\n /** Загружает рейтинг кланов. */\n async topClans(options: RequestOptions = {}): Promise<Clan[]> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/users/stats/top-clans',\n ...this.requestOptions(options),\n });\n\n return pickArray<Clan>(body, 'clans');\n }\n\n /**\n * Подписывается на пользователя.\n *\n * У закрытого профиля вместо подписки отправляется заявка — это видно по полю `status`.\n */\n follow(user: UserRef, options: RequestOptions = {}): Promise<FollowResult> {\n return this.http.request<FollowResult>({\n method: 'POST',\n path: `/api/users/${encodePathSegment(user, 'user')}/follow`,\n body: {},\n ...this.requestOptions(options),\n });\n }\n\n /** Отписывается от пользователя. */\n unfollow(user: UserRef, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: `/api/users/${encodePathSegment(user, 'user')}/follow`,\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Загружает подписчиков пользователя.\n *\n * ⚠️ **Сервер этот список не листает.** Возвращаются первые 20 записей и только они:\n * параметр `page` игнорируется (любая страница отдаёт те же записи и `pagination.page: 1`),\n * `limit` больше 20 молча уменьшается, а `hasMore` всегда `false`. Последнее честно —\n * получить продолжение нечем.\n *\n * Числу `total` доверять тоже не стоит: оно расходится с `followersCount` из профиля —\n * на проверенных аккаунтах занижено примерно на 1–4%.\n */\n followers(user: UserRef, params: UserListParams = {}): Promise<Page<UserSummary>> {\n return this.#userPage(`/api/users/${encodePathSegment(user, 'user')}/followers`, params);\n }\n\n /**\n * Перебирает подписчиков.\n *\n * ⚠️ Перебор закончится после первых 20 записей: сервер список не листает —\n * см. {@link followers}. Метод оставлен на случай, если пагинацию починят.\n */\n iterateFollowers(user: UserRef, params: UserListParams = {}): Paginator<UserSummary> {\n return this.#userPaginator(`/api/users/${encodePathSegment(user, 'user')}/followers`, params);\n }\n\n /** Загружает подписки пользователя. Ограничения те же, что у {@link followers}. */\n following(user: UserRef, params: UserListParams = {}): Promise<Page<UserSummary>> {\n return this.#userPage(`/api/users/${encodePathSegment(user, 'user')}/following`, params);\n }\n\n /** Перебирает подписки. Закончится после первых 20 записей — см. {@link followers}. */\n iterateFollowing(user: UserRef, params: UserListParams = {}): Paginator<UserSummary> {\n return this.#userPaginator(`/api/users/${encodePathSegment(user, 'user')}/following`, params);\n }\n\n /**\n * Проверяет, подписаны ли вы, сразу для нескольких пользователей.\n *\n * @returns объект «идентификатор пользователя → подписаны ли вы»\n *\n * @example\n * ```ts\n * const statuses = await itd.users.followStatus([userA, userB]);\n * // { 'b89dee4f-…': true, '35ea3059-…': false }\n * ```\n */\n followStatus(userIds: UserId[], options: RequestOptions = {}): Promise<Record<string, boolean>> {\n return this.http.request<Record<string, boolean>>({\n method: 'POST',\n path: '/api/users/follow-status',\n body: { userIds },\n ...this.requestOptions(options),\n });\n }\n\n /** Блокирует пользователя. */\n block(user: UserRef, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'POST',\n path: `/api/users/${encodePathSegment(user, 'user')}/block`,\n body: {},\n ...this.requestOptions(options),\n });\n }\n\n /** Снимает блокировку. */\n unblock(user: UserRef, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: `/api/users/${encodePathSegment(user, 'user')}/block`,\n ...this.requestOptions(options),\n });\n }\n\n /** Загружает заблокированных пользователей. Ограничения те же, что у {@link followers}. */\n blocked(params: UserListParams = {}): Promise<Page<UserSummary>> {\n return this.#userPage('/api/users/me/blocked', params);\n }\n\n /** Перебирает заблокированных. Закончится после первых 20 записей — см. {@link followers}. */\n iterateBlocked(params: UserListParams = {}): Paginator<UserSummary> {\n return this.#userPaginator('/api/users/me/blocked', params);\n }\n\n /** Загружает настройки приватности. */\n getPrivacy(options: RequestOptions = {}): Promise<PrivacySettings> {\n return this.http.request<PrivacySettings>({\n method: 'GET',\n path: '/api/users/me/privacy',\n ...this.requestOptions(options),\n });\n }\n\n /** Обновляет настройки приватности. Передавайте только изменяемые поля. */\n updatePrivacy(input: UpdatePrivacyInput, options: RequestOptions = {}): Promise<PrivacySettings> {\n return this.http.request<PrivacySettings>({\n method: 'PUT',\n path: '/api/users/me/privacy',\n body: input,\n ...this.requestOptions(options),\n });\n }\n\n /**\n * Загружает значки профиля и выбранный из них.\n *\n * `activePin` — строка-идентификатор, а не объект.\n */\n async pins(options: RequestOptions = {}): Promise<PinsResult> {\n const body = await this.http.request({\n method: 'GET',\n path: '/api/users/me/pins',\n ...this.requestOptions(options),\n });\n\n return {\n pins: pickArray(body, 'pins'),\n // Сервер отдаёт здесь строку-идентификатор, а не объект значка.\n activePin: pickString(body, 'activePin') ?? null,\n };\n }\n\n /** Выбирает активный значок профиля. */\n setPin(slug: string, options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'PUT',\n path: '/api/users/me/pin',\n body: { slug },\n ...this.requestOptions(options),\n });\n }\n\n /** Снимает активный значок. */\n removePin(options: RequestOptions = {}): Promise<void> {\n return this.http.request<void>({\n method: 'DELETE',\n path: '/api/users/me/pin',\n ...this.requestOptions(options),\n });\n }\n\n #userPage(path: string, params: UserListParams): Promise<Page<UserSummary>> {\n return this.#userList.list({ ...params, path });\n }\n\n #userPaginator(path: string, params: UserListParams): Paginator<UserSummary> {\n return this.#userList.iterate({ ...params, path });\n }\n}\n","import type { VerificationStatus } from '../models/platform.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/**\n * Верификация профиля.\n *\n * Доступна как `itd.verification`.\n */\nexport class VerificationResource extends BaseResource {\n /** Загружает статус заявки. Значение `none` означает, что заявка не подавалась. */\n status(options: RequestOptions = {}): Promise<VerificationStatus> {\n return this.http.request<VerificationStatus>({\n method: 'GET',\n path: '/api/verification/status',\n ...this.requestOptions(options),\n });\n }\n\n /** Подаёт заявку на верификацию с видео. */\n submit(videoUrl: string, options: RequestOptions = {}): Promise<unknown> {\n return this.http.request({\n method: 'POST',\n path: '/api/verification/submit',\n body: { videoUrl },\n ...this.requestOptions(options),\n });\n }\n}\n","import type { FileInput } from './core/attachments/contracts.js';\nimport { type AuthEvents, AuthManager } from './core/auth.js';\nimport { BUILT_IN_SERVICES, type ResolvedConfig, resolveConfig } from './core/config.js';\nimport { CookieJar } from './core/cookies.js';\nimport type { Listener, Unsubscribe } from './core/emitter.js';\nimport { HttpClient } from './core/http.js';\nimport {\n composePipeline,\n createAuthMiddleware,\n createPluginsMiddleware,\n createQueueMiddleware,\n createRetryMiddleware,\n createServicesMiddleware,\n type RequestMiddleware,\n} from './core/middleware.js';\nimport type { PipelineRequest, RequestHandler } from './core/pipeline.js';\nimport type { ItdPlugin } from './core/plugins/contracts.js';\nimport { PluginRegistry } from './core/plugins/registry.js';\nimport { RequestQueuePool } from './core/rate-limit.js';\nimport { type ServiceDefinition, ServiceRegistry } from './core/services.js';\nimport type { ItdSession } from './core/storage.js';\nimport { Transport } from './core/transport.js';\nimport type { UserId } from './models/common.js';\nimport { ItdRealtime, type RealtimeOptions } from './realtime/stream.js';\nimport { AuthResource } from './resources/auth.js';\nimport { CommentsResource } from './resources/comments.js';\nimport { FilesResource } from './resources/files.js';\nimport { HashtagsResource } from './resources/hashtags.js';\nimport { NotificationsResource } from './resources/notifications.js';\nimport { PlatformResource } from './resources/platform.js';\nimport { PostsResource } from './resources/posts.js';\nimport { ReportsResource } from './resources/reports.js';\nimport { SearchResource } from './resources/search.js';\nimport { SubscriptionResource } from './resources/subscription.js';\nimport { TelemetryResource } from './resources/telemetry.js';\nimport { UsersResource } from './resources/users.js';\nimport { VerificationResource } from './resources/verification.js';\nimport type {\n ItdClientOptions,\n RawRequestOptions,\n RequestOptions,\n RetryOptions,\n} from './types/options.js';\n\ndeclare global {\n interface SymbolConstructor {\n readonly asyncDispose: unique symbol;\n }\n}\n\nconst CLIENT_PLUGIN_REGISTRIES = new WeakMap<ItdClient, PluginRegistry>();\n\n/** Проверяет возможность подключить плагин к клиенту без вызова `install()`. @internal */\nexport function assertClientCanUsePlugin(client: ItdClient, plugin: ItdPlugin): void {\n CLIENT_PLUGIN_REGISTRIES.get(client)?.assertCanAdd(plugin);\n}\n\n/** Проверяет возможность отключить плагин клиента без изменения реестра. @internal */\nexport function assertClientCanUnusePlugin(client: ItdClient, name: string): void {\n CLIENT_PLUGIN_REGISTRIES.get(client)?.assertCanRemove(name);\n}\n\n/**\n * Скрытые параметры конструктора — не часть публичного API.\n *\n * @internal\n */\nexport interface ItdClientInternals {\n /**\n * Готовая очередь запросов — так {@link ItdAccounts} с `rateLimitScope: 'shared'` даёт\n * нескольким клиентам одну на всех. Свою клиент в этом случае не заводит и, что важнее,\n * не гасит при `close()`: чужие ожидающие запросы это отменило бы.\n */\n queues?: RequestQueuePool | undefined;\n}\n\n/**\n * Клиент API итд.com.\n *\n * Методы сгруппированы по разделам: `itd.posts`, `itd.users`, `itd.comments`, `itd.auth`,\n * `itd.files`. Авторизация, обновление токена, повторы и очередь запросов работают сами.\n *\n * @example Готовый токен — для разового вызова\n * ```ts\n * const itd = new ItdClient({ auth: '<accessToken>' });\n * const me = await itd.users.me();\n * ```\n *\n * @example Полноценная сессия для бота\n * ```ts\n * import { ItdClient } from 'itd-api';\n * import { FileTokenStorage } from 'itd-api/node';\n *\n * const itd = new ItdClient({\n * // `auth` не обязателен: когда хранилище уже содержит сессию, токен берётся оттуда,\n * // а истёкший продлевается сам. Здесь он нужен на первый запуск.\n * // Вход по паролю требует токена капчи — см. AuthInput и TURNSTILE_SITE_KEY.\n * auth: { email, password, getTurnstileToken },\n * storage: new FileTokenStorage('./.itd-session.json'),\n * rateLimit: { concurrency: 4, rps: 8 },\n * });\n *\n * for await (const post of itd.posts.iterate({ tab: 'following' })) {\n * if (!post.isLiked) await itd.posts.like(post.id);\n * }\n * ```\n */\nexport class ItdClient {\n readonly #config: ResolvedConfig;\n readonly #http: HttpClient;\n readonly #transport: Transport;\n readonly #authManager: AuthManager;\n readonly #jar: CookieJar;\n readonly #queues: RequestQueuePool | undefined;\n /** Заведена ли очередь этим клиентом. Чужую он останавливать не вправе. */\n readonly #ownsQueues: boolean;\n readonly #plugins = new PluginRegistry();\n readonly #services: ServiceRegistry;\n /** Порождённые потоки уведомлений — чтобы `close()` мог закрыть их разом. */\n readonly #streams = new Set<ItdRealtime>();\n\n /** Авторизация, сессии и пароли. */\n readonly auth: AuthResource;\n /** Профили, подписки, блокировки, приватность. */\n readonly users: UsersResource;\n /** Лента, публикация, реакции, репосты, комментарии к постам. */\n readonly posts: PostsResource;\n /** Ответы на комментарии и действия над ними. */\n readonly comments: CommentsResource;\n /** Загрузка файлов и медиа. */\n readonly files: FilesResource;\n /** Уведомления: список, счётчик, отметки о прочтении, настройки. */\n readonly notifications: NotificationsResource;\n /** Хэштеги и посты по ним. */\n readonly hashtags: HashtagsResource;\n /** Глобальный поиск по пользователям и хэштегам. */\n readonly search: SearchResource;\n /** Жалобы на контент и пользователей. */\n readonly reports: ReportsResource;\n /** Верификация профиля. */\n readonly verification: VerificationResource;\n /** Подписка и способы оплаты. */\n readonly subscription: SubscriptionResource;\n /** Сведения о платформе: версии приложений, изменения, анонсы, баннер события. */\n readonly platform: PlatformResource;\n /** Телеметрия просмотров. */\n readonly telemetry: TelemetryResource;\n\n constructor(options: ItdClientOptions = {}, internals: ItdClientInternals = {}) {\n CLIENT_PLUGIN_REGISTRIES.set(this, this.#plugins);\n const config = resolveConfig(options);\n this.#config = config;\n this.#jar = new CookieJar();\n this.#services = new ServiceRegistry(config.baseUrl);\n\n // Встроенные сервисы регистрируются первыми и заменить их нельзя\n for (const service of BUILT_IN_SERVICES) this.#services.define(service);\n for (const service of config.services) this.#services.define(service);\n\n // Очередь может прийти извне — общая на несколько аккаунтов. `rateLimit: false` отключает\n // её и в этом случае: отдельный аккаунт вправе не вставать в общую очередь.\n const shared = config.rateLimit ? internals.queues : undefined;\n const queues =\n shared ??\n (config.rateLimit ? new RequestQueuePool(config.rateLimit, config.clock) : undefined);\n this.#queues = queues;\n // Гасить в `close()` можно только свою: остановка чужой отменила бы ожидающие запросы\n // соседних аккаунтов.\n this.#ownsQueues = shared === undefined;\n\n // Заполняется ниже. Транспорту нужен `getDeviceId` авторизации, а авторизации —\n // транспорт; взаимная ссылка замыкается через отложенный вызов.\n let authManager!: AuthManager;\n\n // Конструкторские хуки идут первыми, за ними — динамически подключаемые хуки плагинов.\n // Один и тот же объект передаётся транспорту и retry-слою, поэтому плагин видит каждую\n // сетевую попытку, в том числе onRetry между ними.\n const hooks = this.#plugins.hooks(config.hooks);\n const transport = new Transport(\n { ...config, hooks },\n {\n cookies: config.useCookieJar ? this.#jar : undefined,\n getDeviceId: () => authManager.getDeviceId(),\n onRateLimit:\n queues && config.rateLimit?.respectHeaders\n ? (limit, remaining, request) => this.#throttleByHeaders(limit, remaining, request)\n : undefined,\n },\n );\n this.#transport = transport;\n\n const pluginsLayer = createPluginsMiddleware(this.#plugins);\n const retriesLayer = createRetryMiddleware({\n clock: config.clock,\n retry: config.retry,\n rateLimitDelays: config.rateLimit?.retryDelays ?? [],\n pauseQueue: queues ? (ms, request) => queues.for(request.service).pause(ms) : undefined,\n hooks,\n logger: config.logger,\n buildUrl: (request) => transport.buildUrl(request),\n });\n\n const authRetry: RetryOptions | undefined = config.retry\n ? {\n attempts: config.retry.attempts,\n baseDelay: config.retry.baseDelay,\n maxDelay: config.retry.maxDelay,\n jitter: config.retry.jitter,\n retryWrites: true,\n ...(config.retry.shouldRetry ? { shouldRetry: config.retry.shouldRetry } : {}),\n }\n : undefined;\n const authPipeline = composePipeline([pluginsLayer, retriesLayer], transport.send);\n // Служебные запросы авторизации проходят через плагины и повторы, но не через очередь\n // и не через сам слой авторизации: они часто запускаются изнутри запроса, который уже ждёт токен.\n // Для них POST безопасен к повтору: refresh/sign-in не создают пользовательский контент.\n const authHandler: RequestHandler = (request) =>\n authRetry && request.retry === undefined\n ? authPipeline({ ...request, retry: authRetry })\n : authPipeline(request);\n authManager = new AuthManager(config, authHandler, this.#jar, {\n onAccountChange: () => this.#disconnectStreams(),\n });\n this.#authManager = authManager;\n\n // Порядок слоёв: очередь снаружи, за ней плагины, сервисы, повторы и авторизация,\n // в сердцевине — транспорт.\n const middlewares: RequestMiddleware[] = [];\n if (queues) {\n middlewares.push(\n createQueueMiddleware((request, task) =>\n queues.for(request.service).schedule(task, request.signal),\n ),\n );\n }\n middlewares.push(pluginsLayer);\n middlewares.push(createServicesMiddleware(this.#services));\n middlewares.push(retriesLayer);\n middlewares.push(\n createAuthMiddleware({\n getAuthHeaders: () => authManager.getAuthHeaders(),\n onUnauthorized: () => authManager.onUnauthorized(),\n autoRefresh: config.autoRefresh,\n }),\n );\n\n const handler = composePipeline(middlewares, transport.send);\n this.#http = new HttpClient({ handler, plugins: this.#plugins, baseUrl: config.baseUrl });\n\n this.files = new FilesResource(this.#http, { fetch: config.fetch });\n\n const uploadFiles = (files: FileInput[], requestOptions?: RequestOptions) =>\n this.files.uploadMany(files, requestOptions ?? {});\n const uploadFile = (file: FileInput, requestOptions?: RequestOptions) =>\n this.files.upload(file, requestOptions ?? {});\n\n this.auth = new AuthResource(this.#http, { auth: this.#authManager });\n this.users = new UsersResource(this.#http, { uploadFile });\n this.posts = new PostsResource(this.#http, { uploadFiles });\n this.comments = new CommentsResource(this.#http, { uploadFiles });\n this.notifications = new NotificationsResource(this.#http);\n this.hashtags = new HashtagsResource(this.#http);\n this.search = new SearchResource(this.#http);\n this.reports = new ReportsResource(this.#http);\n this.verification = new VerificationResource(this.#http);\n this.subscription = new SubscriptionResource(this.#http);\n this.platform = new PlatformResource(this.#http);\n this.telemetry = new TelemetryResource(this.#http);\n }\n\n /** Базовый URL, к которому обращается клиент. */\n get baseUrl(): string {\n return this.#config.baseUrl;\n }\n\n /**\n * Выполняет произвольный запрос к API.\n *\n * Запасной путь для случаев, когда нужного метода ещё нет или ответ сервера разошёлся\n * с документацией. Проходит через ту же авторизацию, очередь и обработку ошибок.\n *\n * @example\n * ```ts\n * const raw = await itd.request({ method: 'GET', path: '/api/posts', raw: true });\n * ```\n */\n request<T = unknown>(options: RawRequestOptions): Promise<T> {\n return this.#http.request<T>(options);\n }\n\n /**\n * Подключает плагин.\n *\n * Плагин работает на уровне транспорта: видит запрос до отправки и разобранный ответ,\n * поэтому одна обёртка охватывает сразу все методы клиента. Подключать можно в любой\n * момент, но обычно это делают сразу после создания клиента.\n *\n * @throws {ItdConfigError} если плагин задан неверно или уже подключён\n *\n * @example\n * ```ts\n * import { crypt } from '@itd-api/crypto';\n *\n * itd.use(crypt());\n * await itd.posts.create({ content: 'секрет' }, { encrypt: 'invis' });\n * ```\n */\n use(plugin: ItdPlugin): this {\n this.#plugins.add(plugin, {\n baseUrl: this.#config.baseUrl,\n logger: this.#config.logger,\n getAuthScope: () => this.#authManager.getAuthScope(),\n getAuthIdentity: () => this.#authManager.getAuthIdentity(),\n });\n return this;\n }\n\n /** Имена подключённых плагинов в фактическом порядке выполнения обёрток. */\n pluginNames(): string[] {\n return this.#plugins.names();\n }\n\n /** Подключён ли плагин с таким именем. */\n hasPlugin(name: string): boolean {\n return this.#plugins.has(name);\n }\n\n /**\n * Отключает плагин и освобождает заведённые им ресурсы.\n *\n * Новые запросы перестают видеть плагин сразу. Очистка дождётся логического запроса,\n * который уже проходил через его обёртку.\n *\n * @returns `false`, если такого плагина не было\n * @throws {ItdConfigError} если от плагина зависит другой подключённый плагин\n */\n unuse(name: string): Promise<boolean> {\n return this.#plugins.remove(name);\n }\n\n /**\n * Регистрирует сервис платформы — домен, отличный от основного.\n *\n * Запросы с `{ service: 'имя' }` уходят на его хост с его заголовками. То же самое умеет\n * опция `services` конструктора. Занятое имя не переопределяется — ни своё, ни встроенное:\n * разовому запросу хост задаётся полем `baseUrl`.\n *\n * Заголовок авторизации по умолчанию уходит только своим — домену клиента и его\n * поддоменам. Стороннему хосту токен нужно разрешить явно: `auth: true`.\n *\n * @throws {ItdConfigError} если определение неверно или имя уже занято\n *\n * @example Сервис платформы на поддомене — токен уходит сам\n * ```ts\n * itd.defineService({\n * name: 'pb',\n * baseUrl: 'https://pbapi.xn--d1ah4a.com',\n * headers: { Referer: 'https://pixel.xn--d1ah4a.com/' },\n * });\n *\n * await itd.request({ method: 'GET', service: 'pb', path: '/api/pixel-info' });\n * ```\n */\n defineService(definition: ServiceDefinition): this {\n this.#services.define(definition);\n return this;\n }\n\n /**\n * Базовый URL зарегистрированного сервиса.\n *\n * @throws {ItdConfigError} если сервис не зарегистрирован\n */\n serviceBaseUrl(name: string): string {\n return this.#services.resolveBaseUrl(name);\n }\n\n /**\n * Подписывается на события авторизации.\n *\n * Полезно, чтобы сохранять сессию во внешнее хранилище или узнавать, что вход\n * окончательно потерян.\n *\n * @returns функция отписки\n *\n * @example\n * ```ts\n * itd.on('tokens', ({ accessToken }) => cache.set('itd', accessToken));\n * itd.on('authError', () => notifyUser('Сессия истекла, войдите заново'));\n * ```\n */\n on<K extends keyof AuthEvents>(event: K, listener: Listener<AuthEvents[K]>): Unsubscribe {\n return this.#authManager.on(event, listener);\n }\n\n /**\n * Создаёт поток уведомлений в реальном времени.\n *\n * Каждый вызов даёт новый независимый поток; обычно он нужен один на приложение.\n * Соединение поднимается методом `connect()` и держится само. Замена авторизации на токен\n * другого пользователя завершает все потоки клиента; смена только сессии их не затрагивает.\n *\n * @example\n * ```ts\n * import { NotificationType } from 'itd-api';\n *\n * const stream = itd.realtime();\n *\n * stream.onNotification(NotificationType.PostComment, async ({ update }) => {\n * await handleComment(update.data.notification);\n * });\n * stream.on('unreadCount', (count) => setBadge(count));\n *\n * await stream.connect();\n * ```\n */\n realtime(options: RealtimeOptions = {}): ItdRealtime {\n let stream!: ItdRealtime;\n stream = new ItdRealtime(\n {\n baseUrl: this.#config.baseUrl,\n fetch: this.#config.fetch,\n baseHeaders: (url) => this.#transport.platformHeaders(url),\n getAuthIdentity: () => this.#authManager.getCurrentAuthIdentity(),\n getAuthScope: () => this.#authManager.getAuthScope(),\n getToken: () => this.#authManager.getAccessToken(),\n refresh: () => this.#authManager.onUnauthorized(),\n fetchUnreadCount: () => this.notifications.count(),\n onConnect: () => this.#streams.add(stream),\n onClose: () => this.#streams.delete(stream),\n logger: this.#config.logger,\n clock: this.#config.clock,\n },\n options,\n );\n\n // Регистрируем для `close()`: поток держит открытое соединение и таймер переподключения.\n this.#streams.add(stream);\n return stream;\n }\n\n /**\n * Освобождает ресурсы клиента: закрывает все потоки уведомлений, отправляет открытые\n * накопители {@link telemetry}, затем останавливает очередь запросов.\n *\n * Метод дожидается активных обработчиков потока. После вызова клиентом можно пользоваться\n * снова; ранее созданный поток можно запустить повторным `connect()`.\n *\n * Общая очередь, полученная от {@link ItdAccounts}, не останавливается: её гасит сам\n * контейнер, когда закрывает все аккаунты разом.\n *\n * @example\n * ```ts\n * await using itd = new ItdClient({ auth: token });\n * // …работа…\n * // dispose() вызовется сам на выходе из блока\n * ```\n */\n async close(): Promise<void> {\n const streams = this.#disconnectStreams();\n try {\n await Promise.all(streams.map((stream) => stream.drain()));\n await this.telemetry.close();\n } finally {\n if (this.#ownsQueues) this.#queues?.stop();\n }\n }\n\n /**\n * Окончательно освобождает клиент: выполняет {@link close} и отключает все плагины.\n *\n * В отличие от `close()`, после `dispose()` плагины не восстанавливаются автоматически.\n * Сам клиент остаётся пригоден для обычных запросов; при необходимости плагины можно\n * подключить заново через {@link use}.\n */\n async dispose(): Promise<void> {\n const results = await Promise.allSettled([this.close(), this.#plugins.dispose()]);\n const errors = results\n .filter((result): result is PromiseRejectedResult => result.status === 'rejected')\n .map((result) => result.reason);\n if (errors.length > 0) throw new AggregateError(errors, 'Не удалось освободить клиент');\n }\n\n /** Завершает потоки до того, как запросы начнут использовать другой аккаунт. */\n #disconnectStreams(): ItdRealtime[] {\n const streams = [...this.#streams];\n for (const stream of streams) stream.disconnect();\n this.#streams.clear();\n return streams;\n }\n\n /** Позволяет использовать клиент с `await using`. */\n [Symbol.asyncDispose](): Promise<void> {\n return this.dispose();\n }\n\n // Fallback для `await using` в Node 18, где `Symbol.asyncDispose` отсутствует.\n static {\n if (\n typeof (Symbol as SymbolConstructor & { asyncDispose?: symbol }).asyncDispose !== 'symbol'\n ) {\n const prototype = ItdClient.prototype as unknown as Record<PropertyKey, unknown>;\n prototype[Symbol.for('Symbol.asyncDispose')] = prototype.undefined;\n delete prototype.undefined;\n }\n }\n\n /** Текущая сессия целиком — чтобы сохранить её самостоятельно. */\n getSession(): Promise<ItdSession | null> {\n return this.#authManager.getSession();\n }\n\n /**\n * Идентификатор аккаунта, под которым работает клиент.\n *\n * Читается из токена доступа и не стоит ни одного запроса. `undefined`, когда сессии\n * ещё нет или токен выдан не в формате JWT. Полезен прежде всего с {@link ItdAccounts}:\n * показывает, какому профилю соответствует восстановленная из хранилища запись.\n * Свежий профиль целиком отдаёт `itd.users.me()`.\n *\n * @remarks\n * JWT только декодируется: его подпись не проверяется. Результат подходит для локального\n * разделения состояния, но не доказывает личность пользователя и не заменяет проверку\n * авторизации сервером.\n *\n * @example\n * ```ts\n * for (const [name, itd] of accounts) console.log(name, await itd.getUserId());\n * ```\n */\n getUserId(): Promise<UserId | undefined> {\n return this.#authManager.getUserId();\n }\n\n /** Восстанавливает сохранённую сессию, включая cookie. */\n setSession(session: ItdSession): Promise<void> {\n return this.#authManager.setSession(session);\n }\n\n /**\n * Придерживает очередь, когда лимит сервера исчерпан.\n *\n * Сервер сообщает остаток в заголовке `x-ratelimit-remaining`. Как только тот доходит\n * до нуля, очередь встаёт на первую паузу лестницы — короткую, потому что окно могло\n * почти истечь. Если оно ещё действует, следующий запрос получит `429`, и дальше\n * лестницу отработает планировщик повторов.\n *\n * Смысл этой паузы прежде всего в том, чтобы при работе в несколько потоков остальные\n * запросы не улетели в стену все разом.\n */\n #throttleByHeaders(\n limit: number | undefined,\n remaining: number | undefined,\n request: PipelineRequest,\n ): void {\n if (remaining === undefined || remaining > 0) return;\n\n const first = this.#config.rateLimit?.retryDelays[0];\n if (first === undefined) return;\n\n this.#queues?.for(request.service).pause(first);\n this.#config.logger?.debug(\n `лимит сервера исчерпан (${remaining} из ${limit ?? '?'}), очередь ждёт ${first} мс`,\n );\n }\n}\n\n/**\n * Создаёт клиент API итд.com.\n *\n * То же, что `new ItdClient(options)`, — для тех, кому привычнее фабрика.\n *\n * @example\n * ```ts\n * const itd = createClient({ auth: token });\n * ```\n */\nexport function createClient(options: ItdClientOptions = {}): ItdClient {\n return new ItdClient(options);\n}\n","import { assertClientCanUnusePlugin, assertClientCanUsePlugin, ItdClient } from './client.js';\nimport { systemClock } from './core/clock.js';\nimport { resolveRateLimit } from './core/config.js';\nimport { Emitter, type Listener, reportListenerError, type Unsubscribe } from './core/emitter.js';\nimport { ItdConfigError } from './core/errors.js';\nimport {\n type ControlledTokenStorage,\n controlledTokenStorage,\n isRestorableSession,\n MemoryMultiTokenStorage,\n type MultiTokenStorage,\n} from './core/multi-storage.js';\nimport type { ItdPlugin } from './core/plugins/contracts.js';\nimport { assertPluginRemovable, orderPluginDefinitions } from './core/plugins/order.js';\nimport { RequestQueuePool } from './core/rate-limit.js';\nimport type { TokenStorage } from './core/storage.js';\nimport type { ItdClientOptions, Logger } from './types/options.js';\n\n/** Как аккаунты делят между собой очередь запросов. */\nexport type RateLimitScope = 'account' | 'shared';\n\n/**\n * Опции конструктора {@link ItdAccounts}.\n *\n * Всё, что понимает `ItdClient`, кроме `auth` и `deviceId`: они у каждого аккаунта свои\n * и задаются в {@link ItdAccounts.addAccount}. Обычный `TokenStorage` клиента здесь заменён\n * общей опцией {@link ItdAccountsOptions.storage} типа {@link MultiTokenStorage}; контейнер\n * сам выдаёт каждому клиенту изолированный срез по имени. Общий `deviceId` особенно вреден —\n * сервер различает по нему записи в списке сессий, и один на всех сложил бы все аккаунты\n * в одну.\n */\nexport interface ItdAccountsOptions\n extends Omit<ItdClientOptions, 'auth' | 'storage' | 'deviceId'> {\n /** Общее хранилище сессий всех аккаунтов. По умолчанию {@link MemoryMultiTokenStorage}. */\n storage?: MultiTokenStorage | undefined;\n /** Плагины, подключаемые каждому аккаунту, в том числе добавленному позже. */\n plugins?: readonly ItdPlugin[] | undefined;\n /**\n * Как делить очередь запросов. По умолчанию `'account'` — своя у каждого.\n *\n * Лимиты итд.com считаются по аккаунту, а при работе через разные прокси общая очередь\n * только мешает. Она нужна в другом случае: когда все аккаунты сидят на одном IP\n * и упираются в ограничение по адресу, — тогда `'shared'` разводит их запросы во времени\n * все разом, а не поаккаунтно.\n *\n * Настройки самой очереди берутся из общей опции `rateLimit`. Личный объект `rateLimit`\n * в этом режиме запрещён, потому что не может изменить уже созданную очередь;\n * `rateLimit: false` у отдельного аккаунта выводит его из неё.\n */\n rateLimitScope?: RateLimitScope | undefined;\n}\n\n/**\n * Настройки одного аккаунта. Общее мультихранилище задаёт контейнер, а аккаунт получает\n * свой срез автоматически; остальное — как у `ItdClient`.\n *\n * При `rateLimitScope: 'shared'` объект `rateLimit` задаётся только контейнеру; аккаунту\n * разрешено передать `false`, чтобы не ставить его запросы в общую очередь.\n */\nexport type AddAccountOptions = Omit<ItdClientOptions, 'storage'>;\n\n/** Что можно уточнить при удалении аккаунта. */\nexport interface RemoveAccountOptions {\n /**\n * Удалить и сохранённую сессию. По умолчанию `false` — аккаунт убирается только\n * из памяти, а его токены остаются в хранилище и переживут перезапуск.\n */\n forget?: boolean | undefined;\n}\n\n/**\n * События авторизации всех аккаунтов сразу.\n *\n * Те же, что у одиночного клиента, плюс имя аккаунта: подписка на контейнер избавляет\n * от нужды вешать обработчик на каждого.\n */\nexport interface AccountEvents {\n /** Токен получен или обновлён. */\n tokens: { account: string; accessToken: string };\n /** Выполнен вход. */\n signIn: { account: string; accessToken: string };\n /** Сессия очищена — вручную или из-за неудачного обновления. */\n signOut: { account: string };\n /** Обновить сессию не удалось; дальнейшие запросы этого аккаунта будут падать с 401. */\n authError: { account: string; error: unknown };\n}\n\n/** Проверяет имя до создания клиента или частичного восстановления контейнера. */\nfunction validateAccountName(name: string): void {\n if (typeof name !== 'string' || name.trim() === '') {\n throw new ItdConfigError('имя аккаунта должно быть непустой строкой');\n }\n}\n\n/**\n * Несколько аккаунтов итд.com в одном месте.\n *\n * Контейнер именованных `ItdClient`: каждый аккаунт получает собственный токен, cookie\n * и `deviceId`, а сессии всех складываются в одно хранилище — обычно в один файл, а не\n * в десяток. Имя аккаунта выбираете вы; сервер о нём ничего не знает.\n *\n * @example Бот на нескольких аккаунтах\n * ```ts\n * import { ItdAccounts } from 'itd-api';\n * import { FileMultiTokenStorage } from 'itd-api/node';\n *\n * await using accounts = new ItdAccounts({\n * storage: new FileMultiTokenStorage('./.itd-sessions.json'),\n * rateLimit: { concurrency: 4 },\n * });\n *\n * // Восстанавливаем тех, кто уже входил раньше: токен возьмётся из хранилища.\n * await accounts.restore();\n *\n * if (!accounts.has('kiow')) {\n * accounts.addAccount('kiow', { auth: { email, password, getTurnstileToken } });\n * }\n *\n * await accounts.account('kiow').posts.create({ content: 'привет' });\n *\n * for (const [name, itd] of accounts) {\n * console.log(name, await itd.getUserId());\n * }\n * ```\n */\nexport class ItdAccounts {\n /** Опции, общие для всех аккаунтов, — без полей, которые задаются поаккаунтно. */\n readonly #base: Omit<ItdClientOptions, 'auth' | 'storage' | 'deviceId'>;\n readonly #storage: MultiTokenStorage;\n readonly #clients = new Map<string, ItdClient>();\n /** Имена, чьё удаление ещё не завершилось: повторно занять их пока нельзя. */\n readonly #removing = new Set<string>();\n /** Управляемые срезы хранилища: после удаления аккаунт больше не может писать через свой. */\n readonly #storageControls = new Map<string, ControlledTokenStorage>();\n /** Подписки на события клиентов — снимаются вместе с аккаунтом. */\n readonly #eventUnsubscribers = new Map<string, Unsubscribe[]>();\n /** Плагины для всех: и для уже заведённых аккаунтов, и для будущих. */\n readonly #plugins: ItdPlugin[];\n /** Имена плагинов, чья асинхронная очистка ещё не завершилась. */\n readonly #removingPlugins = new Set<string>();\n /** Общая очередь. `undefined`, когда у каждого аккаунта своя. */\n readonly #queues: RequestQueuePool | undefined;\n readonly #rateLimitScope: RateLimitScope;\n readonly #emitter: Emitter<AccountEvents>;\n readonly #logger: Logger | undefined;\n\n constructor(options: ItdAccountsOptions = {}) {\n const { storage, plugins, rateLimitScope, ...base } = options;\n\n if (\n rateLimitScope !== undefined &&\n rateLimitScope !== 'account' &&\n rateLimitScope !== 'shared'\n ) {\n throw new ItdConfigError(\"rateLimitScope должен быть 'account' или 'shared'\");\n }\n\n this.#base = base;\n this.#storage = storage ?? new MemoryMultiTokenStorage();\n this.#plugins = orderPluginDefinitions(plugins ?? []);\n this.#rateLimitScope = rateLimitScope ?? 'account';\n\n // Общая очередь заводится сразу: проверить опции лучше при создании контейнера,\n // а не при добавлении первого аккаунта.\n const rateLimit =\n this.#rateLimitScope === 'shared' ? resolveRateLimit(base.rateLimit) : undefined;\n this.#queues = rateLimit\n ? new RequestQueuePool(rateLimit, base.clock ?? systemClock)\n : undefined;\n\n const logger = typeof base.logger === 'object' ? base.logger : undefined;\n this.#logger = logger;\n this.#emitter = new Emitter<AccountEvents>((error) =>\n reportListenerError(logger, 'аккаунтов', error),\n );\n }\n\n /** Общее хранилище сессий — то же, что передано опцией `storage`. */\n get storage(): MultiTokenStorage {\n return this.#storage;\n }\n\n /** Сколько аккаунтов заведено. */\n get size(): number {\n return this.#clients.size;\n }\n\n /** Имена заведённых аккаунтов в порядке добавления. */\n names(): string[] {\n return [...this.#clients.keys()];\n }\n\n /** Заведён ли аккаунт с таким именем. */\n has(name: string): boolean {\n return this.#clients.has(name);\n }\n\n /**\n * Заводит аккаунт.\n *\n * Возвращается обычный `ItdClient` — со всеми ресурсами, плагинами и `realtime()`.\n * Хранилище ему подставляется само: срез общего по имени аккаунта.\n *\n * Опция `auth` не обязательна: когда сессия этого аккаунта уже лежит в хранилище,\n * токен возьмётся оттуда, а истёкший продлится сам.\n *\n * @throws {ItdConfigError} если имя пустое или уже занято\n *\n * @example\n * ```ts\n * accounts.addAccount('bot', { auth: { email, password, getTurnstileToken } });\n * accounts.addAccount('reader', { auth: '<accessToken>' });\n * accounts.addAccount('через-прокси', { fetch: proxyFetch('socks5://…') });\n * ```\n */\n addAccount(name: string, options: AddAccountOptions = {}): ItdClient {\n validateAccountName(name);\n if (this.#clients.has(name)) {\n throw new ItdConfigError(\n `аккаунт «${name}» уже добавлен. Возьмите его через accounts.account('${name}')`,\n );\n }\n if (this.#removing.has(name)) {\n throw new ItdConfigError(\n `аккаунт «${name}» ещё удаляется. Дождитесь завершения removeAccount() перед повторным добавлением`,\n );\n }\n if (\n this.#rateLimitScope === 'shared' &&\n options.rateLimit !== undefined &&\n options.rateLimit !== false\n ) {\n throw new ItdConfigError(\n \"при rateLimitScope: 'shared' настройки rateLimit задаются контейнеру; \" +\n 'аккаунту можно передать только rateLimit: false',\n );\n }\n\n const storageControl = controlledTokenStorage(this.#storage, name);\n let client: ItdClient | undefined;\n try {\n client = new ItdClient(\n this.#mergeOptions(options, storageControl.storage),\n this.#queues ? { queues: this.#queues } : {},\n );\n\n for (const plugin of this.#plugins) client.use(plugin);\n } catch (error) {\n storageControl.revoke();\n if (client) {\n void client.dispose().catch((cleanupError: unknown) => {\n this.#reportPluginCleanup('неудачного добавления аккаунта', cleanupError);\n });\n }\n throw error;\n }\n\n const unsubscribers = this.#forwardEvents(name, client);\n this.#storageControls.set(name, storageControl);\n this.#eventUnsubscribers.set(name, unsubscribers);\n this.#clients.set(name, client);\n\n return client;\n }\n\n /**\n * Клиент аккаунта.\n *\n * @throws {ItdConfigError} если такого аккаунта нет\n *\n * @example\n * ```ts\n * await accounts.account('kiow').posts.like(postId);\n * ```\n */\n account(name: string): ItdClient {\n const client = this.#clients.get(name);\n if (!client) {\n const known = this.names();\n throw new ItdConfigError(\n `аккаунт «${name}» не заведён. ` +\n (known.length > 0\n ? `Известны: ${known.join(', ')}`\n : 'Ни одного аккаунта нет — добавьте его через addAccount() или restore()'),\n );\n }\n\n return client;\n }\n\n /**\n * Поднимает аккаунты, сессии которых уже лежат в хранилище.\n *\n * То, ради чего мультихранилище знает свой состав: после перезапуска процесса\n * ни `auth`, ни капча не нужны — токен, `deviceId` и cookie берутся из сохранённого.\n * Уже заведённые аккаунты не трогаются. Записи, в которых после выхода остался только\n * `deviceId`, пропускаются: авторизованной сессии в них уже нет.\n *\n * @returns имена добавленных аккаунтов\n *\n * @example\n * ```ts\n * const restored = await accounts.restore();\n * console.log(`подняли ${restored.length} аккаунтов без единого входа`);\n * ```\n */\n async restore(): Promise<string[]> {\n const saved = await this.#storage.accounts();\n for (const name of saved) validateAccountName(name);\n\n const candidates = [...new Set(saved)].filter((name) => !this.#clients.has(name));\n const sessions = await Promise.all(\n candidates.map(async (name) => ({ name, session: await this.#storage.get(name) })),\n );\n const added: string[] = [];\n\n for (const { name, session } of sessions) {\n if (!isRestorableSession(session)) continue;\n // Пока читали хранилище, аккаунт могли добавить вручную или начать удалять.\n if (this.#clients.has(name) || this.#removing.has(name)) continue;\n this.addAccount(name);\n added.push(name);\n }\n\n return added;\n }\n\n /**\n * Убирает аккаунт: закрывает его клиента и, если попросить, забывает сессию.\n *\n * Сетевого запроса не выполняет. Чтобы завершить сессию на сервере, вызовите\n * `itd.auth.logout()` до удаления.\n *\n * @returns `false`, если такого аккаунта и не было\n */\n async removeAccount(name: string, options: RemoveAccountOptions = {}): Promise<boolean> {\n const client = this.#clients.get(name);\n if (!client) return false;\n\n this.#removing.add(name);\n this.#clients.delete(name);\n const storageControl = this.#storageControls.get(name);\n storageControl?.revoke();\n this.#storageControls.delete(name);\n\n for (const unsubscribe of this.#eventUnsubscribers.get(name) ?? []) unsubscribe();\n this.#eventUnsubscribers.delete(name);\n\n try {\n const errors: unknown[] = [];\n const closing = await Promise.allSettled([\n client.dispose(),\n storageControl?.drain() ?? Promise.resolve(),\n ]);\n for (const result of closing) {\n if (result.status === 'rejected') errors.push(result.reason);\n }\n\n if (options.forget) {\n await Promise.resolve(this.#storage.clear(name)).catch((error: unknown) => {\n errors.push(error);\n });\n }\n\n if (errors.length > 0) throw errors[0];\n\n return true;\n } finally {\n this.#removing.delete(name);\n }\n }\n\n /**\n * Подключает плагин всем аккаунтам — и заведённым, и будущим.\n *\n * @throws {ItdConfigError} если плагин задан неверно или уже подключён\n *\n * @example\n * ```ts\n * accounts.use(crypt());\n * ```\n */\n use(plugin: ItdPlugin): this {\n if (this.#removingPlugins.has(plugin?.name)) {\n throw new ItdConfigError(\n `плагин «${plugin.name}» ещё отключается; дождитесь завершения accounts.unuse()`,\n );\n }\n const ordered = orderPluginDefinitions([...this.#plugins, plugin]);\n const clients = [...this.#clients.values()];\n for (const client of clients) assertClientCanUsePlugin(client, plugin);\n\n const installed: ItdClient[] = [];\n try {\n for (const client of clients) {\n client.use(plugin);\n installed.push(client);\n }\n } catch (error) {\n // remove() исключает запись из реестра до первого await, поэтому видимое состояние\n // откатывается синхронно, а асинхронные teardown завершаются в фоне.\n for (const client of installed.reverse()) {\n void client.unuse(plugin.name).catch((cleanupError: unknown) => {\n this.#reportPluginCleanup(`отката плагина «${plugin.name}»`, cleanupError);\n });\n }\n throw error;\n }\n this.#plugins.splice(0, this.#plugins.length, ...ordered);\n\n return this;\n }\n\n /** Имена общих плагинов в фактическом порядке выполнения обёрток. */\n pluginNames(): string[] {\n return this.#plugins.map((plugin) => plugin.name);\n }\n\n /** Подключён ли общий плагин с таким именем. */\n hasPlugin(name: string): boolean {\n return this.#plugins.some((plugin) => plugin.name === name);\n }\n\n /**\n * Отключает общий плагин у существующих клиентов и не применяет его к будущим.\n *\n * @returns `false`, если такого плагина не было\n * @throws {ItdConfigError} если от плагина зависит другой общий плагин\n */\n async unuse(name: string): Promise<boolean> {\n const index = this.#plugins.findIndex((plugin) => plugin.name === name);\n if (index < 0) return false;\n assertPluginRemovable(this.#plugins, name);\n const clients = [...this.#clients.values()];\n for (const client of clients) assertClientCanUnusePlugin(client, name);\n this.#plugins.splice(index, 1);\n this.#removingPlugins.add(name);\n\n try {\n const results = await Promise.allSettled(clients.map((client) => client.unuse(name)));\n const errors = results\n .filter((result): result is PromiseRejectedResult => result.status === 'rejected')\n .map((result) => result.reason);\n if (errors.length > 0) {\n throw new AggregateError(errors, `Не удалось отключить плагин «${name}» у всех аккаунтов`);\n }\n return true;\n } finally {\n this.#removingPlugins.delete(name);\n }\n }\n\n /**\n * Подписывается на события авторизации всех аккаунтов сразу.\n *\n * @returns функция отписки\n *\n * @example\n * ```ts\n * accounts.on('authError', ({ account }) => console.warn(`${account}: сессия потеряна`));\n * ```\n */\n on<K extends keyof AccountEvents>(event: K, listener: Listener<AccountEvents[K]>): Unsubscribe {\n return this.#emitter.on(event, listener);\n }\n\n /**\n * Перебор аккаунтов парами «имя — клиент».\n *\n * @example\n * ```ts\n * for (const [name, itd] of accounts) {\n * const me = await itd.users.me();\n * console.log(name, me.nickname);\n * }\n * ```\n */\n [Symbol.iterator](): IterableIterator<[string, ItdClient]> {\n return this.#clients.entries();\n }\n\n /**\n * Закрывает все аккаунты и останавливает общую очередь.\n *\n * Аккаунты остаются в контейнере и работоспособны: новые запросы поднимут всё заново,\n * но уже созданные потоки уведомлений останутся закрытыми.\n *\n * @example\n * ```ts\n * await using accounts = new ItdAccounts({ storage });\n * // …работа…\n * // dispose() вызовется сам на выходе из блока\n * ```\n */\n async close(): Promise<void> {\n await Promise.all([...this.#clients.values()].map((client) => client.close()));\n this.#queues?.stop();\n }\n\n /**\n * Окончательно освобождает контейнер и отключает общие плагины у всех аккаунтов.\n *\n * Для временной остановки потоков и очереди без отключения плагинов используйте\n * {@link close}.\n */\n async dispose(): Promise<void> {\n const results = await Promise.allSettled([\n ...[...this.#clients.values()].map((client) => client.dispose()),\n Promise.resolve().then(() => this.#queues?.stop()),\n ]);\n this.#plugins.splice(0);\n this.#removingPlugins.clear();\n\n const errors = results\n .filter((result): result is PromiseRejectedResult => result.status === 'rejected')\n .map((result) => result.reason);\n if (errors.length > 0) throw new AggregateError(errors, 'Не удалось освободить аккаунты');\n }\n\n /** Позволяет использовать контейнер с `await using`. */\n [Symbol.asyncDispose](): Promise<void> {\n return this.dispose();\n }\n\n // Fallback для `await using` в Node 18, где `Symbol.asyncDispose` отсутствует, —\n // тот же приём, что в ItdClient.\n static {\n if (\n typeof (Symbol as SymbolConstructor & { asyncDispose?: symbol }).asyncDispose !== 'symbol'\n ) {\n const prototype = ItdAccounts.prototype as unknown as Record<PropertyKey, unknown>;\n prototype[Symbol.for('Symbol.asyncDispose')] = prototype.undefined;\n delete prototype.undefined;\n }\n }\n\n /**\n * Собирает опции клиента: общие, поверх них — аккаунтные, и обязательно свой срез\n * хранилища.\n *\n * `headers` и `services` сливаются по ключам, а не заменяются целиком: иначе один свой\n * заголовок у аккаунта стирал бы весь общий набор.\n */\n #mergeOptions(options: AddAccountOptions, storage: TokenStorage): ItdClientOptions {\n const base = this.#base;\n\n return {\n ...base,\n ...options,\n ...(base.headers || options.headers\n ? { headers: { ...base.headers, ...options.headers } }\n : {}),\n ...(base.services || options.services\n ? { services: { ...base.services, ...options.services } }\n : {}),\n storage,\n };\n }\n\n /** Не теряет ошибку фонового teardown, который синхронный API не может await-нуть. */\n #reportPluginCleanup(scope: string, error: unknown): void {\n const message = `Не удалось завершить teardown после ${scope}`;\n if (this.#logger) this.#logger.error(message, error);\n else console.error(`[itd-api] ${message}`, error);\n }\n\n /** Ретранслирует события клиента наружу, добавляя к ним имя аккаунта. */\n #forwardEvents(account: string, client: ItdClient): Unsubscribe[] {\n return [\n client.on('tokens', ({ accessToken }) =>\n this.#emitter.emit('tokens', { account, accessToken }),\n ),\n client.on('signIn', ({ accessToken }) =>\n this.#emitter.emit('signIn', { account, accessToken }),\n ),\n client.on('signOut', () => this.#emitter.emit('signOut', { account })),\n client.on('authError', ({ error }) => this.#emitter.emit('authError', { account, error })),\n ];\n }\n}\n\n/**\n * Создаёт контейнер аккаунтов.\n *\n * То же, что `new ItdAccounts(options)`, — для тех, кому привычнее фабрика.\n */\nexport function createAccounts(options: ItdAccountsOptions = {}): ItdAccounts {\n return new ItdAccounts(options);\n}\n","import { ItdConfigError, ItdFileError, ItdFileErrorReason } from '../errors.js';\nimport { boundedFileStream, isReadableByteStream } from './bounded-stream.js';\nimport {\n DEFAULT_URL_FILE_MAX_BYTES,\n type FileContext,\n type FileStreamContent,\n FileTransferMode,\n type FromStreamOptions,\n type LazyFile,\n type StreamFile,\n type UrlFileOptions,\n} from './contracts.js';\nimport { fileTooLarge, optionalBytes } from './limits.js';\nimport { resolveFileStreamOptions } from './options.js';\nimport { downloadFile, normalizeMimeType, openUrlFile } from './url-source.js';\n\n/** Создаёт URL-источник в выбранном режиме. */\nexport function fromUrl(\n url: string,\n options: UrlFileOptions & { mode: typeof FileTransferMode.Stream },\n): StreamFile;\nexport function fromUrl(\n url: string,\n options?: UrlFileOptions & { mode?: typeof FileTransferMode.Buffer },\n): LazyFile;\nexport function fromUrl(url: string, options: UrlFileOptions): LazyFile | StreamFile;\nexport function fromUrl(url: string, options: UrlFileOptions = {}): LazyFile | StreamFile {\n const resolved = resolveFileStreamOptions(options, DEFAULT_URL_FILE_MAX_BYTES);\n if (resolved.mode === FileTransferMode.Stream) {\n return { open: (context) => openUrlFile(url, options, context) };\n }\n return { load: (context) => downloadFile(url, options, context) };\n}\n\n/**\n * Создаёт повторяемый пользовательский поток.\n *\n * Фабрика вызывается заново для каждой попытки; возвращать один и тот же поток нельзя.\n */\nexport function fromStream(\n factory: (\n context: FileContext,\n ) =>\n | ReadableStream<Uint8Array>\n | FileStreamContent\n | Promise<ReadableStream<Uint8Array> | FileStreamContent>,\n options: FromStreamOptions = {},\n): StreamFile {\n const resolved = resolveFileStreamOptions(\n { ...options, mode: FileTransferMode.Stream },\n undefined,\n );\n optionalBytes(options.size, 'size');\n\n return {\n open: async (context) => {\n let opened: ReadableStream<Uint8Array> | FileStreamContent;\n try {\n opened = await factory(context);\n } catch (error) {\n if (\n error instanceof ItdFileError ||\n error instanceof ItdConfigError ||\n context.signal?.aborted\n ) {\n throw error;\n }\n throw new ItdFileError('не удалось открыть поток вложения', {\n reason: ItdFileErrorReason.Read,\n retryable: true,\n cause: error,\n });\n }\n const content = isReadableByteStream(opened) ? { stream: opened } : opened;\n if (!content || !isReadableByteStream(content.stream)) {\n throw new ItdConfigError('fromStream должен вернуть ReadableStream или { stream }');\n }\n\n const size = options.size ?? content.size;\n optionalBytes(size, 'size');\n if (resolved.maxBytes !== undefined && size !== undefined && size > resolved.maxBytes) {\n await content.close?.();\n throw fileTooLarge(undefined, resolved.maxBytes, size);\n }\n\n const contentType = normalizeMimeType(options.contentType ?? content.contentType);\n return {\n stream: boundedFileStream(content.stream, {\n ...(resolved.maxBytes !== undefined ? { maxBytes: resolved.maxBytes } : {}),\n streamBufferBytes: resolved.streamBufferBytes,\n ...(context.signal ? { signal: context.signal } : {}),\n retryableRead: true,\n }),\n ...((options.filename ?? content.filename)\n ? { filename: options.filename ?? content.filename }\n : {}),\n ...(contentType ? { contentType } : {}),\n ...(size !== undefined ? { size } : {}),\n ...(content.close ? { close: content.close } : {}),\n };\n },\n };\n}\n","import type { MyProfile, Profile } from './users.js';\n\n/**\n * Свой ли это профиль.\n *\n * @example\n * ```ts\n * if (isMyProfile(profile)) console.log(profile.subscription.isActive);\n * ```\n */\nexport function isMyProfile(profile: Profile): profile is MyProfile {\n return 'subscription' in profile;\n}\n","import type { ServiceStatus, StatusDay } from './status.js';\n\nconst STATUS_WINDOW_DAYS = 90;\n\n/**\n * Разворачивает историю сервиса в массив на 90 суток.\n * Сутки без данных становятся `null`.\n *\n * @returns массив, где индекс — сколько суток назад: `[0]` — сегодня\n *\n * @example\n * ```ts\n * const status = await itd.platform.status();\n * const days = statusDays(status.services[0]);\n *\n * days[0]?.uptime; // доступность за сегодня\n * days.filter((day) => day === null).length; // за сколько суток данных нет\n * ```\n */\nexport function statusDays(service: ServiceStatus): (StatusDay | null)[] {\n return Array.from(\n { length: STATUS_WINDOW_DAYS },\n (_, index) => service.days[String(index)] ?? null,\n );\n}\n","import type { Notification } from '../models/notifications.js';\nimport { NotificationType } from '../types/enums.js';\n\n/** Имя, которое подставляется, если участник неизвестен. */\nconst UNKNOWN_ACTOR = 'Пользователь';\n\n/** Текст, который подставляется для неизвестного типа уведомления. */\nconst FALLBACK_TEXT = 'Новое уведомление';\n\n/**\n * Шаблоны текстов уведомлений.\n *\n * Для каждого типа задано две формы: для одного участника и для схлопнутого уведомления,\n * где действие совершили несколько человек.\n */\nconst TEMPLATES: Readonly<\n Record<string, { one: (name: string) => string; many: (name: string, others: number) => string }>\n> = Object.freeze({\n [NotificationType.Follow]: {\n one: (name) => `${name} подписался(-ась) на вас`,\n many: (name, others) => `${name} и ещё ${others} подписались на вас`,\n },\n [NotificationType.FollowRequest]: {\n one: (name) => `${name} хочет подписаться на вас`,\n many: (name, others) => `${name} и ещё ${others} хотят подписаться на вас`,\n },\n [NotificationType.FollowAccepted]: {\n one: (name) => `${name} принял(а) вашу заявку`,\n many: (name, others) => `${name} и ещё ${others} приняли вашу заявку`,\n },\n [NotificationType.PostReaction]: {\n one: (name) => `${name} оценил(а) ваш пост`,\n many: (name, others) => `${name} и ещё ${others} оценили ваш пост`,\n },\n [NotificationType.PostComment]: {\n one: (name) => `${name} прокомментировал(а) ваш пост`,\n many: (name, others) => `${name} и ещё ${others} прокомментировали ваш пост`,\n },\n [NotificationType.PostRepost]: {\n one: (name) => `${name} сделал(а) репост`,\n many: (name, others) => `${name} и ещё ${others} сделали репост`,\n },\n [NotificationType.CommentReaction]: {\n one: (name) => `${name} оценил(а) ваш комментарий`,\n many: (name, others) => `${name} и ещё ${others} оценили ваш комментарий`,\n },\n [NotificationType.CommentReply]: {\n one: (name) => `${name} ответил(а) на ваш комментарий`,\n many: (name, others) => `${name} и ещё ${others} ответили на ваш комментарий`,\n },\n [NotificationType.PostMention]: {\n one: (name) => `${name} упомянул(а) вас в посте`,\n many: (name, others) => `${name} и ещё ${others} упомянули вас в посте`,\n },\n [NotificationType.CommentMention]: {\n one: (name) => `${name} упомянул(а) вас в комментарии`,\n many: (name, others) => `${name} и ещё ${others} упомянули вас в комментарии`,\n },\n [NotificationType.WallPost]: {\n one: (name) => `${name} написал(а) на вашей стене`,\n many: (name, others) => `${name} и ещё ${others} написали на вашей стене`,\n },\n [NotificationType.VerificationApproved]: {\n one: () => 'Ваша заявка на верификацию одобрена',\n many: () => 'Ваша заявка на верификацию одобрена',\n },\n [NotificationType.VerificationRejected]: {\n one: () => 'Ваша заявка на верификацию отклонена',\n many: () => 'Ваша заявка на верификацию отклонена',\n },\n});\n\n/**\n * Собирает текст уведомления на русском.\n *\n * Повторяет формулировки сайта итд.com. Для неизвестного типа возвращает\n * «Новое уведомление» — библиотека не выдумывает текст, которого нет.\n *\n * @example\n * ```ts\n * formatNotificationText(notification);\n * // 'Аня и ещё 2 оценили ваш пост'\n * ```\n */\nexport function formatNotificationText(notification: Notification): string {\n const template = TEMPLATES[notification.type];\n if (!template) return FALLBACK_TEXT;\n\n const first = notification.actors[0];\n const name = first?.displayName || first?.username || UNKNOWN_ACTOR;\n\n const others = notification.count - 1;\n return others > 0 ? template.many(name, others) : template.one(name);\n}\n","import type { Notification } from '../models/notifications.js';\nimport { NotificationType } from '../types/enums.js';\n\n/** Типы, ведущие на пост. */\nconst POST_TYPES = new Set<string>([\n NotificationType.PostReaction,\n NotificationType.PostRepost,\n NotificationType.PostMention,\n NotificationType.WallPost,\n]);\n\n/** Типы, ведущие на комментарий внутри поста. */\nconst COMMENT_TYPES = new Set<string>([\n NotificationType.PostComment,\n NotificationType.CommentReaction,\n NotificationType.CommentReply,\n NotificationType.CommentMention,\n]);\n\n/** Типы, ведущие на профиль. */\nconst FOLLOW_TYPES = new Set<string>([\n NotificationType.Follow,\n NotificationType.FollowRequest,\n NotificationType.FollowAccepted,\n]);\n\n/**\n * Вычисляет адрес, на который ведёт уведомление.\n *\n * Возвращает путь внутри сайта — без домена, чтобы его можно было передать роутеру\n * приложения. Поле `clickUrl` от сервера используется только как запасной вариант:\n * вычисленный путь точнее, поскольку учитывает родительский пост у комментариев.\n *\n * @example\n * ```ts\n * const url = resolveNotificationUrl(notification);\n * // '/@nowkie/post/9f1c…?comment=2b7e…'\n * ```\n */\nexport function resolveNotificationUrl(notification: Notification): string {\n const { type, entityId, parentEntityId, clickUrl } = notification;\n const username = notification.actors[0]?.username;\n\n if (username && entityId) {\n if (POST_TYPES.has(type)) return `/@${username}/post/${entityId}`;\n\n if (COMMENT_TYPES.has(type)) {\n return parentEntityId\n ? `/@${username}/post/${parentEntityId}?comment=${entityId}`\n : `/@${username}/post/${entityId}`;\n }\n }\n\n if (username && FOLLOW_TYPES.has(type)) return `/@${username}`;\n\n return clickUrl || '/notifications';\n}\n","import type { Unsubscribe } from '../core/emitter.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport {\n captureRealtimeMiddleware,\n type RealtimeMiddleware,\n runRealtimeMiddleware,\n withRealtimeMiddlewareSnapshot,\n} from './middleware.js';\nimport type { RealtimeContext } from './updates.js';\n\n/** Выбирает маршрут обновления. `undefined` и `null` означают отсутствие маршрута. */\nexport type RealtimeRouteSelector<\n K extends PropertyKey,\n C extends RealtimeContext = RealtimeContext,\n> = (context: C) => K | null | undefined | Promise<K | null | undefined>;\n\ninterface RouteRegistration<C extends RealtimeContext> {\n readonly middleware: readonly RealtimeMiddleware<C>[];\n}\n\n/**\n * Направляет обновления потока в именованные цепочки промежуточных обработчиков.\n *\n * @example\n * ```ts\n * import { RealtimeRouter, RealtimeUpdateType } from 'itd-api';\n *\n * const router = new RealtimeRouter((context) => context.update.type);\n * router.route(RealtimeUpdateType.Notification, async (context, next) => {\n * if (context.update.type === RealtimeUpdateType.Notification) {\n * await handleNotification(context.update.data.notification);\n * }\n * await next();\n * });\n * stream.use(router.middleware());\n * ```\n */\nexport class RealtimeRouter<\n K extends PropertyKey = PropertyKey,\n C extends RealtimeContext = RealtimeContext,\n> {\n readonly #selector: RealtimeRouteSelector<K, C>;\n readonly #routes = new Map<K, RouteRegistration<C>[]>();\n readonly #fallback: RouteRegistration<C>[] = [];\n\n constructor(selector: RealtimeRouteSelector<K, C>) {\n if (typeof selector !== 'function') {\n throw new ItdConfigError('RealtimeRouter принимает функцию выбора маршрута');\n }\n this.#selector = selector;\n }\n\n /** Добавляет промежуточные обработчики к маршруту и возвращает функцию их удаления. */\n route(key: K, ...middleware: readonly RealtimeMiddleware<C>[]): Unsubscribe {\n if (!isPropertyKey(key))\n throw new ItdConfigError('Ключ realtime route должен быть PropertyKey');\n const registration = this.#registration(middleware);\n const registrations = this.#routes.get(key) ?? [];\n registrations.push(registration);\n this.#routes.set(key, registrations);\n\n return () => {\n const current = this.#routes.get(key);\n if (!current) return;\n const index = current.indexOf(registration);\n if (index >= 0) current.splice(index, 1);\n if (current.length === 0) this.#routes.delete(key);\n };\n }\n\n /** Добавляет промежуточные обработчики для обновлений без зарегистрированного маршрута. */\n otherwise(...middleware: readonly RealtimeMiddleware<C>[]): Unsubscribe {\n const registration = this.#registration(middleware);\n this.#fallback.push(registration);\n\n return () => {\n const index = this.#fallback.indexOf(registration);\n if (index >= 0) this.#fallback.splice(index, 1);\n };\n }\n\n /** Возвращает промежуточный обработчик для `stream.use()`. */\n middleware(): RealtimeMiddleware<C> {\n const middleware: RealtimeMiddleware<C> = (context, next) =>\n this.#captureMiddleware()(context, next);\n return withRealtimeMiddlewareSnapshot(middleware, () => this.#captureMiddleware());\n }\n\n #captureMiddleware(): RealtimeMiddleware<C> {\n const routes = new Map<K, readonly RealtimeMiddleware<C>[]>();\n for (const [key, registrations] of this.#routes) {\n routes.set(\n key,\n registrations.flatMap(({ middleware }) => middleware).map(captureRealtimeMiddleware),\n );\n }\n const fallback = this.#fallback\n .flatMap(({ middleware }) => middleware)\n .map(captureRealtimeMiddleware);\n\n return async (context, next) => {\n const key = await this.#selector(context);\n if (key != null && !isPropertyKey(key)) {\n throw new ItdConfigError(\n 'Функция выбора маршрута должна возвращать PropertyKey, null или undefined',\n );\n }\n const route = key == null ? undefined : routes.get(key);\n const chain = route && route.length > 0 ? route : fallback;\n\n if (chain.length === 0) {\n await next();\n return;\n }\n\n await runRealtimeMiddleware(chain, context, next);\n };\n }\n\n #registration(middleware: readonly RealtimeMiddleware<C>[]): RouteRegistration<C> {\n if (middleware.length === 0) {\n throw new ItdConfigError('Маршрут должен содержать хотя бы один обработчик');\n }\n for (const item of middleware) {\n if (typeof item !== 'function') {\n throw new ItdConfigError('Маршрут принимает только функции обработки');\n }\n }\n return { middleware: [...middleware] };\n }\n}\n\nfunction isPropertyKey(value: unknown): value is PropertyKey {\n return typeof value === 'string' || typeof value === 'number' || typeof value === 'symbol';\n}\n","import type { Span } from '../models/common.js';\nimport { SpanType } from '../types/enums.js';\n\n/** Формат результата {@link renderSpans}. */\nexport const SpanRenderFormat = Object.freeze({\n Html: 'html',\n Markdown: 'markdown',\n Ansi: 'ansi',\n} as const);\nexport type SpanRenderFormat = (typeof SpanRenderFormat)[keyof typeof SpanRenderFormat];\n\nexport interface RenderSpansOptions {\n /** Формат результата. По умолчанию `html`. */\n format?: SpanRenderFormat;\n /** Строит адрес упоминания. `null` или `undefined` отключает ссылку. */\n mentionUrl?: (username: string) => string | null | undefined;\n /** Строит адрес хэштега. `null` или `undefined` отключает ссылку. */\n hashtagUrl?: (tag: string) => string | null | undefined;\n /**\n * Префикс HTML-классов. По умолчанию `itd`; пустая строка или `null` отключает классы.\n *\n * Например, `app` создаёт `app-mention`, `app-hashtag`, `app-quote` и `app-spoiler`.\n */\n classPrefix?: string | null;\n}\n\ninterface NormalizedSpan {\n span: Span;\n start: number;\n end: number;\n}\n\nfunction normalizedSpans(\n content: string,\n spans: readonly Span[] | null | undefined,\n): NormalizedSpan[] {\n const result: NormalizedSpan[] = [];\n for (const span of spans ?? []) {\n if (\n !span ||\n !Number.isFinite(span.offset) ||\n !Number.isFinite(span.length) ||\n span.length <= 0\n ) {\n continue;\n }\n\n const start = Math.max(0, Math.trunc(span.offset));\n const end = Math.min(content.length, Math.trunc(span.offset + span.length));\n if (start < end && start < content.length) result.push({ span, start, end });\n }\n return result;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\nfunction safeHttpUrl(value: string | undefined): string | null {\n if (!value) return null;\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:' ? url.href : null;\n } catch {\n return null;\n }\n}\n\nfunction safeNavigationUrl(value: string | null | undefined): string | null {\n if (!value) return null;\n try {\n const url = new URL(value, 'https://itd.invalid');\n return url.protocol === 'http:' || url.protocol === 'https:' ? value : null;\n } catch {\n return null;\n }\n}\n\nfunction entityValue(span: Span, content: string, kind: 'mention' | 'hashtag'): string {\n const offset = Number.isFinite(span.offset) ? Math.max(0, Math.trunc(span.offset)) : 0;\n const end = Number.isFinite(span.length)\n ? Math.min(content.length, offset + Math.max(0, Math.trunc(span.length)))\n : offset;\n const text = content.slice(offset, end);\n if (kind === 'mention') {\n return span.username ?? span.tag ?? text.replace(/^@/u, '');\n }\n return span.tag ?? text.replace(/^#/u, '');\n}\n\nfunction mentionUrl(username: string, options: RenderSpansOptions): string | null | undefined {\n return options.mentionUrl ? options.mentionUrl(username) : `/@${encodeURIComponent(username)}`;\n}\n\nfunction hashtagUrl(tag: string, options: RenderSpansOptions): string | null | undefined {\n return options.hashtagUrl ? options.hashtagUrl(tag) : `/hashtag/${encodeURIComponent(tag)}`;\n}\n\nfunction classAttribute(name: string, options: RenderSpansOptions): string {\n const prefix = options.classPrefix === undefined ? 'itd' : options.classPrefix;\n return prefix ? ` class=\"${escapeHtml(`${prefix}-${name}`)}\"` : '';\n}\n\nfunction has(active: readonly Span[], type: string): boolean {\n return active.some((span) => span.type === type);\n}\n\nfunction last(active: readonly Span[], type: string): Span | undefined {\n for (let index = active.length - 1; index >= 0; index -= 1) {\n const span = active[index];\n if (span?.type === type) return span;\n }\n return undefined;\n}\n\nfunction renderHtmlSegment(\n text: string,\n active: readonly Span[],\n content: string,\n options: RenderSpansOptions,\n): string {\n let rendered = escapeHtml(text).replace(/\\n/g, '<br>');\n\n if (has(active, SpanType.Bold)) rendered = `<strong>${rendered}</strong>`;\n if (has(active, SpanType.Italic)) rendered = `<em>${rendered}</em>`;\n if (has(active, SpanType.Underline)) rendered = `<u>${rendered}</u>`;\n if (has(active, SpanType.Strike)) rendered = `<s>${rendered}</s>`;\n if (has(active, SpanType.Monospace)) rendered = `<code>${rendered}</code>`;\n if (has(active, SpanType.Quote)) {\n rendered = `<span${classAttribute('quote', options)}>${rendered}</span>`;\n }\n if (has(active, SpanType.Spoiler)) {\n rendered = `<span${classAttribute('spoiler', options)} data-spoiler=\"true\">${rendered}</span>`;\n }\n\n const link = last(active, SpanType.Link);\n const url = safeHttpUrl(link?.url);\n if (url) {\n rendered = `<a href=\"${escapeHtml(url)}\" rel=\"noopener noreferrer\">${rendered}</a>`;\n }\n\n const mention = last(active, SpanType.Mention);\n if (mention) {\n const username = entityValue(mention, content, 'mention');\n const href = safeNavigationUrl(mentionUrl(username, options));\n if (href) {\n rendered = `<a href=\"${escapeHtml(href)}\"${classAttribute('mention', options)}>${rendered}</a>`;\n }\n }\n\n const hashtag = last(active, SpanType.Hashtag);\n if (hashtag) {\n const tag = entityValue(hashtag, content, 'hashtag');\n const href = safeNavigationUrl(hashtagUrl(tag, options));\n if (href) {\n rendered = `<a href=\"${escapeHtml(href)}\"${classAttribute('hashtag', options)}>${rendered}</a>`;\n }\n }\n\n return rendered;\n}\n\nfunction escapeMarkdown(value: string, startsAtLineStart: boolean): string {\n const lines = value.replace(/([\\\\`*_[\\]<>~|&])/g, '\\\\$1').split('\\n');\n return lines\n .map((line, index) => {\n if (index === 0 && !startsAtLineStart) return line;\n return line\n .replace(/^ {0,3}(?=#{1,6}(?:[ \\t]|$))/u, '$&\\\\')\n .replace(/^ {0,3}(?=[+-][ \\t])/u, '$&\\\\')\n .replace(/^(\\s{0,3}\\d{1,9})([.)])(?=[ \\t])/u, '$1\\\\$2')\n .replace(/^(\\s{0,3})(?=-(?:[ \\t]*-){2,}[ \\t]*$)/u, '$1\\\\');\n })\n .join('\\n');\n}\n\nfunction markdownCodeSpan(text: string): string {\n const normalized = text.replace(/\\r\\n?|\\n/g, ' ');\n const longestRun = Math.max(\n 0,\n ...Array.from(normalized.matchAll(/`+/g), (match) => match[0].length),\n );\n const fence = '`'.repeat(longestRun + 1);\n const allSpaces = /^ +$/u.test(normalized);\n const needsPadding =\n normalized.startsWith('`') ||\n normalized.endsWith('`') ||\n (!allSpaces && normalized.startsWith(' ') && normalized.endsWith(' '));\n const body = needsPadding ? ` ${normalized} ` : normalized;\n return `${fence}${body}${fence}`;\n}\n\ninterface RenderSegment {\n start: number;\n text: string;\n active: Span[];\n}\n\nfunction markdownUrl(value: string): string {\n return value.replace(/([\\\\)])/g, '\\\\$1');\n}\n\nfunction renderMarkdownSegment(\n segment: RenderSegment,\n content: string,\n options: RenderSpansOptions,\n): string {\n const { text, active } = segment;\n const startsAtLineStart = segment.start === 0 || content[segment.start - 1] === '\\n';\n let rendered = has(active, SpanType.Monospace)\n ? markdownCodeSpan(text)\n : escapeMarkdown(text, startsAtLineStart);\n if (has(active, SpanType.Bold)) rendered = `**${rendered}**`;\n if (has(active, SpanType.Italic)) rendered = `*${rendered}*`;\n if (has(active, SpanType.Underline)) rendered = `<u>${rendered}</u>`;\n if (has(active, SpanType.Strike)) rendered = `~~${rendered}~~`;\n if (has(active, SpanType.Spoiler)) rendered = `||${rendered}||`;\n\n const link = last(active, SpanType.Link);\n const url = safeHttpUrl(link?.url);\n if (url) rendered = `[${rendered}](${markdownUrl(url)})`;\n\n const mention = last(active, SpanType.Mention);\n if (mention) {\n const username = entityValue(mention, content, 'mention');\n const href = safeNavigationUrl(mentionUrl(username, options));\n if (href) rendered = `[${rendered}](${markdownUrl(href)})`;\n }\n\n const hashtag = last(active, SpanType.Hashtag);\n if (hashtag) {\n const tag = entityValue(hashtag, content, 'hashtag');\n const href = safeNavigationUrl(hashtagUrl(tag, options));\n if (href) rendered = `[${rendered}](${markdownUrl(href)})`;\n }\n\n return rendered;\n}\n\nfunction renderMarkdown(\n segments: readonly RenderSegment[],\n content: string,\n options: RenderSpansOptions,\n): string {\n const groups: { quoted: boolean; text: string }[] = [];\n for (const segment of segments) {\n const quoted = has(segment.active, SpanType.Quote);\n const rendered = renderMarkdownSegment(segment, content, options);\n const previous = groups[groups.length - 1];\n if (previous?.quoted === quoted) previous.text += rendered;\n else groups.push({ quoted, text: rendered });\n }\n\n let result = '';\n for (const [index, group] of groups.entries()) {\n const rendered = group.quoted\n ? group.text\n .split('\\n')\n .map((line) => `> ${line}`)\n .join('\\n')\n : group.text;\n const previous = groups[index - 1];\n if (\n result !== '' &&\n (group.quoted || previous?.quoted) &&\n !result.endsWith('\\n') &&\n !rendered.startsWith('\\n')\n ) {\n result += '\\n';\n }\n result += rendered;\n }\n return result;\n}\n\nfunction renderAnsiSegment(text: string, active: readonly Span[]): string {\n const codes: number[] = [];\n if (has(active, SpanType.Bold)) codes.push(1);\n if (has(active, SpanType.Italic)) codes.push(3);\n if (has(active, SpanType.Underline) || has(active, SpanType.Link)) codes.push(4);\n if (has(active, SpanType.Strike)) codes.push(9);\n if (has(active, SpanType.Spoiler)) codes.push(8);\n if (has(active, SpanType.Quote)) codes.push(2);\n if (has(active, SpanType.Mention) || has(active, SpanType.Hashtag)) codes.push(36);\n\n return codes.length === 0 ? text : `\\u001b[${codes.join(';')}m${text}\\u001b[0m`;\n}\n\n/**\n * Преобразует текст и wire-разметку API в безопасный HTML, Markdown или ANSI.\n *\n * Некорректные серверные spans игнорируются либо обрезаются по границам строки. Пересекающиеся\n * spans разбиваются на независимые сегменты, поэтому HTML остаётся корректно вложенным.\n * Отсутствующий массив считается пустым; формат по умолчанию — HTML.\n */\nexport function renderSpans(\n content: string,\n spans: readonly Span[] | null | undefined = [],\n options: RenderSpansOptions = {},\n): string {\n const normalized = normalizedSpans(content, spans);\n const boundaries = new Set<number>([0, content.length]);\n for (const item of normalized) {\n boundaries.add(item.start);\n boundaries.add(item.end);\n }\n\n const points = [...boundaries].sort((left, right) => left - right);\n const segments: RenderSegment[] = [];\n for (let index = 0; index < points.length - 1; index += 1) {\n const start = points[index];\n const end = points[index + 1];\n if (start === undefined || end === undefined || start === end) continue;\n\n const text = content.slice(start, end);\n const active = normalized\n .filter((item) => item.start <= start && item.end >= end)\n .map((item) => item.span);\n segments.push({ start, text, active });\n }\n\n const format = options.format ?? SpanRenderFormat.Html;\n if (format === SpanRenderFormat.Markdown) return renderMarkdown(segments, content, options);\n\n let result = '';\n for (const segment of segments) {\n switch (format) {\n case SpanRenderFormat.Html:\n result += renderHtmlSegment(segment.text, segment.active, content, options);\n break;\n case SpanRenderFormat.Ansi:\n result += renderAnsiSegment(segment.text, segment.active);\n break;\n }\n }\n\n return result;\n}\n"],"x_google_ignoreList":[25],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["#listeners","#onError","#config","#send","#jar","#emitter","#hooks","#invalidateInFlight","#authScope","#loadSession","#identityForToken","#readExternalToken","#session","#sessionFromConfig","#externalIdentity","#authEpoch","#externalToken","#rotateAuthScope","#hasRefreshSession","#deviceId","#deviceIdLoading","#resolveDeviceId","#saveSession","#signInWithCredentials","#refreshDeduplicated","#noRefreshSessionError","#transitionAuth","#seedRefreshCookie","#loading","#performLoad","#refreshing","#performRefresh","#reloginOrNull","#signingIn","#performSignIn","#resolveTurnstileToken","isRecord","internal","#handler","#baseUrl","scoped","#entries","#removing","#cleanups","#options","#ordered","#trackCleanup","#release","#waitForDrain","#concurrency","#minGap","#waiting","#clock","#active","#drain","#cancelTimer","#nextSlot","#options","#main","#byDestination","#services","#primaryHost","ItdApiErrorClass","#config","#deps","#buildHeaders","#prepareBody","#toTransportError","#readBodyOrFail","#options","#hooks","#middleware","#handlers","#queue","#activeKeys","#drainWaiters","#keysFor","#pump","#resolveDrain","#active","#run","#deps","#options","#dispatcher","#emitter","#clock","#maxAttempts","#reportDispatchError","#handleOverflow","#status","#wanted","#generation","#starting","#attachEnvironmentListeners","#sync","#isCurrentGeneration","#run","#cancelTimer","#detachEnvironment","#controller","#attempt","#setStatus","#scheduleReconnect","#dispatch","#isCurrentConnection","#handleEvent","#emitEngine","#handleFailure","#refreshAndReconnect","#giveUp","#reconnect","#interval","#limit","#clock","#readUpdates","#readCount","#wait","#idleTimeout","#handshakeTimeout","#clock","#lastEventId","#handshake","#read","#deps","#engine","#handleFrame","#deliver","#syncUnreadCount","#options","#maxPages","#state","#finished","#pagesLoaded","#advance","#auth","#state","#uploadFiles","#replies","#fetch","#createBodyFactory","#isStreamInput","#prepareStream","#prepareBuffer","#resolveBuffer","#nameFromMime","#posts","#list","#content","#spans","#formatted","#state","#state","#input","#uploadFiles","#feed","#wall","#liked","#comments","#collectAttachments","#state","#clock","#input","#submit","#result","#sendDwell","#sendInteraction","#telemetryOptions","#requestOptions","#maxBatchSize","#onClose","#dwell","#interactions","#state","#assertOpen","#flushPromise","#flushAll","#close","#disposeCleanup","#closePromise","#batches","#sessionId","#uploadFile","#userList","#userPage","#userPaginator","#runtime","#streams","#uploadFile","#uploadFiles","#auth","#users","#posts","#comments","#files","#notifications","#hashtags","#search","#reports","#verification","#subscription","#platform","#telemetry","#disconnectStreams","#close","#disposePromise","#dispose","#base","#storage","#clients","#removing","#accountRemovals","#storageControls","#eventUnsubscribers","#plugins","#removingPlugins","#queues","#rateLimitScope","#emitter","#logger","#mergeOptions","#reportPluginCleanup","#forwardEvents","#disposePromise","#dispose","#selector","#routes","#fallback","#registration","#captureMiddleware","#middleware","#captureMiddleware","#path","#webSocketImpl","#auth","#idleTimeout","#keepAlive","#handshakeTimeout","#classifyOpenFailure","#clock","#globalImplementation","#urls","#listen"],"sources":["../src/core/emitter.ts","../src/core/jwt.ts","../src/core/operations.ts","../src/core/runtime.ts","../src/core/auth.ts","../src/core/clock.ts","../src/core/version.ts","../src/core/config.ts","../src/core/pipeline.ts","../src/core/http.ts","../src/core/plugins/hooks.ts","../src/core/retry.ts","../src/core/middleware.ts","../src/core/plugins/attempts.ts","../src/core/plugins/order.ts","../src/core/plugins/registry.ts","../src/core/rate-limit.ts","../src/core/services.ts","../src/core/redact.ts","../src/core/unwrap.ts","../src/core/error-factory.ts","../src/core/transport.ts","../src/core/client-runtime.ts","../src/types/enums.ts","../src/realtime/middleware.ts","../src/realtime/reconnect.ts","../src/realtime/transport.ts","../src/notifications/type-map.ts","../src/notifications/normalize.ts","../src/realtime/updates.ts","../src/realtime/engine.ts","../src/realtime/poll.ts","../node_modules/eventsource-parser/dist/index.js","../src/realtime/sse.ts","../src/realtime/stream.ts","../src/core/pagination.ts","../src/resources/base.ts","../src/resources/auth.ts","../src/builders/base.ts","../src/builders/comment.ts","../src/resources/comments.ts","../src/core/attachments/url-source.ts","../src/core/mime.ts","../src/core/multipart.ts","../src/resources/files.ts","../src/resources/hashtags.ts","../src/resources/notifications.ts","../src/core/time.ts","../src/resources/platform.ts","../src/spans/validate.ts","../src/spans/parse.ts","../src/builders/markup.ts","../src/builders/poll.ts","../src/builders/post.ts","../src/resources/posts.ts","../src/builders/report.ts","../src/resources/reports.ts","../src/resources/search.ts","../src/resources/subscription.ts","../src/resources/telemetry.ts","../src/resources/users.ts","../src/resources/verification.ts","../src/client.ts","../src/accounts.ts","../src/core/attachments/factories.ts","../src/models/guards.ts","../src/models/status-helpers.ts","../src/notifications/text.ts","../src/notifications/url.ts","../src/realtime/router.ts","../src/realtime/composer.ts","../src/realtime/websocket.ts","../src/spans/render.ts"],"sourcesContent":["import type { Logger } from '../types/options.js';\n\n/** Обработчик события. */\nexport type Listener<T> = (payload: T) => void;\n\n/** Функция отписки, которую возвращает подписка на событие. */\nexport type Unsubscribe = () => void;\n\n/**\n * Минимальный типизированный источник событий.\n *\n * Своя реализация вместо `EventTarget` и `EventEmitter`: первый есть не везде и требует\n * обёрток `CustomEvent`, второй существует только в Node. Нужны ровно подписка и рассылка.\n *\n * Исключение в обработчике не прерывает рассылку остальным и не роняет библиотеку.\n *\n * @typeParam Events карта «имя события → тип полезной нагрузки». Задаётся интерфейсом,\n * поэтому ограничение на индексную сигнатуру намеренно не накладывается.\n */\nexport class Emitter<Events> {\n readonly #listeners = new Map<keyof Events, Set<Listener<never>>>();\n readonly #onError: ((error: unknown) => void) | undefined;\n\n constructor(onListenerError?: (error: unknown) => void) {\n this.#onError = onListenerError;\n }\n\n /**\n * Подписывается на событие.\n *\n * @returns функция отписки\n *\n * @example\n * ```ts\n * const off = realtime.on('notification', (event) => console.log(event));\n * off();\n * ```\n */\n on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): Unsubscribe {\n const set = this.#listeners.get(event) ?? new Set();\n set.add(listener as Listener<never>);\n this.#listeners.set(event, set);\n\n return () => this.off(event, listener);\n }\n\n /** Подписывается на одно срабатывание. */\n once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): Unsubscribe {\n const off = this.on(event, (payload) => {\n off();\n listener(payload);\n });\n return off;\n }\n\n /** Отписывается от события. */\n off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void {\n this.#listeners.get(event)?.delete(listener as Listener<never>);\n }\n\n /** Рассылает событие подписчикам. */\n emit<K extends keyof Events>(event: K, payload: Events[K]): void {\n const set = this.#listeners.get(event);\n if (!set) return;\n\n // Копия нужна: обработчик вправе отписаться прямо во время рассылки.\n for (const listener of [...set]) {\n try {\n (listener as Listener<Events[K]>)(payload);\n } catch (error) {\n this.#onError?.(error);\n }\n }\n }\n\n /** Сколько подписчиков у события. */\n listenerCount(event: keyof Events): number {\n return this.#listeners.get(event)?.size ?? 0;\n }\n\n /** Снимает все подписки. */\n removeAllListeners(): void {\n this.#listeners.clear();\n }\n}\n\n/**\n * Сообщает об исключении из пользовательского обработчика события: пишет в логгер,\n * при его отсутствии — в консоль.\n *\n * Ошибка чужого обработчика не должна ронять библиотеку, но и пропадать молча ей нельзя:\n * иначе подписка, падающая на каждом событии, выглядит как её отсутствие.\n *\n * @param scope о чьих событиях речь — попадает в текст сообщения\n *\n * @internal\n */\nexport function reportListenerError(\n logger: Logger | undefined,\n scope: string,\n error: unknown,\n): void {\n const message = `Ошибка в обработчике события ${scope}`;\n if (logger) logger.error(message, error);\n else console.error(`[itd-api] ${message}`, error);\n}\n","/**\n * Разбирает строку base64url.\n *\n * `atob` понимает только обычный base64, поэтому алфавит приводится к нему, а недостающее\n * выравнивание добивается. Результат декодируется как UTF-8: в полезной нагрузке токена\n * может оказаться кириллица, и «бинарная» строка из `atob` сломала бы `JSON.parse`.\n */\nfunction decodeBase64Url(segment: string): string {\n const base64 = segment.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), '=');\n\n const binary = atob(padded);\n const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));\n\n return new TextDecoder().decode(bytes);\n}\n\n/** Идентификаторы владельца и сессии, прочитанные из JWT без проверки подписи. */\nexport interface TokenIdentity {\n subject?: string | undefined;\n sessionId?: string | undefined;\n}\n\n/**\n * Читает `sub` и `sid` из полезной нагрузки JWT.\n *\n * Подпись не проверяется — ключа для проверки у клиента нет. Прочитанные значения служат\n * лишь метками для разделения локального состояния и не дают доступа: его определяет сервер.\n */\nexport function readTokenIdentity(token: string): TokenIdentity {\n try {\n const payload = token.split('.')[1];\n if (!payload) return {};\n\n const parsed: unknown = JSON.parse(decodeBase64Url(payload));\n if (typeof parsed !== 'object' || parsed === null) return {};\n\n const { sub, sid } = parsed as { sub?: unknown; sid?: unknown };\n return {\n ...(typeof sub === 'string' && sub.length > 0 ? { subject: sub } : {}),\n ...(typeof sid === 'string' && sid.length > 0 ? { sessionId: sid } : {}),\n };\n } catch {\n // Токен другого формата — не повод падать: поля просто останутся пустыми.\n return {};\n }\n}\n\n/**\n * Читает `sub` из полезной нагрузки JWT.\n *\n * @returns идентификатор владельца токена; `undefined`, если токен не JWT, повреждён\n * или поля `sub` в нём нет\n */\nexport function readTokenSubject(token: string): string | undefined {\n return readTokenIdentity(token).subject;\n}\n","/** HTTP-метод встроенной операции. */\nexport type OperationMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\n/** Семантическая безопасность автоматического повтора операции. */\nexport const RetrySafety = Object.freeze({\n /** Автоматический повтор не создаёт неприемлемого эффекта; обычно это чтение. */\n Safe: 'safe',\n /** Повтор операции приводит к тому же состоянию, что и один вызов. */\n Idempotent: 'idempotent',\n /** Повтор может создать ещё один побочный эффект. */\n Unsafe: 'unsafe',\n} as const);\nexport type RetrySafety = (typeof RetrySafety)[keyof typeof RetrySafety];\n\n/** Минимальное стабильное описание операции, доступное core и плагинам. */\nexport interface OperationDefinition {\n readonly method: OperationMethod;\n readonly retrySafety: RetrySafety;\n}\n\nfunction freezeOperations<const T extends Record<string, OperationDefinition>>(\n operations: T,\n): Readonly<{ readonly [K in keyof T]: Readonly<T[K]> }> {\n for (const definition of Object.values(operations)) Object.freeze(definition);\n return Object.freeze(operations);\n}\n\n/**\n * Каталог встроенных операций.\n *\n * ID описывает смысл вызова и не меняется при переносе HTTP-пути. Method и retrySafety\n * хранятся здесь, чтобы resources, retry и плагины не вели независимые таблицы операций.\n */\nexport const OPERATIONS = freezeOperations({\n 'auth.check': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'auth.signUp': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'auth.signIn': { method: 'POST', retrySafety: RetrySafety.Safe },\n 'auth.verifyOtp': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'auth.resendOtp': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'auth.refresh': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'auth.logout': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'auth.forgotPassword': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'auth.resetPassword': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'auth.changePassword': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'auth.sessions': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'auth.revokeSession': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n 'auth.revokeOtherSessions': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n\n 'users.me': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.updateMe': { method: 'PUT', retrySafety: RetrySafety.Idempotent },\n 'users.deactivate': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n 'users.restore': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'users.createProfile': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'users.get': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.checkUsername': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.search': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.whoToFollow': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.topClans': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.follow': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'users.unfollow': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n 'users.followers': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.following': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.followStatus': { method: 'POST', retrySafety: RetrySafety.Safe },\n 'users.block': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'users.unblock': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n 'users.blocked': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.getPrivacy': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.updatePrivacy': { method: 'PUT', retrySafety: RetrySafety.Idempotent },\n 'users.pins': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'users.setPin': { method: 'PUT', retrySafety: RetrySafety.Idempotent },\n 'users.removePin': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n\n 'posts.list': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'posts.create': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'posts.get': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'posts.update': { method: 'PUT', retrySafety: RetrySafety.Idempotent },\n 'posts.remove': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n 'posts.restore': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'posts.like': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'posts.unlike': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n 'posts.repost': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'posts.unrepost': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n 'posts.pin': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'posts.unpin': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n 'posts.vote': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'posts.stats': { method: 'POST', retrySafety: RetrySafety.Safe },\n 'posts.byUser': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'posts.likedByUser': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'posts.comments': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'posts.comment': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n\n 'comments.replies': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'comments.reply': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'comments.update': { method: 'PATCH', retrySafety: RetrySafety.Idempotent },\n 'comments.remove': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n 'comments.restore': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'comments.like': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'comments.unlike': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n\n 'files.upload': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'files.get': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'files.remove': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n\n 'notifications.list': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'notifications.count': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'notifications.markRead': { method: 'POST', retrySafety: RetrySafety.Idempotent },\n 'notifications.markReadBatch': { method: 'POST', retrySafety: RetrySafety.Idempotent },\n 'notifications.markAllRead': { method: 'POST', retrySafety: RetrySafety.Idempotent },\n 'notifications.getSettings': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'notifications.updateSettings': { method: 'PUT', retrySafety: RetrySafety.Idempotent },\n\n 'realtime.poll.updates': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'realtime.poll.unread': { method: 'GET', retrySafety: RetrySafety.Safe },\n\n 'hashtags.search': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'hashtags.trending': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'hashtags.posts': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'search.all': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'reports.create': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n\n 'subscription.status': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'subscription.pay': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'subscription.setAutoRenewal': { method: 'POST', retrySafety: RetrySafety.Idempotent },\n 'subscription.bindCard': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'subscription.methods': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'subscription.setDefaultMethod': { method: 'POST', retrySafety: RetrySafety.Idempotent },\n 'subscription.removeMethod': { method: 'DELETE', retrySafety: RetrySafety.Unsafe },\n\n 'verification.status': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'verification.submit': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n\n 'platform.version': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'platform.changelog': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'platform.announcements': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'platform.portal': { method: 'GET', retrySafety: RetrySafety.Safe },\n 'platform.status': { method: 'GET', retrySafety: RetrySafety.Safe },\n\n 'telemetry.dwell': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n 'telemetry.interaction': { method: 'POST', retrySafety: RetrySafety.Unsafe },\n} as const satisfies Record<string, OperationDefinition>);\n\n/** Стабильный ID встроенной операции. */\nexport type BuiltInOperationId = keyof typeof OPERATIONS;\n\n/** Пользовательская семантическая операция низкоуровневого запроса. */\nexport type CustomOperationId = `custom:${string}`;\n\n/** ID любого запроса, видимый transformers и hooks. */\nexport type OperationId = BuiltInOperationId | CustomOperationId | 'raw';\n\nconst BUILT_IN_IDS: ReadonlySet<string> = new Set(Object.keys(OPERATIONS));\n\n/** Проверяет принадлежность ID встроенному каталогу. */\nexport function isBuiltInOperationId(value: string): value is BuiltInOperationId {\n return BUILT_IN_IDS.has(value);\n}\n\n/** HTTP-метод встроенной операции. */\nexport function operationMethod(id: BuiltInOperationId): OperationMethod {\n return OPERATIONS[id].method;\n}\n\n/** Политика автоматического повтора встроенной операции. */\nexport function operationRetrySafety(id: BuiltInOperationId): RetrySafety {\n return OPERATIONS[id].retrySafety;\n}\n","import { ItdConfigError } from './errors.js';\n\n/**\n * Как библиотека обращается с cookie.\n *\n * - `browser` — cookie ведёт браузер, запросы уходят с `credentials: 'include'`;\n * - `server` — cookie ведёт встроенный jar, заголовок `Cookie` подставляется вручную;\n * - `auto` — определяется по среде исполнения (значение по умолчанию).\n */\nexport const RuntimeMode = Object.freeze({\n /** Определяется по среде исполнения. Значение по умолчанию. */\n Auto: 'auto',\n /** Cookie ведёт браузер, запросы уходят с `credentials: 'include'`. */\n Browser: 'browser',\n /** Cookie ведёт встроенный jar, заголовок `Cookie` подставляется вручную. */\n Server: 'server',\n} as const);\nexport type RuntimeMode = (typeof RuntimeMode)[keyof typeof RuntimeMode];\n\n/** Распознанная среда исполнения. */\nexport const DetectedRuntime = Object.freeze({\n Browser: 'browser',\n /** Есть `window`, но нет `document`; cookie ведёт нативный сетевой слой. */\n ReactNative: 'react-native',\n Server: 'server',\n} as const);\nexport type DetectedRuntime = (typeof DetectedRuntime)[keyof typeof DetectedRuntime];\n\n/**\n * Определяет среду исполнения.\n *\n * React Native выделен отдельно намеренно: там есть `window`, но нет `document`, а cookie\n * хранит нативный сетевой слой (`NSHTTPCookieStorage` / `OkHttp`). Свой jar в этой среде\n * привёл бы к отправке cookie дважды.\n */\nexport function detectRuntime(): DetectedRuntime {\n const nav = (globalThis as { navigator?: { product?: string } }).navigator;\n if (nav?.product === 'ReactNative') return DetectedRuntime.ReactNative;\n\n if (typeof document !== 'undefined') return DetectedRuntime.Browser;\n\n return DetectedRuntime.Server;\n}\n\n/**\n * Нужно ли вести собственный cookie-jar.\n *\n * Только в серверных средах: в браузере `Set-Cookie` из JS не читается и не нужен,\n * в React Native cookie ведёт нативный слой.\n */\nexport function shouldUseCookieJar(mode: RuntimeMode): boolean {\n if (mode === RuntimeMode.Browser) return false;\n if (mode === RuntimeMode.Server) return true;\n return detectRuntime() === DetectedRuntime.Server;\n}\n\n/**\n * Нужно ли отправлять запросы с `credentials: 'include'`.\n *\n * Только в браузере: именно так refresh-cookie попадает в запрос `POST /api/v1/auth/refresh`.\n * Требует настроенного CORS на стороне итд.com — если его нет, укажите свой прокси в `baseUrl`.\n */\nexport function shouldSendCredentials(mode: RuntimeMode): boolean {\n if (mode === RuntimeMode.Browser) return true;\n if (mode === RuntimeMode.Server) return false;\n return detectRuntime() === DetectedRuntime.Browser;\n}\n\n/**\n * Возвращает реализацию `fetch`.\n *\n * @param custom реализация из конфигурации клиента, если пользователь её передал\n * @throws {ItdConfigError} если `fetch` недоступен — например, на Node ниже 18\n */\nexport function resolveFetch(custom?: typeof fetch): typeof fetch {\n if (custom !== undefined) {\n if (typeof custom !== 'function') {\n throw new ItdConfigError('fetch должен быть функцией');\n }\n return custom;\n }\n\n if (typeof globalThis.fetch === 'function') {\n // Привязка к globalThis обязательна: в некоторых средах несвязанный fetch бросает\n // «Illegal invocation».\n return globalThis.fetch.bind(globalThis);\n }\n\n throw new ItdConfigError(\n 'В этой среде нет глобального fetch. Обновитесь до Node 18+ либо передайте свою ' +\n 'реализацию через опцию fetch.',\n );\n}\n\n/**\n * Проверки бинарных типов, безопасные в любой среде.\n *\n * Обычный `instanceof` здесь недостаточен: конструктора может не быть вовсе, и тогда\n * проверка падает с `ReferenceError`, а не возвращает `false`. `Blob` есть в Node с 18.0,\n * но `File` стал глобальным только в Node 20 — при заявленной поддержке Node 18 голый\n * `instanceof File` ронял бы любую загрузку.\n */\nexport function isBlob(value: unknown): value is Blob {\n return typeof Blob !== 'undefined' && value instanceof Blob;\n}\n\n/** @see {@link isBlob} */\nexport function isFile(value: unknown): value is File {\n return typeof File !== 'undefined' && value instanceof File;\n}\n\n/** Доступно ли потоковое чтение тела ответа — от этого зависит, сработает ли SSE. */\nexport function supportsStreamingBody(): boolean {\n return typeof ReadableStream !== 'undefined' && typeof TextDecoder !== 'undefined';\n}\n\n/**\n * Создаёт идентификатор устройства для заголовка `X-Device-Id` — UUID v4.\n *\n * `crypto.randomUUID` есть в Node 19+, браузерах и Deno, но отсутствует в Node 18 вне\n * защищённого контекста, поэтому предусмотрен запасной путь.\n */\nexport function createDeviceId(): string {\n const webCrypto = (globalThis as { crypto?: Crypto }).crypto;\n\n if (typeof webCrypto?.randomUUID === 'function') return webCrypto.randomUUID();\n\n // Форма UUID v4 собирается вручную: серверу важна стабильность значения, а не его энтропия.\n const bytes = new Uint8Array(16);\n if (typeof webCrypto?.getRandomValues === 'function') webCrypto.getRandomValues(bytes);\n else for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);\n\n // biome-ignore lint/style/noNonNullAssertion: длина массива фиксирована выше\n bytes[6] = (bytes[6]! & 0x0f) | 0x40;\n // biome-ignore lint/style/noNonNullAssertion: длина массива фиксирована выше\n bytes[8] = (bytes[8]! & 0x3f) | 0x80;\n\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');\n\n return [\n hex.slice(0, 8),\n hex.slice(8, 12),\n hex.slice(12, 16),\n hex.slice(16, 20),\n hex.slice(20, 32),\n ].join('-');\n}\n","import type { UserId } from '../models/common.js';\nimport type { AuthInput, CredentialsAuth } from '../types/options.js';\nimport type { AuthConfig } from './config.js';\nimport {\n AUTH_FLAG_COOKIE,\n type CookieJar,\n REFRESH_COOKIE,\n REFRESH_COOKIE_PATH,\n} from './cookies.js';\nimport { Emitter, reportListenerError } from './emitter.js';\nimport { ItdApiError, ItdAuthError, ItdConfigError } from './errors.js';\nimport { readTokenIdentity, readTokenSubject } from './jwt.js';\nimport { operationMethod } from './operations.js';\nimport type { RequestHandler } from './pipeline.js';\nimport { createDeviceId } from './runtime.js';\nimport { copySession, type ItdSession } from './storage.js';\n\n/** Пути эндпоинтов авторизации. */\nexport const AUTH_PATHS = {\n signUp: '/api/v1/auth/sign-up',\n signIn: '/api/v1/auth/sign-in',\n verifyOtp: '/api/v1/auth/verify-otp',\n resendOtp: '/api/v1/auth/resend-otp',\n refresh: '/api/v1/auth/refresh',\n logout: '/api/v1/auth/logout',\n forgotPassword: '/api/v1/auth/forgot-password',\n resetPassword: '/api/v1/auth/reset-password',\n changePassword: '/api/v1/auth/change-password',\n sessions: '/api/v1/auth/sessions',\n} as const;\n\n/**\n * Публичный ключ Cloudflare Turnstile платформы итд.com.\n *\n * Нужен, чтобы отрисовать виджет капчи и получить токен для `signIn`, `signUp`\n * и `forgotPassword`.\n *\n * Ключ привязан к домену: на чужом origin Cloudflare отказывает виджету с кодом `110200`.\n * Поэтому отрисовать его может только код, выполняемый на самом итд.com. Остальным\n * подходит `@itd-api/turnstile`, готовый токен из другого источника или вовсе вход\n * без капчи — по сохранённой сессии либо по токенам, взятым в браузере.\n *\n * @example\n * ```ts\n * turnstile.render('#captcha', {\n * sitekey: TURNSTILE_SITE_KEY,\n * callback: (turnstileToken) => itd.auth.signIn({ email, password, turnstileToken }),\n * });\n * ```\n */\nexport const TURNSTILE_SITE_KEY = '0x4AAAAAACHhxczw6fJGwPBg';\n\n/** Заголовок с идентификатором устройства. Сервер связывает с ним запись в списке сессий. */\nexport const DEVICE_ID_HEADER = 'X-Device-Id';\n\n/** События слоя авторизации. */\nexport interface AuthEvents {\n /** Токен получен или обновлён. */\n tokens: { accessToken: string };\n /** Выполнен вход. */\n signIn: { accessToken: string };\n /** Сессия очищена — вручную или из-за неудачного обновления. */\n signOut: undefined;\n /** Обновить сессию не удалось; дальнейшие запросы будут падать с 401. */\n authError: { error: unknown };\n}\n\n/** Ответ эндпоинтов, выдающих токен. */\ninterface TokenResponse {\n accessToken?: unknown;\n}\n\nfunction readAccessToken(payload: unknown): string | undefined {\n if (typeof payload !== 'object' || payload === null) return undefined;\n const token = (payload as TokenResponse).accessToken;\n return typeof token === 'string' && token.length > 0 ? token : undefined;\n}\n\n/** Убирает поле, которое сохраняли промежуточные версии с поддержкой `getUserId()`. */\nfunction withoutLegacyUserId(session: ItdSession): ItdSession {\n const clean = { ...session };\n delete (clean as ItdSession & { userId?: unknown }).userId;\n return clean;\n}\n\nlet authScopeSequence = 0;\n\nfunction nextAuthScope(): string {\n authScopeSequence += 1;\n return String(authScopeSequence);\n}\n\n/** Области аккаунта и конкретной сессии для локального состояния плагинов. */\nexport interface AuthIdentity {\n /** Идентификатор пользователя; отсутствует у непрозрачного или повреждённого токена. */\n userId?: UserId | undefined;\n /** Идентификатор серверной сессии; отсутствует у непрозрачного или повреждённого токена. */\n sessionId?: string | undefined;\n}\n\ninterface AuthManagerHooks {\n /** Вызывается синхронно перед заменой владельца авторизации. */\n onAccountChange?: (() => void) | undefined;\n}\n\n/**\n * Хранит сессию и продлевает её.\n *\n * Главное здесь — **дедупликация обновления**. Когда десять параллельных запросов\n * одновременно получают `401`, обновление должно произойти один раз, а остальные обязаны\n * дождаться его результата. Иначе сервер увидит десять параллельных `refresh`, и все,\n * кроме первого, скорее всего получат отказ по уже использованному токену.\n */\nexport class AuthManager {\n readonly #config: AuthConfig;\n readonly #send: RequestHandler;\n readonly #jar: CookieJar;\n readonly #emitter: Emitter<AuthEvents>;\n readonly #hooks: AuthManagerHooks;\n\n /** `undefined` — сессия ещё не читалась из хранилища. */\n #session: ItdSession | null | undefined;\n /**\n * Общий промис чтения сессии из хранилища. Дедупликация: параллельные запросы на холодном\n * старте читают хранилище один раз и не заводят каждый свой `deviceId`.\n */\n #loading: Promise<ItdSession | null> | null = null;\n /** Общий промис обновления: к нему присоединяются все, кто получил 401. */\n #refreshing: Promise<string | null> | null = null;\n /** Общий промис входа по логину и паролю. */\n #signingIn: Promise<string> | null = null;\n /** Fallback-область для изоляции состояния плагинов, когда JWT-идентичность недоступна. */\n #authScope = nextAuthScope();\n /** Последний токен внешнего источника; `undefined` означает, что источник ещё не читался. */\n #externalToken: string | null | undefined;\n /** Идентификаторы последнего токена внешнего источника для синхронных потребителей. */\n #externalIdentity: AuthIdentity | undefined;\n /**\n * Идентификатор устройства.\n *\n * Держится отдельно от сессии намеренно: выход из аккаунта не меняет устройство,\n * поэтому `clear()` его не трогает.\n */\n #deviceId: string | undefined;\n /** Общий промис первичной выдачи `deviceId` — чтобы параллельные запросы получили один. */\n #deviceIdLoading: Promise<string> | null = null;\n /**\n * Счётчик смен владельца авторизации.\n *\n * Растёт при каждой операции, которая заменяет или очищает сессию извне: `clear`,\n * `setSession`, `setAccessToken`, вход. `#performRefresh` снимает его значение перед\n * сетевым запросом и сверяет перед записью — иначе запоздавший ответ обновления мог бы\n * воскресить уже очищенную сессию поверх `signOut`.\n */\n #authEpoch = 0;\n\n constructor(\n config: AuthConfig,\n send: RequestHandler,\n jar: CookieJar,\n hooks: AuthManagerHooks = {},\n ) {\n this.#config = config;\n this.#send = send;\n this.#jar = jar;\n this.#hooks = hooks;\n this.#emitter = new Emitter<AuthEvents>((error) =>\n reportListenerError(config.logger, 'авторизации', error),\n );\n }\n\n /** Подписка на события авторизации. */\n get on(): Emitter<AuthEvents>['on'] {\n return this.#emitter.on.bind(this.#emitter);\n }\n\n /** Подписка на одно срабатывание. */\n get once(): Emitter<AuthEvents>['once'] {\n return this.#emitter.once.bind(this.#emitter);\n }\n\n /** Снимает lifecycle-подписки при терминальном освобождении владельца. @internal */\n dispose(): void {\n this.#invalidateInFlight();\n this.#emitter.removeAllListeners();\n }\n\n /**\n * Непрозрачная fallback-область авторизации.\n *\n * Изолирует состояние плагинов, когда идентичность аккаунта из JWT недоступна (непрозрачный\n * токен): значение уникально для владельца и сменяется при замене или очистке сессии.\n */\n getAuthScope(): string {\n return this.#authScope;\n }\n\n /** Загружает сессию и возвращает идентификаторы аккаунта и сессии для плагинов. */\n async getAuthIdentity(): Promise<AuthIdentity> {\n const session = await this.#loadSession();\n if (session?.accessToken) return this.#identityForToken(session.accessToken);\n\n const auth = this.#config.auth;\n if (typeof auth === 'object' && auth !== null && 'getToken' in auth) {\n const token = await this.#readExternalToken(() => auth.getToken());\n return this.#identityForToken(token ?? undefined);\n }\n\n return {};\n }\n\n /** Идентификаторы аккаунта и сессии без чтения хранилища. @internal */\n getCurrentAuthIdentity(): AuthIdentity {\n const session =\n this.#session === undefined ? this.#sessionFromConfig(this.#config.auth) : this.#session;\n return session?.accessToken\n ? this.#identityForToken(session.accessToken)\n : (this.#externalIdentity ?? {});\n }\n\n #rotateAuthScope(): void {\n this.#authScope = nextAuthScope();\n }\n\n /** Отмечает смену владельца авторизации — обесценивает результат идущего обновления. */\n #invalidateInFlight(): void {\n this.#authEpoch += 1;\n }\n\n #identityForToken(accessToken: string | undefined): AuthIdentity {\n const token = accessToken ? readTokenIdentity(accessToken) : {};\n return {\n ...(token.subject ? { userId: token.subject as UserId } : {}),\n ...(token.sessionId ? { sessionId: token.sessionId } : {}),\n };\n }\n\n async #readExternalToken(\n getToken: () => string | null | Promise<string | null>,\n ): Promise<string | null> {\n const token = (await getToken()) ?? null;\n const current = this.#identityForToken(token ?? undefined);\n\n if (this.#externalToken !== undefined && this.#externalToken !== token) {\n this.#rotateAuthScope();\n const previous = this.#externalIdentity ?? {};\n const changed =\n previous.userId !== undefined && current.userId !== undefined\n ? previous.userId !== current.userId\n : true;\n if (changed) this.#hooks.onAccountChange?.();\n }\n\n this.#externalToken = token;\n this.#externalIdentity = current;\n return token;\n }\n\n /** Меняет fallback и завершает realtime, только если фактически сменился аккаунт. */\n #transitionAuth(accessToken: string | undefined, rotateFallback = true): void {\n const knownPrevious = this.#session !== undefined;\n const previous = this.#identityForToken(this.#session?.accessToken);\n const current = this.#identityForToken(accessToken);\n const changed =\n previous.userId !== undefined && current.userId !== undefined\n ? previous.userId !== current.userId\n : previous.userId !== current.userId || rotateFallback;\n\n if (rotateFallback || changed) this.#rotateAuthScope();\n if (knownPrevious && changed) this.#hooks.onAccountChange?.();\n }\n\n /**\n * Есть ли признак живой refresh-сессии.\n *\n * Рядом с refresh-токеном сервер ставит незакрытую cookie `is_auth` — по ней видно,\n * что продлевать сессию вообще есть смысл, и API не дёргается у анонимов.\n * В браузере cookie ведёт сама среда, поэтому там ответ всегда `true`.\n *\n * Асинхронный, потому что признак может лежать в {@link TokenStorage}: до чтения оттуда\n * ответ был бы `false` даже при полностью рабочей сохранённой сессии.\n */\n async hasRefreshSession(): Promise<boolean> {\n await this.#loadSession();\n return this.#hasRefreshSession();\n }\n\n /** То же самое, но без чтения хранилища — для вызовов, где сессия уже загружена. */\n #hasRefreshSession(): boolean {\n if (!this.#config.useCookieJar) return true;\n // Флаг должен принадлежать основному API, а не другому сервису.\n if (this.#jar.has(AUTH_FLAG_COOKIE, this.#config.baseUrl)) return true;\n\n // Явно переданный refresh-токен — тоже основание пробовать.\n return Boolean(this.#session?.refreshToken);\n }\n\n /** Заголовки авторизации для очередного запроса. Пустой объект, если токена нет. */\n async getAuthHeaders(): Promise<Record<string, string>> {\n const token = await this.getAccessToken();\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n\n /**\n * Заголовки уже подготовленной авторизации без чтения storage или вызова внешнего источника.\n *\n * Используются после ожидания транспортной очереди: к этому моменту `getAccessToken()` уже\n * был вызван снаружи неё, но token мог успеть смениться из-за refresh или `setSession()`.\n *\n * @internal\n */\n getCurrentAuthHeaders(): Record<string, string> {\n const session =\n this.#session === undefined ? this.#sessionFromConfig(this.#config.auth) : this.#session;\n const token = session?.accessToken ?? this.#externalToken ?? null;\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n\n /**\n * Идентификатор устройства для заголовка `X-Device-Id`.\n *\n * Заводится один раз и сохраняется в сессии, чтобы пережить перезапуск процесса:\n * сервер связывает с ним запись в списке сессий, и плавающее значение плодило бы\n * по новой сессии на каждый старт.\n */\n getDeviceId(): Promise<string> {\n if (this.#deviceId) return Promise.resolve(this.#deviceId);\n\n // Дедупликация: параллельные вызовы на холодном клиенте получают один `X-Device-Id`.\n this.#deviceIdLoading ??= this.#resolveDeviceId().finally(() => {\n this.#deviceIdLoading = null;\n });\n\n return this.#deviceIdLoading;\n }\n\n async #resolveDeviceId(): Promise<string> {\n const session = await this.#loadSession();\n const deviceId = this.#config.deviceId ?? session?.deviceId ?? createDeviceId();\n\n this.#deviceId = deviceId;\n\n // Записываем, только если значение действительно новое, — иначе каждый первый запрос\n // дёргал бы хранилище без всякой пользы.\n if (session?.deviceId !== deviceId) {\n await this.#saveSession({ ...(session ?? {}), deviceId });\n }\n\n return deviceId;\n }\n\n /**\n * Текущий токен доступа.\n *\n * При необходимости выполняет отложенный вход: если в конфигурации переданы логин\n * и пароль, первый же запрос сам заведёт сессию.\n */\n async getAccessToken(): Promise<string | null> {\n const session = await this.#loadSession();\n if (session?.accessToken) return session.accessToken;\n\n const auth = this.#config.auth;\n if (!auth) return null;\n\n // Внешний источник токена спрашиваем каждый раз: он сам решает, когда обновлять.\n if (typeof auth === 'object' && 'getToken' in auth) {\n return this.#readExternalToken(() => auth.getToken());\n }\n\n if (typeof auth === 'object' && 'email' in auth) {\n return this.#signInWithCredentials(auth);\n }\n\n return null;\n }\n\n /**\n * Реакция транспорта на ответ `401`.\n *\n * @returns `true`, если токен обновлён и запрос имеет смысл повторить\n */\n async onUnauthorized(): Promise<boolean> {\n try {\n const token = await this.#refreshDeduplicated();\n if (token !== null) return true;\n\n // Обновлять нечем — это тоже отказ авторизации, и подписчик должен о нём узнать.\n this.#emitter.emit('authError', { error: this.#noRefreshSessionError() });\n return false;\n } catch (error) {\n this.#emitter.emit('authError', { error });\n return false;\n }\n }\n\n /**\n * Ошибка «сессию продлить нечем».\n *\n * Возникает, только когда обновление даже не начиналось: нет ни cookie `is_auth`,\n * ни refresh-токена. Если сервер ответил отказом, наружу уходит **его** ошибка —\n * подменять её этой значило бы прятать причину (`REFRESH_TOKEN_MISSING`,\n * `SESSION_NOT_FOUND`, `SESSION_REVOKED` — разные поводы и разные действия).\n */\n #noRefreshSessionError(): ItdAuthError {\n return new ItdAuthError({\n status: 401,\n code: 'SESSION_EXPIRED',\n message:\n 'Не удалось обновить сессию: нет ни cookie is_auth, ни refresh-токена. ' +\n 'Войдите заново либо передайте refreshToken в auth.',\n method: 'POST',\n path: AUTH_PATHS.refresh,\n raw: undefined,\n });\n }\n\n /**\n * Обновляет токен доступа.\n *\n * Параллельные вызовы объединяются в один сетевой запрос.\n *\n * @throws {ItdAuthError} если обновить сессию не удалось\n */\n async refresh(): Promise<string> {\n const token = await this.#refreshDeduplicated();\n if (token === null) throw this.#noRefreshSessionError();\n return token;\n }\n\n /** Сохраняет токен, полученный извне, — например после подтверждения OTP. */\n async setAccessToken(accessToken: string): Promise<void> {\n await this.#loadSession();\n this.#invalidateInFlight();\n this.#transitionAuth(accessToken);\n await this.#saveSession({\n ...(this.#session ?? {}),\n accessToken,\n obtainedAt: this.#config.clock.now(),\n });\n this.#emitter.emit('tokens', { accessToken });\n }\n\n /** Текущая сессия целиком. Полезно, чтобы сохранить её самому. */\n async getSession(): Promise<ItdSession | null> {\n const session = await this.#loadSession();\n return session ? copySession(session) : null;\n }\n\n /**\n * Идентификатор владельца сессии.\n *\n * Считается непосредственно из текущего токена и отдельно не сохраняется: после замены\n * токена идентификатор прежнего владельца остаться не может.\n */\n async getUserId(): Promise<UserId | undefined> {\n const session = await this.#loadSession();\n return session?.accessToken ? readTokenSubject(session.accessToken) : undefined;\n }\n\n /** Заменяет сессию и связанные с ней cookie целиком. */\n async setSession(session: ItdSession): Promise<void> {\n await this.#loadSession();\n this.#invalidateInFlight();\n this.#transitionAuth(session.accessToken);\n this.#jar.clear();\n this.#jar.deserialize(session.cookies);\n\n if (session.deviceId) this.#deviceId = session.deviceId;\n\n // Refresh-cookie добавляется до сериализации сессии.\n this.#session = session;\n this.#seedRefreshCookie();\n\n await this.#saveSession(session);\n }\n\n /**\n * Забывает сессию и cookie. Сетевой запрос не выполняется.\n *\n * Идентификатор устройства выход переживает: иначе каждая пара «выход — вход» плодила бы\n * новую запись в списке сессий.\n */\n async clear(): Promise<void> {\n await this.#loadSession();\n this.#invalidateInFlight();\n this.#transitionAuth(undefined);\n this.#session = null;\n this.#jar.clear();\n await this.#config.storage.clear();\n\n if (this.#deviceId) await this.#saveSession({ deviceId: this.#deviceId });\n\n this.#emitter.emit('signOut', undefined);\n }\n\n #loadSession(): Promise<ItdSession | null> {\n if (this.#session !== undefined) return Promise.resolve(this.#session);\n\n this.#loading ??= this.#performLoad().finally(() => {\n this.#loading = null;\n });\n\n return this.#loading;\n }\n\n async #performLoad(): Promise<ItdSession | null> {\n const loaded = (await this.#config.storage.get()) ?? null;\n const stored = loaded ? withoutLegacyUserId(loaded) : null;\n\n // Восстанавливаем cookie: без них не выйдет обновить токен после перезапуска процесса.\n if (stored?.cookies) this.#jar.deserialize(stored.cookies);\n\n const fromConfig = this.#sessionFromConfig(this.#config.auth);\n\n // Хранилище отвечает за состояние сессии и всегда важнее, но недостающие поля\n // берутся из конфигурации: типичный случай — сохранён только accessToken,\n // а refresh-токен приходит из настроек приложения.\n this.#session =\n stored && fromConfig\n ? {\n ...stored,\n accessToken: stored.accessToken ?? fromConfig.accessToken,\n refreshToken: stored.refreshToken ?? fromConfig.refreshToken,\n }\n : (stored ?? fromConfig);\n\n this.#seedRefreshCookie();\n\n return this.#session;\n }\n\n /**\n * Кладёт refresh-токен в jar как cookie `refresh_token`.\n *\n * `POST /api/v1/auth/refresh` читает токен только из cookie, поэтому переданный строкой\n * приходится превращать в неё. В браузере это невозможно — cookie помечена `HttpOnly`,\n * и там обновление работает только на той, что поставил сам сервер.\n */\n #seedRefreshCookie(): void {\n if (!this.#config.useCookieJar) return;\n\n const refreshToken = this.#session?.refreshToken;\n if (!refreshToken) return;\n if (this.#jar.has(REFRESH_COOKIE)) return;\n\n this.#jar.set(this.#config.baseUrl, REFRESH_COOKIE, refreshToken, REFRESH_COOKIE_PATH);\n }\n\n #sessionFromConfig(auth: AuthInput | undefined): ItdSession | null {\n if (!auth) return null;\n if (typeof auth === 'string') {\n return { accessToken: auth, obtainedAt: this.#config.clock.now() };\n }\n\n if ('accessToken' in auth) {\n return {\n accessToken: auth.accessToken,\n refreshToken: auth.refreshToken,\n obtainedAt: this.#config.clock.now(),\n };\n }\n\n return null;\n }\n\n async #saveSession(session: ItdSession): Promise<void> {\n const cookies = this.#config.useCookieJar ? this.#jar.serialize() : undefined;\n const deviceId = session.deviceId ?? this.#deviceId;\n\n const next = withoutLegacyUserId({\n ...session,\n ...(cookies?.length ? { cookies } : {}),\n ...(deviceId ? { deviceId } : {}),\n });\n\n this.#session = next;\n await this.#config.storage.set(next);\n }\n\n /**\n * Обновление с дедупликацией.\n *\n * Все, кто пришёл, пока обновление уже идёт, получают его результат, а не запускают своё.\n */\n #refreshDeduplicated(): Promise<string | null> {\n if (this.#refreshing) return this.#refreshing;\n\n const promise = this.#performRefresh().finally(() => {\n this.#refreshing = null;\n });\n\n this.#refreshing = promise;\n return promise;\n }\n\n async #performRefresh(): Promise<string | null> {\n await this.#loadSession();\n\n if (!this.#hasRefreshSession()) {\n // Нет признаков сессии — обновлять нечего. При наличии логина и пароля\n // пробуем войти заново.\n return this.#reloginOrNull();\n }\n\n // Снимок владельца авторизации: если он сменится, пока идёт сетевой запрос,\n // результат обновления записывать нельзя.\n const epoch = this.#authEpoch;\n\n try {\n // Служебный запрос идёт через общий pipeline. Слой авторизации он пропускает, чтобы\n // не отправлять устаревший Bearer и не запускать рекурсивный refresh. Очередь безопасна:\n // исходная неудачная транспортная попытка освободила свой слот до вызова этого метода.\n const payload = await this.#send({\n operationId: 'auth.refresh',\n method: operationMethod('auth.refresh'),\n path: AUTH_PATHS.refresh,\n skipAuth: true,\n skipAuthRefresh: true,\n // Тела нет намеренно: сервер читает refresh-токен только из cookie — см.\n // #seedRefreshCookie. По той же причине не нужен и устаревший Bearer.\n });\n\n const accessToken = readAccessToken(payload);\n if (!accessToken) return this.#reloginOrNull();\n\n if (this.#authEpoch !== epoch) {\n // Пока шёл refresh, владельца сменили (signOut / setSession / вход): его результат\n // устарел. Не воскрешаем сохранённую сессию, отдаём актуальный токен как есть.\n return this.#session?.accessToken ?? null;\n }\n\n // Сервер выдаёт при обновлении **новый** refresh-токен (`Set-Cookie: refresh_token=…;\n // Max-Age=2592000`) и тут же гасит прежний. Забрать его из jar обязательно: иначе\n // сохранённая строка протухнет и восстановление сессии из хранилища перестанет работать.\n const rotated = this.#jar.getValue(\n REFRESH_COOKIE,\n this.#config.baseUrl + REFRESH_COOKIE_PATH,\n );\n\n // Refresh продолжает ту же серверную сессию. Для непрозрачного токена это единственная\n // надёжная возможность сохранить scope; смена пользователя в JWT всё равно будет обнаружена.\n this.#transitionAuth(accessToken, false);\n await this.#saveSession({\n ...(this.#session ?? {}),\n accessToken,\n ...(rotated ? { refreshToken: rotated } : {}),\n obtainedAt: this.#config.clock.now(),\n });\n\n this.#emitter.emit('tokens', { accessToken });\n return accessToken;\n } catch (error) {\n if (error instanceof ItdApiError) {\n if (this.#authEpoch !== epoch) {\n // Владельца сменили, пока шёл refresh: ошибка относится к уже забытой сессии —\n // не трогаем актуальное состояние.\n return this.#session?.accessToken ?? null;\n }\n\n // Сессия недействительна — чистим её, иначе будем биться в стену на каждом запросе.\n this.#transitionAuth(undefined);\n this.#session = null;\n this.#jar.clear();\n await this.#config.storage.clear();\n\n const relogged = await this.#reloginOrNull();\n if (relogged !== null) return relogged;\n\n // Войти заново нечем — отдаём ошибку сервера как есть. Именно она объясняет,\n // что произошло: REFRESH_TOKEN_MISSING, SESSION_NOT_FOUND, SESSION_REVOKED.\n throw error;\n }\n throw error;\n }\n }\n\n /** Повторный вход, если разрешён настройкой и есть логин с паролем. */\n async #reloginOrNull(): Promise<string | null> {\n const auth = this.#config.auth;\n\n if (\n !this.#config.reloginOnRefreshFailure ||\n !auth ||\n typeof auth !== 'object' ||\n !('email' in auth)\n ) {\n return null;\n }\n\n try {\n return await this.#signInWithCredentials(auth);\n } catch {\n return null;\n }\n }\n\n /**\n * Вход по логину и паролю.\n *\n * Параллельные вызовы объединяются: одновременный старт нескольких запросов не должен\n * приводить к нескольким попыткам входа и блокировке аккаунта.\n */\n #signInWithCredentials(credentials: CredentialsAuth): Promise<string> {\n if (this.#signingIn) return this.#signingIn;\n\n const promise = this.#performSignIn(credentials).finally(() => {\n this.#signingIn = null;\n });\n\n this.#signingIn = promise;\n return promise;\n }\n\n /**\n * Берёт токен капчи для входа.\n *\n * `getTurnstileToken` приоритетнее готовой строки: токен Turnstile одноразовый и живёт\n * несколько минут, поэтому при повторном входе через сутки годится только свежий.\n */\n async #resolveTurnstileToken(credentials: CredentialsAuth): Promise<string> {\n if (credentials.getTurnstileToken) {\n const token = await credentials.getTurnstileToken();\n if (token) return token;\n }\n\n if (credentials.turnstileToken) return credentials.turnstileToken;\n\n throw new ItdConfigError(\n 'Вход по email и паролю требует токен капчи Cloudflare Turnstile: без него сервер ' +\n 'отвечает 422. Передайте auth.getTurnstileToken (источник свежего токена) либо ' +\n 'разовый auth.turnstileToken. Ключ виджета — TURNSTILE_SITE_KEY. В Node токен умеет ' +\n 'добывать отдельный пакет: npm i @itd-api/turnstile, затем ' +\n 'getTurnstileToken: createTurnstileSolver().',\n );\n }\n\n async #performSignIn(credentials: CredentialsAuth): Promise<string> {\n const turnstileToken = await this.#resolveTurnstileToken(credentials);\n\n // Через общий pipeline: плагины, повторы и очередь сохраняются, а слой авторизации\n // пропускается, потому что токена ещё нет.\n const payload = await this.#send({\n operationId: 'auth.signIn',\n method: operationMethod('auth.signIn'),\n path: AUTH_PATHS.signIn,\n body: { email: credentials.email, password: credentials.password, turnstileToken },\n skipAuth: true,\n skipAuthRefresh: true,\n });\n\n const accessToken = readAccessToken(payload);\n\n if (!accessToken) {\n // Сервер запросил подтверждение по коду — автоматически это не пройти.\n throw new ItdConfigError(\n 'Вход по email и паролю требует подтверждения кодом из письма. Автоматический вход ' +\n 'невозможен: воспользуйтесь itd.auth.signInWithOtp() и передайте полученный ' +\n 'accessToken в конфигурацию клиента.',\n );\n }\n\n // Прежний refresh-токен и cookie намеренно не переносятся: вход выдал новую сессию,\n // и держаться за старую было бы ошибкой. Идентификатор устройства добавит #saveSession.\n this.#invalidateInFlight();\n this.#transitionAuth(accessToken);\n await this.#saveSession({ accessToken, obtainedAt: this.#config.clock.now() });\n this.#emitter.emit('tokens', { accessToken });\n this.#emitter.emit('signIn', { accessToken });\n\n return accessToken;\n }\n}\n","/**\n * Часы, которыми клиент измеряет время и планирует отложенную работу.\n *\n * Своя реализация нужна прежде всего в тестах: она позволяет проверять тайм-ауты,\n * повторы и переподключение без ожидания в реальном времени.\n */\nexport interface ItdClock {\n /** Текущее время в миллисекундах с начала эпохи Unix. */\n now(): number;\n /** Планирует вызов после завершения текущего стека и возвращает функцию отмены. */\n schedule(callback: () => void, delay: number): () => void;\n}\n\n/** Системные часы, используемые клиентом по умолчанию. */\nexport const systemClock: ItdClock = Object.freeze({\n now: () => Date.now(),\n schedule(callback: () => void, delay: number): () => void {\n const timer = setTimeout(callback, delay);\n return () => clearTimeout(timer);\n },\n});\n\n/** Общий срок на несколько ожиданий. @internal */\nexport interface Deadline {\n /** Ждёт промис. `false` — срок истёк раньше, чем промис завершился. */\n wait(promise: Promise<unknown>): Promise<boolean>;\n /** Снимает таймер. Обязателен: иначе он удерживает event loop до конца срока. */\n cancel(): void;\n}\n\n/**\n * Заводит срок, общий на все ожидания: от ожидания к ожиданию он не продлевается.\n *\n * @param timeout срок в миллисекундах; `0` — ждать без ограничения\n * @internal\n */\nexport function createDeadline(timeout: number, clock: ItdClock = systemClock): Deadline {\n if (timeout <= 0) {\n return {\n wait: async (promise) => {\n await promise;\n return true;\n },\n cancel: () => {},\n };\n }\n\n let expire!: () => void;\n const expired = new Promise<void>((resolve) => {\n expire = resolve;\n });\n\n return {\n wait: (promise) => Promise.race([promise.then(() => true), expired.then(() => false)]),\n cancel: clock.schedule(() => expire(), timeout),\n };\n}\n","// Этот файл создаётся автоматически: scripts/sync-version.mjs.\n// Менять вручную не нужно — значение берётся из package.json при `npm version` и сборке.\n\n/** Версия библиотеки. Попадает в `User-Agent`. */\nexport const LIBRARY_VERSION = '0.6.0';\n","import type {\n AuthInput,\n ClientHooks,\n ItdClientOptions,\n Logger,\n RetryOptions,\n} from '../types/options.js';\nimport { type ItdClock, systemClock } from './clock.js';\nimport { ItdConfigError } from './errors.js';\nimport { RuntimeMode, resolveFetch, shouldSendCredentials, shouldUseCookieJar } from './runtime.js';\nimport type { ServiceDefinition } from './services.js';\nimport { MemoryTokenStorage, type TokenStorage } from './storage.js';\nimport { normalizeBaseUrl } from './url.js';\nimport { LIBRARY_VERSION } from './version.js';\n\n/** Базовый URL API итд.com. Домен записан в punycode: `итд.com`. */\nexport const DEFAULT_BASE_URL = 'https://xn--d1ah4a.com';\n\n/** Базовый URL страницы статуса. Домен записан в punycode: `статус.итд.com`. */\nexport const DEFAULT_STATUS_BASE_URL = 'https://xn--80a7abcbg.xn--d1ah4a.com';\n\n/** Имя встроенного сервиса статуса. */\nexport const STATUS_SERVICE = 'status';\n\n/** Сервисы, зарегистрированные у любого клиента. */\nexport const BUILT_IN_SERVICES: readonly ServiceDefinition[] = Object.freeze([\n Object.freeze({ name: STATUS_SERVICE, baseUrl: DEFAULT_STATUS_BASE_URL, auth: false }),\n]);\n\n/** Таймаут запроса по умолчанию — 30 секунд. */\nexport const DEFAULT_TIMEOUT = 30_000;\n\n/** Срок ожидания чужого кода при остановке по умолчанию — 10 секунд. */\nexport const DEFAULT_SHUTDOWN_TIMEOUT = 10_000;\n\n// Значение живёт в отдельном модуле, который порождается из package.json скриптом\n// scripts/sync-version.mjs: две записанные вручную версии рано или поздно разъезжаются.\nexport { LIBRARY_VERSION } from './version.js';\n\n/**\n * `User-Agent` по умолчанию.\n *\n * Сайт стоит за DDoS-Guard, и запросы вовсе без `User-Agent` (так делает `fetch` в Node)\n * имеют шанс не пройти фильтр. Префикс `Mozilla/5.0` — дань традиции таких фильтров,\n * дальше идёт честное имя библиотеки: подделываться под браузер она не должна.\n *\n * В браузере заголовок не выставляется — `User-Agent` там запрещён к изменению, и среда\n * молча его игнорирует.\n */\nexport const DEFAULT_USER_AGENT = `Mozilla/5.0 (compatible; itd-api/${LIBRARY_VERSION}; +https://github.com/KiowDev/itd-api)`;\n\n/**\n * Настройки повторов со всеми значениями по умолчанию.\n *\n * Поля перечислены явно, а не через `Required<RetryOptions>`: тот снимает необязательность,\n * но оставляет `| undefined` в типе значения, раз оно указано в исходном интерфейсе.\n */\nexport interface ResolvedRetryOptions {\n attempts: number;\n baseDelay: number;\n maxDelay: number;\n jitter: number;\n shouldRetry: RetryOptions['shouldRetry'];\n}\n\n/**\n * Паузы перед повторами при ответе `429`.\n *\n * Сервер итд.com не присылает `Retry-After` и не сообщает время сброса окна, поэтому\n * паузу приходится подбирать лестницей: от секунды, если окно почти истекло,\n * до полутора минут, если лимит исчерпан всерьёз.\n */\nexport const DEFAULT_RATE_LIMIT_DELAYS = Object.freeze([1000, 5000, 30_000, 60_000, 90_000]);\n\n/** Настройки очереди со всеми значениями по умолчанию. */\nexport interface ResolvedRateLimitOptions {\n concurrency: number;\n rps: number | undefined;\n retryDelays: readonly number[];\n respectHeaders: boolean;\n}\n\n/**\n * Срез конфигурации, нужный слою авторизации.\n *\n * Выделен явно, чтобы {@link AuthManager} не получал целиком `ResolvedConfig` со всеми\n * настройками транспорта, повторов и очереди, к которым он отношения не имеет.\n * `ResolvedConfig` содержит все эти поля, поэтому подходит везде, где ждут `AuthConfig`.\n */\nexport interface AuthConfig {\n baseUrl: string;\n clock: ItdClock;\n auth: AuthInput | undefined;\n storage: TokenStorage;\n useCookieJar: boolean;\n deviceId: string | undefined;\n reloginOnRefreshFailure: boolean;\n logger: Logger | undefined;\n}\n\n/** Конфигурация клиента после подстановки значений по умолчанию и проверок. */\nexport interface ResolvedConfig extends AuthConfig {\n /** Сервисы из опций клиента. Встроенные сюда не входят. */\n services: ServiceDefinition[];\n autoRefresh: boolean;\n fetch: typeof fetch;\n timeout: number;\n /** Срок ожидания чужого кода при остановке. `0` — без срока. */\n shutdownTimeout: number;\n retry: ResolvedRetryOptions | undefined;\n rateLimit: ResolvedRateLimitOptions | undefined;\n hooks: ClientHooks;\n headers: Record<string, string>;\n /** Значение заголовка `User-Agent`. `undefined` — заголовок не выставляется. */\n userAgent: string | undefined;\n mode: RuntimeMode;\n /** Отправлять ли `credentials: 'include'` (в браузере). */\n sendCredentials: boolean;\n}\n\nfunction requirePositive(value: number, name: string): number {\n if (!Number.isFinite(value) || value < 0) {\n throw new ItdConfigError(`${name} должен быть неотрицательным числом, получено: ${value}`);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): boolean {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction requireOptionalBoolean(value: unknown, name: string): void {\n if (value !== undefined && typeof value !== 'boolean') {\n throw new ItdConfigError(`${name} должен быть boolean`);\n }\n}\n\nfunction resolveStorage(storage: TokenStorage | undefined): TokenStorage {\n if (storage === undefined) return new MemoryTokenStorage();\n if (!isRecord(storage)) throw new ItdConfigError('storage должен быть объектом TokenStorage');\n\n for (const method of ['get', 'set', 'clear'] as const) {\n if (typeof storage[method] !== 'function') {\n throw new ItdConfigError(`storage.${method} должен быть функцией`);\n }\n }\n return storage;\n}\n\nfunction resolveHeaders(headers: Record<string, string> | undefined): Record<string, string> {\n if (headers === undefined) return {};\n if (!isRecord(headers)) throw new ItdConfigError('headers должен быть объектом строк');\n\n for (const [name, value] of Object.entries(headers)) {\n if (typeof value !== 'string') {\n throw new ItdConfigError(`headers.${name} должен быть строкой`);\n }\n }\n return { ...headers };\n}\n\nfunction resolveHooks(hooks: ClientHooks | undefined): ClientHooks {\n if (hooks === undefined) return {};\n if (!isRecord(hooks)) throw new ItdConfigError('hooks должен быть объектом');\n\n for (const name of ['onRequest', 'onResponse', 'onError', 'onRetry'] as const) {\n if (hooks[name] !== undefined && typeof hooks[name] !== 'function') {\n throw new ItdConfigError(`hooks.${name} должен быть функцией`);\n }\n }\n return { ...hooks };\n}\n\nfunction resolveLogger(logger: ItdClientOptions['logger']): Logger | undefined {\n if (logger === undefined || logger === false) return undefined;\n if (logger === true) return consoleLogger();\n if (!isRecord(logger)) throw new ItdConfigError('logger должен быть boolean или объектом Logger');\n\n for (const method of ['debug', 'info', 'warn', 'error'] as const) {\n if (typeof logger[method] !== 'function') {\n throw new ItdConfigError(`logger.${method} должен быть функцией`);\n }\n }\n return logger;\n}\n\n/** Логгер поверх `console` — включается опцией `logger: true`. */\nfunction consoleLogger(): Logger {\n return {\n debug: (message, ...args) => console.debug(`[itd-api] ${message}`, ...args),\n info: (message, ...args) => console.info(`[itd-api] ${message}`, ...args),\n warn: (message, ...args) => console.warn(`[itd-api] ${message}`, ...args),\n error: (message, ...args) => console.error(`[itd-api] ${message}`, ...args),\n };\n}\n\n/**\n * Приводит настройки повторов к полному виду.\n *\n * Вызывается и при создании клиента (для глобальной настройки), и на каждый запрос,\n * у которого задан свой `retry`. Возвращает `undefined`, когда повторять не нужно\n * (`retry: false` или единственная попытка).\n *\n * @throws {ItdConfigError} при некорректных значениях\n */\nexport function resolveRetry(retry: ItdClientOptions['retry']): ResolvedRetryOptions | undefined {\n if (retry === false) return undefined;\n if (retry !== undefined && !isRecord(retry)) {\n throw new ItdConfigError('retry должен быть объектом или false');\n }\n\n const options = retry ?? {};\n const attempts = options.attempts ?? 3;\n\n if (!Number.isInteger(attempts) || attempts < 1) {\n throw new ItdConfigError(`retry.attempts должен быть целым числом от 1, получено: ${attempts}`);\n }\n\n const jitter = options.jitter ?? 0.3;\n if (!Number.isFinite(jitter) || jitter < 0 || jitter > 1) {\n throw new ItdConfigError(`retry.jitter должен быть в диапазоне 0…1, получено: ${jitter}`);\n }\n if (options.shouldRetry !== undefined && typeof options.shouldRetry !== 'function') {\n throw new ItdConfigError('retry.shouldRetry должен быть функцией');\n }\n\n const baseDelay = requirePositive(options.baseDelay ?? 500, 'retry.baseDelay');\n const maxDelay = requirePositive(options.maxDelay ?? 30_000, 'retry.maxDelay');\n\n // Одна попытка означает отсутствие повторов — очередь ретраев можно не поднимать.\n if (attempts === 1) return undefined;\n\n return {\n attempts,\n baseDelay,\n maxDelay,\n jitter,\n shouldRetry: options.shouldRetry,\n };\n}\n\n/**\n * Приводит настройки очереди к полному виду. `undefined` — очередь не нужна.\n *\n * Кроме создания клиента вызывается ещё из {@link ItdAccounts}: общая на всех аккаунтов\n * очередь заводится из тех же опций и с теми же проверками.\n *\n * @throws {ItdConfigError} при некорректных значениях\n *\n * @internal\n */\nexport function resolveRateLimit(\n rateLimit: ItdClientOptions['rateLimit'],\n): ResolvedRateLimitOptions | undefined {\n if (rateLimit === false) return undefined;\n if (rateLimit !== undefined && !isRecord(rateLimit)) {\n throw new ItdConfigError('rateLimit должен быть объектом или false');\n }\n\n const defaults = {\n concurrency: 6,\n rps: undefined,\n retryDelays: DEFAULT_RATE_LIMIT_DELAYS,\n respectHeaders: true,\n };\n if (!rateLimit) return defaults;\n\n const concurrency = rateLimit.concurrency ?? 6;\n if (!Number.isInteger(concurrency) || concurrency < 1) {\n throw new ItdConfigError(\n `rateLimit.concurrency должен быть целым числом от 1, получено: ${concurrency}`,\n );\n }\n\n if (rateLimit.rps !== undefined && (!Number.isFinite(rateLimit.rps) || rateLimit.rps <= 0)) {\n throw new ItdConfigError(\n `rateLimit.rps должен быть положительным числом, получено: ${rateLimit.rps}`,\n );\n }\n\n const retryDelays = rateLimit.retryDelays ?? defaults.retryDelays;\n if (!Array.isArray(retryDelays)) {\n throw new ItdConfigError('rateLimit.retryDelays должен быть массивом чисел');\n }\n for (const delay of retryDelays) requirePositive(delay, 'rateLimit.retryDelays');\n requireOptionalBoolean(rateLimit.respectHeaders, 'rateLimit.respectHeaders');\n\n return {\n concurrency,\n rps: rateLimit.rps,\n retryDelays: [...retryDelays],\n respectHeaders: rateLimit.respectHeaders ?? true,\n };\n}\n\n/**\n * Проверяет форму `auth` и сообщает о типичных ошибках понятным текстом.\n *\n * Молчаливое игнорирование неверной формы приводит к загадочным `401`, поэтому\n * ошибка возникает сразу при создании клиента.\n */\nfunction validateAuth(auth: AuthInput | undefined): AuthInput | undefined {\n if (auth === undefined) return undefined;\n\n if (typeof auth === 'string') {\n if (auth.trim() === '') {\n throw new ItdConfigError('auth: передана пустая строка вместо accessToken');\n }\n return auth;\n }\n\n if (typeof auth !== 'object' || auth === null) {\n throw new ItdConfigError(\n `auth должен быть строкой с токеном или объектом, получено: ${typeof auth}`,\n );\n }\n\n if ('getToken' in auth) {\n if (typeof auth.getToken !== 'function') {\n throw new ItdConfigError('auth.getToken должен быть функцией');\n }\n return { ...auth };\n }\n\n if ('accessToken' in auth) {\n if (typeof auth.accessToken !== 'string' || auth.accessToken.trim() === '') {\n throw new ItdConfigError('auth.accessToken должен быть непустой строкой');\n }\n return { ...auth };\n }\n\n if ('email' in auth || 'password' in auth) {\n const { email, password, turnstileToken, getTurnstileToken } = auth as {\n email?: unknown;\n password?: unknown;\n turnstileToken?: unknown;\n getTurnstileToken?: unknown;\n };\n if (typeof email !== 'string' || email.trim() === '') {\n throw new ItdConfigError('auth.email должен быть непустой строкой');\n }\n if (typeof password !== 'string' || password === '') {\n throw new ItdConfigError('auth.password должен быть непустой строкой');\n }\n if (getTurnstileToken !== undefined && typeof getTurnstileToken !== 'function') {\n throw new ItdConfigError('auth.getTurnstileToken должен быть функцией');\n }\n if (\n turnstileToken !== undefined &&\n (typeof turnstileToken !== 'string' || turnstileToken.trim() === '')\n ) {\n throw new ItdConfigError('auth.turnstileToken должен быть непустой строкой');\n }\n\n // Отсутствие капчи — не ошибка конфигурации: сессия может быть восстановлена из\n // хранилища, и до входа по паролю дело вообще не дойдёт. Ошибка возникнет в момент\n // входа, где её текст может объяснить, что именно нужно сделать.\n return { ...auth };\n }\n\n throw new ItdConfigError(\n 'auth не распознан. Ожидается строка с accessToken либо объект ' +\n '{ accessToken }, { email, password } или { getToken }',\n );\n}\n\n/**\n * Разворачивает запись сервисов из опций в определения: имя берётся из ключа, строка\n * означает один только базовый URL. URL проверяется при регистрации сервиса.\n */\nfunction resolveServices(services: ItdClientOptions['services']): ServiceDefinition[] {\n if (services === undefined) return [];\n if (!isRecord(services)) throw new ItdConfigError('services должен быть объектом');\n\n return Object.entries(services).map(([name, value]) => {\n if (typeof value === 'string') return { name, baseUrl: value };\n if (!isRecord(value)) {\n throw new ItdConfigError(`services.${name} должен быть URL или объектом сервиса`);\n }\n return { ...value, name } as ServiceDefinition;\n });\n}\n\n/**\n * Приводит пользовательские опции к полной конфигурации.\n *\n * Все проверки выполняются здесь, до единого сетевого запроса: неверная настройка должна\n * проявляться при создании клиента, а не через полчаса работы бота.\n *\n * @throws {ItdConfigError} при некорректных значениях\n */\nexport function resolveConfig(options: ItdClientOptions = {}): ResolvedConfig {\n if (!isRecord(options)) throw new ItdConfigError('опции клиента должны быть объектом');\n\n const mode: RuntimeMode = options.mode ?? RuntimeMode.Auto;\n\n if (!Object.values(RuntimeMode).includes(mode)) {\n throw new ItdConfigError(\n `mode должен быть одним из ${Object.values(RuntimeMode).join(', ')}, получено: ${mode}`,\n );\n }\n\n const timeout = requirePositive(options.timeout ?? DEFAULT_TIMEOUT, 'timeout');\n const shutdownTimeout = requirePositive(\n options.shutdownTimeout ?? DEFAULT_SHUTDOWN_TIMEOUT,\n 'shutdownTimeout',\n );\n\n if (\n options.clock !== undefined &&\n (typeof options.clock !== 'object' ||\n options.clock === null ||\n typeof options.clock.now !== 'function' ||\n typeof options.clock.schedule !== 'function')\n ) {\n throw new ItdConfigError('clock должен предоставлять методы now() и schedule()');\n }\n requireOptionalBoolean(options.autoRefresh, 'autoRefresh');\n requireOptionalBoolean(options.reloginOnRefreshFailure, 'reloginOnRefreshFailure');\n\n if (\n options.userAgent !== undefined &&\n options.userAgent !== false &&\n typeof options.userAgent !== 'string'\n ) {\n throw new ItdConfigError('userAgent должен быть строкой или false');\n }\n\n if (\n options.deviceId !== undefined &&\n (typeof options.deviceId !== 'string' || options.deviceId.trim() === '')\n ) {\n throw new ItdConfigError('deviceId должен быть непустой строкой');\n }\n\n return {\n baseUrl: normalizeBaseUrl(options.baseUrl ?? DEFAULT_BASE_URL),\n services: resolveServices(options.services),\n auth: validateAuth(options.auth),\n storage: resolveStorage(options.storage),\n autoRefresh: options.autoRefresh ?? true,\n reloginOnRefreshFailure: options.reloginOnRefreshFailure ?? true,\n fetch: resolveFetch(options.fetch),\n clock: options.clock ?? systemClock,\n timeout,\n shutdownTimeout,\n retry: resolveRetry(options.retry),\n rateLimit: resolveRateLimit(options.rateLimit),\n hooks: resolveHooks(options.hooks),\n logger: resolveLogger(options.logger),\n headers: resolveHeaders(options.headers),\n deviceId: options.deviceId,\n // `false` — способ не слать заголовок вовсе; строка заменяет умолчание.\n userAgent: options.userAgent === false ? undefined : (options.userAgent ?? DEFAULT_USER_AGENT),\n mode,\n useCookieJar: shouldUseCookieJar(mode),\n sendCredentials: shouldSendCredentials(mode),\n };\n}\n","import type { OperationRequestOptions } from '../types/options.js';\nimport type { OperationId } from './operations.js';\n\nconst REQUEST_ATTEMPT_STATE = Symbol('itd-api.request-attempt-state');\nconst DISPOSE_CLEANUP_REQUEST = Symbol('itd-api.dispose-cleanup-request');\n\ninterface RequestAttemptState {\n value: number;\n}\n\ntype InternalPipelineRequest = PipelineRequest & {\n [REQUEST_ATTEMPT_STATE]?: RequestAttemptState;\n [DISPOSE_CLEANUP_REQUEST]?: true;\n};\n\n/** Тело, заново подготовленное для одной транспортной попытки. */\nexport interface PreparedRequestBody {\n body: BodyInit;\n /** Заголовки тела, например multipart boundary. Пользовательские заголовки важнее. */\n headers?: Record<string, string> | undefined;\n /** Освобождает открытый файл или входящий HTTP-поток. */\n cleanup?: (() => void | Promise<void>) | undefined;\n}\n\n/** Контекст подготовки повторяемого тела. */\nexport interface RequestBodyContext {\n signal: AbortSignal;\n attempt: number;\n}\n\n/** Создаёт новое тело для каждой транспортной попытки. */\nexport type RequestBodyFactory = (\n context: RequestBodyContext,\n) => PreparedRequestBody | Promise<PreparedRequestBody>;\n\n/**\n * Описание запроса внутри конвейера.\n *\n * Отличается от публичного {@link RawRequestOptions} одним служебным полем: слои конвейера\n * должны уметь дописать заголовки так, чтобы пользовательские `headers` всё равно остались\n * важнее. Смешивать их в одном объекте нельзя — тогда слой авторизации перебивал бы\n * `Authorization`, заданный вызывающим кодом вручную.\n */\nexport interface PipelineRequest extends OperationRequestOptions {\n /** Повторяемое тело. Используется внутренними ресурсами вместо `body`. @internal */\n bodyFactory?: RequestBodyFactory | undefined;\n /**\n * Заголовки, добавленные слоями конвейера.\n *\n * Ставятся до пользовательских `headers` и потому могут быть ими переопределены.\n *\n * @internal\n */\n layerHeaders?: Record<string, string> | undefined;\n /**\n * Номер фактически начатой транспортной попытки, начиная с 1. Проставляет attempt layer.\n *\n * @internal\n */\n attempt?: number | undefined;\n}\n\n/** Запрос на внешней границе pipeline. Низкоуровневый вызов без ID считается `raw`. */\nexport type PipelineRequestInput = Omit<PipelineRequest, 'operationId'> & {\n operationId?: OperationId | undefined;\n};\n\n/** Один раз присваивает низкоуровневому запросу семантический ID до входа в middleware. */\nexport function identifyRequest(request: PipelineRequestInput): PipelineRequest {\n return request.operationId === undefined\n ? { ...request, operationId: 'raw' }\n : (request as PipelineRequest);\n}\n\n/** Привязывает счётчик транспортных попыток к одной логической операции. @internal */\nexport function trackRequestAttempts(request: PipelineRequest): PipelineRequest {\n const internal = request as InternalPipelineRequest;\n if (internal[REQUEST_ATTEMPT_STATE]) return request;\n return { ...request, [REQUEST_ATTEMPT_STATE]: { value: 0 } } as InternalPipelineRequest;\n}\n\n/** Начинает следующую фактическую транспортную попытку логической операции. @internal */\nexport function beginTransportAttempt(request: PipelineRequest): PipelineRequest {\n const tracked = trackRequestAttempts(request) as InternalPipelineRequest;\n const state = tracked[REQUEST_ATTEMPT_STATE];\n if (!state) throw new Error('request attempt state was not initialized');\n state.value += 1;\n return { ...tracked, attempt: state.value };\n}\n\n/** Возвращает номер последней начатой транспортной попытки. @internal */\nexport function currentTransportAttempt(request: PipelineRequest): number {\n return (request as InternalPipelineRequest)[REQUEST_ATTEMPT_STATE]?.value ?? 0;\n}\n\n/** Помечает запрос как часть внутренней финализации уже начатого `dispose()`. @internal */\nexport function markDisposeCleanupRequest(request: PipelineRequestInput): PipelineRequestInput {\n return { ...request, [DISPOSE_CLEANUP_REQUEST]: true } as PipelineRequestInput;\n}\n\n/** Разрешено ли запросу завершать внутреннюю очистку после перехода клиента в terminal state. @internal */\nexport function isDisposeCleanupRequest(request: PipelineRequest): boolean {\n return (request as InternalPipelineRequest)[DISPOSE_CLEANUP_REQUEST] === true;\n}\n\n/** Обработчик запроса. Самый внутренний в цепочке — транспорт. */\nexport type RequestHandler = (request: PipelineRequest) => Promise<unknown>;\n\n/**\n * Слой конвейера запросов.\n *\n * Получает запрос и продолжение цепочки. Может изменить запрос, обработать ошибку\n * продолжения или вовсе не вызывать `next`.\n */\nexport type RequestMiddleware = (\n request: PipelineRequest,\n next: RequestHandler,\n) => Promise<unknown>;\n\n/**\n * Собирает слои в один обработчик.\n *\n * Первый слой оказывается самым внешним. Порядок задаётся внутренним client runtime.\n *\n * @example\n * ```ts\n * const handler = composePipeline(\n * [plugins, services, retry, authRecovery, authPreparation, queue, attempt, authHeaders],\n * transport.send,\n * );\n * ```\n */\nexport function composePipeline(\n middlewares: readonly RequestMiddleware[],\n final: RequestHandler,\n): RequestHandler {\n return middlewares.reduceRight<RequestHandler>(\n (next, middleware) => (request) => middleware(request, next),\n final,\n );\n}\n\n/** Добавляет заголовки слоя, не трогая пользовательские. */\nexport function withLayerHeaders(\n request: PipelineRequest,\n headers: Record<string, string>,\n): PipelineRequest {\n return { ...request, layerHeaders: { ...request.layerHeaders, ...headers } };\n}\n","import { type BuiltInOperationId, operationMethod } from './operations.js';\nimport {\n identifyRequest,\n markDisposeCleanupRequest,\n type PipelineRequest,\n type PipelineRequestInput,\n type RequestHandler,\n} from './pipeline.js';\n\n/** Что нужно фасаду для работы. */\nexport interface HttpClientDeps {\n /** Готовый обработчик — вся цепочка слоёв поверх транспорта. */\n handler: RequestHandler;\n baseUrl: string;\n}\n\n/**\n * Точка входа ресурсов в конвейер запросов.\n *\n * Принимает готовый обработчик — цепочку слоёв поверх транспорта, собранную\n * во внутреннем runtime клиента, — и отдаёт ресурсам методы `request`/`operation`.\n * О слоях и их порядке ресурсы не знают.\n */\nexport class HttpClient {\n readonly #handler: RequestHandler;\n readonly #baseUrl: string;\n\n constructor(deps: HttpClientDeps) {\n this.#handler = deps.handler;\n this.#baseUrl = deps.baseUrl;\n }\n\n /** Базовый URL, к которому обращается клиент. */\n get baseUrl(): string {\n return this.#baseUrl;\n }\n\n /**\n * Выполняет запрос к API через собранный конвейер.\n *\n * @typeParam T ожидаемая форма ответа после снятия обёртки `{ data: … }`\n * @throws {ItdApiError} если сервер ответил статусом ≥ 400\n * @throws {ItdTimeoutError} если истёк таймаут\n * @throws {ItdAbortError} если запрос отменён через `signal`\n * @throws {ItdNetworkError} если запрос не дошёл до сервера\n */\n request<T = unknown>(options: PipelineRequestInput): Promise<T> {\n return this.#handler(identifyRequest(options)) as Promise<T>;\n }\n\n /** Выполняет встроенную семантическую операцию, подставляя её HTTP-метод из каталога. */\n operation<T = unknown>(\n operationId: BuiltInOperationId,\n options: Omit<PipelineRequest, 'operationId' | 'method'>,\n ): Promise<T> {\n return this.request<T>({ ...options, operationId, method: operationMethod(operationId) });\n }\n\n /** Выполняет внутреннюю операцию финализации после начала `ItdClient.dispose()`. @internal */\n cleanupOperation<T = unknown>(\n operationId: BuiltInOperationId,\n options: Omit<PipelineRequest, 'operationId' | 'method'>,\n ): Promise<T> {\n return this.request<T>(\n markDisposeCleanupRequest({\n ...options,\n operationId,\n method: operationMethod(operationId),\n }),\n );\n }\n}\n","import type { ClientHooks } from '../../types/options.js';\n\ntype HookName = keyof ClientHooks;\ntype HookContext<K extends HookName> = Parameters<NonNullable<ClientHooks[K]>>[0];\n\n/** Проверяет наличие публичного hook, не создавая дорогой контекст. @internal */\nexport function hasRequestHook(hooks: ClientHooks, field: HookName): boolean {\n return hooks[field] !== undefined;\n}\n\n/** Последовательно вызывает публичный hook конструктора. @internal */\nexport async function dispatchRequestHook<K extends HookName>(\n hooks: ClientHooks,\n field: K,\n context: HookContext<K>,\n): Promise<void> {\n const hook = hooks[field] as ((value: HookContext<K>) => void | Promise<void>) | undefined;\n await hook?.(context);\n}\n","import type { RetryDecisionContext } from '../types/options.js';\nimport type { ResolvedRetryOptions } from './config.js';\nimport {\n ItdAbortError,\n ItdApiError,\n ItdFileError,\n ItdNetworkError,\n ItdTimeoutError,\n} from './errors.js';\nimport { isBuiltInOperationId, operationRetrySafety, RetrySafety } from './operations.js';\nimport type { PipelineRequest } from './pipeline.js';\n\n/** Политика конкретного логического запроса, передаваемая планировщику повторов. */\nexport type RetryPolicy = RetryDecisionContext;\n\n/**\n * Решает, повторять ли запрос.\n *\n * @returns пауза в миллисекундах перед следующей попыткой либо `undefined`, если повторять не нужно\n */\nexport type RetryScheduler = (\n error: unknown,\n retryAttempt: number,\n policy: RetryPolicy,\n) => number | undefined;\n\n/** Методы raw-запроса, безопасность которых гарантирована самим HTTP-контрактом. */\nconst SAFE_RAW_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']);\n\n/** Может ли транспорт заново получить эквивалентное тело запроса. */\nexport function isRequestBodyReplayable(request: PipelineRequest): boolean {\n if (request.bodyFactory !== undefined) return true;\n return !(typeof ReadableStream !== 'undefined' && request.body instanceof ReadableStream);\n}\n\n/** Разрешает retrySafety запроса из явного override, каталога либо raw fallback. */\nexport function resolveRetryPolicy(request: PipelineRequest): RetryPolicy {\n let retrySafety: RetrySafety;\n\n if (request.retrySafety !== undefined) {\n retrySafety = request.retrySafety;\n } else if (isBuiltInOperationId(request.operationId)) {\n retrySafety = operationRetrySafety(request.operationId);\n } else if (request.operationId === 'raw' && SAFE_RAW_METHODS.has(request.method.toUpperCase())) {\n retrySafety = RetrySafety.Safe;\n } else {\n // Custom operation должна явно описать семантику; угадывать её по HTTP method нельзя.\n retrySafety = RetrySafety.Unsafe;\n }\n\n return {\n operationId: request.operationId,\n retrySafety,\n bodyReplayable: isRequestBodyReplayable(request),\n method: request.method.toUpperCase(),\n path: request.path,\n };\n}\n\n/**\n * Стоит ли повторять запрос после этой ошибки.\n *\n * `429` гарантирует, что операция не была обработана, поэтому допускает даже unsafe retry.\n * Обрыв сети, timeout и `5xx` такой гарантии не дают и требуют safe/idempotent операции.\n * Ошибка подготовки файла возникает до обращения к серверу и потому зависит только от\n * собственного признака retryable и повторяемости тела.\n */\nfunction isRetryable(error: unknown, policy: RetryPolicy): boolean {\n if (error instanceof ItdAbortError || !policy.bodyReplayable) return false;\n\n // Неизвестное runtime-значение (например, из JavaScript без типов) должно вести себя\n // консервативно, а не случайно разрешать повтор unsafe-операции.\n const repeatableOperation =\n policy.retrySafety === RetrySafety.Safe || policy.retrySafety === RetrySafety.Idempotent;\n\n if (error instanceof ItdApiError) {\n if (error.status === 429) return true;\n if (error.status >= 500) return repeatableOperation;\n return false;\n }\n\n if (error instanceof ItdNetworkError || error instanceof ItdTimeoutError) {\n return repeatableOperation;\n }\n\n if (error instanceof ItdFileError) return error.retryable;\n\n return false;\n}\n\n/** Экспоненциальная пауза со случайным разбросом. */\nfunction backoffDelay(\n retryAttempt: number,\n options: ResolvedRetryOptions,\n random: () => number,\n): number {\n const exponential = options.baseDelay * 2 ** (retryAttempt - 1);\n const capped = Math.min(exponential, options.maxDelay);\n const spread = capped * options.jitter * (random() * 2 - 1);\n\n return Math.max(0, Math.round(capped + spread));\n}\n\n/**\n * Собирает планировщик обычных повторов для транспорта.\n *\n * Поведение при `Retry-After`: пауза, названная сервером, соблюдается точно. Если сервер\n * просит ждать дольше `maxDelay`, повтор не выполняется — вызывающий код получает ошибку.\n * Rate-limit ladder основного pipeline ведёт отдельный счётчик и обрабатывается middleware.\n */\nexport function createRetryScheduler(\n options: ResolvedRetryOptions,\n random: () => number = Math.random,\n): RetryScheduler {\n return (error, retryAttempt, policy) => {\n if (retryAttempt >= options.attempts) return undefined;\n if (error instanceof ItdAbortError || !policy.bodyReplayable) return undefined;\n\n if (options.shouldRetry) {\n return options.shouldRetry(error, retryAttempt, policy)\n ? backoffDelay(retryAttempt, options, random)\n : undefined;\n }\n\n if (!isRetryable(error, policy)) return undefined;\n\n if (error instanceof ItdApiError && error.retryAfter !== undefined) {\n return error.retryAfter > options.maxDelay ? undefined : error.retryAfter;\n }\n\n return backoffDelay(retryAttempt, options, random);\n };\n}\n","import type { ClientHooks, Logger, RequestOptions } from '../types/options.js';\nimport { type ItdClock, systemClock } from './clock.js';\nimport { type ResolvedRetryOptions, resolveRetry } from './config.js';\nimport { ItdAbortError, isItdApiError, isItdRateLimitError } from './errors.js';\nimport {\n beginTransportAttempt,\n currentTransportAttempt,\n type PipelineRequest,\n type RequestHandler,\n type RequestMiddleware,\n trackRequestAttempts,\n withLayerHeaders,\n} from './pipeline.js';\nimport { dispatchRequestHook } from './plugins/hooks.js';\nimport type { PluginRegistry } from './plugins/registry.js';\nimport {\n createRetryScheduler,\n type RetryPolicy,\n type RetryScheduler,\n resolveRetryPolicy,\n} from './retry.js';\nimport type { ServiceRegistry } from './services.js';\nimport { normalizeBaseUrl } from './url.js';\n\n/** Ожидание повтора, которое уважает отмену запроса. */\nfunction sleep(clock: ItdClock, ms: number, signal?: AbortSignal): Promise<void> {\n if (!signal) return new Promise((resolve) => clock.schedule(resolve, ms));\n if (signal.aborted) {\n return Promise.reject(new ItdAbortError('Запрос отменён во время ожидания повтора'));\n }\n\n return new Promise((resolve, reject) => {\n const cancel = clock.schedule(() => {\n signal.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n const onAbort = () => {\n cancel();\n reject(new ItdAbortError('Запрос отменён во время ожидания повтора'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/**\n * Слой очереди: ограничение конкурентности и частоты.\n *\n * Должен стоять непосредственно вокруг одной транспортной попытки: тогда ожидание retry\n * не занимает слот, а каждый реальный HTTP-вызов заново учитывается ограничителем частоты.\n *\n * `skipQueue` оставляет продвинутому вызывающему явный способ обойти планировщик.\n */\nexport function createQueueMiddleware(\n schedule: <T>(request: PipelineRequest, task: () => Promise<T>) => Promise<T>,\n): RequestMiddleware {\n return (request, next) =>\n request.skipQueue ? next(request) : schedule(request, () => next(request));\n}\n\n/**\n * Слой плагинов.\n *\n * Стоит снаружи повторов и очереди: operation transformers видят запрос и ответ по одному\n * разу, иначе, например, текст поста зашифруется дважды. Здесь же к операции привязывается\n * snapshot attempt interceptors; сами они выполняются транспортом на каждой попытке.\n */\nexport function createPluginsMiddleware(plugins: PluginRegistry): RequestMiddleware {\n return (request, next) => plugins.run(request, next);\n}\n\n/**\n * Слой сервисов.\n *\n * Запросу с полем `service` подставляет хост сервиса, его заголовки и `skipAuth`, если\n * сервис объявлен публичным. Заданный у запроса `baseUrl` не трогает.\n *\n * Стоит снаружи повторов и авторизации, чтобы выставленный здесь `skipAuth` был ей виден.\n */\nexport function createServicesMiddleware(registry: ServiceRegistry): RequestMiddleware {\n return async (request, next) => {\n const service = request.service === undefined ? undefined : registry.require(request.service);\n let prepared = request;\n\n if (request.baseUrl !== undefined) {\n const baseUrl = normalizeBaseUrl(request.baseUrl);\n if (baseUrl !== request.baseUrl) prepared = { ...prepared, baseUrl };\n\n // Разовый хост не наследует разрешение авторизации от сервиса с другим URL.\n // Явное `skipAuth: false` остаётся способом осознанно отправить токен наружу.\n const matchesService = service?.baseUrl === baseUrl;\n const mayAuthorize = matchesService\n ? service.auth !== false\n : registry.isPrimarySite(baseUrl);\n if (!mayAuthorize && prepared.skipAuth === undefined) {\n prepared = { ...prepared, skipAuth: true };\n }\n }\n\n if (!service) return next(prepared);\n\n if (prepared.baseUrl === undefined) prepared = { ...prepared, baseUrl: service.baseUrl };\n\n if (service.headers) prepared = withLayerHeaders(prepared, service.headers);\n if (service.auth === false && prepared.skipAuth === undefined) {\n prepared = { ...prepared, skipAuth: true };\n }\n\n return next(prepared);\n };\n}\n\n/** Что нужно слою авторизации. */\nexport interface AuthMiddlewareDeps {\n /** Заголовки авторизации для очередного запроса. Пустой объект, если токена нет. */\n getAuthHeaders: () => Promise<Record<string, string>> | Record<string, string>;\n /** Реакция на `401`. Возвращает `true`, если токен обновлён и повтор имеет смысл. */\n onUnauthorized: () => Promise<boolean>;\n /** Обновлять ли токен при `401` автоматически. */\n autoRefresh: boolean;\n}\n\nasync function applyAuth(\n request: PipelineRequest,\n deps: Pick<AuthMiddlewareDeps, 'getAuthHeaders'>,\n): Promise<PipelineRequest> {\n if (request.skipAuth) return request;\n\n const headers = await deps.getAuthHeaders();\n return Object.keys(headers).length > 0 ? withLayerHeaders(request, headers) : request;\n}\n\n/**\n * Подготавливает auth state до входа транспортной попытки в очередь.\n *\n * Загрузка storage, внешний `getToken` и ленивый sign-in могут быть асинхронными; sign-in\n * сам входит в ту же queue. Поэтому эти действия обязаны завершиться до захвата её слота.\n */\nexport function createAuthPreparationMiddleware(deps: {\n prepareAuth: () => void | Promise<void>;\n}): RequestMiddleware {\n return async (request, next) => {\n if (!request.skipAuth) await deps.prepareAuth();\n return next(request);\n };\n}\n\n/**\n * Добавляет уже подготовленные заголовки непосредственно перед transport.\n *\n * В основном pipeline callback синхронен и не запускает I/O, поэтому слой безопасно стоит\n * внутри queue. Если token изменился, пока запрос ждал slot, будет использовано новое значение.\n */\nexport function createAuthHeadersMiddleware(\n deps: Pick<AuthMiddlewareDeps, 'getAuthHeaders'>,\n): RequestMiddleware {\n return async (request, next) => next(await applyAuth(request, deps));\n}\n\n/**\n * Нумерует фактические входы в transport для одной логической операции.\n *\n * Слой находится внутри auth recovery: повтор исходного запроса после успешного refresh\n * получает следующий номер, а сам `auth.refresh` ведёт собственный счётчик.\n */\nexport function createAttemptMiddleware(): RequestMiddleware {\n return (request, next) => next(beginTransportAttempt(request));\n}\n\n/**\n * Обрабатывает `401`: обновляет токен и повторяет транспортную попытку ровно один раз.\n *\n * Стоит снаружи подготовки auth и очереди, поэтому не удерживает её slot во время refresh.\n * Его `next` включает все эти слои: повтор заново готовит auth state и планируется.\n */\nexport function createAuthRecoveryMiddleware(\n deps: Pick<AuthMiddlewareDeps, 'onUnauthorized' | 'autoRefresh'>,\n): RequestMiddleware {\n return async (request, next) => {\n try {\n return await next(request);\n } catch (error) {\n // Обновляем и повторяем ровно один раз, чтобы не зациклиться, если сервер\n // отдаёт 401 и на свежем токене.\n if (\n request.skipAuthRefresh ||\n !deps.autoRefresh ||\n !isItdApiError(error) ||\n error.status !== 401\n ) {\n throw error;\n }\n\n const refreshed = await deps.onUnauthorized();\n if (!refreshed) throw error;\n\n return next({ ...request, skipAuthRefresh: true });\n }\n };\n}\n\n/**\n * Совместимая составная обёртка авторизации.\n *\n * Основной клиент разделяет preparation, recovery и headers вокруг очереди. Эта функция\n * остаётся удобной для автономной сборки pipeline без queue и внутренних тестов.\n */\nexport function createAuthMiddleware(deps: AuthMiddlewareDeps): RequestMiddleware {\n const recovery = createAuthRecoveryMiddleware(deps);\n const headers = createAuthHeadersMiddleware(deps);\n return (request, next) => recovery(request, (prepared) => headers(prepared, next));\n}\n\n/** Что нужно слою повторов. */\nexport interface RetryMiddlewareDeps {\n clock?: ItdClock;\n /** Глобальные настройки повторов. `undefined` — по умолчанию не повторять. */\n retry: ResolvedRetryOptions | undefined;\n /**\n * Паузы перед повторами при ответе `429`.\n *\n * Живут отдельно от `retry`: сервер не присылает `Retry-After`, и экспоненциальный откат\n * тут бесполезен — окно измеряется десятками секунд. Не зависят от `retry.attempts`.\n */\n rateLimitDelays: readonly number[];\n /**\n * Придерживает очередь запроса на паузу `429`. `undefined`, если очереди нет.\n *\n * Тормозится очередь того хоста, который ответил отказом: лимит у каждого свой.\n */\n pauseQueue: ((ms: number, request: PipelineRequest) => void) | undefined;\n hooks: ClientHooks;\n logger: Logger | undefined;\n buildUrl: (request: PipelineRequest) => string;\n}\n\n/**\n * Выбирает планировщик отката для конкретного запроса.\n *\n * `retry` у запроса переопределяет глобальную настройку: `false` выключает повторы,\n * объект задаёт свои. Обработка `429` от этого не зависит — она общая.\n */\nfunction resolveBackoff(\n retry: RequestOptions['retry'],\n global: RetryScheduler | undefined,\n): RetryScheduler | undefined {\n if (retry === undefined) return global;\n if (retry === false) return undefined;\n\n const resolved = resolveRetry(retry);\n return resolved ? createRetryScheduler(resolved) : undefined;\n}\n\n/**\n * Слой повторов.\n *\n * Ответ `429` обрабатывается отдельно от прочих ошибок лестницей пауз и с придержанием\n * всей очереди; сетевые сбои и `5xx` — экспоненциальным откатом. Настройка `retry`\n * у отдельного запроса имеет приоритет над глобальной.\n */\nexport function createRetryMiddleware(deps: RetryMiddlewareDeps): RequestMiddleware {\n const globalScheduler = deps.retry ? createRetryScheduler(deps.retry) : undefined;\n\n const nextDelay = (\n error: unknown,\n retryAttempt: number,\n rateLimitAttempt: number,\n request: PipelineRequest,\n policy: RetryPolicy,\n backoff: RetryScheduler | undefined,\n ): number | undefined => {\n if (isItdRateLimitError(error)) {\n if (!policy.bodyReplayable) return undefined;\n // Пауза, названную сервером, соблюдаем точно; иначе берём очередной шаг лестницы.\n const wait = error.retryAfter ?? deps.rateLimitDelays[rateLimitAttempt - 1];\n if (wait === undefined) return undefined;\n\n deps.pauseQueue?.(wait, request);\n deps.logger?.debug(`лимит частоты, повтор ${rateLimitAttempt} через ${wait} мс`);\n return wait;\n }\n\n return backoff?.(error, retryAttempt, policy);\n };\n\n return async (request, next) => {\n const trackedRequest = trackRequestAttempts(request);\n const method = request.method.toUpperCase();\n const policy = resolveRetryPolicy(request);\n const backoff = resolveBackoff(request.retry, globalScheduler);\n let retryAttempt = 0;\n let rateLimitAttempt = 0;\n\n for (;;) {\n try {\n return await next(trackedRequest);\n } catch (error) {\n const transportAttempt = currentTransportAttempt(trackedRequest);\n const rateLimited = isItdRateLimitError(error);\n if (rateLimited) rateLimitAttempt += 1;\n else retryAttempt += 1;\n\n const delay = nextDelay(error, retryAttempt, rateLimitAttempt, request, policy, backoff);\n if (delay === undefined) throw error;\n\n await dispatchRequestHook(deps.hooks, 'onRetry', {\n operationId: request.operationId,\n method,\n path: request.path,\n url: deps.buildUrl(request),\n // Умолчания транспорта добавляются после слоя повторов и сюда не входят.\n headers: new Headers({ ...request.layerHeaders, ...request.headers }),\n attempt: transportAttempt,\n error,\n delay,\n });\n\n deps.logger?.debug(\n `повтор ${method} ${request.path}, попытка ${transportAttempt + 1} через ${delay} мс`,\n );\n\n await sleep(deps.clock ?? systemClock, delay, request.signal);\n }\n }\n };\n}\n\n/** Собирает обработчик из слоёв. Реэкспорт для удобства сборки в одном месте. */\nexport { composePipeline } from './pipeline.js';\nexport type { RequestHandler, RequestMiddleware };\n","import type { OperationRequestOptions } from '../../types/options.js';\nimport { ItdConfigError } from '../errors.js';\nimport type { AttemptContext, AttemptInterceptor } from './contracts.js';\n\n/** Зарегистрированный interceptor вместе с именем владельца для диагностик. */\nexport interface RegisteredAttemptInterceptor {\n readonly plugin: string;\n readonly interceptor: AttemptInterceptor;\n}\n\nconst ATTEMPT_SCOPE: unique symbol = Symbol('itd-api.attempt-interceptors');\ntype ScopedRequest = OperationRequestOptions & {\n [ATTEMPT_SCOPE]?: readonly RegisteredAttemptInterceptor[];\n};\n\n/** Структурная проверка сохраняет совместимость с fetch-polyfill из другого realm. */\nfunction isResponse(value: unknown): value is Response {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as Partial<Response>;\n return (\n typeof candidate.status === 'number' &&\n typeof candidate.clone === 'function' &&\n typeof candidate.headers?.get === 'function'\n );\n}\n\n/** Привязывает к логической операции неизменяемый снимок attempt interceptors. @internal */\nexport function withAttemptInterceptorScope<T extends OperationRequestOptions>(\n request: T,\n interceptors: readonly RegisteredAttemptInterceptor[],\n): T {\n const scoped = request as ScopedRequest;\n return scoped[ATTEMPT_SCOPE] === interceptors\n ? request\n : ({ ...request, [ATTEMPT_SCOPE]: interceptors } as T);\n}\n\n/** Читает снимок attempt interceptors логической операции. @internal */\nexport function attemptInterceptorScope(\n request: OperationRequestOptions,\n): readonly RegisteredAttemptInterceptor[] {\n return (request as ScopedRequest)[ATTEMPT_SCOPE] ?? [];\n}\n\n/**\n * Выполняет обёртки одной попытки.\n *\n * Каждый `next` одноразовый: interceptor может short-circuit попытку синтетическим\n * `Response`, но не может незаметно породить два сетевых запроса внутри одного attempt.\n */\nexport async function runAttemptInterceptors(\n request: OperationRequestOptions,\n context: AttemptContext,\n execute: () => Promise<Response>,\n): Promise<Response> {\n const chain = attemptInterceptorScope(request).reduceRight<() => Promise<Response>>(\n (next, { plugin, interceptor }) =>\n async () => {\n let called = false;\n const response = await interceptor(context, () => {\n if (called) {\n throw new ItdConfigError(\n `attempt interceptor плагина «${plugin}» вызвал next() больше одного раза`,\n );\n }\n called = true;\n return next();\n });\n\n if (!isResponse(response)) {\n throw new ItdConfigError(\n `attempt interceptor плагина «${plugin}» должен вернуть Response`,\n );\n }\n return response;\n },\n execute,\n );\n\n return chain();\n}\n","import { ItdConfigError } from '../errors.js';\nimport type { ClientPlugin } from './contracts.js';\n\nconst RELATION_FIELDS = ['requires', 'conflicts', 'before', 'after'] as const;\n\nfunction validateNameList(plugin: ClientPlugin, field: (typeof RELATION_FIELDS)[number]): void {\n const values = plugin[field];\n if (values === undefined) return;\n if (!Array.isArray(values)) {\n throw new ItdConfigError(`плагин «${plugin.name}»: ${field} должен быть массивом`);\n }\n\n const seen = new Set<string>();\n for (const value of values as readonly unknown[]) {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new ItdConfigError(`плагин «${plugin.name}»: ${field} содержит пустое имя плагина`);\n }\n if (value === plugin.name) {\n throw new ItdConfigError(`плагин «${plugin.name}» не может указать себя в ${field}`);\n }\n if (seen.has(value)) {\n throw new ItdConfigError(`плагин «${plugin.name}»: ${field} повторяет имя «${value}»`);\n }\n seen.add(value);\n }\n}\n\n/**\n * Проверяет описание плагина без его установки.\n *\n * Нужна не только {@link PluginRegistry}: контейнер аккаунтов обязан отклонять сломанный\n * плагин сразу, даже когда внутри ещё нет ни одного клиента, которому можно поручить\n * полноценную установку.\n *\n * @internal\n */\nexport function validatePluginDefinition(plugin: ClientPlugin): void {\n if (typeof plugin?.install !== 'function') {\n throw new ItdConfigError('Плагин должен быть объектом с методом install()');\n }\n\n const name = plugin.name;\n if (typeof name !== 'string' || name.trim() === '') {\n throw new ItdConfigError('У плагина должно быть непустое имя');\n }\n\n for (const field of RELATION_FIELDS) validateNameList(plugin, field);\n}\n\ninterface OrderedPlugin {\n plugin: ClientPlugin;\n sequence: number;\n}\n\nfunction addEdge(\n from: string,\n to: string,\n edges: Map<string, Set<string>>,\n indegree: Map<string, number>,\n): void {\n const targets = edges.get(from);\n if (!targets || targets.has(to)) return;\n targets.add(to);\n indegree.set(to, (indegree.get(to) ?? 0) + 1);\n}\n\n/**\n * Проверяет зависимости и возвращает плагины в порядке обёрток: внешний идёт раньше.\n *\n * @internal\n */\nexport function orderPluginDefinitions(plugins: readonly ClientPlugin[]): ClientPlugin[] {\n const entries: OrderedPlugin[] = [];\n const byName = new Map<string, OrderedPlugin>();\n\n for (const [sequence, plugin] of plugins.entries()) {\n validatePluginDefinition(plugin);\n if (byName.has(plugin.name)) {\n throw new ItdConfigError(`плагин «${plugin.name}» уже подключён`);\n }\n const entry = { plugin, sequence };\n entries.push(entry);\n byName.set(plugin.name, entry);\n }\n\n for (const { plugin } of entries) {\n for (const required of plugin.requires ?? []) {\n if (!byName.has(required)) {\n throw new ItdConfigError(`плагину «${plugin.name}» требуется плагин «${required}»`);\n }\n }\n for (const conflict of plugin.conflicts ?? []) {\n if (byName.has(conflict)) {\n throw new ItdConfigError(`плагин «${plugin.name}» несовместим с плагином «${conflict}»`);\n }\n }\n for (const { plugin: other } of entries) {\n if (other.conflicts?.includes(plugin.name)) {\n throw new ItdConfigError(`плагин «${plugin.name}» несовместим с плагином «${other.name}»`);\n }\n }\n }\n\n const edges = new Map<string, Set<string>>();\n const indegree = new Map<string, number>();\n for (const { plugin } of entries) {\n edges.set(plugin.name, new Set());\n indegree.set(plugin.name, 0);\n }\n for (const { plugin } of entries) {\n for (const required of plugin.requires ?? []) addEdge(required, plugin.name, edges, indegree);\n for (const target of plugin.before ?? []) {\n if (byName.has(target)) addEdge(plugin.name, target, edges, indegree);\n }\n for (const target of plugin.after ?? []) {\n if (byName.has(target)) addEdge(target, plugin.name, edges, indegree);\n }\n }\n\n const ready = entries.filter(({ plugin }) => indegree.get(plugin.name) === 0);\n ready.sort((a, b) => a.sequence - b.sequence);\n const ordered: ClientPlugin[] = [];\n while (ready.length > 0) {\n const current = ready.shift();\n if (!current) break;\n ordered.push(current.plugin);\n for (const target of edges.get(current.plugin.name) ?? []) {\n const next = (indegree.get(target) ?? 0) - 1;\n indegree.set(target, next);\n if (next === 0) {\n const entry = byName.get(target);\n if (entry) {\n ready.push(entry);\n ready.sort((a, b) => a.sequence - b.sequence);\n }\n }\n }\n }\n\n if (ordered.length !== entries.length) {\n const cycle = entries\n .filter(({ plugin }) => (indegree.get(plugin.name) ?? 0) > 0)\n .map(({ plugin }) => plugin.name);\n throw new ItdConfigError(`циклический порядок плагинов: ${cycle.join(' → ')}`);\n }\n return ordered;\n}\n\n/** Проверяет, можно ли удалить плагин без нарушения обязательных зависимостей. @internal */\nexport function assertPluginRemovable(plugins: readonly ClientPlugin[], name: string): void {\n const dependent = plugins.find((plugin) => plugin.requires?.includes(name));\n if (dependent) {\n throw new ItdConfigError(\n `нельзя отключить плагин «${name}»: от него зависит «${dependent.name}»`,\n );\n }\n}\n","import type { OperationRequestOptions } from '../../types/options.js';\nimport { createDeadline, type Deadline, type ItdClock } from '../clock.js';\nimport { ItdConfigError, ItdStateError } from '../errors.js';\nimport { type RegisteredAttemptInterceptor, withAttemptInterceptorScope } from './attempts.js';\nimport type {\n AttemptInterceptor,\n ClientPlugin,\n OperationTransformer,\n PluginApi,\n PluginTeardown,\n} from './contracts.js';\nimport {\n assertPluginRemovable,\n orderPluginDefinitions,\n validatePluginDefinition,\n} from './order.js';\n\ninterface InstalledPlugin {\n plugin: ClientPlugin;\n transformers: OperationTransformer[];\n interceptors: AttemptInterceptor[];\n teardown: PluginTeardown | undefined;\n activeRequests: number;\n drain: Promise<void> | undefined;\n finishDrain: (() => void) | undefined;\n}\n\n/** Что реестру нужно знать об остановке. @internal */\nexport interface PluginRegistryOptions {\n /** Срок ожидания операций плагина при его отключении, мс. `0` — ждать без ограничения. */\n shutdownTimeout: number;\n clock: ItdClock;\n}\n\n/**\n * Список подключённых плагинов и зарегистрированных ими расширений.\n *\n * {@link run} собирает operation transformers вокруг одной логической операции и прикрепляет\n * к ней неизменяемый снимок attempt interceptors. Transport исполняет этот снимок отдельно\n * для каждой сетевой попытки. Порядок плагинов одинаков для обеих цепочек.\n *\n * @internal\n */\nexport class PluginRegistry {\n readonly #entries = new Map<string, InstalledPlugin>();\n readonly #removing = new Set<string>();\n readonly #cleanups = new Set<Promise<void>>();\n readonly #options: PluginRegistryOptions;\n #ordered: InstalledPlugin[] = [];\n\n constructor(options: PluginRegistryOptions) {\n this.#options = options;\n }\n\n /** Сколько плагинов подключено. */\n get size(): number {\n return this.#entries.size;\n }\n\n /** Имена плагинов в фактическом порядке выполнения. */\n names(): string[] {\n return this.#ordered.map(({ plugin }) => plugin.name);\n }\n\n /** Подключён ли плагин с таким именем. */\n has(name: string): boolean {\n return this.#entries.has(name);\n }\n\n /** Проверяет добавление без вызова `install()`. @internal */\n assertCanAdd(plugin: ClientPlugin): void {\n validatePluginDefinition(plugin);\n if (this.#removing.has(plugin.name)) {\n throw new ItdConfigError(\n `плагин «${plugin.name}» ещё отключается; дождитесь завершения unuse() или dispose()`,\n );\n }\n orderPluginDefinitions([...this.#ordered.map((entry) => entry.plugin), plugin]);\n }\n\n /** Проверяет удаление без изменения реестра. @internal */\n assertCanRemove(name: string): void {\n const entry = this.#entries.get(name);\n if (!entry) return;\n assertPluginRemovable(\n this.#ordered.map((current) => current.plugin),\n name,\n );\n }\n\n /**\n * Подключает плагин.\n *\n * `install()` выполняется синхронно. Каждая регистрация принадлежит установившему её\n * плагину и участвует в общем порядке `before`/`after`.\n *\n * @throws {ItdConfigError} если плагин задан неверно, уже подключён или нарушает зависимости\n */\n add(plugin: ClientPlugin, context: Omit<PluginApi, 'operations' | 'attempts'>): void {\n this.assertCanAdd(plugin);\n const ordered = orderPluginDefinitions([...this.#ordered.map((entry) => entry.plugin), plugin]);\n const transformers: OperationTransformer[] = [];\n const interceptors: AttemptInterceptor[] = [];\n const register = <T>(values: T[], value: T, kind: string): (() => void) => {\n if (typeof value !== 'function') {\n throw new ItdConfigError(`плагин «${plugin.name}» передал в ${kind}.use() не функцию`);\n }\n values.push(value);\n let active = true;\n return () => {\n if (!active) return;\n active = false;\n const index = values.indexOf(value);\n if (index >= 0) values.splice(index, 1);\n };\n };\n const installed = plugin.install({\n ...context,\n operations: {\n use: (transformer) => register(transformers, transformer, 'operations'),\n },\n attempts: {\n use: (interceptor) => register(interceptors, interceptor, 'attempts'),\n },\n });\n if (installed !== undefined && typeof installed !== 'function') {\n throw new ItdConfigError(\n `install() плагина «${plugin.name}» должен вернуть функцию или void`,\n );\n }\n const teardown = installed as PluginTeardown | undefined;\n\n this.#entries.set(plugin.name, {\n plugin,\n transformers,\n interceptors,\n teardown,\n activeRequests: 0,\n drain: undefined,\n finishDrain: undefined,\n });\n this.#ordered = ordered.map((definition) => {\n const entry = this.#entries.get(definition.name);\n if (!entry) throw new ItdConfigError(`плагин «${definition.name}» не установлен`);\n return entry;\n });\n }\n\n /**\n * Отключает плагин и вызывает его функцию очистки.\n *\n * Новые запросы перестают видеть расширения плагина сразу. Снимок уже начавшейся операции,\n * включая её будущие retry, остаётся неизменным; очистка дождётся завершения операции,\n * но не дольше отведённого срока.\n *\n * @returns `false`, если такого плагина не было\n * @throws {ItdStateError} если операции плагина не завершились за отведённый срок\n */\n async remove(name: string): Promise<boolean> {\n const entry = this.#entries.get(name);\n if (!entry) return false;\n this.assertCanRemove(name);\n this.#entries.delete(name);\n this.#ordered = this.#ordered.filter((current) => current !== entry);\n this.#removing.add(name);\n\n const deadline = createDeadline(this.#options.shutdownTimeout, this.#options.clock);\n const cleanup = this.#trackCleanup(\n (async () => {\n const expired = await this.#release(entry, deadline);\n if (expired) throw expired;\n })(),\n );\n try {\n await cleanup;\n return true;\n } finally {\n deadline.cancel();\n this.#removing.delete(name);\n }\n }\n\n /**\n * Отключает все плагины окончательно.\n *\n * Очистка идёт изнутри наружу — в порядке, обратном выполнению расширений. Срок ожидания\n * общий на все плагины.\n */\n async dispose(): Promise<void> {\n const entries = [...this.#ordered].reverse();\n const previousCleanups = [...this.#cleanups];\n this.#entries.clear();\n this.#ordered = [];\n for (const { plugin } of entries) this.#removing.add(plugin.name);\n\n const deadline = createDeadline(this.#options.shutdownTimeout, this.#options.clock);\n const cleanup = this.#trackCleanup(\n (async () => {\n const errors: unknown[] = [];\n const previous = await Promise.allSettled(previousCleanups);\n for (const result of previous) {\n if (result.status === 'rejected') errors.push(result.reason);\n }\n for (const entry of entries) {\n try {\n const expired = await this.#release(entry, deadline);\n if (expired) errors.push(expired);\n } catch (error) {\n errors.push(error);\n } finally {\n this.#removing.delete(entry.plugin.name);\n }\n }\n if (errors.length > 0) {\n throw new AggregateError(errors, 'Не удалось освободить ресурсы плагинов');\n }\n })(),\n );\n try {\n await cleanup;\n } finally {\n deadline.cancel();\n }\n }\n\n /**\n * Прогоняет запрос через operation transformers и прикрепляет attempt interceptors.\n *\n * Снимки обеих цепочек берутся в начале: `unuse()` влияет на новые операции, но не меняет\n * уже выполняющуюся и не удаляет interceptors из её последующих retry.\n *\n * Каждый `next` одноразовый: transformer может завершить операцию сам, но не может породить\n * вторую — для `posts.create` это была бы вторая публикация.\n *\n * @param execute выполнение логической операции, вызываемое самым внутренним transformer\n */\n async run(\n request: OperationRequestOptions,\n execute: (request: OperationRequestOptions) => Promise<unknown>,\n ): Promise<unknown> {\n const entries = [...this.#ordered];\n for (const entry of entries) entry.activeRequests += 1;\n const interceptorScope: RegisteredAttemptInterceptor[] = entries.flatMap((entry) =>\n entry.interceptors.map((interceptor) => ({ plugin: entry.plugin.name, interceptor })),\n );\n const scoped = (current: OperationRequestOptions): OperationRequestOptions =>\n withAttemptInterceptorScope(current, interceptorScope);\n const chain = entries\n .flatMap((entry) =>\n entry.transformers.map((transformer) => ({ plugin: entry.plugin.name, transformer })),\n )\n .reduceRight<(request: OperationRequestOptions) => Promise<unknown>>(\n (next, { plugin, transformer }) =>\n (current) => {\n let called = false;\n return transformer(scoped(current), (prepared) => {\n if (called) {\n throw new ItdConfigError(\n `operation transformer плагина «${plugin}» вызвал next() больше одного раза`,\n );\n }\n called = true;\n return next(scoped(prepared));\n });\n },\n (current) => execute(scoped(current)),\n );\n\n try {\n return await chain(scoped(request));\n } finally {\n for (const entry of entries) {\n entry.activeRequests -= 1;\n if (entry.activeRequests === 0) {\n entry.finishDrain?.();\n entry.finishDrain = undefined;\n entry.drain = undefined;\n }\n }\n }\n }\n\n /**\n * Дожидается операций плагина и освобождает его ресурсы.\n *\n * `teardown` выполняется в любом случае, в том числе после истечения срока.\n *\n * @returns ошибка истёкшего срока, если ждать пришлось дольше отведённого\n */\n async #release(entry: InstalledPlugin, deadline: Deadline): Promise<ItdStateError | undefined> {\n const finished = await deadline.wait(this.#waitForDrain(entry));\n await entry.teardown?.();\n\n return finished\n ? undefined\n : new ItdStateError(\n `плагин «${entry.plugin.name}» не завершил операции за ${this.#options.shutdownTimeout} мс; ` +\n 'ожидание прекращено, ресурсы плагина освобождены',\n );\n }\n\n #waitForDrain(entry: InstalledPlugin): Promise<void> {\n if (entry.activeRequests === 0) return Promise.resolve();\n entry.drain ??= new Promise<void>((resolve) => {\n entry.finishDrain = resolve;\n });\n return entry.drain;\n }\n\n #trackCleanup(cleanup: Promise<void>): Promise<void> {\n this.#cleanups.add(cleanup);\n void cleanup.then(\n () => this.#cleanups.delete(cleanup),\n () => this.#cleanups.delete(cleanup),\n );\n return cleanup;\n }\n}\n","import { type ItdClock, systemClock } from './clock.js';\nimport type { ResolvedRateLimitOptions } from './config.js';\nimport { ItdAbortError } from './errors.js';\n\n/** Задача, ожидающая своей очереди. */\ninterface QueuedTask {\n run: () => void;\n /** Снимает задачу, ещё не начавшую выполняться, — используется при остановке очереди. */\n cancel: (reason: unknown) => void;\n}\n\n/** Ошибка отмены запроса, который ещё не дошёл до транспорта. */\nfunction queueAbortError(): ItdAbortError {\n return new ItdAbortError('Запрос отменён во время ожидания очереди');\n}\n\n/**\n * Очередь запросов: ограничивает одновременность и частоту.\n *\n * Нужна прежде всего ботам: без неё цикл по сотне постов уходит в API одним залпом\n * и упирается в `RATE_LIMIT_EXCEEDED`.\n *\n * Частота выдерживается равномерным разносом стартов (`1000 / rps` между запросами),\n * а не окном со счётчиком: так нагрузка ровная, без всплеска в начале каждой секунды.\n *\n * @internal\n */\nexport class RequestQueue {\n readonly #concurrency: number;\n /** Минимальный промежуток между стартами, мс. `0` — без ограничения частоты. */\n readonly #minGap: number;\n\n readonly #waiting: QueuedTask[] = [];\n #active = 0;\n /** Момент, раньше которого следующий запрос стартовать не должен. */\n #nextSlot = 0;\n readonly #clock: ItdClock;\n #cancelTimer: (() => void) | undefined;\n\n constructor(options: ResolvedRateLimitOptions, clock: ItdClock = systemClock) {\n this.#concurrency = options.concurrency;\n this.#minGap = options.rps ? 1000 / options.rps : 0;\n this.#clock = clock;\n }\n\n /** Сколько задач выполняется прямо сейчас. */\n get active(): number {\n return this.#active;\n }\n\n /** Сколько задач ждёт очереди. */\n get pending(): number {\n return this.#waiting.length;\n }\n\n /**\n * Ставит задачу в очередь.\n *\n * @returns результат задачи; ошибка задачи пробрасывается без изменений\n */\n schedule<T>(task: () => Promise<T>, signal?: AbortSignal): Promise<T> {\n if (signal?.aborted) return Promise.reject(queueAbortError());\n\n return new Promise<T>((resolve, reject) => {\n let queued: QueuedTask;\n\n const detach = () => signal?.removeEventListener('abort', onAbort);\n const onAbort = () => {\n const index = this.#waiting.indexOf(queued);\n if (index < 0) return;\n\n this.#waiting.splice(index, 1);\n queued.cancel(queueAbortError());\n this.#drain();\n };\n\n queued = {\n run: () => {\n detach();\n this.#active += 1;\n\n // `task` обычно возвращает промис, но пользовательский middleware может бросить\n // синхронно. Нормализуем оба пути, чтобы слот всегда освободился.\n Promise.resolve()\n .then(task)\n .then(resolve, reject)\n .finally(() => {\n this.#active -= 1;\n this.#drain();\n });\n },\n cancel: (reason) => {\n detach();\n reject(reason);\n },\n };\n\n this.#waiting.push(queued);\n signal?.addEventListener('abort', onAbort, { once: true });\n\n // Защищает и от нестандартной реализации AbortSignal, которая могла перейти\n // в aborted между первой проверкой и установкой обработчика.\n if (signal?.aborted) onAbort();\n else this.#drain();\n });\n }\n\n /**\n * Останавливает очередь: снимает отложенную паузу и отклоняет ещё не начатые задачи\n * ошибкой `ItdAbortError`. Уже выполняющиеся задачи доводятся до конца.\n */\n stop(): void {\n if (this.#cancelTimer) {\n this.#cancelTimer();\n this.#cancelTimer = undefined;\n }\n this.#nextSlot = 0;\n\n const pending = this.#waiting.splice(0, this.#waiting.length);\n for (const task of pending) {\n task.cancel(new ItdAbortError('Клиент закрыт, запрос отменён'));\n }\n }\n\n /**\n * Придерживает всю очередь на заданное время.\n *\n * Вызывается при получении `429` с заголовком `Retry-After`: тормозить нужно все запросы,\n * а не только тот, который наткнулся на лимит, — иначе остальные продолжат добивать API.\n */\n pause(ms: number): void {\n if (ms <= 0) return;\n this.#nextSlot = Math.max(this.#nextSlot, this.#clock.now() + ms);\n }\n\n /** Запускает столько ожидающих задач, сколько позволяют ограничения. */\n #drain(): void {\n if (this.#waiting.length === 0) {\n // Последний ожидающий запрос мог быть отменён во время длинной паузы. Таймер больше\n // не нужен и не должен удерживать event loop процесса.\n if (this.#cancelTimer) {\n this.#cancelTimer();\n this.#cancelTimer = undefined;\n }\n return;\n }\n if (this.#active >= this.#concurrency) return;\n // Ждём уже запланированного пробуждения, чтобы не плодить таймеры.\n if (this.#cancelTimer) return;\n\n const now = this.#clock.now();\n\n if (this.#nextSlot > now) {\n this.#cancelTimer = this.#clock.schedule(() => {\n this.#cancelTimer = undefined;\n this.#drain();\n }, this.#nextSlot - now);\n return;\n }\n\n const next = this.#waiting.shift();\n if (!next) return;\n\n if (this.#minGap > 0) this.#nextSlot = now + this.#minGap;\n\n next.run();\n\n // Следующая задача может стартовать сразу, если позволяет конкурентность.\n this.#drain();\n }\n}\n\n/**\n * Очереди по конечным направлениям запросов.\n *\n * Ключом обычно служит origin уже разрешённого URL. Поэтому разные локальные имена одного\n * хоста разделяют лимит, а запрос с разовым внешним `baseUrl` не попадает в основную очередь.\n *\n * @internal\n */\nexport class RequestQueuePool {\n readonly #options: ResolvedRateLimitOptions;\n readonly #clock: ItdClock;\n readonly #main: RequestQueue;\n /** Очереди направлений заводятся при первом запросе. */\n readonly #byDestination = new Map<string, RequestQueue>();\n\n constructor(options: ResolvedRateLimitOptions, clock: ItdClock = systemClock) {\n this.#options = options;\n this.#clock = clock;\n this.#main = new RequestQueue(options, clock);\n }\n\n /** Очередь направления. `undefined` сохраняет основную очередь для внутренних клиентов. */\n for(destination: string | undefined): RequestQueue {\n if (destination === undefined) return this.#main;\n\n let queue = this.#byDestination.get(destination);\n if (!queue) {\n queue = new RequestQueue(this.#options, this.#clock);\n this.#byDestination.set(destination, queue);\n }\n return queue;\n }\n\n /** Останавливает все очереди. */\n stop(): void {\n this.#main.stop();\n for (const queue of this.#byDestination.values()) queue.stop();\n this.#byDestination.clear();\n }\n}\n","import { ItdConfigError } from './errors.js';\nimport { hostOf, isSameSite, normalizeBaseUrl } from './url.js';\n\n/** Сервис платформы на отдельном домене. */\nexport interface ServiceDefinition {\n /** Имя, по которому запрос выбирает сервис: `{ service: 'status' }`. */\n name: string;\n /** Базовый URL сервиса. */\n baseUrl: string;\n /** Заголовки, добавляемые к каждому запросу сервиса. Заголовки вызова важнее. */\n headers?: Record<string, string> | undefined;\n /**\n * Слать ли заголовок авторизации.\n *\n * По умолчанию включено для основного хоста и его поддоменов. Для остальных хостов\n * авторизацию нужно разрешить явно: `auth: true`.\n */\n auth?: boolean | undefined;\n}\n\n/**\n * Накладывает пользовательское определение на встроенное.\n *\n * `auth` и `headers` наследуются от встроенного, пока ключ не задан явно: авторизация\n * относится к сервису, а не к адресу. Явный `undefined` возвращает вывод по хосту.\n *\n * @internal\n */\nexport function mergeService(\n builtIn: ServiceDefinition,\n override: ServiceDefinition,\n): ServiceDefinition {\n const auth = 'auth' in override ? override.auth : builtIn.auth;\n const headers = 'headers' in override ? override.headers : builtIn.headers;\n\n return {\n name: builtIn.name,\n baseUrl: override.baseUrl,\n ...(headers === undefined ? {} : { headers }),\n ...(auth === undefined ? {} : { auth }),\n };\n}\n\n/**\n * Именованные сервисы клиента.\n *\n * @internal\n */\nexport class ServiceRegistry {\n readonly #services = new Map<string, ServiceDefinition>();\n /** Хост основного API. */\n readonly #primaryHost: string;\n\n /** @param primaryBaseUrl базовый URL клиента */\n constructor(primaryBaseUrl?: string) {\n this.#primaryHost = primaryBaseUrl ? hostOf(primaryBaseUrl) : '';\n }\n\n /**\n * Регистрирует сервис. Имя очищается от краевых пробелов, базовый URL приводится\n * к каноничному виду, а незаданный `auth` выводится из хоста.\n *\n * @throws {ItdConfigError} если имя пустое, имя занято или `baseUrl` не абсолютный URL\n */\n define(definition: ServiceDefinition): void {\n const raw = definition?.name;\n if (typeof raw !== 'string' || raw.trim() === '') {\n throw new ItdConfigError('У сервиса должно быть непустое имя');\n }\n\n const name = raw.trim();\n if (this.#services.has(name)) {\n throw new ItdConfigError(`Сервис «${name}» уже зарегистрирован и не может быть заменён`);\n }\n\n const baseUrl = normalizeBaseUrl(definition.baseUrl);\n if (definition.auth !== undefined && typeof definition.auth !== 'boolean') {\n throw new ItdConfigError(`services.${name}.auth должен быть boolean`);\n }\n\n let headers: Readonly<Record<string, string>> | undefined;\n if (definition.headers !== undefined) {\n if (\n typeof definition.headers !== 'object' ||\n definition.headers === null ||\n Array.isArray(definition.headers)\n ) {\n throw new ItdConfigError(`services.${name}.headers должен быть объектом строк`);\n }\n for (const [header, value] of Object.entries(definition.headers)) {\n if (typeof value !== 'string') {\n throw new ItdConfigError(`services.${name}.headers.${header} должен быть строкой`);\n }\n }\n headers = Object.freeze({ ...definition.headers });\n }\n\n const normalized = {\n ...definition,\n name,\n baseUrl,\n auth: definition.auth ?? isSameSite(this.#primaryHost, hostOf(baseUrl)),\n ...(headers ? { headers } : {}),\n } satisfies ServiceDefinition;\n\n this.#services.set(name, Object.freeze(normalized));\n }\n\n /** Определение сервиса либо `undefined`, если такого нет. */\n get(name: string): ServiceDefinition | undefined {\n return this.#services.get(name);\n }\n\n /** Зарегистрирован ли сервис с таким именем. */\n has(name: string): boolean {\n return this.#services.has(name);\n }\n\n /**\n * Определение сервиса.\n *\n * @throws {ItdConfigError} если сервис не зарегистрирован\n */\n require(name: string): ServiceDefinition {\n const service = this.#services.get(name);\n if (!service) {\n const known = [...this.#services.keys()];\n throw new ItdConfigError(\n `Сервис «${name}» не зарегистрирован. ` +\n (known.length > 0\n ? `Известны: ${known.join(', ')}`\n : 'Зарегистрируйте его через itd.defineService() или опцию services'),\n );\n }\n return service;\n }\n\n /**\n * Базовый URL сервиса.\n *\n * @throws {ItdConfigError} если сервис не зарегистрирован\n */\n resolveBaseUrl(name: string): string {\n return this.require(name).baseUrl;\n }\n\n /**\n * Принадлежит ли URL основному хосту клиента или его поддомену.\n *\n * Используется для безопасного значения по умолчанию у разового `baseUrl`: Bearer-токен\n * не должен уходить на посторонний хост без явного `skipAuth: false`.\n *\n * @internal\n */\n isPrimarySite(baseUrl: string): boolean {\n return isSameSite(this.#primaryHost, hostOf(baseUrl));\n }\n}\n","import { isBlob } from './runtime.js';\n\n/** Заголовки, значение которых нельзя писать в лог целиком. */\nconst SECRET_HEADERS = new Set(['authorization', 'cookie', 'set-cookie', 'x-api-key']);\n\n/** Поля тела запроса, значение которых нельзя писать в лог. */\nconst SECRET_FIELDS = new Set([\n 'password',\n 'oldpassword',\n 'newpassword',\n 'accesstoken',\n 'refreshtoken',\n 'currentpassword',\n 'flowtoken',\n 'token',\n 'turnstiletoken',\n 'otp',\n]);\n\n/** Query-параметры, содержащие секреты. */\nconst SECRET_QUERY_PARAMS = new Set(['token', 'access_token']);\n\n/**\n * Прячет середину секрета, оставляя концы для сопоставления.\n *\n * @example\n * ```ts\n * maskSecret('eyJhbGciOiJIUzI1NiJ9.abc'); // 'eyJh…(24)…abc'\n * ```\n */\nexport function maskSecret(value: string): string {\n if (value.length <= 8) return '…';\n return `${value.slice(0, 4)}…(${value.length})…${value.slice(-3)}`;\n}\n\n/** Маскирует секретные query-параметры URL перед записью в логи и ошибки. */\nexport function redactUrl(url: string): string {\n try {\n const parsed = new URL(url);\n const entries = [...parsed.searchParams.entries()];\n parsed.search = '';\n for (const [name, value] of entries) {\n parsed.searchParams.append(\n name,\n SECRET_QUERY_PARAMS.has(name.toLowerCase()) ? maskSecret(value) : value,\n );\n }\n return parsed.toString();\n } catch {\n return url.replace(/([?&](?:token|access_token)=)[^&#]*/gi, '$1[скрыто]');\n }\n}\n\n/**\n * Готовит заголовки к записи в лог.\n *\n * `Authorization` и `Cookie` маскируются: логи часто уезжают в системы сбора,\n * и токен доступа в них попадать не должен.\n */\nexport function redactHeaders(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n\n headers.forEach((value, name) => {\n if (SECRET_HEADERS.has(name.toLowerCase())) {\n // У Authorization маскируем только сам токен, схему («Bearer») оставляем видимой.\n const spaceAt = value.indexOf(' ');\n result[name] =\n spaceAt > 0\n ? `${value.slice(0, spaceAt)} ${maskSecret(value.slice(spaceAt + 1))}`\n : maskSecret(value);\n return;\n }\n result[name] = value;\n });\n\n return result;\n}\n\n/**\n * Готовит тело запроса к записи в лог: пароли, токены и коды OTP заменяются заглушкой.\n *\n * Обходит вложенные объекты и массивы. `FormData` и бинарные тела не раскрываются вовсе.\n */\nexport function redactBody(body: unknown): unknown {\n if (body === null || body === undefined) return body;\n\n if (typeof FormData !== 'undefined' && body instanceof FormData) return '[FormData]';\n if (isBlob(body)) return '[Blob]';\n if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return '[binary]';\n\n if (Array.isArray(body)) return body.map(redactBody);\n\n if (typeof body === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(body)) {\n result[key] = SECRET_FIELDS.has(key.toLowerCase()) ? '[скрыто]' : redactBody(value);\n }\n return result;\n }\n\n return body;\n}\n","/**\n * Снимает внешнюю обёртку `{ data: … }`.\n *\n * Это единственное преобразование ответа, которое делает транспортный слой. Формы ответов у\n * итд.com непоследовательны: одни эндпоинты заворачивают полезную нагрузку в `data`, другие\n * отдают её напрямую (`GET /api/notifications/` → `{ notifications, hasMore }`), третьи кладут\n * список в поле по имени сущности. Угадывать имя поля опасно, поэтому его разбирает каждый\n * метод ресурса явно, а здесь снимается только однозначная обёртка.\n *\n * Обёртка считается обёрткой, лишь когда `data` — **единственный** ключ объекта. Ответ\n * `{ data: …, meta: … }` вернётся как есть.\n *\n * @example\n * ```ts\n * unwrapData({ data: { posts: [] } }); // { posts: [] }\n * unwrapData({ notifications: [], hasMore }); // без изменений\n * unwrapData({ data: [], hasMore: true }); // без изменений — ключей больше одного\n * ```\n */\nexport function unwrapData(body: unknown): unknown {\n if (typeof body !== 'object' || body === null || Array.isArray(body)) return body;\n\n const keys = Object.keys(body);\n if (keys.length !== 1 || keys[0] !== 'data') return body;\n\n return (body as { data: unknown }).data;\n}\n\n/**\n * Обычный объект — не `null`, не массив.\n *\n * Живёт здесь, а не в каждом разборщике ответа: одна и та же проверка нужна и фабрике\n * ошибок, и приведению уведомлений.\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Непустая строка либо `undefined`. Отличается от {@link pickString} тем, что берёт значение, а не поле. */\nexport function asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/**\n * Достаёт поле-список из ответа, уже прошедшего {@link unwrapData}.\n *\n * Если поля нет или оно не массив, возвращается пустой массив: сервер иногда опускает\n * пустые коллекции, и падать из-за этого библиотека не должна.\n */\nexport function pickArray<T>(source: unknown, field: string): T[] {\n if (typeof source !== 'object' || source === null) return [];\n const value = (source as Record<string, unknown>)[field];\n return Array.isArray(value) ? (value as T[]) : [];\n}\n\n/** Достаёт объект по имени поля. Возвращает `undefined`, если это не объект. */\nexport function pickObject(source: unknown, field: string): Record<string, unknown> | undefined {\n if (typeof source !== 'object' || source === null) return undefined;\n const value = (source as Record<string, unknown>)[field];\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\n/** Достаёт булево поле. Возвращает `fallback`, если поля нет или тип не тот. */\nexport function pickBoolean(source: unknown, field: string, fallback = false): boolean {\n if (typeof source !== 'object' || source === null) return fallback;\n const value = (source as Record<string, unknown>)[field];\n return typeof value === 'boolean' ? value : fallback;\n}\n\n/** Достаёт числовое поле. Возвращает `fallback`, если поля нет или значение не конечное число. */\nexport function pickNumber(source: unknown, field: string, fallback: number): number {\n if (typeof source !== 'object' || source === null) return fallback;\n const value = (source as Record<string, unknown>)[field];\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n\n/** Достаёт строковое поле. Возвращает `undefined`, если поля нет, оно пустое или не строка. */\nexport function pickString(source: unknown, field: string): string | undefined {\n if (typeof source !== 'object' || source === null) return undefined;\n const value = (source as Record<string, unknown>)[field];\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n","import type { ItdErrorCode } from '../types/enums.js';\nimport {\n type ItdApiError,\n ItdApiError as ItdApiErrorClass,\n ItdAuthError,\n ItdConflictError,\n type ItdFieldErrors,\n ItdForbiddenError,\n ItdNotFoundError,\n ItdPhoneVerificationError,\n ItdRateLimitError,\n ItdServerError,\n ItdValidationError,\n} from './errors.js';\nimport { redactBody } from './redact.js';\nimport { asString, isRecord } from './unwrap.js';\n\n/** Разобранное тело ошибки, приведённое к одной форме. */\nexport interface ParsedErrorBody {\n code: ItdErrorCode;\n message: string;\n detail: string | undefined;\n title: string | undefined;\n fieldErrors: ItdFieldErrors;\n /** Идентификатор пользователя, если сервер его вернул (нужен для ссылки подтверждения телефона). */\n userId: string | undefined;\n}\n\n/**\n * Сводит ошибки по полям к единой форме `{ поле: [сообщения] }`.\n *\n * Понимает две документированные формы:\n * - `errors: { email: ['…'] }` либо `errors: { email: '…' }`\n * - `violations: [{ field: 'email', message: '…' }]`\n */\nfunction collectFieldErrors(source: Record<string, unknown>): ItdFieldErrors {\n const result: ItdFieldErrors = {};\n\n const errors = source.errors;\n if (isRecord(errors)) {\n for (const [field, value] of Object.entries(errors)) {\n if (Array.isArray(value)) {\n const messages = value.filter((item): item is string => typeof item === 'string');\n if (messages.length > 0) result[field] = messages;\n } else if (typeof value === 'string') {\n result[field] = [value];\n }\n }\n }\n\n const violations = source.violations;\n if (Array.isArray(violations)) {\n for (const violation of violations) {\n if (!isRecord(violation)) continue;\n const field = asString(violation.field) ?? asString(violation.property);\n const message = asString(violation.message);\n if (!field || !message) continue;\n const existing = result[field];\n if (existing) existing.push(message);\n else result[field] = [message];\n }\n }\n\n return result;\n}\n\n/**\n * Разбирает тело ответа с ошибкой.\n *\n * API отдаёт ошибки в двух формах — с обёрткой `{ error: … }` и без неё, — а при сбое на\n * уровне прокси может вернуть вообще не JSON. Все три случая сводятся к одной структуре.\n *\n * @param body тело ответа: разобранный JSON, строка или `undefined`\n * @param status HTTP-статус, используется для сообщения по умолчанию\n * @param statusText текст статуса из ответа\n */\nexport function parseErrorBody(body: unknown, status: number, statusText = ''): ParsedErrorBody {\n const fallbackMessage = statusText ? `HTTP ${status} ${statusText}` : `HTTP ${status}`;\n\n // Не-JSON: HTML от прокси, простой текст, пустое тело.\n if (typeof body === 'string') {\n return {\n code: 'UNKNOWN_ERROR',\n message: asString(body.trim()) ?? fallbackMessage,\n detail: undefined,\n title: undefined,\n fieldErrors: {},\n userId: undefined,\n };\n }\n\n if (!isRecord(body)) {\n return {\n code: 'UNKNOWN_ERROR',\n message: fallbackMessage,\n detail: undefined,\n title: undefined,\n fieldErrors: {},\n userId: undefined,\n };\n }\n\n // Ответ вида `{ type: \"validation\", on: \"query\", found: {…} }` — так сервер сообщает\n // о непрошедшей проверке параметров. Ни кода, ни текста в нём нет.\n if (body.type === 'validation') {\n const target = asString(body.on);\n\n return {\n code: 'VALIDATION_ERROR',\n message: target\n ? `Проверка не пройдена: некорректные данные в «${target}»`\n : 'Проверка входных данных не пройдена',\n detail: undefined,\n title: undefined,\n fieldErrors: {},\n userId: undefined,\n };\n }\n\n // Форма `{ error: { code, message, … } }` — работаем с вложенным объектом,\n // но поля верхнего уровня остаются запасным источником.\n const inner = isRecord(body.error) ? body.error : body;\n\n const message =\n asString(inner.message) ??\n asString(inner.detail) ??\n asString(inner.title) ??\n // `{ \"error\": \"Invalid token\" }` — так отвечает сервер на недействительный токен.\n asString(body.error) ??\n fallbackMessage;\n\n return {\n code: (asString(inner.code) ?? asString(body.code) ?? 'UNKNOWN_ERROR') as ItdErrorCode,\n message,\n detail: asString(inner.detail),\n title: asString(inner.title),\n fieldErrors: { ...collectFieldErrors(body), ...collectFieldErrors(inner) },\n userId: asString(inner.userId) ?? asString(body.userId),\n };\n}\n\n/**\n * Переводит заголовок `Retry-After` в миллисекунды.\n *\n * Поддерживает обе формы из спецификации HTTP: число секунд и дату.\n * Возвращает `undefined`, если заголовка нет или он не разбирается.\n */\nexport function parseRetryAfter(\n header: string | null | undefined,\n now = Date.now(),\n): number | undefined {\n if (!header) return undefined;\n\n const seconds = Number(header);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n\n const date = Date.parse(header);\n if (Number.isFinite(date)) return Math.max(0, date - now);\n\n return undefined;\n}\n\n/** Читает целое число из заголовка. */\nfunction readIntHeader(headers: Headers | undefined, name: string): number | undefined {\n const raw = headers?.get(name);\n if (raw === null || raw === undefined) return undefined;\n\n const value = Number.parseInt(raw, 10);\n return Number.isFinite(value) ? value : undefined;\n}\n\n/**\n * Читает сведения об ограничении частоты.\n *\n * Сервер сообщает размер окна и остаток, но **не сообщает время сброса** — поэтому\n * точный момент, когда можно повторить, из ответа не вывести.\n */\nexport function readRateLimit(headers: Headers | undefined): {\n limit: number | undefined;\n remaining: number | undefined;\n} {\n return {\n limit: readIntHeader(headers, 'x-ratelimit-limit'),\n remaining: readIntHeader(headers, 'x-ratelimit-remaining'),\n };\n}\n\nconst REQUEST_ID_HEADERS = ['x-request-id', 'x-requestid', 'request-id', 'x-correlation-id'];\n\n/** Достаёт идентификатор запроса из заголовков ответа — пригодится при обращении в поддержку. */\nexport function getRequestId(headers: Headers | undefined): string | undefined {\n if (!headers) return undefined;\n for (const name of REQUEST_ID_HEADERS) {\n const value = headers.get(name);\n if (value) return value;\n }\n return undefined;\n}\n\n/** Соответствие кода ошибки конкретному классу. Приоритетнее, чем HTTP-статус. */\nconst CODE_TO_CLASS: Record<string, new (init: never) => ItdApiError> = {\n VALIDATION_ERROR: ItdValidationError,\n RATE_LIMIT_EXCEEDED: ItdRateLimitError,\n UNAUTHORIZED: ItdAuthError,\n SESSION_EXPIRED: ItdAuthError,\n SESSION_REVOKED: ItdAuthError,\n SESSION_INVALID_REFRESH_TOKEN: ItdAuthError,\n // Оба приходят с `/auth/refresh`: первый — когда cookie refresh_token не долетела,\n // второй — когда она есть, но сессия за ней уже мертва.\n REFRESH_TOKEN_MISSING: ItdAuthError,\n SESSION_NOT_FOUND: ItdAuthError,\n ACCOUNT_INVALID_CREDENTIALS: ItdAuthError,\n ACCESS_DENIED: ItdForbiddenError,\n ENTITY_NOT_FOUND: ItdNotFoundError,\n // Сервер отвечает именно так: `{ error: { code: 'NOT_FOUND', message: 'Post not found' } }`.\n NOT_FOUND: ItdNotFoundError,\n ENTITY_ALREADY_EXISTS: ItdConflictError,\n};\n\n/** Соответствие HTTP-статуса классу — запасной вариант, когда код ничего не говорит. */\nfunction classByStatus(status: number): new (init: never) => ItdApiError {\n if (status === 401) return ItdAuthError;\n if (status === 403) return ItdForbiddenError;\n if (status === 404) return ItdNotFoundError;\n if (status === 409) return ItdConflictError;\n if (status === 422) return ItdValidationError;\n if (status === 429) return ItdRateLimitError;\n if (status >= 500) return ItdServerError;\n return ItdApiErrorClass;\n}\n\n/**\n * Готовит тело ответа к сохранению в `ItdApiError.raw`.\n *\n * При `422` сервер возвращает присланное тело эхом: `{ type: 'validation', on: 'body',\n * found: { email: '…', password: '…' } }`. Ошибка обычно доезжает до логов и систем сбора\n * вроде Sentry — пароль в ней оказаться не должен. Имена полей для диагностики остаются,\n * значения секретов заменяются заглушкой.\n */\nfunction safeRawBody(body: unknown): unknown {\n if (!isRecord(body) || body.type !== 'validation' || !isRecord(body.found)) return body;\n\n return { ...body, found: redactBody(body.found) };\n}\n\n/** Что нужно знать о запросе, чтобы построить ошибку. */\nexport interface ErrorContext {\n method: string;\n path: string;\n status: number;\n /** Текущее время в миллисекундах с начала эпохи Unix. */\n now?: number | undefined;\n statusText?: string | undefined;\n headers?: Headers | undefined;\n response?: Response | undefined;\n /** Тело ответа: разобранный JSON либо строка. */\n body: unknown;\n}\n\n/**\n * Строит типизированную ошибку из ответа сервера.\n *\n * Класс выбирается сначала по коду ошибки, затем по HTTP-статусу — так `VALIDATION_ERROR`\n * со статусом `400` всё равно станет {@link ItdValidationError}.\n */\nexport function createApiError(context: ErrorContext): ItdApiError {\n const parsed = parseErrorBody(context.body, context.status, context.statusText);\n const rateLimit = readRateLimit(context.headers);\n\n const init = {\n rateLimit: rateLimit.limit,\n rateLimitRemaining: rateLimit.remaining,\n status: context.status,\n code: parsed.code,\n message: parsed.message,\n detail: parsed.detail,\n title: parsed.title,\n fieldErrors: parsed.fieldErrors,\n requestId: getRequestId(context.headers),\n method: context.method,\n path: context.path,\n raw: safeRawBody(context.body),\n response: context.response,\n retryAfter: parseRetryAfter(context.headers?.get('retry-after'), context.now),\n };\n\n if (parsed.code === 'PHONE_VERIFICATION_REQUIRED') {\n return new ItdPhoneVerificationError({ ...init, userId: parsed.userId });\n }\n\n const Ctor = CODE_TO_CLASS[parsed.code] ?? classByStatus(context.status);\n return new Ctor(init as never);\n}\n","import type { ClientHooks, Logger } from '../types/options.js';\nimport type { ItdClock } from './clock.js';\nimport type { CookieJar } from './cookies.js';\nimport { createApiError, readRateLimit } from './error-factory.js';\nimport {\n ItdAbortError,\n ItdConfigError,\n ItdError,\n ItdNetworkError,\n ItdTimeoutError,\n} from './errors.js';\nimport {\n identifyRequest,\n type PipelineRequest,\n type PipelineRequestInput,\n type PreparedRequestBody,\n} from './pipeline.js';\nimport { runAttemptInterceptors } from './plugins/attempts.js';\nimport { dispatchRequestHook, hasRequestHook } from './plugins/hooks.js';\nimport { redactBody, redactHeaders } from './redact.js';\nimport { isBlob } from './runtime.js';\nimport { unwrapData } from './unwrap.js';\nimport { buildQuery, joinUrl } from './url.js';\n\n/**\n * Настройки, нужные транспорту.\n *\n * Узкий срез общей конфигурации: авторизация, хранилище, повторы и очередь транспорта\n * не касаются, и он не должен иметь к ним доступ.\n */\nexport interface TransportConfig {\n baseUrl: string;\n fetch: typeof fetch;\n timeout: number;\n clock: ItdClock;\n headers: Record<string, string>;\n /** Значение заголовка `User-Agent`. `undefined` — заголовок не выставляется. */\n userAgent: string | undefined;\n useCookieJar: boolean;\n sendCredentials: boolean;\n hooks: ClientHooks;\n logger: Logger | undefined;\n}\n\n/**\n * Внешние части, которыми пользуется транспорт.\n *\n * Все обязательны к передаче и все могут отсутствовать по существу: cookie-jar не нужен\n * в браузере, идентификатор устройства — при выключенной авторизации, сведения об ограничении\n * частоты интересны только тогда, когда есть очередь, а сигнал жизни — только у клиента.\n */\nexport interface TransportDeps {\n /** Хранилище cookie. `undefined` — cookie ведёт сама среда. */\n cookies: CookieJar | undefined;\n /**\n * Идентификатор устройства для заголовка `X-Device-Id`. Отправляется на всех запросах,\n * включая анонимные (`sign-in`).\n */\n getDeviceId: (() => Promise<string>) | undefined;\n /**\n * Сообщает об остатке лимита из заголовков ответа.\n *\n * Вызывается после **каждого** ответа, включая ошибочные, — так очередь узнаёт\n * об исчерпании лимита заранее и успевает притормозить до отказа сервера.\n */\n onRateLimit:\n | ((limit: number | undefined, remaining: number | undefined, request: PipelineRequest) => void)\n | undefined;\n /** Сигнал времени жизни владельца: `dispose()` клиента отменяет начатые запросы. */\n lifetimeSignal: AbortSignal | undefined;\n}\n\n/** Ставит заголовок, превращая ошибку среды в понятную ошибку конфигурации. */\nfunction setHeader(headers: Headers, name: string, value: string): void {\n try {\n headers.set(name, value);\n } catch (cause) {\n throw new ItdConfigError(\n `Некорректный HTTP-заголовок ${JSON.stringify(name)}: проверьте его имя и значение.`,\n { cause },\n );\n }\n}\n\n/** Тело, которое отправляется как есть, без сериализации в JSON. */\nfunction isRawBody(body: unknown): body is BodyInit {\n if (typeof body !== 'object' || body === null) return typeof body === 'string';\n return (\n (typeof FormData !== 'undefined' && body instanceof FormData) ||\n isBlob(body) ||\n (typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams) ||\n (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) ||\n body instanceof ArrayBuffer ||\n ArrayBuffer.isView(body)\n );\n}\n\n/**\n * Читает тело ответа один раз.\n *\n * Ответ можно прочитать только однократно, а тело нужно и при успехе, и при ошибке,\n * поэтому чтение происходит здесь, до ветвления по статусу.\n */\nasync function readBody(response: Response): Promise<unknown> {\n if (response.status === 204 || response.status === 205) return undefined;\n if (response.headers.get('content-length') === '0') return undefined;\n\n const contentType = response.headers.get('content-type') ?? '';\n\n if (contentType.includes('json')) {\n const text = await response.text();\n if (text === '') return undefined;\n try {\n return JSON.parse(text) as unknown;\n } catch {\n // Заголовок обещал JSON, но тело им не является — отдаём как текст,\n // чтобы сообщение об ошибке осталось информативным.\n return text;\n }\n }\n\n const text = await response.text();\n return text === '' ? undefined : text;\n}\n\n/** Ошибка отмены в формате `fetch`. */\nfunction createAbortError(): Error {\n const error = new Error('Операция прервана');\n error.name = 'AbortError';\n return error;\n}\n\n/** Прерывает ожидание промиса при срабатывании сигнала. */\nfunction abortable<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) {\n // Результат больше не нужен, но позднее отклонение исходного промиса не должно стать\n // необработанным. Ошибку отмены возвращаем сразу: готовый промис не может её опередить.\n void promise.catch(() => {});\n return Promise.reject(createAbortError());\n }\n\n let onAbort: (() => void) | undefined;\n\n const interrupted = new Promise<never>((_resolve, reject) => {\n onAbort = () => reject(createAbortError());\n signal.addEventListener('abort', onAbort, { once: true });\n });\n\n return Promise.race([promise, interrupted]).finally(() => {\n if (onAbort) signal.removeEventListener('abort', onAbort);\n });\n}\n\n/** Результат объединения отмены запроса, отмены клиента и таймаута. */\ninterface AbortBundle {\n signal: AbortSignal;\n /** Сработал ли именно таймаут — от этого зависит класс ошибки. */\n timedOut: () => boolean;\n cleanup: () => void;\n}\n\n/**\n * Объединяет пользовательский `AbortSignal`, сигнал жизни клиента и таймаут.\n *\n * Реализовано вручную, а не через `AbortSignal.any`: последний появился только в Node 20,\n * а библиотека поддерживает Node 18. Причина отмены переносится от источника как есть.\n */\nfunction createAbortBundle(\n userSignal: AbortSignal | undefined,\n lifetimeSignal: AbortSignal | undefined,\n timeout: number,\n clock: ItdClock,\n): AbortBundle {\n const controller = new AbortController();\n let timedOut = false;\n\n const link = (source: AbortSignal | undefined): (() => void) | undefined => {\n if (!source) return undefined;\n if (source.aborted) {\n controller.abort(source.reason);\n return undefined;\n }\n\n const onAbort = () => controller.abort(source.reason);\n source.addEventListener('abort', onAbort, { once: true });\n return () => source.removeEventListener('abort', onAbort);\n };\n\n const unlink = [link(userSignal), link(lifetimeSignal)];\n\n const cancelTimer =\n timeout > 0\n ? clock.schedule(() => {\n timedOut = true;\n controller.abort();\n }, timeout)\n : undefined;\n\n return {\n signal: controller.signal,\n timedOut: () => timedOut,\n cleanup: () => {\n cancelTimer?.();\n for (const detach of unlink) detach?.();\n },\n };\n}\n\n/**\n * Единственное место, откуда библиотека ходит в сеть.\n *\n * Отвечает за сборку URL, заголовки, cookie, таймауты, разбор ответа и превращение любой\n * неудачи в типизированную ошибку. Авторизация, повторы, очередь и плагины — отдельные\n * слои конвейера, и транспорт о них не знает.\n */\nexport class Transport {\n readonly #config: TransportConfig;\n readonly #deps: TransportDeps;\n\n constructor(config: TransportConfig, deps: TransportDeps) {\n this.#config = config;\n this.#deps = deps;\n }\n\n /** Базовый URL, к которому обращается транспорт. */\n get baseUrl(): string {\n return this.#config.baseUrl;\n }\n\n /**\n * Выполняет один сетевой запрос.\n *\n * @throws {ItdApiError} если сервер ответил статусом ≥ 400\n * @throws {ItdTimeoutError} если истёк таймаут\n * @throws {ItdAbortError} если запрос отменён через `signal`\n * @throws {ItdNetworkError} если запрос не дошёл до сервера\n */\n send = async (input: PipelineRequestInput): Promise<unknown> => {\n const request = identifyRequest(input);\n const method = request.method.toUpperCase();\n const url = this.buildUrl(request);\n const headers = await this.#buildHeaders(request, url);\n const attempt = request.attempt ?? 1;\n\n const timeout = request.timeout ?? this.#config.timeout;\n const abort = createAbortBundle(\n request.signal,\n this.#deps.lifetimeSignal,\n timeout,\n this.#config.clock,\n );\n const startedAt = this.#config.clock.now();\n let cleanupBody: (() => void | Promise<void>) | undefined;\n\n try {\n let body: BodyInit | undefined;\n try {\n const prepared = await this.#prepareBody(request, headers, abort.signal, attempt);\n body = prepared.body;\n cleanupBody = prepared.cleanup;\n } catch (error) {\n const failure = this.#toTransportError(error, abort, request, method, timeout);\n const context = {\n operationId: request.operationId,\n method,\n path: request.path,\n url,\n headers,\n attempt,\n };\n await dispatchRequestHook(this.#config.hooks, 'onError', {\n ...context,\n duration: this.#config.clock.now() - startedAt,\n error: failure,\n });\n throw failure;\n }\n\n const context = {\n operationId: request.operationId,\n method,\n path: request.path,\n url,\n headers,\n attempt,\n };\n await dispatchRequestHook(this.#config.hooks, 'onRequest', context);\n\n this.#config.logger?.debug(`→ ${method} ${request.path}`, {\n headers: redactHeaders(headers),\n body: request.bodyFactory ? '[повторяемое тело]' : redactBody(request.body),\n });\n\n let response: Response;\n try {\n const init: RequestInit & { duplex?: 'half' } = {\n method,\n headers,\n signal: abort.signal,\n ...(body !== undefined ? { body } : {}),\n ...(this.#config.sendCredentials ? { credentials: 'include' as const } : {}),\n };\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n init.duplex = 'half';\n }\n response = await runAttemptInterceptors(\n request,\n { ...context, body, signal: abort.signal },\n async () => {\n try {\n return await this.#config.fetch(url, init);\n } catch (error) {\n throw this.#toTransportError(error, abort, request, method, timeout);\n }\n },\n );\n } catch (error) {\n const duration = this.#config.clock.now() - startedAt;\n\n await dispatchRequestHook(this.#config.hooks, 'onError', { ...context, duration, error });\n this.#config.logger?.warn(\n `× ${method} ${request.path} (${duration} мс): ${error instanceof Error ? error.message : String(error)}`,\n );\n\n throw error;\n }\n\n if (this.#deps.onRateLimit) {\n const { limit, remaining } = readRateLimit(response.headers);\n this.#deps.onRateLimit(limit, remaining, request);\n }\n\n if (this.#config.useCookieJar) this.#deps.cookies?.setFromResponse(url, response);\n\n // Хук получает собственную ветвь тела: чтение ответа внутри хука не должно лишать\n // транспорт возможности разобрать основной ответ.\n if (response.ok && hasRequestHook(this.#config.hooks, 'onResponse')) {\n await dispatchRequestHook(this.#config.hooks, 'onResponse', {\n ...context,\n status: response.status,\n duration: this.#config.clock.now() - startedAt,\n response: response.clone(),\n });\n }\n\n const payload = await this.#readBodyOrFail(\n response,\n context,\n request,\n method,\n abort,\n timeout,\n );\n const duration = this.#config.clock.now() - startedAt;\n\n if (!response.ok) {\n const error = createApiError({\n method,\n path: request.path,\n status: response.status,\n now: this.#config.clock.now(),\n statusText: response.statusText,\n headers: response.headers,\n response,\n body: payload,\n });\n\n await dispatchRequestHook(this.#config.hooks, 'onError', { ...context, duration, error });\n this.#config.logger?.warn(\n `← ${response.status} ${method} ${request.path} (${duration} мс): ${error.message}`,\n );\n\n throw error;\n }\n\n this.#config.logger?.debug(`← ${response.status} ${method} ${request.path} (${duration} мс)`);\n\n return request.raw ? payload : unwrapData(payload);\n } finally {\n try {\n await cleanupBody?.();\n } catch (error) {\n this.#config.logger?.warn(`не удалось закрыть тело ${method} ${request.path}`, error);\n } finally {\n abort.cleanup();\n }\n }\n };\n\n /** Подготавливает тело внутри попытки, чтобы поток можно было открыть заново при retry. */\n async #prepareBody(\n request: PipelineRequest,\n headers: Headers,\n signal: AbortSignal,\n attempt: number,\n ): Promise<{ body: BodyInit | undefined; cleanup: (() => void | Promise<void>) | undefined }> {\n if (request.bodyFactory) {\n if (request.body !== undefined && request.body !== null) {\n throw new ItdConfigError('body и bodyFactory нельзя задавать одновременно');\n }\n\n const pending = Promise.resolve(request.bodyFactory({ signal, attempt }));\n let prepared: PreparedRequestBody;\n try {\n prepared = await abortable(pending, signal);\n } catch (error) {\n if (signal.aborted) {\n void pending\n .then(async (late) => {\n try {\n await late.cleanup?.();\n } catch (cleanupError) {\n this.#config.logger?.warn(\n `не удалось закрыть отложенное тело ${request.method} ${request.path}`,\n cleanupError,\n );\n }\n })\n .catch(() => {});\n }\n throw error;\n }\n for (const [name, value] of Object.entries(prepared.headers ?? {})) {\n if (!headers.has(name)) setHeader(headers, name, value);\n }\n return { body: prepared.body, cleanup: prepared.cleanup };\n }\n\n if (request.body === undefined || request.body === null) {\n return { body: undefined, cleanup: undefined };\n }\n if (isRawBody(request.body)) {\n return { body: request.body, cleanup: undefined };\n }\n\n if (!headers.has('Content-Type')) headers.set('Content-Type', 'application/json');\n return { body: JSON.stringify(request.body), cleanup: undefined };\n }\n\n /** Читает тело и преобразует ошибку чтения в транспортную ошибку библиотеки. */\n async #readBodyOrFail(\n response: Response,\n context: {\n operationId: PipelineRequest['operationId'];\n method: string;\n path: string;\n url: string;\n headers: Headers;\n attempt: number;\n },\n request: PipelineRequest,\n method: string,\n abort: AbortBundle,\n timeout: number,\n ): Promise<unknown> {\n const startedAt = this.#config.clock.now();\n\n try {\n return await abortable(readBody(response), abort.signal);\n } catch (error) {\n await response.body?.cancel().catch(() => {});\n\n const failure = this.#toTransportError(error, abort, request, method, timeout);\n\n await dispatchRequestHook(this.#config.hooks, 'onError', {\n ...context,\n duration: this.#config.clock.now() - startedAt,\n error: failure,\n });\n this.#config.logger?.warn(\n `× ${method} ${request.path}: не удалось прочитать тело ответа — ${failure.message}`,\n );\n\n throw failure;\n }\n }\n\n /**\n * Итоговый URL со строкой запроса. Нужен и слою повторов — для хука `onRetry`.\n *\n * Хост берётся из самого запроса, если он там задан: у сервисов платформы свои домены.\n */\n buildUrl(request: PipelineRequest): string {\n const base = request.baseUrl ?? this.#config.baseUrl;\n return joinUrl(base, request.path) + buildQuery(request.query);\n }\n\n /**\n * Собирает общие заголовки клиента: `User-Agent`, идентификатор устройства,\n * заголовки конфигурации и cookie для указанного адреса.\n */\n async platformHeaders(url: string): Promise<Headers> {\n const headers = new Headers();\n\n // Сервер этот заголовок не требует, но ожидает: дешевле отправить, чем разбираться,\n // почему часть запросов не проходит фильтры.\n headers.set('X-Requested-With', 'XMLHttpRequest');\n\n // В браузере это запрещённый заголовок: среда молча его игнорирует, ошибки не будет.\n if (this.#config.userAgent) setHeader(headers, 'User-Agent', this.#config.userAgent);\n\n if (this.#deps.getDeviceId) {\n setHeader(headers, 'X-Device-Id', await this.#deps.getDeviceId());\n }\n\n for (const [name, value] of Object.entries(this.#config.headers))\n setHeader(headers, name, value);\n\n if (this.#config.useCookieJar && this.#deps.cookies) {\n const cookie = this.#deps.cookies.getHeader(url);\n if (cookie) setHeader(headers, 'Cookie', cookie);\n }\n\n return headers;\n }\n\n /**\n * Дополняет общие заголовки значением `Accept`, заголовками конвейера и вызова.\n * Заголовки вызова применяются последними.\n */\n async #buildHeaders(request: PipelineRequest, url: string): Promise<Headers> {\n const headers = await this.platformHeaders(url);\n\n // Заголовок из конфигурации имеет приоритет над значением по умолчанию.\n if (!headers.has('Accept')) headers.set('Accept', 'application/json');\n\n for (const [name, value] of Object.entries(request.layerHeaders ?? {}))\n setHeader(headers, name, value);\n\n for (const [name, value] of Object.entries(request.headers ?? {})) {\n setHeader(headers, name, value);\n }\n\n return headers;\n }\n\n /** Превращает исключение `fetch` в понятную ошибку библиотеки. */\n #toTransportError(\n error: unknown,\n abort: AbortBundle,\n request: PipelineRequest,\n method: string,\n timeout: number,\n ): ItdError {\n // Пользовательская отмена с собственным `reason` реджектит `fetch` этим значением, а не\n // `AbortError`, — поэтому опираемся на состояние сигнала, а не только на имя ошибки.\n const aborted = abort.signal.aborted || (error instanceof Error && error.name === 'AbortError');\n\n if (aborted && abort.timedOut()) {\n return new ItdTimeoutError({ timeout, method, path: request.path });\n }\n\n if (aborted) {\n // Причину задаёт `abort(reason)` пользователя либо освобождение клиента.\n const reason = abort.signal.reason;\n return new ItdAbortError(\n `Запрос ${method} ${request.path} отменён`,\n reason !== undefined ? { cause: reason } : undefined,\n );\n }\n\n if (error instanceof ItdError) return error;\n\n return new ItdNetworkError(\n `Не удалось выполнить ${method} ${request.path}: ${error instanceof Error ? error.message : String(error)}`,\n { method, path: request.path, cause: error },\n );\n }\n}\n","import type { ItdClientOptions } from '../types/options.js';\nimport { AuthManager } from './auth.js';\nimport { BUILT_IN_SERVICES, type ResolvedConfig, resolveConfig } from './config.js';\nimport { CookieJar } from './cookies.js';\nimport { ItdAbortError } from './errors.js';\nimport { HttpClient } from './http.js';\nimport {\n composePipeline,\n createAttemptMiddleware,\n createAuthHeadersMiddleware,\n createAuthPreparationMiddleware,\n createAuthRecoveryMiddleware,\n createPluginsMiddleware,\n createQueueMiddleware,\n createRetryMiddleware,\n createServicesMiddleware,\n type RequestMiddleware,\n} from './middleware.js';\nimport { isDisposeCleanupRequest, type PipelineRequest, type RequestHandler } from './pipeline.js';\nimport { PluginRegistry } from './plugins/registry.js';\nimport { RequestQueuePool } from './rate-limit.js';\nimport { mergeService, ServiceRegistry } from './services.js';\nimport { Transport } from './transport.js';\nimport { originOf } from './url.js';\n\n/** Имена стадий основного request pipeline в порядке выполнения. @internal */\nexport const ClientRuntimeStage = Object.freeze({\n OperationPlugins: 'operation_plugins',\n Services: 'services',\n Retry: 'retry',\n AuthRecovery: 'auth_recovery',\n AuthPreparation: 'auth_preparation',\n Queue: 'queue',\n Attempt: 'attempt',\n AuthHeaders: 'auth_headers',\n Transport: 'transport',\n} as const);\n/** @internal */\nexport type ClientRuntimeStage = (typeof ClientRuntimeStage)[keyof typeof ClientRuntimeStage];\n\n/** Скрытые зависимости runtime, которыми владеет внешний контейнер. @internal */\nexport interface ClientRuntimeInternals {\n /** Общая очередь нескольких клиентов; runtime не останавливает её самостоятельно. */\n queues?: RequestQueuePool | undefined;\n /** Проверяет терминальное состояние фасада до входа в pipeline. */\n assertActive?: ((action: string) => void) | undefined;\n /** Вызывается перед сменой владельца авторизации. */\n onAccountChange?: (() => void) | undefined;\n}\n\n/**\n * Собранная внутренняя часть клиента.\n *\n * Фасад владеет ресурсами и realtime-потоками, runtime — сетевой механикой и registries.\n * Контракт намеренно не экспортируется из корневой точки входа.\n *\n * @internal\n */\nexport interface ClientRuntime {\n readonly config: ResolvedConfig;\n readonly http: HttpClient;\n readonly auth: AuthManager;\n readonly cookies: CookieJar;\n readonly plugins: PluginRegistry;\n readonly services: ServiceRegistry;\n /** Фактический порядок стадий; используется contract-тестом и диагностикой. */\n readonly stageOrder: readonly ClientRuntimeStage[];\n platformHeaders(url: string): Promise<Headers>;\n /** Временно останавливает только принадлежащие runtime очереди. */\n close(): void;\n /** Отменяет начатые запросы, снимает auth listeners и освобождает плагины. */\n dispose(): Promise<void>;\n}\n\ninterface PipelineStage {\n name: ClientRuntimeStage;\n middleware: RequestMiddleware;\n}\n\n/** Регистрирует встроенные сервисы и накладывает пользовательские overrides. */\nfunction createServiceRegistry(config: ResolvedConfig): ServiceRegistry {\n const services = new ServiceRegistry(config.baseUrl);\n const overrides = new Map(config.services.map((service) => [service.name.trim(), service]));\n\n for (const builtIn of BUILT_IN_SERVICES) {\n const override = overrides.get(builtIn.name);\n overrides.delete(builtIn.name);\n services.define(override ? mergeService(builtIn, override) : builtIn);\n }\n for (const service of overrides.values()) services.define(service);\n\n return services;\n}\n\n/** Собирает внутренний runtime клиента и единственный request pipeline. @internal */\nexport function createClientRuntime(\n options: ItdClientOptions = {},\n internals: ClientRuntimeInternals = {},\n): ClientRuntime {\n const config = resolveConfig(options);\n const jar = new CookieJar();\n const plugins = new PluginRegistry({\n shutdownTimeout: config.shutdownTimeout,\n clock: config.clock,\n });\n const services = createServiceRegistry(config);\n\n // Транспорт объединяет этот сигнал с сигналом запроса: dispose() отменяет начатые запросы.\n const lifetime = new AbortController();\n\n // Общая очередь принадлежит ItdAccounts. `rateLimit: false` исключает отдельный клиент\n // из неё, поэтому переданный pool учитывается только при включённом ограничителе.\n const sharedQueues = config.rateLimit ? internals.queues : undefined;\n const queues =\n sharedQueues ??\n (config.rateLimit ? new RequestQueuePool(config.rateLimit, config.clock) : undefined);\n const ownsQueues = sharedQueues === undefined;\n\n // Transport читает deviceId авторизации, а AuthManager отправляет свои запросы через\n // готовый pipeline. Цикл замыкается ленивыми callbacks строго внутри этой фабрики.\n let auth!: AuthManager;\n let transport!: Transport;\n\n const queueFor = (request: PipelineRequest) => {\n const destination = originOf(transport.buildUrl(request));\n return queues?.for(destination || undefined);\n };\n\n /**\n * Сервер сообщает остаток окна в `x-ratelimit-remaining`, но не сообщает момент сброса.\n * При нуле заранее ставим очередь конечного origin на первую, самую короткую паузу из\n * `retryDelays`: окно могло почти истечь, поэтому длинный backoff здесь преждевременен.\n * Если окно всё ещё закрыто, следующий `429` применит самостоятельную лестницу повторов.\n * Общая пауза очереди не даёт параллельным запросам одновременно ударить в тот же лимит.\n */\n const throttleByHeaders = (\n limit: number | undefined,\n remaining: number | undefined,\n request: PipelineRequest,\n ): void => {\n if (remaining === undefined || remaining > 0) return;\n\n const first = config.rateLimit?.retryDelays[0];\n if (first === undefined) return;\n\n queueFor(request)?.pause(first);\n config.logger?.debug(\n `лимит сервера исчерпан (${remaining} из ${limit ?? '?'}), очередь ждёт ${first} мс`,\n );\n };\n\n transport = new Transport(\n { ...config, hooks: config.hooks },\n {\n cookies: config.useCookieJar ? jar : undefined,\n getDeviceId: () => auth.getDeviceId(),\n onRateLimit: queues && config.rateLimit?.respectHeaders ? throttleByHeaders : undefined,\n lifetimeSignal: lifetime.signal,\n },\n );\n\n const stages: PipelineStage[] = [\n {\n name: ClientRuntimeStage.OperationPlugins,\n middleware: createPluginsMiddleware(plugins),\n },\n {\n name: ClientRuntimeStage.Services,\n middleware: createServicesMiddleware(services),\n },\n {\n name: ClientRuntimeStage.Retry,\n middleware: createRetryMiddleware({\n clock: config.clock,\n retry: config.retry,\n rateLimitDelays: config.rateLimit?.retryDelays ?? [],\n pauseQueue: queues ? (ms, request) => queueFor(request)?.pause(ms) : undefined,\n hooks: config.hooks,\n logger: config.logger,\n buildUrl: (request) => transport.buildUrl(request),\n }),\n },\n {\n name: ClientRuntimeStage.AuthRecovery,\n middleware: createAuthRecoveryMiddleware({\n onUnauthorized: () => auth.onUnauthorized(),\n autoRefresh: config.autoRefresh,\n }),\n },\n {\n name: ClientRuntimeStage.AuthPreparation,\n middleware: createAuthPreparationMiddleware({\n prepareAuth: () => auth.getAccessToken().then(() => undefined),\n }),\n },\n ];\n\n if (queues) {\n stages.push({\n name: ClientRuntimeStage.Queue,\n middleware: createQueueMiddleware((request, task) => {\n const queue = queueFor(request);\n return queue ? queue.schedule(task, request.signal) : task();\n }),\n });\n }\n\n stages.push({\n name: ClientRuntimeStage.Attempt,\n middleware: createAttemptMiddleware(),\n });\n\n stages.push({\n name: ClientRuntimeStage.AuthHeaders,\n middleware: createAuthHeadersMiddleware({\n getAuthHeaders: () => auth.getCurrentAuthHeaders(),\n }),\n });\n\n const handler = composePipeline(\n stages.map(({ middleware }) => middleware),\n transport.send,\n );\n const clientHandler: RequestHandler = (request) => {\n try {\n if (!isDisposeCleanupRequest(request)) {\n internals.assertActive?.('выполнить новый запрос');\n }\n } catch (error) {\n return Promise.reject(error);\n }\n return handler(request);\n };\n\n // Auth использует тот же handler: sign-in и refresh объявляют skip-флаги точечно,\n // вместо отдельного pipeline с постепенно расходящимся порядком стадий.\n auth = new AuthManager(config, clientHandler, jar, {\n onAccountChange: internals.onAccountChange,\n });\n const http = new HttpClient({ handler: clientHandler, baseUrl: config.baseUrl });\n const stageOrder = Object.freeze([\n ...stages.map(({ name }) => name),\n ClientRuntimeStage.Transport,\n ]);\n\n return {\n config,\n http,\n auth,\n cookies: jar,\n plugins,\n services,\n stageOrder,\n platformHeaders: (url) => transport.platformHeaders(url),\n close: () => {\n if (ownsQueues) queues?.stop();\n },\n dispose: async () => {\n // Порядок: после отправки телеметрии в #close() фасада и до ожидания плагинов.\n lifetime.abort(new ItdAbortError('Клиент освобождён через dispose(), запрос отменён'));\n auth.dispose();\n await plugins.dispose();\n },\n };\n}\n","/**\n * Перечисления API итд.com.\n *\n * Здесь намеренно не используется `enum` из TypeScript. Вместо него — пара «замороженный\n * объект + одноимённый тип». Такой приём даёт всё, ради чего берут `enum`\n * (`FeedTab.Popular`, перебор значений в рантайме), и при этом:\n *\n * - **стирается без остатка** — `enum` порождает рантайм-код и отвергается средами,\n * которые просто срезают типы (`node --experimental-strip-types`);\n * - **не запрещает обычные строки** — `itd.posts.list({ tab: 'popular' })` остаётся валидным,\n * тогда как строковый `enum` считает это ошибкой типа и вынуждает всех импортировать себя;\n * - **позволяет открытые множества** — там, где документация перечисляет значения не полностью,\n * тип расширяется через {@link Loose}, а объект остаётся справочником известных значений.\n *\n * @example\n * ```ts\n * import { FeedTab } from 'itd-api';\n *\n * await itd.posts.list({ tab: FeedTab.Popular }); // без магических строк\n * await itd.posts.list({ tab: 'popular' }); // и так тоже можно\n *\n * Object.values(FeedTab); // ['popular', 'following', 'clan']\n * ```\n *\n * @packageDocumentation\n */\n\n/**\n * Открытое строковое перечисление.\n *\n * Даёт автодополнение известных значений, но не ломается, если сервер пришлёт новое.\n * Используется там, где документация API перечисляет значения не полностью («`everyone` и др.»).\n */\nexport type Loose<T extends string> = T | (string & {});\n\n/**\n * Вкладка ленты `GET /api/posts`.\n *\n * Множество закрытое: неизвестное значение сервер отвергнет.\n */\nexport const FeedTab = Object.freeze({\n /** Популярное. Курсор здесь — номер страницы в виде строки (`\"2\"`, `\"6\"`…). */\n Popular: 'popular',\n /** Записи тех, на кого вы подписаны. Курсор — отметка времени последнего поста. */\n Following: 'following',\n /** Лента клана. Курсор, как и в подписках, — отметка времени. */\n Clan: 'clan',\n} as const);\nexport type FeedTab = (typeof FeedTab)[keyof typeof FeedTab];\n\n/** Порядок комментариев к посту. */\nexport const CommentSort = Object.freeze({\n /** Сначала новые. */\n Newest: 'newest',\n /** Сначала старые. */\n Oldest: 'oldest',\n /** Сначала популярные. */\n Popular: 'popular',\n} as const);\nexport type CommentSort = (typeof CommentSort)[keyof typeof CommentSort];\n\n/** Тип вложения. */\nexport const AttachmentType = Object.freeze({\n Image: 'image',\n Video: 'video',\n /** Голосовые комментарии: `audio/ogg`, с полем `duration`. */\n Audio: 'audio',\n} as const);\nexport type AttachmentType = (typeof AttachmentType)[keyof typeof AttachmentType];\n\n/**\n * Тип фрагмента разметки в тексте поста или комментария.\n *\n * Первые два сервер расставляет сам при разборе текста, остальные приходят от редактора.\n * Тип открытый: набор может пополниться.\n *\n * @example\n * ```ts\n * await itd.posts.update(postId, {\n * content: 'жирное слово',\n * spans: [{ type: SpanType.Bold, offset: 0, length: 6 }],\n * });\n * ```\n */\nexport const SpanType = Object.freeze({\n /** Хэштег. Название без решётки лежит в `tag`. */\n Hashtag: 'hashtag',\n /** Упоминание. Имя пользователя лежит в `tag`. */\n Mention: 'mention',\n /** Ссылка. Адрес лежит в `url`, а не в `tag`. */\n Link: 'link',\n Bold: 'bold',\n Italic: 'italic',\n Underline: 'underline',\n /** Зачёркнутый. */\n Strike: 'strike',\n /** Спойлер: текст скрыт до нажатия. */\n Spoiler: 'spoiler',\n /** Моноширинный. */\n Monospace: 'monospace',\n Quote: 'quote',\n} as const);\nexport type SpanType = Loose<(typeof SpanType)[keyof typeof SpanType]>;\n\n/** На что подаётся жалоба. */\nexport const ReportTargetType = Object.freeze({\n Post: 'post',\n Comment: 'comment',\n User: 'user',\n} as const);\nexport type ReportTargetType = (typeof ReportTargetType)[keyof typeof ReportTargetType];\n\n/** Причина жалобы. Множество закрытое. */\nexport const ReportReason = Object.freeze({\n Spam: 'spam',\n Violence: 'violence',\n Hate: 'hate',\n Adult: 'adult',\n Fraud: 'fraud',\n Other: 'other',\n} as const);\nexport type ReportReason = (typeof ReportReason)[keyof typeof ReportReason];\n\n/** Состояние realtime-соединения. */\nexport const RealtimeStatus = Object.freeze({\n Connecting: 'connecting',\n Connected: 'connected',\n Error: 'error',\n Disconnected: 'disconnected',\n} as const);\nexport type RealtimeStatus = (typeof RealtimeStatus)[keyof typeof RealtimeStatus];\n\n/** Состояние сервиса платформы. Тип открытый. */\nexport const ServiceState = Object.freeze({\n /** Работает штатно. */\n Operational: 'operational',\n /** Работает с деградацией. */\n Degraded: 'degraded',\n /** Недоступен. */\n Downtime: 'downtime',\n} as const);\nexport type ServiceState = Loose<(typeof ServiceState)[keyof typeof ServiceState]>;\n\n/** Вид происшествия в истории сервиса. Тип открытый. */\nexport const IncidentKind = Object.freeze({\n /** Недоступен. */\n Down: 'down',\n /** Деградация. */\n Degraded: 'deg',\n} as const);\nexport type IncidentKind = Loose<(typeof IncidentKind)[keyof typeof IncidentKind]>;\n\n/**\n * Уровень доступа к разделу профиля.\n *\n * Общий набор значений для полей `wallAccess` и `likesVisibility` настроек приватности.\n * Тип открытый: сервер может прислать значение вне этого перечня.\n */\nexport const AccessType = Object.freeze({\n /** Никто. */\n Nobody: 'nobody',\n /** Только взаимные подписки. */\n Mutual: 'mutual',\n /** Подписчики. */\n Followers: 'followers',\n /** Все. */\n Everyone: 'everyone',\n} as const);\nexport type AccessType = Loose<(typeof AccessType)[keyof typeof AccessType]>;\n\n/** Кто может писать на стену профиля. Псевдоним {@link AccessType}. */\nexport const WallAccess = AccessType;\nexport type WallAccess = AccessType;\n\n/** Кто видит реакции пользователя. Псевдоним {@link AccessType}. */\nexport const LikesVisibility = AccessType;\nexport type LikesVisibility = AccessType;\n\n/**\n * Канонический тип уведомления (новое поколение имён).\n *\n * REST-эндпоинт `/api/notifications/` отдаёт старые имена (`like`, `comment`, `reply`,\n * `repost`, `mention`), SSE-поток — новые. Библиотека приводит их к этому набору,\n * сохраняя исходное значение в поле `rawType`.\n */\nexport const NotificationType = Object.freeze({\n /** Реакция на пост. Старое имя — `like`. */\n PostReaction: 'post_reaction',\n /** Комментарий к посту. Старое имя — `comment`. */\n PostComment: 'post_comment',\n /** Ответ на комментарий. Старое имя — `reply`. */\n CommentReply: 'comment_reply',\n /** Репост. Старое имя — `repost`. */\n PostRepost: 'post_repost',\n /** Упоминание в посте. Старое имя — `mention`. */\n PostMention: 'post_mention',\n /** Реакция на комментарий. */\n CommentReaction: 'comment_reaction',\n /** Упоминание в комментарии. */\n CommentMention: 'comment_mention',\n /** Запись на вашей стене. */\n WallPost: 'wall_post',\n /** На вас подписались. */\n Follow: 'follow',\n /** Заявка на подписку (закрытый профиль). */\n FollowRequest: 'follow_request',\n /** Заявка на подписку принята. */\n FollowAccepted: 'follow_accepted',\n /** Верификация одобрена. Приходит только по REST. */\n VerificationApproved: 'verification_approved',\n /** Верификация отклонена. Приходит только по REST. */\n VerificationRejected: 'verification_rejected',\n} as const);\nexport type NotificationType = Loose<(typeof NotificationType)[keyof typeof NotificationType]>;\n\n/**\n * Тип взаимодействия с контентом в телеметрии (`POST /api/v1/x`, поле `t`).\n *\n * Кодируется числом.\n */\nexport const InteractionType = Object.freeze({\n /** Открытие фотографии. */\n PhotoOpen: 1,\n /** Прогресс просмотра видео. Несёт поля `pm`/`dm`. */\n VideoProgress: 2,\n} as const);\nexport type InteractionType = (typeof InteractionType)[keyof typeof InteractionType];\n\n/**\n * Источник показа поста в телеметрии (поле `s`).\n *\n * Кодируется числом. Поле применимо к источникам `PostPage` и `Link`; для лент источник\n * передаётся контекстом `sc`.\n */\nexport const ViewSource = Object.freeze({\n FeedGlobal: 1,\n FeedFollowing: 2,\n FeedClan: 3,\n Profile: 4,\n Hashtag: 5,\n PostPage: 6,\n Link: 7,\n Search: 8,\n} as const);\nexport type ViewSource = (typeof ViewSource)[keyof typeof ViewSource];\n\n/**\n * Причина завершения просмотра поста в телеметрии (`POST /api/v1/i`, поле `r`).\n *\n * Кодируется числом.\n */\nexport const ViewReason = Object.freeze({\n /** Пост ушёл из зоны видимости при обычной прокрутке. */\n Normal: 0,\n /** Потеря фокуса окна. */\n Blur: 1,\n /** Вкладка скрыта. */\n Hidden: 2,\n /** Уход со страницы (`pagehide`). */\n PageHide: 3,\n /** Элемент перестал наблюдаться. */\n Unobserve: 4,\n /** Достигнут порог времени просмотра. */\n ThresholdMet: 5,\n} as const);\nexport type ViewReason = (typeof ViewReason)[keyof typeof ViewReason];\n\n/**\n * Строковые коды ошибок из поля `code`.\n *\n * Ключи намеренно повторяют написание сервера: код из ответа API можно найти здесь\n * поиском один в один, без мысленного перевода регистра.\n *\n * Список открыт — сервер может добавить новый код, и это не должно ломать типизацию.\n *\n * @example\n * ```ts\n * if (err.hasCode(ItdErrorCode.OTP_INVALID)) await restartOtpFlow();\n * ```\n */\nexport const ItdErrorCode = Object.freeze({\n BAD_REQUEST: 'BAD_REQUEST',\n UNAUTHORIZED: 'UNAUTHORIZED',\n ACCESS_DENIED: 'ACCESS_DENIED',\n ENTITY_NOT_FOUND: 'ENTITY_NOT_FOUND',\n ENTITY_ALREADY_EXISTS: 'ENTITY_ALREADY_EXISTS',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n BUSINESS_RULE_VIOLATION: 'BUSINESS_RULE_VIOLATION',\n RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',\n UNKNOWN_ERROR: 'UNKNOWN_ERROR',\n /** Сервер отвечает так на `404`, `ENTITY_NOT_FOUND` в этом случае не приходит. */\n NOT_FOUND: 'NOT_FOUND',\n /** На практике не приходит: вместо него сервер шлёт `TURNSTILE_VERIFICATION_FAILED`. */\n CAPTCHA_FAILED: 'CAPTCHA_FAILED',\n /** Капча не пройдена: токен Turnstile недействителен, просрочен или уже использован. */\n TURNSTILE_VERIFICATION_FAILED: 'TURNSTILE_VERIFICATION_FAILED',\n OTP_INVALID: 'OTP_INVALID',\n /** `flowToken` неизвестен или просрочен — поток подтверждения нужно начинать заново. */\n INVALID_FLOW_TOKEN: 'INVALID_FLOW_TOKEN',\n ACCOUNT_DEACTIVATED: 'ACCOUNT_DEACTIVATED',\n ACCOUNT_EMAIL_DOMAIN_NOT_ALLOWED: 'ACCOUNT_EMAIL_DOMAIN_NOT_ALLOWED',\n ACCOUNT_INVALID_CREDENTIALS: 'ACCOUNT_INVALID_CREDENTIALS',\n ACCOUNT_TEMPORARILY_LOCKED: 'ACCOUNT_TEMPORARILY_LOCKED',\n ACCOUNT_CURRENT_PASSWORD_INCORRECT: 'ACCOUNT_CURRENT_PASSWORD_INCORRECT',\n SESSION_EXPIRED: 'SESSION_EXPIRED',\n SESSION_REVOKED: 'SESSION_REVOKED',\n SESSION_INVALID_REFRESH_TOKEN: 'SESSION_INVALID_REFRESH_TOKEN',\n /** Запрос обновления пришёл без cookie `refresh_token` — продлевать нечего. */\n REFRESH_TOKEN_MISSING: 'REFRESH_TOKEN_MISSING',\n /** Cookie `refresh_token` есть, но сессии за ней уже нет: отозвана или истекла. */\n SESSION_NOT_FOUND: 'SESSION_NOT_FOUND',\n MISSING_FLOW_TOKEN: 'MISSING_FLOW_TOKEN',\n PROFILE_USERNAME_TAKEN: 'PROFILE_USERNAME_TAKEN',\n PROFILE_RESTRICTION_ACTIVE: 'PROFILE_RESTRICTION_ACTIVE',\n PROFILE_MODIFICATION_RESTRICTED: 'PROFILE_MODIFICATION_RESTRICTED',\n CONTENT_MODERATION_FAILED: 'CONTENT_MODERATION_FAILED',\n FILE_TOO_LARGE: 'FILE_TOO_LARGE',\n UNSUPPORTED_FILE_TYPE: 'UNSUPPORTED_FILE_TYPE',\n UPLOAD_FAILED: 'UPLOAD_FAILED',\n VIDEO_REQUIRES_VERIFICATION: 'VIDEO_REQUIRES_VERIFICATION',\n PHONE_VERIFICATION_REQUIRED: 'PHONE_VERIFICATION_REQUIRED',\n WRITE_ACCESS_RESTRICTED: 'WRITE_ACCESS_RESTRICTED',\n} as const);\nexport type ItdErrorCode = Loose<(typeof ItdErrorCode)[keyof typeof ItdErrorCode]>;\n","import type { Unsubscribe } from '../core/emitter.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport type { RealtimeContext, RealtimeContextBase } from './updates.js';\n\n/** Продолжает цепочку промежуточных обработчиков потока. */\nexport type RealtimeNext = () => Promise<void>;\n\n/** Обрабатывает обновление потока до его передачи подписчикам. */\nexport type RealtimeMiddleware<C extends RealtimeContextBase = RealtimeContext> = (\n context: C,\n next: RealtimeNext,\n) => void | Promise<void>;\n\n/** Объект, предоставляющий снимок промежуточного обработчика потока. */\nexport interface RealtimeMiddlewareObj<C extends RealtimeContextBase = RealtimeContext> {\n middleware(): RealtimeMiddleware<C>;\n}\n\n/** Асинхронный обработчик нормализованного обновления потока. */\nexport type RealtimeHandler<C extends RealtimeContextBase = RealtimeContext> = (\n context: C,\n) => unknown | Promise<unknown>;\n\n/** Условие отбора контекста потока. */\nexport type RealtimePredicate<C extends RealtimeContextBase = RealtimeContext> = (\n context: C,\n) => boolean;\n\n/** Проверка, сужающая тип контекста потока. */\nexport type RealtimeTypeGuard<C extends B, B extends RealtimeContextBase = RealtimeContext> = (\n context: B,\n) => context is C;\n\n/** Ключи, по которым обновления нельзя обрабатывать одновременно. */\nexport type RealtimeSequentializer<C extends RealtimeContextBase = RealtimeContext> = (\n context: C,\n) => PropertyKey | readonly PropertyKey[] | undefined;\n\nconst REALTIME_MIDDLEWARE_SNAPSHOT = Symbol('itd-api.realtime.middlewareSnapshot');\n\ntype SnapshottingRealtimeMiddleware<C extends RealtimeContextBase> = RealtimeMiddleware<C> & {\n [REALTIME_MIDDLEWARE_SNAPSHOT]?: () => RealtimeMiddleware<C>;\n};\n\n/** @internal */\nexport function captureRealtimeMiddleware<C extends RealtimeContextBase>(\n middleware: RealtimeMiddleware<C>,\n): RealtimeMiddleware<C> {\n const capture = (middleware as SnapshottingRealtimeMiddleware<C>)[REALTIME_MIDDLEWARE_SNAPSHOT];\n if (!capture) return middleware;\n\n const snapshot = capture();\n if (typeof snapshot !== 'function') {\n throw new TypeError('Снимок промежуточного обработчика должен быть функцией');\n }\n return snapshot;\n}\n\n/** @internal */\nexport function withRealtimeMiddlewareSnapshot<C extends RealtimeContextBase>(\n middleware: RealtimeMiddleware<C>,\n capture: () => RealtimeMiddleware<C>,\n): RealtimeMiddleware<C> {\n Object.defineProperty(middleware, REALTIME_MIDDLEWARE_SNAPSHOT, { value: capture });\n return middleware;\n}\n\n/** Создаёт отложенный промежуточный обработчик для объекта. @internal */\nexport function deferRealtimeMiddleware<C extends RealtimeContextBase>(\n source: RealtimeMiddlewareObj<C>,\n): RealtimeMiddleware<C> {\n const capture = (): RealtimeMiddleware<C> => {\n const middleware = source.middleware();\n if (typeof middleware !== 'function') {\n throw new ItdConfigError('realtime middleware() должен возвращать функцию обработки');\n }\n return captureRealtimeMiddleware(middleware);\n };\n\n const deferred: RealtimeMiddleware<C> = (context, next) => capture()(context, next);\n return withRealtimeMiddlewareSnapshot(deferred, capture);\n}\n\ninterface HandlerRegistration<C extends RealtimeContextBase> {\n readonly predicate: RealtimePredicate<C>;\n readonly handler: RealtimeHandler<C>;\n}\n\ninterface DispatchWork<C extends RealtimeContextBase> {\n readonly context: C;\n readonly middleware: readonly RealtimeMiddleware<C>[];\n readonly handlers: readonly HandlerRegistration<C>[];\n readonly keys: readonly PropertyKey[];\n /** Ключ снимка: ожидающая работа с тем же ключом заменяется новой. */\n readonly coalesceKey: PropertyKey | undefined;\n}\n\n/** Предел обновлений, ожидающих обработки. @internal */\nexport const MAX_PENDING_UPDATES = 256;\n\nexport interface RealtimeDispatcherOptions<C extends RealtimeContextBase = RealtimeContext> {\n concurrency: number;\n sequentialize?: RealtimeSequentializer<C> | undefined;\n}\n\nexport interface RealtimeDispatcherHooks<C extends RealtimeContextBase = RealtimeContext> {\n deliver: (context: C) => void;\n middlewareError: (error: unknown, context: C) => void;\n handlerError: (error: unknown, context: C) => void;\n /** Очередь достигла предела: обновление не принято, поток обязан прекратить их приём. */\n overflow: () => void;\n}\n\n/** Выполняет промежуточные обработчики по порядку и запрещает повторный вызов `next()`. */\nexport async function runRealtimeMiddleware<C extends RealtimeContextBase>(\n middleware: readonly RealtimeMiddleware<C>[],\n context: C,\n terminal: RealtimeNext,\n): Promise<void> {\n let lastIndex = -1;\n\n const dispatch = async (index: number): Promise<void> => {\n if (index <= lastIndex) throw new Error('next() в промежуточном обработчике вызван повторно');\n lastIndex = index;\n\n const current = middleware[index];\n if (!current) {\n await terminal();\n return;\n }\n\n let downstream: Promise<void> | undefined;\n let duplicateCalls: Promise<void> | undefined;\n let failure: { error: unknown } | undefined;\n\n const next: RealtimeNext = () => {\n if (!downstream) {\n downstream = dispatch(index + 1);\n return downstream;\n }\n\n const duplicate = Promise.reject(\n new Error('next() в одном промежуточном обработчике вызван повторно'),\n );\n duplicateCalls = Promise.all(duplicateCalls ? [duplicateCalls, duplicate] : [duplicate]).then(\n () => undefined,\n );\n return duplicate;\n };\n\n try {\n await current(context, next);\n } catch (error) {\n failure = { error };\n }\n\n try {\n await downstream;\n await duplicateCalls;\n } catch (error) {\n failure ??= { error };\n }\n\n if (failure) throw failure.error;\n };\n\n await dispatch(0);\n}\n\n/** Планирует нормализованные обновления и отслеживает незавершённые обработчики. */\nexport class RealtimeDispatcher<C extends RealtimeContextBase = RealtimeContext> {\n readonly #options: RealtimeDispatcherOptions<C>;\n readonly #hooks: RealtimeDispatcherHooks<C>;\n readonly #middleware: RealtimeMiddleware<C>[] = [];\n readonly #handlers: HandlerRegistration<C>[] = [];\n readonly #queue: DispatchWork<C>[] = [];\n readonly #activeKeys = new Set<PropertyKey>();\n readonly #drainWaiters = new Set<() => void>();\n\n #active = 0;\n\n constructor(options: RealtimeDispatcherOptions<C>, hooks: RealtimeDispatcherHooks<C>) {\n this.#options = options;\n this.#hooks = hooks;\n }\n\n use(middleware: RealtimeMiddleware<C>): Unsubscribe {\n this.#middleware.push(middleware);\n return () => {\n const index = this.#middleware.indexOf(middleware);\n if (index >= 0) this.#middleware.splice(index, 1);\n };\n }\n\n on(predicate: RealtimePredicate<C>, handler: RealtimeHandler<C>): Unsubscribe {\n const registration = { predicate, handler };\n this.#handlers.push(registration);\n\n return () => {\n const index = this.#handlers.indexOf(registration);\n if (index >= 0) this.#handlers.splice(index, 1);\n };\n }\n\n /**\n * Принимает обновление к обработке.\n *\n * @param coalesceKey ожидающая работа с тем же ключом заменяется новой\n */\n dispatch(context: C, coalesceKey?: PropertyKey): void {\n let keys: readonly PropertyKey[];\n let middleware: readonly RealtimeMiddleware<C>[];\n try {\n keys = this.#keysFor(context);\n middleware = this.#middleware.map(captureRealtimeMiddleware);\n } catch (error) {\n this.#hooks.middlewareError(error, context);\n return;\n }\n\n const replaced =\n coalesceKey === undefined\n ? -1\n : this.#queue.findIndex((pending) => pending.coalesceKey === coalesceKey);\n if (replaced < 0 && this.#queue.length >= MAX_PENDING_UPDATES) {\n this.#hooks.overflow();\n return;\n }\n\n const work: DispatchWork<C> = {\n context,\n middleware,\n handlers: [...this.#handlers],\n keys,\n coalesceKey,\n };\n if (replaced >= 0) this.#queue[replaced] = work;\n else this.#queue.push(work);\n\n this.#pump();\n }\n\n /** Отбрасывает обновления, обработка которых ещё не началась. */\n clearPending(): void {\n this.#queue.length = 0;\n this.#resolveDrain();\n }\n\n /** Ждёт завершения активных и поставленных в очередь обновлений. */\n drain(): Promise<void> {\n if (this.#active === 0 && this.#queue.length === 0) return Promise.resolve();\n return new Promise<void>((resolve) => this.#drainWaiters.add(resolve));\n }\n\n #keysFor(context: C): readonly PropertyKey[] {\n const value = this.#options.sequentialize?.(context);\n if (value === undefined) return [];\n\n const values = Array.isArray(value) ? value : [value];\n for (const key of values) {\n if (typeof key !== 'string' && typeof key !== 'number' && typeof key !== 'symbol') {\n throw new TypeError('sequentialize() должен возвращать PropertyKey или их список');\n }\n }\n return [...new Set(values)];\n }\n\n #pump(): void {\n while (this.#active < this.#options.concurrency) {\n const blockedKeys = new Set(this.#activeKeys);\n const index = this.#queue.findIndex(({ keys }) => {\n const runnable = keys.every((key) => !blockedKeys.has(key));\n if (!runnable) {\n for (const key of keys) blockedKeys.add(key);\n }\n return runnable;\n });\n if (index < 0) break;\n\n const [work] = this.#queue.splice(index, 1);\n if (!work) break;\n\n this.#active += 1;\n for (const key of work.keys) this.#activeKeys.add(key);\n void this.#run(work);\n }\n\n this.#resolveDrain();\n }\n\n async #run(work: DispatchWork<C>): Promise<void> {\n try {\n await runRealtimeMiddleware(work.middleware, work.context, async () => {\n for (const { predicate, handler } of work.handlers) {\n try {\n if (predicate(work.context)) await handler(work.context);\n } catch (error) {\n this.#hooks.handlerError(error, work.context);\n }\n }\n\n this.#hooks.deliver(work.context);\n });\n } catch (error) {\n this.#hooks.middlewareError(error, work.context);\n } finally {\n this.#active -= 1;\n for (const key of work.keys) this.#activeKeys.delete(key);\n this.#pump();\n }\n }\n\n #resolveDrain(): void {\n if (this.#active !== 0 || this.#queue.length !== 0) return;\n for (const resolve of this.#drainWaiters) resolve();\n this.#drainWaiters.clear();\n }\n}\n","/**\n * Паузы перед попытками переподключения, мс.\n *\n * Значения совпадают с теми, что использует сайт итд.com, — поведение библиотеки\n * не отличается от привычного пользователю.\n */\nexport const RECONNECT_BACKOFF = Object.freeze([1000, 2000, 4000, 8000, 16000, 30000]);\n\n/** Доля случайного разброса паузы. */\nexport const RECONNECT_JITTER = 0.3;\n\n/**\n * Сколько раз пытаться переподключиться подряд.\n *\n * После исчерпания поток сообщает `giveup` и ждёт ручного `connect()`.\n */\nexport const MAX_RECONNECT_ATTEMPTS = 15;\n\n/** Настройки переподключения. */\nexport interface ReconnectOptions {\n /** Таблица пауз. Последнее значение действует для всех дальнейших попыток. */\n backoff?: readonly number[];\n /** Доля разброса, 0…1. */\n jitter?: number;\n /** Предел числа попыток. */\n maxAttempts?: number;\n}\n\n/**\n * Вычисляет паузу перед попыткой переподключения.\n *\n * Разброс нужен, чтобы клиенты, отключившиеся одновременно из-за перезапуска сервера,\n * не вернулись тоже одновременно.\n *\n * @param attempt номер попытки, начиная с нуля\n * @param random источник случайности; подменяется в тестах\n */\nexport function reconnectDelay(\n attempt: number,\n options: ReconnectOptions = {},\n random: () => number = Math.random,\n): number {\n const backoff = options.backoff ?? RECONNECT_BACKOFF;\n const jitter = options.jitter ?? RECONNECT_JITTER;\n\n const base = backoff[Math.min(attempt, backoff.length - 1)] ?? 30_000;\n const spread = base * jitter * (random() * 2 - 1);\n\n return Math.max(0, Math.round(base + spread));\n}\n","import type { BuiltInOperationId } from '../core/operations.js';\nimport type { QueryParams } from '../core/url.js';\n\n/** Запрос транспорта к конвейеру клиента. */\nexport interface RealtimeRequestInput {\n operationId: BuiltInOperationId;\n path: string;\n query?: QueryParams | undefined;\n signal: AbortSignal;\n}\n\n/**\n * Порт к конвейеру клиента: очередь, авторизация, повторы, плагины и хуки.\n *\n * Ответ приходит уже разобранным и без обёртки `{ data: … }`, а неудача — типизированной\n * ошибкой библиотеки.\n */\nexport type RealtimeRequest = (input: RealtimeRequestInput) => Promise<unknown>;\n\n/** Событие, пришедшее по каналу реального времени. */\nexport interface TransportEvent {\n /** Имя события: `notification`, `unread_count` и другие. */\n name: string;\n /** Полезная нагрузка, уже разобранная из JSON. */\n data: unknown;\n}\n\n/** Что транспорт получает от клиента при подключении. */\nexport interface TransportContext {\n /** Базовый URL API. */\n baseUrl: string;\n /** Разрешено ли передавать токен этому сервису. */\n authorize: boolean;\n /** Реализация `fetch`. */\n fetch: typeof fetch;\n /**\n * Выполнение обычных HTTP-запросов транспорта через конвейер клиента.\n *\n * Есть только у потока, созданного клиентом: конвейер принадлежит ему.\n */\n request?: RealtimeRequest | undefined;\n /**\n * Общие заголовки клиента: `User-Agent`, `X-Device-Id`, заголовки конфигурации\n * и cookie для указанного адреса.\n */\n baseHeaders: (url: string) => Promise<Headers>;\n /** Текущий токен доступа. */\n getToken: () => Promise<string | null>;\n /** Отмена подключения. */\n signal: AbortSignal;\n /** Сообщает о полученном событии. */\n onEvent: (event: TransportEvent) => void;\n /** Сообщает о разобранном, но некорректном сообщении. Соединение при этом живёт. */\n onParseError: (error: unknown, raw: string) => void;\n /** Вызывается, когда соединение установлено. */\n onOpen: () => void;\n}\n\n/** Канал получения исходных событий в реальном времени. */\nexport interface RealtimeTransport {\n /** Понятное имя для логов и диагностики. */\n readonly name: string;\n\n /**\n * Держит соединение, пока оно живо.\n *\n * Должен завершиться, когда поток закрылся, и бросить исключение при ошибке.\n * Отмена через `context.signal` должна приводить к `AbortError`.\n */\n connect(context: TransportContext): Promise<void>;\n}\n\n/** Ошибка, по которой видно, что сервер отверг авторизацию потока. */\nexport class UnauthorizedStreamError extends Error {\n constructor() {\n super('Поток событий отверг токен доступа');\n this.name = 'UnauthorizedStreamError';\n }\n}\n","import { NotificationType } from '../types/enums.js';\n\n/**\n * Соответствие коротких имён типов уведомлений развёрнутым.\n *\n * Сервер — и в списке, и в потоке событий — присылает короткие имена: `like`, `comment`,\n * `reply`, `repost`, `comment_like`. Развёрнутые (`post_reaction`, `post_comment`)\n * встречаются в оформлении интерфейса, поэтому библиотека приводит типы к ним:\n * они однозначно называют и объект, и действие.\n *\n * Пришедшее значение всегда остаётся в поле `rawType`.\n */\nexport const NOTIFICATION_TYPE_ALIASES: Readonly<Record<string, NotificationType>> = Object.freeze({\n like: NotificationType.PostReaction,\n comment: NotificationType.PostComment,\n comment_like: NotificationType.CommentReaction,\n reply: NotificationType.CommentReply,\n repost: NotificationType.PostRepost,\n mention: NotificationType.PostMention,\n});\n\nconst KNOWN_TYPES = new Set<string>(Object.values(NotificationType));\n\n/**\n * Приводит имя типа к каноническому.\n *\n * Неизвестное значение возвращается без изменений, чтобы не менять смысл нового типа\n * уведомления на другой.\n *\n * @example\n * ```ts\n * canonicalNotificationType('like'); // 'post_reaction'\n * canonicalNotificationType('post_reaction'); // 'post_reaction'\n * canonicalNotificationType('новое_событие'); // 'новое_событие'\n * ```\n */\nexport function canonicalNotificationType(rawType: string): NotificationType {\n return NOTIFICATION_TYPE_ALIASES[rawType] ?? rawType;\n}\n\n/**\n * Известен ли библиотеке этот тип уведомления.\n *\n * Полезно, чтобы решить, показывать ли уведомление, для которого нет своего оформления.\n */\nexport function isKnownNotificationType(type: string): boolean {\n return KNOWN_TYPES.has(canonicalNotificationType(type));\n}\n","import { asString, isRecord } from '../core/unwrap.js';\nimport type { Notification } from '../models/notifications.js';\nimport type { Actor } from '../models/users.js';\nimport { canonicalNotificationType } from './type-map.js';\n\n/** Событие потока уведомлений после разбора. */\nexport interface NotificationEvent {\n /** Само уведомление в единой форме. */\n notification: Notification;\n /**\n * Актуальное число непрочитанных, если сервер его сообщил.\n *\n * Клиент не увеличивает счётчик сам: значение приходит с сервера.\n */\n unreadCount: number | undefined;\n /** Нужно ли проиграть звук. */\n sound: boolean;\n}\n\nfunction asActor(value: unknown): Actor | undefined {\n if (!isRecord(value)) return undefined;\n const id = asString(value.id);\n if (!id) return undefined;\n\n return {\n id,\n username: asString(value.username) ?? '',\n displayName: asString(value.displayName) ?? '',\n avatar: asString(value.avatar) ?? '',\n ...(typeof value.isFollowing === 'boolean' ? { isFollowing: value.isFollowing } : {}),\n ...(typeof value.isFollowedBy === 'boolean' ? { isFollowedBy: value.isFollowedBy } : {}),\n };\n}\n\n/** Собирает участников: сервер присылает либо одного `actor`, либо массив `actors`. */\nfunction readActors(source: Record<string, unknown>): Actor[] {\n if (Array.isArray(source.actors)) {\n return source.actors.map(asActor).filter((actor): actor is Actor => actor !== undefined);\n }\n\n const single = asActor(source.actor);\n return single ? [single] : [];\n}\n\n/**\n * Приводит уведомление к единой форме.\n *\n * Нужна потому, что REST-список и поток событий описывают одно и то же событие по-разному:\n * различаются имена типов (`like` против `post_reaction`), имена полей\n * (`targetId`/`entityId`, `read`/`isRead`, `preview`/`entityPreview`) и число участников\n * (`actor` против массива `actors`). После приведения объекты из обоих источников\n * можно складывать в один список.\n *\n * Исходные данные не теряются: имя типа с сервера остаётся в `rawType`,\n * весь объект целиком — в `raw`.\n *\n * @param input уведомление из REST-ответа либо полезная нагрузка события потока\n *\n * @example\n * ```ts\n * const fromRest = normalizeNotification(restItem);\n * const fromStream = normalizeNotification(event.payload);\n * // одинаковая форма — можно объединять\n * ```\n */\nexport function normalizeNotification(input: unknown): Notification {\n const source = isRecord(input) ? input : {};\n\n // Поток кладёт полезную нагрузку то в payload, то прямо в корень события.\n const payload = isRecord(source.payload) ? source.payload : source;\n\n const rawType = asString(payload.type) ?? asString(source.type) ?? '';\n const createdAt = asString(payload.createdAt) ?? asString(source.createdAt) ?? '';\n const readAt = asString(payload.readAt) ?? asString(source.readAt);\n\n const isRead =\n typeof payload.isRead === 'boolean'\n ? payload.isRead\n : typeof payload.read === 'boolean'\n ? payload.read\n : Boolean(readAt);\n\n // Сервер описывает событие парой «цель — предмет», и смысл пары зависит от типа:\n //\n // комментарий, реакция на него — цель = пост, предмет = комментарий\n // репост — цель = сам репост, предмет = исходный пост\n // реакция на пост, подписка — предмета нет\n //\n // Событие относится к предмету, только когда тот является комментарием; иначе объект\n // события — цель. Без этого различия ссылка на репост вела бы на чужой исходный пост.\n const subjectId = asString(payload.subjectId);\n const targetId = asString(payload.targetId);\n const subjectIsComment = payload.subjectType === 'comment';\n const clickUrl = asString(payload.clickUrl);\n\n return {\n id: asString(payload.id) ?? asString(source.id) ?? '',\n type: canonicalNotificationType(rawType),\n rawType,\n entityId:\n asString(payload.entityId) ?? (subjectIsComment ? (subjectId ?? targetId) : targetId) ?? null,\n parentEntityId:\n asString(payload.parentEntityId) ?? (subjectIsComment ? (targetId ?? null) : null),\n isRead,\n actors: readActors(payload),\n count: typeof payload.count === 'number' && payload.count > 0 ? payload.count : 1,\n preview: asString(payload.entityPreview) ?? asString(payload.preview) ?? null,\n ...(clickUrl ? { clickUrl } : {}),\n createdAt,\n updatedAt: asString(payload.updatedAt) ?? readAt ?? createdAt,\n raw: input,\n };\n}\n\n/**\n * Разбирает событие `notification` из потока.\n *\n * Кроме самого уведомления событие несёт служебные поля уровня конверта: актуальный\n * счётчик непрочитанных и признак звука.\n */\nexport function readNotificationEvent(data: unknown): NotificationEvent {\n const source = isRecord(data) ? data : {};\n\n return {\n notification: normalizeNotification(data),\n unreadCount: typeof source.unreadCount === 'number' ? source.unreadCount : undefined,\n sound: source.sound === true,\n };\n}\n\n/**\n * Разбирает событие `unread_count` из потока.\n *\n * Возвращает `undefined`, если сервер прислал событие без вложенного `payload`.\n */\nexport function readUnreadCountEvent(data: unknown): number | undefined {\n if (!isRecord(data)) return undefined;\n\n const payload = isRecord(data.payload) ? data.payload : undefined;\n if (!payload) return undefined;\n\n return typeof payload.count === 'number' ? payload.count : undefined;\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { Notification } from '../models/notifications.js';\nimport type { NotificationEvent } from '../notifications/normalize.js';\nimport { readNotificationEvent, readUnreadCountEvent } from '../notifications/normalize.js';\nimport type { NotificationType } from '../types/enums.js';\nimport type { ItdRealtime } from './stream.js';\nimport type { TransportEvent } from './transport.js';\n\n/** Типы нормализованных обновлений потока. */\nexport const RealtimeUpdateType = Object.freeze({\n Notification: 'notification',\n UnreadCount: 'unreadCount',\n Unknown: 'unknown',\n} as const);\n\n/** Источники нормализованных обновлений потока. */\nexport const RealtimeUpdateOrigin = Object.freeze({\n Stream: 'stream',\n Sync: 'sync',\n} as const);\nexport type RealtimeUpdateOrigin = (typeof RealtimeUpdateOrigin)[keyof typeof RealtimeUpdateOrigin];\n\n/** Уведомление с типом, суженным фильтром потока. */\nexport type NotificationOfType<T extends NotificationType> = Omit<Notification, 'type'> & {\n type: T;\n};\n\n/** Конверт уведомления с типом, суженным фильтром потока. */\nexport type NotificationEventOfType<T extends NotificationType> = Omit<\n NotificationEvent,\n 'notification'\n> & {\n notification: NotificationOfType<T>;\n};\n\n/** Нормализованное уведомление из потока. */\nexport interface RealtimeNotificationUpdate<T extends NotificationType = NotificationType> {\n readonly type: typeof RealtimeUpdateType.Notification;\n readonly data: NotificationEventOfType<T>;\n}\n\n/** Актуальное число непрочитанных уведомлений. */\nexport interface RealtimeUnreadCountUpdate {\n readonly type: typeof RealtimeUpdateType.UnreadCount;\n readonly data: number;\n}\n\n/** Неизвестное библиотеке событие потока. */\nexport interface RealtimeUnknownUpdate {\n readonly type: typeof RealtimeUpdateType.Unknown;\n readonly name: string;\n readonly data: unknown;\n}\n\n/** Данные, проходящие через промежуточные обработчики потока. */\nexport type RealtimeUpdate =\n | RealtimeNotificationUpdate\n | RealtimeUnreadCountUpdate\n | RealtimeUnknownUpdate;\n\n/** Тип нормализованного обновления потока. */\nexport type RealtimeUpdateType = RealtimeUpdate['type'];\n\n/** Обновление потока указанного типа. */\nexport type RealtimeUpdateOfType<T extends RealtimeUpdateType> = Extract<\n RealtimeUpdate,\n { type: T }\n>;\n\n/**\n * Общая форма контекста обработки: то, что есть у любого потока независимо от домена.\n *\n * Контекст — обычный объектный литерал, а не класс с геттерами и не `Object.freeze`:\n * плагины-флейворы присваивают в него свои поля (`ctx.session = …`), а `@itd-api/hydrate`\n * подменяет `update` и `stream` через `Object.defineProperty`.\n *\n * @typeParam U нормализованное обновление домена\n * @typeParam S поток, который его получил\n */\nexport interface RealtimeContextBase<U = unknown, S = unknown> {\n /** Нормализованные данные обновления. */\n readonly update: U;\n /** Поток, который получил обновление. */\n readonly stream: S;\n /** Исходный кадр транспорта. Для начальной REST-синхронизации равен `undefined`. */\n readonly raw: TransportEvent | undefined;\n /** Откуда получены данные. */\n readonly origin: RealtimeUpdateOrigin;\n}\n\n/** Контекст обработки одного обновления потока уведомлений. */\nexport type RealtimeContext<U extends RealtimeUpdate = RealtimeUpdate> = RealtimeContextBase<\n U,\n ItdRealtime\n>;\n\n/** Контекст уведомления с типом, суженным фильтром. */\nexport type RealtimeNotificationContext<T extends NotificationType = NotificationType> =\n RealtimeContext<RealtimeNotificationUpdate<T>>;\n\n/** Условия отбора уведомлений. Все указанные поля объединяются через логическое И. */\nexport interface RealtimeNotificationFilter<T extends NotificationType = NotificationType> {\n /** Один или несколько канонических типов уведомления. */\n type?: T | readonly T[];\n /** Идентификатор хотя бы одного участника уведомления. */\n actorId?: string;\n /** Идентификатор объекта события. */\n entityId?: string | null;\n /** Идентификатор родительского объекта. */\n parentEntityId?: string | null;\n /** Дополнительная проверка после сопоставления полей. */\n predicate?: (context: RealtimeNotificationContext<T>) => boolean;\n}\n\n/** Краткая или объектная форма фильтра уведомлений. */\nexport type RealtimeNotificationSelector<T extends NotificationType = NotificationType> =\n | T\n | readonly T[]\n | RealtimeNotificationFilter<T>;\n\n/** Проверяет форму фильтра уведомлений. */\nexport function validateNotificationSelector(selector: unknown): void {\n if (typeof selector === 'string') {\n if (selector.length === 0) throw new ItdConfigError('Тип уведомления не должен быть пустым');\n return;\n }\n\n if (Array.isArray(selector)) {\n if (selector.length === 0 || selector.some((type) => typeof type !== 'string' || !type)) {\n throw new ItdConfigError('Список типов уведомлений должен содержать непустые строки');\n }\n return;\n }\n\n if (typeof selector !== 'object' || selector === null) {\n throw new ItdConfigError('Фильтр уведомлений должен быть строкой, списком или объектом');\n }\n\n const filter = selector as Record<string, unknown>;\n if (filter.type !== undefined) validateNotificationSelector(filter.type);\n if (filter.actorId !== undefined && typeof filter.actorId !== 'string') {\n throw new ItdConfigError('Фильтр уведомлений: actorId должен быть строкой');\n }\n for (const field of ['entityId', 'parentEntityId'] as const) {\n const value = filter[field];\n if (value !== undefined && value !== null && typeof value !== 'string') {\n throw new ItdConfigError(`Фильтр уведомлений: ${field} должен быть строкой или null`);\n }\n }\n if (filter.predicate !== undefined && typeof filter.predicate !== 'function') {\n throw new ItdConfigError('Фильтр уведомлений: predicate должен быть функцией');\n }\n}\n\n/** Преобразует транспортный кадр в одно логическое обновление. */\nexport function readRealtimeUpdate(event: TransportEvent): RealtimeUpdate | undefined {\n if (event.name === 'notification') {\n return {\n type: RealtimeUpdateType.Notification,\n data: readNotificationEvent(event.data),\n };\n }\n\n if (event.name === 'unread_count') {\n const count = readUnreadCountEvent(event.data);\n return count === undefined ? undefined : { type: RealtimeUpdateType.UnreadCount, data: count };\n }\n\n return { type: RealtimeUpdateType.Unknown, name: event.name, data: event.data };\n}\n\n/** Проверяет объектный или краткий фильтр уведомления. */\nexport function matchesNotification<T extends NotificationType>(\n context: RealtimeNotificationContext,\n selector: RealtimeNotificationSelector<T>,\n): context is RealtimeNotificationContext<T> {\n const notification = context.update.data.notification;\n\n if (typeof selector === 'string') return notification.type === selector;\n if (Array.isArray(selector)) return selector.includes(notification.type as T);\n\n const filter = selector as RealtimeNotificationFilter<T>;\n const types = filter.type === undefined ? undefined : [filter.type].flat();\n\n if (types && !types.includes(notification.type as T)) return false;\n if (\n filter.actorId !== undefined &&\n !notification.actors.some(({ id }) => id === filter.actorId)\n ) {\n return false;\n }\n if (filter.entityId !== undefined && notification.entityId !== filter.entityId) return false;\n if (\n filter.parentEntityId !== undefined &&\n notification.parentEntityId !== filter.parentEntityId\n ) {\n return false;\n }\n\n return filter.predicate?.(context as RealtimeNotificationContext<T>) ?? true;\n}\n\n/** Сужает произвольный контекст потока до контекста уведомления. */\nexport function isNotificationContext(\n context: RealtimeContext,\n): context is RealtimeNotificationContext {\n return context.update.type === RealtimeUpdateType.Notification;\n}\n","import { type ItdClock, systemClock } from '../core/clock.js';\nimport { Emitter, type Listener, reportListenerError, type Unsubscribe } from '../core/emitter.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport { RealtimeStatus } from '../types/enums.js';\nimport type { Logger } from '../types/options.js';\nimport {\n deferRealtimeMiddleware,\n RealtimeDispatcher,\n type RealtimeHandler,\n type RealtimeMiddleware,\n type RealtimeMiddlewareObj,\n type RealtimePredicate,\n type RealtimeSequentializer,\n} from './middleware.js';\nimport { MAX_RECONNECT_ATTEMPTS, type ReconnectOptions, reconnectDelay } from './reconnect.js';\nimport {\n type RealtimeRequest,\n type RealtimeTransport,\n type TransportEvent,\n UnauthorizedStreamError,\n} from './transport.js';\nimport { type RealtimeContextBase, RealtimeUpdateOrigin } from './updates.js';\n\n/** Зачем движок просит домен синхронизироваться. */\nexport type RealtimeSyncReason = 'initial' | 'reconnect';\n\n/** Доставляет обновление, полученное во время синхронизации, если её запуск ещё актуален. */\ntype RealtimeSyncDispatch<U> = (update: U) => void;\n\n/**\n * События, которые движок рассылает сам.\n *\n * Ни одно из них не зависит от домена, поэтому они одинаковы у любого потока.\n * Домен расширяет эту карту своими событиями.\n */\nexport interface RealtimeEngineEvents<C extends RealtimeContextBase = RealtimeContextBase> {\n /** Изменилось состояние соединения. */\n status: RealtimeStatus;\n /** Соединение оборвалось; будет предпринята попытка переподключения. */\n error: { error: unknown; willReconnect: boolean };\n /** Сообщение не удалось разобрать. Соединение при этом продолжает работать. */\n parseError: { error: unknown; raw: string };\n /** Запланировано переподключение. */\n reconnect: { attempt: number; delay: number };\n /** Попытки исчерпаны — соединение восстановится только ручным `connect()`. */\n giveup: undefined;\n /** Любой исходный кадр транспорта. Отправляется до нормализации и обработчиков. */\n message: TransportEvent;\n /** Промежуточный обработчик потока завершился исключением. */\n middlewareError: { error: unknown; context: C };\n /** Обработчик обновления завершился исключением. */\n handlerError: { error: unknown; context: C };\n}\n\n/**\n * Что движок получает от домена.\n *\n * @typeParam U нормализованное обновление домена\n * @typeParam C контекст обработки, который домен собирает вокруг обновления\n *\n * @internal\n */\nexport interface RealtimeEngineDeps<U, C extends RealtimeContextBase<U, unknown>> {\n baseUrl: string;\n /** Разрешено ли транспорту передавать токен этому сервису. */\n authorize: boolean;\n fetch: typeof fetch;\n /** Конвейер клиента — см. {@link TransportContext.request}. */\n request?: RealtimeRequest | undefined;\n clock?: ItdClock | undefined;\n /** Общие заголовки клиента для адреса — см. {@link TransportContext.baseHeaders}. */\n baseHeaders: (url: string) => Promise<Headers>;\n getToken: () => Promise<string | null>;\n /** Обновляет токен после отказа авторизации. Возвращает `true`, если удалось. */\n refresh: () => Promise<boolean>;\n /** Вызывается при запуске ранее закрытого потока. */\n onConnect?: (() => void) | undefined;\n /** Вызывается при явном закрытии потока. */\n onClose?: (() => void) | undefined;\n logger?: Logger | undefined;\n\n transport: RealtimeTransport;\n\n /**\n * Доменная обработка сырого кадра до нормализации. `true` — кадр поглощён и дальше\n * не идёт. Нужна там, где кадр не является обновлением: у уведомлений так приходит\n * `connected`.\n */\n handleFrame?: ((event: TransportEvent) => boolean) | undefined;\n /** Превращает кадр в доменное обновление; `undefined` — кадр игнорируется. */\n readUpdate: (event: TransportEvent) => U | undefined;\n /**\n * Ключ коалесцирования: ожидающее обновление с тем же ключом заменяется новым.\n * `undefined` — обновление не коалесцируется.\n */\n coalesceKey?: ((update: U) => PropertyKey | undefined) | undefined;\n /** Собирает контекст. Точка, куда домен добавляет свои поля и действия. */\n createContext: (update: U, raw: TransportEvent | undefined, origin: RealtimeUpdateOrigin) => C;\n /** Доставляет обновление доменным подписчикам после цепочки обработчиков. */\n deliver: (update: U) => void;\n /**\n * Снимок и догон: до первого подключения и перед каждым переподключением.\n *\n * Идёт через обычный конвейер клиента, поэтому заодно обновляет протухший токен.\n * Полученные обновления передаются в `dispatch`: движок отбросит их, если за время\n * запроса поток успели закрыть или запустить заново. Исключение не отменяет подключение —\n * оно уходит в логгер.\n */\n sync?:\n | ((reason: RealtimeSyncReason, dispatch: RealtimeSyncDispatch<U>) => Promise<void>)\n | undefined;\n}\n\n/** Настройки движка: переподключение, параллелизм и реакция на среду. */\nexport interface RealtimeEngineOptions<C extends RealtimeContextBase = RealtimeContextBase>\n extends ReconnectOptions {\n /** Максимальное число одновременно обрабатываемых обновлений. По умолчанию 1. */\n concurrency?: number | undefined;\n /** Возвращает ключи обновлений, которые нельзя обрабатывать одновременно. */\n sequentialize?: RealtimeSequentializer<C> | undefined;\n /** Переподключаться, когда вкладка снова становится видимой. По умолчанию `true`. */\n reconnectOnVisible?: boolean | undefined;\n /** Переподключаться при восстановлении сети. По умолчанию `true`. */\n reconnectOnOnline?: boolean | undefined;\n}\n\n/** Проверяет настройки общей механики потока. */\nfunction validateRealtimeEngineOptions<C extends RealtimeContextBase>(\n options: RealtimeEngineOptions<C>,\n): void {\n const positiveInteger = (value: number | undefined, name: string): void => {\n if (value === undefined) return;\n if (!Number.isInteger(value) || value < 0) {\n throw new ItdConfigError(\n `realtime.${name} должен быть целым неотрицательным числом, получено: ${value}`,\n );\n }\n };\n const duration = (value: number, name: string): void => {\n if (!Number.isFinite(value) || value < 0) {\n throw new ItdConfigError(\n `realtime.${name} должен быть числом не меньше 0, получено: ${value}`,\n );\n }\n };\n\n positiveInteger(options.maxAttempts, 'maxAttempts');\n positiveInteger(options.concurrency, 'concurrency');\n if (options.concurrency === 0) {\n throw new ItdConfigError('realtime.concurrency должен быть больше нуля');\n }\n if (options.jitter !== undefined && !(options.jitter >= 0 && options.jitter <= 1)) {\n throw new ItdConfigError(\n `realtime.jitter должен быть в диапазоне 0…1, получено: ${options.jitter}`,\n );\n }\n if (options.backoff !== undefined) {\n if (!Array.isArray(options.backoff) || options.backoff.length === 0) {\n throw new ItdConfigError('realtime.backoff должен быть непустым списком пауз');\n }\n for (const delay of options.backoff) duration(delay, 'backoff');\n }\n if (options.sequentialize !== undefined && typeof options.sequentialize !== 'function') {\n throw new ItdConfigError('realtime.sequentialize должен быть функцией');\n }\n}\n\n/**\n * Общая механика потока событий: соединение, переподключение с backoff, обновление\n * токена, реакция на среду, статусы и очередь обработчиков.\n *\n * Ничего не знает о домене: что считать обновлением, как собрать контекст и кому его\n * доставить, решают {@link RealtimeEngineDeps}. Поэтому один и тот же движок несёт\n * и поток уведомлений, и любой другой.\n *\n * @internal\n */\nexport class RealtimeEngine<\n U,\n C extends RealtimeContextBase<U, unknown>,\n E extends RealtimeEngineEvents<C> = RealtimeEngineEvents<C>,\n> {\n readonly #deps: RealtimeEngineDeps<U, C>;\n readonly #options: RealtimeEngineOptions<C>;\n readonly #dispatcher: RealtimeDispatcher<C>;\n readonly #emitter: Emitter<E>;\n readonly #clock: ItdClock;\n readonly #maxAttempts: number;\n\n #controller: AbortController | undefined;\n /**\n * Хочет ли вызывающий код, чтобы соединение было живо.\n *\n * Отдельно от `#controller`, потому что тот появляется только после `await` внутри\n * {@link connect}. Без этого флага два вызова подряд проскочили бы проверку оба\n * и подняли два соединения, а `disconnect()` во время синхронизации не был бы замечен\n * и соединение поднялось бы уже после отмены.\n */\n #wanted = false;\n #status: RealtimeStatus = RealtimeStatus.Disconnected;\n #attempt = 0;\n #generation = 0;\n #starting: object | undefined;\n #cancelTimer: (() => void) | undefined;\n #detachEnvironment: (() => void) | undefined;\n\n constructor(deps: RealtimeEngineDeps<U, C>, options: RealtimeEngineOptions<C> = {}) {\n validateRealtimeEngineOptions(options);\n\n this.#deps = deps;\n this.#options = options;\n this.#clock = deps.clock ?? systemClock;\n this.#maxAttempts = options.maxAttempts ?? MAX_RECONNECT_ATTEMPTS;\n this.#emitter = new Emitter<E>((error) => reportListenerError(deps.logger, 'realtime', error));\n this.#dispatcher = new RealtimeDispatcher<C>(\n {\n concurrency: options.concurrency ?? 1,\n ...(options.sequentialize ? { sequentialize: options.sequentialize } : {}),\n },\n {\n deliver: (context) => this.#deps.deliver(context.update),\n middlewareError: (error, context) =>\n this.#reportDispatchError('middlewareError', error, context),\n handlerError: (error, context) => this.#reportDispatchError('handlerError', error, context),\n overflow: () => this.#handleOverflow(),\n },\n );\n }\n\n /** Текущее состояние соединения. */\n get status(): RealtimeStatus {\n return this.#status;\n }\n\n /** Имя используемого транспорта. */\n get transport(): string {\n return this.#deps.transport.name;\n }\n\n /** Подписывается на событие потока. @returns функция отписки */\n on<K extends keyof E>(event: K, listener: Listener<E[K]>): Unsubscribe {\n return this.#emitter.on(event, listener);\n }\n\n /** Подписывается на одно срабатывание события потока. */\n once<K extends keyof E>(event: K, listener: Listener<E[K]>): Unsubscribe {\n return this.#emitter.once(event, listener);\n }\n\n /** Рассылает доменное событие через общий эмиттер потока. */\n emit<K extends keyof E>(event: K, payload: E[K]): void {\n this.#emitter.emit(event, payload);\n }\n\n /** Снимает подписки на события. Обработчики обновлений и middleware остаются. */\n removeAllListeners(): void {\n this.#emitter.removeAllListeners();\n }\n\n /** Добавляет промежуточный обработчик. @returns функция его удаления */\n use(middleware: RealtimeMiddleware<C> | RealtimeMiddlewareObj<C>): Unsubscribe {\n if (typeof middleware === 'function') return this.#dispatcher.use(middleware);\n if (\n typeof middleware !== 'object' ||\n middleware === null ||\n typeof middleware.middleware !== 'function'\n ) {\n throw new ItdConfigError(\n 'realtime.use() принимает функцию обработки или объект с middleware()',\n );\n }\n return this.#dispatcher.use(deferRealtimeMiddleware(middleware));\n }\n\n /** Подписывает обработчик обновлений, подходящих под условие. */\n onUpdate(predicate: RealtimePredicate<C>, handler: RealtimeHandler<C>): Unsubscribe {\n return this.#dispatcher.on(predicate, handler);\n }\n\n /**\n * Поднимает соединение.\n *\n * Повторный вызов при уже живом соединении ничего не делает. Возвращает управление\n * сразу после запуска: соединение живёт в фоне.\n */\n async connect(): Promise<void> {\n if (this.#wanted) return;\n this.#wanted = true;\n const generation = ++this.#generation;\n const starting = {};\n this.#starting = starting;\n this.#deps.onConnect?.();\n\n this.#attachEnvironmentListeners();\n await this.#sync('initial', generation, starting);\n\n // Пока шла синхронизация, поток могли закрыть и даже запустить заново.\n if (this.#starting !== starting) return;\n this.#starting = undefined;\n if (this.#isCurrentGeneration(generation)) this.#run(generation);\n }\n\n /** Закрывает соединение и отменяет запланированные попытки. */\n disconnect(): void {\n this.#wanted = false;\n this.#generation += 1;\n this.#starting = undefined;\n\n if (this.#cancelTimer) {\n this.#cancelTimer();\n this.#cancelTimer = undefined;\n }\n\n this.#detachEnvironment?.();\n this.#detachEnvironment = undefined;\n\n this.#controller?.abort();\n this.#controller = undefined;\n this.#attempt = 0;\n this.#dispatcher.clearPending();\n\n this.#setStatus(RealtimeStatus.Disconnected);\n this.#deps.onClose?.();\n }\n\n /** Ждёт завершения всех принятых обновлений. */\n drain(): Promise<void> {\n return this.#dispatcher.drain();\n }\n\n /** Пропускает актуальное обновление через цепочку обработчиков. */\n #dispatch(update: U, raw: TransportEvent | undefined, origin: RealtimeUpdateOrigin): void {\n if (!this.#wanted) return;\n this.#dispatcher.dispatch(\n this.#deps.createContext(update, raw, origin),\n this.#deps.coalesceKey?.(update),\n );\n }\n\n /**\n * Очередь обновлений достигла предела: закрывает соединение и переподключается,\n * когда обработчики разберут очередь.\n */\n #handleOverflow(): void {\n const controller = this.#controller;\n if (!this.#wanted || !controller) return;\n\n const generation = this.#generation;\n controller.abort();\n this.#controller = undefined;\n this.#setStatus(RealtimeStatus.Error);\n\n const error = new Error('Обработчики не успевают за потоком: очередь обновлений переполнена');\n void this.#dispatcher.drain().then(() => {\n if (this.#isCurrentGeneration(generation) && !this.#controller) {\n this.#scheduleReconnect(error);\n }\n });\n }\n\n /** Выполняет доменную синхронизацию, не роняя подключение из-за её ошибки. */\n async #sync(reason: RealtimeSyncReason, generation: number, starting: object): Promise<void> {\n if (!this.#deps.sync) return;\n\n try {\n await this.#deps.sync(reason, (update) => {\n if (this.#starting !== starting || !this.#isCurrentGeneration(generation)) return;\n this.#dispatch(update, undefined, RealtimeUpdateOrigin.Sync);\n });\n } catch (error) {\n this.#deps.logger?.debug(`не удалось синхронизировать поток (${reason})`, error);\n }\n }\n\n /** Запускает попытку подключения; повторы планирует сам. */\n #run(generation = this.#generation): void {\n if (!this.#isCurrentGeneration(generation)) return;\n\n // Страховка от потерянного соединения: если предыдущее ещё живо, закрываем его,\n // иначе его AbortController остался бы недостижимым и поток — незакрытым.\n this.#controller?.abort();\n\n const controller = new AbortController();\n this.#controller = controller;\n this.#setStatus(RealtimeStatus.Connecting);\n\n void this.#deps.transport\n .connect({\n baseUrl: this.#deps.baseUrl,\n authorize: this.#deps.authorize,\n fetch: this.#deps.fetch,\n request: this.#deps.request,\n baseHeaders: this.#deps.baseHeaders,\n getToken: this.#deps.getToken,\n signal: controller.signal,\n onOpen: () => {\n if (!this.#isCurrentConnection(controller, generation)) return;\n this.#attempt = 0;\n this.#setStatus(RealtimeStatus.Connected);\n },\n onEvent: (event) => {\n if (this.#isCurrentConnection(controller, generation)) this.#handleEvent(event);\n },\n onParseError: (error, raw) => {\n if (this.#isCurrentConnection(controller, generation)) {\n this.#emitEngine('parseError', { error, raw });\n }\n },\n })\n .then(\n () => {\n // Штатное закрытие потока — тоже повод переподключиться.\n if (this.#isCurrentConnection(controller, generation)) {\n this.#handleFailure(new Error('Соединение с потоком событий закрыто'));\n }\n },\n (error: unknown) => {\n if (this.#isCurrentConnection(controller, generation)) this.#handleFailure(error);\n },\n );\n }\n\n #handleEvent(event: TransportEvent): void {\n if (!this.#wanted) return;\n this.#emitEngine('message', event);\n\n if (this.#deps.handleFrame?.(event)) return;\n\n const update = this.#deps.readUpdate(event);\n if (update !== undefined) this.#dispatch(update, event, RealtimeUpdateOrigin.Stream);\n }\n\n #reportDispatchError(\n event: 'middlewareError' | 'handlerError',\n error: unknown,\n context: C,\n ): void {\n if (this.#emitter.listenerCount(event) > 0) {\n this.#emitEngine(event, { error, context });\n return;\n }\n\n const message =\n event === 'middlewareError'\n ? 'Ошибка в промежуточном обработчике потока'\n : 'Ошибка в обработчике обновления потока';\n if (this.#deps.logger) this.#deps.logger.error(message, error);\n else console.error(`[itd-api] ${message}`, error);\n }\n\n #handleFailure(error: unknown): void {\n this.#controller = undefined;\n\n if (error instanceof UnauthorizedStreamError) {\n void this.#refreshAndReconnect(error);\n return;\n }\n\n this.#setStatus(RealtimeStatus.Error);\n this.#scheduleReconnect(error);\n }\n\n /** Обновляет токен и переподключается; при неудаче прекращает попытки. */\n async #refreshAndReconnect(error: unknown): Promise<void> {\n const generation = this.#generation;\n const starting = {};\n this.#starting = starting;\n this.#setStatus(RealtimeStatus.Error);\n\n const refreshed = await this.#deps.refresh().catch(() => false);\n\n if (this.#starting !== starting || !this.#isCurrentGeneration(generation)) return;\n this.#starting = undefined;\n\n if (!refreshed) {\n this.#giveUp(error);\n return;\n }\n\n this.#scheduleReconnect(error);\n }\n\n #scheduleReconnect(error: unknown): void {\n if (!this.#wanted) return;\n\n if (this.#attempt >= this.#maxAttempts) {\n this.#giveUp(error);\n return;\n }\n\n const delay = reconnectDelay(this.#attempt, this.#options);\n this.#attempt += 1;\n\n this.#emitEngine('error', { error, willReconnect: true });\n this.#emitEngine('reconnect', { attempt: this.#attempt, delay });\n\n this.#cancelTimer = this.#clock.schedule(() => {\n this.#cancelTimer = undefined;\n void this.#reconnect();\n }, delay);\n }\n\n /** Догон через REST идёт до сокета: он же обновляет протухший токен. */\n async #reconnect(): Promise<void> {\n if (!this.#wanted || this.#starting) return;\n\n const generation = this.#generation;\n const starting = {};\n this.#starting = starting;\n await this.#sync('reconnect', generation, starting);\n\n if (this.#starting !== starting) return;\n this.#starting = undefined;\n if (this.#isCurrentGeneration(generation)) this.#run(generation);\n }\n\n /**\n * Завершает автоматические попытки переподключения.\n *\n * Владелец узнаёт об этом так же, как при {@link disconnect}. `onClose` вызывается\n * до событий: обработчик `giveup` может тут же вызвать `connect()`.\n */\n #giveUp(error: unknown): void {\n this.#wanted = false;\n this.#generation += 1;\n this.#starting = undefined;\n this.#attempt = 0;\n\n this.#detachEnvironment?.();\n this.#detachEnvironment = undefined;\n\n this.#deps.onClose?.();\n\n this.#emitEngine('error', { error, willReconnect: false });\n this.#emitEngine('giveup', undefined);\n }\n\n /**\n * Подписывается на события среды.\n *\n * Возврат вкладки из фона и восстановление сети — самые частые причины «мёртвого»\n * соединения. У сайта итд.com такой обработки нет.\n */\n #attachEnvironmentListeners(): void {\n if (this.#detachEnvironment) return;\n\n const target = globalThis as unknown as {\n addEventListener?: (type: string, listener: () => void) => void;\n removeEventListener?: (type: string, listener: () => void) => void;\n document?: { visibilityState?: string };\n };\n\n if (typeof target.addEventListener !== 'function') return;\n\n const wake = () => {\n // Реагируем, только если соединения сейчас нет и попытка не запланирована.\n if (this.#controller || this.#cancelTimer || this.#starting) return;\n if (this.#status === RealtimeStatus.Disconnected) return;\n\n this.#attempt = 0;\n void this.#reconnect();\n };\n\n const onVisibility = () => {\n if (target.document?.visibilityState === 'visible') wake();\n };\n\n const listeners: [string, () => void][] = [];\n\n if (this.#options.reconnectOnVisible !== false && target.document) {\n listeners.push(['visibilitychange', onVisibility]);\n }\n if (this.#options.reconnectOnOnline !== false) {\n listeners.push(['online', wake]);\n }\n\n for (const [type, listener] of listeners) target.addEventListener(type, listener);\n\n this.#detachEnvironment = () => {\n for (const [type, listener] of listeners) target.removeEventListener?.(type, listener);\n };\n }\n\n #setStatus(status: RealtimeStatus): void {\n if (this.#status === status) return;\n this.#status = status;\n this.#emitEngine('status', status);\n }\n\n #isCurrentGeneration(generation: number): boolean {\n return this.#wanted && this.#generation === generation;\n }\n\n #isCurrentConnection(controller: AbortController, generation: number): boolean {\n return (\n this.#isCurrentGeneration(generation) &&\n this.#controller === controller &&\n !controller.signal.aborted\n );\n }\n\n /** Рассылает событие из общей части карты, не теряя доменные события типа `E`. */\n #emitEngine<K extends keyof RealtimeEngineEvents<C>>(\n event: K,\n payload: RealtimeEngineEvents<C>[K],\n ): void {\n // `E` гарантированно содержит общую карту. Приведение нужно только потому, что\n // TypeScript допускает теоретическое сужение унаследованного поля в подтипе `E`.\n (this.#emitter as unknown as Emitter<RealtimeEngineEvents<C>>).emit(event, payload);\n }\n}\n","import { type ItdClock, systemClock } from '../core/clock.js';\nimport { ItdConfigError, isItdAuthError } from '../core/errors.js';\nimport { pickArray, pickNumber } from '../core/unwrap.js';\nimport type { RealtimeRequest, RealtimeTransport, TransportContext } from './transport.js';\nimport { UnauthorizedStreamError } from './transport.js';\n\n/** Настройки опроса. */\nexport interface PollTransportOptions {\n /** Часы опроса. Обычно подменяются только в тестах. */\n clock?: ItdClock;\n /** Как часто опрашивать сервер, мс. По умолчанию 15 000. */\n interval?: number;\n /** Сколько уведомлений запрашивать за раз. По умолчанию 20. */\n limit?: number;\n}\n\n/**\n * Запасной транспорт: обычный опрос REST вместо потока.\n *\n * Нужен там, где `fetch` не умеет отдавать тело по частям, — например в части сборок\n * React Native. Наружу выдаёт те же события, что и {@link SseTransport}, поэтому\n * вызывающий код разницы не замечает.\n *\n * Новыми считаются уведомления, которых не было в предыдущем ответе; чтобы список\n * известных не рос бесконечно, он ограничен последними двумя страницами.\n */\nexport class PollTransport implements RealtimeTransport {\n readonly name = 'poll';\n\n readonly #interval: number;\n readonly #limit: number;\n readonly #clock: ItdClock;\n\n constructor(options: PollTransportOptions = {}) {\n this.#clock = options.clock ?? systemClock;\n this.#interval = options.interval ?? 15_000;\n this.#limit = options.limit ?? 20;\n }\n\n async connect(context: TransportContext): Promise<void> {\n const request = context.request;\n if (!request) {\n throw new ItdConfigError(\n 'опрос уведомлений выполняется через конвейер клиента; создайте поток вызовом itd.realtime()',\n );\n }\n\n const seen = new Set<string>();\n let firstRun = true;\n let lastUnreadCount: number | undefined;\n\n while (!context.signal.aborted) {\n const payload = await this.#readUpdates(request, context.signal);\n\n // Соединение считается установленным только после первого успешного ответа: иначе\n // при постоянно недоступной сети каждая попытка обнуляла бы счётчик и maxAttempts\n // не срабатывал бы никогда.\n context.onOpen();\n\n const items = pickArray<Record<string, unknown>>(payload, 'notifications');\n\n for (const item of [...items].reverse()) {\n const id = typeof item.id === 'string' ? item.id : undefined;\n if (!id || seen.has(id)) continue;\n\n seen.add(id);\n // На первом проходе список уже существующих уведомлений не считается новыми\n // событиями: иначе подключение сразу выдало бы всю историю.\n if (!firstRun) context.onEvent({ name: 'notification', data: { payload: item } });\n }\n\n // Ограничиваем память: держим только последние две страницы идентификаторов.\n if (seen.size > this.#limit * 2) {\n const excess = [...seen].slice(0, seen.size - this.#limit * 2);\n for (const id of excess) seen.delete(id);\n }\n\n const count = await this.#readCount(request, context.signal);\n if (count !== undefined && count !== lastUnreadCount) {\n lastUnreadCount = count;\n context.onEvent({ name: 'unread_count', data: { payload: { count } } });\n }\n\n firstRun = false;\n await this.#wait(context.signal);\n }\n }\n\n async #readUpdates(request: RealtimeRequest, signal: AbortSignal): Promise<unknown> {\n try {\n return await request({\n operationId: 'realtime.poll.updates',\n path: '/api/notifications/',\n query: { limit: this.#limit, offset: 0 },\n signal,\n });\n } catch (error) {\n // Для потока это тот же отказ авторизации, что и `401` от сервера событий.\n if (isItdAuthError(error)) throw new UnauthorizedStreamError();\n throw error;\n }\n }\n\n async #readCount(request: RealtimeRequest, signal: AbortSignal): Promise<number | undefined> {\n try {\n const payload = await request({\n operationId: 'realtime.poll.unread',\n path: '/api/notifications/count',\n signal,\n });\n return pickNumber(payload, 'count', 0);\n } catch {\n // Счётчик — вспомогательная величина: его недоступность не должна рвать опрос.\n return undefined;\n }\n }\n\n /** Ждёт следующего опроса, прерываясь при отмене. */\n #wait(signal: AbortSignal): Promise<void> {\n // Подписка на уже сработавший сигнал не вызывается никогда — без этой проверки\n // отмена, пришедшая перед самым ожиданием, стоила бы лишнего интервала.\n if (signal.aborted) return Promise.resolve();\n\n return new Promise<void>((resolve) => {\n const cancel = this.#clock.schedule(finish, this.#interval);\n\n function finish() {\n cancel();\n signal.removeEventListener('abort', finish);\n resolve();\n }\n\n signal.addEventListener('abort', finish, { once: true });\n });\n }\n}\n","class ParseError extends Error {\n constructor(message, options) {\n super(message), this.name = \"ParseError\", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;\n }\n}\nconst LF = 10, CR = 13, SPACE = 32;\nfunction noop(_arg) {\n}\nfunction createParser(config) {\n if (typeof config == \"function\")\n throw new TypeError(\n \"`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?\"\n );\n const { onEvent = noop, onError = noop, onRetry = noop, onComment, maxBufferSize } = config, pendingFragments = [];\n let pendingFragmentsLength = 0, isFirstChunk = !0, id, data = \"\", dataLines = 0, eventType, terminated = !1;\n function feed(chunk) {\n if (terminated)\n throw new Error(\n \"Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.\"\n );\n if (isFirstChunk && (isFirstChunk = !1, chunk.charCodeAt(0) === 239 && chunk.charCodeAt(1) === 187 && chunk.charCodeAt(2) === 191 && (chunk = chunk.slice(3))), pendingFragments.length === 0) {\n const trailing2 = processLines(chunk);\n trailing2 !== \"\" && (pendingFragments.push(trailing2), pendingFragmentsLength = trailing2.length), checkBufferSize();\n return;\n }\n if (chunk.indexOf(`\n`) === -1 && chunk.indexOf(\"\\r\") === -1) {\n pendingFragments.push(chunk), pendingFragmentsLength += chunk.length, checkBufferSize();\n return;\n }\n pendingFragments.push(chunk);\n const input = pendingFragments.join(\"\");\n pendingFragments.length = 0, pendingFragmentsLength = 0;\n const trailing = processLines(input);\n trailing !== \"\" && (pendingFragments.push(trailing), pendingFragmentsLength = trailing.length), checkBufferSize();\n }\n function checkBufferSize() {\n maxBufferSize !== void 0 && (pendingFragmentsLength + data.length <= maxBufferSize || (terminated = !0, pendingFragments.length = 0, pendingFragmentsLength = 0, id = void 0, data = \"\", dataLines = 0, eventType = void 0, onError(\n new ParseError(`Buffered data exceeded max buffer size of ${maxBufferSize} characters`, {\n type: \"max-buffer-size-exceeded\"\n })\n )));\n }\n function processLines(chunk) {\n let searchIndex = 0;\n if (chunk.indexOf(\"\\r\") === -1) {\n let lfIndex = chunk.indexOf(`\n`, searchIndex);\n for (; lfIndex !== -1; ) {\n if (searchIndex === lfIndex) {\n dataLines > 0 && onEvent({ id, event: eventType, data }), id = void 0, data = \"\", dataLines = 0, eventType = void 0, searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`\n`, searchIndex);\n continue;\n }\n const firstCharCode = chunk.charCodeAt(searchIndex);\n if (isDataPrefix(chunk, searchIndex, firstCharCode)) {\n const valueStart = chunk.charCodeAt(searchIndex + 5) === SPACE ? searchIndex + 6 : searchIndex + 5, value = chunk.slice(valueStart, lfIndex);\n if (dataLines === 0 && chunk.charCodeAt(lfIndex + 1) === LF) {\n onEvent({ id, event: eventType, data: value }), id = void 0, data = \"\", eventType = void 0, searchIndex = lfIndex + 2, lfIndex = chunk.indexOf(`\n`, searchIndex);\n continue;\n }\n data = dataLines === 0 ? value : `${data}\n${value}`, dataLines++;\n } else isEventPrefix(chunk, searchIndex, firstCharCode) ? eventType = chunk.slice(\n chunk.charCodeAt(searchIndex + 6) === SPACE ? searchIndex + 7 : searchIndex + 6,\n lfIndex\n ) || void 0 : parseLine(chunk, searchIndex, lfIndex);\n searchIndex = lfIndex + 1, lfIndex = chunk.indexOf(`\n`, searchIndex);\n }\n return chunk.slice(searchIndex);\n }\n for (; searchIndex < chunk.length; ) {\n const crIndex = chunk.indexOf(\"\\r\", searchIndex), lfIndex = chunk.indexOf(`\n`, searchIndex);\n let lineEnd = -1;\n if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = crIndex < lfIndex ? crIndex : lfIndex : crIndex !== -1 ? crIndex === chunk.length - 1 ? lineEnd = -1 : lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1)\n break;\n parseLine(chunk, searchIndex, lineEnd), searchIndex = lineEnd + 1, chunk.charCodeAt(searchIndex - 1) === CR && chunk.charCodeAt(searchIndex) === LF && searchIndex++;\n }\n return chunk.slice(searchIndex);\n }\n function parseLine(chunk, start, end) {\n if (start === end) {\n dispatchEvent();\n return;\n }\n const firstCharCode = chunk.charCodeAt(start);\n if (isDataPrefix(chunk, start, firstCharCode)) {\n const valueStart = chunk.charCodeAt(start + 5) === SPACE ? start + 6 : start + 5, value2 = chunk.slice(valueStart, end);\n data = dataLines === 0 ? value2 : `${data}\n${value2}`, dataLines++;\n return;\n }\n if (isEventPrefix(chunk, start, firstCharCode)) {\n eventType = chunk.slice(chunk.charCodeAt(start + 6) === SPACE ? start + 7 : start + 6, end) || void 0;\n return;\n }\n if (firstCharCode === 105 && chunk.charCodeAt(start + 1) === 100 && chunk.charCodeAt(start + 2) === 58) {\n const value2 = chunk.slice(chunk.charCodeAt(start + 3) === SPACE ? start + 4 : start + 3, end);\n id = value2.includes(\"\\0\") ? void 0 : value2;\n return;\n }\n if (firstCharCode === 58) {\n if (onComment) {\n const line2 = chunk.slice(start, end);\n onComment(line2.slice(chunk.charCodeAt(start + 1) === SPACE ? 2 : 1));\n }\n return;\n }\n const line = chunk.slice(start, end), fieldSeparatorIndex = line.indexOf(\":\");\n if (fieldSeparatorIndex === -1) {\n processField(line, \"\", line);\n return;\n }\n const field = line.slice(0, fieldSeparatorIndex), offset = line.charCodeAt(fieldSeparatorIndex + 1) === SPACE ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);\n processField(field, value, line);\n }\n function processField(field, value, line) {\n switch (field) {\n case \"event\":\n eventType = value || void 0;\n break;\n case \"data\":\n data = dataLines === 0 ? value : `${data}\n${value}`, dataLines++;\n break;\n case \"id\":\n id = value.includes(\"\\0\") ? void 0 : value;\n break;\n case \"retry\":\n /^\\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(\n new ParseError(`Invalid \\`retry\\` value: \"${value}\"`, {\n type: \"invalid-retry\",\n value,\n line\n })\n );\n break;\n default:\n onError(\n new ParseError(\n `Unknown field \"${field.length > 20 ? `${field.slice(0, 20)}\\u2026` : field}\"`,\n { type: \"unknown-field\", field, value, line }\n )\n );\n break;\n }\n }\n function dispatchEvent() {\n dataLines > 0 && onEvent({\n id,\n event: eventType,\n data\n }), id = void 0, data = \"\", dataLines = 0, eventType = void 0;\n }\n function reset(options = {}) {\n if (options.consume && pendingFragments.length > 0) {\n const incompleteLine = pendingFragments.join(\"\");\n parseLine(incompleteLine, 0, incompleteLine.length);\n }\n isFirstChunk = !0, id = void 0, data = \"\", dataLines = 0, eventType = void 0, pendingFragments.length = 0, pendingFragmentsLength = 0, terminated = !1;\n }\n return { feed, reset };\n}\nfunction isDataPrefix(chunk, i, firstCharCode) {\n return firstCharCode === 100 && chunk.charCodeAt(i + 1) === 97 && chunk.charCodeAt(i + 2) === 116 && chunk.charCodeAt(i + 3) === 97 && chunk.charCodeAt(i + 4) === 58;\n}\nfunction isEventPrefix(chunk, i, firstCharCode) {\n return firstCharCode === 101 && chunk.charCodeAt(i + 1) === 118 && chunk.charCodeAt(i + 2) === 101 && chunk.charCodeAt(i + 3) === 110 && chunk.charCodeAt(i + 4) === 116 && chunk.charCodeAt(i + 5) === 58;\n}\nexport {\n ParseError,\n createParser\n};\n//# sourceMappingURL=index.js.map\n","import { createParser } from 'eventsource-parser';\nimport { type ItdClock, systemClock } from '../core/clock.js';\nimport { joinUrl } from '../core/url.js';\nimport {\n type RealtimeTransport,\n type TransportContext,\n UnauthorizedStreamError,\n} from './transport.js';\n\n/** Путь потока уведомлений. */\nexport const STREAM_PATH = '/api/notifications/stream';\n\n/** Настройки SSE-транспорта. */\nexport interface SseTransportOptions {\n /** Часы потока. Обычно подменяются только в тестах. */\n clock?: ItdClock;\n /**\n * Сколько миллисекунд ждать данных, прежде чем считать соединение мёртвым.\n *\n * Сервер не присылает keep-alive, а оборванное TCP-соединение может не закрыться\n * само — без этой проверки поток «тихо умирает» и новых уведомлений не приходит.\n * По умолчанию 90 000. `0` отключает проверку.\n */\n idleTimeout?: number;\n /**\n * Сколько миллисекунд ждать ответа на запрос потока, прежде чем оборвать попытку.\n *\n * Проверка молчания ({@link idleTimeout}) начинается только после получения тела ответа.\n * Если `fetch` завис на установке соединения, без этого таймаута переподключение не\n * запустится до системного сетевого таймаута. По умолчанию 20 000. `0` отключает проверку.\n */\n handshakeTimeout?: number;\n}\n\n/**\n * Транспорт поверх Server-Sent Events.\n *\n * Нативный `EventSource` не подходит: он не умеет отправлять заголовок `Authorization`,\n * а поток требует Bearer-токен. Поэтому используется `fetch` с чтением тела по частям.\n *\n * Разбор кадров делегирован `eventsource-parser`, который корректно обрабатывает\n * многострочный `data:`, перевод строки `\\r\\n`, `data:` без пробела и кадр, разорванный\n * между сетевыми чанками. В разборе на сайте итд.com каждый из этих случаев обрабатывается\n * неверно.\n */\nexport class SseTransport implements RealtimeTransport {\n readonly name = 'sse';\n\n readonly #idleTimeout: number;\n readonly #handshakeTimeout: number;\n readonly #clock: ItdClock;\n /** Идентификатор последнего события — отправляется при переподключении. */\n #lastEventId: string | undefined;\n\n constructor(options: SseTransportOptions = {}) {\n this.#clock = options.clock ?? systemClock;\n this.#idleTimeout = options.idleTimeout ?? 90_000;\n this.#handshakeTimeout = options.handshakeTimeout ?? 20_000;\n }\n\n async connect(context: TransportContext): Promise<void> {\n const token = context.authorize ? await context.getToken() : null;\n if (context.authorize && !token) throw new UnauthorizedStreamError();\n\n const url = joinUrl(context.baseUrl, STREAM_PATH);\n\n const headers = await context.baseHeaders(url);\n headers.set('Accept', 'text/event-stream');\n if (token) headers.set('Authorization', `Bearer ${token}`);\n headers.set('Cache-Control', 'no-cache');\n\n // Сервер пока не присылает id, но поддержка не мешает и пригодится, если начнёт.\n if (this.#lastEventId) headers.set('Last-Event-ID', this.#lastEventId);\n\n // Отдельный контроллер, связанный с сигналом потока: помимо внешней отмены он позволяет\n // оборвать зависшее рукопожатие своим таймаутом. Живёт до конца чтения тела.\n const controller = new AbortController();\n const relayAbort = () => controller.abort(context.signal.reason);\n if (context.signal.aborted) controller.abort(context.signal.reason);\n else context.signal.addEventListener('abort', relayAbort, { once: true });\n\n try {\n const response = await this.#handshake(url, headers, context, controller);\n\n if (context.authorize && response.status === 401) throw new UnauthorizedStreamError();\n if (!response.ok) throw new Error(`Поток уведомлений вернул статус ${response.status}`);\n if (!response.body) throw new Error('Ответ потока уведомлений пуст');\n\n context.onOpen();\n\n await this.#read(response.body, context);\n } finally {\n context.signal.removeEventListener('abort', relayAbort);\n }\n }\n\n /** Выполняет запрос потока, обрывая его, если ответ не пришёл за отведённое время. */\n async #handshake(\n url: string,\n headers: Headers,\n context: TransportContext,\n controller: AbortController,\n ): Promise<Response> {\n let expired = false;\n const cancelTimer =\n this.#handshakeTimeout > 0\n ? this.#clock.schedule(() => {\n expired = true;\n // Абортим связанный контроллер: `fetch` реджектит, и внешний код переподключится.\n controller.abort(new Error('Истёк таймаут рукопожатия SSE'));\n }, this.#handshakeTimeout)\n : undefined;\n\n try {\n return await context.fetch(url, { method: 'GET', headers, signal: controller.signal });\n } catch (error) {\n if (expired) throw new Error('Поток уведомлений не ответил: истёк таймаут рукопожатия');\n throw error;\n } finally {\n cancelTimer?.();\n }\n }\n\n async #read(body: ReadableStream<Uint8Array>, context: TransportContext): Promise<void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n\n const parser = createParser({\n onEvent: (message) => {\n if (message.id) this.#lastEventId = message.id;\n\n let data: unknown;\n try {\n data = JSON.parse(message.data);\n } catch (error) {\n // Одно битое сообщение не повод рвать соединение.\n context.onParseError(error, message.data);\n return;\n }\n\n // Имя события может отсутствовать — тогда тип лежит внутри полезной нагрузки.\n const name =\n message.event ??\n (typeof data === 'object' && data !== null && 'type' in data\n ? String((data as { type: unknown }).type)\n : 'message');\n\n context.onEvent({ name, data });\n },\n });\n\n let cancelIdleTimer: (() => void) | undefined;\n\n const armIdleTimer = () => {\n if (this.#idleTimeout <= 0) return;\n cancelIdleTimer?.();\n\n cancelIdleTimer = this.#clock.schedule(() => {\n // Отмена чтения завершит цикл, и внешний код переподключится.\n void reader.cancel(new Error('Поток молчит дольше допустимого')).catch(() => {});\n }, this.#idleTimeout);\n };\n\n armIdleTimer();\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n\n armIdleTimer();\n parser.feed(decoder.decode(value, { stream: true }));\n }\n } finally {\n cancelIdleTimer?.();\n reader.releaseLock?.();\n }\n }\n}\n","import type { AuthIdentity } from '../core/auth.js';\nimport type { ItdClock } from '../core/clock.js';\nimport type { Listener, Unsubscribe } from '../core/emitter.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport { supportsStreamingBody } from '../core/runtime.js';\nimport { pickString } from '../core/unwrap.js';\nimport type { NotificationEvent } from '../notifications/normalize.js';\nimport type { NotificationType, RealtimeStatus } from '../types/enums.js';\nimport type { Logger } from '../types/options.js';\nimport { RealtimeEngine, type RealtimeEngineEvents, type RealtimeSyncReason } from './engine.js';\nimport type {\n RealtimeHandler,\n RealtimeMiddleware,\n RealtimeMiddlewareObj,\n RealtimePredicate,\n RealtimeSequentializer,\n RealtimeTypeGuard,\n} from './middleware.js';\nimport { PollTransport } from './poll.js';\nimport type { ReconnectOptions } from './reconnect.js';\nimport { SseTransport } from './sse.js';\nimport type { RealtimeRequest, RealtimeTransport, TransportEvent } from './transport.js';\nimport {\n isNotificationContext,\n matchesNotification,\n type RealtimeContext,\n type RealtimeNotificationContext,\n type RealtimeNotificationSelector,\n type RealtimeUpdate,\n type RealtimeUpdateOfType,\n RealtimeUpdateType,\n readRealtimeUpdate,\n validateNotificationSelector,\n} from './updates.js';\n\n/**\n * События потока уведомлений.\n *\n * Общая часть — {@link RealtimeEngineEvents}: статусы, ошибки и переподключение одинаковы\n * у любого потока. Ниже — то, что есть только у уведомлений.\n */\nexport interface RealtimeEvents<C extends RealtimeContext = RealtimeContext>\n extends RealtimeEngineEvents<C> {\n /** Пришло новое уведомление. */\n notification: NotificationEvent;\n /**\n * Сервер подтвердил подключение и назвал получателя событий.\n *\n * Приходит первым кадром сразу после установки соединения.\n */\n ready: { userId: string | undefined };\n /**\n * Получено актуальное число непрочитанных.\n *\n * При подключении клиент может запросить начальное значение через REST. Затем событие\n * возникает, только если счётчик пришёл в потоке. В остальных случаях обновляйте его\n * в приложении либо запрашивайте `itd.notifications.count()`.\n */\n unreadCount: number;\n}\n\n/** Способ получения событий. */\nexport const RealtimeTransportKind = Object.freeze({\n /** Поток событий, если среда умеет читать тело по частям, иначе опрос. */\n Auto: 'auto',\n /** Поток `text/event-stream`. */\n Sse: 'sse',\n /** Периодический опрос REST. */\n Poll: 'poll',\n} as const);\nexport type RealtimeTransportKind =\n (typeof RealtimeTransportKind)[keyof typeof RealtimeTransportKind];\n\n/** Настройки потока уведомлений. */\nexport interface RealtimeOptions<C extends RealtimeContext = RealtimeContext>\n extends ReconnectOptions {\n /**\n * Транспорт. По умолчанию `auto`: поток событий, если среда умеет читать тело ответа\n * по частям, иначе опрос.\n *\n * Можно передать и свою реализацию {@link RealtimeTransport} — это пригодится, если\n * у платформы появится WebSocket либо нужен нестандартный способ доставки.\n */\n transport?: RealtimeTransportKind | RealtimeTransport;\n /**\n * Молчание сервера, после которого соединение считается мёртвым, мс. По умолчанию 90 000.\n *\n * Сервер не присылает keep-alive, поэтому без этой проверки оборванное соединение\n * может незаметно «зависнуть».\n */\n idleTimeout?: number;\n /**\n * Сколько ждать ответа на запрос потока, прежде чем оборвать попытку, мс. По умолчанию\n * 20 000. Защищает от зависания на установке соединения, когда {@link idleTimeout} ещё\n * не действует. `0` отключает проверку. Только для потокового транспорта.\n */\n handshakeTimeout?: number;\n /** Как часто опрашивать сервер, если используется запасной транспорт. */\n pollInterval?: number;\n /**\n * Запрашивать число непрочитанных при подключении. По умолчанию `true`.\n *\n * Так поступает сайт итд.com: поток присылает только новые события, а начальное\n * значение счётчика нужно получить отдельно.\n */\n syncCount?: boolean;\n /**\n * Переподключаться, когда вкладка снова становится видимой. По умолчанию `true`.\n *\n * Только в браузере. У сайта итд.com такой обработки нет, из-за чего вкладка,\n * пролежавшая в фоне, может остаться без соединения.\n */\n reconnectOnVisible?: boolean;\n /** Переподключаться при восстановлении сети. По умолчанию `true`. Только в браузере. */\n reconnectOnOnline?: boolean;\n /** Максимальное число одновременно обрабатываемых обновлений. По умолчанию 1. */\n concurrency?: number;\n /** Возвращает ключи обновлений, которые нельзя обрабатывать одновременно. */\n sequentialize?: RealtimeSequentializer<C>;\n}\n\n/** Проверяет настройки потока. @throws {ItdConfigError} при некорректных значениях */\nfunction validateRealtimeOptions<C extends RealtimeContext>(options: RealtimeOptions<C>): void {\n const duration = (value: number | undefined, name: string, min: number): void => {\n if (value === undefined) return;\n if (!Number.isFinite(value) || value < min) {\n throw new ItdConfigError(\n `realtime.${name} должен быть числом не меньше ${min}, получено: ${value}`,\n );\n }\n };\n\n duration(options.pollInterval, 'pollInterval', 1);\n duration(options.idleTimeout, 'idleTimeout', 0);\n duration(options.handshakeTimeout, 'handshakeTimeout', 0);\n}\n\n/** Что поток получает от клиента. */\nexport interface RealtimeDeps {\n baseUrl: string;\n /** Разрешено ли транспорту передавать токен этому сервису. */\n authorize?: boolean | undefined;\n fetch: typeof fetch;\n /** Конвейер клиента — см. {@link TransportContext.request}. */\n request?: RealtimeRequest | undefined;\n clock?: ItdClock;\n /** Общие заголовки клиента для адреса — см. {@link TransportContext.baseHeaders}. */\n baseHeaders: (url: string) => Promise<Headers>;\n /** Идентификаторы аккаунта и сессии создавшего поток клиента. */\n getAuthIdentity?: (() => AuthIdentity) | undefined;\n /** Непрозрачная область авторизации создавшего поток клиента. */\n getAuthScope?: (() => string) | undefined;\n getToken: () => Promise<string | null>;\n /** Обновляет токен после отказа авторизации. Возвращает `true`, если удалось. */\n refresh: () => Promise<boolean>;\n /** Загружает начальное число непрочитанных. */\n fetchUnreadCount: () => Promise<number>;\n /** Вызывается при явном закрытии потока. */\n onClose?: (() => void) | undefined;\n /** Вызывается при запуске ранее закрытого потока. */\n onConnect?: (() => void) | undefined;\n logger?: Logger | undefined;\n}\n\nconst REALTIME_CONNECT_GUARDS = new WeakMap<object, () => void>();\n\n/** Связывает поток с lifecycle создавшего его клиента. @internal */\nexport function setRealtimeConnectGuard(stream: ItdRealtime, guard: () => void): void {\n REALTIME_CONNECT_GUARDS.set(stream, guard);\n}\n\n/**\n * Поток уведомлений в реальном времени.\n *\n * Получается вызовом `itd.realtime()`. Соединение поднимается методом {@link connect}\n * и держится само: обрывы, обновление токена и повторные попытки библиотека берёт на себя.\n *\n * Параметр типа задаёт форму контекста: плагин может расширить её своими полями\n * (`ItdRealtime<RealtimeContext & SessionFlavor<S>>`) и типизировать обработчики.\n *\n * @example\n * ```ts\n * import { NotificationType } from 'itd-api';\n *\n * const stream = itd.realtime();\n *\n * stream.onNotification(NotificationType.PostComment, async ({ update }) => {\n * await saveCommentNotification(update.data.notification);\n * });\n * stream.on('status', (status) => console.log('соединение:', status));\n *\n * await stream.connect();\n * // …позже\n * stream.disconnect();\n * await stream.drain();\n * ```\n */\nexport class ItdRealtime<C extends RealtimeContext = RealtimeContext> {\n readonly #deps: RealtimeDeps;\n readonly #engine: RealtimeEngine<RealtimeUpdate, C, RealtimeEvents<C>>;\n\n constructor(deps: RealtimeDeps, options: RealtimeOptions<C> = {}) {\n validateRealtimeOptions(options);\n\n this.#deps = deps;\n this.#engine = new RealtimeEngine<RealtimeUpdate, C, RealtimeEvents<C>>(\n {\n baseUrl: deps.baseUrl,\n authorize: deps.authorize ?? true,\n fetch: deps.fetch,\n request: deps.request,\n clock: deps.clock,\n baseHeaders: deps.baseHeaders,\n getToken: deps.getToken,\n refresh: deps.refresh,\n onConnect: deps.onConnect,\n onClose: deps.onClose,\n logger: deps.logger,\n\n transport: createTransport(deps, options),\n handleFrame: (event) => this.#handleFrame(event),\n readUpdate: readRealtimeUpdate,\n coalesceKey: (update) =>\n update.type === RealtimeUpdateType.UnreadCount ? update.type : undefined,\n createContext: (update, raw, origin) =>\n // Флейворные поля появляются в контексте позже — их присваивают плагины\n // в своих middleware, поэтому здесь собирается базовая форма.\n ({ update, stream: this, raw, origin }) as unknown as C,\n deliver: (update) => this.#deliver(update),\n ...(options.syncCount === false\n ? {}\n : {\n sync: (reason: RealtimeSyncReason, dispatch: (update: RealtimeUpdate) => void) =>\n this.#syncUnreadCount(reason, dispatch),\n }),\n },\n options,\n );\n }\n\n /** Текущее состояние соединения. */\n get status(): RealtimeStatus {\n return this.#engine.status;\n }\n\n /** Имя используемого транспорта. */\n get transport(): string {\n return this.#engine.transport;\n }\n\n /** Базовый URL клиента, создавшего поток. @internal */\n get baseUrl(): string {\n return this.#deps.baseUrl;\n }\n\n /** Идентификаторы аккаунта и сессии клиента, создавшего поток. @internal */\n getAuthIdentity(): AuthIdentity | undefined {\n return this.#deps.getAuthIdentity?.();\n }\n\n /** Непрозрачная область авторизации создавшего поток клиента. @internal */\n getAuthScope(): string | undefined {\n return this.#deps.getAuthScope?.();\n }\n\n /** Подписывается на событие потока. @returns функция отписки */\n on<K extends keyof RealtimeEvents<C>>(\n event: K,\n listener: Listener<RealtimeEvents<C>[K]>,\n ): Unsubscribe {\n return this.#engine.on(event, listener);\n }\n\n /** Подписывается на одно срабатывание. */\n once<K extends keyof RealtimeEvents<C>>(\n event: K,\n listener: Listener<RealtimeEvents<C>[K]>,\n ): Unsubscribe {\n return this.#engine.once(event, listener);\n }\n\n /**\n * Добавляет промежуточный обработчик или объект, предоставляющий его через `middleware()`.\n *\n * Обработчики выполняются в порядке регистрации. Если `next()` не вызван, обновление не\n * передаётся дальше по цепочке, асинхронным обработчикам и слушателям событий.\n *\n * @returns функция удаления обработчика\n */\n use(middleware: RealtimeMiddleware<C> | RealtimeMiddlewareObj<C>): Unsubscribe {\n return this.#engine.use(middleware);\n }\n\n /** Подписывает асинхронный обработчик на все нормализованные обновления. */\n onUpdate(handler: RealtimeHandler<C>): Unsubscribe;\n /** Подписывает асинхронный обработчик на обновление указанного типа. */\n onUpdate<T extends RealtimeUpdateType>(\n type: T,\n handler: RealtimeHandler<C & RealtimeContext<RealtimeUpdateOfType<T>>>,\n ): Unsubscribe;\n /** Подписывает асинхронный обработчик по функции сужения типа. */\n onUpdate<N extends C>(guard: RealtimeTypeGuard<N, C>, handler: RealtimeHandler<N>): Unsubscribe;\n /** Подписывает асинхронный обработчик по пользовательскому условию. */\n onUpdate(predicate: RealtimePredicate<C>, handler: RealtimeHandler<C>): Unsubscribe;\n onUpdate(\n selectorOrHandler: RealtimeUpdateType | RealtimePredicate<C> | RealtimeHandler<C>,\n selectedHandler?: RealtimeHandler<C>,\n ): Unsubscribe {\n const selectAll = selectedHandler === undefined;\n const handler = selectAll ? (selectorOrHandler as RealtimeHandler<C>) : selectedHandler;\n if (typeof handler !== 'function') {\n throw new ItdConfigError('realtime.onUpdate() принимает функцию обработчика');\n }\n if (\n !selectAll &&\n typeof selectorOrHandler !== 'function' &&\n !Object.values(RealtimeUpdateType).includes(selectorOrHandler)\n ) {\n throw new ItdConfigError(`Неизвестный тип обновления потока: ${String(selectorOrHandler)}`);\n }\n\n let predicate: RealtimePredicate<C>;\n if (selectAll) {\n predicate = () => true;\n } else {\n const selector = selectorOrHandler as RealtimeUpdateType | RealtimePredicate<C>;\n predicate =\n typeof selector === 'function' ? selector : (context) => context.update.type === selector;\n }\n\n return this.#engine.onUpdate(predicate, handler);\n }\n\n /** Подписывает асинхронный обработчик на уведомления, подходящие под фильтр. */\n onNotification<T extends NotificationType>(\n selector: RealtimeNotificationSelector<T>,\n handler: RealtimeHandler<C & RealtimeNotificationContext<T>>,\n ): Unsubscribe;\n /** Подписывает асинхронный обработчик по функции сужения типа уведомления. */\n onNotification<N extends C & RealtimeNotificationContext>(\n guard: (context: C & RealtimeNotificationContext) => context is N,\n handler: RealtimeHandler<N>,\n ): Unsubscribe;\n /** Подписывает асинхронный обработчик по пользовательскому условию. */\n onNotification(\n predicate: (context: C & RealtimeNotificationContext) => boolean,\n handler: RealtimeHandler<C & RealtimeNotificationContext>,\n ): Unsubscribe;\n onNotification(\n selector:\n | RealtimeNotificationSelector\n | ((context: C & RealtimeNotificationContext) => boolean),\n handler: (context: never) => unknown | Promise<unknown>,\n ): Unsubscribe {\n if (typeof handler !== 'function') {\n throw new ItdConfigError('realtime.onNotification() принимает функцию обработчика');\n }\n if (typeof selector !== 'function') validateNotificationSelector(selector);\n\n const predicate: RealtimePredicate<C> = (context) => {\n if (!isNotificationContext(context)) return false;\n return typeof selector === 'function'\n ? selector(context)\n : matchesNotification(context, selector);\n };\n\n return this.#engine.onUpdate(predicate, handler as RealtimeHandler<C>);\n }\n\n /**\n * Поднимает соединение.\n *\n * Повторный вызов при уже живом соединении ничего не делает — это защита от двойного\n * подключения при перерисовке интерфейса.\n *\n * Возвращает управление сразу после запуска: соединение живёт в фоне.\n *\n * @throws если создавший поток клиент уже освобождён\n */\n async connect(): Promise<void> {\n REALTIME_CONNECT_GUARDS.get(this)?.();\n return this.#engine.connect();\n }\n\n /** Закрывает соединение и отменяет запланированные попытки. */\n disconnect(): void {\n this.#engine.disconnect();\n }\n\n /** Ждёт завершения всех принятых обновлений. */\n drain(): Promise<void> {\n return this.#engine.drain();\n }\n\n /** Снимает подписки `on()` и `once()`. Остальные обработчики остаются. */\n removeAllListeners(): void {\n this.#engine.removeAllListeners();\n }\n\n /** Кадр `connected` — не обновление, а подтверждение подключения. */\n #handleFrame(event: TransportEvent): boolean {\n if (event.name !== 'connected') return false;\n\n // Строго строка: `String(null)` дал бы подписчику осмысленно выглядящее «null».\n this.#engine.emit('ready', { userId: pickString(event.data, 'userId') });\n return true;\n }\n\n /**\n * Загружает начальное число непрочитанных.\n *\n * Только при первом подключении: поток присылает счётчик сам, а лишний запрос\n * на каждой попытке переподключения нагружал бы сервер во время недоступности сети.\n */\n async #syncUnreadCount(\n reason: RealtimeSyncReason,\n dispatch: (update: RealtimeUpdate) => void,\n ): Promise<void> {\n if (reason !== 'initial') return;\n\n const count = await this.#deps.fetchUnreadCount();\n dispatch({ type: RealtimeUpdateType.UnreadCount, data: count });\n }\n\n #deliver(update: RealtimeUpdate): void {\n if (update.type === RealtimeUpdateType.Notification) {\n this.#engine.emit('notification', update.data);\n if (update.data.unreadCount !== undefined) {\n this.#engine.emit('unreadCount', update.data.unreadCount);\n }\n return;\n }\n\n if (update.type === RealtimeUpdateType.UnreadCount) {\n this.#engine.emit('unreadCount', update.data);\n return;\n }\n\n if (update.type === RealtimeUpdateType.Unknown) return;\n\n assertNeverUpdate(update);\n }\n}\n\n/** Выбирает транспорт по настройкам и возможностям среды. */\nfunction createTransport<C extends RealtimeContext>(\n deps: RealtimeDeps,\n options: RealtimeOptions<C>,\n): RealtimeTransport {\n const kind = options.transport ?? RealtimeTransportKind.Auto;\n\n if (typeof kind === 'object') return kind;\n\n if (\n kind === RealtimeTransportKind.Poll ||\n (kind === RealtimeTransportKind.Auto && !supportsStreamingBody())\n ) {\n return new PollTransport({\n ...(deps.clock ? { clock: deps.clock } : {}),\n ...(options.pollInterval !== undefined ? { interval: options.pollInterval } : {}),\n });\n }\n\n return new SseTransport({\n ...(deps.clock ? { clock: deps.clock } : {}),\n ...(options.idleTimeout !== undefined ? { idleTimeout: options.idleTimeout } : {}),\n ...(options.handshakeTimeout !== undefined\n ? { handshakeTimeout: options.handshakeTimeout }\n : {}),\n });\n}\n\nfunction assertNeverUpdate(update: never): never {\n throw new TypeError(`Необработанное обновление потока: ${String(update)}`);\n}\n","import { ItdConfigError } from './errors.js';\nimport { pickArray, pickBoolean, pickNumber, pickObject, pickString } from './unwrap.js';\n\n/**\n * Страница списка — единая форма для всех трёх схем пагинации API.\n *\n * Какие необязательные поля заполнены, зависит от эндпоинта: у ленты это `nextCursor`,\n * у подписчиков — `page` и `total`, у уведомлений — `nextOffset`. Обычно они не нужны:\n * перебор берёт на себя {@link Paginator}.\n */\nexport interface Page<T> {\n /** Элементы страницы. */\n items: T[];\n /** Есть ли следующая страница. */\n hasMore: boolean;\n /**\n * Курсор следующей страницы.\n *\n * Непрозрачен: у вкладки `popular` это номер страницы, у `following` — отметка времени.\n * Передавайте его обратно как есть и не пытайтесь разобрать.\n */\n nextCursor?: string | null | undefined;\n /** Номер текущей страницы при постраничной схеме. */\n page?: number | undefined;\n /** Запрошенный размер страницы. */\n limit?: number | undefined;\n /** Общее число элементов, если сервер его сообщил. */\n total?: number | undefined;\n /** Смещение для следующего запроса при схеме со смещением. */\n nextOffset?: number | undefined;\n /** Исходный ответ — на случай, если документация разошлась с реальностью. */\n raw: unknown;\n}\n\n/** Схема пагинации эндпоинта. */\nexport const PaginationMode = Object.freeze({\n /** Следующая страница запрашивается непрозрачным курсором. */\n Cursor: 'cursor',\n /** Следующая страница запрашивается номером. */\n Page: 'page',\n /** Следующая страница запрашивается смещением от начала списка. */\n Offset: 'offset',\n} as const);\nexport type PaginationMode = (typeof PaginationMode)[keyof typeof PaginationMode];\n\n/** Применяет преобразование к элементам страницы, сохраняя сведения о пагинации. */\nexport function mapPage<T, R>(page: Page<T>, map: (item: T) => R): Page<R> {\n return { ...page, items: page.items.map(map) };\n}\n\n/** Позиция, с которой запрашивается очередная страница. */\nexport interface PageState {\n cursor?: string | undefined;\n page?: number | undefined;\n offset?: number | undefined;\n}\n\n/** Настройки перебора страниц. */\nexport interface PaginatorOptions<T> {\n mode: PaginationMode;\n /** Загружает одну страницу для указанной позиции. */\n load: (state: PageState) => Promise<Page<T>>;\n /**\n * С какой позиции начать. По умолчанию с начала списка.\n *\n * Нужно, чтобы перебор можно было продолжить с сохранённого курсора, а не только\n * начать заново.\n */\n start?: PageState | undefined;\n /**\n * Предохранитель от бесконечного перебора. По умолчанию 1000.\n *\n * Сработает, только если сервер бесконечно сообщает `hasMore` — при нормальной работе\n * перебор останавливается сам.\n */\n maxPages?: number | undefined;\n /** Отмена перебора. */\n signal?: AbortSignal | undefined;\n}\n\n/**\n * Достаёт список из ответа, перебирая все формы, которые встречаются у сервера.\n *\n * Список может прийти под именем сущности, под её альтернативным именем\n * (`followers` вместо `users`) либо голым массивом.\n *\n * @param fields имена полей в порядке предпочтения\n */\nfunction readItems<T>(body: unknown, fields: readonly string[]): T[] {\n if (Array.isArray(body)) return body as T[];\n\n for (const field of fields) {\n const items = pickArray<T>(body, field);\n if (items.length > 0) return items;\n }\n\n // Пустой список тоже валиден: возвращаем по первому известному имени.\n const primary = fields[0];\n return primary === undefined ? [] : pickArray<T>(body, primary);\n}\n\n/**\n * Достаёт курсор следующей страницы.\n *\n * Проверяются все известные места: внутри `pagination`, рядом со списком и во вложенном\n * объекте `meta.cursor.next`.\n */\nfunction readCursor(body: unknown): string | null {\n const pagination = pickObject(body, 'pagination');\n const meta = pickObject(body, 'meta');\n const metaCursor = pickObject(meta, 'cursor');\n\n return (\n pickString(pagination, 'nextCursor') ??\n pickString(body, 'nextCursor') ??\n pickString(body, 'cursor') ??\n pickString(metaCursor, 'next') ??\n null\n );\n}\n\n/**\n * Читает страницу курсорной схемы.\n *\n * Используется лентой, постами пользователя и постами по хэштегу.\n *\n * @param fields имена возможных полей со списком; первое — основное\n */\nexport function readCursorPage<T>(body: unknown, ...fields: string[]): Page<T> {\n const pagination = pickObject(body, 'pagination');\n const items = readItems<T>(body, fields);\n const nextCursor = readCursor(body);\n\n return {\n items,\n // Если признака продолжения нет, ориентируемся на наличие курсора.\n hasMore: pickBoolean(pagination, 'hasMore', pickBoolean(body, 'hasMore', nextCursor !== null)),\n nextCursor,\n limit: pickNumber(pagination, 'limit', 0) || undefined,\n raw: body,\n };\n}\n\n/**\n * Читает страницу курсорной схемы с курсором рядом со списком.\n *\n * Так устроены комментарии к посту и ответы на комментарий: `nextCursor` и `hasMore`\n * лежат на одном уровне со списком, а не внутри объекта `pagination`.\n */\nexport function readFlatCursorPage<T>(body: unknown, ...fields: string[]): Page<T> {\n const items = readItems<T>(body, fields);\n const nextCursor = readCursor(body);\n const total = pickNumber(body, 'total', -1);\n\n return {\n items,\n hasMore: pickBoolean(body, 'hasMore', nextCursor !== null),\n nextCursor,\n ...(total >= 0 ? { total } : {}),\n raw: body,\n };\n}\n\n/**\n * Читает страницу постраничной схемы: `data.pagination.{page,limit,total,hasMore}`.\n *\n * Если сервер вместо этого прислал курсор, он тоже подхватывается — некоторые списки\n * отвечают в курсорной форме, хотя документация описывает постраничную.\n */\nexport function readPagedPage<T>(body: unknown, ...fields: string[]): Page<T> {\n const pagination = pickObject(body, 'pagination');\n const items = readItems<T>(body, fields);\n const nextCursor = readCursor(body);\n\n return {\n items,\n hasMore: pickBoolean(pagination, 'hasMore', pickBoolean(body, 'hasMore', nextCursor !== null)),\n page: pickNumber(pagination, 'page', 1),\n limit: pickNumber(pagination, 'limit', 0) || undefined,\n total: pickNumber(pagination, 'total', 0),\n ...(nextCursor !== null ? { nextCursor } : {}),\n raw: body,\n };\n}\n\n/**\n * Читает страницу схемы со смещением.\n *\n * Используется для списков со смещением, включая уведомления.\n */\nexport function readOffsetPage<T>(body: unknown, field: string, offset: number): Page<T> {\n const items = pickArray<T>(body, field);\n\n return {\n items,\n hasMore: pickBoolean(body, 'hasMore'),\n nextOffset: offset + items.length,\n raw: body,\n };\n}\n\n/**\n * Перебор страниц списка.\n *\n * Скрывает различия трёх схем пагинации: перебор элементов, страниц и сбор в массив\n * выглядят одинаково независимо от эндпоинта.\n *\n * **Одноразовый.** Позиция хранится внутри, поэтому повторный перебор того же объекта\n * ничего не вернёт: он продолжится с места, где закончился прошлый. Нужен второй проход —\n * возьмите новый перебор у того же метода ресурса.\n *\n * @example Перебор элементов\n * ```ts\n * for await (const post of itd.posts.iterate({ tab: 'following' })) {\n * console.log(post.content);\n * }\n * ```\n *\n * @example Первые сто элементов\n * ```ts\n * const posts = await itd.posts.iterate({ tab: 'popular' }).collect(100);\n * ```\n *\n * @example Постранично\n * ```ts\n * for await (const page of itd.users.followers('nowkie').pages()) {\n * console.log(page.items.length, 'из', page.total);\n * }\n * ```\n */\nexport class Paginator<T> implements AsyncIterable<T> {\n readonly #options: PaginatorOptions<T>;\n readonly #maxPages: number;\n\n #state: PageState;\n #finished = false;\n #pagesLoaded = 0;\n\n constructor(options: PaginatorOptions<T>) {\n if (\n options.maxPages !== undefined &&\n (!Number.isInteger(options.maxPages) || options.maxPages < 1)\n ) {\n throw new ItdConfigError(\n `maxPages должен быть целым числом не меньше 1, получено: ${options.maxPages}`,\n );\n }\n\n this.#options = options;\n this.#maxPages = options.maxPages ?? 1000;\n this.#state = options.start ?? {};\n }\n\n /**\n * Загружает следующую страницу.\n *\n * @returns страница либо `null`, если перебор закончен\n */\n async next(): Promise<Page<T> | null> {\n if (this.#finished) return null;\n if (this.#options.signal?.aborted) return null;\n\n if (this.#pagesLoaded >= this.#maxPages) {\n this.#finished = true;\n return null;\n }\n\n const previous = this.#state;\n const page = await this.#options.load(previous);\n this.#pagesLoaded += 1;\n\n this.#state = this.#advance(previous, page);\n return page;\n }\n\n /**\n * Перебирает страницы целиком.\n *\n * Полезно, когда нужны сведения о самой странице — например `total`.\n */\n async *pages(): AsyncGenerator<Page<T>, void, undefined> {\n for (;;) {\n const page = await this.next();\n if (!page) return;\n yield page;\n }\n }\n\n /** Перебирает элементы всех страниц подряд. */\n async *[Symbol.asyncIterator](): AsyncGenerator<T, void, undefined> {\n for await (const page of this.pages()) {\n for (const item of page.items) {\n if (this.#options.signal?.aborted) return;\n yield item;\n }\n }\n }\n\n /**\n * Собирает элементы в массив.\n *\n * @param max сколько элементов достаточно; без него перебираются все страницы\n */\n async collect(max?: number): Promise<T[]> {\n // Ноль и отрицательное число — «ничего не нужно»: не делаем ни одного запроса.\n if (max !== undefined && max <= 0) return [];\n\n const result: T[] = [];\n\n for await (const item of this) {\n result.push(item);\n if (max !== undefined && result.length >= max) break;\n }\n\n return result;\n }\n\n /**\n * Вычисляет позицию следующей страницы и решает, продолжать ли.\n *\n * Здесь же стоят предохранители: пустая страница при `hasMore`, неизменившийся курсор\n * и отсутствие курсора останавливают перебор. Без них ошибка на сервере превратилась бы\n * в бесконечный цикл запросов.\n */\n #advance(previous: PageState, page: Page<T>): PageState {\n if (!page.hasMore || page.items.length === 0) {\n this.#finished = true;\n return previous;\n }\n\n if (this.#options.mode === PaginationMode.Cursor) {\n const cursor = page.nextCursor ?? undefined;\n\n if (!cursor || cursor === previous.cursor) {\n this.#finished = true;\n return previous;\n }\n\n return { cursor };\n }\n\n if (this.#options.mode === PaginationMode.Page) {\n return { page: (previous.page ?? 1) + 1 };\n }\n\n return { offset: page.nextOffset ?? (previous.offset ?? 0) + page.items.length };\n }\n}\n","import type { HttpClient } from '../core/http.js';\nimport type { BuiltInOperationId } from '../core/operations.js';\nimport type { Page, PageState, PaginationMode } from '../core/pagination.js';\nimport { Paginator } from '../core/pagination.js';\nimport type { QueryParams } from '../core/url.js';\nimport type { PaginationOptions, RequestOptions } from '../types/options.js';\n\n/**\n * Описание перебираемого эндпоинта.\n *\n * Одно место, где заданы путь, параметры запроса, чтение страницы и схема пагинации.\n * {@link BaseResource.paginated} строит из него и разовую загрузку, и перебор.\n *\n * @typeParam T тип элемента списка\n * @typeParam P тип параметров метода\n */\nexport interface ListingSpec<T, P extends object> {\n /** Стабильная семантическая операция списка. */\n operationId: BuiltInOperationId | ((params: P) => BuiltInOperationId);\n /** Путь эндпоинта. */\n path: (params: P) => string;\n /** Параметры запроса без полей пагинации — их добавит перебор. */\n query: (params: P) => QueryParams;\n /** Читает страницу из ответа. Получает позицию — она нужна схеме со смещением. */\n read: (body: unknown, state: PageState) => Page<T>;\n /** Схема пагинации эндпоинта. */\n mode: PaginationMode;\n /** Начальная позиция, вычисленная из параметров (курсор, номер или смещение). */\n start: (params: P) => PageState;\n}\n\n/** Пара методов, собранная из {@link ListingSpec}: разовая загрузка и перебор. */\nexport interface Listing<T, P extends object> {\n /** Загружает одну страницу с позиции, заданной параметрами. */\n list(params: P, options?: RequestOptions): Promise<Page<T>>;\n /** Перебирает страницы, сама подставляя позиции. */\n iterate(params: P, options?: PaginationOptions): Paginator<T>;\n}\n\n/** Общая основа всех групп методов клиента. */\nexport class BaseResource {\n /** @internal */\n protected readonly http: HttpClient;\n\n constructor(http: HttpClient) {\n this.http = http;\n }\n\n /**\n * Собирает перебор страниц.\n *\n * @param mode схема пагинации эндпоинта\n * @param load загружает одну страницу для указанной позиции\n * @param options только управление самим перебором: предел, отмена и начальная позиция\n */\n protected paginate<T>(\n mode: PaginationMode,\n load: (state: PageState) => Promise<Page<T>>,\n options?: PaginationOptions & { start?: PageState },\n ): Paginator<T> {\n return new Paginator<T>({\n mode,\n load,\n ...(options?.maxPages !== undefined ? { maxPages: options.maxPages } : {}),\n ...(options?.signal !== undefined ? { signal: options.signal } : {}),\n ...(options?.start !== undefined ? { start: options.start } : {}),\n });\n }\n\n /**\n * Собирает пару «загрузка страницы + перебор» из одного описания.\n *\n * Путь, параметры запроса и разбор ответа задаются один раз; `list` и `iterate`\n * строятся из них.\n *\n * @example\n * ```ts\n * #feed = this.paginated<Post, FeedParams>({\n * operationId: 'posts.list',\n * path: () => '/api/posts',\n * query: (p) => ({ tab: p.tab, limit: p.limit }),\n * start: (p) => (p.cursor ? { cursor: p.cursor } : {}),\n * read: (body) => readCursorPage<Post>(body, 'posts'),\n * mode: PaginationMode.Cursor,\n * });\n * ```\n */\n protected paginated<T, P extends object>(spec: ListingSpec<T, P>): Listing<T, P> {\n const load = async (\n params: P,\n state: PageState,\n options: RequestOptions = {},\n ): Promise<Page<T>> => {\n const operationId =\n typeof spec.operationId === 'function' ? spec.operationId(params) : spec.operationId;\n const body = await this.http.operation(operationId, {\n path: spec.path(params),\n query: withPageState(spec.query(params), state),\n ...options,\n });\n return spec.read(body, state);\n };\n\n return {\n list: (params, options) => load(params, spec.start(params), options),\n iterate: (params, options = {}) => {\n const { maxPages, ...requestOptions } = options;\n return this.paginate<T>(spec.mode, (state) => load(params, state, requestOptions), {\n ...(maxPages !== undefined ? { maxPages } : {}),\n ...(requestOptions.signal !== undefined ? { signal: requestOptions.signal } : {}),\n start: spec.start(params),\n });\n },\n };\n }\n}\n\n/** Добавляет позицию страницы в параметры запроса. */\nexport function withPageState(query: QueryParams, state: PageState): QueryParams {\n return {\n ...query,\n ...(state.cursor !== undefined ? { cursor: state.cursor } : {}),\n ...(state.page !== undefined ? { page: state.page } : {}),\n ...(state.offset !== undefined ? { offset: state.offset } : {}),\n };\n}\n","import { AUTH_PATHS, type AuthManager, type TURNSTILE_SITE_KEY } from '../core/auth.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport type { HttpClient } from '../core/http.js';\nimport { pickArray, pickString } from '../core/unwrap.js';\nimport type { Session } from '../models/account.js';\nimport type { AuthState } from '../models/users.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Учётные данные для входа. */\nexport interface Credentials {\n email: string;\n password: string;\n}\n\n/**\n * Учётные данные вместе с токеном капчи.\n *\n * `turnstileToken` обязателен: без него сервер отвечает `VALIDATION_ERROR`, с недействительным —\n * `TURNSTILE_VERIFICATION_FAILED`. Токен даёт виджет Cloudflare Turnstile с ключом\n * {@link TURNSTILE_SITE_KEY}; он одноразовый и живёт несколько минут.\n */\nexport interface CaptchaCredentials extends Credentials {\n turnstileToken: string;\n}\n\n/** Запрос письма для сброса пароля. Капча обязательна, как и при входе. */\nexport interface ForgotPasswordInput {\n email: string;\n turnstileToken: string;\n}\n\n/**\n * Установка нового пароля.\n *\n * Сброс идёт тем же потоком с кодом, что и вход: {@link AuthResource.forgotPassword} выдаёт\n * `flowToken`, письмо приносит `otp`, и всё это вместе с новым паролем уходит сюда.\n */\nexport interface ResetPasswordInput {\n email: string;\n otp: string;\n flowToken: string;\n newPassword: string;\n}\n\n/** Чем закончился вход: сразу токеном или запросом кода подтверждения. */\nexport const SignInStatus = Object.freeze({\n Authenticated: 'authenticated',\n OtpRequired: 'otp_required',\n} as const);\nexport type SignInStatus = (typeof SignInStatus)[keyof typeof SignInStatus];\n\n/**\n * Результат входа.\n *\n * Сервер может как сразу выдать токен, так и потребовать код подтверждения — размеченное\n * объединение делает оба случая явными.\n */\nexport type SignInResult =\n | { status: 'authenticated'; accessToken: string }\n | { status: 'otp_required'; flowToken: string | undefined };\n\n/**\n * Авторизация, сессии и пароли.\n *\n * Доступна как `itd.auth`.\n */\nexport class AuthResource extends BaseResource {\n readonly #auth: AuthManager;\n\n constructor(http: HttpClient, deps: { auth: AuthManager }) {\n super(http);\n this.#auth = deps.auth;\n }\n\n /**\n * Проверяет состояние авторизации и возвращает текущего пользователя.\n *\n * Без авторизации возвращает `authenticated: false` и `user: null`.\n */\n check(options: RequestOptions = {}): Promise<AuthState> {\n return this.http.operation<AuthState>('auth.check', {\n path: '/api/profile',\n ...options,\n });\n }\n\n /**\n * Регистрирует аккаунт и запускает подтверждение по коду.\n *\n * @returns `flowToken`, который нужно передать в {@link verifyOtp}\n */\n async signUp(credentials: CaptchaCredentials, options: RequestOptions = {}): Promise<string> {\n const body = await this.http.operation('auth.signUp', {\n path: AUTH_PATHS.signUp,\n body: credentials,\n skipAuth: true,\n skipAuthRefresh: true,\n ...options,\n });\n\n const flowToken = pickString(body, 'flowToken');\n if (!flowToken) {\n throw new ItdConfigError('Сервер не вернул flowToken при регистрации');\n }\n\n return flowToken;\n }\n\n /**\n * Выполняет вход.\n *\n * Если сервер потребовал код подтверждения, вернётся `status: 'otp_required'` —\n * тогда продолжайте через {@link verifyOtp} либо воспользуйтесь {@link signInWithOtp}.\n *\n * При успешном входе токен сохраняется в клиенте автоматически.\n *\n * @param credentials email, пароль и обязательный токен капчи — см. {@link CaptchaCredentials}\n */\n async signIn(\n credentials: CaptchaCredentials,\n options: RequestOptions = {},\n ): Promise<SignInResult> {\n const body = await this.http.operation('auth.signIn', {\n path: AUTH_PATHS.signIn,\n body: credentials,\n skipAuth: true,\n skipAuthRefresh: true,\n ...options,\n });\n\n const accessToken = pickString(body, 'accessToken');\n\n if (accessToken) {\n await this.#auth.setAccessToken(accessToken);\n return { status: SignInStatus.Authenticated, accessToken };\n }\n\n return { status: SignInStatus.OtpRequired, flowToken: pickString(body, 'flowToken') };\n }\n\n /**\n * Подтверждает вход кодом из письма.\n *\n * Полученный токен сохраняется в клиенте автоматически.\n */\n async verifyOtp(\n input: Credentials & { otp: string; flowToken: string },\n options: RequestOptions = {},\n ): Promise<string> {\n const body = await this.http.operation('auth.verifyOtp', {\n path: AUTH_PATHS.verifyOtp,\n body: input,\n skipAuth: true,\n skipAuthRefresh: true,\n ...options,\n });\n\n const accessToken = pickString(body, 'accessToken');\n if (!accessToken) {\n throw new ItdConfigError('Сервер не вернул accessToken после подтверждения кода');\n }\n\n await this.#auth.setAccessToken(accessToken);\n return accessToken;\n }\n\n /** Отправляет код подтверждения повторно. */\n resendOtp(\n input: { email: string; flowToken: string },\n options: RequestOptions = {},\n ): Promise<void> {\n return this.http.operation<void>('auth.resendOtp', {\n path: AUTH_PATHS.resendOtp,\n body: input,\n skipAuth: true,\n skipAuthRefresh: true,\n ...options,\n });\n }\n\n /**\n * Полный вход с подтверждением по коду.\n *\n * Удобно для скриптов и ботов: код запрашивается функцией `getOtp`, а всё остальное\n * библиотека делает сама.\n *\n * @example\n * ```ts\n * import { createInterface } from 'node:readline/promises';\n *\n * const rl = createInterface({ input: process.stdin, output: process.stdout });\n *\n * const token = await itd.auth.signInWithOtp({\n * email, password,\n * getOtp: () => rl.question('Код из письма: '),\n * });\n * ```\n */\n async signInWithOtp(\n input: CaptchaCredentials & { getOtp: () => string | Promise<string> },\n options: RequestOptions = {},\n ): Promise<string> {\n const { getOtp, ...credentials } = input;\n const result = await this.signIn(credentials, options);\n\n if (result.status === SignInStatus.Authenticated) return result.accessToken;\n\n if (!result.flowToken) {\n throw new ItdConfigError(\n 'Сервер запросил код подтверждения, но не вернул flowToken — продолжить вход невозможно',\n );\n }\n\n const otp = await getOtp();\n\n // Токен капчи сюда не передаётся: он одноразовый и уже потрачен на sign-in,\n // а verify-otp капчу не требует.\n return this.verifyOtp(\n {\n email: credentials.email,\n password: credentials.password,\n otp,\n flowToken: result.flowToken,\n },\n options,\n );\n }\n\n /**\n * Обновляет токен доступа.\n *\n * Параллельные вызовы объединяются в один сетевой запрос. При включённом `autoRefresh`\n * вызывать вручную обычно не нужно.\n */\n refresh(): Promise<string> {\n return this.#auth.refresh();\n }\n\n /**\n * Есть ли признак живой сессии обновления.\n *\n * Проверяет cookie `is_auth`, которую сервер ставит рядом с refresh-токеном, а также\n * refresh-токен, переданный строкой. Позволяет не дёргать API у неавторизованного\n * пользователя. В браузере всегда `true`: cookie ведёт сама среда, и прочитать её\n * из JS нельзя.\n *\n * Читает {@link TokenStorage}, поэтому результат верен и до первого запроса.\n *\n * @example\n * ```ts\n * if (await itd.auth.hasRefreshSession()) await itd.auth.refresh();\n * else redirectToLogin();\n * ```\n */\n hasRefreshSession(): Promise<boolean> {\n return this.#auth.hasRefreshSession();\n }\n\n /** Завершает текущую сессию на сервере и очищает локальную. */\n async logout(options: RequestOptions = {}): Promise<void> {\n await this.http.operation<void>('auth.logout', {\n path: AUTH_PATHS.logout,\n skipAuthRefresh: true,\n ...options,\n });\n\n await this.#auth.clear();\n }\n\n /**\n * Завершает все сессии пользователя и очищает локальную.\n *\n * Собран из двух запросов, потому что единого эндпоинта на сервере нет:\n * `POST /api/v1/auth/logout-all` отвечает `404`. Сначала отзываются все прочие сессии\n * (`DELETE /api/v1/auth/sessions`), затем завершается текущая — в обратном порядке\n * отзывать было бы уже нечем.\n */\n async logoutAll(options: RequestOptions = {}): Promise<void> {\n await this.revokeOtherSessions(options);\n await this.logout(options);\n }\n\n /** Забывает сессию локально, не обращаясь к серверу. */\n signOut(): Promise<void> {\n return this.#auth.clear();\n }\n\n /**\n * Запрашивает письмо с кодом для сброса пароля.\n *\n * @returns `flowToken`, который нужно передать в {@link resetPassword}\n */\n async forgotPassword(input: ForgotPasswordInput, options: RequestOptions = {}): Promise<string> {\n const body = await this.http.operation('auth.forgotPassword', {\n path: AUTH_PATHS.forgotPassword,\n body: input,\n skipAuth: true,\n skipAuthRefresh: true,\n ...options,\n });\n\n const flowToken = pickString(body, 'flowToken');\n if (!flowToken) {\n throw new ItdConfigError('Сервер не вернул flowToken при запросе сброса пароля');\n }\n\n return flowToken;\n }\n\n /**\n * Устанавливает новый пароль по коду из письма.\n *\n * Сервер ждёт все четыре поля сразу — `email`, `otp`, `flowToken` и `newPassword`;\n * при нехватке любого отвечает `422`.\n */\n resetPassword(input: ResetPasswordInput, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('auth.resetPassword', {\n path: AUTH_PATHS.resetPassword,\n body: input,\n skipAuth: true,\n skipAuthRefresh: true,\n ...options,\n });\n }\n\n /**\n * Полный сброс пароля с кодом из письма.\n *\n * Тот же приём, что и {@link signInWithOtp}: код запрашивается функцией `getOtp`,\n * остальное библиотека делает сама.\n *\n * @example\n * ```ts\n * await itd.auth.resetPasswordWithOtp({\n * email,\n * turnstileToken,\n * newPassword,\n * getOtp: () => rl.question('Код из письма: '),\n * });\n * ```\n */\n async resetPasswordWithOtp(\n input: ForgotPasswordInput & {\n newPassword: string;\n getOtp: () => string | Promise<string>;\n },\n options: RequestOptions = {},\n ): Promise<void> {\n const flowToken = await this.forgotPassword(\n { email: input.email, turnstileToken: input.turnstileToken },\n options,\n );\n\n const otp = await input.getOtp();\n\n await this.resetPassword(\n { email: input.email, otp, flowToken, newPassword: input.newPassword },\n options,\n );\n }\n\n /**\n * Меняет пароль. Требует действующей сессии.\n *\n * При неверном текущем пароле сервер отвечает `ACCOUNT_CURRENT_PASSWORD_INCORRECT`.\n *\n * @example\n * ```ts\n * await itd.auth.changePassword({ currentPassword, newPassword });\n * ```\n */\n changePassword(\n input: { currentPassword: string; newPassword: string },\n options: RequestOptions = {},\n ): Promise<void> {\n return this.http.operation<void>('auth.changePassword', {\n path: AUTH_PATHS.changePassword,\n body: { currentPassword: input.currentPassword, newPassword: input.newPassword },\n ...options,\n });\n }\n\n /** Загружает список активных сессий. У текущей поле `isCurrent` равно `true`. */\n async sessions(options: RequestOptions = {}): Promise<Session[]> {\n const body = await this.http.operation('auth.sessions', {\n path: AUTH_PATHS.sessions,\n ...options,\n });\n\n return pickArray<Session>(body, 'sessions');\n }\n\n /** Завершает указанную сессию. */\n revokeSession(sessionId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('auth.revokeSession', {\n path: `${AUTH_PATHS.sessions}/${encodeURIComponent(sessionId)}`,\n ...options,\n });\n }\n\n /** Завершает все сессии, кроме текущей. */\n revokeOtherSessions(options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('auth.revokeOtherSessions', {\n path: AUTH_PATHS.sessions,\n ...options,\n });\n }\n}\n","/** Метка билдера. Через `Symbol.for` — чтобы распознавание переживало смешивание ESM и CJS. */\nexport const BUILDER = Symbol.for('itd.builder');\n\n/**\n * Билдер входных данных.\n *\n * Билдеры необязательны: любой метод, принимающий билдер, принимает и обычный объект.\n * Проверки одинаковы в обоих случаях.\n */\nexport interface ItdBuilder<T> {\n /** @internal */\n readonly [BUILDER]: true;\n /**\n * Собирает и проверяет результат.\n *\n * @throws {ItdConfigError} если нарушены требования к данным\n */\n build(): T;\n /** Чтобы билдер корректно вёл себя внутри `JSON.stringify`. */\n toJSON(): T;\n}\n\n/**\n * Три равноправные формы входа: обычный объект, готовый билдер или функция-настройщик.\n *\n * @example\n * ```ts\n * itd.posts.create({ content: 'привет' }); // объект\n * itd.posts.create(post().content('привет')); // билдер\n * itd.posts.create((p) => p.content('привет')); // функция\n * ```\n */\nexport type BuilderInput<T, B extends ItdBuilder<T>> = T | B | ((builder: B) => B | T);\n\n/** Является ли значение билдером. */\nexport function isBuilder<T>(value: unknown): value is ItdBuilder<T> {\n return typeof value === 'object' && value !== null && BUILDER in value;\n}\n\n/**\n * Приводит любую из трёх форм входа к готовому объекту.\n *\n * Проверка выполняется всегда, независимо от формы: обычный объект проходит ровно те же\n * правила, что и результат билдера.\n *\n * @param input то, что передал пользователь\n * @param factory создаёт пустой билдер для формы с функцией\n * @param validate проверяет собранный объект\n */\nexport function resolveInput<T, B extends ItdBuilder<T>>(\n input: BuilderInput<T, B>,\n factory: () => B,\n validate: (value: T) => T,\n): T {\n if (typeof input === 'function') {\n const result = (input as (builder: B) => B | T)(factory());\n return isBuilder<T>(result) ? result.build() : validate(result as T);\n }\n\n if (isBuilder<T>(input)) return input.build();\n\n return validate(input as T);\n}\n","import type { FileInput } from '../core/attachments/contracts.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport type { UserId } from '../models/common.js';\nimport type { CreateCommentInput } from '../types/params.js';\nimport { BUILDER, type BuilderInput, type ItdBuilder, resolveInput } from './base.js';\n\n/** Внутреннее состояние {@link CommentBuilder}. */\ninterface CommentState extends CreateCommentInput {\n content: string;\n attachmentIds: string[];\n files: FileInput[];\n /** Голосовой комментарий: текста быть не должно, вложение ровно одно. */\n voice: boolean;\n}\n\n/**\n * Проверяет данные комментария.\n *\n * @throws {ItdConfigError} если комментарий пуст или голосовой собран неверно\n */\nexport function validateComment(input: CreateCommentInput): CreateCommentInput {\n const content = typeof input?.content === 'string' ? input.content : '';\n const attachmentIds = input?.attachmentIds ?? [];\n const files = input?.files ?? [];\n\n const hasContent = content.trim() !== '';\n const hasAttachments = attachmentIds.length > 0 || files.length > 0;\n\n if (!hasContent && !hasAttachments) {\n throw new ItdConfigError('Комментарий пуст: нужен текст или вложение');\n }\n\n return input;\n}\n\n/** Дополнительная проверка голосового комментария. */\nfunction validateVoice(state: CommentState): void {\n if (state.content.trim() !== '') {\n throw new ItdConfigError(\n 'У голосового комментария не может быть текста: API принимает либо текст, либо аудио',\n );\n }\n\n const total = state.files.length + state.attachmentIds.length;\n if (total !== 1) {\n throw new ItdConfigError(\n `Голосовой комментарий требует ровно одно аудиовложение, передано: ${total}`,\n );\n }\n}\n\n/**\n * Билдер комментария и ответа на комментарий.\n *\n * Неизменяемый: каждый вызов возвращает новый экземпляр. Создаётся функцией {@link comment}.\n */\nexport class CommentBuilder implements ItdBuilder<CreateCommentInput> {\n /** @internal */\n readonly [BUILDER] = true as const;\n\n readonly #state: CommentState;\n\n /** @internal Создавайте билдер функцией {@link comment}. */\n constructor(state: CommentState) {\n this.#state = state;\n }\n\n /** Задаёт текст комментария. */\n content(text: string): CommentBuilder {\n return new CommentBuilder({ ...this.#state, content: text });\n }\n\n /** Прикладывает файл — он будет загружен перед отправкой. */\n attach(file: FileInput): CommentBuilder {\n return new CommentBuilder({ ...this.#state, files: [...this.#state.files, file] });\n }\n\n /** Прикладывает уже загруженное вложение. */\n attachId(attachmentId: string): CommentBuilder {\n return new CommentBuilder({\n ...this.#state,\n attachmentIds: [...this.#state.attachmentIds, attachmentId],\n });\n }\n\n /**\n * Делает комментарий голосовым.\n *\n * Текста у такого комментария быть не должно, а вложение ровно одно — аудио в формате\n * `audio/ogg`. Так его принимает API.\n *\n * @example\n * ```ts\n * import { fromPath } from 'itd-api/node';\n *\n * await itd.posts.comment(postId, (c) => c.voice(fromPath('./answer.ogg')));\n * ```\n */\n voice(audio: FileInput): CommentBuilder {\n return new CommentBuilder({ ...this.#state, files: [audio], voice: true });\n }\n\n /**\n * Кому адресован ответ.\n *\n * Имеет смысл только в `itd.comments.reply()`; при отправке комментария к посту\n * это поле вызовет ошибку.\n */\n replyTo(userId: UserId): CommentBuilder {\n return new CommentBuilder({ ...this.#state, replyToUserId: userId });\n }\n\n build(): CreateCommentInput {\n const { content, attachmentIds, files, voice, ...rest } = this.#state;\n\n if (voice) validateVoice(this.#state);\n\n return validateComment({\n ...rest,\n content,\n ...(attachmentIds.length > 0 ? { attachmentIds } : {}),\n ...(files.length > 0 ? { files } : {}),\n });\n }\n\n toJSON(): CreateCommentInput {\n return this.build();\n }\n}\n\n/**\n * Начинает сборку комментария.\n *\n * @param content текст; можно задать позже методом {@link CommentBuilder.content}\n *\n * @example\n * ```ts\n * import { comment } from 'itd-api';\n *\n * await itd.posts.comment(postId, comment('согласен').attach({ url: memeUrl }));\n * ```\n */\nexport function comment(content = ''): CommentBuilder {\n return new CommentBuilder({ content, attachmentIds: [], files: [], voice: false });\n}\n\n/** Что принимает параметр комментария: объект, билдер или функция-настройщик. */\nexport type CommentInput = BuilderInput<CreateCommentInput, CommentBuilder>;\n\n/**\n * Приводит любую форму входа к готовым данным комментария.\n *\n * @param allowReplyTo разрешено ли поле `replyToUserId`; в комментарии к посту — нет\n */\nexport function resolveComment(input: CommentInput, allowReplyTo = false): CreateCommentInput {\n const resolved = resolveInput(input, () => comment(), validateComment);\n\n if (!allowReplyTo && resolved.replyToUserId !== undefined) {\n throw new ItdConfigError(\n 'replyTo применим только к ответу на комментарий (itd.comments.reply). ' +\n 'В комментарии к посту адресат не указывается.',\n );\n }\n\n return resolved;\n}\n","import { type CommentInput, resolveComment } from '../builders/comment.js';\nimport type { FileInput } from '../core/attachments/contracts.js';\nimport type { HttpClient } from '../core/http.js';\nimport { type Page, PaginationMode, type Paginator, readPagedPage } from '../core/pagination.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { Comment, LikeResult } from '../models/content.js';\nimport type { PaginationOptions, RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Параметры запроса ответов на комментарий. */\nexport interface RepliesParams {\n limit?: number;\n page?: number;\n}\n\n/**\n * Комментарии и ответы на них.\n *\n * Доступна как `itd.comments`. Комментарии **к посту** живут в `itd.posts`:\n * `itd.posts.comments()` и `itd.posts.comment()`.\n */\nexport class CommentsResource extends BaseResource {\n readonly #uploadFiles: (files: FileInput[], options?: RequestOptions) => Promise<string[]>;\n\n /** Ответы на комментарий: `/api/comments/{id}/replies`, постраничная пагинация. */\n readonly #replies = this.paginated<Comment, RepliesParams & { commentId: string }>({\n operationId: 'comments.replies',\n path: (p) => `/api/comments/${encodePathSegment(p.commentId, 'commentId')}/replies`,\n query: (p) => ({ limit: p.limit }),\n start: (p) => (p.page !== undefined ? { page: p.page } : {}),\n read: (body) => readPagedPage<Comment>(body, 'replies'),\n mode: PaginationMode.Page,\n });\n\n constructor(\n http: HttpClient,\n deps: { uploadFiles: (files: FileInput[], options?: RequestOptions) => Promise<string[]> },\n ) {\n super(http);\n this.#uploadFiles = deps.uploadFiles;\n }\n\n /**\n * Загружает страницу ответов на комментарий.\n *\n * Здесь пагинация **постраничная**, в отличие от комментариев к посту, где курсорная.\n */\n replies(\n commentId: string,\n params: RepliesParams = {},\n options: RequestOptions = {},\n ): Promise<Page<Comment>> {\n return this.#replies.list({ ...params, commentId }, options);\n }\n\n /** Перебирает ответы на комментарий. */\n iterateReplies(\n commentId: string,\n params: RepliesParams = {},\n options: PaginationOptions = {},\n ): Paginator<Comment> {\n return this.#replies.iterate({ ...params, commentId }, options);\n }\n\n /**\n * Отвечает на комментарий.\n *\n * @example\n * ```ts\n * await itd.comments.reply(commentId, 'согласен');\n * await itd.comments.reply(commentId, (c) => c.content('и вот почему').replyTo(userId));\n * ```\n */\n async reply(\n commentId: string,\n input: CommentInput | string,\n options: RequestOptions = {},\n ): Promise<Comment> {\n const data = resolveComment(typeof input === 'string' ? { content: input } : input, true);\n\n const existing = data.attachmentIds ?? [];\n const files = data.files ?? [];\n const attachmentIds =\n files.length > 0 ? [...existing, ...(await this.#uploadFiles(files, options))] : existing;\n\n return this.http.operation<Comment>('comments.reply', {\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}/replies`,\n body: {\n content: data.content ?? '',\n attachmentIds,\n ...(data.replyToUserId ? { replyToUserId: data.replyToUserId } : {}),\n },\n ...options,\n });\n }\n\n /** Редактирует текст комментария. */\n update(commentId: string, content: string, options: RequestOptions = {}): Promise<Comment> {\n return this.http.operation<Comment>('comments.update', {\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}`,\n body: { content },\n ...options,\n });\n }\n\n /** Удаляет комментарий. Восстановить его можно через {@link restore}. */\n remove(commentId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('comments.remove', {\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}`,\n ...options,\n });\n }\n\n /** Восстанавливает удалённый комментарий. */\n restore(commentId: string, options: RequestOptions = {}): Promise<Comment> {\n return this.http.operation<Comment>('comments.restore', {\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}/restore`,\n ...options,\n });\n }\n\n /** Ставит реакцию на комментарий. */\n like(commentId: string, options: RequestOptions = {}): Promise<LikeResult> {\n return this.http.operation<LikeResult>('comments.like', {\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}/like`,\n ...options,\n });\n }\n\n /** Убирает реакцию с комментария. */\n unlike(commentId: string, options: RequestOptions = {}): Promise<LikeResult> {\n return this.http.operation<LikeResult>('comments.unlike', {\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}/like`,\n ...options,\n });\n }\n}\n","import { ItdConfigError, ItdFileError, ItdFileErrorReason } from '../errors.js';\nimport { boundedFileStream } from './bounded-stream.js';\nimport {\n DEFAULT_URL_FILE_MAX_BYTES,\n type FileContent,\n type FileContext,\n type FileStreamContent,\n type UrlFileOptions,\n} from './contracts.js';\nimport { fileTooLarge } from './limits.js';\nimport { resolveFileStreamOptions } from './options.js';\n\n/** Убирает параметры MIME и приводит его к форме для сравнения. */\nexport function normalizeMimeType(contentType: string | undefined): string | undefined {\n const normalized = contentType?.split(';', 1)[0]?.trim().toLowerCase();\n return normalized || undefined;\n}\n\n/** Достаёт имя файла из пути URL. */\nfunction filenameFromUrl(url: URL): string | undefined {\n const last = url.pathname.split('/').pop();\n if (!last) return undefined;\n\n try {\n return decodeURIComponent(last) || undefined;\n } catch {\n return last;\n }\n}\n\n/** Проверяет объявленный размер и возвращает его, если заголовок корректен. */\nfunction declaredSize(\n response: Response,\n maxBytes: number | undefined,\n url: string,\n): number | undefined {\n const header = response.headers.get('content-length');\n if (header === null) return undefined;\n\n const size = Number(header);\n if (!Number.isFinite(size) || size < 0 || !Number.isInteger(size)) return undefined;\n if (maxBytes !== undefined && size > maxBytes) throw fileTooLarge(url, maxBytes, size);\n return size;\n}\n\n/** Получает HTTP-ответ источника и проверяет его статус. */\nasync function fetchFile(\n target: string,\n options: UrlFileOptions,\n context: FileContext,\n): Promise<{ response: Response; url: URL; size: number | undefined }> {\n let requested: URL;\n try {\n requested = new URL(target);\n } catch {\n throw new ItdConfigError(`«${target}» не разбирается как адрес`);\n }\n\n if (requested.protocol !== 'http:' && requested.protocol !== 'https:') {\n throw new ItdConfigError(\n `вложение по адресу поддерживает только http и https, получено: ${requested.protocol}`,\n );\n }\n\n let response: Response;\n try {\n response = await context.fetch(requested, {\n ...(context.signal ? { signal: context.signal } : {}),\n });\n } catch (error) {\n if (context.signal?.aborted || (error instanceof Error && error.name === 'AbortError')) {\n throw error;\n }\n\n throw new ItdFileError(`не удалось получить файл по адресу ${requested.href}`, {\n reason: ItdFileErrorReason.Network,\n url: requested.href,\n retryable: true,\n cause: error,\n });\n }\n\n const finalUrl = response.url ? new URL(response.url) : requested;\n if (!response.ok) {\n await response.body?.cancel().catch(() => {});\n throw new ItdFileError(`источник ${finalUrl.href} ответил статусом ${response.status}`, {\n reason: ItdFileErrorReason.Http,\n url: finalUrl.href,\n status: response.status,\n retryable: response.status === 408 || response.status === 429 || response.status >= 500,\n });\n }\n\n const { maxBytes } = resolveFileStreamOptions(options, DEFAULT_URL_FILE_MAX_BYTES);\n let size: number | undefined;\n try {\n size = declaredSize(response, maxBytes, finalUrl.href);\n } catch (error) {\n await response.body?.cancel().catch(() => {});\n throw error;\n }\n return {\n response,\n url: finalUrl,\n size,\n };\n}\n\n/** Читает ответ с контролем размера до создания итогового `Blob`. */\nasync function responseBlob(\n response: Response,\n url: string,\n maxBytes: number | undefined,\n streamBufferBytes: number,\n signal: AbortSignal | undefined,\n): Promise<Blob> {\n if (!response.body) {\n const blob = await response.blob();\n if (maxBytes !== undefined && blob.size > maxBytes) {\n throw fileTooLarge(url, maxBytes, blob.size);\n }\n return blob;\n }\n\n const chunks: Uint8Array[] = [];\n const stream = boundedFileStream(response.body, {\n ...(maxBytes !== undefined ? { maxBytes } : {}),\n streamBufferBytes,\n ...(signal ? { signal } : {}),\n url,\n retryableRead: true,\n });\n const reader = stream.getReader();\n\n try {\n for (;;) {\n const next = await reader.read();\n if (next.done) break;\n chunks.push(next.value);\n }\n } finally {\n reader.releaseLock();\n }\n\n return new Blob(chunks.map((chunk) => Uint8Array.from(chunk).buffer));\n}\n\n/** Скачивает файл целиком с ограничением размера. @internal */\nexport async function downloadFile(\n target: string,\n options: UrlFileOptions,\n context: FileContext,\n): Promise<FileContent> {\n const resolved = resolveFileStreamOptions(options, DEFAULT_URL_FILE_MAX_BYTES);\n const { response, url } = await fetchFile(target, options, context);\n const blob = await responseBlob(\n response,\n url.href,\n resolved.maxBytes,\n resolved.streamBufferBytes,\n context.signal,\n );\n const contentType = normalizeMimeType(\n options.contentType ?? response.headers.get('content-type') ?? undefined,\n );\n const filename = options.filename ?? filenameFromUrl(url);\n\n return {\n file: new Blob([blob], { type: contentType ?? '' }),\n ...(filename ? { filename } : {}),\n ...(contentType ? { contentType } : {}),\n };\n}\n\n/** Открывает HTTP-ответ как ограниченный поток. @internal */\nexport async function openUrlFile(\n target: string,\n options: UrlFileOptions,\n context: FileContext,\n): Promise<FileStreamContent> {\n const resolved = resolveFileStreamOptions(options, DEFAULT_URL_FILE_MAX_BYTES);\n const { response, url, size } = await fetchFile(target, options, context);\n if (!response.body) {\n throw new ItdFileError(`источник ${url.href} не предоставил потоковое тело`, {\n reason: ItdFileErrorReason.StreamUnavailable,\n url: url.href,\n });\n }\n\n const stream = boundedFileStream(response.body, {\n ...(resolved.maxBytes !== undefined ? { maxBytes: resolved.maxBytes } : {}),\n streamBufferBytes: resolved.streamBufferBytes,\n ...(context.signal ? { signal: context.signal } : {}),\n url: url.href,\n retryableRead: true,\n });\n const filename = options.filename ?? filenameFromUrl(url);\n const contentType = normalizeMimeType(\n options.contentType ?? response.headers.get('content-type') ?? undefined,\n );\n\n return {\n stream,\n ...(filename ? { filename } : {}),\n ...(contentType ? { contentType } : {}),\n ...(size !== undefined ? { size } : {}),\n close: () => stream.cancel().catch(() => {}),\n };\n}\n","import { ItdConfigError } from './errors.js';\n\n/** Изображения, которые принимает `POST /api/files/upload`. */\nexport const IMAGE_MIME_TYPES = Object.freeze([\n 'image/jpeg',\n 'image/png',\n 'image/gif',\n 'image/webp',\n 'image/avif',\n 'image/heic',\n 'image/heif',\n] as const);\nexport type ImageMimeType = (typeof IMAGE_MIME_TYPES)[number];\n\n/** Видео, которые принимает `POST /api/files/upload`. */\nexport const VIDEO_MIME_TYPES = Object.freeze([\n 'video/mp4',\n 'video/webm',\n 'video/quicktime',\n] as const);\nexport type VideoMimeType = (typeof VIDEO_MIME_TYPES)[number];\n\n/** Аудио для голосовых комментариев. */\nexport const AUDIO_MIME_TYPES = Object.freeze(['audio/ogg'] as const);\nexport type AudioMimeType = (typeof AUDIO_MIME_TYPES)[number];\n\n/** Все типы, которые принимает загрузка. */\nexport const ALLOWED_MIME_TYPES = Object.freeze([\n ...IMAGE_MIME_TYPES,\n ...VIDEO_MIME_TYPES,\n ...AUDIO_MIME_TYPES,\n] as const);\nexport type AllowedMimeType = (typeof ALLOWED_MIME_TYPES)[number];\n\n/**\n * Соответствие расширения и MIME-типа.\n *\n * Своя маленькая таблица вместо зависимости: библиотеке нужны ровно те типы,\n * которые принимает API.\n */\nconst EXTENSION_TO_MIME: Readonly<Record<string, string>> = Object.freeze({\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n jfif: 'image/jpeg',\n png: 'image/png',\n gif: 'image/gif',\n webp: 'image/webp',\n avif: 'image/avif',\n heic: 'image/heic',\n heif: 'image/heif',\n mp4: 'video/mp4',\n m4v: 'video/mp4',\n webm: 'video/webm',\n mov: 'video/quicktime',\n qt: 'video/quicktime',\n ogg: 'audio/ogg',\n oga: 'audio/ogg',\n opus: 'audio/ogg',\n});\n\n/** Определяет MIME-тип по расширению имени файла. */\nexport function mimeFromFilename(filename: string): string | undefined {\n const dot = filename.lastIndexOf('.');\n if (dot < 0) return undefined;\n\n return EXTENSION_TO_MIME[filename.slice(dot + 1).toLowerCase()];\n}\n\n/** Разрешён ли тип к загрузке. */\nexport function isAllowedMime(mimeType: string): mimeType is AllowedMimeType {\n return (ALLOWED_MIME_TYPES as readonly string[]).includes(mimeType.toLowerCase());\n}\n\n/**\n * Проверяет тип файла до отправки.\n *\n * Отказ на стороне клиента экономит и время, и трафик: тот же `UNSUPPORTED_FILE_TYPE`\n * пришёл бы с сервера, но уже после загрузки всего файла.\n *\n * @throws {ItdConfigError} если тип не поддерживается\n */\nexport function assertAllowedMime(mimeType: string | undefined, filename?: string): void {\n if (!mimeType) {\n throw new ItdConfigError(\n `Не удалось определить тип файла${filename ? ` «${filename}»` : ''}. ` +\n 'Укажите его явно: { file, filename, contentType }.',\n );\n }\n\n if (!isAllowedMime(mimeType)) {\n throw new ItdConfigError(\n `Тип «${mimeType}» не поддерживается загрузкой. Допустимые: ${ALLOWED_MIME_TYPES.join(', ')}.`,\n );\n }\n}\n","/** Один потоковый файл в multipart/form-data. */\nexport interface MultipartFile {\n stream: ReadableStream<Uint8Array>;\n filename: string;\n contentType: string;\n}\n\n/** Результат сборки потокового multipart-тела. */\nexport interface MultipartBody {\n body: ReadableStream<Uint8Array>;\n contentType: string;\n cancel: (reason?: unknown) => Promise<void>;\n}\n\nconst encoder = new TextEncoder();\n\n/** Убирает возможность внедрить дополнительный multipart-заголовок через имя файла. */\nfunction quotedFilename(filename: string): string {\n return filename\n .replace(/[\\r\\n]/g, '')\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"');\n}\n\n/** Удаляет разделители строк, чтобы значение не создало новый multipart-заголовок. */\nfunction headerValue(value: string): string {\n return value.replace(/[\\r\\n]/g, '');\n}\n\n/** Создаёт уникальную границу без зависимости от Web Crypto. */\nfunction boundary(): string {\n return `----itd-api-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Кодирует один файл в multipart без полной буферизации.\n *\n * Читает следующий чанк только когда потребитель запросил данные.\n */\nexport function createMultipartFileBody(file: MultipartFile): MultipartBody {\n const marker = boundary();\n const prefix = encoder.encode(\n `--${marker}\\r\\n` +\n `Content-Disposition: form-data; name=\"file\"; filename=\"${quotedFilename(file.filename)}\"\\r\\n` +\n `Content-Type: ${headerValue(file.contentType)}\\r\\n\\r\\n`,\n );\n const suffix = encoder.encode(`\\r\\n--${marker}--\\r\\n`);\n const reader = file.stream.getReader();\n let stage: 'prefix' | 'file' | 'suffix' | 'done' = 'prefix';\n let cancelled = false;\n\n const cancel = async (reason?: unknown) => {\n if (cancelled) return;\n cancelled = true;\n await reader.cancel(reason).catch(() => {});\n };\n\n const body = new ReadableStream<Uint8Array>({\n async pull(controller) {\n if (stage === 'prefix') {\n stage = 'file';\n controller.enqueue(prefix);\n return;\n }\n\n if (stage === 'file') {\n try {\n const next = await reader.read();\n if (!next.done) {\n controller.enqueue(next.value);\n return;\n }\n stage = 'suffix';\n } catch (error) {\n stage = 'done';\n controller.error(error);\n return;\n }\n }\n\n if (stage === 'suffix') {\n stage = 'done';\n controller.enqueue(suffix);\n controller.close();\n }\n },\n cancel,\n });\n\n return {\n body,\n contentType: `multipart/form-data; boundary=${marker}`,\n cancel,\n };\n}\n","import { boundedFileStream, isBoundedFileStream } from '../core/attachments/bounded-stream.js';\nimport {\n type FileContent,\n type FileContext,\n type FileInput,\n type FileStreamContent,\n FileTransferMode,\n type LazyFile,\n type StreamFile,\n type UrlFile,\n} from '../core/attachments/contracts.js';\nimport { resolveFileStreamOptions } from '../core/attachments/options.js';\nimport { downloadFile, normalizeMimeType, openUrlFile } from '../core/attachments/url-source.js';\nimport { ItdConfigError, ItdError, ItdFileError, ItdFileErrorReason } from '../core/errors.js';\nimport type { HttpClient } from '../core/http.js';\nimport { assertAllowedMime, mimeFromFilename } from '../core/mime.js';\nimport { createMultipartFileBody } from '../core/multipart.js';\nimport type { RequestBodyFactory } from '../core/pipeline.js';\nimport { isBlob, isFile } from '../core/runtime.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Ответ загрузки файла. */\nexport interface UploadedFile {\n /** Идентификатор вложения — его передают в `attachmentIds`. */\n id: string;\n /** Адрес файла на CDN. */\n url: string;\n}\n\n/** Настройки загрузки. */\nexport interface UploadOptions {\n /** Имя файла. Используется для определения MIME, если тип не задан. */\n filename?: string;\n /** MIME-тип. По умолчанию определяется по имени или `Blob`. */\n contentType?: string;\n /** Проверять тип до отправки. По умолчанию `true`. */\n validateMime?: boolean;\n /** Дополнительный предел размера для любого вида источника. */\n maxBytes?: number | undefined;\n /** Размер очереди библиотеки при потоковой передаче. */\n streamBufferBytes?: number | undefined;\n}\n\n/** Таймаут одной попытки загрузки файла — 5 минут. */\nexport const DEFAULT_UPLOAD_TIMEOUT = 300_000;\n\ninterface PreparedBuffer {\n mode: 'buffer';\n blob: Blob;\n filename: string;\n}\n\ninterface PreparedStream {\n mode: 'stream';\n stream: ReadableStream<Uint8Array>;\n filename: string;\n contentType: string;\n close?: (() => void | Promise<void>) | undefined;\n}\n\ntype PreparedFile = PreparedBuffer | PreparedStream;\n\n/** Файлы и медиа. */\nexport class FilesResource extends BaseResource {\n readonly #fetch: typeof fetch;\n\n constructor(http: HttpClient, deps: { fetch: typeof fetch }) {\n super(http);\n this.#fetch = deps.fetch;\n }\n\n /**\n * Загружает файл и возвращает его идентификатор.\n *\n * Потоковый источник открывается заново при каждой повторной попытке. Буферный источник\n * после успешного чтения переиспользуется.\n */\n upload(\n input: FileInput,\n uploadOptions: UploadOptions = {},\n requestOptions: RequestOptions = {},\n ): Promise<UploadedFile> {\n const bodyFactory = this.#createBodyFactory(input, uploadOptions);\n\n return this.http.operation<UploadedFile>('files.upload', {\n path: '/api/files/upload',\n bodyFactory,\n ...requestOptions,\n timeout: requestOptions.timeout ?? DEFAULT_UPLOAD_TIMEOUT,\n });\n }\n\n /** Загружает несколько файлов последовательно, сохраняя порядок. */\n async uploadMany(\n files: FileInput[],\n uploadOptions: UploadOptions = {},\n requestOptions: RequestOptions = {},\n ): Promise<string[]> {\n const ids: string[] = [];\n for (const file of files) {\n ids.push((await this.upload(file, uploadOptions, requestOptions)).id);\n }\n return ids;\n }\n\n /**\n * Загружает сведения о файле.\n *\n * Для ещё не прикреплённого файла сервер может ответить `404`.\n */\n get(fileId: string, options: RequestOptions = {}): Promise<unknown> {\n return this.http.operation('files.get', {\n path: `/api/files/${encodePathSegment(fileId, 'fileId')}`,\n ...options,\n });\n }\n\n /** Удаляет загруженный файл. */\n remove(fileId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('files.remove', {\n path: `/api/files/${encodePathSegment(fileId, 'fileId')}`,\n ...options,\n });\n }\n\n /**\n * Создаёт фабрику тела. Успешно подготовленный буфер кешируется, а поток открывается\n * заново, поэтому каждая транспортная попытка получает непрочитанное тело.\n */\n #createBodyFactory(input: FileInput, options: UploadOptions): RequestBodyFactory {\n const streamMode = this.#isStreamInput(input);\n let buffered: Promise<PreparedBuffer> | undefined;\n\n return async ({ signal, attempt }) => {\n const context: FileContext = { fetch: this.#fetch, signal, attempt };\n let prepared: PreparedFile;\n\n try {\n if (streamMode) {\n prepared = await this.#prepareStream(input, options, context);\n } else {\n buffered ??= this.#prepareBuffer(input, options, context).catch((error: unknown) => {\n buffered = undefined;\n throw error;\n });\n prepared = await buffered;\n }\n } catch (error) {\n if (error instanceof ItdError || signal.aborted) throw error;\n throw new ItdFileError('не удалось получить содержимое вложения', {\n reason: ItdFileErrorReason.Read,\n retryable: true,\n cause: error,\n });\n }\n\n if (prepared.mode === 'buffer') {\n const form = new FormData();\n form.set('file', prepared.blob, prepared.filename);\n return { body: form };\n }\n\n const multipart = createMultipartFileBody(prepared);\n return {\n body: multipart.body,\n headers: { 'Content-Type': multipart.contentType },\n cleanup: async () => {\n await multipart.cancel();\n await prepared.close?.();\n },\n };\n };\n }\n\n /** Определяет режим без чтения источника. */\n #isStreamInput(input: FileInput): boolean {\n if (typeof input !== 'object' || input === null) return false;\n if ('open' in input) return true;\n if ('url' in input) {\n return resolveFileStreamOptions(input).mode === FileTransferMode.Stream;\n }\n return false;\n }\n\n /** Получает и проверяет буферный источник. */\n async #prepareBuffer(\n input: FileInput,\n options: UploadOptions,\n context: FileContext,\n ): Promise<PreparedBuffer> {\n const content = await this.#resolveBuffer(input, context);\n const filename =\n options.filename ??\n content.filename ??\n this.#nameFromMime(options.contentType ?? content.contentType);\n const contentType =\n normalizeMimeType(options.contentType ?? content.contentType) ??\n normalizeMimeType(isBlob(content.file) ? content.file.type : undefined) ??\n mimeFromFilename(filename);\n\n if (options.validateMime !== false) assertAllowedMime(contentType, filename);\n\n const blob =\n isBlob(content.file) && (!contentType || content.file.type === contentType)\n ? content.file\n : new Blob([content.file as BlobPart], { type: contentType ?? '' });\n\n const limits = resolveFileStreamOptions(options);\n if (limits.maxBytes !== undefined && blob.size > limits.maxBytes) {\n throw new ItdFileError(`файл больше предела в ${limits.maxBytes} байт: ${blob.size}`, {\n reason: ItdFileErrorReason.TooLarge,\n limit: limits.maxBytes,\n actual: blob.size,\n });\n }\n\n return { mode: 'buffer', blob, filename };\n }\n\n /** Открывает и проверяет потоковый источник. */\n async #prepareStream(\n input: FileInput,\n options: UploadOptions,\n context: FileContext,\n ): Promise<PreparedStream> {\n let opened: FileStreamContent;\n if (typeof input === 'object' && input !== null && 'open' in input) {\n opened = await (input as StreamFile).open(context);\n } else if (typeof input === 'object' && input !== null && 'url' in input) {\n const { url, ...urlOptions } = input as UrlFile;\n opened = await openUrlFile(url, urlOptions, context);\n } else {\n throw new ItdConfigError('потоковое вложение должно иметь форму { open } или { url, mode }');\n }\n\n if (\n !opened ||\n typeof ReadableStream === 'undefined' ||\n !(opened.stream instanceof ReadableStream)\n ) {\n await opened?.close?.();\n throw new ItdConfigError('потоковый источник должен вернуть { stream: ReadableStream }');\n }\n\n try {\n const filename =\n options.filename ??\n opened.filename ??\n this.#nameFromMime(options.contentType ?? opened.contentType);\n const contentType =\n normalizeMimeType(options.contentType ?? opened.contentType) ?? mimeFromFilename(filename);\n if (options.validateMime !== false) assertAllowedMime(contentType, filename);\n\n const limits = resolveFileStreamOptions({\n mode: FileTransferMode.Stream,\n ...(options.maxBytes !== undefined ? { maxBytes: options.maxBytes } : {}),\n ...(options.streamBufferBytes !== undefined\n ? { streamBufferBytes: options.streamBufferBytes }\n : {}),\n });\n if (\n limits.maxBytes !== undefined &&\n opened.size !== undefined &&\n opened.size > limits.maxBytes\n ) {\n throw new ItdFileError(`файл больше предела в ${limits.maxBytes} байт: ${opened.size}`, {\n reason: ItdFileErrorReason.TooLarge,\n limit: limits.maxBytes,\n actual: opened.size,\n });\n }\n\n const keepExistingLimits =\n isBoundedFileStream(opened.stream) &&\n options.maxBytes === undefined &&\n options.streamBufferBytes === undefined;\n\n return {\n mode: 'stream',\n stream: keepExistingLimits\n ? opened.stream\n : boundedFileStream(opened.stream, {\n ...(limits.maxBytes !== undefined ? { maxBytes: limits.maxBytes } : {}),\n streamBufferBytes: limits.streamBufferBytes,\n signal: context.signal,\n }),\n filename,\n contentType: contentType ?? '',\n ...(opened.close ? { close: opened.close } : {}),\n };\n } catch (error) {\n await opened.close?.();\n throw error;\n }\n }\n\n /** Разрешает только буферные формы. */\n #resolveBuffer(input: FileInput, context: FileContext): FileContent | Promise<FileContent> {\n if (input instanceof ArrayBuffer || ArrayBuffer.isView(input) || isBlob(input)) {\n return {\n file: input as Blob | ArrayBuffer | Uint8Array,\n ...(isFile(input) ? { filename: input.name } : {}),\n };\n }\n\n if (typeof input !== 'object' || input === null) {\n throw new ItdConfigError(\n `вложение задано значением типа ${typeof input}; ожидается бинарное значение ` +\n 'или объект { file }, { url }, { load }, { open }. ' +\n \"Для файла на диске используйте fromPath('./photo.jpg') из itd-api/node\",\n );\n }\n\n if ('load' in input) return (input as LazyFile).load(context);\n if ('url' in input) {\n const { url, ...urlOptions } = input as UrlFile;\n return downloadFile(url, urlOptions, context);\n }\n if ('file' in input) return input as FileContent;\n if ('open' in input) {\n throw new ItdConfigError('потоковый источник нельзя использовать как буферный');\n }\n\n throw new ItdConfigError(\n 'вложение не распознано: ожидается { file }, { url }, { load } или { open }',\n );\n }\n\n /** Подбирает непустое имя, обязательное для multipart. */\n #nameFromMime(contentType: string | undefined): string {\n const extension = normalizeMimeType(contentType)?.split('/')[1];\n return extension ? `file.${extension}` : 'file';\n }\n}\n","import { type Page, PaginationMode, type Paginator, readCursorPage } from '../core/pagination.js';\nimport { pickArray } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { Hashtag, Post } from '../models/content.js';\nimport type { PaginationOptions, RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Параметры запроса постов по хэштегу. */\nexport interface HashtagPostsParams {\n limit?: number;\n cursor?: string;\n}\n\n/**\n * Хэштеги.\n *\n * Доступна как `itd.hashtags`.\n */\nexport class HashtagsResource extends BaseResource {\n /** Посты по хэштегу: `/api/hashtags/{tag}/posts`, курсорная пагинация. */\n readonly #posts = this.paginated<Post, HashtagPostsParams & { tag: string }>({\n operationId: 'hashtags.posts',\n path: (p) => `/api/hashtags/${encodePathSegment(p.tag, 'tag')}/posts`,\n query: (p) => ({ limit: p.limit }),\n start: (p) => (p.cursor ? { cursor: p.cursor } : {}),\n read: (body) => readCursorPage<Post>(body, 'posts'),\n mode: PaginationMode.Cursor,\n });\n\n /**\n * Ищет хэштеги.\n *\n * Без строки запроса возвращает общий список.\n */\n async search(\n query?: string,\n params: { limit?: number } = {},\n options: RequestOptions = {},\n ): Promise<Hashtag[]> {\n const body = await this.http.operation('hashtags.search', {\n path: '/api/hashtags',\n query: { q: query, limit: params.limit },\n ...options,\n });\n\n return pickArray<Hashtag>(body, 'hashtags');\n }\n\n /** Загружает трендовые хэштеги. */\n async trending(\n params: { limit?: number } = {},\n options: RequestOptions = {},\n ): Promise<Hashtag[]> {\n const body = await this.http.operation('hashtags.trending', {\n path: '/api/hashtags/trending',\n query: { limit: params.limit },\n ...options,\n });\n\n return pickArray<Hashtag>(body, 'hashtags');\n }\n\n /**\n * Загружает страницу постов по хэштегу.\n *\n * @param tag название без решётки; кодируется автоматически, поэтому кириллица\n * и пробелы допустимы\n */\n posts(\n tag: string,\n params: HashtagPostsParams = {},\n options: RequestOptions = {},\n ): Promise<Page<Post>> {\n return this.#posts.list({ ...params, tag }, options);\n }\n\n /** Перебирает посты по хэштегу. */\n iteratePosts(\n tag: string,\n params: HashtagPostsParams = {},\n options: PaginationOptions = {},\n ): Paginator<Post> {\n return this.#posts.iterate({ ...params, tag }, options);\n }\n}\n","import { type Page, PaginationMode, type Paginator, readOffsetPage } from '../core/pagination.js';\nimport { pickBoolean, pickNumber } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { Notification, NotificationSettings } from '../models/notifications.js';\nimport { normalizeNotification } from '../notifications/normalize.js';\nimport type { PaginationOptions, RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\nconst NOTIFICATION_SETTING_KEYS = [\n 'enabled',\n 'sound',\n 'follows',\n 'wallPosts',\n 'likes',\n 'comments',\n 'mentions',\n] as const;\n\n/**\n * Сколько идентификаторов уходит в одном запросе на отметку прочтения.\n *\n * Столько же отправляет сайт итд.com — значит на сервере, скорее всего, есть ограничение.\n */\nconst READ_BATCH_SIZE = 20;\n\n/** Параметры запроса списка уведомлений. */\nexport interface NotificationListParams {\n limit?: number;\n /** Смещение от начала списка. */\n offset?: number;\n}\n\n/** Изменяемые настройки уведомлений. */\nexport type UpdateNotificationSettingsInput = Partial<NotificationSettings>;\n\n/**\n * Читает настройки уведомлений.\n *\n * Сервер отдаёт плоский объект: `enabled`, `sound`, `follows`, `wallPosts`, `likes`,\n * `comments`, `mentions`. Отсутствующая настройка считается включённой — так же\n * ведёт себя сайт итд.com.\n */\nfunction readSettings(body: unknown): NotificationSettings {\n const settings = {} as NotificationSettings;\n for (const key of NOTIFICATION_SETTING_KEYS) settings[key] = pickBoolean(body, key, true);\n return settings;\n}\n\n/**\n * Уведомления: список, счётчик, отметки о прочтении, настройки.\n *\n * Доступна как `itd.notifications`. Все уведомления приведены к единой форме, поэтому\n * объекты отсюда и из потока событий можно складывать в один список.\n */\nexport class NotificationsResource extends BaseResource {\n /** Уведомления: `/api/notifications/`, пагинация по смещению. */\n readonly #list = this.paginated<Notification, NotificationListParams>({\n operationId: 'notifications.list',\n // Завершающий слэш обязателен: без него сервер отвечает ошибкой.\n path: () => '/api/notifications/',\n query: (p) => ({ limit: p.limit }),\n start: (p) => ({ offset: p.offset ?? 0 }),\n read: (body, state) => {\n const page = readOffsetPage<unknown>(body, 'notifications', state.offset ?? 0);\n // Сайт итд.com оборачивает смещение в строку и притворяется, что это курсор;\n // библиотека отдаёт честное число, а сами уведомления — в единой форме.\n return { ...page, items: page.items.map(normalizeNotification) };\n },\n mode: PaginationMode.Offset,\n });\n\n /**\n * Загружает страницу уведомлений.\n *\n * Пагинация здесь основана на смещении.\n *\n * @example\n * ```ts\n * const page = await itd.notifications.list({ limit: 20 });\n * const next = await itd.notifications.list({ limit: 20, offset: page.nextOffset });\n * ```\n */\n list(\n params: NotificationListParams = {},\n options: RequestOptions = {},\n ): Promise<Page<Notification>> {\n return this.#list.list(params, options);\n }\n\n /**\n * Перебирает уведомления.\n *\n * @example\n * ```ts\n * for await (const notification of itd.notifications.iterate()) {\n * console.log(formatNotificationText(notification));\n * }\n * ```\n */\n iterate(\n params: NotificationListParams = {},\n options: PaginationOptions = {},\n ): Paginator<Notification> {\n return this.#list.iterate(params, options);\n }\n\n /** Загружает число непрочитанных уведомлений. */\n async count(options: RequestOptions = {}): Promise<number> {\n const body = await this.http.operation('notifications.count', {\n path: '/api/notifications/count',\n ...options,\n });\n\n return pickNumber(body, 'count', 0);\n }\n\n /**\n * Отмечает уведомление прочитанным.\n *\n * @returns сколько записей отметил сервер\n */\n async markRead(notificationId: string, options: RequestOptions = {}): Promise<number> {\n const body = await this.http.operation('notifications.markRead', {\n path: `/api/notifications/${encodePathSegment(notificationId, 'notificationId')}/read`,\n ...options,\n });\n\n return pickNumber(body, 'markedCount', 0);\n }\n\n /**\n * Отмечает прочитанными сразу несколько уведомлений.\n *\n * Список автоматически режется на части по 20 идентификаторов — столько же отправляет\n * сайт итд.com, поэтому на сервере вероятен предел. Части уходят последовательно,\n * результат суммируется.\n *\n * @returns сколько записей отметил сервер суммарно\n */\n async markReadBatch(ids: string[], options: RequestOptions = {}): Promise<number> {\n let marked = 0;\n\n for (let index = 0; index < ids.length; index += READ_BATCH_SIZE) {\n const chunk = ids.slice(index, index + READ_BATCH_SIZE);\n\n const body = await this.http.operation('notifications.markReadBatch', {\n path: '/api/notifications/read-batch',\n body: { ids: chunk },\n ...options,\n });\n\n marked += pickNumber(body, 'markedCount', 0);\n }\n\n return marked;\n }\n\n /** Отмечает прочитанными все уведомления. */\n async markAllRead(options: RequestOptions = {}): Promise<number> {\n const body = await this.http.operation('notifications.markAllRead', {\n path: '/api/notifications/read-all',\n ...options,\n });\n\n return pickNumber(body, 'markedCount', 0);\n }\n\n /** Загружает настройки уведомлений. */\n async getSettings(options: RequestOptions = {}): Promise<NotificationSettings> {\n const body = await this.http.operation('notifications.getSettings', {\n path: '/api/notifications/settings',\n ...options,\n });\n\n return readSettings(body);\n }\n\n /**\n * Обновляет настройки уведомлений.\n *\n * Отправляются только изменяемые поля, в том же виде, в каком сервер их возвращает.\n */\n async updateSettings(\n input: UpdateNotificationSettingsInput,\n options: RequestOptions = {},\n ): Promise<NotificationSettings> {\n const payload: Record<string, boolean> = {};\n\n for (const key of NOTIFICATION_SETTING_KEYS) {\n const value = input[key];\n if (value !== undefined) payload[key] = value;\n }\n\n const body = await this.http.operation('notifications.updateSettings', {\n path: '/api/notifications/settings',\n body: payload,\n ...options,\n });\n\n return readSettings(body);\n }\n}\n","/** Отметка времени без часового пояса: `2026-07-23 23:14:25`, возможно с долями секунды. */\nimport type { IsoDate } from '../models/common.js';\n\nconst NAIVE_STAMP = /^(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)$/;\n\n/**\n * Приводит отметку времени без часового пояса к ISO-8601, считая её временем UTC.\n *\n * Строку другого вида возвращает нетронутой.\n *\n * @example\n * ```ts\n * utcStampToIso('2026-07-23 23:14:25'); // '2026-07-23T23:14:25Z'\n * utcStampToIso('2026-07-23T23:14:25Z'); // без изменений\n * ```\n */\nexport function utcStampToIso(value: string): string {\n const match = typeof value === 'string' ? NAIVE_STAMP.exec(value) : null;\n if (!match) return value;\n\n const iso = `${match[1]}T${match[2]}Z`;\n return Number.isFinite(Date.parse(iso)) ? iso : value;\n}\n\n/**\n * Разбирает дату API в объект `Date`.\n *\n * @returns `null`, если строки нет или она не разбирается\n *\n * @example\n * ```ts\n * const created = toDate(post.createdAt);\n * ```\n */\nexport function toDate(value: IsoDate | null | undefined): Date | null {\n if (!value) return null;\n const date = new Date(value);\n return Number.isFinite(date.getTime()) ? date : null;\n}\n","import { STATUS_SERVICE } from '../core/config.js';\nimport { utcStampToIso } from '../core/time.js';\nimport { isRecord, pickArray } from '../core/unwrap.js';\nimport type { Announcement, ChangelogEntry, Portal } from '../models/platform.js';\nimport type { PlatformStatus, ServiceStatus } from '../models/status.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Требования к версии одного приложения платформы. */\nexport interface PlatformClientVersion {\n /** Минимальная поддерживаемая версия приложения. */\n minVersion: string;\n /** Последняя доступная версия приложения. */\n latestVersion: string;\n /** Адрес страницы обновления приложения. */\n updateUrl: string;\n}\n\n/** Версии клиентских приложений платформы. */\nexport interface PlatformVersions {\n android: PlatformClientVersion;\n ios: PlatformClientVersion;\n [client: string]: PlatformClientVersion;\n}\n\n/** Приводит `last_checked` каждого сервиса к ISO. Остальное остаётся как прислал сервер. */\nfunction normalizeStatus(body: PlatformStatus): PlatformStatus {\n if (!isRecord(body) || !Array.isArray(body.services)) return body;\n\n return {\n ...body,\n services: body.services.map((service: ServiceStatus) =>\n typeof service?.last_checked === 'string'\n ? { ...service, last_checked: utcStampToIso(service.last_checked) }\n : service,\n ),\n };\n}\n\n/**\n * Сведения о платформе: версии приложений, изменения, анонсы, баннер события.\n *\n * Доступна как `itd.platform`.\n */\nexport class PlatformResource extends BaseResource {\n /**\n * Загружает минимальные и актуальные версии клиентских приложений.\n *\n * Endpoint публичный: автоматическая авторизация в запрос не добавляется.\n *\n * @example\n * ```ts\n * const versions = await itd.platform.version();\n * console.log(versions.android.latestVersion);\n * ```\n */\n version(options: RequestOptions = {}): Promise<PlatformVersions> {\n return this.http.operation<PlatformVersions>('platform.version', {\n path: '/api/platform/version',\n skipAuth: true,\n ...options,\n });\n }\n\n /** Загружает журнал изменений. */\n async changelog(options: RequestOptions = {}): Promise<ChangelogEntry[]> {\n const body = await this.http.operation('platform.changelog', {\n path: '/api/platform/changelog',\n ...options,\n });\n\n return Array.isArray(body) ? (body as ChangelogEntry[]) : [];\n }\n\n /** Загружает анонсы платформы. */\n async announcements(options: RequestOptions = {}): Promise<Announcement[]> {\n const body = await this.http.operation('platform.announcements', {\n path: '/api/platform/announcements',\n ...options,\n });\n\n return pickArray<Announcement>(body, 'announcements');\n }\n\n /** Загружает баннер текущего события — виджет «портал». */\n portal(options: RequestOptions = {}): Promise<Portal> {\n return this.http.operation<Portal>('platform.portal', {\n path: '/api/v1/portal',\n ...options,\n });\n }\n\n /**\n * Загружает состояние сервисов платформы за последние 90 суток.\n *\n * Идёт на хост `статус.итд.com` без авторизации. Ответ кэшируется сервером на минуту.\n * История по суткам приходит разреженной, ровный массив даёт `statusDays`.\n *\n * @example\n * ```ts\n * const status = await itd.platform.status();\n *\n * if (status.overall_status !== 'operational') {\n * const broken = status.services.filter((s) => s.current_status !== 'operational');\n * console.log('лежит:', broken.map((s) => s.name).join(', '));\n * }\n * ```\n */\n async status(options: RequestOptions = {}): Promise<PlatformStatus> {\n const body = await this.http.operation<PlatformStatus>('platform.status', {\n service: STATUS_SERVICE,\n path: '/api/status',\n ...options,\n });\n\n return normalizeStatus(body);\n }\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { Span } from '../models/common.js';\n\n/**\n * Проверяет, что spans целиком лежат внутри текста.\n *\n * Возвращает копии, чтобы последующая мутация входного массива не меняла уже собранные данные.\n */\nexport function validateSpans(content: string, spans: unknown): Span[] {\n if (!Array.isArray(spans)) {\n throw new ItdConfigError('spans должен быть массивом');\n }\n if (content.length === 0 && spans.length > 0) {\n throw new ItdConfigError('Нельзя задать spans без текста: сначала укажите непустой content');\n }\n\n return spans.map((span, index) => {\n if (!span || typeof span !== 'object') {\n throw new ItdConfigError(`Некорректный span №${index + 1}: ожидался объект`);\n }\n\n const candidate = span as Partial<Span>;\n if (\n typeof candidate.type !== 'string' ||\n !Number.isInteger(candidate.offset) ||\n !Number.isInteger(candidate.length) ||\n (candidate.offset ?? -1) < 0 ||\n (candidate.length ?? 0) <= 0 ||\n (candidate.offset ?? 0) + (candidate.length ?? 0) > content.length\n ) {\n throw new ItdConfigError(\n `Некорректный span №${index + 1}: offset и length должны указывать на непустой фрагмент content`,\n );\n }\n\n return { ...candidate } as Span;\n });\n}\n","import type { TextMarkup } from '../builders/markup.js';\nimport type { Span } from '../models/common.js';\nimport { SpanType } from '../types/enums.js';\nimport { validateSpans } from './validate.js';\n\n/** Настройки безопасного импорта разметки. */\nexport interface ParseMarkupOptions {\n /**\n * Разрешённые схемы ссылок без завершающего двоеточия.\n *\n * По умолчанию разрешены только `http` и `https`. Относительные ссылки не превращаются\n * в spans: wire-формату нужен самостоятельный адрес.\n */\n allowedLinkProtocols?: readonly string[];\n}\n\ninterface MutableMarkup {\n content: string;\n spans: Span[];\n}\n\ninterface OpenHtmlSpan {\n tag: string;\n start: number;\n span: Omit<Span, 'offset' | 'length'>;\n}\n\nconst HTML_BLOCK_TAGS = new Set([\n 'address',\n 'article',\n 'aside',\n 'blockquote',\n 'div',\n 'footer',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'header',\n 'li',\n 'main',\n 'nav',\n 'ol',\n 'p',\n 'pre',\n 'section',\n 'ul',\n]);\n\nconst HTML_DROPPED_TAGS = new Set([\n 'applet',\n 'canvas',\n 'embed',\n 'iframe',\n 'math',\n 'noscript',\n 'object',\n 'script',\n 'style',\n 'svg',\n 'template',\n]);\n\nconst HTML_ENTITIES: Readonly<Record<string, string>> = Object.freeze({\n amp: '&',\n apos: \"'\",\n gt: '>',\n lt: '<',\n nbsp: '\\u00a0',\n quot: '\"',\n});\n\nfunction append(output: MutableMarkup, value: string): void {\n output.content += value;\n}\n\nfunction addSpan(\n output: MutableMarkup,\n start: number,\n span: Omit<Span, 'offset' | 'length'>,\n): void {\n const length = output.content.length - start;\n if (length > 0) output.spans.push({ ...span, offset: start, length });\n}\n\nfunction finish(output: MutableMarkup): TextMarkup {\n const unique = new Map<string, Span>();\n for (const span of output.spans) {\n const key = [\n span.type,\n span.offset,\n span.length,\n span.url ?? '',\n span.tag ?? '',\n span.username ?? '',\n span.id ?? '',\n ].join('\\u0000');\n if (!unique.has(key)) unique.set(key, span);\n }\n\n return {\n content: output.content,\n spans: validateSpans(output.content, [...unique.values()]).sort(\n (left, right) => left.offset - right.offset || right.length - left.length,\n ),\n };\n}\n\nfunction allowedProtocols(options: ParseMarkupOptions): Set<string> {\n const values = options.allowedLinkProtocols ?? ['http', 'https'];\n return new Set(\n values\n .map((value) => `${value.toLowerCase().replace(/:$/u, '')}:`)\n .filter((value) => value !== ':'),\n );\n}\n\nfunction safeLink(value: string, protocols: ReadonlySet<string>): string | null {\n try {\n const url = new URL(value);\n return url.hostname !== '' && protocols.has(url.protocol.toLowerCase()) ? value : null;\n } catch {\n return null;\n }\n}\n\nfunction isEscaped(value: string, index: number): boolean {\n let slashes = 0;\n for (let cursor = index - 1; cursor >= 0 && value[cursor] === '\\\\'; cursor -= 1) slashes += 1;\n return slashes % 2 === 1;\n}\n\nfunction findClosing(value: string, marker: string, from: number): number {\n let cursor = from;\n while (cursor < value.length) {\n const found = value.indexOf(marker, cursor);\n if (found < 0) return -1;\n if (!isEscaped(value, found)) return found;\n cursor = found + marker.length;\n }\n return -1;\n}\n\nfunction findBracketEnd(value: string, from: number, open: string, close: string): number {\n let depth = 1;\n for (let cursor = from; cursor < value.length; cursor += 1) {\n const character = value[cursor];\n if (character === '\\\\') {\n cursor += 1;\n continue;\n }\n if (character === open) depth += 1;\n if (character === close) {\n depth -= 1;\n if (depth === 0) return cursor;\n }\n }\n return -1;\n}\n\nfunction decodeEntities(value: string): string {\n return value.replace(/&(#(?:x[\\da-f]+|\\d+)|[a-z][a-z\\d]+);/giu, (raw, entity: string) => {\n if (entity.startsWith('#x') || entity.startsWith('#X')) {\n const code = Number.parseInt(entity.slice(2), 16);\n return Number.isFinite(code) && code <= 0x10ffff ? String.fromCodePoint(code) : raw;\n }\n if (entity.startsWith('#')) {\n const code = Number.parseInt(entity.slice(1), 10);\n return Number.isFinite(code) && code <= 0x10ffff ? String.fromCodePoint(code) : raw;\n }\n return HTML_ENTITIES[entity.toLowerCase()] ?? raw;\n });\n}\n\nfunction normalizeCodeSpan(value: string): string {\n const normalized = value.replace(/\\r\\n?|\\n/gu, ' ');\n if (\n normalized.length >= 2 &&\n normalized.startsWith(' ') &&\n normalized.endsWith(' ') &&\n !/^ +$/u.test(normalized)\n ) {\n return normalized.slice(1, -1);\n }\n return normalized;\n}\n\nfunction markdownStyle(\n source: string,\n index: number,\n): { marker: string; type: Span['type'] } | undefined {\n const styles = [\n { marker: '**', type: SpanType.Bold },\n { marker: '__', type: SpanType.Bold },\n { marker: '~~', type: SpanType.Strike },\n { marker: '||', type: SpanType.Spoiler },\n { marker: '*', type: SpanType.Italic },\n { marker: '_', type: SpanType.Italic },\n ] as const;\n\n for (const style of styles) {\n if (!source.startsWith(style.marker, index)) continue;\n if (style.marker.includes('_')) {\n const before = source[index - 1] ?? '';\n const after = source[index + style.marker.length] ?? '';\n if (/[\\p{L}\\p{N}]/u.test(before) && /[\\p{L}\\p{N}]/u.test(after)) continue;\n }\n return style;\n }\n return undefined;\n}\n\nfunction parseMarkdownInline(\n source: string,\n output: MutableMarkup,\n protocols: ReadonlySet<string>,\n): void {\n let index = 0;\n while (index < source.length) {\n const character = source[index] ?? '';\n\n if (character === '\\\\' && index + 1 < source.length) {\n append(output, source[index + 1] ?? '');\n index += 2;\n continue;\n }\n\n if (character === '`') {\n const run = source.slice(index).match(/^`+/u)?.[0] ?? '`';\n const close = findClosing(source, run, index + run.length);\n if (close > index + run.length) {\n const start = output.content.length;\n append(output, normalizeCodeSpan(source.slice(index + run.length, close)));\n addSpan(output, start, { type: SpanType.Monospace });\n index = close + run.length;\n continue;\n }\n }\n\n const image = source.startsWith('![', index);\n if ((image || character === '[') && !isEscaped(source, index)) {\n const labelStart = index + (image ? 2 : 1);\n const labelEnd = findBracketEnd(source, labelStart, '[', ']');\n if (labelEnd >= 0 && source[labelEnd + 1] === '(') {\n const destinationEnd = findBracketEnd(source, labelEnd + 2, '(', ')');\n if (destinationEnd >= 0) {\n const label = source.slice(labelStart, labelEnd);\n const rawDestination = source.slice(labelEnd + 2, destinationEnd).trim();\n const destination = rawDestination.replace(/\\\\([\\\\)])/gu, '$1');\n const start = output.content.length;\n parseMarkdownInline(label, output, protocols);\n if (!image) {\n const url = safeLink(destination, protocols);\n if (url) addSpan(output, start, { type: SpanType.Link, url });\n }\n index = destinationEnd + 1;\n continue;\n }\n }\n }\n\n if (source.slice(index, index + 3).toLowerCase() === '<u>') {\n const close = source.toLowerCase().indexOf('</u>', index + 3);\n if (close > index + 3) {\n const start = output.content.length;\n parseMarkdownInline(source.slice(index + 3, close), output, protocols);\n addSpan(output, start, { type: SpanType.Underline });\n index = close + 4;\n continue;\n }\n }\n\n if (character === '<') {\n const close = source.indexOf('>', index + 1);\n if (close > index + 1) {\n const candidate = decodeEntities(source.slice(index + 1, close));\n const url = safeLink(candidate, protocols);\n if (url) {\n const start = output.content.length;\n append(output, candidate);\n addSpan(output, start, { type: SpanType.Link, url });\n index = close + 1;\n continue;\n }\n }\n }\n\n const style = markdownStyle(source, index);\n if (style) {\n const close = findClosing(source, style.marker, index + style.marker.length);\n if (close > index + style.marker.length) {\n const start = output.content.length;\n parseMarkdownInline(source.slice(index + style.marker.length, close), output, protocols);\n addSpan(output, start, { type: style.type });\n index = close + style.marker.length;\n continue;\n }\n }\n\n if (character === '&') {\n const entity = source.slice(index).match(/^&(?:#(?:x[\\da-f]+|\\d+)|[a-z][a-z\\d]+);/iu)?.[0];\n if (entity) {\n append(output, decodeEntities(entity));\n index += entity.length;\n continue;\n }\n }\n\n append(output, character);\n index += 1;\n }\n}\n\nfunction lineEnd(source: string, from: number): number {\n const found = source.indexOf('\\n', from);\n return found < 0 ? source.length : found;\n}\n\n/**\n * Преобразует безопасное подмножество Markdown в текст и wire-spans.\n *\n * Поддерживаются bold, italic, strike, spoiler, inline/fenced code, ссылки, underline\n * через `<u>` и цитаты `>`. Неподдерживаемая и незакрытая разметка остаётся текстом.\n * У изображений сохраняется alt-текст без URL.\n */\nexport function parseMarkdown(source: string, options: ParseMarkupOptions = {}): TextMarkup {\n const output: MutableMarkup = { content: '', spans: [] };\n const protocols = allowedProtocols(options);\n let position = 0;\n\n while (position < source.length) {\n const end = lineEnd(source, position);\n const line = source.slice(position, end);\n const hasNewline = end < source.length;\n const fence = line.match(/^ {0,3}(`{3,}|~{3,})[^\\n]*$/u)?.[1];\n\n if (fence) {\n let closeStart = hasNewline ? end + 1 : source.length;\n let closeEnd = closeStart;\n let found = false;\n while (closeStart < source.length) {\n closeEnd = lineEnd(source, closeStart);\n const closeLine = source.slice(closeStart, closeEnd);\n const marker = closeLine.match(/^ {0,3}(`{3,}|~{3,})\\s*$/u)?.[1];\n if (marker !== undefined && marker[0] === fence[0] && marker.length >= fence.length) {\n found = true;\n break;\n }\n closeStart = closeEnd < source.length ? closeEnd + 1 : source.length;\n }\n\n if (found) {\n const bodyStart = end + 1;\n let body = source.slice(bodyStart, closeStart);\n if (body.endsWith('\\n')) body = body.slice(0, -1);\n const start = output.content.length;\n append(output, body);\n addSpan(output, start, { type: SpanType.Monospace });\n if (closeEnd < source.length) append(output, '\\n');\n position = closeEnd < source.length ? closeEnd + 1 : source.length;\n continue;\n }\n }\n\n const quote = line.match(/^ {0,3}> ?(.*)$/u);\n if (quote) {\n const start = output.content.length;\n parseMarkdownInline(quote[1] ?? '', output, protocols);\n addSpan(output, start, { type: SpanType.Quote });\n } else {\n parseMarkdownInline(line, output, protocols);\n }\n\n if (hasNewline) append(output, '\\n');\n position = hasNewline ? end + 1 : source.length;\n }\n\n return finish(output);\n}\n\nfunction readAttribute(source: string, name: string): string | undefined {\n const pattern = new RegExp(\n `(?:^|\\\\s)${name}\\\\s*=\\\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\\\s\"'=<>\\\\x60]+))`,\n 'iu',\n );\n const match = source.match(pattern);\n const value = match?.[1] ?? match?.[2] ?? match?.[3];\n return value === undefined ? undefined : decodeEntities(value);\n}\n\nfunction htmlSpan(\n tag: string,\n attributes: string,\n protocols: ReadonlySet<string>,\n): Omit<Span, 'offset' | 'length'> | undefined {\n switch (tag) {\n case 'b':\n case 'strong':\n return { type: SpanType.Bold };\n case 'em':\n case 'i':\n return { type: SpanType.Italic };\n case 'u':\n return { type: SpanType.Underline };\n case 'del':\n case 's':\n case 'strike':\n return { type: SpanType.Strike };\n case 'code':\n case 'pre':\n return { type: SpanType.Monospace };\n case 'blockquote':\n return { type: SpanType.Quote };\n case 'a': {\n const href = readAttribute(attributes, 'href');\n const url = href ? safeLink(href, protocols) : null;\n return url ? { type: SpanType.Link, url } : undefined;\n }\n case 'span': {\n const classes = readAttribute(attributes, 'class')?.split(/\\s+/u) ?? [];\n if (\n readAttribute(attributes, 'data-spoiler') === 'true' ||\n classes.some((value) => value.endsWith('-spoiler'))\n ) {\n return { type: SpanType.Spoiler };\n }\n if (classes.some((value) => value.endsWith('-quote'))) {\n return { type: SpanType.Quote };\n }\n return undefined;\n }\n default:\n return undefined;\n }\n}\n\nfunction ensureBlockBreak(output: MutableMarkup): void {\n if (output.content !== '' && !output.content.endsWith('\\n')) append(output, '\\n');\n}\n\nfunction findHtmlTagEnd(source: string, from: number): number {\n let quote: '\"' | \"'\" | undefined;\n\n for (let index = from; index < source.length; index += 1) {\n const character = source[index];\n if (quote) {\n if (character === quote) quote = undefined;\n continue;\n }\n if (character === '\"' || character === \"'\") {\n quote = character;\n continue;\n }\n if (character === '>') return index;\n }\n\n return -1;\n}\n\n/**\n * Преобразует ограниченный HTML в обычный текст и wire-spans.\n *\n * Разрешены только семантические теги форматирования. Атрибуты событий игнорируются,\n * содержимое `script`, `style`, `iframe`, `object`, SVG и подобных активных элементов\n * удаляется. Опасный `href` сохраняет текст ссылки, но не создаёт span.\n */\nexport function parseHtml(source: string, options: ParseMarkupOptions = {}): TextMarkup {\n const output: MutableMarkup = { content: '', spans: [] };\n const protocols = allowedProtocols(options);\n const stack: OpenHtmlSpan[] = [];\n let position = 0;\n\n const closeTag = (tag: string): void => {\n for (let index = stack.length - 1; index >= 0; index -= 1) {\n const open = stack[index];\n if (open?.tag !== tag) continue;\n stack.splice(index, 1);\n addSpan(output, open.start, open.span);\n return;\n }\n };\n\n while (position < source.length) {\n if (source.startsWith('<!--', position)) {\n const end = source.indexOf('-->', position + 4);\n position = end < 0 ? source.length : end + 3;\n continue;\n }\n\n if (source[position] === '<') {\n const end = findHtmlTagEnd(source, position + 1);\n if (end >= 0) {\n const raw = source.slice(position, end + 1);\n const parsed = raw.match(/^<\\s*(\\/?)\\s*([a-z][\\w-]*)([\\s\\S]*?)\\/?\\s*>$/iu);\n if (parsed) {\n const closing = parsed[1] === '/';\n const tag = (parsed[2] ?? '').toLowerCase();\n const attributes = parsed[3] ?? '';\n\n if (!closing && HTML_DROPPED_TAGS.has(tag)) {\n const closingPattern = new RegExp(`<\\\\s*\\\\/\\\\s*${tag}\\\\s*>`, 'igu');\n closingPattern.lastIndex = end + 1;\n const match = closingPattern.exec(source);\n position = match ? match.index + match[0].length : source.length;\n continue;\n }\n\n if (tag === 'br' && !closing) {\n append(output, '\\n');\n position = end + 1;\n continue;\n }\n\n if (tag === 'img' && !closing) {\n append(output, readAttribute(attributes, 'alt') ?? '');\n position = end + 1;\n continue;\n }\n\n if (closing) {\n closeTag(tag);\n if (HTML_BLOCK_TAGS.has(tag)) ensureBlockBreak(output);\n } else {\n if (HTML_BLOCK_TAGS.has(tag)) ensureBlockBreak(output);\n const span = htmlSpan(tag, attributes, protocols);\n if (span) stack.push({ tag, start: output.content.length, span });\n }\n\n position = end + 1;\n continue;\n }\n }\n }\n\n const next = source.indexOf('<', position + 1);\n const end = next < 0 ? source.length : next;\n append(output, decodeEntities(source.slice(position, end)));\n position = end;\n }\n\n while (stack.length > 0) {\n const open = stack.pop();\n if (open) addSpan(output, open.start, open.span);\n }\n\n while (output.content.endsWith('\\n')) output.content = output.content.slice(0, -1);\n return finish(output);\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { Span } from '../models/common.js';\nimport { validateSpans } from '../spans/validate.js';\nimport { SpanType } from '../types/enums.js';\nimport { BUILDER, type BuilderInput, type ItdBuilder, resolveInput } from './base.js';\n\n/** Текст вместе с рассчитанной разметкой. */\nexport interface TextMarkup {\n content: string;\n spans: Span[];\n}\n\n/** Описание фрагмента без смещения: его вычисляет {@link MarkupBuilder}. */\nexport type MarkupSpan = Omit<Span, 'offset' | 'length'>;\n\n/** Что принимает метод разметки: результат, билдер или функция-настройщик. */\nexport type MarkupInput = BuilderInput<TextMarkup, MarkupBuilder>;\n\n/**\n * Содержимое форматированного фрагмента.\n *\n * Строка создаёт простой фрагмент. Билдер, готовая разметка или функция позволяют вложить\n * одни spans в другие, как при последовательном форматировании выделения в редакторе сайта.\n */\nexport type MarkupContent = string | MarkupInput;\n\n/** Какие сущности искать в {@link autoSpans}. */\nexport interface AutoSpansOptions {\n /** Находить `#хэштеги`. По умолчанию `true`. */\n hashtags?: boolean;\n /** Находить `@упоминания`. По умолчанию `true`. */\n mentions?: boolean;\n /** Находить абсолютные HTTP(S)-ссылки. По умолчанию `true`. */\n links?: boolean;\n}\n\nfunction validateMarkup(value: TextMarkup): TextMarkup {\n if (!value || typeof value.content !== 'string' || !Array.isArray(value.spans)) {\n throw new ItdConfigError('Разметка должна содержать строку content и массив spans');\n }\n\n return {\n content: value.content,\n spans: validateSpans(value.content, value.spans).sort(\n (left, right) => left.offset - right.offset || right.length - left.length,\n ),\n };\n}\n\nfunction entityName(value: string, prefix: '#' | '@', label: string): string {\n const name = value.startsWith(prefix) ? value.slice(prefix.length) : value;\n if (name === '' || /\\s/u.test(name)) {\n throw new ItdConfigError(`${label} должен быть непустым и не содержать пробелов`);\n }\n return name;\n}\n\n/**\n * Неизменяемый билдер текста с разметкой.\n *\n * Каждый метод дописывает фрагмент и сам считает `offset` и `length` в единицах UTF-16 —\n * именно такие индексы использует JavaScript-редактор сайта.\n */\nexport class MarkupBuilder implements ItdBuilder<TextMarkup> {\n /** @internal */\n readonly [BUILDER] = true as const;\n\n readonly #content: string;\n readonly #spans: Span[];\n\n /** @internal Создавайте билдер функцией {@link markup}. */\n constructor(content: string, spans: Span[]) {\n this.#content = content;\n this.#spans = spans;\n }\n\n /** Дописывает обычный текст без разметки. */\n text(value: string): MarkupBuilder {\n return new MarkupBuilder(this.#content + value, this.#spans);\n }\n\n /** Дописывает переводы строк. */\n newline(count = 1): MarkupBuilder {\n if (!Number.isInteger(count) || count < 0) {\n throw new ItdConfigError('Количество переводов строк должно быть целым неотрицательным');\n }\n return this.text('\\n'.repeat(count));\n }\n\n /** @internal Добавляет готовый фрагмент и сдвигает его spans к текущему концу текста. */\n #formatted(value: MarkupContent, outerSpans: readonly MarkupSpan[]): MarkupBuilder {\n const fragment =\n typeof value === 'string' ? { content: value, spans: [] } : resolveMarkup(value);\n if (fragment.content.length === 0) {\n throw new ItdConfigError('Размечаемый фрагмент не может быть пустым');\n }\n\n const offset = this.#content.length;\n const shifted = fragment.spans.map((span) => ({\n ...span,\n offset: span.offset + offset,\n }));\n const outer = outerSpans.map((span) => ({\n ...span,\n offset,\n length: fragment.content.length,\n }));\n\n return new MarkupBuilder(this.#content + fragment.content, [\n ...this.#spans,\n ...shifted,\n ...outer,\n ]);\n }\n\n /**\n * Дописывает фрагмент с произвольным типом разметки.\n *\n * Вложенный билдер позволяет форматировать часть фрагмента дополнительным стилем.\n * Для нескольких стилей на всём фрагменте используйте {@link styled}.\n */\n span(value: MarkupContent, span: MarkupSpan): MarkupBuilder {\n return this.#formatted(value, [span]);\n }\n\n /**\n * Дописывает фрагмент с несколькими стилями на одном диапазоне.\n *\n * Для `link`, которому нужен `url`, используйте {@link link}; произвольные spans с\n * метаданными можно объединять через вложенные вызовы {@link span}.\n */\n styled(value: MarkupContent, ...types: Span['type'][]): MarkupBuilder {\n if (types.length === 0) return this.#formatted(value, []);\n return this.#formatted(\n value,\n types.map((type) => ({ type })),\n );\n }\n\n /** Дописывает `#хэштег` и сохраняет имя без решётки в `tag`. */\n hashtag(tag: string): MarkupBuilder {\n const name = entityName(tag, '#', 'Хэштег');\n return this.span(`#${name}`, { type: SpanType.Hashtag, tag: name });\n }\n\n /** Дописывает `@username` и сохраняет имя пользователя в `username`. */\n mention(username: string): MarkupBuilder {\n const name = entityName(username, '@', 'Упоминание');\n return this.span(`@${name}`, { type: SpanType.Mention, username: name });\n }\n\n /**\n * Дописывает ссылку.\n *\n * Для строки адрес по умолчанию становится и текстом ссылки. Вложенному форматированному\n * фрагменту URL нужно передать явно.\n */\n link(content: MarkupContent, url?: string): MarkupBuilder {\n const resolvedUrl = url ?? (typeof content === 'string' ? content : undefined);\n if (resolvedUrl === undefined) {\n throw new ItdConfigError('Для форматированного текста ссылки нужно явно передать URL');\n }\n return this.span(content, { type: SpanType.Link, url: resolvedUrl });\n }\n\n bold(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Bold });\n }\n\n italic(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Italic });\n }\n\n underline(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Underline });\n }\n\n strike(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Strike });\n }\n\n spoiler(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Spoiler });\n }\n\n monospace(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Monospace });\n }\n\n quote(content: MarkupContent): MarkupBuilder {\n return this.span(content, { type: SpanType.Quote });\n }\n\n build(): TextMarkup {\n return validateMarkup({ content: this.#content, spans: this.#spans });\n }\n\n toJSON(): TextMarkup {\n return this.build();\n }\n}\n\n/** Начинает сборку текста с автоматически вычисляемыми смещениями. */\nexport function markup(content = ''): MarkupBuilder {\n return new MarkupBuilder(content, []);\n}\n\n/** Приводит любую форму разметки к тексту и массиву spans. */\nexport function resolveMarkup(input: MarkupInput): TextMarkup {\n return resolveInput(input, () => markup(), validateMarkup);\n}\n\nfunction trimUrlEnd(value: string): string {\n let result = value;\n const pairs = [\n ['(', ')'],\n ['[', ']'],\n ['{', '}'],\n ] as const;\n\n let changed: boolean;\n do {\n const before = result;\n while (/[.,!?;:…'\"»”]$/u.test(result)) result = result.slice(0, -1);\n\n for (const [open, close] of pairs) {\n while (result.endsWith(close) && result.split(close).length > result.split(open).length) {\n result = result.slice(0, -1);\n }\n }\n changed = result !== before;\n } while (changed);\n\n return result;\n}\n\nfunction overlaps(span: Span, start: number, end: number): boolean {\n return span.offset < end && start < span.offset + span.length;\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return (url.protocol === 'http:' || url.protocol === 'https:') && url.hostname !== '';\n } catch {\n return false;\n }\n}\n\nfunction previousCodePoint(text: string, index: number): string {\n if (index <= 0) return '';\n const last = text.charCodeAt(index - 1);\n if (last >= 0xdc00 && last <= 0xdfff && index >= 2) {\n const first = text.charCodeAt(index - 2);\n if (first >= 0xd800 && first <= 0xdbff) return text.slice(index - 2, index);\n }\n return text[index - 1] ?? '';\n}\n\n/**\n * Находит в тексте те сущности, которые сайт получает после серверного разбора:\n * HTTP(S)-ссылки, `#хэштеги` и `@упоминания`.\n *\n * Смещения выражены в UTF-16 code units, поэтому совпадают с `String#slice`,\n * `substring`, DOM Selection и wire-форматом сайта даже при наличии эмодзи.\n */\nexport function autoSpans(text: string, options: AutoSpansOptions = {}): Span[] {\n const includeLinks = options.links ?? true;\n const includeHashtags = options.hashtags ?? true;\n const includeMentions = options.mentions ?? true;\n const spans: Span[] = [];\n const links: Span[] = [];\n\n const urlPattern = /https?:\\/\\/[^\\s<>\"'`]+/giu;\n for (const match of text.matchAll(urlPattern)) {\n const raw = trimUrlEnd(match[0]);\n if (raw.length === 0 || match.index === undefined || !isHttpUrl(raw)) continue;\n links.push({\n type: SpanType.Link,\n offset: match.index,\n length: raw.length,\n url: raw,\n });\n }\n if (includeLinks) spans.push(...links);\n\n if (includeHashtags || includeMentions) {\n const entityPattern = /#([\\p{L}\\p{M}\\p{N}_]+)|@([a-z\\d][a-z\\d._-]*)/giu;\n\n for (const match of text.matchAll(entityPattern)) {\n if (match.index === undefined) continue;\n const offset = match.index;\n const previous = previousCodePoint(text, offset);\n if (/[\\p{L}\\p{M}\\p{N}_#@.]/u.test(previous)) continue;\n\n const end = offset + match[0].length;\n if (links.some((span) => overlaps(span, offset, end))) continue;\n\n const hashtag = match[1];\n const username = match[2]?.replace(/[._-]+$/u, '');\n if (hashtag !== undefined && includeHashtags) {\n spans.push({\n type: SpanType.Hashtag,\n offset,\n length: match[0].length,\n tag: hashtag,\n });\n } else if (username !== undefined && includeMentions) {\n spans.push({\n type: SpanType.Mention,\n offset,\n length: username.length + 1,\n username,\n });\n }\n }\n }\n\n return spans.sort((left, right) => left.offset - right.offset || right.length - left.length);\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { CreatePollInput } from '../types/params.js';\nimport { BUILDER, type BuilderInput, type ItdBuilder, resolveInput } from './base.js';\n\n/** Минимальное число вариантов ответа. */\nconst MIN_OPTIONS = 2;\n\n/** Ограничения опроса — те же, что действуют в интерфейсе итд.com. */\nconst MAX_OPTIONS = 10;\nconst MAX_QUESTION_LENGTH = 200;\nconst MAX_OPTION_LENGTH = 100;\n\n/**\n * Проверяет данные опроса.\n *\n * Применяется и к билдеру, и к обычному объекту — правила одни и те же.\n *\n * @throws {ItdConfigError} если вопрос пуст, вариантов меньше двух или есть дубли\n */\nexport function validatePoll(input: CreatePollInput): CreatePollInput {\n const question = input?.question;\n\n if (typeof question !== 'string' || question.trim() === '') {\n throw new ItdConfigError('Опрос требует непустого вопроса');\n }\n\n if (question.trim().length > MAX_QUESTION_LENGTH) {\n throw new ItdConfigError(\n `Вопрос длиннее ${MAX_QUESTION_LENGTH} символов (передано ${question.trim().length})`,\n );\n }\n\n const options = Array.isArray(input.options) ? input.options : [];\n\n const texts = options.map((option, index) => {\n const text = typeof option?.text === 'string' ? option.text.trim() : '';\n if (text === '') {\n throw new ItdConfigError(`Вариант ответа №${index + 1} пуст — у каждого должен быть текст`);\n }\n if (text.length > MAX_OPTION_LENGTH) {\n throw new ItdConfigError(\n `Вариант ответа №${index + 1} длиннее ${MAX_OPTION_LENGTH} символов (передано ${text.length})`,\n );\n }\n return text;\n });\n\n if (texts.length < MIN_OPTIONS) {\n throw new ItdConfigError(\n `Опрос требует минимум ${MIN_OPTIONS} варианта, передано: ${texts.length}`,\n );\n }\n\n if (texts.length > MAX_OPTIONS) {\n throw new ItdConfigError(\n `Опрос допускает не больше ${MAX_OPTIONS} вариантов, передано: ${texts.length}`,\n );\n }\n\n const seen = new Set<string>();\n for (const text of texts) {\n if (seen.has(text)) {\n throw new ItdConfigError(`Вариант «${text}» повторяется — варианты должны различаться`);\n }\n seen.add(text);\n }\n\n return {\n question: question.trim(),\n options: texts.map((text) => ({ text })),\n // Поле обязательно: без него сервер отвергает создание поста с опросом,\n // даже когда выбор одиночный.\n multipleChoice: input.multipleChoice ?? false,\n };\n}\n\n/**\n * Билдер опроса.\n *\n * Неизменяемый: каждый вызов возвращает новый экземпляр, поэтому заготовку можно\n * переиспользовать, не боясь её испортить. Создаётся функцией {@link poll}.\n */\nexport class PollBuilder implements ItdBuilder<CreatePollInput> {\n /** @internal */\n readonly [BUILDER] = true as const;\n\n readonly #state: CreatePollInput;\n\n /** @internal Создавайте билдер функцией {@link poll}. */\n constructor(state: CreatePollInput) {\n this.#state = state;\n }\n\n /** Задаёт вопрос. */\n question(text: string): PollBuilder {\n return new PollBuilder({ ...this.#state, question: text });\n }\n\n /** Добавляет один вариант ответа. */\n option(text: string): PollBuilder {\n return new PollBuilder({ ...this.#state, options: [...this.#state.options, { text }] });\n }\n\n /**\n * Добавляет несколько вариантов сразу.\n *\n * @example\n * ```ts\n * poll('ну как?').options('да', 'нет', 'не знаю');\n * ```\n */\n options(...texts: string[]): PollBuilder {\n return new PollBuilder({\n ...this.#state,\n options: [...this.#state.options, ...texts.map((text) => ({ text }))],\n });\n }\n\n /** Разрешает выбор нескольких вариантов. */\n multipleChoice(enabled = true): PollBuilder {\n return new PollBuilder({ ...this.#state, multipleChoice: enabled });\n }\n\n build(): CreatePollInput {\n return validatePoll(this.#state);\n }\n\n toJSON(): CreatePollInput {\n return this.build();\n }\n}\n\n/**\n * Начинает сборку опроса.\n *\n * @param question вопрос; можно задать позже методом {@link PollBuilder.question}\n *\n * @example\n * ```ts\n * import { poll } from 'itd-api';\n *\n * const q = poll('Какой язык лучше?')\n * .options('TypeScript', 'JavaScript')\n * .multipleChoice();\n *\n * await itd.posts.create({ content: 'голосуем', poll: q });\n * ```\n */\nexport function poll(question = ''): PollBuilder {\n return new PollBuilder({ question, options: [] });\n}\n\n/** Что принимает параметр опроса: объект, билдер или функция-настройщик. */\nexport type PollInput = BuilderInput<CreatePollInput, PollBuilder>;\n\n/** Приводит любую форму входа к готовым данным опроса. */\nexport function resolvePoll(input: PollInput): CreatePollInput {\n return resolveInput(input, () => poll(), validatePoll);\n}\n","import type { FileInput } from '../core/attachments/contracts.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport type { Span, UserId } from '../models/common.js';\nimport { type ParseMarkupOptions, parseHtml, parseMarkdown } from '../spans/parse.js';\nimport { validateSpans } from '../spans/validate.js';\nimport { SpanType } from '../types/enums.js';\nimport type { CreatePostData, UpdatePostInput } from '../types/params.js';\nimport { BUILDER, type BuilderInput, type ItdBuilder, isBuilder } from './base.js';\nimport { type AutoSpansOptions, autoSpans, type MarkupInput, resolveMarkup } from './markup.js';\nimport { type PollInput, resolvePoll } from './poll.js';\n\n/** UUID любой версии. */\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst BUILD_UPDATE = Symbol.for('itd.postBuilder.update');\n\n/** Данные для создания поста, включая поддерживаемые builder-формы вложенного опроса. */\nexport interface CreatePostInput extends Omit<CreatePostData, 'poll'> {\n /** Опрос: обычный объект, {@link PollBuilder} или функция-настройщик. */\n poll?: PollInput;\n}\n\n/**\n * Проверяет и нормализует данные поста.\n *\n * Отдельного внимания заслуживает `wallRecipientId`: API принимает там **только UUID**,\n * а имя пользователя молча приводит к ошибке на сервере. Проверка здесь превращает\n * это в понятное сообщение до отправки запроса.\n *\n * @throws {ItdConfigError} если пост пуст или получатель стены задан именем пользователя\n */\nexport function validatePost(input: CreatePostInput): CreatePostData {\n const content = typeof input?.content === 'string' ? input.content : '';\n const attachmentIds = input?.attachmentIds ?? [];\n const files = input?.files ?? [];\n\n const hasContent = content.trim() !== '';\n const hasAttachments = attachmentIds.length > 0 || files.length > 0;\n const hasPoll = Boolean(input?.poll);\n\n if (!hasContent && !hasAttachments && !hasPoll) {\n throw new ItdConfigError('Пост пуст: нужен текст, вложение или опрос');\n }\n\n const wallRecipientId = input.wallRecipientId;\n if (wallRecipientId !== undefined && wallRecipientId !== null) {\n if (!UUID_PATTERN.test(wallRecipientId)) {\n throw new ItdConfigError(\n `wallRecipientId должен быть UUID, а не именем пользователя (получено: «${wallRecipientId}»). ` +\n 'Идентификатор можно взять из профиля: (await itd.users.get(username)).id',\n );\n }\n }\n\n // Опрос внутри обычного объекта тоже может быть билдером или функцией — приводим его\n // здесь, чтобы форма записи не влияла на результат и на проверки.\n const { poll: inputPoll, ...data } = input;\n return {\n ...data,\n ...(input.spans !== undefined ? { spans: validateSpans(content, input.spans) } : {}),\n ...(inputPoll !== undefined ? { poll: resolvePoll(inputPoll) } : {}),\n };\n}\n\n/** Внутреннее состояние {@link PostBuilder}. */\ninterface PostState extends CreatePostInput {\n content: string;\n contentSet: boolean;\n attachmentIds: string[];\n files: FileInput[];\n}\n\n/**\n * Билдер поста.\n *\n * Неизменяемый: каждый вызов возвращает новый экземпляр, поэтому заготовку можно\n * переиспользовать. Создаётся функцией {@link post}.\n *\n * @example Заготовка для нескольких постов\n * ```ts\n * const onWall = post().onWall(userId);\n *\n * await itd.posts.create(onWall.content('первый'));\n * await itd.posts.create(onWall.content('второй')); // заготовка не испорчена\n * ```\n */\nexport class PostBuilder implements ItdBuilder<CreatePostData> {\n /** @internal */\n readonly [BUILDER] = true as const;\n\n readonly #state: PostState;\n\n /** @internal Создавайте билдер функцией {@link post}. */\n constructor(state: PostState) {\n this.#state = state;\n }\n\n /**\n * Задаёт текст поста, заменяя прежний вместе с его разметкой.\n *\n * Spans привязаны к конкретному тексту, поэтому после замены их нужно задать заново\n * через {@link spans}, {@link markup} или {@link autoSpans}.\n */\n content(text: string): PostBuilder {\n const state: PostState = { ...this.#state, content: text, contentSet: true };\n delete state.spans;\n return new PostBuilder(state);\n }\n\n /** Дописывает текст к уже заданному. */\n append(text: string): PostBuilder {\n const separator = this.#state.content === '' ? '' : '\\n';\n return new PostBuilder({\n ...this.#state,\n content: this.#state.content + separator + text,\n contentSet: true,\n });\n }\n\n /**\n * Задаёт готовую разметку текста. Смещения проверяются при {@link build}.\n *\n * Для автоматического поиска сущностей есть {@link autoSpans}, а для вычисления смещений\n * при сборке текста — {@link markup}.\n */\n spans(spans: Span[]): PostBuilder {\n return new PostBuilder({ ...this.#state, spans });\n }\n\n /**\n * Заменяет текст и разметку результатом {@link MarkupBuilder}.\n *\n * @example\n * ```ts\n * post().markup((m) => m.text('смотрите ').hashtag('котики').text(' от ').mention('nowkie'));\n * ```\n */\n markup(input: MarkupInput): PostBuilder {\n const result = resolveMarkup(input);\n return new PostBuilder({\n ...this.#state,\n content: result.content,\n contentSet: true,\n spans: result.spans,\n });\n }\n\n /** Заменяет текст и spans результатом безопасного разбора Markdown. */\n markdown(source: string, options: ParseMarkupOptions = {}): PostBuilder {\n return this.markup(parseMarkdown(source, options));\n }\n\n /** Заменяет текст и spans результатом безопасного разбора ограниченного HTML. */\n html(source: string, options: ParseMarkupOptions = {}): PostBuilder {\n return this.markup(parseHtml(source, options));\n }\n\n /**\n * Находит HTTP(S)-ссылки, хэштеги и упоминания в уже заданном тексте.\n *\n * Ручные стили сохраняются. Повторный вызов не дублирует уже найденные сущности.\n */\n autoSpans(options: AutoSpansOptions = {}): PostBuilder {\n const current = this.#state.spans ?? [];\n const entityTypes = new Set<string>([SpanType.Hashtag, SpanType.Mention, SpanType.Link]);\n const detected = autoSpans(this.#state.content, options).filter((candidate) => {\n return !current.some(\n (span) =>\n entityTypes.has(span.type) &&\n span.offset < candidate.offset + candidate.length &&\n candidate.offset < span.offset + span.length,\n );\n });\n\n return new PostBuilder({\n ...this.#state,\n spans: [...current, ...detected].sort(\n (left, right) => left.offset - right.offset || right.length - left.length,\n ),\n });\n }\n\n /**\n * Публикует пост на стене другого пользователя.\n *\n * @param userId **UUID** пользователя; имя пользователя не подойдёт\n */\n onWall(userId: UserId): PostBuilder {\n return new PostBuilder({ ...this.#state, wallRecipientId: userId });\n }\n\n /**\n * Прикладывает файл — он будет загружен перед публикацией.\n *\n * Порядок вызовов сохраняется в порядке вложений.\n */\n attach(file: FileInput): PostBuilder {\n return new PostBuilder({ ...this.#state, files: [...this.#state.files, file] });\n }\n\n /** Прикладывает уже загруженное вложение по его идентификатору. */\n attachId(attachmentId: string): PostBuilder {\n return new PostBuilder({\n ...this.#state,\n attachmentIds: [...this.#state.attachmentIds, attachmentId],\n });\n }\n\n /**\n * Добавляет опрос.\n *\n * Принимает объект, {@link PollBuilder} или функцию-настройщик.\n *\n * @example\n * ```ts\n * post('голосуем').poll((q) => q.question('ну как?').options('да', 'нет'));\n * ```\n */\n poll(input: PollInput): PostBuilder {\n return new PostBuilder({ ...this.#state, poll: resolvePoll(input) });\n }\n\n #input(includeEmptyContent = false): CreatePostInput {\n const { content, contentSet: _contentSet, attachmentIds, files, ...rest } = this.#state;\n return {\n ...rest,\n ...(content !== '' || includeEmptyContent ? { content } : {}),\n ...(attachmentIds.length > 0 ? { attachmentIds } : {}),\n ...(files.length > 0 ? { files } : {}),\n };\n }\n\n build(): CreatePostData {\n return validatePost(this.#input());\n }\n\n /** @internal Собирает данные по правилам `posts.update`, не применяя правила создания. */\n [BUILD_UPDATE](): UpdatePostInput {\n return validatePostUpdate(this.#input(this.#state.contentSet));\n }\n\n toJSON(): CreatePostData {\n return this.build();\n }\n}\n\n/**\n * Начинает сборку поста.\n *\n * @param content текст; можно задать позже методом {@link PostBuilder.content}\n *\n * @example\n * ```ts\n * import { post } from 'itd-api';\n *\n * await itd.posts.create(\n * post('смотрите что нашёл')\n * .attach({ url: 'https://example.com/photo.jpg' })\n * .poll((q) => q.question('нравится?').options('да', 'нет')),\n * );\n * ```\n */\nexport function post(content?: string): PostBuilder {\n return new PostBuilder({\n content: content ?? '',\n contentSet: content !== undefined,\n attachmentIds: [],\n files: [],\n });\n}\n\n/** Что принимает параметр поста: объект, билдер или функция-настройщик. */\nexport type PostInput = BuilderInput<CreatePostInput, PostBuilder>;\n\n/** Приводит любую форму входа к готовым данным поста. */\nexport function resolvePost(input: PostInput): CreatePostData {\n const resolved = typeof input === 'function' ? input(post()) : input;\n return isBuilder<CreatePostData>(resolved)\n ? validatePost(resolved.build())\n : validatePost(resolved);\n}\n\n/** Что принимает `posts.update`: объект, билдер поста или функция-настройщик. */\nexport type PostUpdateInput =\n | UpdatePostInput\n | PostBuilder\n | ((builder: PostBuilder) => PostBuilder | UpdatePostInput);\n\nfunction validatePostUpdate(input: CreatePostInput | UpdatePostInput): UpdatePostInput {\n if (!input || typeof input !== 'object') {\n throw new ItdConfigError('Для обновления поста нужен объект с явно заданным content');\n }\n\n const unsupported = ['wallRecipientId', 'attachmentIds', 'files', 'poll'].filter(\n (key) => (input as CreatePostInput)[key as keyof CreatePostInput] !== undefined,\n );\n if (unsupported.length > 0) {\n throw new ItdConfigError(\n `posts.update изменяет только content и spans; не поддерживаются: ${unsupported.join(', ')}`,\n );\n }\n\n if (input.content === undefined) {\n throw new ItdConfigError(\n 'posts.update требует явно заданный content; обновление одних spans могло бы стереть текст поста',\n );\n }\n if (typeof input.content !== 'string') {\n throw new ItdConfigError('content обновляемого поста должен быть строкой');\n }\n if (input.spans !== undefined && !Array.isArray(input.spans)) {\n throw new ItdConfigError('spans обновляемого поста должен быть массивом');\n }\n\n return {\n content: input.content,\n ...(input.spans !== undefined ? { spans: validateSpans(input.content, input.spans) } : {}),\n };\n}\n\n/** Приводит вход `posts.update` к поддерживаемым данным поста. */\nexport function resolvePostUpdate(input: PostUpdateInput): UpdatePostInput {\n if (typeof input === 'function') {\n const result = input(post());\n return isBuilder<CreatePostInput>(result)\n ? (result as PostBuilder)[BUILD_UPDATE]()\n : validatePostUpdate(result);\n }\n\n return isBuilder<CreatePostInput>(input)\n ? (input as PostBuilder)[BUILD_UPDATE]()\n : validatePostUpdate(input);\n}\n","import { type CommentInput, resolveComment } from '../builders/comment.js';\nimport {\n type PostInput,\n type PostUpdateInput,\n resolvePost,\n resolvePostUpdate,\n} from '../builders/post.js';\nimport type { FileInput } from '../core/attachments/contracts.js';\nimport type { HttpClient } from '../core/http.js';\nimport {\n type Page,\n PaginationMode,\n type Paginator,\n readCursorPage,\n readFlatCursorPage,\n} from '../core/pagination.js';\nimport { pickArray } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { UserRef } from '../models/common.js';\nimport type {\n Comment,\n LikeResult,\n PinPostResult,\n Poll,\n Post,\n PostStats,\n} from '../models/content.js';\nimport type { CommentSort, FeedTab } from '../types/enums.js';\nimport type { PaginationOptions, RequestOptions } from '../types/options.js';\nimport type { CreateCommentInput, CreatePostData } from '../types/params.js';\nimport { BaseResource } from './base.js';\n\n/** Курсорная позиция из параметров: если курсор задан — с него, иначе с начала. */\nfunction cursorStart(params: { cursor?: string | undefined }): { cursor?: string } {\n return params.cursor ? { cursor: params.cursor } : {};\n}\n\n/** Параметры запроса ленты. */\nexport interface FeedParams {\n /** Вкладка ленты. По умолчанию сервер отдаёт популярное. */\n tab?: FeedTab;\n /** Сколько постов на страницу. */\n limit?: number;\n /**\n * Курсор следующей страницы из предыдущего ответа.\n *\n * Передавайте значение как есть: его формат зависит от вкладки и может измениться.\n */\n cursor?: string;\n}\n\n/** Параметры запроса постов пользователя. */\nexport interface UserPostsParams {\n limit?: number;\n cursor?: string;\n /** Порядок сортировки. */\n sort?: string;\n /** Закреплённый пост, чтобы сервер поднял его наверх. */\n pinnedPostId?: string;\n}\n\n/** Параметры запроса комментариев к посту. */\nexport interface CommentsParams {\n limit?: number;\n /**\n * Курсор следующей страницы: идентификатор последнего полученного комментария.\n *\n * Передавайте значение из `nextCursor` предыдущего ответа как есть.\n */\n cursor?: string;\n sort?: CommentSort;\n}\n\n/**\n * Посты: лента, публикация, реакции, репосты, комментарии.\n *\n * Доступна как `itd.posts`.\n */\nexport class PostsResource extends BaseResource {\n readonly #uploadFiles: (files: FileInput[], options?: RequestOptions) => Promise<string[]>;\n\n /** Лента: `/api/posts`, курсорная пагинация. */\n readonly #feed = this.paginated<Post, FeedParams>({\n operationId: 'posts.list',\n path: () => '/api/posts',\n query: (p) => ({ tab: p.tab, limit: p.limit }),\n start: cursorStart,\n read: (body) => readCursorPage<Post>(body, 'posts'),\n mode: PaginationMode.Cursor,\n });\n\n /** Стена пользователя: `/api/posts/user/{user}`. */\n readonly #wall = this.paginated<Post, UserPostsParams & { user: UserRef }>({\n operationId: 'posts.byUser',\n path: (p) => `/api/posts/user/${encodePathSegment(p.user, 'user')}`,\n query: (p) => ({ limit: p.limit, sort: p.sort, pinnedPostId: p.pinnedPostId }),\n start: cursorStart,\n read: (body) => readCursorPage<Post>(body, 'posts'),\n mode: PaginationMode.Cursor,\n });\n\n /** Понравившиеся посты пользователя: `/api/posts/user/{user}/liked`. */\n readonly #liked = this.paginated<Post, UserPostsParams & { user: UserRef }>({\n operationId: 'posts.likedByUser',\n path: (p) => `/api/posts/user/${encodePathSegment(p.user, 'user')}/liked`,\n query: (p) => ({ limit: p.limit }),\n start: cursorStart,\n read: (body) => readCursorPage<Post>(body, 'posts'),\n mode: PaginationMode.Cursor,\n });\n\n /** Комментарии к посту: курсор лежит рядом со списком, поэтому свой reader. */\n readonly #comments = this.paginated<Comment, CommentsParams & { postId: string }>({\n operationId: 'posts.comments',\n path: (p) => `/api/posts/${encodePathSegment(p.postId, 'postId')}/comments`,\n query: (p) => ({ limit: p.limit, sort: p.sort }),\n start: cursorStart,\n read: (body) => readFlatCursorPage<Comment>(body, 'comments'),\n mode: PaginationMode.Cursor,\n });\n\n constructor(\n http: HttpClient,\n deps: { uploadFiles: (files: FileInput[], options?: RequestOptions) => Promise<string[]> },\n ) {\n super(http);\n this.#uploadFiles = deps.uploadFiles;\n }\n\n /**\n * Загружает страницу ленты.\n *\n * @example\n * ```ts\n * const page = await itd.posts.list({ tab: FeedTab.Following, limit: 20 });\n * const next = await itd.posts.list({ tab: FeedTab.Following, cursor: page.nextCursor ?? undefined });\n * ```\n */\n list(params: FeedParams = {}, options: RequestOptions = {}): Promise<Page<Post>> {\n return this.#feed.list(params, options);\n }\n\n /**\n * Перебирает ленту, сама подставляя курсоры.\n *\n * @example\n * ```ts\n * for await (const post of itd.posts.iterate({ tab: 'following' })) {\n * console.log(post.author.username, post.content);\n * }\n * ```\n */\n iterate(params: FeedParams = {}, options: PaginationOptions = {}): Paginator<Post> {\n return this.#feed.iterate(params, options);\n }\n\n /**\n * Публикует пост.\n *\n * Принимает обычный объект, {@link PostBuilder} или функцию-настройщик. Файлы из поля\n * `files` загружаются автоматически, порядок вложений сохраняется.\n *\n * @example\n * ```ts\n * await itd.posts.create({ content: 'привет' });\n * await itd.posts.create((p) => p.content('привет').attach({ url: 'https://example.com/photo.jpg' }));\n * ```\n */\n async create(input: PostInput, options: RequestOptions = {}): Promise<Post> {\n const data = resolvePost(input);\n const attachmentIds = await this.#collectAttachments(data, options);\n\n return this.http.operation<Post>('posts.create', {\n path: '/api/posts',\n body: {\n content: data.content ?? '',\n ...(data.spans ? { spans: data.spans } : {}),\n ...(data.wallRecipientId ? { wallRecipientId: data.wallRecipientId } : {}),\n ...(attachmentIds.length > 0 ? { attachmentIds } : {}),\n ...(data.poll ? { poll: data.poll } : {}),\n },\n ...options,\n });\n }\n\n /**\n * Загружает один пост вместе с топовыми комментариями.\n *\n * В отличие от списков, здесь у поста заполнено поле `comments`.\n */\n get(postId: string, options: RequestOptions = {}): Promise<Post> {\n return this.http.operation<Post>('posts.get', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}`,\n ...options,\n });\n }\n\n /**\n * Редактирует текст и разметку поста.\n *\n * Как и {@link create}, принимает объект, готовый {@link PostBuilder} или\n * функцию-настройщик. Поля создания поста, которые update endpoint не поддерживает\n * (вложения, опрос и стена), отвергаются до запроса.\n */\n update(postId: string, input: PostUpdateInput, options: RequestOptions = {}): Promise<Post> {\n const data = resolvePostUpdate(input);\n return this.http.operation<Post>('posts.update', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}`,\n body: { content: data.content, ...(data.spans ? { spans: data.spans } : {}) },\n ...options,\n });\n }\n\n /** Удаляет пост. Восстановить его можно через {@link restore}. */\n remove(postId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('posts.remove', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}`,\n ...options,\n });\n }\n\n /** Восстанавливает удалённый пост. */\n restore(postId: string, options: RequestOptions = {}): Promise<Post> {\n return this.http.operation<Post>('posts.restore', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/restore`,\n ...options,\n });\n }\n\n /** Ставит реакцию на пост. */\n like(postId: string, options: RequestOptions = {}): Promise<LikeResult> {\n return this.http.operation<LikeResult>('posts.like', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/like`,\n ...options,\n });\n }\n\n /** Убирает реакцию с поста. */\n unlike(postId: string, options: RequestOptions = {}): Promise<LikeResult> {\n return this.http.operation<LikeResult>('posts.unlike', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/like`,\n ...options,\n });\n }\n\n /**\n * Делает репост с необязательным комментарием.\n *\n * Вложения к репосту не поддерживаются: сервер их игнорирует, поэтому параметров\n * для файлов здесь нет.\n */\n repost(postId: string, content = '', options: RequestOptions = {}): Promise<Post> {\n return this.http.operation<Post>('posts.repost', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/repost`,\n body: { content },\n ...options,\n });\n }\n\n /** Отменяет репост. */\n unrepost(postId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('posts.unrepost', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/repost`,\n ...options,\n });\n }\n\n /** Закрепляет пост в профиле. */\n pin(postId: string, options: RequestOptions = {}): Promise<PinPostResult> {\n return this.http.operation<PinPostResult>('posts.pin', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/pin`,\n ...options,\n });\n }\n\n /** Открепляет пост. */\n unpin(postId: string, options: RequestOptions = {}): Promise<PinPostResult> {\n return this.http.operation<PinPostResult>('posts.unpin', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/pin`,\n ...options,\n });\n }\n\n /**\n * Голосует в опросе.\n *\n * @param optionIds выбранные варианты; несколько допустимы только при `multipleChoice`\n */\n vote(postId: string, optionIds: string[], options: RequestOptions = {}): Promise<Poll> {\n return this.http.operation<Poll>('posts.vote', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/poll/vote`,\n body: { optionIds },\n ...options,\n });\n }\n\n /** Запрашивает счётчики сразу для нескольких постов. */\n async stats(ids: string[], options: RequestOptions = {}): Promise<PostStats[]> {\n const body = await this.http.operation('posts.stats', {\n path: '/api/posts/stats',\n body: { ids },\n ...options,\n });\n\n return pickArray<PostStats>(body, 'posts');\n }\n\n /**\n * Загружает страницу стены пользователя.\n *\n * Это **не только его собственные посты**: сюда попадают и записи, которые другие\n * оставили на его стене — у них `author` чужой, а `wallRecipient` указывает на владельца\n * стены. Поэтому число записей обычно больше, чем `postsCount` из профиля; чтобы\n * получить только авторские посты, отфильтруйте по `post.author.id`.\n *\n * Принимает и UUID, и имя пользователя.\n */\n byUser(\n user: UserRef,\n params: UserPostsParams = {},\n options: RequestOptions = {},\n ): Promise<Page<Post>> {\n return this.#wall.list({ ...params, user }, options);\n }\n\n /** Перебирает стену пользователя. Что именно в неё входит — см. {@link byUser}. */\n iterateByUser(\n user: UserRef,\n params: UserPostsParams = {},\n options: PaginationOptions = {},\n ): Paginator<Post> {\n return this.#wall.iterate({ ...params, user }, options);\n }\n\n /** Загружает страницу постов, которые пользователь отметил реакцией. */\n likedByUser(\n user: UserRef,\n params: UserPostsParams = {},\n options: RequestOptions = {},\n ): Promise<Page<Post>> {\n return this.#liked.list({ ...params, user }, options);\n }\n\n /** Перебирает посты, которые пользователь отметил реакцией. */\n iterateLikedByUser(\n user: UserRef,\n params: UserPostsParams = {},\n options: PaginationOptions = {},\n ): Paginator<Post> {\n return this.#liked.iterate({ ...params, user }, options);\n }\n\n /**\n * Загружает страницу комментариев к посту.\n *\n * У этого эндпоинта курсор и признак продолжения лежат рядом со списком, а не внутри\n * объекта `pagination`, как у остальных, — разница скрыта внутри.\n */\n comments(\n postId: string,\n params: CommentsParams = {},\n options: RequestOptions = {},\n ): Promise<Page<Comment>> {\n return this.#comments.list({ ...params, postId }, options);\n }\n\n /** Перебирает комментарии к посту. */\n iterateComments(\n postId: string,\n params: CommentsParams = {},\n options: PaginationOptions = {},\n ): Paginator<Comment> {\n return this.#comments.iterate({ ...params, postId }, options);\n }\n\n /**\n * Комментирует пост.\n *\n * @example\n * ```ts\n * await itd.posts.comment(postId, 'согласен');\n * await itd.posts.comment(postId, (c) => c.content('смотри').attach(blob));\n * ```\n */\n async comment(\n postId: string,\n input: CommentInput | string,\n options: RequestOptions = {},\n ): Promise<Comment> {\n const data = resolveComment(typeof input === 'string' ? { content: input } : input);\n const attachmentIds = await this.#collectAttachments(data, options);\n\n return this.http.operation<Comment>('posts.comment', {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/comments`,\n body: { content: data.content ?? '', attachmentIds },\n ...options,\n });\n }\n\n /**\n * Отправляет голосовой комментарий.\n *\n * Текста у такого комментария нет: сервер ждёт пустой `content` и одно аудиовложение\n * в формате `audio/ogg`.\n *\n * @example\n * ```ts\n * import { fromPath } from 'itd-api/node';\n *\n * await itd.posts.voiceComment(postId, fromPath('./answer.ogg'));\n * ```\n */\n voiceComment(postId: string, audio: FileInput, options: RequestOptions = {}): Promise<Comment> {\n return this.comment(postId, { content: '', files: [audio] }, options);\n }\n\n /** Загружает файлы из входных данных и объединяет их с уже готовыми идентификаторами. */\n async #collectAttachments(\n data: CreatePostData | CreateCommentInput,\n options: RequestOptions,\n ): Promise<string[]> {\n const existing = data.attachmentIds ?? [];\n const files = data.files ?? [];\n\n if (files.length === 0) return existing;\n\n const uploaded = await this.#uploadFiles(files, options);\n return [...existing, ...uploaded];\n }\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport { ReportReason, ReportTargetType } from '../types/enums.js';\nimport type { CreateReportInput } from '../types/params.js';\nimport { BUILDER, type BuilderInput, type ItdBuilder, resolveInput } from './base.js';\n\nconst REASONS = new Set<string>(Object.values(ReportReason));\nconst TARGET_TYPES = new Set<string>(Object.values(ReportTargetType));\n\n/**\n * Проверяет данные жалобы.\n *\n * @throws {ItdConfigError} если объект жалобы или причина заданы неверно\n */\nexport function validateReport(input: CreateReportInput): CreateReportInput {\n if (!input?.targetId || typeof input.targetId !== 'string') {\n throw new ItdConfigError('Жалоба требует идентификатор объекта (targetId)');\n }\n\n if (!TARGET_TYPES.has(input.targetType)) {\n throw new ItdConfigError(\n `targetType должен быть одним из ${[...TARGET_TYPES].join(', ')}, ` +\n `получено: ${String(input.targetType)}`,\n );\n }\n\n if (!REASONS.has(input.reason)) {\n throw new ItdConfigError(\n `Неизвестная причина жалобы «${String(input.reason)}». Допустимые: ${[...REASONS].join(', ')}`,\n );\n }\n\n return input;\n}\n\n/**\n * Билдер жалобы.\n *\n * Точка входа задаёт объект жалобы и его тип одновременно, поэтому рассогласовать\n * `targetType` и `targetId` невозможно. Создаётся объектом {@link report}.\n */\nexport class ReportBuilder implements ItdBuilder<CreateReportInput> {\n /** @internal */\n readonly [BUILDER] = true as const;\n\n readonly #state: Partial<CreateReportInput>;\n\n /** @internal Создавайте билдер через {@link report}. */\n constructor(state: Partial<CreateReportInput>) {\n this.#state = state;\n }\n\n /** Указывает причину жалобы. */\n reason(reason: ReportReason): ReportBuilder {\n return new ReportBuilder({ ...this.#state, reason });\n }\n\n /** Добавляет пояснение в свободной форме. */\n description(text: string): ReportBuilder {\n return new ReportBuilder({ ...this.#state, description: text });\n }\n\n build(): CreateReportInput {\n return validateReport(this.#state as CreateReportInput);\n }\n\n toJSON(): CreateReportInput {\n return this.build();\n }\n}\n\nfunction start(targetType: ReportTargetType, targetId: string): ReportBuilder {\n return new ReportBuilder({ targetType, targetId });\n}\n\n/**\n * Начинает сборку жалобы.\n *\n * Тип объекта выбирается точкой входа, так что указать идентификатор комментария\n * с типом «пост» нельзя в принципе.\n *\n * @example\n * ```ts\n * import { report, ReportReason } from 'itd-api';\n *\n * await itd.reports.create(report.post(postId).reason(ReportReason.Spam));\n * await itd.reports.create(report.user(userId).reason('fraud').description('пишет в личку'));\n * ```\n */\nexport const report = Object.freeze({\n /** Жалоба на пост. */\n post: (postId: string) => start(ReportTargetType.Post, postId),\n /** Жалоба на комментарий. */\n comment: (commentId: string) => start(ReportTargetType.Comment, commentId),\n /** Жалоба на пользователя. */\n user: (userId: string) => start(ReportTargetType.User, userId),\n});\n\n/** Что принимает параметр жалобы: объект, билдер или функция-настройщик. */\nexport type ReportInput = BuilderInput<CreateReportInput, ReportBuilder>;\n\n/** Приводит любую форму входа к готовым данным жалобы. */\nexport function resolveReport(input: ReportInput): CreateReportInput {\n return resolveInput(input, () => new ReportBuilder({}), validateReport);\n}\n","import { type ReportInput, resolveReport } from '../builders/report.js';\nimport type { Report } from '../models/platform.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/**\n * Жалобы на контент и пользователей.\n *\n * Доступна как `itd.reports`.\n */\nexport class ReportsResource extends BaseResource {\n /**\n * Отправляет жалобу.\n *\n * Повторная жалоба на тот же объект отклоняется сервером с сообщением\n * «Вы уже отправляли жалобу на этот контент».\n *\n * @example\n * ```ts\n * await itd.reports.create(report.post(postId).reason('spam'));\n * await itd.reports.create({ targetType: 'user', targetId, reason: 'fraud' });\n * ```\n */\n create(input: ReportInput, options: RequestOptions = {}): Promise<Report> {\n const data = resolveReport(input);\n\n return this.http.operation<Report>('reports.create', {\n path: '/api/reports',\n body: data,\n ...options,\n });\n }\n}\n","import { pickArray } from '../core/unwrap.js';\nimport type { Hashtag } from '../models/content.js';\nimport type { UserSummary } from '../models/users.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Результат глобального поиска. */\nexport interface SearchResult {\n users: UserSummary[];\n hashtags: Hashtag[];\n}\n\n/**\n * Глобальный поиск.\n *\n * Доступна как `itd.search`.\n */\nexport class SearchResource extends BaseResource {\n /**\n * Ищет пользователей и хэштеги одним запросом.\n *\n * @example\n * ```ts\n * const { users, hashtags } = await itd.search.all('арт');\n * ```\n */\n async all(query: string, options: RequestOptions = {}): Promise<SearchResult> {\n const body = await this.http.operation('search.all', {\n path: '/api/search',\n query: { q: query },\n ...options,\n });\n\n return {\n users: pickArray<UserSummary>(body, 'users'),\n hashtags: pickArray<Hashtag>(body, 'hashtags'),\n };\n }\n}\n","import { encodePathSegment } from '../core/url.js';\nimport type { PaymentMethod, Subscription } from '../models/account.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/**\n * Подписка и способы оплаты.\n *\n * Доступна как `itd.subscription`.\n */\nexport class SubscriptionResource extends BaseResource {\n /** Загружает состояние подписки и её цену. */\n status(options: RequestOptions = {}): Promise<Subscription> {\n return this.http.operation<Subscription>('subscription.status', {\n // Завершающий слэш обязателен.\n path: '/api/v1/subscription/',\n ...options,\n });\n }\n\n /**\n * Запускает оплату подписки.\n *\n * Форма ответа в документации API не описана, поэтому тип результата не уточняется.\n */\n pay(options: RequestOptions = {}): Promise<unknown> {\n return this.http.operation('subscription.pay', {\n path: '/api/v1/subscription/pay',\n ...options,\n });\n }\n\n /** Включает или отключает автопродление. */\n setAutoRenewal(enabled: boolean, options: RequestOptions = {}): Promise<unknown> {\n return this.http.operation('subscription.setAutoRenewal', {\n path: '/api/v1/subscription/auto-renewal',\n body: { enabled },\n ...options,\n });\n }\n\n /** Запускает привязку карты. */\n bindCard(options: RequestOptions = {}): Promise<unknown> {\n return this.http.operation('subscription.bindCard', {\n path: '/api/v1/subscription/bind-card',\n ...options,\n });\n }\n\n /** Загружает список способов оплаты. Пустой массив, если карт нет. */\n async methods(options: RequestOptions = {}): Promise<PaymentMethod[]> {\n const body = await this.http.operation('subscription.methods', {\n path: '/api/v1/subscription/methods',\n ...options,\n });\n\n return Array.isArray(body) ? (body as PaymentMethod[]) : [];\n }\n\n /** Делает способ оплаты основным. */\n setDefaultMethod(methodId: string, options: RequestOptions = {}): Promise<unknown> {\n return this.http.operation('subscription.setDefaultMethod', {\n path: `/api/v1/subscription/methods/${encodePathSegment(methodId, 'methodId')}/default`,\n ...options,\n });\n }\n\n /** Удаляет способ оплаты. */\n removeMethod(methodId: string, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('subscription.removeMethod', {\n path: `/api/v1/subscription/methods/${encodePathSegment(methodId, 'methodId')}`,\n ...options,\n });\n }\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { HttpClient } from '../core/http.js';\nimport { createDeviceId } from '../core/runtime.js';\nimport { InteractionType, type ViewReason, type ViewSource } from '../types/enums.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/** Параметры событий телеметрии. */\nexport interface TelemetryOptions {\n /** Переопределяет идентификатор сессии телеметрии (`sid`) для этого запроса. */\n sid?: string;\n}\n\n/** Часы, используемые tracker-ом для измерения времени просмотра. */\nexport interface TelemetryClock {\n /** Возвращает текущее время в миллисекундах. */\n now(): number;\n}\n\n/** Опции tracker-а просмотра. */\nexport interface ViewTrackerOptions extends TelemetryOptions {\n /** Часы для измерения времени. По умолчанию используется `Date.now()`. */\n clock?: TelemetryClock;\n}\n\n/** Опции накопителя телеметрии. */\nexport interface TelemetryBatchOptions extends TelemetryOptions {\n /**\n * Максимальное число событий в одном запросе.\n *\n * Значение по умолчанию — 50. Это размер клиентской пачки, а не заявленный лимит API.\n */\n maxBatchSize?: number;\n /** Часы для tracker-ов, созданных через накопитель. */\n clock?: TelemetryClock;\n}\n\n/** Событие просмотра поста для {@link TelemetryResource.dwell}. */\nexport interface DwellEntry {\n /** Метка показа — поле `vs` объекта поста. */\n vs: string;\n /** Время появления поста в зоне видимости, epoch-мс. */\n enterAt: number;\n /** Время ухода из зоны видимости, epoch-мс. */\n exitAt: number;\n /** Причина завершения просмотра. */\n reason: ViewReason;\n /** Длительность просмотра в мс. По умолчанию `exitAt - enterAt`. */\n durationMs?: number;\n /** Контекст источника показа. */\n sourceContext?: string;\n /** Источник показа. */\n source?: ViewSource;\n /** Пост уже встречался в этой сессии. */\n repeat?: boolean;\n}\n\n/** Событие взаимодействия с контентом для {@link TelemetryResource.interaction}. */\nexport interface InteractionEntry {\n /** Тип взаимодействия. */\n type: InteractionType;\n /** Метка показа — поле `vs` объекта поста. */\n vs: string;\n /** Идентификатор поста. */\n postId: string;\n /** Индекс вложения, начиная с нуля. */\n mediaIndex?: number;\n /** Источник показа. */\n source?: ViewSource;\n /** Просмотренная позиция видео в мс. */\n positionMs?: number;\n /** Длительность видео в мс. */\n durationMs?: number;\n}\n\n/** Данные для {@link TelemetryResource.startView}. */\nexport interface ViewTrackerInput {\n /** Метка показа — поле `vs` объекта поста. */\n vs: string;\n /** Контекст источника показа. */\n sourceContext?: string;\n /** Источник показа. */\n source?: ViewSource;\n /** Пост уже встречался в этой сессии. */\n repeat?: boolean;\n}\n\n/** Данные открытия фотографии. */\nexport interface PhotoOpenInput {\n /** Метка показа — поле `vs` объекта поста. */\n vs: string;\n /** Идентификатор поста. */\n postId: string;\n /** Индекс открытого вложения, начиная с нуля. */\n mediaIndex: number;\n /** Источник показа. */\n source?: ViewSource;\n}\n\n/** Данные о прогрессе просмотра видео. */\nexport interface VideoProgressInput {\n /** Метка показа — поле `vs` объекта поста. */\n vs: string;\n /** Идентификатор поста. */\n postId: string;\n /** Просмотренная позиция видео в мс. */\n positionMs: number;\n /** Длительность видео в мс. */\n durationMs: number;\n /** Источник показа. */\n source?: ViewSource;\n}\n\n/** Активное измерение времени просмотра. */\nexport interface ViewTracker {\n /** Время начала измерения в миллисекундах. */\n readonly enteredAt: number;\n /** Был ли уже вызван {@link finish}. */\n readonly finished: boolean;\n /**\n * Завершает измерение и передаёт событие.\n *\n * Повторный вызов возвращает тот же Promise и не создаёт второе событие.\n */\n finish(reason: ViewReason): Promise<void>;\n}\n\n/** Явно управляемый накопитель событий телеметрии. */\nexport interface TelemetryBatch {\n /** Число ожидающих событий просмотра. */\n readonly pendingDwell: number;\n /** Число ожидающих событий взаимодействия. */\n readonly pendingInteractions: number;\n /** Закрыт ли накопитель. */\n readonly closed: boolean;\n /** Добавляет одно или несколько событий просмотра. */\n dwell(entry: DwellEntry | readonly DwellEntry[]): this;\n /** Добавляет одно или несколько событий взаимодействия. */\n interaction(entry: InteractionEntry | readonly InteractionEntry[]): this;\n /** Добавляет событие открытия фотографии. */\n photoOpen(input: PhotoOpenInput): this;\n /** Добавляет событие прогресса просмотра видео. */\n videoProgress(input: VideoProgressInput): this;\n /** Начинает измерение просмотра, которое после `finish()` попадёт в этот накопитель. */\n startView(input: ViewTrackerInput): ViewTracker;\n /**\n * Отправляет накопленные события.\n *\n * Опции позволяют заменить, например, отменённый `signal` при повторной попытке.\n */\n flush(options?: RequestOptions): Promise<void>;\n /** Отправляет накопленные события и закрывает накопитель. */\n close(): Promise<void>;\n}\n\nconst DEFAULT_BATCH_SIZE = 50;\nconst SYSTEM_CLOCK: TelemetryClock = Object.freeze({ now: () => Date.now() });\n\nfunction assertNonEmpty(value: string, name: string): void {\n if (value.trim().length === 0) throw new ItdConfigError(`${name} должен быть непустой строкой`);\n}\n\nfunction assertFinite(value: number, name: string): void {\n if (!Number.isFinite(value)) throw new ItdConfigError(`${name} должен быть конечным числом`);\n}\n\nfunction assertNonNegative(value: number, name: string): void {\n assertFinite(value, name);\n if (value < 0) throw new ItdConfigError(`${name} должен быть неотрицательным числом`);\n}\n\nfunction validateDwell(entry: DwellEntry): DwellEntry {\n assertNonEmpty(entry.vs, 'vs');\n assertFinite(entry.enterAt, 'enterAt');\n assertFinite(entry.exitAt, 'exitAt');\n if (entry.exitAt < entry.enterAt) {\n throw new ItdConfigError('exitAt не может быть раньше enterAt');\n }\n if (entry.durationMs !== undefined) assertNonNegative(entry.durationMs, 'durationMs');\n return { ...entry };\n}\n\nfunction validateInteraction(entry: InteractionEntry): InteractionEntry {\n assertNonEmpty(entry.vs, 'vs');\n assertNonEmpty(entry.postId, 'postId');\n if (entry.mediaIndex !== undefined) {\n assertNonNegative(entry.mediaIndex, 'mediaIndex');\n if (!Number.isInteger(entry.mediaIndex)) {\n throw new ItdConfigError('mediaIndex должен быть целым числом');\n }\n }\n if (entry.positionMs !== undefined) assertNonNegative(entry.positionMs, 'positionMs');\n if (entry.durationMs !== undefined) assertNonNegative(entry.durationMs, 'durationMs');\n return { ...entry };\n}\n\nfunction photoOpenEntry(input: PhotoOpenInput): InteractionEntry {\n return validateInteraction({ type: InteractionType.PhotoOpen, ...input });\n}\n\nfunction videoProgressEntry(input: VideoProgressInput): InteractionEntry {\n return validateInteraction({ type: InteractionType.VideoProgress, ...input });\n}\n\nclass ViewTrackerImpl implements ViewTracker {\n readonly enteredAt: number;\n readonly #clock: TelemetryClock;\n readonly #input: ViewTrackerInput;\n readonly #submit: (entry: DwellEntry) => Promise<void> | void;\n #result: Promise<void> | undefined;\n\n constructor(\n input: ViewTrackerInput,\n clock: TelemetryClock,\n submit: (entry: DwellEntry) => Promise<void> | void,\n ) {\n assertNonEmpty(input.vs, 'vs');\n this.enteredAt = clock.now();\n assertFinite(this.enteredAt, 'clock.now()');\n this.#input = { ...input };\n this.#clock = clock;\n this.#submit = submit;\n }\n\n get finished(): boolean {\n return this.#result !== undefined;\n }\n\n finish(reason: ViewReason): Promise<void> {\n if (this.#result) return this.#result;\n\n try {\n const exitAt = this.#clock.now();\n assertFinite(exitAt, 'clock.now()');\n if (exitAt < this.enteredAt) {\n throw new ItdConfigError('часы не могут вернуться назад во время измерения просмотра');\n }\n\n this.#result = Promise.resolve(\n this.#submit({\n ...this.#input,\n enterAt: this.enteredAt,\n exitAt,\n reason,\n }),\n );\n } catch (error) {\n this.#result = Promise.reject(error);\n }\n\n return this.#result;\n }\n}\n\ntype SendDwell = (\n entries: readonly DwellEntry[],\n telemetryOptions: TelemetryOptions,\n requestOptions: RequestOptions,\n disposeCleanup: boolean,\n) => Promise<unknown>;\ntype SendInteraction = (\n entries: readonly InteractionEntry[],\n telemetryOptions: TelemetryOptions,\n requestOptions: RequestOptions,\n disposeCleanup: boolean,\n) => Promise<unknown>;\n\nclass TelemetryBatchImpl implements TelemetryBatch {\n readonly #sendDwell: SendDwell;\n readonly #sendInteraction: SendInteraction;\n readonly #telemetryOptions: TelemetryOptions;\n readonly #requestOptions: RequestOptions;\n readonly #clock: TelemetryClock;\n readonly #maxBatchSize: number;\n readonly #onClose: () => void;\n readonly #dwell: DwellEntry[] = [];\n readonly #interactions: InteractionEntry[] = [];\n #state: 'open' | 'closing' | 'closed' = 'open';\n #flushPromise: Promise<void> | undefined;\n #closePromise: Promise<void> | undefined;\n #disposeCleanup = false;\n\n constructor(\n sendDwell: SendDwell,\n sendInteraction: SendInteraction,\n options: TelemetryBatchOptions,\n requestOptions: RequestOptions,\n onClose: () => void,\n ) {\n const {\n maxBatchSize = DEFAULT_BATCH_SIZE,\n clock = SYSTEM_CLOCK,\n ...telemetryOptions\n } = options;\n if (!Number.isInteger(maxBatchSize) || maxBatchSize < 1) {\n throw new ItdConfigError('maxBatchSize должен быть целым числом от 1');\n }\n this.#sendDwell = sendDwell;\n this.#sendInteraction = sendInteraction;\n this.#telemetryOptions = telemetryOptions;\n this.#requestOptions = requestOptions;\n this.#clock = clock;\n this.#maxBatchSize = maxBatchSize;\n this.#onClose = onClose;\n }\n\n get pendingDwell(): number {\n return this.#dwell.length;\n }\n\n get pendingInteractions(): number {\n return this.#interactions.length;\n }\n\n get closed(): boolean {\n return this.#state === 'closed';\n }\n\n dwell(entry: DwellEntry | readonly DwellEntry[]): this {\n this.#assertOpen();\n const entries = Array.isArray(entry) ? entry : [entry];\n this.#dwell.push(...entries.map(validateDwell));\n return this;\n }\n\n interaction(entry: InteractionEntry | readonly InteractionEntry[]): this {\n this.#assertOpen();\n const entries = Array.isArray(entry) ? entry : [entry];\n this.#interactions.push(...entries.map(validateInteraction));\n return this;\n }\n\n photoOpen(input: PhotoOpenInput): this {\n return this.interaction(photoOpenEntry(input));\n }\n\n videoProgress(input: VideoProgressInput): this {\n return this.interaction(videoProgressEntry(input));\n }\n\n startView(input: ViewTrackerInput): ViewTracker {\n this.#assertOpen();\n return new ViewTrackerImpl(input, this.#clock, (entry) => {\n this.dwell(entry);\n });\n }\n\n flush(options: RequestOptions = {}): Promise<void> {\n if (this.#state === 'closed') return Promise.resolve();\n if (this.#flushPromise) return this.#flushPromise;\n\n const running = this.#flushAll({ ...this.#requestOptions, ...options });\n const tracked = running.finally(() => {\n if (this.#flushPromise === tracked) this.#flushPromise = undefined;\n });\n this.#flushPromise = tracked;\n return tracked;\n }\n\n close(): Promise<void> {\n return this.#close(false);\n }\n\n /** Внутренняя финализация после перехода клиента в terminal state. */\n closeForDispose(): Promise<void> {\n this.prepareForDispose();\n return this.#close(true);\n }\n\n /** Переключает ещё не начатые части flush на terminal-cleanup канал. */\n prepareForDispose(): void {\n this.#disposeCleanup = true;\n }\n\n #close(disposeCleanup: boolean): Promise<void> {\n this.#disposeCleanup ||= disposeCleanup;\n if (this.#state === 'closed') return Promise.resolve();\n if (this.#closePromise) return this.#closePromise;\n\n this.#state = 'closing';\n const running = this.flush()\n .then(() => {\n this.#state = 'closed';\n this.#onClose();\n })\n .catch((error: unknown) => {\n this.#state = 'open';\n throw error;\n });\n const tracked = running.finally(() => {\n if (this.#closePromise === tracked) this.#closePromise = undefined;\n });\n this.#closePromise = tracked;\n return tracked;\n }\n\n async #flushAll(options: RequestOptions): Promise<void> {\n while (this.#dwell.length > 0) {\n const chunk = this.#dwell.splice(0, this.#maxBatchSize);\n try {\n await this.#sendDwell(chunk, this.#telemetryOptions, options, this.#disposeCleanup);\n } catch (error) {\n this.#dwell.unshift(...chunk);\n throw error;\n }\n }\n\n while (this.#interactions.length > 0) {\n const chunk = this.#interactions.splice(0, this.#maxBatchSize);\n try {\n await this.#sendInteraction(chunk, this.#telemetryOptions, options, this.#disposeCleanup);\n } catch (error) {\n this.#interactions.unshift(...chunk);\n throw error;\n }\n }\n }\n\n #assertOpen(): void {\n if (this.#state !== 'open') {\n throw new ItdConfigError('накопитель телеметрии уже закрывается или закрыт');\n }\n }\n}\n\nconst TELEMETRY_DISPOSE_CLOSERS = new WeakMap<TelemetryResource, () => Promise<void>>();\nconst TELEMETRY_DISPOSE_PREPARERS = new WeakMap<TelemetryResource, () => void>();\n\n/**\n * Телеметрия просмотров и взаимодействий.\n *\n * Ничего не отправляет автоматически: каждый запрос, tracker или накопитель создаётся\n * явным вызовом пользователя. Доступна как `itd.telemetry`.\n */\nexport class TelemetryResource extends BaseResource {\n #sessionId: string | undefined;\n readonly #batches = new Set<TelemetryBatchImpl>();\n\n constructor(http: HttpClient) {\n super(http);\n // Финализация накопителей нужна только владельцу lifecycle. Замыкания на приватные\n // методы отдают её `ItdClient.dispose()`, не добавляя ресурсу публичных членов.\n TELEMETRY_DISPOSE_PREPARERS.set(this, () => {\n for (const batch of this.#batches) batch.prepareForDispose();\n });\n TELEMETRY_DISPOSE_CLOSERS.set(this, () => this.#close(true));\n }\n\n /** Идентификатор сессии телеметрии, общий для всех событий этого ресурса. */\n get sessionId(): string {\n this.#sessionId ??= createDeviceId();\n return this.#sessionId;\n }\n\n /** Отправляет события просмотра постов (`POST /api/v1/i`). */\n dwell(\n entries: readonly DwellEntry[],\n telemetryOptions: TelemetryOptions = {},\n requestOptions: RequestOptions = {},\n ): Promise<unknown> {\n const validated = entries.map(validateDwell);\n return this.#sendDwell(validated, telemetryOptions, requestOptions, false);\n }\n\n #sendDwell(\n entries: readonly DwellEntry[],\n telemetryOptions: TelemetryOptions,\n requestOptions: RequestOptions,\n disposeCleanup: boolean,\n ): Promise<unknown> {\n const options = {\n path: '/api/v1/i',\n body: {\n sid: telemetryOptions.sid ?? this.sessionId,\n e: entries.map((entry) => ({\n md: entry.durationMs ?? entry.exitAt - entry.enterAt,\n et: entry.enterAt,\n xt: entry.exitAt,\n r: entry.reason,\n v: entry.vs,\n ...(entry.sourceContext !== undefined ? { sc: entry.sourceContext } : {}),\n ...(entry.source !== undefined ? { s: entry.source } : {}),\n ...(entry.repeat ? { b: 1 } : {}),\n })),\n },\n ...requestOptions,\n };\n return disposeCleanup\n ? this.http.cleanupOperation('telemetry.dwell', options)\n : this.http.operation('telemetry.dwell', options);\n }\n\n /** Отправляет события взаимодействия с контентом (`POST /api/v1/x`). */\n interaction(\n entries: readonly InteractionEntry[],\n telemetryOptions: TelemetryOptions = {},\n requestOptions: RequestOptions = {},\n ): Promise<unknown> {\n const validated = entries.map(validateInteraction);\n return this.#sendInteraction(validated, telemetryOptions, requestOptions, false);\n }\n\n #sendInteraction(\n entries: readonly InteractionEntry[],\n telemetryOptions: TelemetryOptions,\n requestOptions: RequestOptions,\n disposeCleanup: boolean,\n ): Promise<unknown> {\n const options = {\n path: '/api/v1/x',\n body: {\n sid: telemetryOptions.sid ?? this.sessionId,\n e: entries.map((entry) => ({\n t: entry.type,\n v: entry.vs,\n ai: entry.postId,\n ...(entry.mediaIndex !== undefined ? { mi: entry.mediaIndex } : {}),\n ...(entry.source !== undefined ? { s: entry.source } : {}),\n ...(entry.positionMs !== undefined ? { pm: Math.round(entry.positionMs) } : {}),\n ...(entry.durationMs !== undefined ? { dm: Math.round(entry.durationMs) } : {}),\n })),\n },\n ...requestOptions,\n };\n return disposeCleanup\n ? this.http.cleanupOperation('telemetry.interaction', options)\n : this.http.operation('telemetry.interaction', options);\n }\n\n /** Начинает измерять время просмотра и отправляет результат после `finish()`. */\n startView(\n input: ViewTrackerInput,\n options: ViewTrackerOptions = {},\n requestOptions: RequestOptions = {},\n ): ViewTracker {\n const { clock = SYSTEM_CLOCK, ...telemetryOptions } = options;\n return new ViewTrackerImpl(input, clock, async (entry) => {\n await this.dwell([entry], telemetryOptions, requestOptions);\n });\n }\n\n /** Отправляет событие открытия фотографии. */\n photoOpen(\n input: PhotoOpenInput,\n telemetryOptions: TelemetryOptions = {},\n requestOptions: RequestOptions = {},\n ): Promise<unknown> {\n return this.interaction([photoOpenEntry(input)], telemetryOptions, requestOptions);\n }\n\n /** Отправляет событие прогресса просмотра видео. */\n videoProgress(\n input: VideoProgressInput,\n telemetryOptions: TelemetryOptions = {},\n requestOptions: RequestOptions = {},\n ): Promise<unknown> {\n return this.interaction([videoProgressEntry(input)], telemetryOptions, requestOptions);\n }\n\n /**\n * Создаёт накопитель с явными `flush()` и `close()`.\n *\n * Создание и добавление записей не выполняют сетевых запросов.\n */\n batch(options: TelemetryBatchOptions = {}, requestOptions: RequestOptions = {}): TelemetryBatch {\n let batch: TelemetryBatchImpl;\n batch = new TelemetryBatchImpl(\n (entries, telemetry, request, cleanup) =>\n this.#sendDwell(entries, telemetry, request, cleanup),\n (entries, telemetry, request, cleanup) =>\n this.#sendInteraction(entries, telemetry, request, cleanup),\n options,\n requestOptions,\n () => {\n this.#batches.delete(batch);\n },\n );\n this.#batches.add(batch);\n return batch;\n }\n\n /** Закрывает все созданные накопители, отправляя оставшиеся записи. */\n async close(): Promise<void> {\n return this.#close(false);\n }\n\n async #close(disposeCleanup: boolean): Promise<void> {\n const results = await Promise.allSettled(\n [...this.#batches].map((batch) => (disposeCleanup ? batch.closeForDispose() : batch.close())),\n );\n const errors = results\n .filter((result): result is PromiseRejectedResult => result.status === 'rejected')\n .map((result) => result.reason);\n if (errors.length > 0) {\n throw new AggregateError(errors, 'Не удалось отправить накопленную телеметрию');\n }\n }\n}\n\n/** Помечает открытые накопители для terminal cleanup до первой асинхронной границы. @internal */\nexport function prepareTelemetryForDispose(resource: TelemetryResource | undefined): void {\n if (resource) TELEMETRY_DISPOSE_PREPARERS.get(resource)?.();\n}\n\n/** Финализирует созданные накопители после перехода клиента в terminal state. @internal */\nexport function closeTelemetryForDispose(resource: TelemetryResource | undefined): Promise<void> {\n return resource\n ? (TELEMETRY_DISPOSE_CLOSERS.get(resource)?.() ?? Promise.resolve())\n : Promise.resolve();\n}\n","import type { FileInput } from '../core/attachments/contracts.js';\nimport type { HttpClient } from '../core/http.js';\nimport type { BuiltInOperationId } from '../core/operations.js';\nimport { type Page, PaginationMode, type Paginator, readPagedPage } from '../core/pagination.js';\nimport { pickArray, pickBoolean, pickString } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { UserId, UserRef } from '../models/common.js';\nimport type { Clan } from '../models/platform.js';\nimport type {\n FollowResult,\n MyProfile,\n PinsResult,\n PrivacySettings,\n PublicProfile,\n UserSummary,\n} from '../models/users.js';\nimport type { PaginationOptions, RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\nimport type { UploadedFile, UploadOptions } from './files.js';\n\n/**\n * Параметры списков пользователей.\n *\n * ⚠️ Списки подписчиков, подписок и заблокированных на сервере **не листаются**:\n * `page` он игнорирует, а `limit` зажимает на 20. Подробности — в {@link UsersResource.followers}.\n */\nexport interface UserListParams {\n /** Сколько записей вернуть. Значения больше 20 сервер молча уменьшает до 20. */\n limit?: number;\n /** Номер страницы. Сервер его игнорирует — оставлен на случай, если пагинацию починят. */\n page?: number;\n}\n\n/** Изменяемые поля своего профиля. */\nexport interface UpdateProfileInput {\n displayName?: string;\n username?: string;\n /** Эмодзи-аватар: символ клана, а не адрес картинки. */\n avatar?: string;\n bio?: string;\n /** Идентификатор загруженного файла баннера. `null` удаляет текущий баннер. */\n bannerId?: string | null;\n}\n\n/** Изменяемые настройки приватности. */\nexport type UpdatePrivacyInput = Partial<PrivacySettings>;\n\n/**\n * Пользователи: профили, подписки, блокировки, приватность.\n *\n * Доступна как `itd.users`.\n */\nexport class UsersResource extends BaseResource {\n readonly #uploadFile: (\n file: FileInput,\n uploadOptions?: UploadOptions,\n requestOptions?: RequestOptions,\n ) => Promise<UploadedFile>;\n\n constructor(\n http: HttpClient,\n deps: {\n uploadFile: (\n file: FileInput,\n uploadOptions?: UploadOptions,\n requestOptions?: RequestOptions,\n ) => Promise<UploadedFile>;\n },\n ) {\n super(http);\n this.#uploadFile = deps.uploadFile;\n }\n\n /**\n * Списки пользователей: подписчики, подписки, заблокированные.\n *\n * Путь приходит в параметрах — так один описатель обслуживает все три эндпоинта. Имена\n * полей перечислены с запасом: списки приходят под `users`, но альтернативное имя ничего\n * не стоит и спасает, если эндпоинт назовёт список по-своему. `page` уходит в запрос, хотя\n * сервер его сейчас не читает (см. {@link followers}): когда починят — заработает само.\n */\n readonly #userList = this.paginated<\n UserSummary,\n UserListParams & { path: string; operationId: BuiltInOperationId }\n >({\n operationId: (p) => p.operationId,\n path: (p) => p.path,\n query: (p) => ({ limit: p.limit }),\n start: (p) => (p.page !== undefined ? { page: p.page } : {}),\n read: (body) => readPagedPage<UserSummary>(body, 'users', 'followers', 'following', 'blocked'),\n mode: PaginationMode.Page,\n });\n\n /** Загружает свой профиль — с подпиской и признаком подтверждённого телефона. */\n me(options: RequestOptions = {}): Promise<MyProfile> {\n return this.http.operation<MyProfile>('users.me', {\n path: '/api/users/me',\n ...options,\n });\n }\n\n /** Обновляет свой профиль. Передавайте только изменяемые поля. */\n updateMe(input: UpdateProfileInput, options: RequestOptions = {}): Promise<MyProfile> {\n return this.http.operation<MyProfile>('users.updateMe', {\n path: '/api/users/me',\n body: input,\n ...options,\n });\n }\n\n /**\n * Загружает изображение и устанавливает его баннером профиля.\n *\n * Для установки используется идентификатор, полученный от `/api/files/upload`.\n * Если файл уже загружен, используйте {@link updateMe}: `{ bannerId: file.id }`.\n *\n * @example\n * ```ts\n * await itd.users.setBanner(file, { filename: 'banner.webp' });\n * ```\n */\n async setBanner(\n file: FileInput,\n uploadOptions: UploadOptions = {},\n requestOptions: RequestOptions = {},\n ): Promise<MyProfile> {\n const uploaded = await this.#uploadFile(file, uploadOptions, requestOptions);\n return this.updateMe({ bannerId: uploaded.id }, requestOptions);\n }\n\n /** Удаляет баннер профиля, устанавливая `bannerId` в `null`. */\n removeBanner(options: RequestOptions = {}): Promise<MyProfile> {\n return this.updateMe({ bannerId: null }, options);\n }\n\n /** Деактивирует аккаунт. Вернуть его можно через {@link restore}. */\n deactivate(options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('users.deactivate', {\n path: '/api/users/me',\n ...options,\n });\n }\n\n /** Восстанавливает деактивированный аккаунт. */\n restore(options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('users.restore', {\n path: '/api/users/me/restore',\n ...options,\n });\n }\n\n /** Создаёт профиль после регистрации. */\n createProfile(\n input: { username: string; displayName: string; avatar?: string },\n options: RequestOptions = {},\n ): Promise<MyProfile> {\n return this.http.operation<MyProfile>('users.createProfile', {\n path: '/api/users/profile',\n body: input,\n ...options,\n });\n }\n\n /**\n * Загружает профиль пользователя.\n *\n * @param user UUID **или** имя пользователя — подходит и то, и другое\n *\n * @example\n * ```ts\n * const profile = await itd.users.get('nowkie');\n * await itd.posts.create({ content: 'привет', wallRecipientId: profile.id });\n * ```\n */\n get(user: UserRef, options: RequestOptions = {}): Promise<PublicProfile> {\n return this.http.operation<PublicProfile>('users.get', {\n path: `/api/users/${encodePathSegment(user, 'user')}`,\n ...options,\n });\n }\n\n /** Проверяет, свободно ли имя пользователя. */\n async checkUsername(username: string, options: RequestOptions = {}): Promise<boolean> {\n const body = await this.http.operation('users.checkUsername', {\n path: '/api/users/check-username',\n query: { username },\n ...options,\n });\n\n return pickBoolean(body, 'available');\n }\n\n /** Ищет пользователей по строке запроса. */\n async search(\n query: string,\n params: { limit?: number } = {},\n options: RequestOptions = {},\n ): Promise<UserSummary[]> {\n const body = await this.http.operation('users.search', {\n path: '/api/users/search',\n query: { q: query, limit: params.limit },\n ...options,\n });\n\n return pickArray<UserSummary>(body, 'users');\n }\n\n /** Загружает рекомендации, на кого подписаться. */\n async whoToFollow(options: RequestOptions = {}): Promise<UserSummary[]> {\n const body = await this.http.operation('users.whoToFollow', {\n path: '/api/users/suggestions/who-to-follow',\n ...options,\n });\n\n return pickArray<UserSummary>(body, 'users');\n }\n\n /** Загружает рейтинг кланов. */\n async topClans(options: RequestOptions = {}): Promise<Clan[]> {\n const body = await this.http.operation('users.topClans', {\n path: '/api/users/stats/top-clans',\n ...options,\n });\n\n return pickArray<Clan>(body, 'clans');\n }\n\n /**\n * Подписывается на пользователя.\n *\n * У закрытого профиля вместо подписки отправляется заявка — это видно по полю `status`.\n */\n follow(user: UserRef, options: RequestOptions = {}): Promise<FollowResult> {\n return this.http.operation<FollowResult>('users.follow', {\n path: `/api/users/${encodePathSegment(user, 'user')}/follow`,\n body: {},\n ...options,\n });\n }\n\n /** Отписывается от пользователя. */\n unfollow(user: UserRef, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('users.unfollow', {\n path: `/api/users/${encodePathSegment(user, 'user')}/follow`,\n ...options,\n });\n }\n\n /**\n * Загружает подписчиков пользователя.\n *\n * ⚠️ **Сервер этот список не листает.** Возвращаются первые 20 записей и только они:\n * параметр `page` игнорируется (любая страница отдаёт те же записи и `pagination.page: 1`),\n * `limit` больше 20 молча уменьшается, а `hasMore` всегда `false`. Последнее честно —\n * получить продолжение нечем.\n *\n * Числу `total` доверять тоже не стоит: оно расходится с `followersCount` из профиля —\n * на проверенных аккаунтах занижено примерно на 1–4%.\n */\n followers(\n user: UserRef,\n params: UserListParams = {},\n options: RequestOptions = {},\n ): Promise<Page<UserSummary>> {\n return this.#userPage(\n 'users.followers',\n `/api/users/${encodePathSegment(user, 'user')}/followers`,\n params,\n options,\n );\n }\n\n /**\n * Перебирает подписчиков.\n *\n * ⚠️ Перебор закончится после первых 20 записей: сервер список не листает —\n * см. {@link followers}. Метод оставлен на случай, если пагинацию починят.\n */\n iterateFollowers(\n user: UserRef,\n params: UserListParams = {},\n options: PaginationOptions = {},\n ): Paginator<UserSummary> {\n return this.#userPaginator(\n 'users.followers',\n `/api/users/${encodePathSegment(user, 'user')}/followers`,\n params,\n options,\n );\n }\n\n /** Загружает подписки пользователя. Ограничения те же, что у {@link followers}. */\n following(\n user: UserRef,\n params: UserListParams = {},\n options: RequestOptions = {},\n ): Promise<Page<UserSummary>> {\n return this.#userPage(\n 'users.following',\n `/api/users/${encodePathSegment(user, 'user')}/following`,\n params,\n options,\n );\n }\n\n /** Перебирает подписки. Закончится после первых 20 записей — см. {@link followers}. */\n iterateFollowing(\n user: UserRef,\n params: UserListParams = {},\n options: PaginationOptions = {},\n ): Paginator<UserSummary> {\n return this.#userPaginator(\n 'users.following',\n `/api/users/${encodePathSegment(user, 'user')}/following`,\n params,\n options,\n );\n }\n\n /**\n * Проверяет, подписаны ли вы, сразу для нескольких пользователей.\n *\n * @returns объект «идентификатор пользователя → подписаны ли вы»\n *\n * @example\n * ```ts\n * const statuses = await itd.users.followStatus([userA, userB]);\n * // { 'b89dee4f-…': true, '35ea3059-…': false }\n * ```\n */\n followStatus(userIds: UserId[], options: RequestOptions = {}): Promise<Record<string, boolean>> {\n return this.http.operation<Record<string, boolean>>('users.followStatus', {\n path: '/api/users/follow-status',\n body: { userIds },\n ...options,\n });\n }\n\n /** Блокирует пользователя. */\n block(user: UserRef, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('users.block', {\n path: `/api/users/${encodePathSegment(user, 'user')}/block`,\n body: {},\n ...options,\n });\n }\n\n /** Снимает блокировку. */\n unblock(user: UserRef, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('users.unblock', {\n path: `/api/users/${encodePathSegment(user, 'user')}/block`,\n ...options,\n });\n }\n\n /** Загружает заблокированных пользователей. Ограничения те же, что у {@link followers}. */\n blocked(params: UserListParams = {}, options: RequestOptions = {}): Promise<Page<UserSummary>> {\n return this.#userPage('users.blocked', '/api/users/me/blocked', params, options);\n }\n\n /** Перебирает заблокированных. Закончится после первых 20 записей — см. {@link followers}. */\n iterateBlocked(\n params: UserListParams = {},\n options: PaginationOptions = {},\n ): Paginator<UserSummary> {\n return this.#userPaginator('users.blocked', '/api/users/me/blocked', params, options);\n }\n\n /** Загружает настройки приватности. */\n getPrivacy(options: RequestOptions = {}): Promise<PrivacySettings> {\n return this.http.operation<PrivacySettings>('users.getPrivacy', {\n path: '/api/users/me/privacy',\n ...options,\n });\n }\n\n /** Обновляет настройки приватности. Передавайте только изменяемые поля. */\n updatePrivacy(input: UpdatePrivacyInput, options: RequestOptions = {}): Promise<PrivacySettings> {\n return this.http.operation<PrivacySettings>('users.updatePrivacy', {\n path: '/api/users/me/privacy',\n body: input,\n ...options,\n });\n }\n\n /**\n * Загружает значки профиля и выбранный из них.\n *\n * `activePin` — строка-идентификатор, а не объект.\n */\n async pins(options: RequestOptions = {}): Promise<PinsResult> {\n const body = await this.http.operation('users.pins', {\n path: '/api/users/me/pins',\n ...options,\n });\n\n return {\n pins: pickArray(body, 'pins'),\n // Сервер отдаёт здесь строку-идентификатор, а не объект значка.\n activePin: pickString(body, 'activePin') ?? null,\n };\n }\n\n /** Выбирает активный значок профиля. */\n setPin(slug: string, options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('users.setPin', {\n path: '/api/users/me/pin',\n body: { slug },\n ...options,\n });\n }\n\n /** Снимает активный значок. */\n removePin(options: RequestOptions = {}): Promise<void> {\n return this.http.operation<void>('users.removePin', {\n path: '/api/users/me/pin',\n ...options,\n });\n }\n\n #userPage(\n operationId: BuiltInOperationId,\n path: string,\n params: UserListParams,\n options: RequestOptions,\n ): Promise<Page<UserSummary>> {\n return this.#userList.list({ ...params, operationId, path }, options);\n }\n\n #userPaginator(\n operationId: BuiltInOperationId,\n path: string,\n params: UserListParams,\n options: PaginationOptions,\n ): Paginator<UserSummary> {\n return this.#userList.iterate({ ...params, operationId, path }, options);\n }\n}\n","import type { VerificationStatus } from '../models/platform.js';\nimport type { RequestOptions } from '../types/options.js';\nimport { BaseResource } from './base.js';\n\n/**\n * Верификация профиля.\n *\n * Доступна как `itd.verification`.\n */\nexport class VerificationResource extends BaseResource {\n /** Загружает статус заявки. Значение `none` означает, что заявка не подавалась. */\n status(options: RequestOptions = {}): Promise<VerificationStatus> {\n return this.http.operation<VerificationStatus>('verification.status', {\n path: '/api/verification/status',\n ...options,\n });\n }\n\n /** Подаёт заявку на верификацию с видео. */\n submit(videoUrl: string, options: RequestOptions = {}): Promise<unknown> {\n return this.http.operation('verification.submit', {\n path: '/api/verification/submit',\n body: { videoUrl },\n ...options,\n });\n }\n}\n","import type { FileInput } from './core/attachments/contracts.js';\nimport type { AuthEvents } from './core/auth.js';\nimport { type ClientRuntime, createClientRuntime } from './core/client-runtime.js';\nimport { createDeadline } from './core/clock.js';\nimport type { Listener, Unsubscribe } from './core/emitter.js';\nimport { ItdStateError } from './core/errors.js';\nimport type { ClientPlugin } from './core/plugins/contracts.js';\nimport type { PluginRegistry } from './core/plugins/registry.js';\nimport type { RequestQueuePool } from './core/rate-limit.js';\nimport type { ServiceDefinition } from './core/services.js';\nimport type { ItdSession } from './core/storage.js';\nimport type { UserId } from './models/common.js';\nimport { ItdRealtime, type RealtimeOptions, setRealtimeConnectGuard } from './realtime/stream.js';\nimport { AuthResource } from './resources/auth.js';\nimport { CommentsResource } from './resources/comments.js';\nimport { FilesResource, type UploadOptions } from './resources/files.js';\nimport { HashtagsResource } from './resources/hashtags.js';\nimport { NotificationsResource } from './resources/notifications.js';\nimport { PlatformResource } from './resources/platform.js';\nimport { PostsResource } from './resources/posts.js';\nimport { ReportsResource } from './resources/reports.js';\nimport { SearchResource } from './resources/search.js';\nimport { SubscriptionResource } from './resources/subscription.js';\nimport {\n closeTelemetryForDispose,\n prepareTelemetryForDispose,\n TelemetryResource,\n} from './resources/telemetry.js';\nimport { UsersResource } from './resources/users.js';\nimport { VerificationResource } from './resources/verification.js';\nimport type { ItdClientOptions, RawRequestOptions, RequestOptions } from './types/options.js';\n\ndeclare global {\n interface SymbolConstructor {\n readonly asyncDispose: unique symbol;\n }\n}\n\nconst CLIENT_PLUGIN_REGISTRIES = new WeakMap<ItdClient, PluginRegistry>();\nconst DISPOSED_CLIENTS = new WeakSet<ItdClient>();\n\nfunction assertClientActive(client: ItdClient, action: string): void {\n if (!DISPOSED_CLIENTS.has(client)) return;\n throw new ItdStateError(\n `ItdClient уже окончательно освобождён через dispose(); нельзя ${action}. Создайте новый клиент`,\n );\n}\n\n/** Проверяет возможность подключить плагин к клиенту без вызова `install()`. @internal */\nexport function assertClientCanUsePlugin(client: ItdClient, plugin: ClientPlugin): void {\n assertClientActive(client, 'подключить плагин');\n CLIENT_PLUGIN_REGISTRIES.get(client)?.assertCanAdd(plugin);\n}\n\n/** Проверяет возможность отключить плагин клиента без изменения реестра. @internal */\nexport function assertClientCanUnusePlugin(client: ItdClient, name: string): void {\n CLIENT_PLUGIN_REGISTRIES.get(client)?.assertCanRemove(name);\n}\n\n/**\n * Скрытые параметры конструктора — не часть публичного API.\n *\n * @internal\n */\ninterface ItdClientInternals {\n /**\n * Готовая очередь запросов — так {@link ItdAccounts} с `rateLimitScope: 'shared'` даёт\n * нескольким клиентам одну на всех. Свою клиент в этом случае не заводит и, что важнее,\n * не гасит при `close()`: чужие ожидающие запросы это отменило бы.\n */\n queues?: RequestQueuePool | undefined;\n}\n\nconst CLIENT_INTERNALS = new WeakMap<ItdClientOptions, ItdClientInternals>();\n\n/** Создаёт клиент с зависимостями внешнего контейнера, не расширяя публичный конструктор. @internal */\nexport function createManagedClient(\n options: ItdClientOptions,\n internals: ItdClientInternals,\n): ItdClient {\n const managedOptions = { ...options };\n CLIENT_INTERNALS.set(managedOptions, internals);\n return new ItdClient(managedOptions);\n}\n\n/**\n * Клиент API итд.com.\n *\n * Методы сгруппированы по разделам: `itd.posts`, `itd.users`, `itd.comments`, `itd.auth`,\n * `itd.files`. Авторизация, обновление токена, повторы и очередь запросов работают сами.\n *\n * @example Готовый токен — для разового вызова\n * ```ts\n * const itd = new ItdClient({ auth: '<accessToken>' });\n * const me = await itd.users.me();\n * ```\n *\n * @example Полноценная сессия для бота\n * ```ts\n * import { ItdClient } from 'itd-api';\n * import { FileTokenStorage } from 'itd-api/node';\n *\n * const itd = new ItdClient({\n * // `auth` не обязателен: когда хранилище уже содержит сессию, токен берётся оттуда,\n * // а истёкший продлевается сам. Здесь он нужен на первый запуск.\n * // Вход по паролю требует токена капчи — см. AuthInput и TURNSTILE_SITE_KEY.\n * auth: { email, password, getTurnstileToken },\n * storage: new FileTokenStorage('./.itd-session.json'),\n * rateLimit: { concurrency: 4, rps: 8 },\n * });\n *\n * for await (const post of itd.posts.iterate({ tab: 'following' })) {\n * if (!post.isLiked) await itd.posts.like(post.id);\n * }\n * ```\n */\nexport class ItdClient {\n readonly #runtime: ClientRuntime;\n /** Порождённые потоки уведомлений — чтобы `close()` мог закрыть их разом. */\n readonly #streams = new Set<ItdRealtime>();\n /** Общий результат терминальной очистки для идемпотентных повторных вызовов. */\n #disposePromise: Promise<void> | undefined;\n\n // Ресурсы создаются при первом обращении: клиенту редко нужны все тринадцать разом,\n // а `close()` не должен поднимать накопитель телеметрии только ради его закрытия.\n #auth: AuthResource | undefined;\n #users: UsersResource | undefined;\n #posts: PostsResource | undefined;\n #comments: CommentsResource | undefined;\n #files: FilesResource | undefined;\n #notifications: NotificationsResource | undefined;\n #hashtags: HashtagsResource | undefined;\n #search: SearchResource | undefined;\n #reports: ReportsResource | undefined;\n #verification: VerificationResource | undefined;\n #subscription: SubscriptionResource | undefined;\n #platform: PlatformResource | undefined;\n #telemetry: TelemetryResource | undefined;\n\n /**\n * Загрузка вложений для ресурсов, которые принимают файлы.\n *\n * Обращается к {@link files} в момент вызова, поэтому ресурс поднимается только тогда,\n * когда файл действительно отправляют.\n */\n readonly #uploadFile = (\n file: FileInput,\n uploadOptions?: UploadOptions,\n requestOptions?: RequestOptions,\n ) => this.files.upload(file, uploadOptions ?? {}, requestOptions ?? {});\n readonly #uploadFiles = (files: FileInput[], requestOptions?: RequestOptions) =>\n this.files.uploadMany(files, {}, requestOptions ?? {});\n\n /** Авторизация, сессии и пароли. */\n get auth(): AuthResource {\n this.#auth ??= new AuthResource(this.#runtime.http, { auth: this.#runtime.auth });\n return this.#auth;\n }\n\n /** Профили, подписки, блокировки, приватность. */\n get users(): UsersResource {\n this.#users ??= new UsersResource(this.#runtime.http, { uploadFile: this.#uploadFile });\n return this.#users;\n }\n\n /** Лента, публикация, реакции, репосты, комментарии к постам. */\n get posts(): PostsResource {\n this.#posts ??= new PostsResource(this.#runtime.http, { uploadFiles: this.#uploadFiles });\n return this.#posts;\n }\n\n /** Ответы на комментарии и действия над ними. */\n get comments(): CommentsResource {\n this.#comments ??= new CommentsResource(this.#runtime.http, { uploadFiles: this.#uploadFiles });\n return this.#comments;\n }\n\n /** Загрузка файлов и медиа. */\n get files(): FilesResource {\n this.#files ??= new FilesResource(this.#runtime.http, { fetch: this.#runtime.config.fetch });\n return this.#files;\n }\n\n /** Уведомления: список, счётчик, отметки о прочтении, настройки. */\n get notifications(): NotificationsResource {\n this.#notifications ??= new NotificationsResource(this.#runtime.http);\n return this.#notifications;\n }\n\n /** Хэштеги и посты по ним. */\n get hashtags(): HashtagsResource {\n this.#hashtags ??= new HashtagsResource(this.#runtime.http);\n return this.#hashtags;\n }\n\n /** Глобальный поиск по пользователям и хэштегам. */\n get search(): SearchResource {\n this.#search ??= new SearchResource(this.#runtime.http);\n return this.#search;\n }\n\n /** Жалобы на контент и пользователей. */\n get reports(): ReportsResource {\n this.#reports ??= new ReportsResource(this.#runtime.http);\n return this.#reports;\n }\n\n /** Верификация профиля. */\n get verification(): VerificationResource {\n this.#verification ??= new VerificationResource(this.#runtime.http);\n return this.#verification;\n }\n\n /** Подписка и способы оплаты. */\n get subscription(): SubscriptionResource {\n this.#subscription ??= new SubscriptionResource(this.#runtime.http);\n return this.#subscription;\n }\n\n /** Сведения о платформе: версии приложений, изменения, анонсы, баннер события. */\n get platform(): PlatformResource {\n this.#platform ??= new PlatformResource(this.#runtime.http);\n return this.#platform;\n }\n\n /** Телеметрия просмотров. */\n get telemetry(): TelemetryResource {\n this.#telemetry ??= new TelemetryResource(this.#runtime.http);\n return this.#telemetry;\n }\n\n constructor(options: ItdClientOptions = {}) {\n const internals = CLIENT_INTERNALS.get(options) ?? {};\n this.#runtime = createClientRuntime(options, {\n queues: internals.queues,\n assertActive: (action) => assertClientActive(this, action),\n onAccountChange: () => this.#disconnectStreams(),\n });\n CLIENT_PLUGIN_REGISTRIES.set(this, this.#runtime.plugins);\n }\n\n /** Базовый URL, к которому обращается клиент. */\n get baseUrl(): string {\n return this.#runtime.config.baseUrl;\n }\n\n /**\n * Выполняет произвольный запрос к API.\n *\n * Запасной путь для случаев, когда нужного метода ещё нет или ответ сервера разошёлся\n * с документацией. Проходит через ту же авторизацию, очередь и обработку ошибок.\n *\n * @example\n * ```ts\n * const raw = await itd.request({ method: 'GET', path: '/api/posts', raw: true });\n * ```\n *\n * @throws {ItdStateError} если клиент уже освобождён через {@link dispose}\n */\n request<T = unknown>(options: RawRequestOptions): Promise<T> {\n return this.#runtime.http.request<T>({\n ...options,\n operationId: options.operationId ?? 'raw',\n });\n }\n\n /**\n * Подключает плагин.\n *\n * Плагин может независимо регистрировать transformer логической операции и interceptor\n * транспортной попытки. Оба контракта охватывают все методы клиента. Подключать плагин\n * можно в любой момент, но обычно это делают сразу после создания клиента.\n *\n * @throws {ItdConfigError} если плагин задан неверно или уже подключён\n * @throws {ItdStateError} если клиент уже освобождён через {@link dispose}\n *\n * @example\n * ```ts\n * import { crypt } from '@itd-api/crypto';\n *\n * itd.use(crypt());\n * await itd.posts.create({\n * content: 'секрет',\n * }, {\n * extensions: { crypto: { encrypt: 'invisible' } },\n * });\n * ```\n */\n use(plugin: ClientPlugin): this {\n assertClientActive(this, 'подключить плагин');\n this.#runtime.plugins.add(plugin, {\n baseUrl: this.#runtime.config.baseUrl,\n logger: this.#runtime.config.logger,\n getAuthScope: () => this.#runtime.auth.getAuthScope(),\n getAuthIdentity: () => this.#runtime.auth.getAuthIdentity(),\n });\n return this;\n }\n\n /** Имена подключённых плагинов в фактическом порядке выполнения обёрток. */\n pluginNames(): string[] {\n return this.#runtime.plugins.names();\n }\n\n /** Подключён ли плагин с таким именем. */\n hasPlugin(name: string): boolean {\n return this.#runtime.plugins.has(name);\n }\n\n /**\n * Отключает плагин и освобождает заведённые им ресурсы.\n *\n * Новые запросы перестают видеть плагин сразу. Очистка дождётся логического запроса,\n * который уже проходил через его обёртку.\n *\n * @returns `false`, если такого плагина не было\n * @throws {ItdConfigError} если от плагина зависит другой подключённый плагин\n */\n unuse(name: string): Promise<boolean> {\n return this.#runtime.plugins.remove(name);\n }\n\n /**\n * Регистрирует сервис платформы — домен, отличный от основного.\n *\n * Запросы с `{ service: 'имя' }` уходят на его хост с его заголовками. То же самое умеет\n * опция `services` конструктора. Занятое имя не переопределяется — ни своё, ни встроенное:\n * разовому запросу хост задаётся полем `baseUrl`.\n *\n * Заголовок авторизации по умолчанию уходит только своим — домену клиента и его\n * поддоменам. Стороннему хосту токен нужно разрешить явно: `auth: true`.\n *\n * @throws {ItdConfigError} если определение неверно или имя уже занято\n * @throws {ItdStateError} если клиент уже освобождён через {@link dispose}\n *\n * @example Сервис платформы на поддомене — токен уходит сам\n * ```ts\n * itd.defineService({\n * name: 'pb',\n * baseUrl: 'https://pbapi.xn--d1ah4a.com',\n * headers: { Referer: 'https://pixel.xn--d1ah4a.com/' },\n * });\n *\n * await itd.request({ method: 'GET', service: 'pb', path: '/api/pixel-info' });\n * ```\n */\n defineService(definition: ServiceDefinition): this {\n assertClientActive(this, 'зарегистрировать сервис');\n this.#runtime.services.define(definition);\n return this;\n }\n\n /**\n * Базовый URL зарегистрированного сервиса.\n *\n * @throws {ItdConfigError} если сервис не зарегистрирован\n */\n serviceBaseUrl(name: string): string {\n return this.#runtime.services.resolveBaseUrl(name);\n }\n\n /**\n * Подписывается на события авторизации.\n *\n * Полезно, чтобы сохранять сессию во внешнее хранилище или узнавать, что вход\n * окончательно потерян.\n *\n * @returns функция отписки\n *\n * @example\n * ```ts\n * itd.on('tokens', ({ accessToken }) => cache.set('itd', accessToken));\n * itd.on('authError', () => notifyUser('Сессия истекла, войдите заново'));\n * ```\n */\n on<K extends keyof AuthEvents>(event: K, listener: Listener<AuthEvents[K]>): Unsubscribe {\n assertClientActive(this, 'подписаться на события');\n return this.#runtime.auth.on(event, listener);\n }\n\n /**\n * Создаёт поток уведомлений в реальном времени.\n *\n * Каждый вызов даёт новый независимый поток; обычно он нужен один на приложение.\n * Соединение поднимается методом `connect()` и держится само. Замена авторизации на токен\n * другого пользователя завершает все потоки клиента; смена только сессии их не затрагивает.\n *\n * @example\n * ```ts\n * import { NotificationType } from 'itd-api';\n *\n * const stream = itd.realtime();\n *\n * stream.onNotification(NotificationType.PostComment, async ({ update }) => {\n * await handleComment(update.data.notification);\n * });\n * stream.on('unreadCount', (count) => setBadge(count));\n *\n * await stream.connect();\n * ```\n *\n * @throws {ItdStateError} если клиент уже освобождён через {@link dispose}\n */\n realtime(options: RealtimeOptions = {}): ItdRealtime {\n assertClientActive(this, 'создать realtime-поток');\n let stream!: ItdRealtime;\n stream = new ItdRealtime(\n {\n baseUrl: this.#runtime.config.baseUrl,\n fetch: this.#runtime.config.fetch,\n request: ({ operationId, path, query, signal }) =>\n this.#runtime.http.operation(operationId, { path, query, signal }),\n baseHeaders: (url) => this.#runtime.platformHeaders(url),\n getAuthIdentity: () => this.#runtime.auth.getCurrentAuthIdentity(),\n getAuthScope: () => this.#runtime.auth.getAuthScope(),\n getToken: () => this.#runtime.auth.getAccessToken(),\n refresh: () => this.#runtime.auth.onUnauthorized(),\n fetchUnreadCount: () => this.notifications.count(),\n onConnect: () => this.#streams.add(stream),\n onClose: () => this.#streams.delete(stream),\n logger: this.#runtime.config.logger,\n clock: this.#runtime.config.clock,\n },\n options,\n );\n setRealtimeConnectGuard(stream, () => assertClientActive(this, 'подключить realtime-поток'));\n\n // Регистрируем для `close()`: поток держит открытое соединение и таймер переподключения.\n this.#streams.add(stream);\n return stream;\n }\n\n /**\n * Освобождает ресурсы клиента: закрывает все потоки уведомлений, отправляет открытые\n * накопители {@link telemetry}, затем останавливает очередь запросов.\n *\n * Метод дожидается активных обработчиков потока, но не дольше `shutdownTimeout`. После\n * вызова клиентом можно пользоваться снова; ранее созданный поток можно запустить\n * повторным `connect()`.\n *\n * Общая очередь, полученная от {@link ItdAccounts}, не останавливается: её гасит сам\n * контейнер, когда закрывает все аккаунты разом.\n *\n * Терминальное освобождение — это {@link dispose}.\n *\n * @throws {ItdStateError} если обработчики потока не завершились за отведённый срок\n */\n async close(): Promise<void> {\n return this.#close(false);\n }\n\n async #close(disposeCleanup: boolean): Promise<void> {\n const streams = this.#disconnectStreams();\n // Terminal cleanup должен пометить batch до первого await: параллельный обычный close()\n // мог уже начать drain потоков, но ещё не дойти до отправки телеметрии.\n if (disposeCleanup) prepareTelemetryForDispose(this.#telemetry);\n const { shutdownTimeout, clock } = this.#runtime.config;\n const deadline = createDeadline(shutdownTimeout, clock);\n let stuck: ItdRealtime[] = [];\n try {\n const waited = await Promise.all(\n streams.map(async (stream) => ((await deadline.wait(stream.drain())) ? undefined : stream)),\n );\n stuck = waited.filter((stream): stream is ItdRealtime => stream !== undefined);\n\n // Через геттер закрытие клиента поднимало бы накопитель телеметрии только ради\n // того, чтобы его тут же закрыть.\n if (disposeCleanup) await closeTelemetryForDispose(this.#telemetry);\n else await this.#telemetry?.close();\n } finally {\n deadline.cancel();\n this.#runtime.close();\n }\n\n // Об истёкшем сроке сообщаем после отправки телеметрии.\n if (stuck.length > 0) {\n throw new ItdStateError(\n `обработчики потоков (${stuck.map((stream) => stream.transport).join(', ')}) ` +\n `не завершились за ${shutdownTimeout} мс; ожидание прекращено`,\n );\n }\n }\n\n /**\n * Окончательно освобождает клиент: выполняет {@link close}, отменяет незавершённые\n * запросы и отключает все плагины.\n *\n * Терминальное состояние устанавливается сразу при первом вызове. После этого новые\n * запросы, подключение плагинов, регистрация сервисов и создание или повторный запуск\n * realtime-потоков завершаются с {@link ItdStateError}. Повторные вызовы возвращают\n * тот же результат очистки.\n *\n * Ожидание обработчиков потока и операций плагинов ограничено `shutdownTimeout`.\n *\n * @example\n * ```ts\n * await using itd = new ItdClient({ auth: token });\n * // …работа…\n * // dispose() вызовется сам на выходе из блока\n * ```\n */\n dispose(): Promise<void> {\n if (this.#disposePromise) return this.#disposePromise;\n DISPOSED_CLIENTS.add(this);\n this.#disposePromise = this.#dispose();\n return this.#disposePromise;\n }\n\n async #dispose(): Promise<void> {\n const errors: unknown[] = [];\n try {\n // Сначала завершаем потоки и телеметрию через ещё установленный plugin pipeline.\n await this.#close(true);\n } catch (error) {\n errors.push(error);\n }\n try {\n await this.#runtime.dispose();\n } catch (error) {\n errors.push(error);\n }\n if (errors.length > 0) throw new AggregateError(errors, 'Не удалось освободить клиент');\n }\n\n /** Завершает потоки до того, как запросы начнут использовать другой аккаунт. */\n #disconnectStreams(): ItdRealtime[] {\n const streams = [...this.#streams];\n for (const stream of streams) stream.disconnect();\n this.#streams.clear();\n return streams;\n }\n\n /** Позволяет использовать клиент с `await using`. */\n [Symbol.asyncDispose](): Promise<void> {\n return this.dispose();\n }\n\n // Fallback для `await using` в Node 18, где `Symbol.asyncDispose` отсутствует.\n static {\n if (\n typeof (Symbol as SymbolConstructor & { asyncDispose?: symbol }).asyncDispose !== 'symbol'\n ) {\n const prototype = ItdClient.prototype as unknown as Record<PropertyKey, unknown>;\n prototype[Symbol.for('Symbol.asyncDispose')] = prototype.undefined;\n delete prototype.undefined;\n }\n }\n\n /** Текущая сессия целиком — чтобы сохранить её самостоятельно. */\n getSession(): Promise<ItdSession | null> {\n return this.#runtime.auth.getSession();\n }\n\n /**\n * Идентификатор аккаунта, под которым работает клиент.\n *\n * Читается из токена доступа и не стоит ни одного запроса. `undefined`, когда сессии\n * ещё нет или токен выдан не в формате JWT. Полезен прежде всего с {@link ItdAccounts}:\n * показывает, какому профилю соответствует восстановленная из хранилища запись.\n * Свежий профиль целиком отдаёт `itd.users.me()`.\n *\n * @remarks\n * JWT только декодируется: его подпись не проверяется. Результат подходит для локального\n * разделения состояния, но не доказывает личность пользователя и не заменяет проверку\n * авторизации сервером.\n *\n * @example\n * ```ts\n * for (const [name, itd] of accounts) console.log(name, await itd.getUserId());\n * ```\n */\n getUserId(): Promise<UserId | undefined> {\n return this.#runtime.auth.getUserId();\n }\n\n /**\n * Восстанавливает сохранённую сессию, включая cookie.\n *\n * @throws {ItdStateError} если клиент уже освобождён через {@link dispose}\n */\n setSession(session: ItdSession): Promise<void> {\n try {\n assertClientActive(this, 'изменить сессию');\n } catch (error) {\n return Promise.reject(error);\n }\n return this.#runtime.auth.setSession(session);\n }\n}\n\n/**\n * Создаёт клиент API итд.com.\n *\n * То же, что `new ItdClient(options)`, — для тех, кому привычнее фабрика.\n *\n * @example\n * ```ts\n * const itd = createClient({ auth: token });\n * ```\n */\nexport function createClient(options: ItdClientOptions = {}): ItdClient {\n return new ItdClient(options);\n}\n","import {\n assertClientCanUnusePlugin,\n assertClientCanUsePlugin,\n createManagedClient,\n type ItdClient,\n} from './client.js';\nimport { systemClock } from './core/clock.js';\nimport { resolveRateLimit } from './core/config.js';\nimport { Emitter, type Listener, reportListenerError, type Unsubscribe } from './core/emitter.js';\nimport { ItdConfigError, ItdStateError } from './core/errors.js';\nimport {\n type ControlledTokenStorage,\n controlledTokenStorage,\n isRestorableSession,\n MemoryMultiTokenStorage,\n type MultiTokenStorage,\n} from './core/multi-storage.js';\nimport type { ClientPlugin } from './core/plugins/contracts.js';\nimport { assertPluginRemovable, orderPluginDefinitions } from './core/plugins/order.js';\nimport { RequestQueuePool } from './core/rate-limit.js';\nimport type { TokenStorage } from './core/storage.js';\nimport type { ItdClientOptions, Logger } from './types/options.js';\n\n/** Как аккаунты делят между собой очередь запросов. */\nexport type RateLimitScope = 'account' | 'shared';\n\n/**\n * Опции конструктора {@link ItdAccounts}.\n *\n * Всё, что понимает `ItdClient`, кроме `auth` и `deviceId`: они у каждого аккаунта свои\n * и задаются в {@link ItdAccounts.addAccount}. Обычный `TokenStorage` клиента здесь заменён\n * общей опцией {@link ItdAccountsOptions.storage} типа {@link MultiTokenStorage}; контейнер\n * сам выдаёт каждому клиенту изолированный срез по имени. Общий `deviceId` особенно вреден —\n * сервер различает по нему записи в списке сессий, и один на всех сложил бы все аккаунты\n * в одну.\n */\nexport interface ItdAccountsOptions\n extends Omit<ItdClientOptions, 'auth' | 'storage' | 'deviceId'> {\n /** Общее хранилище сессий всех аккаунтов. По умолчанию {@link MemoryMultiTokenStorage}. */\n storage?: MultiTokenStorage | undefined;\n /** Плагины, подключаемые каждому аккаунту, в том числе добавленному позже. */\n plugins?: readonly ClientPlugin[] | undefined;\n /**\n * Как делить очередь запросов. По умолчанию `'account'` — своя у каждого.\n *\n * Лимиты итд.com считаются по аккаунту, а при работе через разные прокси общая очередь\n * только мешает. Она нужна в другом случае: когда все аккаунты сидят на одном IP\n * и упираются в ограничение по адресу, — тогда `'shared'` разводит их запросы во времени\n * все разом, а не поаккаунтно.\n *\n * Настройки самой очереди берутся из общей опции `rateLimit`. Личный объект `rateLimit`\n * в этом режиме запрещён, потому что не может изменить уже созданную очередь;\n * `rateLimit: false` у отдельного аккаунта выводит его из неё.\n */\n rateLimitScope?: RateLimitScope | undefined;\n}\n\n/**\n * Настройки одного аккаунта. Общее мультихранилище задаёт контейнер, а аккаунт получает\n * свой срез автоматически; остальное — как у `ItdClient`.\n *\n * При `rateLimitScope: 'shared'` объект `rateLimit` задаётся только контейнеру; аккаунту\n * разрешено передать `false`, чтобы не ставить его запросы в общую очередь.\n */\nexport type AddAccountOptions = Omit<ItdClientOptions, 'storage'>;\n\n/** Что можно уточнить при удалении аккаунта. */\nexport interface RemoveAccountOptions {\n /**\n * Удалить и сохранённую сессию. По умолчанию `false` — аккаунт убирается только\n * из памяти, а его токены остаются в хранилище и переживут перезапуск.\n */\n forget?: boolean | undefined;\n}\n\n/**\n * События авторизации всех аккаунтов сразу.\n *\n * Те же, что у одиночного клиента, плюс имя аккаунта: подписка на контейнер избавляет\n * от нужды вешать обработчик на каждого.\n */\nexport interface AccountEvents {\n /** Токен получен или обновлён. */\n tokens: { account: string; accessToken: string };\n /** Выполнен вход. */\n signIn: { account: string; accessToken: string };\n /** Сессия очищена — вручную или из-за неудачного обновления. */\n signOut: { account: string };\n /** Обновить сессию не удалось; дальнейшие запросы этого аккаунта будут падать с 401. */\n authError: { account: string; error: unknown };\n}\n\n/** Проверяет имя до создания клиента или частичного восстановления контейнера. */\nfunction validateAccountName(name: string): void {\n if (typeof name !== 'string' || name.trim() === '') {\n throw new ItdConfigError('имя аккаунта должно быть непустой строкой');\n }\n}\n\nconst DISPOSED_ACCOUNTS = new WeakSet<ItdAccounts>();\n\nfunction assertAccountsActive(accounts: ItdAccounts, action: string): void {\n if (!DISPOSED_ACCOUNTS.has(accounts)) return;\n throw new ItdStateError(\n `ItdAccounts уже окончательно освобождён через dispose(); нельзя ${action}. Создайте новый контейнер`,\n );\n}\n\n/**\n * Несколько аккаунтов итд.com в одном месте.\n *\n * Контейнер именованных `ItdClient`: каждый аккаунт получает собственный токен, cookie\n * и `deviceId`, а сессии всех складываются в одно хранилище — обычно в один файл, а не\n * в десяток. Имя аккаунта выбираете вы; сервер о нём ничего не знает.\n *\n * @example Бот на нескольких аккаунтах\n * ```ts\n * import { ItdAccounts } from 'itd-api';\n * import { FileMultiTokenStorage } from 'itd-api/node';\n *\n * await using accounts = new ItdAccounts({\n * storage: new FileMultiTokenStorage('./.itd-sessions.json'),\n * rateLimit: { concurrency: 4 },\n * });\n *\n * // Восстанавливаем тех, кто уже входил раньше: токен возьмётся из хранилища.\n * await accounts.restore();\n *\n * if (!accounts.has('kiow')) {\n * accounts.addAccount('kiow', { auth: { email, password, getTurnstileToken } });\n * }\n *\n * await accounts.account('kiow').posts.create({ content: 'привет' });\n *\n * for (const [name, itd] of accounts) {\n * console.log(name, await itd.getUserId());\n * }\n * ```\n */\nexport class ItdAccounts {\n /** Опции, общие для всех аккаунтов, — без полей, которые задаются поаккаунтно. */\n readonly #base: Omit<ItdClientOptions, 'auth' | 'storage' | 'deviceId'>;\n readonly #storage: MultiTokenStorage;\n readonly #clients = new Map<string, ItdClient>();\n /** Имена, чьё удаление ещё не завершилось: повторно занять их пока нельзя. */\n readonly #removing = new Set<string>();\n /** Уже начатые удаления, которых должен дождаться терминальный dispose(). */\n readonly #accountRemovals = new Set<Promise<void>>();\n /** Управляемые срезы хранилища: после удаления аккаунт больше не может писать через свой. */\n readonly #storageControls = new Map<string, ControlledTokenStorage>();\n /** Подписки на события клиентов — снимаются вместе с аккаунтом. */\n readonly #eventUnsubscribers = new Map<string, Unsubscribe[]>();\n /** Плагины для всех: и для уже заведённых аккаунтов, и для будущих. */\n readonly #plugins: ClientPlugin[];\n /** Имена плагинов, чья асинхронная очистка ещё не завершилась. */\n readonly #removingPlugins = new Set<string>();\n /** Общая очередь. `undefined`, когда у каждого аккаунта своя. */\n readonly #queues: RequestQueuePool | undefined;\n readonly #rateLimitScope: RateLimitScope;\n readonly #emitter: Emitter<AccountEvents>;\n readonly #logger: Logger | undefined;\n /** Общий результат терминальной очистки для идемпотентных повторных вызовов. */\n #disposePromise: Promise<void> | undefined;\n\n constructor(options: ItdAccountsOptions = {}) {\n const { storage, plugins, rateLimitScope, ...base } = options;\n\n if (\n rateLimitScope !== undefined &&\n rateLimitScope !== 'account' &&\n rateLimitScope !== 'shared'\n ) {\n throw new ItdConfigError(\"rateLimitScope должен быть 'account' или 'shared'\");\n }\n\n this.#base = base;\n this.#storage = storage ?? new MemoryMultiTokenStorage();\n this.#plugins = orderPluginDefinitions(plugins ?? []);\n this.#rateLimitScope = rateLimitScope ?? 'account';\n\n // Общая очередь заводится сразу: проверить опции лучше при создании контейнера,\n // а не при добавлении первого аккаунта.\n const rateLimit =\n this.#rateLimitScope === 'shared' ? resolveRateLimit(base.rateLimit) : undefined;\n this.#queues = rateLimit\n ? new RequestQueuePool(rateLimit, base.clock ?? systemClock)\n : undefined;\n\n const logger = typeof base.logger === 'object' ? base.logger : undefined;\n this.#logger = logger;\n this.#emitter = new Emitter<AccountEvents>((error) =>\n reportListenerError(logger, 'аккаунтов', error),\n );\n }\n\n /** Общее хранилище сессий — то же, что передано опцией `storage`. */\n get storage(): MultiTokenStorage {\n return this.#storage;\n }\n\n /** Сколько аккаунтов заведено. */\n get size(): number {\n return this.#clients.size;\n }\n\n /** Имена заведённых аккаунтов в порядке добавления. */\n names(): string[] {\n return [...this.#clients.keys()];\n }\n\n /** Заведён ли аккаунт с таким именем. */\n has(name: string): boolean {\n return this.#clients.has(name);\n }\n\n /**\n * Заводит аккаунт.\n *\n * Возвращается обычный `ItdClient` — со всеми ресурсами, плагинами и `realtime()`.\n * Хранилище ему подставляется само: срез общего по имени аккаунта.\n *\n * Опция `auth` не обязательна: когда сессия этого аккаунта уже лежит в хранилище,\n * токен возьмётся оттуда, а истёкший продлится сам.\n *\n * @throws {ItdConfigError} если имя пустое или уже занято\n *\n * @example\n * ```ts\n * accounts.addAccount('bot', { auth: { email, password, getTurnstileToken } });\n * accounts.addAccount('reader', { auth: '<accessToken>' });\n * accounts.addAccount('через-прокси', { fetch: proxyFetch('socks5://…') });\n * ```\n */\n addAccount(name: string, options: AddAccountOptions = {}): ItdClient {\n assertAccountsActive(this, 'добавить аккаунт');\n validateAccountName(name);\n if (this.#clients.has(name)) {\n throw new ItdConfigError(\n `аккаунт «${name}» уже добавлен. Возьмите его через accounts.account('${name}')`,\n );\n }\n if (this.#removing.has(name)) {\n throw new ItdConfigError(\n `аккаунт «${name}» ещё удаляется. Дождитесь завершения removeAccount() перед повторным добавлением`,\n );\n }\n if (\n this.#rateLimitScope === 'shared' &&\n options.rateLimit !== undefined &&\n options.rateLimit !== false\n ) {\n throw new ItdConfigError(\n \"при rateLimitScope: 'shared' настройки rateLimit задаются контейнеру; \" +\n 'аккаунту можно передать только rateLimit: false',\n );\n }\n\n const storageControl = controlledTokenStorage(this.#storage, name);\n let client: ItdClient | undefined;\n try {\n client = createManagedClient(\n this.#mergeOptions(options, storageControl.storage),\n this.#queues ? { queues: this.#queues } : {},\n );\n\n for (const plugin of this.#plugins) client.use(plugin);\n } catch (error) {\n storageControl.revoke();\n if (client) {\n void client.dispose().catch((cleanupError: unknown) => {\n this.#reportPluginCleanup('неудачного добавления аккаунта', cleanupError);\n });\n }\n throw error;\n }\n\n const unsubscribers = this.#forwardEvents(name, client);\n this.#storageControls.set(name, storageControl);\n this.#eventUnsubscribers.set(name, unsubscribers);\n this.#clients.set(name, client);\n\n return client;\n }\n\n /**\n * Клиент аккаунта.\n *\n * @throws {ItdConfigError} если такого аккаунта нет\n *\n * @example\n * ```ts\n * await accounts.account('kiow').posts.like(postId);\n * ```\n */\n account(name: string): ItdClient {\n assertAccountsActive(this, 'получить клиент аккаунта');\n const client = this.#clients.get(name);\n if (!client) {\n const known = this.names();\n throw new ItdConfigError(\n `аккаунт «${name}» не заведён. ` +\n (known.length > 0\n ? `Известны: ${known.join(', ')}`\n : 'Ни одного аккаунта нет — добавьте его через addAccount() или restore()'),\n );\n }\n\n return client;\n }\n\n /**\n * Поднимает аккаунты, сессии которых уже лежат в хранилище.\n *\n * То, ради чего мультихранилище знает свой состав: после перезапуска процесса\n * ни `auth`, ни капча не нужны — токен, `deviceId` и cookie берутся из сохранённого.\n * Уже заведённые аккаунты не трогаются. Записи, в которых после выхода остался только\n * `deviceId`, пропускаются: авторизованной сессии в них уже нет.\n *\n * @returns имена добавленных аккаунтов\n *\n * @example\n * ```ts\n * const restored = await accounts.restore();\n * console.log(`подняли ${restored.length} аккаунтов без единого входа`);\n * ```\n */\n async restore(): Promise<string[]> {\n assertAccountsActive(this, 'восстановить аккаунты');\n const saved = await this.#storage.accounts();\n for (const name of saved) validateAccountName(name);\n\n const candidates = [...new Set(saved)].filter((name) => !this.#clients.has(name));\n const sessions = await Promise.all(\n candidates.map(async (name) => ({ name, session: await this.#storage.get(name) })),\n );\n const added: string[] = [];\n\n for (const { name, session } of sessions) {\n if (!isRestorableSession(session)) continue;\n // Пока читали хранилище, аккаунт могли добавить вручную или начать удалять.\n if (this.#clients.has(name) || this.#removing.has(name)) continue;\n this.addAccount(name);\n added.push(name);\n }\n\n return added;\n }\n\n /**\n * Убирает аккаунт: закрывает его клиента и, если попросить, забывает сессию.\n *\n * Сетевого запроса не выполняет. Чтобы завершить сессию на сервере, вызовите\n * `itd.auth.logout()` до удаления.\n *\n * @returns `false`, если такого аккаунта и не было\n */\n async removeAccount(name: string, options: RemoveAccountOptions = {}): Promise<boolean> {\n assertAccountsActive(this, 'удалить аккаунт');\n const client = this.#clients.get(name);\n if (!client) return false;\n\n this.#removing.add(name);\n this.#clients.delete(name);\n const storageControl = this.#storageControls.get(name);\n storageControl?.revoke();\n this.#storageControls.delete(name);\n\n for (const unsubscribe of this.#eventUnsubscribers.get(name) ?? []) unsubscribe();\n this.#eventUnsubscribers.delete(name);\n\n const removal = (async () => {\n const errors: unknown[] = [];\n const closing = await Promise.allSettled([\n client.dispose(),\n storageControl?.drain() ?? Promise.resolve(),\n ]);\n for (const result of closing) {\n if (result.status === 'rejected') errors.push(result.reason);\n }\n\n if (options.forget) {\n await Promise.resolve(this.#storage.clear(name)).catch((error: unknown) => {\n errors.push(error);\n });\n }\n\n if (errors.length > 0) throw errors[0];\n })();\n this.#accountRemovals.add(removal);\n\n try {\n await removal;\n return true;\n } finally {\n this.#accountRemovals.delete(removal);\n this.#removing.delete(name);\n }\n }\n\n /**\n * Подключает плагин всем аккаунтам — и заведённым, и будущим.\n *\n * @throws {ItdConfigError} если плагин задан неверно или уже подключён\n *\n * @example\n * ```ts\n * accounts.use(crypt());\n * ```\n */\n use(plugin: ClientPlugin): this {\n assertAccountsActive(this, 'подключить плагин');\n if (this.#removingPlugins.has(plugin?.name)) {\n throw new ItdConfigError(\n `плагин «${plugin.name}» ещё отключается; дождитесь завершения accounts.unuse()`,\n );\n }\n const ordered = orderPluginDefinitions([...this.#plugins, plugin]);\n const clients = [...this.#clients.values()];\n for (const client of clients) assertClientCanUsePlugin(client, plugin);\n\n const installed: ItdClient[] = [];\n try {\n for (const client of clients) {\n client.use(plugin);\n installed.push(client);\n }\n } catch (error) {\n // remove() исключает запись из реестра до первого await, поэтому видимое состояние\n // откатывается синхронно, а асинхронные teardown завершаются в фоне.\n for (const client of installed.reverse()) {\n void client.unuse(plugin.name).catch((cleanupError: unknown) => {\n this.#reportPluginCleanup(`отката плагина «${plugin.name}»`, cleanupError);\n });\n }\n throw error;\n }\n this.#plugins.splice(0, this.#plugins.length, ...ordered);\n\n return this;\n }\n\n /** Имена общих плагинов в фактическом порядке выполнения обёрток. */\n pluginNames(): string[] {\n return this.#plugins.map((plugin) => plugin.name);\n }\n\n /** Подключён ли общий плагин с таким именем. */\n hasPlugin(name: string): boolean {\n return this.#plugins.some((plugin) => plugin.name === name);\n }\n\n /**\n * Отключает общий плагин у существующих клиентов и не применяет его к будущим.\n *\n * @returns `false`, если такого плагина не было\n * @throws {ItdConfigError} если от плагина зависит другой общий плагин\n */\n async unuse(name: string): Promise<boolean> {\n const index = this.#plugins.findIndex((plugin) => plugin.name === name);\n if (index < 0) return false;\n assertPluginRemovable(this.#plugins, name);\n const clients = [...this.#clients.values()];\n for (const client of clients) assertClientCanUnusePlugin(client, name);\n this.#plugins.splice(index, 1);\n this.#removingPlugins.add(name);\n\n try {\n const results = await Promise.allSettled(clients.map((client) => client.unuse(name)));\n const errors = results\n .filter((result): result is PromiseRejectedResult => result.status === 'rejected')\n .map((result) => result.reason);\n if (errors.length > 0) {\n throw new AggregateError(errors, `Не удалось отключить плагин «${name}» у всех аккаунтов`);\n }\n return true;\n } finally {\n this.#removingPlugins.delete(name);\n }\n }\n\n /**\n * Подписывается на события авторизации всех аккаунтов сразу.\n *\n * @returns функция отписки\n *\n * @example\n * ```ts\n * accounts.on('authError', ({ account }) => console.warn(`${account}: сессия потеряна`));\n * ```\n */\n on<K extends keyof AccountEvents>(event: K, listener: Listener<AccountEvents[K]>): Unsubscribe {\n assertAccountsActive(this, 'подписаться на события');\n return this.#emitter.on(event, listener);\n }\n\n /**\n * Перебор аккаунтов парами «имя — клиент».\n *\n * @example\n * ```ts\n * for (const [name, itd] of accounts) {\n * const me = await itd.users.me();\n * console.log(name, me.nickname);\n * }\n * ```\n */\n [Symbol.iterator](): IterableIterator<[string, ItdClient]> {\n return this.#clients.entries();\n }\n\n /**\n * Закрывает все аккаунты и останавливает общую очередь.\n *\n * Аккаунты остаются в контейнере и работоспособны: новые запросы поднимут всё заново,\n * но уже созданные потоки уведомлений останутся закрытыми.\n *\n * @example\n * ```ts\n * await using accounts = new ItdAccounts({ storage });\n * // …работа…\n * // dispose() вызовется сам на выходе из блока\n * ```\n */\n async close(): Promise<void> {\n try {\n await Promise.all([...this.#clients.values()].map((client) => client.close()));\n } finally {\n this.#queues?.stop();\n }\n }\n\n /**\n * Окончательно освобождает контейнер и отключает общие плагины у всех аккаунтов.\n *\n * Для временной остановки потоков и очереди без отключения плагинов используйте\n * {@link close}. Терминальное состояние устанавливается сразу: контейнер отзывает\n * storage-срезы и подписки, а новые аккаунты, запросы его клиентов и плагины после\n * первого `dispose()` больше не допускаются.\n */\n dispose(): Promise<void> {\n if (this.#disposePromise) return this.#disposePromise;\n DISPOSED_ACCOUNTS.add(this);\n this.#disposePromise = this.#dispose();\n return this.#disposePromise;\n }\n\n async #dispose(): Promise<void> {\n const clients = [...this.#clients.values()];\n const controls = [...this.#storageControls.values()];\n const accountRemovals = [...this.#accountRemovals];\n\n for (const control of controls) control.revoke();\n for (const unsubscribers of this.#eventUnsubscribers.values()) {\n for (const unsubscribe of unsubscribers) unsubscribe();\n }\n this.#eventUnsubscribers.clear();\n this.#storageControls.clear();\n this.#clients.clear();\n this.#emitter.removeAllListeners();\n\n const results = await Promise.allSettled([\n ...clients.map((client) => client.dispose()),\n ...controls.map((control) => control.drain()),\n ...accountRemovals,\n Promise.resolve().then(() => this.#queues?.stop()),\n ]);\n this.#plugins.splice(0);\n this.#removingPlugins.clear();\n\n const errors = results\n .filter((result): result is PromiseRejectedResult => result.status === 'rejected')\n .map((result) => result.reason);\n if (errors.length > 0) throw new AggregateError(errors, 'Не удалось освободить аккаунты');\n }\n\n /** Позволяет использовать контейнер с `await using`. */\n [Symbol.asyncDispose](): Promise<void> {\n return this.dispose();\n }\n\n // Fallback для `await using` в Node 18, где `Symbol.asyncDispose` отсутствует, —\n // тот же приём, что в ItdClient.\n static {\n if (\n typeof (Symbol as SymbolConstructor & { asyncDispose?: symbol }).asyncDispose !== 'symbol'\n ) {\n const prototype = ItdAccounts.prototype as unknown as Record<PropertyKey, unknown>;\n prototype[Symbol.for('Symbol.asyncDispose')] = prototype.undefined;\n delete prototype.undefined;\n }\n }\n\n /**\n * Собирает опции клиента: общие, поверх них — аккаунтные, и обязательно свой срез\n * хранилища.\n *\n * `headers` и `services` сливаются по ключам, а не заменяются целиком: иначе один свой\n * заголовок у аккаунта стирал бы весь общий набор.\n */\n #mergeOptions(options: AddAccountOptions, storage: TokenStorage): ItdClientOptions {\n const base = this.#base;\n\n return {\n ...base,\n ...options,\n ...(base.headers || options.headers\n ? { headers: { ...base.headers, ...options.headers } }\n : {}),\n ...(base.services || options.services\n ? { services: { ...base.services, ...options.services } }\n : {}),\n storage,\n };\n }\n\n /** Не теряет ошибку фонового teardown, который синхронный API не может await-нуть. */\n #reportPluginCleanup(scope: string, error: unknown): void {\n const message = `Не удалось завершить teardown после ${scope}`;\n if (this.#logger) this.#logger.error(message, error);\n else console.error(`[itd-api] ${message}`, error);\n }\n\n /** Ретранслирует события клиента наружу, добавляя к ним имя аккаунта. */\n #forwardEvents(account: string, client: ItdClient): Unsubscribe[] {\n return [\n client.on('tokens', ({ accessToken }) =>\n this.#emitter.emit('tokens', { account, accessToken }),\n ),\n client.on('signIn', ({ accessToken }) =>\n this.#emitter.emit('signIn', { account, accessToken }),\n ),\n client.on('signOut', () => this.#emitter.emit('signOut', { account })),\n client.on('authError', ({ error }) => this.#emitter.emit('authError', { account, error })),\n ];\n }\n}\n\n/**\n * Создаёт контейнер аккаунтов.\n *\n * То же, что `new ItdAccounts(options)`, — для тех, кому привычнее фабрика.\n */\nexport function createAccounts(options: ItdAccountsOptions = {}): ItdAccounts {\n return new ItdAccounts(options);\n}\n","import { ItdConfigError, ItdFileError, ItdFileErrorReason } from '../errors.js';\nimport { boundedFileStream, isReadableByteStream } from './bounded-stream.js';\nimport {\n DEFAULT_URL_FILE_MAX_BYTES,\n type FileContext,\n type FileStreamContent,\n FileTransferMode,\n type FromStreamOptions,\n type LazyFile,\n type StreamFile,\n type UrlFileOptions,\n} from './contracts.js';\nimport { fileTooLarge, optionalBytes } from './limits.js';\nimport { resolveFileStreamOptions } from './options.js';\nimport { downloadFile, normalizeMimeType, openUrlFile } from './url-source.js';\n\n/** Создаёт URL-источник в выбранном режиме. */\nexport function fromUrl(\n url: string,\n options: UrlFileOptions & { mode: typeof FileTransferMode.Stream },\n): StreamFile;\nexport function fromUrl(\n url: string,\n options?: UrlFileOptions & { mode?: typeof FileTransferMode.Buffer },\n): LazyFile;\nexport function fromUrl(url: string, options: UrlFileOptions): LazyFile | StreamFile;\nexport function fromUrl(url: string, options: UrlFileOptions = {}): LazyFile | StreamFile {\n const resolved = resolveFileStreamOptions(options, DEFAULT_URL_FILE_MAX_BYTES);\n if (resolved.mode === FileTransferMode.Stream) {\n return { open: (context) => openUrlFile(url, options, context) };\n }\n return { load: (context) => downloadFile(url, options, context) };\n}\n\n/**\n * Создаёт повторяемый пользовательский поток.\n *\n * Фабрика вызывается заново для каждой попытки; возвращать один и тот же поток нельзя.\n */\nexport function fromStream(\n factory: (\n context: FileContext,\n ) =>\n | ReadableStream<Uint8Array>\n | FileStreamContent\n | Promise<ReadableStream<Uint8Array> | FileStreamContent>,\n options: FromStreamOptions = {},\n): StreamFile {\n const resolved = resolveFileStreamOptions(\n { ...options, mode: FileTransferMode.Stream },\n undefined,\n );\n optionalBytes(options.size, 'size');\n\n return {\n open: async (context) => {\n let opened: ReadableStream<Uint8Array> | FileStreamContent;\n try {\n opened = await factory(context);\n } catch (error) {\n if (\n error instanceof ItdFileError ||\n error instanceof ItdConfigError ||\n context.signal?.aborted\n ) {\n throw error;\n }\n throw new ItdFileError('не удалось открыть поток вложения', {\n reason: ItdFileErrorReason.Read,\n retryable: true,\n cause: error,\n });\n }\n const content = isReadableByteStream(opened) ? { stream: opened } : opened;\n if (!content || !isReadableByteStream(content.stream)) {\n throw new ItdConfigError('fromStream должен вернуть ReadableStream или { stream }');\n }\n\n const size = options.size ?? content.size;\n optionalBytes(size, 'size');\n if (resolved.maxBytes !== undefined && size !== undefined && size > resolved.maxBytes) {\n await content.close?.();\n throw fileTooLarge(undefined, resolved.maxBytes, size);\n }\n\n const contentType = normalizeMimeType(options.contentType ?? content.contentType);\n return {\n stream: boundedFileStream(content.stream, {\n ...(resolved.maxBytes !== undefined ? { maxBytes: resolved.maxBytes } : {}),\n streamBufferBytes: resolved.streamBufferBytes,\n ...(context.signal ? { signal: context.signal } : {}),\n retryableRead: true,\n }),\n ...((options.filename ?? content.filename)\n ? { filename: options.filename ?? content.filename }\n : {}),\n ...(contentType ? { contentType } : {}),\n ...(size !== undefined ? { size } : {}),\n ...(content.close ? { close: content.close } : {}),\n };\n },\n };\n}\n","import type { MyProfile, Profile } from './users.js';\n\n/**\n * Свой ли это профиль.\n *\n * @example\n * ```ts\n * if (isMyProfile(profile)) console.log(profile.subscription.isActive);\n * ```\n */\nexport function isMyProfile(profile: Profile): profile is MyProfile {\n return 'subscription' in profile;\n}\n","import type { ServiceStatus, StatusDay } from './status.js';\n\nconst STATUS_WINDOW_DAYS = 90;\n\n/**\n * Разворачивает историю сервиса в массив на 90 суток.\n * Сутки без данных становятся `null`.\n *\n * @returns массив, где индекс — сколько суток назад: `[0]` — сегодня\n *\n * @example\n * ```ts\n * const status = await itd.platform.status();\n * const days = statusDays(status.services[0]);\n *\n * days[0]?.uptime; // доступность за сегодня\n * days.filter((day) => day === null).length; // за сколько суток данных нет\n * ```\n */\nexport function statusDays(service: ServiceStatus): (StatusDay | null)[] {\n return Array.from(\n { length: STATUS_WINDOW_DAYS },\n (_, index) => service.days[String(index)] ?? null,\n );\n}\n","import type { Notification } from '../models/notifications.js';\nimport { NotificationType } from '../types/enums.js';\n\n/** Имя, которое подставляется, если участник неизвестен. */\nconst UNKNOWN_ACTOR = 'Пользователь';\n\n/** Текст, который подставляется для неизвестного типа уведомления. */\nconst FALLBACK_TEXT = 'Новое уведомление';\n\n/**\n * Шаблоны текстов уведомлений.\n *\n * Для каждого типа задано две формы: для одного участника и для схлопнутого уведомления,\n * где действие совершили несколько человек.\n */\nconst TEMPLATES: Readonly<\n Record<string, { one: (name: string) => string; many: (name: string, others: number) => string }>\n> = Object.freeze({\n [NotificationType.Follow]: {\n one: (name) => `${name} подписался(-ась) на вас`,\n many: (name, others) => `${name} и ещё ${others} подписались на вас`,\n },\n [NotificationType.FollowRequest]: {\n one: (name) => `${name} хочет подписаться на вас`,\n many: (name, others) => `${name} и ещё ${others} хотят подписаться на вас`,\n },\n [NotificationType.FollowAccepted]: {\n one: (name) => `${name} принял(а) вашу заявку`,\n many: (name, others) => `${name} и ещё ${others} приняли вашу заявку`,\n },\n [NotificationType.PostReaction]: {\n one: (name) => `${name} оценил(а) ваш пост`,\n many: (name, others) => `${name} и ещё ${others} оценили ваш пост`,\n },\n [NotificationType.PostComment]: {\n one: (name) => `${name} прокомментировал(а) ваш пост`,\n many: (name, others) => `${name} и ещё ${others} прокомментировали ваш пост`,\n },\n [NotificationType.PostRepost]: {\n one: (name) => `${name} сделал(а) репост`,\n many: (name, others) => `${name} и ещё ${others} сделали репост`,\n },\n [NotificationType.CommentReaction]: {\n one: (name) => `${name} оценил(а) ваш комментарий`,\n many: (name, others) => `${name} и ещё ${others} оценили ваш комментарий`,\n },\n [NotificationType.CommentReply]: {\n one: (name) => `${name} ответил(а) на ваш комментарий`,\n many: (name, others) => `${name} и ещё ${others} ответили на ваш комментарий`,\n },\n [NotificationType.PostMention]: {\n one: (name) => `${name} упомянул(а) вас в посте`,\n many: (name, others) => `${name} и ещё ${others} упомянули вас в посте`,\n },\n [NotificationType.CommentMention]: {\n one: (name) => `${name} упомянул(а) вас в комментарии`,\n many: (name, others) => `${name} и ещё ${others} упомянули вас в комментарии`,\n },\n [NotificationType.WallPost]: {\n one: (name) => `${name} написал(а) на вашей стене`,\n many: (name, others) => `${name} и ещё ${others} написали на вашей стене`,\n },\n [NotificationType.VerificationApproved]: {\n one: () => 'Ваша заявка на верификацию одобрена',\n many: () => 'Ваша заявка на верификацию одобрена',\n },\n [NotificationType.VerificationRejected]: {\n one: () => 'Ваша заявка на верификацию отклонена',\n many: () => 'Ваша заявка на верификацию отклонена',\n },\n});\n\n/**\n * Собирает текст уведомления на русском.\n *\n * Повторяет формулировки сайта итд.com. Для неизвестного типа возвращает\n * «Новое уведомление» — библиотека не выдумывает текст, которого нет.\n *\n * @example\n * ```ts\n * formatNotificationText(notification);\n * // 'Аня и ещё 2 оценили ваш пост'\n * ```\n */\nexport function formatNotificationText(notification: Notification): string {\n const template = TEMPLATES[notification.type];\n if (!template) return FALLBACK_TEXT;\n\n const first = notification.actors[0];\n const name = first?.displayName || first?.username || UNKNOWN_ACTOR;\n\n const others = notification.count - 1;\n return others > 0 ? template.many(name, others) : template.one(name);\n}\n","import type { Notification } from '../models/notifications.js';\nimport { NotificationType } from '../types/enums.js';\n\n/** Типы, ведущие на пост. */\nconst POST_TYPES = new Set<string>([\n NotificationType.PostReaction,\n NotificationType.PostRepost,\n NotificationType.PostMention,\n NotificationType.WallPost,\n]);\n\n/** Типы, ведущие на комментарий внутри поста. */\nconst COMMENT_TYPES = new Set<string>([\n NotificationType.PostComment,\n NotificationType.CommentReaction,\n NotificationType.CommentReply,\n NotificationType.CommentMention,\n]);\n\n/** Типы, ведущие на профиль. */\nconst FOLLOW_TYPES = new Set<string>([\n NotificationType.Follow,\n NotificationType.FollowRequest,\n NotificationType.FollowAccepted,\n]);\n\n/**\n * Вычисляет адрес, на который ведёт уведомление.\n *\n * Возвращает путь внутри сайта — без домена, чтобы его можно было передать роутеру\n * приложения. Поле `clickUrl` от сервера используется только как запасной вариант:\n * вычисленный путь точнее, поскольку учитывает родительский пост у комментариев.\n *\n * @example\n * ```ts\n * const url = resolveNotificationUrl(notification);\n * // '/@nowkie/post/9f1c…?comment=2b7e…'\n * ```\n */\nexport function resolveNotificationUrl(notification: Notification): string {\n const { type, entityId, parentEntityId, clickUrl } = notification;\n const username = notification.actors[0]?.username;\n\n if (username && entityId) {\n if (POST_TYPES.has(type)) return `/@${username}/post/${entityId}`;\n\n if (COMMENT_TYPES.has(type)) {\n return parentEntityId\n ? `/@${username}/post/${parentEntityId}?comment=${entityId}`\n : `/@${username}/post/${entityId}`;\n }\n }\n\n if (username && FOLLOW_TYPES.has(type)) return `/@${username}`;\n\n return clickUrl || '/notifications';\n}\n","import type { Unsubscribe } from '../core/emitter.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport {\n captureRealtimeMiddleware,\n type RealtimeMiddleware,\n type RealtimeMiddlewareObj,\n runRealtimeMiddleware,\n withRealtimeMiddlewareSnapshot,\n} from './middleware.js';\nimport type { RealtimeContext, RealtimeContextBase } from './updates.js';\n\n/** Выбирает маршрут обновления. `undefined` и `null` означают отсутствие маршрута. */\nexport type RealtimeRouteSelector<\n K extends PropertyKey,\n C extends RealtimeContextBase = RealtimeContext,\n> = (context: C) => K | null | undefined | Promise<K | null | undefined>;\n\ninterface RouteRegistration<C extends RealtimeContextBase> {\n readonly middleware: readonly RealtimeMiddleware<C>[];\n}\n\n/**\n * Направляет обновления потока в именованные цепочки промежуточных обработчиков.\n *\n * @example\n * ```ts\n * import { RealtimeRouter, RealtimeUpdateType } from 'itd-api';\n *\n * const router = new RealtimeRouter((context) => context.update.type);\n * router.route(RealtimeUpdateType.Notification, async (context, next) => {\n * if (context.update.type === RealtimeUpdateType.Notification) {\n * await handleNotification(context.update.data.notification);\n * }\n * await next();\n * });\n * stream.use(router);\n * ```\n */\nexport class RealtimeRouter<\n K extends PropertyKey = PropertyKey,\n C extends RealtimeContextBase = RealtimeContext,\n> implements RealtimeMiddlewareObj<C>\n{\n readonly #selector: RealtimeRouteSelector<K, C>;\n readonly #routes = new Map<K, RouteRegistration<C>[]>();\n readonly #fallback: RouteRegistration<C>[] = [];\n\n constructor(selector: RealtimeRouteSelector<K, C>) {\n if (typeof selector !== 'function') {\n throw new ItdConfigError('RealtimeRouter принимает функцию выбора маршрута');\n }\n this.#selector = selector;\n }\n\n /** Добавляет промежуточные обработчики к маршруту и возвращает функцию их удаления. */\n route(key: K, ...middleware: readonly RealtimeMiddleware<C>[]): Unsubscribe {\n if (!isPropertyKey(key))\n throw new ItdConfigError('Ключ realtime route должен быть PropertyKey');\n const registration = this.#registration(middleware);\n const registrations = this.#routes.get(key) ?? [];\n registrations.push(registration);\n this.#routes.set(key, registrations);\n\n return () => {\n const current = this.#routes.get(key);\n if (!current) return;\n const index = current.indexOf(registration);\n if (index >= 0) current.splice(index, 1);\n if (current.length === 0) this.#routes.delete(key);\n };\n }\n\n /** Добавляет промежуточные обработчики для обновлений без зарегистрированного маршрута. */\n otherwise(...middleware: readonly RealtimeMiddleware<C>[]): Unsubscribe {\n const registration = this.#registration(middleware);\n this.#fallback.push(registration);\n\n return () => {\n const index = this.#fallback.indexOf(registration);\n if (index >= 0) this.#fallback.splice(index, 1);\n };\n }\n\n /** Возвращает снимок маршрутов для `stream.use(router)` или ручной композиции. */\n middleware(): RealtimeMiddleware<C> {\n const middleware: RealtimeMiddleware<C> = (context, next) =>\n this.#captureMiddleware()(context, next);\n return withRealtimeMiddlewareSnapshot(middleware, () => this.#captureMiddleware());\n }\n\n #captureMiddleware(): RealtimeMiddleware<C> {\n const routes = new Map<K, readonly RealtimeMiddleware<C>[]>();\n for (const [key, registrations] of this.#routes) {\n routes.set(\n key,\n registrations.flatMap(({ middleware }) => middleware).map(captureRealtimeMiddleware),\n );\n }\n const fallback = this.#fallback\n .flatMap(({ middleware }) => middleware)\n .map(captureRealtimeMiddleware);\n\n return async (context, next) => {\n const key = await this.#selector(context);\n if (key != null && !isPropertyKey(key)) {\n throw new ItdConfigError(\n 'Функция выбора маршрута должна возвращать PropertyKey, null или undefined',\n );\n }\n const route = key == null ? undefined : routes.get(key);\n const chain = route && route.length > 0 ? route : fallback;\n\n if (chain.length === 0) {\n await next();\n return;\n }\n\n await runRealtimeMiddleware(chain, context, next);\n };\n }\n\n #registration(middleware: readonly RealtimeMiddleware<C>[]): RouteRegistration<C> {\n if (middleware.length === 0) {\n throw new ItdConfigError('Маршрут должен содержать хотя бы один обработчик');\n }\n for (const item of middleware) {\n if (typeof item !== 'function') {\n throw new ItdConfigError('Маршрут принимает только функции обработки');\n }\n }\n return { middleware: [...middleware] };\n }\n}\n\nfunction isPropertyKey(value: unknown): value is PropertyKey {\n return typeof value === 'string' || typeof value === 'number' || typeof value === 'symbol';\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport {\n captureRealtimeMiddleware,\n deferRealtimeMiddleware,\n type RealtimeMiddleware,\n type RealtimeMiddlewareObj,\n type RealtimeNext,\n type RealtimeTypeGuard,\n runRealtimeMiddleware,\n withRealtimeMiddlewareSnapshot,\n} from './middleware.js';\nimport { RealtimeRouter, type RealtimeRouteSelector } from './router.js';\nimport type { RealtimeContext, RealtimeContextBase } from './updates.js';\n\n/** Функция или объектный middleware, который можно добавить в {@link RealtimeComposer}. */\nexport type RealtimeMiddlewareLike<C extends RealtimeContextBase = RealtimeContext> =\n | RealtimeMiddleware<C>\n | RealtimeMiddlewareObj<C>;\n\n/** Один middleware или последовательность middleware для ветки composer. */\nexport type RealtimeMiddlewareGroup<C extends RealtimeContextBase = RealtimeContext> =\n | RealtimeMiddlewareLike<C>\n | readonly RealtimeMiddlewareLike<C>[];\n\n/** Синхронное или асинхронное условие ветвления composer. */\nexport type RealtimeFilter<C extends RealtimeContextBase = RealtimeContext> = (\n context: C,\n) => boolean | Promise<boolean>;\n\n/** Ошибка локальной realtime-ветки вместе с контекстом обрабатываемого обновления. */\nexport interface RealtimeErrorContext<C extends RealtimeContextBase = RealtimeContext> {\n /** Исходное исключение middleware. */\n readonly error: unknown;\n /** Контекст обновления, на котором завершилась ветка. */\n readonly context: C;\n}\n\n/** Обработчик локальной границы ошибок composer. */\nexport type RealtimeErrorBoundary<C extends RealtimeContextBase = RealtimeContext> = (\n failure: RealtimeErrorContext<C>,\n next: RealtimeNext,\n) => unknown | Promise<unknown>;\n\n/** Именованные ветки для {@link RealtimeComposer.route}. */\nexport type RealtimeRouteTable<\n K extends string | symbol,\n C extends RealtimeContextBase = RealtimeContext,\n> = Partial<Record<K, RealtimeMiddlewareGroup<C>>>;\n\nfunction middlewareFunction<C extends RealtimeContextBase>(\n middleware: RealtimeMiddlewareLike<C>,\n): RealtimeMiddleware<C> {\n if (typeof middleware === 'function') return middleware;\n if (\n typeof middleware !== 'object' ||\n middleware === null ||\n typeof middleware.middleware !== 'function'\n ) {\n throw new ItdConfigError(\n 'RealtimeComposer принимает функцию обработки или объект с middleware()',\n );\n }\n return deferRealtimeMiddleware(middleware);\n}\n\nfunction middlewareGroup<C extends RealtimeContextBase>(\n group: RealtimeMiddlewareGroup<C>,\n): RealtimeMiddleware<C>[] {\n const middleware = Array.isArray(group) ? group : [group];\n if (middleware.length === 0) {\n throw new ItdConfigError('Ветка RealtimeComposer должна содержать хотя бы один middleware');\n }\n return middleware.map((item) => middlewareFunction(item));\n}\n\ninterface BoundaryContinuation {\n readonly next: RealtimeNext;\n readonly called: () => boolean;\n readonly settled: () => Promise<void>;\n}\n\nfunction boundaryContinuation(): BoundaryContinuation {\n let called = false;\n let duplicate: Promise<void> | undefined;\n\n return {\n next: () => {\n if (!called) {\n called = true;\n return Promise.resolve();\n }\n\n const failure = Promise.reject(\n new Error('next() в обработчике realtime error boundary вызван повторно'),\n );\n duplicate = Promise.all(duplicate ? [duplicate, failure] : [failure]).then(() => undefined);\n return failure;\n },\n called: () => called,\n settled: async () => {\n await duplicate;\n },\n };\n}\n\nasync function runBoundaryStep(\n run: () => unknown | Promise<unknown>,\n continuation: BoundaryContinuation,\n): Promise<void> {\n let failure: { error: unknown } | undefined;\n try {\n await run();\n } catch (error) {\n failure = { error };\n }\n try {\n await continuation.settled();\n } catch (error) {\n failure ??= { error };\n }\n if (failure) throw failure.error;\n}\n\n/**\n * Собирает переиспользуемый feature-модуль из realtime middleware.\n *\n * Composer не открывает соединение и не планирует конкурентность: готовый объект подключается\n * через `stream.use(composer)`, а выполнение остаётся обязанностью существующего dispatcher.\n * Для каждого принятого update используется снимок всей вложенной структуры composer.\n *\n * @example\n * ```ts\n * const feature = new RealtimeComposer<AppRealtimeContext>();\n * const safe = feature.errorBoundary(reportFeatureError);\n * safe.filter(isPostUpdate).use(handlePost);\n * stream.use(feature);\n * ```\n */\nexport class RealtimeComposer<C extends RealtimeContextBase = RealtimeContext>\n implements RealtimeMiddlewareObj<C>\n{\n readonly #middleware: RealtimeMiddleware<C>[] = [];\n\n constructor(...middleware: readonly RealtimeMiddlewareLike<C>[]) {\n this.use(...middleware);\n }\n\n /** Добавляет middleware в конец текущей onion-цепочки. */\n use(...middleware: readonly RealtimeMiddlewareLike<C>[]): this {\n const normalized = middleware.map(middlewareFunction);\n this.#middleware.push(...normalized);\n return this;\n }\n\n /** Создаёт дочернюю ветку, выполняемую только когда type guard принимает контекст. */\n filter<N extends C>(\n predicate: RealtimeTypeGuard<N, C>,\n ...middleware: readonly RealtimeMiddlewareLike<N>[]\n ): RealtimeComposer<N>;\n /** Создаёт дочернюю ветку по синхронному или асинхронному условию. */\n filter(\n predicate: RealtimeFilter<C>,\n ...middleware: readonly RealtimeMiddlewareLike<C>[]\n ): RealtimeComposer<C>;\n filter<N extends C>(\n predicate: RealtimeFilter<C>,\n ...middleware: readonly RealtimeMiddlewareLike<N>[]\n ): RealtimeComposer<N> {\n if (typeof predicate !== 'function') {\n throw new ItdConfigError('RealtimeComposer.filter() принимает функцию условия');\n }\n\n const child = new RealtimeComposer<N>(...middleware);\n const source: RealtimeMiddlewareObj<C> = {\n middleware: () => {\n const nested = captureRealtimeMiddleware(child.middleware());\n return async (context, next) => {\n if (await predicate(context)) await nested(context as N, next);\n else await next();\n };\n },\n };\n this.#middleware.push(deferRealtimeMiddleware(source));\n return child;\n }\n\n /**\n * Направляет контекст в одну именованную ветку.\n *\n * Неизвестный ключ без fallback пропускает update следующему внешнему middleware. Для\n * динамической регистрации и числовых ключей используйте {@link RealtimeRouter} напрямую.\n */\n route<K extends string | symbol>(\n selector: RealtimeRouteSelector<K, C>,\n routes: RealtimeRouteTable<K, C>,\n fallback?: RealtimeMiddlewareGroup<C>,\n ): this {\n if (typeof routes !== 'object' || routes === null || Array.isArray(routes)) {\n throw new ItdConfigError('RealtimeComposer.route() принимает объект именованных веток');\n }\n\n const router = new RealtimeRouter<K, C>(selector);\n let registrations = 0;\n for (const key of Reflect.ownKeys(routes)) {\n const group = routes[key as K];\n if (group === undefined) continue;\n router.route(key as K, ...middlewareGroup(group));\n registrations += 1;\n }\n if (fallback !== undefined) {\n router.otherwise(...middlewareGroup(fallback));\n registrations += 1;\n }\n if (registrations === 0) {\n throw new ItdConfigError('RealtimeComposer.route() требует хотя бы одну ветку или fallback');\n }\n\n return this.use(router);\n }\n\n /**\n * Создаёт дочернюю ветку с локальной границей ошибок.\n *\n * Граница защищает только переданные и затем добавленные в возвращённый composer middleware.\n * Ошибки внешней цепочки намеренно не перехватываются. Обработчик может вызвать `next()`,\n * чтобы после ошибки продолжить внешнюю цепочку, либо повторно выбросить исключение. Внешняя\n * цепочка начинается после полного завершения защищённой ветки, а не входит в её onion-вызов.\n */\n errorBoundary(\n handler: RealtimeErrorBoundary<C>,\n ...middleware: readonly RealtimeMiddlewareLike<C>[]\n ): RealtimeComposer<C> {\n if (typeof handler !== 'function') {\n throw new ItdConfigError('RealtimeComposer.errorBoundary() принимает обработчик ошибки');\n }\n\n const child = new RealtimeComposer<C>(...middleware);\n const source: RealtimeMiddlewareObj<C> = {\n middleware: () => {\n const nested = captureRealtimeMiddleware(child.middleware());\n return async (context, next) => {\n let continuation = boundaryContinuation();\n try {\n await runBoundaryStep(() => nested(context, continuation.next), continuation);\n } catch (error) {\n continuation = boundaryContinuation();\n await runBoundaryStep(\n () => handler({ error, context }, continuation.next),\n continuation,\n );\n }\n\n if (continuation.called()) await next();\n };\n },\n };\n this.#middleware.push(deferRealtimeMiddleware(source));\n return child;\n }\n\n /** Возвращает snapshot-aware middleware для `stream.use()` или вложенного composer. */\n middleware(): RealtimeMiddleware<C> {\n const middleware: RealtimeMiddleware<C> = (context, next) =>\n this.#captureMiddleware()(context, next);\n return withRealtimeMiddlewareSnapshot(middleware, () => this.#captureMiddleware());\n }\n\n #captureMiddleware(): RealtimeMiddleware<C> {\n const snapshot = this.#middleware.map(captureRealtimeMiddleware);\n return (context, next) => runRealtimeMiddleware(snapshot, context, next);\n }\n}\n","import { type ItdClock, systemClock } from '../core/clock.js';\nimport { ItdConfigError } from '../core/errors.js';\nimport { maskSecret, redactUrl } from '../core/redact.js';\nimport { buildQuery, joinUrl } from '../core/url.js';\nimport {\n type RealtimeTransport,\n type TransportContext,\n UnauthorizedStreamError,\n} from './transport.js';\n\n/** Стандартный путь WebSocket-подключения. */\nexport const WEBSOCKET_PATH = '/api/ws';\n\n/** Предел кадров, ожидающих разбора. Переполнение закрывает соединение. @internal */\nexport const MAX_PENDING_FRAMES = 256;\n\n/** Дополнительные параметры конструктора, поддерживаемые Node-реализациями вроде `ws`. */\nexport interface WebSocketImplementationOptions {\n headers?: Record<string, string> | undefined;\n handshakeTimeout?: number | undefined;\n}\n\n/** Конструктор WebSocket, который можно передать вместо глобальной реализации. */\nexport interface WebSocketLike {\n new (\n url: string | URL,\n protocols?: string | string[],\n options?: WebSocketImplementationOptions,\n ): unknown;\n}\n\n/** Определяет, был ли отказ до открытия сокета вызван недействительным токеном. */\nexport type WebSocketOpenFailureClassifier = (\n error: unknown,\n signal: AbortSignal,\n) => boolean | Promise<boolean>;\n\n/** Настройки WebSocket-транспорта. */\nexport interface WebSocketTransportOptions {\n /** Путь апгрейда. По умолчанию `/api/ws`. */\n path?: string | undefined;\n /** Реализация WebSocket для сред без глобальной либо для передачи заголовков апгрейда. */\n webSocketImpl?: WebSocketLike | undefined;\n /** Способ передачи токена. `auto` выбирает заголовок при инъекции и query иначе. */\n auth?: 'query' | 'header' | 'auto' | undefined;\n /** Молчание открытого соединения до переподключения, мс. По умолчанию 90 000. */\n idleTimeout?: number | undefined;\n /** Период текстового `ping`, мс. По умолчанию 30 000. */\n keepAlive?: number | undefined;\n /** Максимальное время установки соединения, мс. По умолчанию 20 000. */\n handshakeTimeout?: number | undefined;\n /**\n * Проверяет отказ до `open`, когда среда скрыла HTTP-статус WebSocket-upgrade.\n * `true` преобразует отказ в {@link UnauthorizedStreamError}.\n */\n classifyOpenFailure?: WebSocketOpenFailureClassifier | undefined;\n /** Часы транспорта. Обычно подменяются только в тестах. */\n clock?: ItdClock | undefined;\n}\n\ninterface SocketMessageEvent {\n readonly data: unknown;\n}\n\ninterface SocketCloseEvent {\n readonly code?: unknown;\n readonly reason?: unknown;\n readonly wasClean?: unknown;\n}\n\ninterface WebSocketConnection {\n readonly readyState: number;\n binaryType?: string;\n addEventListener(type: string, listener: (event: unknown) => void): void;\n removeEventListener(type: string, listener: (event: unknown) => void): void;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n}\n\nconst CONNECTING = 0;\nconst OPEN = 1;\nconst NORMAL_CLOSURE = 1000;\nconst UNAUTHORIZED_CLOSE_CODES = new Set([1008, 4401]);\n\nfunction validateDuration(value: number | undefined, name: string): void {\n if (value !== undefined && (!Number.isFinite(value) || value < 0)) {\n throw new ItdConfigError(\n `WebSocketTransport.${name} должен быть числом не меньше 0, получено: ${value}`,\n );\n }\n}\n\nfunction isWebSocketConnection(value: unknown): value is WebSocketConnection {\n if (typeof value !== 'object' || value === null) return false;\n const socket = value as Partial<WebSocketConnection>;\n return (\n typeof socket.readyState === 'number' &&\n typeof socket.addEventListener === 'function' &&\n typeof socket.removeEventListener === 'function' &&\n typeof socket.send === 'function' &&\n typeof socket.close === 'function'\n );\n}\n\nfunction abortError(signal: AbortSignal): unknown {\n if (signal.reason !== undefined) return signal.reason;\n const error = new Error('WebSocket-подключение отменено');\n error.name = 'AbortError';\n return error;\n}\n\nfunction errorMessage(error: unknown): string | undefined {\n if (error instanceof Error && error.message) return error.message;\n if (typeof error === 'object' && error !== null && 'message' in error) {\n const message = (error as { message?: unknown }).message;\n if (typeof message === 'string' && message) return message;\n }\n return undefined;\n}\n\nfunction safeDetail(error: unknown, url: string, token: string | null): string | undefined {\n const message = errorMessage(error);\n if (!message) return undefined;\n const safe = message.replaceAll(url, redactUrl(url));\n return token ? safe.replaceAll(token, maskSecret(token)) : safe;\n}\n\nfunction readCloseEvent(event: unknown): { code: number; reason: string; wasClean: boolean } {\n const close =\n typeof event === 'object' && event !== null ? (event as SocketCloseEvent) : undefined;\n return {\n code: typeof close?.code === 'number' ? close.code : 1006,\n reason: typeof close?.reason === 'string' ? close.reason : '',\n wasClean: close?.wasClean === true,\n };\n}\n\nfunction readMessage(event: unknown): string | Promise<string> | undefined {\n const data =\n typeof event === 'object' && event !== null && 'data' in event\n ? (event as SocketMessageEvent).data\n : undefined;\n\n if (typeof data === 'string') return data;\n if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);\n if (ArrayBuffer.isView(data)) {\n return new TextDecoder().decode(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));\n }\n if (typeof Blob !== 'undefined' && data instanceof Blob) return data.text();\n return undefined;\n}\n\nfunction eventName(data: unknown): string {\n return typeof data === 'object' && data !== null && 'type' in data\n ? String((data as { type: unknown }).type)\n : 'message';\n}\n\n/** Транспорт исходных realtime-событий поверх стандартного WebSocket. */\nexport class WebSocketTransport implements RealtimeTransport {\n readonly name = 'ws';\n\n readonly #path: string;\n readonly #webSocketImpl: WebSocketLike | undefined;\n readonly #auth: 'query' | 'header' | 'auto';\n readonly #idleTimeout: number;\n readonly #keepAlive: number;\n readonly #handshakeTimeout: number;\n readonly #classifyOpenFailure: WebSocketOpenFailureClassifier | undefined;\n readonly #clock: ItdClock;\n\n constructor(options: WebSocketTransportOptions = {}) {\n const path = options.path ?? WEBSOCKET_PATH;\n if (\n typeof path !== 'string' ||\n path.trim() === '' ||\n path.includes('?') ||\n path.includes('#') ||\n /^[a-z][a-z\\d+.-]*:/i.test(path)\n ) {\n throw new ItdConfigError('WebSocketTransport.path должен быть путём без query и fragment');\n }\n if (options.webSocketImpl !== undefined && typeof options.webSocketImpl !== 'function') {\n throw new ItdConfigError('WebSocketTransport.webSocketImpl должен быть конструктором');\n }\n if (\n options.classifyOpenFailure !== undefined &&\n typeof options.classifyOpenFailure !== 'function'\n ) {\n throw new ItdConfigError('WebSocketTransport.classifyOpenFailure должен быть функцией');\n }\n\n const auth = options.auth ?? 'auto';\n if (auth !== 'auto' && auth !== 'query' && auth !== 'header') {\n throw new ItdConfigError(`Неизвестный способ авторизации WebSocket: ${String(auth)}`);\n }\n if (auth === 'header' && !options.webSocketImpl) {\n throw new ItdConfigError(\n 'WebSocketTransport.auth=\"header\" требует webSocketImpl с поддержкой заголовков',\n );\n }\n\n validateDuration(options.idleTimeout, 'idleTimeout');\n validateDuration(options.keepAlive, 'keepAlive');\n validateDuration(options.handshakeTimeout, 'handshakeTimeout');\n\n this.#path = path;\n this.#webSocketImpl = options.webSocketImpl;\n this.#auth = auth;\n this.#idleTimeout = options.idleTimeout ?? 90_000;\n this.#keepAlive = options.keepAlive ?? 30_000;\n this.#handshakeTimeout = options.handshakeTimeout ?? 20_000;\n this.#classifyOpenFailure = options.classifyOpenFailure;\n this.#clock = options.clock ?? systemClock;\n }\n\n async connect(context: TransportContext): Promise<void> {\n if (context.signal.aborted) throw abortError(context.signal);\n\n const token = context.authorize ? await context.getToken() : null;\n if (context.signal.aborted) throw abortError(context.signal);\n if (context.authorize && !token) throw new UnauthorizedStreamError();\n\n const injected = this.#webSocketImpl !== undefined;\n const implementation = this.#webSocketImpl ?? this.#globalImplementation();\n const auth = this.#auth === 'auto' ? (injected ? 'header' : 'query') : this.#auth;\n const { requestUrl, socketUrl } = this.#urls(context.baseUrl);\n const url = auth === 'query' && token ? `${socketUrl}${buildQuery({ token })}` : socketUrl;\n\n let headers: Record<string, string> | undefined;\n if (injected) {\n const resolved = await context.baseHeaders(requestUrl);\n if (context.signal.aborted) throw abortError(context.signal);\n if (auth === 'header' && token) resolved.set('Authorization', `Bearer ${token}`);\n headers = Object.fromEntries(resolved.entries());\n }\n\n let value: unknown;\n try {\n value = injected\n ? new implementation(url, [], {\n ...(headers ? { headers } : {}),\n ...(this.#handshakeTimeout > 0 ? { handshakeTimeout: this.#handshakeTimeout } : {}),\n })\n : new implementation(url);\n } catch (error) {\n const detail = safeDetail(error, url, token);\n throw new Error(\n `Не удалось создать WebSocket ${redactUrl(url)}${detail ? `: ${detail}` : ''}`,\n );\n }\n\n if (!isWebSocketConnection(value)) {\n throw new ItdConfigError(\n 'webSocketImpl вернул объект без WebSocket-методов addEventListener, send и close',\n );\n }\n\n try {\n value.binaryType = 'arraybuffer';\n } catch {\n // Не все совместимые реализации позволяют менять способ выдачи бинарных кадров.\n }\n\n return this.#listen(value, url, token, context);\n }\n\n #globalImplementation(): WebSocketLike {\n const implementation = Reflect.get(globalThis, 'WebSocket');\n if (typeof implementation !== 'function') {\n throw new ItdConfigError(\n 'В этой среде нет WebSocket: передайте реализацию через webSocketImpl',\n );\n }\n return implementation as WebSocketLike;\n }\n\n #urls(baseUrl: string): { requestUrl: string; socketUrl: string } {\n const request = new URL(joinUrl(baseUrl, this.#path));\n const socket = new URL(request);\n if (request.protocol === 'https:') socket.protocol = 'wss:';\n else if (request.protocol === 'http:') socket.protocol = 'ws:';\n else {\n throw new ItdConfigError(\n `WebSocketTransport требует baseUrl с http или https, получено: ${request.protocol}`,\n );\n }\n return { requestUrl: request.toString(), socketUrl: socket.toString() };\n }\n\n #listen(\n socket: WebSocketConnection,\n url: string,\n token: string | null,\n context: TransportContext,\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n let settled = false;\n let finishing = false;\n let opened = false;\n let discardMessages = false;\n let socketError: unknown;\n let messageQueue = Promise.resolve();\n let pendingFrames = 0;\n let cancelHandshake: (() => void) | undefined;\n let cancelIdle: (() => void) | undefined;\n let cancelKeepAlive: (() => void) | undefined;\n\n const stopSocketListeners = () => {\n cancelHandshake?.();\n cancelIdle?.();\n cancelKeepAlive?.();\n cancelHandshake = undefined;\n cancelIdle = undefined;\n cancelKeepAlive = undefined;\n socket.removeEventListener('open', onOpen);\n socket.removeEventListener('message', onMessage);\n socket.removeEventListener('error', onError);\n socket.removeEventListener('close', onClose);\n };\n\n const finish = (error?: unknown) => {\n if (settled) return;\n settled = true;\n stopSocketListeners();\n context.signal.removeEventListener('abort', onAbort);\n if (error !== undefined) reject(error);\n else resolve();\n };\n\n const closeWithError = (error: unknown, code = 4000, reason = 'transport error') => {\n if (settled || finishing) return;\n socketError = error;\n finishing = true;\n stopSocketListeners();\n try {\n if (socket.readyState === CONNECTING || socket.readyState === OPEN) {\n socket.close(code, reason);\n }\n } catch {\n // Ошибка уже сохранена и будет возвращена вызывающему коду.\n }\n void messageQueue.then(\n () => finish(error),\n (queueError: unknown) => finish(queueError),\n );\n };\n\n const armIdleTimer = () => {\n cancelIdle?.();\n if (this.#idleTimeout <= 0) return;\n cancelIdle = this.#clock.schedule(() => {\n closeWithError(\n new Error(`WebSocket ${redactUrl(url)} молчит дольше допустимого`),\n 4000,\n 'idle timeout',\n );\n }, this.#idleTimeout);\n };\n\n const scheduleKeepAlive = () => {\n if (this.#keepAlive <= 0 || settled || finishing) return;\n cancelKeepAlive = this.#clock.schedule(() => {\n if (settled || finishing || socket.readyState !== OPEN) return;\n try {\n socket.send('ping');\n } catch (error) {\n const detail = safeDetail(error, url, token);\n closeWithError(\n new Error(\n `Не удалось отправить WebSocket ping ${redactUrl(url)}` +\n (detail ? `: ${detail}` : ''),\n ),\n );\n return;\n }\n scheduleKeepAlive();\n }, this.#keepAlive);\n };\n\n const onOpen = () => {\n if (settled || finishing || opened) return;\n opened = true;\n cancelHandshake?.();\n cancelHandshake = undefined;\n armIdleTimer();\n scheduleKeepAlive();\n try {\n context.onOpen();\n } catch (error) {\n closeWithError(error);\n }\n };\n\n const onMessage = (event: unknown) => {\n if (settled || finishing) return;\n armIdleTimer();\n let pending: Promise<{ readonly raw: string } | { readonly error: unknown }> | undefined;\n try {\n const raw = readMessage(event);\n if (raw !== undefined) {\n pending = Promise.resolve(raw).then(\n (value) => ({ raw: value }),\n (error: unknown) => ({ error }),\n );\n }\n } catch (error) {\n pending = Promise.resolve({ error });\n }\n if (pending === undefined) return;\n\n if (pendingFrames >= MAX_PENDING_FRAMES) {\n closeWithError(\n new Error(`WebSocket ${redactUrl(url)}: очередь кадров переполнена`),\n 4000,\n 'queue overflow',\n );\n return;\n }\n\n pendingFrames += 1;\n messageQueue = messageQueue\n .then(async () => {\n pendingFrames -= 1;\n if (settled || discardMessages) return;\n\n const decoded = await pending;\n if ('error' in decoded) {\n context.onParseError(decoded.error, '[binary WebSocket frame]');\n return;\n }\n const { raw } = decoded;\n if (settled || discardMessages || raw === 'pong') return;\n\n let data: unknown;\n try {\n data = JSON.parse(raw) as unknown;\n } catch (error) {\n context.onParseError(error, raw);\n return;\n }\n context.onEvent({ name: eventName(data), data });\n })\n .catch((error: unknown) => {\n if (finishing) throw error;\n closeWithError(error);\n });\n };\n\n const onError = (event: unknown) => {\n if (settled || finishing) return;\n socketError =\n typeof event === 'object' && event !== null && 'error' in event\n ? (event as { error?: unknown }).error\n : event;\n };\n\n const finishClose = async (event: unknown): Promise<void> => {\n const { code, reason, wasClean } = readCloseEvent(event);\n let outcome: unknown;\n\n if (context.authorize && UNAUTHORIZED_CLOSE_CODES.has(code)) {\n outcome = new UnauthorizedStreamError();\n } else if (socketError !== undefined) {\n const detail = safeDetail(socketError, url, token);\n outcome = new Error(\n `WebSocket ${redactUrl(url)} завершился ошибкой${detail ? `: ${detail}` : ''}`,\n );\n } else if (opened && code === NORMAL_CLOSURE && wasClean) {\n outcome = undefined;\n } else {\n const safeReason = token ? reason.replaceAll(token, maskSecret(token)) : reason;\n outcome = new Error(\n `WebSocket ${redactUrl(url)} закрыт с кодом ${code}` +\n (safeReason ? `: ${safeReason}` : ''),\n );\n }\n\n if (\n !opened &&\n context.authorize &&\n !(outcome instanceof UnauthorizedStreamError) &&\n this.#classifyOpenFailure\n ) {\n try {\n if (await this.#classifyOpenFailure(outcome, context.signal)) {\n outcome = new UnauthorizedStreamError();\n }\n } catch {\n // Ошибка проверки не должна скрывать исходную ошибку соединения.\n }\n }\n\n if (context.signal.aborted) {\n discardMessages = true;\n finish(abortError(context.signal));\n return;\n }\n\n try {\n await messageQueue;\n finish(outcome);\n } catch (error) {\n finish(error);\n }\n };\n\n const onClose = (event: unknown) => {\n if (settled || finishing) return;\n if (context.signal.aborted) {\n discardMessages = true;\n finish(abortError(context.signal));\n return;\n }\n finishing = true;\n stopSocketListeners();\n void finishClose(event);\n };\n\n const onAbort = () => {\n if (settled) return;\n const error = abortError(context.signal);\n discardMessages = true;\n finishing = true;\n stopSocketListeners();\n try {\n if (socket.readyState === CONNECTING || socket.readyState === OPEN) {\n socket.close(NORMAL_CLOSURE, 'aborted');\n }\n } catch {\n // Отмена всё равно завершает connect(), даже если реализация уже закрыла сокет.\n }\n finish(error);\n };\n\n socket.addEventListener('open', onOpen);\n socket.addEventListener('message', onMessage);\n socket.addEventListener('error', onError);\n socket.addEventListener('close', onClose);\n context.signal.addEventListener('abort', onAbort, { once: true });\n\n if (this.#handshakeTimeout > 0) {\n cancelHandshake = this.#clock.schedule(() => {\n closeWithError(\n new Error(`Истёк таймаут установки WebSocket ${redactUrl(url)}`),\n 4000,\n 'handshake timeout',\n );\n }, this.#handshakeTimeout);\n }\n\n if (context.signal.aborted) onAbort();\n });\n }\n}\n","import type { Span } from '../models/common.js';\nimport { SpanType } from '../types/enums.js';\n\n/** Формат результата {@link renderSpans}. */\nexport const SpanRenderFormat = Object.freeze({\n Html: 'html',\n Markdown: 'markdown',\n Ansi: 'ansi',\n} as const);\nexport type SpanRenderFormat = (typeof SpanRenderFormat)[keyof typeof SpanRenderFormat];\n\nexport interface RenderSpansOptions {\n /** Формат результата. По умолчанию `html`. */\n format?: SpanRenderFormat;\n /** Строит адрес упоминания. `null` или `undefined` отключает ссылку. */\n mentionUrl?: (username: string) => string | null | undefined;\n /** Строит адрес хэштега. `null` или `undefined` отключает ссылку. */\n hashtagUrl?: (tag: string) => string | null | undefined;\n /**\n * Префикс HTML-классов. По умолчанию `itd`; пустая строка или `null` отключает классы.\n *\n * Например, `app` создаёт `app-mention`, `app-hashtag`, `app-quote` и `app-spoiler`.\n */\n classPrefix?: string | null;\n}\n\ninterface NormalizedSpan {\n span: Span;\n start: number;\n end: number;\n}\n\nfunction normalizedSpans(\n content: string,\n spans: readonly Span[] | null | undefined,\n): NormalizedSpan[] {\n const result: NormalizedSpan[] = [];\n for (const span of spans ?? []) {\n if (\n !span ||\n !Number.isFinite(span.offset) ||\n !Number.isFinite(span.length) ||\n span.length <= 0\n ) {\n continue;\n }\n\n const start = Math.max(0, Math.trunc(span.offset));\n const end = Math.min(content.length, Math.trunc(span.offset + span.length));\n if (start < end && start < content.length) result.push({ span, start, end });\n }\n return result;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\n}\n\nfunction safeHttpUrl(value: string | undefined): string | null {\n if (!value) return null;\n try {\n const url = new URL(value);\n return url.protocol === 'http:' || url.protocol === 'https:' ? url.href : null;\n } catch {\n return null;\n }\n}\n\nfunction safeNavigationUrl(value: string | null | undefined): string | null {\n if (!value) return null;\n try {\n const url = new URL(value, 'https://itd.invalid');\n return url.protocol === 'http:' || url.protocol === 'https:' ? value : null;\n } catch {\n return null;\n }\n}\n\nfunction entityValue(span: Span, content: string, kind: 'mention' | 'hashtag'): string {\n const offset = Number.isFinite(span.offset) ? Math.max(0, Math.trunc(span.offset)) : 0;\n const end = Number.isFinite(span.length)\n ? Math.min(content.length, offset + Math.max(0, Math.trunc(span.length)))\n : offset;\n const text = content.slice(offset, end);\n if (kind === 'mention') {\n return span.username ?? span.tag ?? text.replace(/^@/u, '');\n }\n return span.tag ?? text.replace(/^#/u, '');\n}\n\nfunction mentionUrl(username: string, options: RenderSpansOptions): string | null | undefined {\n return options.mentionUrl ? options.mentionUrl(username) : `/@${encodeURIComponent(username)}`;\n}\n\nfunction hashtagUrl(tag: string, options: RenderSpansOptions): string | null | undefined {\n return options.hashtagUrl ? options.hashtagUrl(tag) : `/hashtag/${encodeURIComponent(tag)}`;\n}\n\nfunction classAttribute(name: string, options: RenderSpansOptions): string {\n const prefix = options.classPrefix === undefined ? 'itd' : options.classPrefix;\n return prefix ? ` class=\"${escapeHtml(`${prefix}-${name}`)}\"` : '';\n}\n\nfunction has(active: readonly Span[], type: string): boolean {\n return active.some((span) => span.type === type);\n}\n\nfunction last(active: readonly Span[], type: string): Span | undefined {\n for (let index = active.length - 1; index >= 0; index -= 1) {\n const span = active[index];\n if (span?.type === type) return span;\n }\n return undefined;\n}\n\nfunction renderHtmlSegment(\n text: string,\n active: readonly Span[],\n content: string,\n options: RenderSpansOptions,\n): string {\n let rendered = escapeHtml(text).replace(/\\n/g, '<br>');\n\n if (has(active, SpanType.Bold)) rendered = `<strong>${rendered}</strong>`;\n if (has(active, SpanType.Italic)) rendered = `<em>${rendered}</em>`;\n if (has(active, SpanType.Underline)) rendered = `<u>${rendered}</u>`;\n if (has(active, SpanType.Strike)) rendered = `<s>${rendered}</s>`;\n if (has(active, SpanType.Monospace)) rendered = `<code>${rendered}</code>`;\n if (has(active, SpanType.Quote)) {\n rendered = `<span${classAttribute('quote', options)}>${rendered}</span>`;\n }\n if (has(active, SpanType.Spoiler)) {\n rendered = `<span${classAttribute('spoiler', options)} data-spoiler=\"true\">${rendered}</span>`;\n }\n\n const link = last(active, SpanType.Link);\n const url = safeHttpUrl(link?.url);\n if (url) {\n rendered = `<a href=\"${escapeHtml(url)}\" rel=\"noopener noreferrer\">${rendered}</a>`;\n }\n\n const mention = last(active, SpanType.Mention);\n if (mention) {\n const username = entityValue(mention, content, 'mention');\n const href = safeNavigationUrl(mentionUrl(username, options));\n if (href) {\n rendered = `<a href=\"${escapeHtml(href)}\"${classAttribute('mention', options)}>${rendered}</a>`;\n }\n }\n\n const hashtag = last(active, SpanType.Hashtag);\n if (hashtag) {\n const tag = entityValue(hashtag, content, 'hashtag');\n const href = safeNavigationUrl(hashtagUrl(tag, options));\n if (href) {\n rendered = `<a href=\"${escapeHtml(href)}\"${classAttribute('hashtag', options)}>${rendered}</a>`;\n }\n }\n\n return rendered;\n}\n\nfunction escapeMarkdown(value: string, startsAtLineStart: boolean): string {\n const lines = value.replace(/([\\\\`*_[\\]<>~|&])/g, '\\\\$1').split('\\n');\n return lines\n .map((line, index) => {\n if (index === 0 && !startsAtLineStart) return line;\n return line\n .replace(/^ {0,3}(?=#{1,6}(?:[ \\t]|$))/u, '$&\\\\')\n .replace(/^ {0,3}(?=[+-][ \\t])/u, '$&\\\\')\n .replace(/^(\\s{0,3}\\d{1,9})([.)])(?=[ \\t])/u, '$1\\\\$2')\n .replace(/^(\\s{0,3})(?=-(?:[ \\t]*-){2,}[ \\t]*$)/u, '$1\\\\');\n })\n .join('\\n');\n}\n\nfunction markdownCodeSpan(text: string): string {\n const normalized = text.replace(/\\r\\n?|\\n/g, ' ');\n const longestRun = Math.max(\n 0,\n ...Array.from(normalized.matchAll(/`+/g), (match) => match[0].length),\n );\n const fence = '`'.repeat(longestRun + 1);\n const allSpaces = /^ +$/u.test(normalized);\n const needsPadding =\n normalized.startsWith('`') ||\n normalized.endsWith('`') ||\n (!allSpaces && normalized.startsWith(' ') && normalized.endsWith(' '));\n const body = needsPadding ? ` ${normalized} ` : normalized;\n return `${fence}${body}${fence}`;\n}\n\ninterface RenderSegment {\n start: number;\n text: string;\n active: Span[];\n}\n\nfunction markdownUrl(value: string): string {\n return value.replace(/([\\\\)])/g, '\\\\$1');\n}\n\nfunction renderMarkdownSegment(\n segment: RenderSegment,\n content: string,\n options: RenderSpansOptions,\n): string {\n const { text, active } = segment;\n const startsAtLineStart = segment.start === 0 || content[segment.start - 1] === '\\n';\n let rendered = has(active, SpanType.Monospace)\n ? markdownCodeSpan(text)\n : escapeMarkdown(text, startsAtLineStart);\n if (has(active, SpanType.Bold)) rendered = `**${rendered}**`;\n if (has(active, SpanType.Italic)) rendered = `*${rendered}*`;\n if (has(active, SpanType.Underline)) rendered = `<u>${rendered}</u>`;\n if (has(active, SpanType.Strike)) rendered = `~~${rendered}~~`;\n if (has(active, SpanType.Spoiler)) rendered = `||${rendered}||`;\n\n const link = last(active, SpanType.Link);\n const url = safeHttpUrl(link?.url);\n if (url) rendered = `[${rendered}](${markdownUrl(url)})`;\n\n const mention = last(active, SpanType.Mention);\n if (mention) {\n const username = entityValue(mention, content, 'mention');\n const href = safeNavigationUrl(mentionUrl(username, options));\n if (href) rendered = `[${rendered}](${markdownUrl(href)})`;\n }\n\n const hashtag = last(active, SpanType.Hashtag);\n if (hashtag) {\n const tag = entityValue(hashtag, content, 'hashtag');\n const href = safeNavigationUrl(hashtagUrl(tag, options));\n if (href) rendered = `[${rendered}](${markdownUrl(href)})`;\n }\n\n return rendered;\n}\n\nfunction renderMarkdown(\n segments: readonly RenderSegment[],\n content: string,\n options: RenderSpansOptions,\n): string {\n const groups: { quoted: boolean; text: string }[] = [];\n for (const segment of segments) {\n const quoted = has(segment.active, SpanType.Quote);\n const rendered = renderMarkdownSegment(segment, content, options);\n const previous = groups[groups.length - 1];\n if (previous?.quoted === quoted) previous.text += rendered;\n else groups.push({ quoted, text: rendered });\n }\n\n let result = '';\n for (const [index, group] of groups.entries()) {\n const rendered = group.quoted\n ? group.text\n .split('\\n')\n .map((line) => `> ${line}`)\n .join('\\n')\n : group.text;\n const previous = groups[index - 1];\n if (\n result !== '' &&\n (group.quoted || previous?.quoted) &&\n !result.endsWith('\\n') &&\n !rendered.startsWith('\\n')\n ) {\n result += '\\n';\n }\n result += rendered;\n }\n return result;\n}\n\nfunction renderAnsiSegment(text: string, active: readonly Span[]): string {\n const codes: number[] = [];\n if (has(active, SpanType.Bold)) codes.push(1);\n if (has(active, SpanType.Italic)) codes.push(3);\n if (has(active, SpanType.Underline) || has(active, SpanType.Link)) codes.push(4);\n if (has(active, SpanType.Strike)) codes.push(9);\n if (has(active, SpanType.Spoiler)) codes.push(8);\n if (has(active, SpanType.Quote)) codes.push(2);\n if (has(active, SpanType.Mention) || has(active, SpanType.Hashtag)) codes.push(36);\n\n return codes.length === 0 ? text : `\\u001b[${codes.join(';')}m${text}\\u001b[0m`;\n}\n\n/**\n * Преобразует текст и wire-разметку API в безопасный HTML, Markdown или ANSI.\n *\n * Некорректные серверные spans игнорируются либо обрезаются по границам строки. Пересекающиеся\n * spans разбиваются на независимые сегменты, поэтому HTML остаётся корректно вложенным.\n * Отсутствующий массив считается пустым; формат по умолчанию — HTML.\n */\nexport function renderSpans(\n content: string,\n spans: readonly Span[] | null | undefined = [],\n options: RenderSpansOptions = {},\n): string {\n const normalized = normalizedSpans(content, spans);\n const boundaries = new Set<number>([0, content.length]);\n for (const item of normalized) {\n boundaries.add(item.start);\n boundaries.add(item.end);\n }\n\n const points = [...boundaries].sort((left, right) => left - right);\n const segments: RenderSegment[] = [];\n for (let index = 0; index < points.length - 1; index += 1) {\n const start = points[index];\n const end = points[index + 1];\n if (start === undefined || end === undefined || start === end) continue;\n\n const text = content.slice(start, end);\n const active = normalized\n .filter((item) => item.start <= start && item.end >= end)\n .map((item) => item.span);\n segments.push({ start, text, active });\n }\n\n const format = options.format ?? SpanRenderFormat.Html;\n if (format === SpanRenderFormat.Markdown) return renderMarkdown(segments, content, options);\n\n let result = '';\n for (const segment of segments) {\n switch (format) {\n case SpanRenderFormat.Html:\n result += renderHtmlSegment(segment.text, segment.active, content, options);\n break;\n case SpanRenderFormat.Ansi:\n result += renderAnsiSegment(segment.text, segment.active);\n break;\n }\n }\n\n return result;\n}\n"],"x_google_ignoreList":[32],"mappings":"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"}