itd-api 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -18
- package/dist/events/index.cjs +3 -3
- package/dist/events/index.d.cts +2 -2
- package/dist/events/index.d.ts +2 -2
- package/dist/events/index.js +3 -3
- package/dist/index.cjs +26 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -6
- package/dist/index.d.ts +9 -6
- package/dist/index.js +13 -8
- package/dist/index.js.map +1 -1
- package/dist/node/index.cjs +5 -4
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +2 -2
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.js +4 -3
- package/dist/node/index.js.map +1 -1
- package/dist/rest/index.cjs +16 -4
- package/dist/rest/index.cjs.map +1 -1
- package/dist/rest/index.d.cts +5 -3
- package/dist/rest/index.d.ts +5 -3
- package/dist/rest/index.js +9 -5
- package/dist/rest/index.js.map +1 -1
- package/dist/shared/{events-TfhKh26b.js → events-C5wnTXPT.js} +3 -3
- package/dist/shared/{events-TfhKh26b.js.map → events-C5wnTXPT.js.map} +1 -1
- package/dist/shared/{events-QyhQZrnC.d.ts → events-CqtkP65d.d.ts} +2 -2
- package/dist/shared/{events-Br2r7coi.d.cts → events-DGAWlCaI.d.cts} +2 -2
- package/dist/shared/{events-iTteVsd_.cjs → events-FZfnEez0.cjs} +3 -3
- package/dist/shared/{events-iTteVsd_.cjs.map → events-FZfnEez0.cjs.map} +1 -1
- package/dist/shared/{storage-BLmXmzKF.cjs → key-value-store-B5GYVEYZ.cjs} +1 -85
- package/dist/shared/key-value-store-B5GYVEYZ.cjs.map +1 -0
- package/dist/shared/{storage-CCxn0Kkv.js → key-value-store-Bt2PSMxY.js} +2 -68
- package/dist/shared/key-value-store-Bt2PSMxY.js.map +1 -0
- package/dist/shared/key-value-store-COZaNHZS.d.cts +69 -0
- package/dist/shared/key-value-store-COZaNHZS.d.ts +69 -0
- package/dist/shared/{multi-storage-DccjD7Ww.d.ts → multi-storage-BEL7tcKn.d.cts} +3 -2
- package/dist/shared/{multi-storage-CCUgkqvI.cjs → multi-storage-CDKLa956.cjs} +8 -7
- package/dist/shared/{multi-storage-CCUgkqvI.cjs.map → multi-storage-CDKLa956.cjs.map} +1 -1
- package/dist/shared/{multi-storage-CjAPB5Kq.d.cts → multi-storage-LkljOSzA.d.ts} +3 -2
- package/dist/shared/{multi-storage-1AEZTczk.js → multi-storage-SoW05uzO.js} +4 -3
- package/dist/shared/{multi-storage-1AEZTczk.js.map → multi-storage-SoW05uzO.js.map} +1 -1
- package/dist/shared/{options-BUfoHE9G.cjs → options-DOJYtoti.cjs} +2 -2
- package/dist/shared/{options-BUfoHE9G.cjs.map → options-DOJYtoti.cjs.map} +1 -1
- package/dist/shared/{options-BNP633A5.js → options-DhgGg7Ms.js} +2 -2
- package/dist/shared/{options-BNP633A5.js.map → options-DhgGg7Ms.js.map} +1 -1
- package/dist/shared/{redact-DB9EMGJx.cjs → redact-BP00URmQ.cjs} +15 -4
- package/dist/shared/{redact-DB9EMGJx.cjs.map → redact-BP00URmQ.cjs.map} +1 -1
- package/dist/shared/{redact-FNK29Fv-.js → redact-Ba5iSVYZ.js} +15 -4
- package/dist/shared/{redact-FNK29Fv-.js.map → redact-Ba5iSVYZ.js.map} +1 -1
- package/dist/shared/{render-DVB7T8pm.d.ts → render-B4qis7Sb.d.ts} +348 -3
- package/dist/shared/{render-BszcKF7S.d.cts → render-DDwy2Ame.d.cts} +348 -3
- package/dist/shared/{render-DNTESBSl.cjs → render-Dqi8Owqk.cjs} +457 -4
- package/dist/shared/render-Dqi8Owqk.cjs.map +1 -0
- package/dist/shared/{render-CiL1VS0b.js → render-hiWFh2s4.js} +411 -6
- package/dist/shared/render-hiWFh2s4.js.map +1 -0
- package/dist/shared/storage-BQrtKon9.d.cts +86 -0
- package/dist/shared/storage-CTRGT5wF.d.ts +86 -0
- package/dist/shared/storage-CfHKzVHf.js +71 -0
- package/dist/shared/storage-CfHKzVHf.js.map +1 -0
- package/dist/shared/storage-QNVxzpDB.cjs +88 -0
- package/dist/shared/storage-QNVxzpDB.cjs.map +1 -0
- package/dist/shared/{url-CFN1sT9S.cjs → url-Bsb3xE5U.cjs} +83 -4
- package/dist/shared/{url-CFN1sT9S.cjs.map → url-Bsb3xE5U.cjs.map} +1 -1
- package/dist/shared/{url-B3ocFlHj.js → url-DP33mp3s.js} +78 -5
- package/dist/shared/{url-B3ocFlHj.js.map → url-DP33mp3s.js.map} +1 -1
- package/dist/shared/{url-Be9jlWRv.d.ts → url-XBsMdAcv.d.cts} +75 -2
- package/dist/shared/{url-Be9jlWRv.d.cts → url-XBsMdAcv.d.ts} +75 -2
- package/dist/web/index.cjs +4 -3
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts +1 -1
- package/dist/web/index.d.ts +1 -1
- package/dist/web/index.js +2 -1
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/shared/render-CiL1VS0b.js.map +0 -1
- package/dist/shared/render-DNTESBSl.cjs.map +0 -1
- package/dist/shared/storage-BLmXmzKF.cjs.map +0 -1
- package/dist/shared/storage-CCxn0Kkv.js.map +0 -1
- package/dist/shared/storage-C_eICCep.d.cts +0 -152
- package/dist/shared/storage-C_eICCep.d.ts +0 -152
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"render-CiL1VS0b.js","names":["#base","#operations","#operationOwners","#bucketOwners","#bucketLimits","#bucketOverrides","#fetch","#openStream","#loadBuffer","#resolveBuffer","#resolveStream","#deps","#features","#serviceOwners","#disposed","#resources","#disposed","#runtime","#features","#resources","#options","#maxPages","#state","#finished","#pagesLoaded","#advance","#list","#context","#state","#uploadFiles","#replies","#files","#createBodyFactory","#filename","#posts","#status","#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"],"sources":["../../src/core/feature-catalog.ts","../../src/core/attachments/url-source.ts","../../src/core/attachments/resolver.ts","../../src/core/features.ts","../../src/core/managed-resources.ts","../../src/core/feature-host.ts","../../src/resources/pagination.ts","../../src/resources/base.ts","../../src/resources/notifications.ts","../../src/domain/time.ts","../../src/resources/status.ts","../../src/builders/base.ts","../../src/builders/comment.ts","../../src/resources/comments.ts","../../src/core/multipart.ts","../../src/domain/mime.ts","../../src/resources/files.ts","../../src/resources/hashtags.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/rest/resources.ts","../../src/core/attachments/factories.ts","../../src/models/guards.ts","../../src/models/status-helpers.ts","../../src/spans/render.ts"],"sourcesContent":["import type { OperationCatalog } from './catalog.js';\nimport { ItdConfigError } from './errors.js';\nimport type { OperationDefinition } from './operation.js';\nimport type { RateLimitBucketOverride } from './options.js';\n\n/** Описание операции, добавленной поверх базового каталога. @internal */\nexport interface RegisteredOperationDefinition extends OperationDefinition {\n readonly bucket: string;\n}\n\n/**\n * Изменяемый слой над каталогом конкретного API.\n *\n * Базовый каталог остаётся замороженным и общим для всех клиентов, а регистрации feature\n * принадлежат одному facade. Поэтому установка модуля в одном клиенте не меняет остальные.\n *\n * @internal\n */\nexport class ExtensibleOperationCatalog implements OperationCatalog {\n readonly #base: OperationCatalog;\n readonly #operations = new Map<string, RegisteredOperationDefinition>();\n readonly #operationOwners = new Map<string, string>();\n readonly #bucketOwners = new Map<string, string>();\n readonly #bucketLimits: Record<string, number>;\n readonly #bucketOverrides: Record<string, RateLimitBucketOverride>;\n\n constructor(base: OperationCatalog) {\n this.#base = base;\n this.#bucketLimits = { ...base.bucketLimits };\n this.#bucketOverrides = { ...base.bucketOverrides };\n }\n\n get bucketLimits(): Readonly<Record<string, number>> {\n return this.#bucketLimits;\n }\n\n get bucketOverrides(): Readonly<Record<string, RateLimitBucketOverride>> {\n return this.#bucketOverrides;\n }\n\n get defaultBucket(): string {\n return this.#base.defaultBucket;\n }\n\n definitionOf(id: string) {\n return this.#operations.get(id) ?? this.#base.definitionOf(id);\n }\n\n retrySafetyOf(id: string) {\n return this.#operations.get(id)?.retrySafety ?? this.#base.retrySafetyOf(id);\n }\n\n methodOf(id: string) {\n return this.#operations.get(id)?.method ?? this.#base.methodOf(id);\n }\n\n bucketOf(id: string): string {\n return this.#operations.get(id)?.bucket ?? this.#base.bucketOf(id);\n }\n\n isKnownBucket(name: string): boolean {\n return this.#bucketOwners.has(name) || this.#base.isKnownBucket(name);\n }\n\n /** Регистрирует принадлежащий feature бакет и возвращает откат регистрации. */\n registerBucket(owner: string, name: string, definition: RateLimitBucketOverride): () => void {\n if (this.#base.isKnownBucket(name) || this.#bucketOwners.has(name)) {\n throw new ItdConfigError(`feature «${owner}»: бакет «${name}» уже зарегистрирован`);\n }\n\n this.#bucketOwners.set(name, owner);\n if (definition.limit !== undefined) this.#bucketLimits[name] = definition.limit;\n if (definition.concurrency !== undefined || definition.rps !== undefined) {\n this.#bucketOverrides[name] = Object.freeze({\n ...(definition.concurrency === undefined ? {} : { concurrency: definition.concurrency }),\n ...(definition.rps === undefined ? {} : { rps: definition.rps }),\n });\n }\n\n return () => {\n if (this.#bucketOwners.get(name) !== owner) return;\n this.#bucketOwners.delete(name);\n delete this.#bucketLimits[name];\n delete this.#bucketOverrides[name];\n };\n }\n\n /** Регистрирует операцию и возвращает откат регистрации. */\n registerOperation(\n owner: string,\n id: string,\n definition: RegisteredOperationDefinition,\n ): () => void {\n const ownerPrefix = `${owner}.`;\n if (!id.startsWith(ownerPrefix) || id.length === ownerPrefix.length) {\n throw new ItdConfigError(\n `feature «${owner}»: операция «${id}» должна находиться в пространстве имён «${owner}»`,\n );\n }\n\n const occupiedBy = this.#operationOwners.get(id);\n if (occupiedBy !== undefined) {\n throw new ItdConfigError(\n `feature «${owner}»: операция «${id}» уже зарегистрирована feature «${occupiedBy}»`,\n );\n }\n\n if (this.#base.methodOf(id) !== undefined) {\n throw new ItdConfigError(\n `feature «${owner}»: операция «${id}» уже существует в базовом каталоге`,\n );\n }\n\n this.#operationOwners.set(id, owner);\n this.#operations.set(id, Object.freeze({ ...definition }));\n\n return () => {\n if (this.#operationOwners.get(id) !== owner) return;\n this.#operationOwners.delete(id);\n this.#operations.delete(id);\n };\n }\n}\n","import { ItdConfigError, ItdFileError, ItdFileErrorReason } from '../errors.js';\nimport { redactErrorCause, redactUrl } from '../redact.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(`«${redactUrl(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 const safeUrl = redactUrl(requested.href);\n throw new ItdFileError(`не удалось получить файл по адресу ${safeUrl}`, {\n reason: ItdFileErrorReason.Network,\n url: safeUrl,\n retryable: true,\n cause: redactErrorCause(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 const safeUrl = redactUrl(finalUrl.href);\n throw new ItdFileError(`источник ${safeUrl} ответил статусом ${response.status}`, {\n reason: ItdFileErrorReason.Http,\n url: safeUrl,\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 const safeUrl = redactUrl(url.href);\n throw new ItdFileError(`источник ${safeUrl} не предоставил потоковое тело`, {\n reason: ItdFileErrorReason.StreamUnavailable,\n url: safeUrl,\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, ItdError, ItdFileError, ItdFileErrorReason } from '../errors.js';\nimport { redactErrorCause } from '../redact.js';\nimport { isBlob, isFile } from '../runtime.js';\nimport { boundedFileStream, isBoundedFileStream, isReadableByteStream } from './bounded-stream.js';\nimport {\n type FileContent,\n type FileContext,\n type FileInput,\n FileTransferMode,\n type LazyFile,\n type StreamFile,\n type UrlFile,\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/** Дополнительные требования потребителя к подготовленному источнику. */\nexport interface ResolveFileOptions {\n /** Имя, которое имеет приоритет над именем источника. */\n readonly filename?: string | undefined;\n /** MIME-тип, который имеет приоритет над типом источника. */\n readonly contentType?: string | undefined;\n /** Дополнительный предел размера для любого вида источника. */\n readonly maxBytes?: number | undefined;\n /** Размер очереди библиотеки при потоковом чтении. */\n readonly streamBufferBytes?: number | undefined;\n /** Отклонить поток, размер которого нельзя узнать до чтения. */\n readonly requireKnownSize?: boolean | undefined;\n}\n\n/** Буфер, полностью принадлежащий вызывающему коду. */\nexport interface PreparedBufferSource {\n readonly mode: typeof FileTransferMode.Buffer;\n readonly blob: Blob;\n readonly size: number;\n readonly filename?: string | undefined;\n readonly contentType: string | undefined;\n}\n\n/**\n * Одна открытая сессия чтения потокового источника.\n *\n * Потребитель обязан вызвать {@link close} в `finally`. Повторный вызов безопасен и ждёт\n * тот же результат освобождения ресурсов.\n */\nexport interface PreparedStreamSource {\n readonly mode: typeof FileTransferMode.Stream;\n readonly stream: ReadableStream<Uint8Array>;\n readonly size: number | undefined;\n readonly filename?: string | undefined;\n readonly contentType: string | undefined;\n close(): Promise<void>;\n}\n\n/** Подготовленный файловый источник без правил конкретного протокола загрузки. */\nexport type PreparedFileSource = PreparedBufferSource | PreparedStreamSource;\n\n/** Контекст одной попытки открытия файлового источника. */\nexport interface ResolveFileContext {\n readonly signal?: AbortSignal | undefined;\n /** Номер попытки потребителя, начиная с 1. */\n readonly attempt?: number | undefined;\n}\n\n/** Узкий файловый порт, доступный подключаемым предметным модулям. */\nexport interface FileResolver {\n resolve(\n input: FileInput,\n options?: ResolveFileOptions,\n context?: ResolveFileContext,\n ): Promise<PreparedFileSource>;\n}\n\n/** Внутренний контракт нужен `FilesResource` для сохранения семантики повторных попыток. */\nexport interface InternalFileResolver extends FileResolver {\n mode(input: FileInput): FileTransferMode;\n}\n\n/** Возвращает функцию освобождения, которая исполняет действие не более одного раза. */\nfunction closeOnce(action: () => void | Promise<void>): () => Promise<void> {\n let closing: Promise<void> | undefined;\n return () => (closing ??= Promise.resolve().then(action));\n}\n\n/** Добавляет поле только для непустого значения, не выдумывая имя или MIME. */\nfunction optionalText<K extends 'filename' | 'contentType'>(\n name: K,\n value: string | undefined,\n): Partial<Record<K, string>> {\n const normalized = value?.trim();\n return normalized ? ({ [name]: normalized } as Partial<Record<K, string>>) : {};\n}\n\n/** Единая реализация распознавания и открытия `FileInput`. @internal */\nclass DefaultFileResolver implements InternalFileResolver {\n readonly #fetch: typeof fetch;\n\n constructor(fetchImplementation: typeof fetch) {\n this.#fetch = fetchImplementation;\n }\n\n mode(input: FileInput): FileTransferMode {\n if (typeof input !== 'object' || input === null) return FileTransferMode.Buffer;\n if ('open' in input) return FileTransferMode.Stream;\n if ('url' in input) return resolveFileStreamOptions(input).mode;\n return FileTransferMode.Buffer;\n }\n\n async resolve(\n input: FileInput,\n options: ResolveFileOptions = {},\n context: ResolveFileContext = {},\n ): Promise<PreparedFileSource> {\n const attempt = context.attempt ?? 1;\n if (!Number.isInteger(attempt) || attempt < 1) {\n throw new ItdConfigError(`attempt должен быть целым числом от 1, получено: ${attempt}`);\n }\n const maxBytes = optionalBytes(options.maxBytes, 'maxBytes');\n if (context.signal?.aborted) {\n throw context.signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n const fileContext: FileContext = {\n fetch: this.#fetch,\n attempt,\n ...(context.signal ? { signal: context.signal } : {}),\n };\n\n try {\n const source =\n this.mode(input) === FileTransferMode.Stream\n ? await this.#openStream(input, options, fileContext, maxBytes)\n : await this.#loadBuffer(input, options, fileContext, maxBytes);\n if (context.signal?.aborted) {\n if (source.mode === FileTransferMode.Stream) await source.close();\n throw context.signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n return source;\n } catch (error) {\n if (error instanceof ItdError || context.signal?.aborted) throw error;\n throw new ItdFileError('не удалось получить содержимое вложения', {\n reason: ItdFileErrorReason.Read,\n retryable: true,\n cause: redactErrorCause(error),\n });\n }\n }\n\n async #loadBuffer(\n input: FileInput,\n options: ResolveFileOptions,\n context: FileContext,\n maxBytes: number | undefined,\n ): Promise<PreparedBufferSource> {\n const content = await this.#resolveBuffer(input, context);\n if (typeof content !== 'object' || content === null || !('file' in content)) {\n throw new ItdConfigError('буферный источник должен вернуть объект с полем file');\n }\n if (\n !isBlob(content.file) &&\n !(content.file instanceof ArrayBuffer) &&\n !ArrayBuffer.isView(content.file)\n ) {\n throw new ItdConfigError('поле file должно содержать Blob, ArrayBuffer или Uint8Array');\n }\n\n const contentType =\n normalizeMimeType(options.contentType ?? content.contentType) ??\n normalizeMimeType(isBlob(content.file) ? content.file.type : undefined);\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 if (maxBytes !== undefined && blob.size > maxBytes) {\n throw fileTooLarge(undefined, maxBytes, blob.size);\n }\n\n return {\n mode: FileTransferMode.Buffer,\n blob,\n size: blob.size,\n ...optionalText('filename', options.filename ?? content.filename),\n contentType,\n };\n }\n\n async #openStream(\n input: FileInput,\n options: ResolveFileOptions,\n context: FileContext,\n maxBytes: number | undefined,\n ): Promise<PreparedStreamSource> {\n const opened = await this.#resolveStream(input, context);\n if (!opened || !isReadableByteStream(opened.stream)) {\n await opened?.close?.();\n throw new ItdConfigError('потоковый источник должен вернуть { stream: ReadableStream }');\n }\n\n let handedOff = false;\n const closeOpened = closeOnce(async () => {\n await opened.close?.();\n });\n try {\n optionalBytes(opened.size, 'size');\n if (maxBytes !== undefined && opened.size !== undefined && opened.size > maxBytes) {\n throw fileTooLarge(undefined, maxBytes, opened.size);\n }\n if (options.requireKnownSize === true && opened.size === undefined) {\n throw new ItdConfigError('размер потокового вложения должен быть известен до чтения');\n }\n\n const limits = resolveFileStreamOptions({\n mode: FileTransferMode.Stream,\n ...(maxBytes !== undefined ? { maxBytes } : {}),\n ...(options.streamBufferBytes !== undefined\n ? { streamBufferBytes: options.streamBufferBytes }\n : {}),\n });\n const keepExistingLimits =\n isBoundedFileStream(opened.stream) &&\n options.maxBytes === undefined &&\n options.streamBufferBytes === undefined;\n const stream = keepExistingLimits\n ? opened.stream\n : boundedFileStream(opened.stream, {\n ...(limits.maxBytes !== undefined ? { maxBytes: limits.maxBytes } : {}),\n streamBufferBytes: limits.streamBufferBytes,\n ...(context.signal ? { signal: context.signal } : {}),\n });\n let detachAbort: (() => void) | undefined;\n const close = closeOnce(async () => {\n detachAbort?.();\n detachAbort = undefined;\n await stream.cancel().catch(() => {});\n await closeOpened();\n });\n if (context.signal) {\n const onAbort = (): void => {\n void close().catch(() => {});\n };\n if (context.signal.aborted) {\n await close();\n throw context.signal.reason ?? new DOMException('Aborted', 'AbortError');\n } else {\n context.signal.addEventListener('abort', onAbort, { once: true });\n detachAbort = () => context.signal?.removeEventListener('abort', onAbort);\n }\n }\n handedOff = true;\n\n return {\n mode: FileTransferMode.Stream,\n stream,\n size: opened.size,\n ...optionalText('filename', options.filename ?? opened.filename),\n contentType: normalizeMimeType(options.contentType ?? opened.contentType),\n close,\n };\n } finally {\n if (!handedOff) await closeOpened();\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 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 throw new ItdConfigError(\n 'вложение не распознано: ожидается { file }, { url }, { load } или { open }',\n );\n }\n\n #resolveStream(input: FileInput, context: FileContext) {\n if (typeof input === 'object' && input !== null && 'open' in input) {\n return (input as StreamFile).open(context);\n }\n if (typeof input === 'object' && input !== null && 'url' in input) {\n const { url, ...urlOptions } = input as UrlFile;\n return openUrlFile(url, urlOptions, context);\n }\n throw new ItdConfigError('потоковое вложение должно иметь форму { open } или { url, mode }');\n }\n}\n\n/** Создаёт механизм подготовки файлов, привязанный к настроенному `fetch` клиента. @internal */\nexport function createFileResolver(fetchImplementation: typeof fetch): InternalFileResolver {\n return new DefaultFileResolver(fetchImplementation);\n}\n","import type { FileInput } from './attachments/contracts.js';\nimport type {\n FileResolver,\n PreparedFileSource,\n ResolveFileContext,\n ResolveFileOptions,\n} from './attachments/resolver.js';\nimport type { ItdClock } from './clock.js';\nimport type { ClientConnection } from './connection.js';\nimport { ItdAbortError, ItdConfigError, ItdStateError } from './errors.js';\nimport type { HttpClient } from './execution/http.js';\nimport type { ExtensibleOperationCatalog } from './feature-catalog.js';\nimport type { ManagedClientResource } from './managed-resources.js';\nimport {\n defineOperation,\n type FeatureOperationId,\n identityResult,\n type OperationAnnotations,\n type OperationContract,\n type OperationMethod,\n RetrySafety,\n} from './operation.js';\nimport type { Logger, RateLimitBucketOverride, RawRequestOptions } from './options.js';\nimport { mergeService, type ServiceDefinition, type ServiceRegistry } from './services.js';\n\n/** Параметры запроса feature: маршрут задаёт ресурс, transport metadata — manifest. */\nexport type FeatureRequestOptions = Omit<\n RawRequestOptions,\n 'operationId' | 'method' | 'service' | 'baseUrl' | 'retrySafety' | 'rateLimitBucket'\n>;\n\n/** Операция одного feature. Локальный ключ используется в {@link FeatureContext.request}. */\nexport interface FeatureOperationDefinition<T = unknown> {\n readonly method: OperationMethod;\n readonly retrySafety: RetrySafety;\n /** Имя сервиса из {@link ClientFeature.services}; без него используется основной API. */\n readonly service?: string | undefined;\n /** Локальное имя бакета из {@link ClientFeature.buckets}. */\n readonly bucket?: string | undefined;\n /** Преобразует тело ответа в публичный результат операции. По умолчанию возвращает тело без изменений. */\n readonly read?: OperationContract<T>['read'] | undefined;\n /** Необязательные метаданные, интерпретируемые подключёнными плагинами. */\n readonly annotations?: OperationAnnotations | undefined;\n}\n\n/** Начальные ограничения нового серверного счётчика feature. */\nexport interface FeatureBucketDefinition extends RateLimitBucketOverride {}\n\n/** Результат синхронной сборки API feature. */\nexport interface FeatureInstallation<TApi> {\n readonly api: TApi;\n /** Не терминальная остановка фоновых ресурсов при `client.close()`. */\n readonly close?: (() => void | Promise<void>) | undefined;\n /** Терминальное освобождение ресурсов при `client.dispose()`. */\n readonly dispose?: (() => void | Promise<void>) | undefined;\n}\n\n/**\n * Подключаемый предметный модуль клиента.\n *\n * Manifest регистрируется атомарно до `setup()`. `setup()` не должен ходить в сеть: он создаёт\n * только типизированный facade, а запросы выполняются лениво его методами.\n */\nexport interface ClientFeature<TApi> {\n /** Пространство имён модуля; глобальные ID операций начинаются с `<name>.`. */\n readonly name: string;\n readonly services?: readonly ServiceDefinition[] | undefined;\n /** Операции по локальным именам; глобальный ID каждой строит реестр. */\n readonly operations: Readonly<Record<string, FeatureOperationDefinition>>;\n readonly buckets?: Readonly<Record<string, FeatureBucketDefinition>> | undefined;\n setup(context: FeatureContext): FeatureInstallation<TApi>;\n}\n\n/** Доступ подключаемого модуля к клиенту. */\nexport interface FeatureContext {\n readonly featureName: string;\n readonly baseUrl: string;\n readonly signal: AbortSignal;\n readonly clock: ItdClock;\n readonly logger: Logger | undefined;\n /** Открывает файловый источник без правил конкретного протокола загрузки. */\n readonly files: FileResolver;\n\n /**\n * Выполняет объявленную операцию через авторизацию, плагины, повторы и очередь клиента.\n *\n * Форма результата задаётся один раз полем `read` в описании операции. Плагины получают\n * готовый результат и не видят функцию преобразования.\n */\n request<T = unknown>(operation: string, options: FeatureRequestOptions): Promise<T>;\n /** Возвращает фактический URL объявленного сервиса с учётом настроек клиента. */\n serviceBaseUrl(name: string): string;\n /** Окружение долговременного соединения объявленного сервиса. */\n connection(name: string): ClientConnection;\n /** Регистрирует активный долгоживущий ресурс до его остановки. */\n manage(resource: ManagedClientResource): () => void;\n /** Проверяет, что создавший модуль клиент ещё работает. */\n assertActive(action: string): void;\n}\n\ninterface InstalledFeature {\n readonly name: string;\n readonly controller: AbortController;\n readonly close: (() => void | Promise<void>) | undefined;\n readonly dispose: (() => void | Promise<void>) | undefined;\n readonly cleanup: readonly (() => void)[];\n}\n\n/** Зависимости реестра, принадлежащие одному клиенту. @internal */\nexport interface FeatureRegistryDeps {\n readonly http: HttpClient;\n readonly services: ServiceRegistry;\n /** Настройки сервисов до применения значений по умолчанию. */\n readonly serviceOverrides: readonly ServiceDefinition[];\n readonly catalog: ExtensibleOperationCatalog;\n readonly baseUrl: string;\n readonly clock: ItdClock;\n readonly logger: Logger | undefined;\n readonly files: FileResolver;\n readonly assertActive: (action: string) => void;\n readonly connection: (serviceName?: string) => ClientConnection;\n readonly manage: (resource: ManagedClientResource) => () => void;\n readonly registerBucket: (\n name: string,\n definition: FeatureBucketDefinition,\n ) => (() => void) | undefined;\n}\n\ninterface ResolvedFeatureOperation {\n readonly contract: OperationContract<unknown, FeatureOperationId>;\n readonly service: string | undefined;\n}\n\nfunction requireName(value: unknown, kind: string): string {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new ItdConfigError(`${kind} должен иметь непустое имя`);\n }\n return value.trim();\n}\n\nfunction requireFeatureName(value: unknown): string {\n const name = requireName(value, 'Feature');\n if (!/^[a-z][a-z0-9-]*$/.test(name)) {\n throw new ItdConfigError(`имя feature «${name}» должно соответствовать [a-z][a-z0-9-]*`);\n }\n return name;\n}\n\nfunction validateBucket(owner: string, localName: string, bucket: FeatureBucketDefinition): void {\n if (typeof bucket !== 'object' || bucket === null || Array.isArray(bucket)) {\n throw new ItdConfigError(`feature «${owner}»: бакет «${localName}» должен быть объектом`);\n }\n if (bucket.limit !== undefined && (!Number.isFinite(bucket.limit) || bucket.limit <= 0)) {\n throw new ItdConfigError(\n `feature «${owner}»: limit бакета «${localName}» должен быть положительным числом`,\n );\n }\n if (\n bucket.concurrency !== undefined &&\n (!Number.isInteger(bucket.concurrency) || bucket.concurrency < 1)\n ) {\n throw new ItdConfigError(\n `feature «${owner}»: concurrency бакета «${localName}» должен быть целым числом от 1`,\n );\n }\n if (bucket.rps !== undefined && (!Number.isFinite(bucket.rps) || bucket.rps <= 0)) {\n throw new ItdConfigError(\n `feature «${owner}»: rps бакета «${localName}» должен быть положительным числом`,\n );\n }\n}\n\n/** Реестр подключаемых модулей клиента. @internal */\nexport class FeatureRegistry {\n readonly #deps: FeatureRegistryDeps;\n readonly #features = new Map<string, InstalledFeature>();\n readonly #serviceOwners = new Map<string, string>();\n #disposed = false;\n\n constructor(deps: FeatureRegistryDeps) {\n this.#deps = deps;\n }\n\n install<TApi>(feature: ClientFeature<TApi>): TApi {\n this.#deps.assertActive('установить feature');\n if (this.#disposed) throw new ItdStateError('Реестр feature уже освобождён');\n if (typeof feature !== 'object' || feature === null) {\n throw new ItdConfigError('feature должен быть объектом');\n }\n\n const name = requireFeatureName(feature.name);\n if (this.#features.has(name)) {\n throw new ItdConfigError(`feature «${name}» уже установлен`);\n }\n if (typeof feature.setup !== 'function') {\n throw new ItdConfigError(`feature «${name}» должен предоставлять setup()`);\n }\n if (\n typeof feature.operations !== 'object' ||\n feature.operations === null ||\n Array.isArray(feature.operations)\n ) {\n throw new ItdConfigError(`feature «${name}» должен объявлять operations`);\n }\n if (feature.services !== undefined && !Array.isArray(feature.services)) {\n throw new ItdConfigError(`feature «${name}»: services должен быть массивом`);\n }\n if (\n feature.buckets !== undefined &&\n (typeof feature.buckets !== 'object' ||\n feature.buckets === null ||\n Array.isArray(feature.buckets))\n ) {\n throw new ItdConfigError(`feature «${name}»: buckets должен быть объектом`);\n }\n\n const rollback: Array<() => void> = [];\n const services = new Set<string>();\n const bucketNames = new Map<string, string>();\n const operations = new Map<string, ResolvedFeatureOperation>();\n const controller = new AbortController();\n let committed = false;\n const reportRollbackError = (message: string, error: unknown): void => {\n try {\n this.#deps.logger?.error(message, error);\n } catch {\n // Ошибка записи в журнал не заменяет ошибку установки.\n }\n };\n\n try {\n for (const service of feature.services ?? []) {\n const serviceName = requireName(service?.name, `Сервис feature «${name}»`);\n const owner = this.#serviceOwners.get(serviceName);\n if (owner !== undefined) {\n throw new ItdConfigError(\n `feature «${name}»: сервис «${serviceName}» уже принадлежит feature «${owner}»`,\n );\n }\n if (services.has(serviceName)) {\n throw new ItdConfigError(`feature «${name}»: сервис «${serviceName}» объявлен повторно`);\n }\n\n const existed = this.#deps.services.has(serviceName);\n if (!existed) {\n this.#deps.services.define(service);\n rollback.push(() => void this.#deps.services.delete(serviceName));\n } else {\n const override = this.#deps.serviceOverrides.find(\n (candidate) => candidate.name.trim() === serviceName,\n );\n if (override) {\n const previous = this.#deps.services.replace(mergeService(service, override));\n rollback.push(() => void this.#deps.services.replace(previous));\n }\n }\n this.#serviceOwners.set(serviceName, name);\n rollback.push(() => {\n if (this.#serviceOwners.get(serviceName) === name)\n this.#serviceOwners.delete(serviceName);\n });\n services.add(serviceName);\n }\n\n for (const [rawLocalName, bucket] of Object.entries(feature.buckets ?? {})) {\n const localName = requireName(rawLocalName, `Бакет feature «${name}»`);\n validateBucket(name, localName, bucket);\n const globalName = `feature:${name}/${localName}`;\n const unregisterCatalog = this.#deps.catalog.registerBucket(name, globalName, bucket);\n rollback.push(unregisterCatalog);\n const unregisterQueue = this.#deps.registerBucket(globalName, bucket);\n if (unregisterQueue) rollback.push(unregisterQueue);\n bucketNames.set(localName, globalName);\n }\n\n for (const [rawLocalName, definition] of Object.entries(feature.operations)) {\n const localName = requireName(rawLocalName, `Операция feature «${name}»`);\n if (typeof definition !== 'object' || definition === null) {\n throw new ItdConfigError(\n `feature «${name}»: операция «${localName}» должна быть объектом`,\n );\n }\n const methods: readonly OperationMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];\n if (!methods.includes(definition.method)) {\n throw new ItdConfigError(\n `feature «${name}»: операция «${localName}» содержит неизвестный HTTP-метод`,\n );\n }\n const retrySafety: readonly RetrySafety[] = Object.values(RetrySafety);\n if (!retrySafety.includes(definition.retrySafety)) {\n throw new ItdConfigError(\n `feature «${name}»: операция «${localName}» содержит неизвестную retrySafety`,\n );\n }\n if (definition.service !== undefined && typeof definition.service !== 'string') {\n throw new ItdConfigError(\n `feature «${name}»: service операции «${localName}» должен быть строкой`,\n );\n }\n if (definition.bucket !== undefined && typeof definition.bucket !== 'string') {\n throw new ItdConfigError(\n `feature «${name}»: bucket операции «${localName}» должен быть строкой`,\n );\n }\n if (definition.read !== undefined && typeof definition.read !== 'function') {\n throw new ItdConfigError(\n `feature «${name}»: read операции «${localName}» должна быть функцией`,\n );\n }\n if (\n definition.annotations !== undefined &&\n (typeof definition.annotations !== 'object' ||\n definition.annotations === null ||\n Array.isArray(definition.annotations))\n ) {\n throw new ItdConfigError(\n `feature «${name}»: annotations операции «${localName}» должны быть объектом`,\n );\n }\n if (definition.service !== undefined && !services.has(definition.service)) {\n throw new ItdConfigError(\n `feature «${name}»: операция «${localName}» ссылается на необъявленный сервис «${definition.service}»`,\n );\n }\n\n const bucket =\n definition.bucket === undefined\n ? this.#deps.catalog.defaultBucket\n : bucketNames.get(definition.bucket);\n if (bucket === undefined) {\n throw new ItdConfigError(\n `feature «${name}»: операция «${localName}» ссылается на необъявленный бакет «${definition.bucket}»`,\n );\n }\n\n const operationId = `${name}.${localName}` as FeatureOperationId;\n const contract = defineOperation(\n operationId,\n {\n method: definition.method,\n retrySafety: definition.retrySafety,\n bucket,\n ...(definition.annotations === undefined\n ? {}\n : { annotations: definition.annotations }),\n },\n definition.read ?? identityResult,\n );\n const unregister = this.#deps.catalog.registerOperation(name, operationId, {\n method: contract.method,\n retrySafety: contract.retrySafety,\n bucket,\n ...(contract.annotations === undefined ? {} : { annotations: contract.annotations }),\n });\n rollback.push(unregister);\n operations.set(localName, {\n contract,\n service: definition.service,\n });\n }\n\n const context: FeatureContext = Object.freeze({\n featureName: name,\n baseUrl: this.#deps.baseUrl,\n signal: controller.signal,\n clock: this.#deps.clock,\n logger: this.#deps.logger,\n files: Object.freeze({\n resolve: (\n input: FileInput,\n options?: ResolveFileOptions,\n context?: ResolveFileContext,\n ): Promise<PreparedFileSource> => {\n try {\n this.#deps.assertActive(`открыть файловый источник feature «${name}»`);\n if (!committed || controller.signal.aborted) {\n throw new ItdStateError(`feature «${name}» не активен`);\n }\n return this.#deps.files.resolve(input, options, context);\n } catch (error) {\n return Promise.reject(error);\n }\n },\n }),\n request: <T>(operation: string, options: FeatureRequestOptions): Promise<T> => {\n try {\n this.#deps.assertActive(`выполнить операцию feature «${name}»`);\n if (!committed || controller.signal.aborted) {\n throw new ItdStateError(`feature «${name}» не активен`);\n }\n const resolved = operations.get(operation);\n if (!resolved) {\n throw new ItdConfigError(`feature «${name}» не объявляет операцию «${operation}»`);\n }\n const scoped = { ...options } as Partial<RawRequestOptions>;\n delete scoped.operationId;\n delete scoped.method;\n delete scoped.service;\n delete scoped.baseUrl;\n delete scoped.retrySafety;\n delete scoped.rateLimitBucket;\n return this.#deps.http.execute(resolved.contract, {\n ...(scoped as FeatureRequestOptions),\n ...(resolved.service === undefined ? {} : { service: resolved.service }),\n }) as Promise<T>;\n } catch (error) {\n return Promise.reject(error);\n }\n },\n serviceBaseUrl: (serviceName: string): string => {\n if (!services.has(serviceName)) {\n throw new ItdConfigError(`feature «${name}» не объявляет сервис «${serviceName}»`);\n }\n return this.#deps.services.resolveBaseUrl(serviceName);\n },\n connection: (serviceName: string): ClientConnection => {\n if (!services.has(serviceName)) {\n throw new ItdConfigError(`feature «${name}» не объявляет сервис «${serviceName}»`);\n }\n return this.#deps.connection(serviceName);\n },\n manage: (resource: ManagedClientResource): (() => void) => {\n this.#deps.assertActive(`зарегистрировать ресурс feature «${name}»`);\n if (controller.signal.aborted) {\n throw new ItdStateError(`feature «${name}» не активен`);\n }\n const unregister = this.#deps.manage(resource);\n let registered = true;\n const release = (): void => {\n if (!registered) return;\n registered = false;\n unregister();\n };\n if (!committed) {\n rollback.push(() => {\n if (!registered) return;\n if (committed) {\n release();\n return;\n }\n\n try {\n resource.stop();\n } catch (error) {\n reportRollbackError(\n `Не удалось остановить ресурс «${resource.kind}» при откате feature «${name}»`,\n error,\n );\n } finally {\n release();\n }\n void Promise.resolve()\n .then(() => resource.drain())\n .catch((error: unknown) => {\n reportRollbackError(\n `Не удалось освободить ресурс «${resource.kind}» при откате feature «${name}»`,\n error,\n );\n });\n });\n }\n return release;\n },\n assertActive: (action: string): void => {\n this.#deps.assertActive(action);\n if (!committed || controller.signal.aborted) {\n throw new ItdStateError(`feature «${name}» не активен`);\n }\n },\n });\n\n const installation = feature.setup(context);\n if (typeof installation !== 'object' || installation === null || !('api' in installation)) {\n throw new ItdConfigError(`setup() feature «${name}» должен вернуть объект с полем api`);\n }\n if (installation.close !== undefined && typeof installation.close !== 'function') {\n throw new ItdConfigError(`feature «${name}»: close должен быть функцией`);\n }\n if (installation.dispose !== undefined && typeof installation.dispose !== 'function') {\n throw new ItdConfigError(`feature «${name}»: dispose должен быть функцией`);\n }\n\n this.#features.set(name, {\n name,\n controller,\n close: installation.close,\n dispose: installation.dispose,\n cleanup: [...rollback],\n });\n committed = true;\n return installation.api;\n } catch (error) {\n controller.abort(new ItdAbortError(`Установка feature «${name}» отменена`));\n for (const undo of rollback.reverse()) undo();\n throw error;\n }\n }\n\n names(): string[] {\n return [...this.#features.keys()];\n }\n\n has(name: string): boolean {\n return this.#features.has(name);\n }\n\n async close(): Promise<void> {\n const results = await Promise.allSettled(\n [...this.#features.values()].reverse().map(async (feature) => feature.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) throw new AggregateError(errors, 'Не удалось закрыть feature');\n }\n\n async dispose(): Promise<void> {\n if (this.#disposed) return;\n this.#disposed = true;\n const errors: unknown[] = [];\n const disposals: Promise<void>[] = [];\n const features = [...this.#features.values()].reverse();\n\n // Все модули переводятся в терминальное состояние до ожидания чужого кода. Один\n // зависший dispose не должен помешать отмене и очистке остальных модулей.\n for (const feature of features) {\n feature.controller.abort(new ItdAbortError(`Feature «${feature.name}» освобождён`));\n try {\n disposals.push(Promise.resolve(feature.dispose?.()).then(() => undefined));\n } catch (error) {\n errors.push(error);\n }\n }\n\n for (const feature of features) {\n for (const cleanup of [...feature.cleanup].reverse()) {\n try {\n cleanup();\n } catch (error) {\n errors.push(error);\n }\n }\n }\n this.#features.clear();\n\n const results = await Promise.allSettled(disposals);\n for (const result of results) {\n if (result.status === 'rejected') errors.push(result.reason);\n }\n if (errors.length > 0) throw new AggregateError(errors, 'Не удалось освободить feature');\n }\n}\n","import { ItdConfigError, ItdStateError } from './errors.js';\n\n/** Долгоживущий ресурс клиента. */\nexport interface ManagedClientResource {\n /** Короткое имя для диагностики зависшей остановки. */\n readonly kind: string;\n /** Синхронно прекращает принимать новую работу и запускает отмену. */\n stop(): void;\n /** Завершается после остановки обработчиков и освобождения внешних ресурсов. */\n drain(): Promise<void>;\n}\n\nexport interface ManagedResourceSnapshot {\n readonly resources: readonly ManagedClientResource[];\n readonly errors: readonly unknown[];\n}\n\n/** Реестр активных ресурсов клиента. @internal */\nexport class ManagedResourceRegistry {\n readonly #resources = new Set<ManagedClientResource>();\n #disposed = false;\n\n register(resource: ManagedClientResource): () => void {\n if (this.#disposed) throw new ItdStateError('Lifecycle клиента уже освобождён');\n if (\n typeof resource !== 'object' ||\n resource === null ||\n typeof resource.kind !== 'string' ||\n resource.kind.trim() === '' ||\n typeof resource.stop !== 'function' ||\n typeof resource.drain !== 'function'\n ) {\n throw new ItdConfigError('Managed resource должен предоставлять kind, stop() и drain()');\n }\n\n this.#resources.add(resource);\n let registered = true;\n return () => {\n if (!registered) return;\n registered = false;\n this.#resources.delete(resource);\n };\n }\n\n /** Останавливает snapshot синхронно, чтобы новые auth-данные ещё не были опубликованы. */\n stopAll(): ManagedResourceSnapshot {\n const resources = [...this.#resources];\n const errors: unknown[] = [];\n for (const resource of resources) {\n try {\n resource.stop();\n } catch (error) {\n errors.push(error);\n }\n }\n return { resources, errors };\n }\n\n dispose(): void {\n this.#disposed = true;\n this.#resources.clear();\n }\n}\n","import { createFileResolver, type InternalFileResolver } from './attachments/resolver.js';\nimport { createDeadline } from './clock.js';\nimport { ItdStateError } from './errors.js';\nimport type { ClientRuntime } from './execution/client-runtime.js';\nimport type { ExtensibleOperationCatalog } from './feature-catalog.js';\nimport { type ClientFeature, FeatureRegistry } from './features.js';\nimport { type ManagedClientResource, ManagedResourceRegistry } from './managed-resources.js';\n\n/** Зависимости подключаемых модулей клиента. @internal */\nexport interface FeatureHostOptions {\n readonly catalog: ExtensibleOperationCatalog;\n readonly assertActive: (action: string) => void;\n}\n\n/**\n * Управляет подключаемыми модулями и их ресурсами.\n *\n * @internal\n */\nexport class ClientFeatureHost {\n readonly #runtime: ClientRuntime;\n readonly #features: FeatureRegistry;\n readonly #resources = new ManagedResourceRegistry();\n /** Общий файловый порт ресурсов и подключаемых модулей этого клиента. @internal */\n readonly files: InternalFileResolver;\n\n constructor(runtime: ClientRuntime, options: FeatureHostOptions) {\n this.#runtime = runtime;\n this.files = createFileResolver(runtime.config.fetch);\n this.#features = new FeatureRegistry({\n http: runtime.http,\n services: runtime.services,\n serviceOverrides: runtime.config.services,\n catalog: options.catalog,\n baseUrl: runtime.config.baseUrl,\n clock: runtime.config.clock,\n logger: runtime.config.logger,\n files: this.files,\n assertActive: options.assertActive,\n connection: (serviceName) => runtime.connection(serviceName),\n manage: (resource) => this.#resources.register(resource),\n registerBucket: (name, definition) => runtime.registerRateLimitBucket(name, definition),\n });\n }\n\n install<TApi>(feature: ClientFeature<TApi>): TApi {\n return this.#features.install(feature);\n }\n\n names(): string[] {\n return this.#features.names();\n }\n\n has(name: string): boolean {\n return this.#features.has(name);\n }\n\n /** Регистрирует ресурс самого клиента. @internal */\n manage(resource: ManagedClientResource): () => void {\n return this.#resources.register(resource);\n }\n\n /** Останавливает ресурсы перед сменой авторизации. */\n stop(): void {\n const { errors } = this.#resources.stopAll();\n if (errors.length > 0) {\n this.#runtime.config.logger?.error('Не удалось остановить ресурсы клиента', errors);\n }\n }\n\n /** Останавливает ресурсы и ждёт функции закрытия модулей. */\n async close(): Promise<void> {\n const snapshot = this.#resources.stopAll();\n const errors = [...snapshot.errors];\n const deadline = createDeadline(\n this.#runtime.config.shutdownTimeout,\n this.#runtime.config.clock,\n );\n const tasks: Array<{ kind: string; promise: Promise<unknown> }> = [\n ...snapshot.resources.map((resource) => ({\n kind: resource.kind,\n promise: Promise.resolve().then(() => resource.drain()),\n })),\n {\n kind: 'feature lifecycle',\n promise: Promise.resolve().then(() => this.#features.close()),\n },\n ];\n\n try {\n const settled = await Promise.all(\n tasks.map(async ({ kind, promise }) => {\n const result = promise.catch((error: unknown) => {\n errors.push(error);\n });\n return (await deadline.wait(result)) ? undefined : kind;\n }),\n );\n const stuck = settled.filter((kind): kind is string => kind !== undefined);\n if (stuck.length > 0) {\n errors.push(\n new ItdStateError(\n `ресурсы (${stuck.join(', ')}) не завершились за ${this.#runtime.config.shutdownTimeout} мс`,\n ),\n );\n }\n } finally {\n deadline.cancel();\n }\n\n if (errors.length === 1) throw errors[0];\n if (errors.length > 1) throw new AggregateError(errors, 'Не удалось закрыть feature-host');\n }\n\n async dispose(): Promise<void> {\n this.#resources.dispose();\n const deadline = createDeadline(\n this.#runtime.config.shutdownTimeout,\n this.#runtime.config.clock,\n );\n let failed = false;\n let failure: unknown;\n const settled = this.#features.dispose().catch((error: unknown) => {\n failed = true;\n failure = error;\n });\n\n try {\n if (!(await deadline.wait(settled))) {\n throw new ItdStateError(\n `освобождение подключаемых модулей не завершилось за ${this.#runtime.config.shutdownTimeout} мс`,\n );\n }\n } finally {\n deadline.cancel();\n }\n\n if (failed) throw failure;\n }\n}\n\nexport function createFeatureHost(\n runtime: ClientRuntime,\n options: FeatureHostOptions,\n): ClientFeatureHost {\n return new ClientFeatureHost(runtime, options);\n}\n","import { ItdConfigError } from '../core/errors.js';\nimport type { OperationContract } from '../core/operation.js';\nimport type { OperationRequestOptions } from '../core/options.js';\nimport { pickArray, pickBoolean, pickNumber, pickObject, pickString } from '../core/unwrap.js';\nimport { type BuiltInOperationId, defineBuiltInOperation } from '../domain/operations.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/** Создаёт единый контракт пагинированной операции. */\nexport function pageOperation<T, TId extends BuiltInOperationId = BuiltInOperationId>(\n id: TId,\n read: (body: unknown, request: Readonly<OperationRequestOptions>) => Page<T>,\n): OperationContract<Page<T>, TId> {\n return defineBuiltInOperation<Page<T>, TId>(id, read);\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, HttpOperationOptions } from '../core/execution/http.js';\nimport type { OperationContract } from '../core/operation.js';\nimport type { PaginationOptions, RequestOptions } from '../core/options.js';\nimport type { QueryParams } from '../core/url.js';\nimport type { BuiltInOperationId } from '../domain/operations.js';\nimport type { Page, PageState, PaginationMode } from './pagination.js';\nimport { Paginator } from './pagination.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 operation:\n | OperationContract<Page<T>, BuiltInOperationId>\n | ((params: P) => OperationContract<Page<T>, BuiltInOperationId>);\n /** Путь эндпоинта. */\n path: (params: P) => string;\n /** Параметры запроса без полей пагинации — их добавит перебор. */\n query: (params: P) => QueryParams;\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 protected voidOperation(\n operation: OperationContract<void, BuiltInOperationId>,\n options: HttpOperationOptions,\n ): Promise<void> {\n return this.http.execute(operation, options);\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 * operation: POSTS_LIST,\n * path: () => '/api/posts',\n * query: (p) => ({ tab: p.tab, limit: p.limit }),\n * start: (p) => (p.cursor ? { cursor: p.cursor } : {}),\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 operation =\n typeof spec.operation === 'function' ? spec.operation(params) : spec.operation;\n return this.http.execute(operation, {\n path: spec.path(params),\n query: withPageState(spec.query(params), state),\n ...options,\n });\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 type { PaginationOptions, RequestOptions } from '../core/options.js';\nimport { pickBoolean, pickNumber } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport { defineBuiltInOperation } from '../domain/operations.js';\nimport type { Notification, NotificationSettings } from '../models/notifications.js';\nimport { normalizeNotification } from '../notifications/normalize.js';\nimport { BaseResource } from './base.js';\nimport {\n type Page,\n PaginationMode,\n type Paginator,\n pageOperation,\n readOffsetPage,\n} from './pagination.js';\n\nconst NOTIFICATIONS_LIST = pageOperation<Notification>('notifications.list', (body, request) => {\n const offset = Number(request.query?.offset ?? 0);\n const page = readOffsetPage<unknown>(body, 'notifications', Number.isFinite(offset) ? offset : 0);\n return { ...page, items: page.items.map(normalizeNotification) };\n});\nconst NOTIFICATIONS_COUNT = defineBuiltInOperation<number>('notifications.count', (body) =>\n pickNumber(body, 'count', 0),\n);\nconst markedCountOperation = <\n TId extends\n | 'notifications.markRead'\n | 'notifications.markReadBatch'\n | 'notifications.markAllRead',\n>(\n id: TId,\n) => defineBuiltInOperation<number, TId>(id, (body) => pickNumber(body, 'markedCount', 0));\nconst NOTIFICATIONS_MARK_READ = markedCountOperation('notifications.markRead');\nconst NOTIFICATIONS_MARK_READ_BATCH = markedCountOperation('notifications.markReadBatch');\nconst NOTIFICATIONS_MARK_ALL_READ = markedCountOperation('notifications.markAllRead');\nconst NOTIFICATIONS_GET_SETTINGS = defineBuiltInOperation<NotificationSettings>(\n 'notifications.getSettings',\n readSettings,\n);\nconst NOTIFICATIONS_UPDATE_SETTINGS = defineBuiltInOperation<NotificationSettings>(\n 'notifications.updateSettings',\n readSettings,\n);\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 operation: NOTIFICATIONS_LIST,\n // Завершающий слэш обязателен: без него сервер отвечает ошибкой.\n path: () => '/api/notifications/',\n query: (p) => ({ limit: p.limit }),\n start: (p) => ({ offset: p.offset ?? 0 }),\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 count(options: RequestOptions = {}): Promise<number> {\n return this.http.execute(NOTIFICATIONS_COUNT, {\n path: '/api/notifications/count',\n ...options,\n });\n }\n\n /**\n * Отмечает уведомление прочитанным.\n *\n * @returns сколько записей отметил сервер\n */\n markRead(notificationId: string, options: RequestOptions = {}): Promise<number> {\n return this.http.execute(NOTIFICATIONS_MARK_READ, {\n path: `/api/notifications/${encodePathSegment(notificationId, 'notificationId')}/read`,\n ...options,\n });\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 marked += await this.http.execute(NOTIFICATIONS_MARK_READ_BATCH, {\n path: '/api/notifications/read-batch',\n body: { ids: chunk },\n ...options,\n });\n }\n\n return marked;\n }\n\n /** Отмечает прочитанными все уведомления. */\n markAllRead(options: RequestOptions = {}): Promise<number> {\n return this.http.execute(NOTIFICATIONS_MARK_ALL_READ, {\n path: '/api/notifications/read-all',\n ...options,\n });\n }\n\n /** Загружает настройки уведомлений. */\n getSettings(options: RequestOptions = {}): Promise<NotificationSettings> {\n return this.http.execute(NOTIFICATIONS_GET_SETTINGS, {\n path: '/api/notifications/settings',\n ...options,\n });\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 return this.http.execute(NOTIFICATIONS_UPDATE_SETTINGS, {\n path: '/api/notifications/settings',\n body: payload,\n ...options,\n });\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 { DEFAULT_STATUS_BASE_URL, STATUS_SERVICE } from '../core/config.js';\nimport type { ClientFeature, FeatureContext } from '../core/features.js';\nimport { RetrySafety } from '../core/operation.js';\nimport type { RequestOptions } from '../core/options.js';\nimport { isRecord } from '../core/unwrap.js';\nimport { utcStampToIso } from '../domain/time.js';\nimport type { PlatformStatus, ServiceStatus } from '../models/status.js';\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/** API встроенного status feature. @internal */\nexport class StatusResource {\n readonly #context: FeatureContext;\n\n constructor(context: FeatureContext) {\n this.#context = context;\n }\n\n get(options: RequestOptions = {}): Promise<PlatformStatus> {\n return this.#context.request<PlatformStatus>('get', {\n path: '/api/status',\n ...options,\n });\n }\n}\n\n/** Встроенный модуль страницы состояния платформы. @internal */\nexport function createStatusFeature(): ClientFeature<StatusResource> {\n return {\n name: 'status',\n services: [\n {\n name: STATUS_SERVICE,\n baseUrl: DEFAULT_STATUS_BASE_URL,\n auth: false,\n },\n ],\n operations: {\n get: {\n method: 'GET',\n retrySafety: RetrySafety.Safe,\n service: STATUS_SERVICE,\n read: (body) => normalizeStatus(body as PlatformStatus),\n },\n },\n setup: (context) => ({ api: new StatusResource(context) }),\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 { CreateCommentInput } from '../domain/params.js';\nimport type { UserId } from '../models/common.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/execution/http.js';\nimport type { PaginationOptions, RequestOptions } from '../core/options.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { Comment, LikeResult } from '../models/content.js';\nimport { passthroughOperation, voidOperation } from '../operations/common.js';\nimport { BaseResource } from './base.js';\nimport {\n type Page,\n PaginationMode,\n type Paginator,\n pageOperation,\n readPagedPage,\n} from './pagination.js';\n\nconst COMMENTS_REPLIES = pageOperation<Comment>('comments.replies', (body) =>\n readPagedPage<Comment>(body, 'replies'),\n);\nconst COMMENTS_REPLY = passthroughOperation<Comment>('comments.reply');\nconst COMMENTS_UPDATE = passthroughOperation<Comment>('comments.update');\nconst COMMENTS_RESTORE = passthroughOperation<Comment>('comments.restore');\nconst COMMENTS_LIKE = passthroughOperation<LikeResult>('comments.like');\nconst COMMENTS_UNLIKE = passthroughOperation<LikeResult>('comments.unlike');\nconst COMMENTS_REMOVE = voidOperation('comments.remove');\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 operation: 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 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.execute(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.execute(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.voidOperation(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.execute(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.execute(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.execute(COMMENTS_UNLIKE, {\n path: `/api/comments/${encodePathSegment(commentId, 'commentId')}/like`,\n ...options,\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 { ItdConfigError } from '../core/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","import { type FileInput, FileTransferMode } from '../core/attachments/contracts.js';\nimport type {\n InternalFileResolver,\n PreparedBufferSource,\n PreparedFileSource,\n} from '../core/attachments/resolver.js';\nimport type { HttpClient } from '../core/execution/http.js';\nimport type { RequestBodyFactory } from '../core/execution/pipeline.js';\nimport { createMultipartFileBody } from '../core/multipart.js';\nimport type { RequestOptions } from '../core/options.js';\nimport { encodePathSegment } from '../core/url.js';\nimport { assertAllowedMime, mimeFromFilename } from '../domain/mime.js';\nimport { passthroughOperation, voidOperation } from '../operations/common.js';\nimport { BaseResource } from './base.js';\n\nconst FILES_UPLOAD = passthroughOperation<UploadedFile>('files.upload');\nconst FILES_GET = passthroughOperation<unknown>('files.get');\nconst FILES_REMOVE = voidOperation('files.remove');\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\n/** Файлы и медиа. */\nexport class FilesResource extends BaseResource {\n readonly #files: InternalFileResolver;\n\n constructor(http: HttpClient, deps: { files: InternalFileResolver }) {\n super(http);\n this.#files = deps.files;\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.execute(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.execute(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.voidOperation(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.#files.mode(input) === FileTransferMode.Stream;\n let buffered: Promise<PreparedBufferSource> | undefined;\n\n return async ({ signal, attempt }) => {\n let prepared: PreparedFileSource;\n if (streamMode) {\n prepared = await this.#files.resolve(input, options, { signal, attempt });\n } else {\n buffered ??= this.#files\n .resolve(input, options, { signal, attempt })\n .then((source) => source as PreparedBufferSource)\n .catch((error: unknown) => {\n buffered = undefined;\n throw error;\n });\n prepared = await buffered;\n }\n\n if (prepared.mode === FileTransferMode.Buffer) {\n const filename = this.#filename(prepared, options);\n const contentType = prepared.contentType ?? mimeFromFilename(filename);\n if (options.validateMime !== false) assertAllowedMime(contentType, filename);\n const blob =\n !contentType || prepared.blob.type === contentType\n ? prepared.blob\n : new Blob([prepared.blob], { type: contentType });\n const form = new FormData();\n form.set('file', blob, filename);\n return { body: form };\n }\n\n let multipart: ReturnType<typeof createMultipartFileBody> | undefined;\n try {\n const filename = this.#filename(prepared, options);\n const contentType = prepared.contentType ?? mimeFromFilename(filename);\n if (options.validateMime !== false) assertAllowedMime(contentType, filename);\n multipart = createMultipartFileBody({\n stream: prepared.stream,\n filename,\n contentType: contentType ?? '',\n });\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 } catch (error) {\n await prepared.close();\n throw error;\n }\n };\n }\n\n /** Подбирает непустое имя, обязательное для multipart. */\n #filename(source: PreparedFileSource, options: UploadOptions): string {\n if (options.filename) return options.filename;\n if (source.filename) return source.filename;\n const extension = options.contentType?.split(';', 1)[0]?.trim().split('/')[1];\n return extension ? `file.${extension}` : 'file';\n }\n}\n","import type { PaginationOptions, RequestOptions } from '../core/options.js';\nimport { pickArray } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport { defineBuiltInOperation } from '../domain/operations.js';\nimport type { Hashtag, Post } from '../models/content.js';\nimport { BaseResource } from './base.js';\nimport {\n type Page,\n PaginationMode,\n type Paginator,\n pageOperation,\n readCursorPage,\n} from './pagination.js';\n\nconst HASHTAG_POSTS = pageOperation<Post>('hashtags.posts', (body) =>\n readCursorPage<Post>(body, 'posts'),\n);\nconst HASHTAGS_SEARCH = defineBuiltInOperation<Hashtag[]>('hashtags.search', (body) =>\n pickArray<Hashtag>(body, 'hashtags'),\n);\nconst HASHTAGS_TRENDING = defineBuiltInOperation<Hashtag[]>('hashtags.trending', (body) =>\n pickArray<Hashtag>(body, 'hashtags'),\n);\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 operation: HASHTAG_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 mode: PaginationMode.Cursor,\n });\n\n /**\n * Ищет хэштеги.\n *\n * Без строки запроса возвращает общий список.\n */\n search(\n query?: string,\n params: { limit?: number } = {},\n options: RequestOptions = {},\n ): Promise<Hashtag[]> {\n return this.http.execute(HASHTAGS_SEARCH, {\n path: '/api/hashtags',\n query: { q: query, limit: params.limit },\n ...options,\n });\n }\n\n /** Загружает трендовые хэштеги. */\n trending(params: { limit?: number } = {}, options: RequestOptions = {}): Promise<Hashtag[]> {\n return this.http.execute(HASHTAGS_TRENDING, {\n path: '/api/hashtags/trending',\n query: { limit: params.limit },\n ...options,\n });\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 { RequestOptions } from '../core/options.js';\nimport { pickArray } from '../core/unwrap.js';\nimport { defineBuiltInOperation } from '../domain/operations.js';\nimport type { Announcement, ChangelogEntry, Portal } from '../models/platform.js';\nimport type { PlatformStatus } from '../models/status.js';\nimport { passthroughOperation } from '../operations/common.js';\nimport { BaseResource } from './base.js';\nimport type { StatusResource } from './status.js';\n\nconst PLATFORM_CHANGELOG = defineBuiltInOperation<ChangelogEntry[]>('platform.changelog', (body) =>\n Array.isArray(body) ? (body as ChangelogEntry[]) : [],\n);\nconst PLATFORM_ANNOUNCEMENTS = defineBuiltInOperation<Announcement[]>(\n 'platform.announcements',\n (body) => pickArray<Announcement>(body, 'announcements'),\n);\nconst PLATFORM_VERSION = passthroughOperation<PlatformVersions>('platform.version');\nconst PLATFORM_PORTAL = passthroughOperation<Portal>('platform.portal');\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/**\n * Сведения о платформе: версии приложений, изменения, анонсы, баннер события.\n *\n * Доступна как `itd.platform`.\n */\nexport class PlatformResource extends BaseResource {\n readonly #status: StatusResource;\n\n /** @internal */\n constructor(http: ConstructorParameters<typeof BaseResource>[0], status: StatusResource) {\n super(http);\n this.#status = status;\n }\n\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.execute(PLATFORM_VERSION, {\n path: '/api/platform/version',\n skipAuth: true,\n ...options,\n });\n }\n\n /** Загружает журнал изменений. */\n changelog(options: RequestOptions = {}): Promise<ChangelogEntry[]> {\n return this.http.execute(PLATFORM_CHANGELOG, {\n path: '/api/platform/changelog',\n ...options,\n });\n }\n\n /** Загружает анонсы платформы. */\n announcements(options: RequestOptions = {}): Promise<Announcement[]> {\n return this.http.execute(PLATFORM_ANNOUNCEMENTS, {\n path: '/api/platform/announcements',\n ...options,\n });\n }\n\n /** Загружает баннер текущего события — виджет «портал». */\n portal(options: RequestOptions = {}): Promise<Portal> {\n return this.http.execute(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 return this.#status.get(options);\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_]*[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];\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 '../domain/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 { CreatePostData, UpdatePostInput } from '../domain/params.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 { 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/execution/http.js';\nimport type { PaginationOptions, RequestOptions } from '../core/options.js';\nimport { pickArray } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport { defineBuiltInOperation } from '../domain/operations.js';\nimport type { CreateCommentInput, CreatePostData } from '../domain/params.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 { passthroughOperation, voidOperation } from '../operations/common.js';\nimport type { CommentSort, FeedTab } from '../types/enums.js';\nimport { BaseResource } from './base.js';\nimport {\n type Page,\n PaginationMode,\n type Paginator,\n pageOperation,\n readCursorPage,\n readFlatCursorPage,\n} from './pagination.js';\n\nconst POSTS_LIST = pageOperation<Post>('posts.list', (body) => readCursorPage<Post>(body, 'posts'));\nconst POSTS_BY_USER = pageOperation<Post>('posts.byUser', (body) =>\n readCursorPage<Post>(body, 'posts'),\n);\nconst POSTS_LIKED_BY_USER = pageOperation<Post>('posts.likedByUser', (body) =>\n readCursorPage<Post>(body, 'posts'),\n);\nconst POSTS_COMMENTS = pageOperation<Comment>('posts.comments', (body) =>\n readFlatCursorPage<Comment>(body, 'comments'),\n);\nconst POSTS_STATS = defineBuiltInOperation<PostStats[]>('posts.stats', (body) =>\n pickArray<PostStats>(body, 'posts'),\n);\nconst POSTS_CREATE = passthroughOperation<Post>('posts.create');\nconst POSTS_GET = passthroughOperation<Post>('posts.get');\nconst POSTS_UPDATE = passthroughOperation<Post>('posts.update');\nconst POSTS_RESTORE = passthroughOperation<Post>('posts.restore');\nconst POSTS_LIKE = passthroughOperation<LikeResult>('posts.like');\nconst POSTS_UNLIKE = passthroughOperation<LikeResult>('posts.unlike');\nconst POSTS_REPOST = passthroughOperation<Post>('posts.repost');\nconst POSTS_PIN = passthroughOperation<PinPostResult>('posts.pin');\nconst POSTS_UNPIN = passthroughOperation<PinPostResult>('posts.unpin');\nconst POSTS_VOTE = passthroughOperation<Poll>('posts.vote');\nconst POSTS_COMMENT = passthroughOperation<Comment>('posts.comment');\nconst POSTS_REMOVE = voidOperation('posts.remove');\nconst POSTS_UNREPOST = voidOperation('posts.unrepost');\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 operation: POSTS_LIST,\n path: () => '/api/posts',\n query: (p) => ({ tab: p.tab, limit: p.limit }),\n start: cursorStart,\n mode: PaginationMode.Cursor,\n });\n\n /** Стена пользователя: `/api/posts/user/{user}`. */\n readonly #wall = this.paginated<Post, UserPostsParams & { user: UserRef }>({\n operation: POSTS_BY_USER,\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 mode: PaginationMode.Cursor,\n });\n\n /** Понравившиеся посты пользователя: `/api/posts/user/{user}/liked`. */\n readonly #liked = this.paginated<Post, UserPostsParams & { user: UserRef }>({\n operation: POSTS_LIKED_BY_USER,\n path: (p) => `/api/posts/user/${encodePathSegment(p.user, 'user')}/liked`,\n query: (p) => ({ limit: p.limit }),\n start: cursorStart,\n mode: PaginationMode.Cursor,\n });\n\n /** Комментарии к посту: курсор лежит рядом со списком, поэтому свой reader. */\n readonly #comments = this.paginated<Comment, CommentsParams & { postId: string }>({\n operation: 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 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.execute(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.execute(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.execute(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.voidOperation(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.execute(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.execute(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.execute(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.execute(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.voidOperation(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.execute(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.execute(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.execute(POSTS_VOTE, {\n path: `/api/posts/${encodePathSegment(postId, 'postId')}/poll/vote`,\n body: { optionIds },\n ...options,\n });\n }\n\n /** Запрашивает счётчики сразу для нескольких постов. */\n stats(ids: string[], options: RequestOptions = {}): Promise<PostStats[]> {\n return this.http.execute(POSTS_STATS, {\n path: '/api/posts/stats',\n body: { ids },\n ...options,\n });\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.execute(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 type { CreateReportInput } from '../domain/params.js';\nimport { ReportReason, ReportTargetType } from '../types/enums.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 { RequestOptions } from '../core/options.js';\nimport type { Report } from '../models/platform.js';\nimport { passthroughOperation } from '../operations/common.js';\nimport { BaseResource } from './base.js';\n\nconst REPORTS_CREATE = passthroughOperation<Report>('reports.create');\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.execute(REPORTS_CREATE, {\n path: '/api/reports',\n body: data,\n ...options,\n });\n }\n}\n","import type { RequestOptions } from '../core/options.js';\nimport { pickArray } from '../core/unwrap.js';\nimport { defineBuiltInOperation } from '../domain/operations.js';\nimport type { Hashtag } from '../models/content.js';\nimport type { UserSummary } from '../models/users.js';\nimport { BaseResource } from './base.js';\n\n/** Результат глобального поиска. */\nexport interface SearchResult {\n users: UserSummary[];\n hashtags: Hashtag[];\n}\n\nconst SEARCH_ALL = defineBuiltInOperation<SearchResult>('search.all', (body) => ({\n users: pickArray<UserSummary>(body, 'users'),\n hashtags: pickArray<Hashtag>(body, 'hashtags'),\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 all(query: string, options: RequestOptions = {}): Promise<SearchResult> {\n return this.http.execute(SEARCH_ALL, {\n path: '/api/search',\n query: { q: query },\n ...options,\n });\n }\n}\n","import type { RequestOptions } from '../core/options.js';\nimport { encodePathSegment } from '../core/url.js';\nimport { defineBuiltInOperation } from '../domain/operations.js';\nimport type { PaymentMethod, Subscription } from '../models/account.js';\nimport { passthroughOperation, voidOperation } from '../operations/common.js';\nimport { BaseResource } from './base.js';\n\nconst SUBSCRIPTION_METHODS = defineBuiltInOperation<PaymentMethod[]>(\n 'subscription.methods',\n (body) => (Array.isArray(body) ? (body as PaymentMethod[]) : []),\n);\nconst SUBSCRIPTION_STATUS = passthroughOperation<Subscription>('subscription.status');\nconst SUBSCRIPTION_PAY = passthroughOperation<unknown>('subscription.pay');\nconst SUBSCRIPTION_SET_AUTO_RENEWAL = passthroughOperation<unknown>('subscription.setAutoRenewal');\nconst SUBSCRIPTION_BIND_CARD = passthroughOperation<unknown>('subscription.bindCard');\nconst SUBSCRIPTION_SET_DEFAULT_METHOD = passthroughOperation<unknown>(\n 'subscription.setDefaultMethod',\n);\nconst SUBSCRIPTION_REMOVE_METHOD = voidOperation('subscription.removeMethod');\n\n/**\n * Подписка и способы оплаты.\n *\n * Доступна как `itd.subscription`.\n */\nexport class SubscriptionResource extends BaseResource {\n /** Загружает состояние подписки и её цену. */\n status(options: RequestOptions = {}): Promise<Subscription> {\n return this.http.execute(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.execute(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.execute(SUBSCRIPTION_SET_AUTO_RENEWAL, {\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.execute(SUBSCRIPTION_BIND_CARD, {\n path: '/api/v1/subscription/bind-card',\n ...options,\n });\n }\n\n /** Загружает список способов оплаты. Пустой массив, если карт нет. */\n methods(options: RequestOptions = {}): Promise<PaymentMethod[]> {\n return this.http.execute(SUBSCRIPTION_METHODS, {\n path: '/api/v1/subscription/methods',\n ...options,\n });\n }\n\n /** Делает способ оплаты основным. */\n setDefaultMethod(methodId: string, options: RequestOptions = {}): Promise<unknown> {\n return this.http.execute(SUBSCRIPTION_SET_DEFAULT_METHOD, {\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.voidOperation(SUBSCRIPTION_REMOVE_METHOD, {\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/execution/http.js';\nimport type { RequestOptions } from '../core/options.js';\nimport { createDeviceId } from '../core/runtime.js';\nimport { defineBuiltInOperation } from '../domain/operations.js';\nimport { InteractionType, type ViewReason, type ViewSource } from '../types/enums.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 /** Внутренняя отправка данных после вызова `dispose()`. */\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.execute(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.execute(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/** Отправляет и закрывает созданные накопители после вызова `dispose()`. @internal */\nexport function closeTelemetryForDispose(resource: TelemetryResource | undefined): Promise<void> {\n return resource\n ? (TELEMETRY_DISPOSE_CLOSERS.get(resource)?.() ?? Promise.resolve())\n : Promise.resolve();\n}\nconst TELEMETRY_DWELL = defineBuiltInOperation<unknown>('telemetry.dwell');\nconst TELEMETRY_INTERACTION = defineBuiltInOperation<unknown>('telemetry.interaction');\n","import type { FileInput } from '../core/attachments/contracts.js';\nimport type { HttpClient } from '../core/execution/http.js';\nimport type { OperationContract } from '../core/operation.js';\nimport type { PaginationOptions, RequestOptions } from '../core/options.js';\nimport { pickArray, pickBoolean, pickString } from '../core/unwrap.js';\nimport { encodePathSegment } from '../core/url.js';\nimport type { BuiltInOperationId } from '../domain/operations.js';\nimport { defineBuiltInOperation } from '../domain/operations.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 { passthroughOperation, voidOperation } from '../operations/common.js';\nimport { BaseResource } from './base.js';\nimport type { UploadedFile, UploadOptions } from './files.js';\nimport {\n type Page,\n PaginationMode,\n type Paginator,\n pageOperation,\n readPagedPage,\n} from './pagination.js';\n\nconst userListOperation = <TId extends BuiltInOperationId>(id: TId) =>\n pageOperation<UserSummary, TId>(id, (body) =>\n readPagedPage<UserSummary>(body, 'users', 'followers', 'following', 'blocked'),\n );\nconst USERS_FOLLOWERS = userListOperation('users.followers');\nconst USERS_FOLLOWING = userListOperation('users.following');\nconst USERS_BLOCKED = userListOperation('users.blocked');\nconst USER_LIST_OPERATIONS = {\n 'users.followers': USERS_FOLLOWERS,\n 'users.following': USERS_FOLLOWING,\n 'users.blocked': USERS_BLOCKED,\n} as const;\nconst USERS_CHECK_USERNAME = defineBuiltInOperation<boolean>('users.checkUsername', (body) =>\n pickBoolean(body, 'available'),\n);\nconst USERS_SEARCH = defineBuiltInOperation<UserSummary[]>('users.search', (body) =>\n pickArray<UserSummary>(body, 'users'),\n);\nconst USERS_WHO_TO_FOLLOW = defineBuiltInOperation<UserSummary[]>('users.whoToFollow', (body) =>\n pickArray<UserSummary>(body, 'users'),\n);\nconst USERS_TOP_CLANS = defineBuiltInOperation<Clan[]>('users.topClans', (body) =>\n pickArray<Clan>(body, 'clans'),\n);\nconst USERS_PINS = defineBuiltInOperation<PinsResult>('users.pins', (body) => ({\n pins: pickArray(body, 'pins'),\n activePin: pickString(body, 'activePin') ?? null,\n}));\nconst USERS_ME = passthroughOperation<MyProfile>('users.me');\nconst USERS_UPDATE_ME = passthroughOperation<MyProfile>('users.updateMe');\nconst USERS_CREATE_PROFILE = passthroughOperation<MyProfile>('users.createProfile');\nconst USERS_GET = passthroughOperation<PublicProfile>('users.get');\nconst USERS_FOLLOW = passthroughOperation<FollowResult>('users.follow');\nconst USERS_FOLLOW_STATUS = passthroughOperation<Record<string, boolean>>('users.followStatus');\nconst USERS_GET_PRIVACY = passthroughOperation<PrivacySettings>('users.getPrivacy');\nconst USERS_UPDATE_PRIVACY = passthroughOperation<PrivacySettings>('users.updatePrivacy');\nconst USERS_DEACTIVATE = voidOperation('users.deactivate');\nconst USERS_RESTORE = voidOperation('users.restore');\nconst USERS_UNFOLLOW = voidOperation('users.unfollow');\nconst USERS_BLOCK = voidOperation('users.block');\nconst USERS_UNBLOCK = voidOperation('users.unblock');\nconst USERS_SET_PIN = voidOperation('users.setPin');\nconst USERS_REMOVE_PIN = voidOperation('users.removePin');\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 & {\n path: string;\n operation: OperationContract<Page<UserSummary>, BuiltInOperationId>;\n }\n >({\n operation: (p) => p.operation,\n path: (p) => p.path,\n query: (p) => ({ limit: p.limit }),\n start: (p) => (p.page !== undefined ? { page: p.page } : {}),\n mode: PaginationMode.Page,\n });\n\n /** Загружает свой профиль — с подпиской и признаком подтверждённого телефона. */\n me(options: RequestOptions = {}): Promise<MyProfile> {\n return this.http.execute(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.execute(USERS_UPDATE_ME, {\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.voidOperation(USERS_DEACTIVATE, {\n path: '/api/users/me',\n ...options,\n });\n }\n\n /** Восстанавливает деактивированный аккаунт. */\n restore(options: RequestOptions = {}): Promise<void> {\n return this.voidOperation(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.execute(USERS_CREATE_PROFILE, {\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.execute(USERS_GET, {\n path: `/api/users/${encodePathSegment(user, 'user')}`,\n ...options,\n });\n }\n\n /** Проверяет, свободно ли имя пользователя. */\n checkUsername(username: string, options: RequestOptions = {}): Promise<boolean> {\n return this.http.execute(USERS_CHECK_USERNAME, {\n path: '/api/users/check-username',\n query: { username },\n ...options,\n });\n }\n\n /** Ищет пользователей по строке запроса. */\n search(\n query: string,\n params: { limit?: number } = {},\n options: RequestOptions = {},\n ): Promise<UserSummary[]> {\n return this.http.execute(USERS_SEARCH, {\n path: '/api/users/search',\n query: { q: query, limit: params.limit },\n ...options,\n });\n }\n\n /** Загружает рекомендации, на кого подписаться. */\n whoToFollow(options: RequestOptions = {}): Promise<UserSummary[]> {\n return this.http.execute(USERS_WHO_TO_FOLLOW, {\n path: '/api/users/suggestions/who-to-follow',\n ...options,\n });\n }\n\n /** Загружает рейтинг кланов. */\n topClans(options: RequestOptions = {}): Promise<Clan[]> {\n return this.http.execute(USERS_TOP_CLANS, {\n path: '/api/users/stats/top-clans',\n ...options,\n });\n }\n\n /**\n * Подписывается на пользователя.\n *\n * У закрытого профиля вместо подписки отправляется заявка — это видно по полю `status`.\n */\n follow(user: UserRef, options: RequestOptions = {}): Promise<FollowResult> {\n return this.http.execute(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.voidOperation(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.execute(USERS_FOLLOW_STATUS, {\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.voidOperation(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.voidOperation(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.execute(USERS_GET_PRIVACY, {\n path: '/api/users/me/privacy',\n ...options,\n });\n }\n\n /** Обновляет настройки приватности. Передавайте только изменяемые поля. */\n updatePrivacy(input: UpdatePrivacyInput, options: RequestOptions = {}): Promise<PrivacySettings> {\n return this.http.execute(USERS_UPDATE_PRIVACY, {\n path: '/api/users/me/privacy',\n body: input,\n ...options,\n });\n }\n\n /**\n * Загружает значки профиля и выбранный из них.\n *\n * `activePin` — строка-идентификатор, а не объект.\n */\n pins(options: RequestOptions = {}): Promise<PinsResult> {\n return this.http.execute(USERS_PINS, {\n path: '/api/users/me/pins',\n ...options,\n });\n }\n\n /** Выбирает активный значок профиля. */\n setPin(slug: string, options: RequestOptions = {}): Promise<void> {\n return this.voidOperation(USERS_SET_PIN, {\n path: '/api/users/me/pin',\n body: { slug },\n ...options,\n });\n }\n\n /** Снимает активный значок. */\n removePin(options: RequestOptions = {}): Promise<void> {\n return this.voidOperation(USERS_REMOVE_PIN, {\n path: '/api/users/me/pin',\n ...options,\n });\n }\n\n #userPage(\n operationId: keyof typeof USER_LIST_OPERATIONS,\n path: string,\n params: UserListParams,\n options: RequestOptions,\n ): Promise<Page<UserSummary>> {\n return this.#userList.list(\n { ...params, operation: USER_LIST_OPERATIONS[operationId], path },\n options,\n );\n }\n\n #userPaginator(\n operationId: keyof typeof USER_LIST_OPERATIONS,\n path: string,\n params: UserListParams,\n options: PaginationOptions,\n ): Paginator<UserSummary> {\n return this.#userList.iterate(\n { ...params, operation: USER_LIST_OPERATIONS[operationId], path },\n options,\n );\n }\n}\n","import type { RequestOptions } from '../core/options.js';\nimport type { VerificationStatus } from '../models/platform.js';\nimport { passthroughOperation } from '../operations/common.js';\nimport { BaseResource } from './base.js';\n\nconst VERIFICATION_STATUS = passthroughOperation<VerificationStatus>('verification.status');\nconst VERIFICATION_SUBMIT = passthroughOperation<unknown>('verification.submit');\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.execute(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.execute(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 { InternalFileResolver } from '../core/attachments/resolver.js';\nimport type { HttpClient } from '../core/execution/http.js';\nimport type { RequestOptions } from '../core/options.js';\nimport { CommentsResource } from '../resources/comments.js';\nimport { FilesResource, type UploadOptions } from '../resources/files.js';\nimport { HashtagsResource } from '../resources/hashtags.js';\nimport type { 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 type { StatusResource } from '../resources/status.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';\n\n/** Что нужно ресурсам, чтобы ходить в API. */\nexport interface ResourceDeps<N extends NotificationsResource> {\n /** Точка входа в конвейер запросов. */\n http: HttpClient;\n /** Общий механизм подготовки файловых источников клиента. */\n files: InternalFileResolver;\n /** Встроенный status feature, которому делегирует `platform.status()`. */\n status: StatusResource;\n /** Выбирает базовый REST-ресурс либо API уведомлений полного клиента. */\n createNotifications(http: HttpClient): N;\n}\n\n/**\n * Ресурсы, которым достаточно конвейера запросов.\n *\n * `auth` сюда не входит: он управляет сессией и потому принадлежит полному клиенту.\n */\nexport interface RestResources<N extends NotificationsResource = NotificationsResource> {\n readonly users: UsersResource;\n readonly posts: PostsResource;\n readonly comments: CommentsResource;\n readonly files: FilesResource;\n readonly notifications: N;\n readonly hashtags: HashtagsResource;\n readonly search: SearchResource;\n readonly reports: ReportsResource;\n readonly verification: VerificationResource;\n readonly subscription: SubscriptionResource;\n readonly platform: PlatformResource;\n readonly telemetry: TelemetryResource;\n /**\n * Отправляет накопленную телеметрию, если накопитель вообще поднимали.\n *\n * @param forDispose терминальная очистка: записи уходят мимо проверки состояния клиента\n */\n closeTelemetry(forDispose: boolean): Promise<void>;\n /** Помечает накопители до первой асинхронной границы терминальной очистки. */\n prepareTelemetryClose(): void;\n}\n\n/**\n * Собирает набор ресурсов, создавая каждый при первом обращении.\n *\n * Ленивость существенна: клиенту редко нужны все двенадцать разом, а закрытие не должно\n * поднимать накопитель телеметрии только ради того, чтобы его тут же закрыть.\n *\n * Набор общий для полного клиента и минимального REST-клиента — иначе список ресурсов\n * и их зависимости разъезжались бы по двум фасадам.\n */\nexport function createResources<N extends NotificationsResource>(\n deps: ResourceDeps<N>,\n): RestResources<N> {\n let users: UsersResource | undefined;\n let posts: PostsResource | undefined;\n let comments: CommentsResource | undefined;\n let files: FilesResource | undefined;\n let notifications: N | undefined;\n let hashtags: HashtagsResource | undefined;\n let search: SearchResource | undefined;\n let reports: ReportsResource | undefined;\n let verification: VerificationResource | undefined;\n let subscription: SubscriptionResource | undefined;\n let platform: PlatformResource | undefined;\n let telemetry: TelemetryResource | undefined;\n\n const bag: RestResources<N> = {\n get users() {\n users ??= new UsersResource(deps.http, { uploadFile });\n return users;\n },\n get posts() {\n posts ??= new PostsResource(deps.http, { uploadFiles });\n return posts;\n },\n get comments() {\n comments ??= new CommentsResource(deps.http, { uploadFiles });\n return comments;\n },\n get files() {\n files ??= new FilesResource(deps.http, { files: deps.files });\n return files;\n },\n get notifications() {\n notifications ??= deps.createNotifications(deps.http);\n return notifications;\n },\n get hashtags() {\n hashtags ??= new HashtagsResource(deps.http);\n return hashtags;\n },\n get search() {\n search ??= new SearchResource(deps.http);\n return search;\n },\n get reports() {\n reports ??= new ReportsResource(deps.http);\n return reports;\n },\n get verification() {\n verification ??= new VerificationResource(deps.http);\n return verification;\n },\n get subscription() {\n subscription ??= new SubscriptionResource(deps.http);\n return subscription;\n },\n get platform() {\n platform ??= new PlatformResource(deps.http, deps.status);\n return platform;\n },\n get telemetry() {\n telemetry ??= new TelemetryResource(deps.http);\n return telemetry;\n },\n\n closeTelemetry: (forDispose) =>\n forDispose ? closeTelemetryForDispose(telemetry) : (telemetry?.close() ?? Promise.resolve()),\n prepareTelemetryClose: () => prepareTelemetryForDispose(telemetry),\n };\n\n // Загрузка вложений обращается к `files` в момент вызова, поэтому ресурс поднимается\n // только тогда, когда файл действительно отправляют.\n const uploadFile = (\n file: FileInput,\n uploadOptions?: UploadOptions,\n requestOptions?: RequestOptions,\n ) => bag.files.upload(file, uploadOptions ?? {}, requestOptions ?? {});\n const uploadFiles = (input: FileInput[], requestOptions?: RequestOptions) =>\n bag.files.uploadMany(input, {}, requestOptions ?? {});\n\n return bag;\n}\n","import { ItdConfigError, ItdFileError, ItdFileErrorReason } from '../errors.js';\nimport { redactErrorCause } from '../redact.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: redactErrorCause(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 { 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, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\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"],"mappings":"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"}
|