@torrow/sdk-auth 0.7.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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/auth-session/app-credentials.ts","../../../src/auth-session/otp-flow.ts","../../../src/auth-session/identity-client.ts","../../../src/auth-session/auth-protocol.ts","../../../src/auth-session/token-storage.ts","../../../src/auth-session/local-storage-token-storage.ts","../../../src/auth-session/auth-session-not-ready.ts","../../../src/auth-session/TokenSession.ts","../../../src/auth-session/fetch-with-401-retry.ts","../../../src/auth-session/merge-tokens.ts","../../../src/auth-session/torrow-auth.ts","../../../src/auth-session/link-login.ts","../../../src/auth-session/sleep.ts","../../../src/auth-session/otp-login.ts","../../../src/auth-session/mobile-id.ts"],"sourcesContent":["/** Defaults и преобразование credentials приложения в HTTP Basic. */\nimport type { AuthIdentityBasic } from \"@torrow/sdk-client\";\nimport type { AppCredentials } from \"./types.js\";\n\n/**\n * Дефолтные app credentials SDK для `/api/v1/auth/*`, если {@link TokenSessionOptions.appCredentials}\n * не передан. Своё приложение может переопределить явной парой.\n *\n * Публикуется в npm `@torrow/sdk-auth` (inspectable); свой prod-secret — только override / BFF.\n */\nexport const DEFAULT_APP_CREDENTIALS: AppCredentials = {\n clientId: \"sdk.torrow.net\",\n clientSecret: \"4YM1qYq87faSXaFqXTBcnJrn/jj0AOXl6tTsfubLyCE=\",\n};\n\n/**\n * Подставляет {@link DEFAULT_APP_CREDENTIALS}, если `input` не задан.\n * Неполную явную пару **не** дозаполняет — её отсечёт `appCredentialsToAuthIdentityBasic`.\n *\n * @internal Не экспортируется из `@torrow/sdk-auth`.\n */\nexport function resolveAppCredentials(input?: AppCredentials): AppCredentials {\n return input ?? DEFAULT_APP_CREDENTIALS;\n}\n\n/**\n * Внутренний адаптер слоёв: product DX → HTTP Basic wire.\n *\n * - **auth-пакет:** `AppCredentials` (`clientId` / `clientSecret`)\n * - **sdk-client:** `AuthIdentityBasic` (`username` / `password`) на `TorrowClientConfig.authIdentityBasic`\n *\n * Не экспортируется из `@torrow/sdk-auth`: приложению достаточно `appCredentials`.\n * Low-level `new TorrowClient({ authIdentityBasic })` — собирай `AuthIdentityBasic` сам.\n */\nexport function appCredentialsToAuthIdentityBasic(\n appCredentials: AppCredentials | undefined,\n): AuthIdentityBasic | undefined {\n const clientId = appCredentials?.clientId?.trim();\n const clientSecret = appCredentials?.clientSecret?.trim();\n if (!clientId || !clientSecret) return undefined;\n return { username: clientId, password: clientSecret };\n}\n","import {\n isAuthCaptchaType,\n isDeviceType,\n isSendCodeAccountType,\n isVerificationMethod,\n TokenStatusValues,\n VerificationMethodValues,\n type DeviceInfo,\n type AuthScope,\n type TokenResp,\n type SendCodeAccountType,\n type VerificationMethod,\n} from \"@torrow/sdk-client\";\nimport type { AppCredentials, TokenSessionOptions } from \"./types.js\";\nimport { createIdentityClient } from \"./identity-client.js\";\nimport {\n requestAuthToken,\n sendOtpCode,\n tokenTransition,\n tokenResponseFields,\n} from \"./auth-protocol.js\";\nimport type { TorrowAuthLoginWithOtpOptions } from \"./torrow-auth.js\";\n\n/**\n * Опции пошагового входа без TokenStorage и пользовательской сессии.\n *\n * HTTP-настройки из {@link TokenSessionOptions} применяются к обоим шагам:\n * - `baseUrl` — адрес шлюза без `/api/v1`; по умолчанию `https://torrow.net`.\n * - `fetch` — реализация HTTP-транспорта; по умолчанию глобальный `fetch`.\n * - `defaultHeaders` — дополнительные заголовки каждого запроса.\n * - `userAgent` — значение заголовка `User-Agent` для диагностики сервера.\n *\n * Между `start` и `complete`, в том числе на разных экземплярах flow,\n * приложение сохраняет одинаковые deviceInfo, scopes и splitTokens.\n */\nexport type TorrowAuthOtpFlowOptions = Pick<\n TokenSessionOptions,\n \"baseUrl\" | \"fetch\" | \"defaultHeaders\" | \"userAgent\"\n> & {\n /**\n * Учётные данные приложения для Basic на `/api/v1/auth/*`.\n * Если не заданы — используется {@link DEFAULT_APP_CREDENTIALS};\n * явная пара полностью заменяет default и требует непустых clientId/clientSecret.\n */\n appCredentials?: AppCredentials;\n /**\n * Обязательное описание устройства для `TokenReq.deviceInfo` при `complete`.\n * Требуются непустой deviceId и поддерживаемый deviceType; flow не генерирует deviceId.\n */\n deviceInfo: DeviceInfo;\n /**\n * Запрашиваемые области доступа → `TokenReq.scopes` при `complete`.\n * Если поле отсутствует или массив пуст — `user_api` и `offline_access`.\n */\n scopes?: AuthScope[];\n /**\n * Разделение access/refresh сервером → `TokenReq.splitTokens` при `complete`.\n * false: полные токены в JSON; используйте для Node, MCP и интеграционных ключей.\n * true: части токенов в JSON, остальные части в HttpOnly cookies;\n * только для браузерного приложения с настроенной передачей cookies.\n * SDK не управляет cookies и не восстанавливает полные токены из частей.\n * Явные true/false передаются без изменения; отсутствующее поле не отправляется.\n */\n splitTokens?: boolean;\n};\n/**\n * Вход отправки кода; поля наследуются из {@link TorrowAuthLoginWithOtpOptions}.\n * account задаёт Phone/Email и получателя; language — язык (default ru),\n * regionStorageId — регион (default 70), captcha/captchaType — ответ и провайдер captcha,\n * inviteSessionId — приглашение. preferredMethod — пожелание серверу,\n * supportedMethods — способы, которые приложение умеет обработать.\n * UI и получение captcha остаются в приложении.\n */\nexport type OtpStartInput = Pick<\n TorrowAuthLoginWithOtpOptions,\n | \"account\"\n | \"language\"\n | \"regionStorageId\"\n | \"captcha\"\n | \"captchaType\"\n | \"inviteSessionId\"\n | \"preferredMethod\"\n | \"supportedMethods\"\n>;\n/** Опции одного HTTP-шага. */\nexport type OtpRequestOptions = {\n /** Отмена текущего HTTP-шага; не отменяет независимый cleanup приложения. */\n signal?: AbortSignal;\n};\n/**\n * Действие приложения для продолжения входа, отдельно от серверного TokenStatus.\n * AwaitingCode — первоначальный ввод classic OTP либо новая фаза ввода SMS-кода\n * после fallback из Pending в той же Mobile ID auth-сессии.\n * Pending — ожидание Mobile ID, complete вызывается без кода.\n */\nexport enum OtpPhaseValues {\n /** Требуется первоначальный OTP или SMS-код после Mobile ID fallback. */\n AwaitingCode = \"AwaitingCode\",\n /** Ожидание Mobile ID; следующий запрос выполняется без кода. */\n Pending = \"Pending\",\n}\n/** JSON-значение фазы; принимает строковый литерал или член {@link OtpPhaseValues}. */\nexport type OtpPhase = `${OtpPhaseValues}`;\n/** Переносимое продолжение; приложение обеспечивает его целостность. */\nexport type OtpChallenge = {\n /** Идентификатор auth-сессии из SendCodeResp; сохраняется между шагами. */\n sessionId: string;\n /** Тип получателя первоначального кода: Phone или Email. */\n accountType: SendCodeAccountType;\n /** Фактический способ подтверждения; при SMS fallback меняется на Sms. */\n usedMethod: VerificationMethod;\n /** Следующее действие приложения: ввод кода или ожидание без кода. */\n phase: OtpPhase;\n /** Локальный deadline в Unix milliseconds, без продления; не гарантирует серверный срок. TTL из SendCodeResp либо 180 секунд от начала start. */\n expiresAtMs: number;\n /** Подсказка длины кода из SendCodeResp; default 6 для classic Email, 4 для Phone/Mobile ID. В переносимом challenge может отсутствовать. */\n codeLength?: number;\n /** Интервал ожидания в секундах для Pending; default 3. SDK сам не выполняет polling. */\n pollIntervalSeconds?: number;\n /** Оставшиеся попытки из SendCodeResp.attemptCountToUseSession; SDK не уменьшает их. Отсутствие означает, что корректная подсказка не получена. */\n attemptsRemaining?: number;\n /** Время следующей отправки из SendCodeResp.nextAvailableUsageTime в Unix milliseconds; отсутствует без корректной подсказки. */\n nextSendAtMs?: number;\n /** Доступные способы из SendCodeResp; отсутствуют, если список отсутствовал или содержал неизвестные значения. */\n availableMethods?: VerificationMethod[];\n};\n/** Параметры одной попытки обмена или ожидания. */\nexport type OtpCompleteInput = {\n /** Переносимое продолжение; проверяется перед запросом, целостность обеспечивает приложение. */\n challenge: OtpChallenge;\n /** Непустой OTP в AwaitingCode; в Pending поле должно отсутствовать. */\n code?: string;\n};\n/** Данные для явного cleanup вызывающим приложением. */\nexport type OtpCleanupData = {\n /** Refresh-секрет для явного отзыва приложением; не предназначен для диагностики. */\n refreshToken: string;\n};\n/** Причины локального отказа; HTTP и отмена пробрасываются без преобразования. */\nexport enum OtpFlowErrorCodeValues {\n /** Некорректные опции, вход start или код для текущей фазы. */\n InvalidArguments = \"InvalidArguments\",\n /** Повреждённое, противоречивое или просроченное продолжение. */\n InvalidChallenge = \"InvalidChallenge\",\n /** Ответ не соответствует строгому контракту шага. */\n InvalidResponse = \"InvalidResponse\",\n /** Фактический способ подтверждения отсутствует или не поддерживается для аккаунта. */\n UnsupportedMethod = \"UnsupportedMethod\",\n}\n/** Строковое представление безопасной причины отказа. */\nexport type OtpFlowErrorCode = `${OtpFlowErrorCodeValues}`;\n/** Отказ с безопасной диагностикой и отдельно доступным секретом для cleanup. */\nexport class OtpFlowError extends Error {\n readonly name = \"OtpFlowError\";\n readonly #cleanup: OtpCleanupData | undefined;\n constructor(\n /** Безопасная причина локального отказа, без исходного ответа и секретов. */\n readonly code: OtpFlowErrorCode,\n cleanup?: OtpCleanupData,\n ) {\n super(`OTP flow: ${code}`);\n this.#cleanup = cleanup ? Object.freeze({ ...cleanup }) : undefined;\n }\n /** Секрет доступен явно; не является перечисляемым полем исключения. */\n get cleanup(): OtpCleanupData | undefined {\n return this.#cleanup;\n }\n}\n/**\n * Результат одного шага: серверный статус отдельно от фазы challenge.\n * Pending — сервер ещё не завершил аутентификацию, следующий запрос без кода.\n * AwaitingSmsCode — продолжение Mobile ID-сценария через SMS fallback,\n * если аутентификацию через Mobile ID не удалось завершить. Возвращённый challenge\n * меняет фазу Pending на AwaitingCode и метод на Sms; sessionId и deadline сохраняются.\n * TokenReady — токены выданы; Failed — терминальный отказ без продолжения.\n * Обычный Email/SMS OTP начинается с AwaitingCode после start,\n * без отдельного серверного AwaitingEmailCode.\n */\nexport type OtpCompleteResult =\n | {\n /** Сервер ещё ожидает подтверждения либо запросил SMS fallback. */\n status: TokenStatusValues.Pending | TokenStatusValues.AwaitingSmsCode;\n /** Продолжение с новым действием приложения и прежним deadline. */\n challenge: OtpChallenge;\n /** Неожиданный refresh для явного отзыва; отсутствует, если сервер его не выдал. */\n cleanup?: OtpCleanupData;\n }\n | {\n /** Терминальный отказ сервера; продолжение не возвращается. */\n status: TokenStatusValues.Failed;\n /** Неожиданный refresh для явного отзыва; отсутствует без refresh в ответе. */\n cleanup?: OtpCleanupData;\n }\n | {\n /** Сервер явно подтвердил выдачу токенов. */\n status: TokenStatusValues.TokenReady;\n /**\n * Проверенные поля TokenResp: accessToken обязателен, refreshToken и expiration\n * (TTL в секундах) сохраняются при наличии; splitTokens определяет полноту токенов.\n */\n tokens: Omit<TokenResp, \"status\" | \"accessToken\"> & {\n /** Явный статус готовности из token-ответа. */\n status: TokenStatusValues.TokenReady;\n /** Непустой access token из ответа после нормализации aliases. */\n accessToken: string;\n };\n };\n/** Пошаговый интерфейс: каждый метод выполняет только один upstream-запрос. */\nexport interface OtpFlow {\n /** Отправляет один код и возвращает переносимое продолжение без токенов. */\n start(\n input: OtpStartInput,\n options?: OtpRequestOptions,\n ): Promise<OtpChallenge>;\n /** Проверяет продолжение и выполняет одну попытку обмена/ожидания без сохранения. */\n complete(\n input: OtpCompleteInput,\n options?: OtpRequestOptions,\n ): Promise<OtpCompleteResult>;\n}\n\n/**\n * Создаёт OTP-flow с фиксированными настройками транспорта и token-обмена.\n * Создание не выполняет HTTP; start и complete выполняют по одному запросу.\n * Challenge и токены остаются у приложения, без storage, polling и refresh внутри flow.\n * @param options Настройки HTTP, credentials приложения, устройства и выдачи токенов.\n * @returns Независимые шаги отправки кода и завершения/ожидания аутентификации.\n * @throws OtpFlowError При некорректных опциях устройства, scopes или splitTokens.\n */\nexport function createOtpFlow(options: TorrowAuthOtpFlowOptions): OtpFlow {\n // Проверяем runtime-вход, поскольку JavaScript-клиент может обойти TypeScript-контракт.\n if (\n !isRecord(options) ||\n !isRecord(options.deviceInfo) ||\n typeof options.deviceInfo.deviceId !== \"string\" ||\n !options.deviceInfo.deviceId.trim() ||\n typeof options.deviceInfo.deviceType !== \"string\" ||\n !isDeviceType(options.deviceInfo.deviceType) ||\n (options.scopes !== undefined &&\n (!Array.isArray(options.scopes) ||\n !options.scopes.every(\n (scope) => typeof scope === \"string\" && scope.trim(),\n ))) ||\n (options.splitTokens !== undefined &&\n typeof options.splitTokens !== \"boolean\")\n )\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidArguments);\n // Basic относится к приложению. Копии устройства/scopes фиксируют настройки\n // экземпляра без открытия TokenSession и без хранения auth-продолжения.\n const client = createIdentityClient(options);\n const deviceInfo = { ...options.deviceInfo };\n const scopes = options.scopes ? [...options.scopes] : undefined;\n const splitTokens = options.splitTokens;\n return {\n async start(input, requestOptions) {\n // Некорректный ввод и уже отменённый шаг отсеиваем до отправки OTP.\n validateStartInput(input);\n requestOptions?.signal?.throwIfAborted();\n // Отсчёт срока начинается до HTTP: время ожидания upstream тоже расходует TTL.\n const startedAt = Date.now();\n const response: unknown = await sendOtpCode(\n client,\n {\n ...input,\n accountName: input.account.accountName,\n type: input.account.accountType,\n },\n requestOptions?.signal,\n );\n // Ответ сервера недоверенный: sessionId и фактический usedMethod обязательны.\n // preferredMethod был только пожеланием и не определяет возвращаемую фазу.\n if (\n !isRecord(response) ||\n typeof response.sessionId !== \"string\" ||\n !response.sessionId.trim()\n )\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidResponse);\n if (!supportedMethod(response.usedMethod, input.account.accountType))\n throw new OtpFlowError(OtpFlowErrorCodeValues.UnsupportedMethod);\n // Без серверного TTL задаём локальный срок продолжения, а не гарантию срока\n // auth-сессии на сервере. Явно некорректный или уже истёкший срок отклоняем.\n const ttl = response.sessionLifeTimeInSeconds ?? 180;\n if (!positive(ttl)) throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidResponse);\n const expiresAtMs = startedAt + ttl * 1000;\n if (!timestamp(expiresAtMs) || expiresAtMs <= Date.now())\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidResponse);\n // Mobile ID начинается с ожидания без кода, остальные методы — с ввода OTP.\n const mobile = response.usedMethod === VerificationMethodValues.MtsMobileId;\n const nextSendAtMs =\n response.nextAvailableUsageTime instanceof Date\n ? response.nextAvailableUsageTime.getTime()\n : typeof response.nextAvailableUsageTime === \"string\"\n ? Date.parse(response.nextAvailableUsageTime)\n : undefined;\n // Собираем только поля переносимого challenge: даты как Unix milliseconds,\n // необязательные подсказки проверяем, неизвестные поля ответа не копируем.\n return {\n sessionId: response.sessionId.trim(),\n accountType: input.account.accountType,\n usedMethod: response.usedMethod,\n phase: mobile ? OtpPhaseValues.Pending : OtpPhaseValues.AwaitingCode,\n expiresAtMs,\n codeLength: codeLength(response.codeLength)\n ? response.codeLength\n : input.account.accountType === \"Email\" && !mobile\n ? 6\n : 4,\n ...(mobile\n ? {\n pollIntervalSeconds: positive(response.pollIntervalSeconds)\n ? response.pollIntervalSeconds\n : 3,\n }\n : {}),\n ...(attempts(response.attemptCountToUseSession)\n ? { attemptsRemaining: response.attemptCountToUseSession }\n : {}),\n ...(timestamp(nextSendAtMs) ? { nextSendAtMs } : {}),\n ...(methods(response.availableMethods)\n ? { availableMethods: [...response.availableMethods] }\n : {}),\n };\n },\n async complete(input, requestOptions) {\n // Продолжение может прийти из JSON на новом экземпляре flow. Проверяем его\n // структуру и deadline до HTTP; целостность и привязку к пользователю обеспечивает app.\n if (!isRecord(input)) throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidArguments);\n requestOptions?.signal?.throwIfAborted();\n const continuation = validateChallenge(input.challenge);\n // Фаза определяет допустимость кода: Pending не должен повторно отправлять\n // ранее введённый SMS-код, AwaitingCode требует нового непустого значения.\n if (\n (continuation.phase === OtpPhaseValues.Pending && input.code !== undefined) ||\n (continuation.phase === OtpPhaseValues.AwaitingCode &&\n (typeof input.code !== \"string\" || !input.code.trim()))\n )\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidArguments);\n // Ровно одна попытка token-обмена. Polling, интервалы и повторы выбирает приложение.\n const raw: unknown = await requestAuthToken(\n client,\n {\n sessionId: continuation.sessionId,\n code: input.code?.trim() ?? \"\",\n deviceInfo,\n scopes,\n splitTokens,\n },\n requestOptions?.signal,\n );\n if (!isRecord(raw)) throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidResponse);\n // Извлекаем aliases и доступный refresh до строгой проверки ответа:\n // даже повреждённый ответ может потребовать явного cleanup приложением.\n const fields = tokenResponseFields(raw);\n const refreshToken =\n typeof fields.refresh === \"string\" && fields.refresh.trim()\n ? fields.refresh.trim()\n : undefined;\n const cleanup = refreshToken ? { refreshToken } : undefined;\n // Новый flow требует явный известный status, в отличие от legacy-адаптеров.\n // Некорректные token-поля не скрываем нормализацией; cleanup остаётся в ошибке.\n const status = tokenTransition(\n typeof fields.status === \"string\"\n ? fields.status.trim()\n : fields.status,\n );\n if (\n !status ||\n (fields.access != null && typeof fields.access !== \"string\") ||\n (fields.refresh != null && typeof fields.refresh !== \"string\") ||\n (fields.expiration != null &&\n (typeof fields.expiration !== \"number\" ||\n !Number.isFinite(fields.expiration) ||\n fields.expiration < 0))\n )\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidResponse, cleanup);\n // Ожидание возвращаем вызывающему коду, сохраняя исходный deadline.\n // Это также обрабатывает возврат к Pending после попытки SMS fallback.\n if (status === TokenStatusValues.Pending)\n return {\n status,\n cleanup,\n challenge: {\n ...continuation,\n phase: OtpPhaseValues.Pending,\n usedMethod: VerificationMethodValues.MtsMobileId,\n pollIntervalSeconds: continuation.pollIntervalSeconds ?? 3,\n },\n };\n if (status === TokenStatusValues.AwaitingSmsCode) {\n // Та же auth-сессия продолжается через SMS fallback: меняем фазу и метод,\n // сохраняя sessionId и исходный deadline, без повторной отправки sendCode.\n const { pollIntervalSeconds: _interval, ...challenge } = continuation;\n return {\n status,\n cleanup,\n challenge: {\n ...challenge,\n phase: OtpPhaseValues.AwaitingCode,\n usedMethod: VerificationMethodValues.Sms,\n codeLength: challenge.codeLength || 4,\n },\n };\n }\n // Терминальный отказ не возвращает challenge; возможный refresh требует cleanup.\n if (status === TokenStatusValues.Failed) return { status, cleanup };\n // TokenReady действителен только с непустым access. Возвращаем известные\n // token-поля без сохранения; режим splitTokens определяет их полноту на сервере.\n if (typeof fields.access !== \"string\" || !fields.access.trim())\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidResponse, cleanup);\n return {\n status,\n tokens: {\n status,\n accessToken: fields.access.trim(),\n ...(refreshToken ? { refreshToken } : {}),\n ...(typeof fields.expiration === \"number\"\n ? { expiration: fields.expiration }\n : {}),\n },\n };\n },\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction positive(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0;\n}\nfunction codeLength(value: unknown): value is number {\n return positive(value) && Number.isInteger(value) && value <= 32;\n}\nfunction attempts(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\nfunction timestamp(value: unknown): value is number {\n return (\n typeof value === \"number\" &&\n Number.isSafeInteger(value) &&\n value >= 0 &&\n value <= 8.64e15\n );\n}\nfunction methods(value: unknown): value is VerificationMethod[] {\n return (\n Array.isArray(value) &&\n value.every(\n (method) => typeof method === \"string\" && isVerificationMethod(method),\n )\n );\n}\nfunction supportedMethod(\n value: unknown,\n accountType: SendCodeAccountType,\n): value is VerificationMethod {\n return (\n typeof value === \"string\" &&\n isVerificationMethod(value) &&\n (value !== VerificationMethodValues.UnknownMethod || accountType === \"Email\")\n );\n}\n\nfunction validateStartInput(input: unknown): void {\n if (\n !isRecord(input) ||\n !isRecord(input.account) ||\n typeof input.account.accountType !== \"string\" ||\n !isSendCodeAccountType(input.account.accountType) ||\n typeof input.account.accountName !== \"string\" ||\n !input.account.accountName.trim()\n )\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidArguments);\n for (const field of [\"language\", \"captcha\", \"inviteSessionId\"]) {\n if (input[field] !== undefined && typeof input[field] !== \"string\")\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidArguments);\n }\n if (\n (input.regionStorageId !== undefined && !attempts(input.regionStorageId)) ||\n (input.captchaType !== undefined &&\n (typeof input.captchaType !== \"string\" ||\n !isAuthCaptchaType(input.captchaType))) ||\n (input.preferredMethod !== undefined &&\n (typeof input.preferredMethod !== \"string\" ||\n !isVerificationMethod(input.preferredMethod))) ||\n (input.supportedMethods !== undefined && !methods(input.supportedMethods))\n )\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidArguments);\n}\n\n/** Проверяет недоверенное продолжение и копирует только поля публичного контракта. */\nfunction validateChallenge(value: unknown): OtpChallenge {\n if (\n !isRecord(value) ||\n typeof value.sessionId !== \"string\" ||\n !value.sessionId.trim() ||\n typeof value.accountType !== \"string\" ||\n !isSendCodeAccountType(value.accountType) ||\n !supportedMethod(value.usedMethod, value.accountType) ||\n (value.phase !== OtpPhaseValues.Pending && value.phase !== OtpPhaseValues.AwaitingCode) ||\n !timestamp(value.expiresAtMs) ||\n value.expiresAtMs <= Date.now() ||\n (value.phase === OtpPhaseValues.Pending) !== (value.usedMethod === VerificationMethodValues.MtsMobileId)\n )\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidChallenge);\n if (\n (value.codeLength !== undefined && !codeLength(value.codeLength)) ||\n (value.pollIntervalSeconds !== undefined &&\n !positive(value.pollIntervalSeconds)) ||\n (value.attemptsRemaining !== undefined &&\n !attempts(value.attemptsRemaining)) ||\n (value.nextSendAtMs !== undefined && !timestamp(value.nextSendAtMs)) ||\n (value.availableMethods !== undefined && !methods(value.availableMethods))\n )\n throw new OtpFlowError(OtpFlowErrorCodeValues.InvalidChallenge);\n return {\n sessionId: value.sessionId.trim(),\n accountType: value.accountType,\n usedMethod: value.usedMethod,\n phase: value.phase,\n expiresAtMs: value.expiresAtMs,\n ...(codeLength(value.codeLength) ? { codeLength: value.codeLength } : {}),\n ...(positive(value.pollIntervalSeconds)\n ? { pollIntervalSeconds: value.pollIntervalSeconds }\n : {}),\n ...(attempts(value.attemptsRemaining)\n ? { attemptsRemaining: value.attemptsRemaining }\n : {}),\n ...(timestamp(value.nextSendAtMs)\n ? { nextSendAtMs: value.nextSendAtMs }\n : {}),\n ...(methods(value.availableMethods)\n ? { availableMethods: [...value.availableMethods] }\n : {}),\n };\n}\n","import { TorrowClient } from \"@torrow/sdk-client\";\nimport {\n appCredentialsToAuthIdentityBasic,\n resolveAppCredentials,\n} from \"./app-credentials.js\";\nimport type { TokenSessionOptions } from \"./types.js\";\n\n/** Создаёт транспорт auth с Basic приложения без открытия пользовательской сессии. */\nexport function createIdentityClient(\n options: Pick<\n TokenSessionOptions,\n \"appCredentials\" | \"baseUrl\" | \"fetch\" | \"defaultHeaders\" | \"userAgent\"\n >,\n): TorrowClient {\n const authIdentityBasic = appCredentialsToAuthIdentityBasic(\n resolveAppCredentials(options.appCredentials),\n );\n if (!authIdentityBasic) {\n throw new Error(\n \"TokenSession: неполная appCredentials (нужны непустые clientId и clientSecret). Без опции используется DEFAULT_APP_CREDENTIALS.\",\n );\n }\n return new TorrowClient({\n baseUrl: options.baseUrl,\n fetch: options.fetch,\n defaultHeaders: options.defaultHeaders,\n userAgent: options.userAgent,\n authIdentityBasic,\n });\n}\n","import {\n DefaultAuthScopes,\n isTokenStatus,\n TokenStatusValues,\n type DeviceInfo,\n type AuthScope,\n type SendCodeReqForOtp,\n type SendCodeResp,\n type TokenReq,\n type TokenResp,\n} from \"@torrow/sdk-client\";\n\n/** Поля отправки OTP, общие для пошагового flow и старого callback-входа. */\nexport type OtpSendInput = Omit<SendCodeReqForOtp, \"accountName\"> & {\n accountName: string;\n};\n\n/** Минимальный транспорт auth-протокола; настоящий клиент и HTTP stand-in используют одну форму. */\nexport type AuthProtocolClient = {\n auth: {\n requestToken(\n body: TokenReq,\n options?: { signal?: AbortSignal },\n ): Promise<TokenResp>;\n };\n};\n\n/** Собирает sendCode с существующими defaults SDK. */\nexport function buildOtpSendRequest(input: OtpSendInput): SendCodeReqForOtp {\n const accountName = input.accountName.trim();\n if (!accountName) throw new Error(\"runOtpLogin: accountName is empty\");\n return {\n accountName,\n type: input.type,\n language: input.language?.trim() || \"ru\",\n regionStorageId:\n typeof input.regionStorageId === \"number\" &&\n Number.isFinite(input.regionStorageId)\n ? input.regionStorageId\n : 70,\n ...(input.captcha?.trim() ? { captcha: input.captcha.trim() } : {}),\n ...(input.captchaType ? { captchaType: input.captchaType } : {}),\n ...(input.inviteSessionId?.trim()\n ? { inviteSessionId: input.inviteSessionId.trim() }\n : {}),\n ...(input.preferredMethod\n ? { preferredMethod: input.preferredMethod }\n : {}),\n ...(input.supportedMethods?.length\n ? { supportedMethods: input.supportedMethods }\n : {}),\n };\n}\n\n/** Один sendCode; проверка результата и legacy-исключения принадлежат вызывающему интерфейсу. */\nexport function sendOtpCode(\n client: {\n auth: {\n sendCode(\n body: SendCodeReqForOtp,\n options?: { signal?: AbortSignal },\n ): Promise<SendCodeResp>;\n };\n },\n input: OtpSendInput,\n signal?: AbortSignal,\n): Promise<SendCodeResp> {\n return client.auth.sendCode(buildOtpSendRequest(input), { signal });\n}\n\n/** Параметры token-обмена до применения defaults SDK. */\nexport type AuthTokenRequestInput = {\n /** Идентификатор созданной auth-сессии для обмена кода или polling. */\n sessionId: string;\n /** OTP-код; пустая строка для Mobile ID polling. */\n code: string;\n /** Устройство, для которого выдаются токены. */\n deviceInfo: DeviceInfo;\n /** Области доступа; отсутствие или пустой массив включает DefaultAuthScopes. */\n scopes?: AuthScope[];\n /**\n * Серверное разделение токенов между JSON и HttpOnly cookies.\n * false — полные токены для Node/MCP; true — браузерный режим с настроенными cookies.\n * Отсутствующее поле не отправляется; тип согласован с {@link TokenReq}.\n */\n splitTokens?: TokenReq[\"splitTokens\"];\n};\n\n/** Один token-запрос с общими defaults, без нормализации, persist и повторов. */\nexport function requestAuthToken(\n client: AuthProtocolClient,\n input: AuthTokenRequestInput,\n signal?: AbortSignal,\n): Promise<TokenResp> {\n return client.auth.requestToken(\n {\n sessionId: input.sessionId,\n code: input.code,\n deviceInfo: input.deviceInfo,\n scopes: input.scopes?.length ? input.scopes : [...DefaultAuthScopes],\n ...(input.splitTokens !== undefined\n ? { splitTokens: input.splitTokens }\n : {}),\n },\n { signal },\n );\n}\n\n/** Распознаёт переход протокола; допуск отсутствующего status включается только legacy-адаптацией. */\nexport function tokenTransition(\n status: unknown,\n legacy = false,\n): TokenStatusValues | undefined {\n if (legacy && status == null) return TokenStatusValues.TokenReady;\n return typeof status === \"string\" && isTokenStatus(status)\n ? status\n : undefined;\n}\n\n/** Извлекает поддерживаемые имена полей без потери некорректных значений перед проверкой. */\nexport function tokenResponseFields(o: Record<string, unknown>) {\n return {\n access: o.accessToken ?? o.AccessToken ?? o.access_token,\n refresh: o.refreshToken ?? o.RefreshToken ?? o.refresh_token,\n expiration: o.expiration ?? o.Expiration ?? o.expires_in,\n status: o.status ?? o.Status,\n };\n}\n","/**\n * In-memory реализация {@link TokenStorage}.\n *\n * Тесты, эфемерные процессы, `TorrowAuth.fromTokens` без диска.\n * Для Node-примеров обычно {@link FileTokenStorage}; для браузера —\n * {@link LocalStorageTokenStorage}.\n */\nimport type { AuthTokenSnapshot, TokenStorage } from \"./types.js\";\n\n/** In-memory хранилище токенов сессии. */\nexport class MemoryTokenStorage implements TokenStorage {\n private snapshot: AuthTokenSnapshot;\n\n constructor(initial?: AuthTokenSnapshot) {\n this.snapshot = { ...(initial ?? {}) };\n }\n\n async load(): Promise<AuthTokenSnapshot> {\n return { ...this.snapshot };\n }\n\n async save(snapshot: AuthTokenSnapshot): Promise<void> {\n this.snapshot = {\n accessToken: snapshot.accessToken?.trim() || undefined,\n refreshToken: snapshot.refreshToken?.trim() || undefined,\n accessExpiresAtMs: snapshot.accessExpiresAtMs || undefined,\n deviceId: snapshot.deviceId?.trim() || undefined,\n };\n }\n}\n","/**\n * Persist snapshot в браузерный **`localStorage`** — реализация {@link TokenStorage}.\n *\n * Для виджетов / SPA (`TorrowAuth.fromTokens` / `connect`). В Node без\n * `globalThis.localStorage` конструктор бросит. Не путать с {@link FileTokenStorage}.\n */\nimport type { AuthTokenSnapshot, TokenStorage } from \"./types.js\";\n\ntype LocalStorageLike = {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem?(key: string): void;\n};\n\nfunction resolveLocalStorage(): LocalStorageLike {\n const g = globalThis as { localStorage?: LocalStorageLike };\n if (!g.localStorage || typeof g.localStorage.getItem !== \"function\") {\n throw new Error(\n \"LocalStorageTokenStorage: globalThis.localStorage is not available in this environment\",\n );\n }\n return g.localStorage;\n}\n\n/**\n * Persist snapshot токенов в браузерный `localStorage` (виджет / SPA).\n */\nexport class LocalStorageTokenStorage implements TokenStorage {\n private readonly store: LocalStorageLike;\n\n constructor(\n readonly key: string,\n store?: LocalStorageLike,\n ) {\n const trimmed = key.trim();\n if (!trimmed) {\n throw new Error(\"LocalStorageTokenStorage: empty key\");\n }\n this.key = trimmed;\n this.store = store ?? resolveLocalStorage();\n }\n\n async load(): Promise<AuthTokenSnapshot> {\n try {\n const raw = this.store.getItem(this.key);\n if (!raw?.trim()) return {};\n const parsed = JSON.parse(raw) as unknown;\n if (parsed === null || typeof parsed !== \"object\") return {};\n const o = parsed as AuthTokenSnapshot;\n return {\n accessToken: typeof o.accessToken === \"string\" ? o.accessToken : undefined,\n refreshToken: typeof o.refreshToken === \"string\" ? o.refreshToken : undefined,\n accessExpiresAtMs:\n typeof o.accessExpiresAtMs === \"number\" ? o.accessExpiresAtMs : undefined,\n deviceId: typeof o.deviceId === \"string\" ? o.deviceId : undefined,\n };\n } catch {\n return {};\n }\n }\n\n async save(snapshot: AuthTokenSnapshot): Promise<void> {\n const toSave: AuthTokenSnapshot = {\n accessToken: snapshot.accessToken?.trim() || undefined,\n refreshToken: snapshot.refreshToken?.trim() || undefined,\n accessExpiresAtMs: snapshot.accessExpiresAtMs || undefined,\n deviceId: snapshot.deviceId?.trim() || undefined,\n };\n this.store.setItem(this.key, JSON.stringify(toSave));\n }\n}\n","import { TorrowHttpError } from \"@torrow/sdk-client\";\n\n/** Сессия auth ещё не подтверждена (например Telegram до Start у бота). */\nexport class AuthSessionNotReadyError extends Error {\n readonly name = \"AuthSessionNotReadyError\";\n readonly cause?: unknown;\n\n constructor(message = \"Auth session is not ready yet\", cause?: unknown) {\n super(message);\n this.cause = cause;\n }\n}\n\n/**\n * `true`, если auth-сессия ещё не готова к `requestToken` (poll в `runLinkLogin`,\n * wrap в `TokenSession.requestTokenWithCode`).\n *\n * - {@link AuthSessionNotReadyError}\n * - {@link TorrowHttpError}, если в `message` / `apiErrorName` / stringified `body`\n * (lowercase) есть `session is not ready` или `unauthorized_client`\n */\nexport function isAuthSessionNotReadyYet(e: unknown): boolean {\n if (e instanceof AuthSessionNotReadyError) return true;\n if (!(e instanceof TorrowHttpError)) return false;\n const text = [\n e.message,\n e.apiErrorName,\n typeof e.body === \"string\" ? e.body : JSON.stringify(e.body ?? \"\"),\n ]\n .join(\" \")\n .toLowerCase();\n return text.includes(\"session is not ready\") || text.includes(\"unauthorized_client\");\n}\n","/**\n * Ядро пользовательской сессии: persist + проактивный refresh + single-flight + 401-retry.\n *\n * Low-level API: {@link TokenSession.open}. Канон для приложений —\n * **`TorrowAuth.connect` / `login*` / `fromTokens`** (см. `torrow-auth.ts`).\n *\n * Логин снаружи: `TorrowAuth.loginWithOtp` / `loginWithLink`, либо\n * `requestTokenWithCode` / `applyTokenResp` на уже открытой сессии.\n * Не путать статус `\"unauthenticated\"` с `TorrowAuth.anonymous` (клиент без сессии).\n */\nimport {\n DefaultAuthScopes,\n TorrowClient,\n TorrowHttpError,\n DeviceTypeValues,\n TokenStatusValues,\n type AuthScope,\n type DeviceInfo,\n type TokenResp,\n} from \"@torrow/sdk-client\";\nimport {\n AuthSessionNotReadyError,\n isAuthSessionNotReadyYet,\n} from \"./auth-session-not-ready.js\";\nimport { createFetchWith401Retry } from \"./fetch-with-401-retry.js\";\nimport {\n accessExpiresAtMsFromJwt,\n accessTokenLooksLikeJwt,\n assertTokenRespReadyForStorage,\n mergeAuthTokens,\n normalizeTokenResp,\n} from \"./merge-tokens.js\";\nimport {\n appCredentialsToAuthIdentityBasic,\n resolveAppCredentials,\n} from \"./app-credentials.js\";\nimport type {\n AuthTokenSnapshot,\n RefreshFailureAction,\n TokenSessionOptions,\n TokenSessionStatus,\n TokenSessionStatusListener,\n TokenStorage,\n} from \"./types.js\";\nimport { createIdentityClient } from \"./identity-client.js\";\nimport { requestAuthToken, tokenTransition } from \"./auth-protocol.js\";\n\nconst DEFAULT_SKEW_MS = 30_000;\nconst SESSION_CLOSED_MESSAGE = \"TokenSession: session closed (logout)\";\n\nfunction defaultOnRefreshFailure(err: unknown): RefreshFailureAction {\n if (!(err instanceof TorrowHttpError)) return \"keep\";\n const s = err.status;\n if (s === 429 || s === 502 || s === 503 || s === 504) return \"keep\";\n return \"clear\";\n}\n\ntype RefreshPolicyResult =\n | { outcome: \"refreshed\"; resp: TokenResp }\n | { outcome: \"unchanged\" }\n | { outcome: \"cleared\" };\n\n/**\n * Пользовательская сессия (класс). См. file overview выше.\n */\nexport class TokenSession {\n private tokenSnapshot: AuthTokenSnapshot = {};\n private ensureFreshMutex: Promise<void> = Promise.resolve();\n private _client: TorrowClient | undefined;\n private readonly statusListeners = new Set<TokenSessionStatusListener>();\n private closed = false;\n /** Set in refreshWithFailurePolicy catch; not cleared on success (see public refresh). */\n private lastRefreshError: unknown;\n\n private constructor(private readonly opts: TokenSessionOptions) {}\n\n static async open(opts: TokenSessionOptions): Promise<TokenSession> {\n const session = new TokenSession(opts);\n session.tokenSnapshot = await opts.tokenStorage.load();\n if (!session.tokenSnapshot.deviceId?.trim()) {\n const di = session.resolveDeviceInfo();\n const id = di.deviceId?.trim();\n if (id) {\n session.tokenSnapshot = { ...session.tokenSnapshot, deviceId: id };\n await session.persist(false);\n }\n }\n session._client = session.buildClient();\n if (!opts.skipEnsureFreshOnOpen) {\n await session.ensureFresh();\n }\n return session;\n }\n\n /** Persist, переданный в `TokenSession.open` / `TorrowAuth.*`. */\n get tokenStorage(): TokenStorage {\n return this.opts.tokenStorage;\n }\n\n get client(): TorrowClient {\n if (!this._client) {\n throw new Error(\"TokenSession: client not initialized (call TokenSession.open)\");\n }\n return this._client;\n }\n\n /** Текущий статус сессии. */\n get status(): TokenSessionStatus {\n return this.computeStatus();\n }\n\n /** Подписка на смену статуса; возвращает unsubscribe. Сразу вызывает listener с текущим статусом. */\n onStatusChange(listener: TokenSessionStatusListener): () => void {\n this.statusListeners.add(listener);\n try {\n listener(this.computeStatus());\n } catch {\n // listener errors must not break session\n }\n return () => {\n this.statusListeners.delete(listener);\n };\n }\n\n /** Текущий снимок токенов сессии (копия {@link AuthTokenSnapshot}). */\n getTokenSnapshot(): AuthTokenSnapshot {\n return { ...this.tokenSnapshot };\n }\n\n /** `deviceInfo` из options open или fallback Desktop + deviceId из tokenSnapshot. */\n getDeviceInfo(): DeviceInfo {\n return this.resolveDeviceInfo();\n }\n\n async getAccessToken(): Promise<string | undefined> {\n if (this.closed) return undefined;\n await this.ensureFresh();\n return this.tokenSnapshot.accessToken?.trim() || undefined;\n }\n\n /**\n * Сохранить ответ `requestToken` / `refresh` (только TokenReady).\n * Сериализован с `ensureFresh` / `logout` через mutex. Дефолт merge намеренно\n * (refresh-shaped); логин — `{ replace: true }`. После `logout` — новая сессия.\n */\n async applyTokenResp(tokens: TokenResp, opts?: { replace?: boolean }): Promise<void> {\n await this.runExclusive(() => this.applyTokenRespUnlocked(tokens, opts));\n }\n\n async clearTokens(): Promise<void> {\n await this.runExclusive(() => this.clearTokensUnlocked());\n }\n\n /**\n * Revoke refresh (если есть) + очистка storage.\n * Единственное публичное имя выхода из сессии.\n */\n async logout(): Promise<void> {\n await this.runExclusive(async () => {\n this.closed = true;\n const refresh = this.tokenSnapshot.refreshToken?.trim();\n if (refresh) {\n try {\n await this.identityClient().auth.revoke({ token: refresh });\n } catch {\n // всё равно чистим локально\n }\n }\n await this.clearTokensUnlocked();\n });\n }\n\n /**\n * Получить токены по **id auth-сессии** (`sessionId` из `sendCode` / `createSession`)\n * и **OTP-коду доступа**, затем сохранить их в storage сессии.\n *\n * Один вызов `POST /api/v1/auth/token` (`requestToken`) с `{ sessionId, code }`.\n * При успехе (`TokenReady`) — persist; если сессия ещё не готова —\n * {@link AuthSessionNotReadyError} (поллинг для deep-link — внутри `TorrowAuth.loginWithLink`).\n *\n * HTTP вне mutex; persist сериализован через {@link applyTokenResp} (не оборачивать\n * весь метод в `runExclusive` — вложенный mutex с публичным `applyTokenResp`).\n */\n async requestTokenWithCode(args: {\n sessionId: string;\n code: string;\n scopes?: AuthScope[];\n signal?: AbortSignal;\n }): Promise<TokenResp> {\n this.assertNotClosedForWrite();\n const sessionId = args.sessionId.trim();\n const code = args.code.trim();\n if (!sessionId || !code) {\n throw new Error(\"TokenSession.requestTokenWithCode: sessionId and code are required\");\n }\n // Не создаём OTP-challenge: метод также обслуживает deep-link и legacy-ответы.\n // Прямой вызов строгого flow возможен только при отдельном изменении старого контракта.\n const scopes =\n args.scopes && args.scopes.length > 0 ? args.scopes : [...DefaultAuthScopes];\n const deviceInfo = this.resolveDeviceInfo();\n let resp: TokenResp;\n try {\n resp = normalizeTokenResp(\n await requestAuthToken(\n this.identityClient(),\n { sessionId, code, scopes, deviceInfo },\n args.signal,\n ),\n );\n } catch (e: unknown) {\n if (isAuthSessionNotReadyYet(e)) {\n throw new AuthSessionNotReadyError(\"Auth session is not ready yet\", e);\n }\n throw e;\n }\n if (tokenTransition(resp.status, true) !== TokenStatusValues.TokenReady) {\n throw new AuthSessionNotReadyError(\n `TokenResp.status=${resp.status} (ожидался TokenReady)`,\n );\n }\n const ready = assertTokenRespReadyForStorage(resp, \"TokenSession.requestTokenWithCode\");\n await this.applyTokenResp(ready, { replace: true });\n return ready;\n }\n\n /**\n * Явный refresh (single-flight через общий mutex сессии).\n * Сериализован с `logout` / `ensureFresh` / `applyTokenResp`.\n * При гонке с `logout()`: promise может resolve с `TokenResp`, но snapshot уже очищен —\n * не используй return value; смотри `session.status` / `getTokenSnapshot()`.\n */\n async refresh(): Promise<TokenResp> {\n return this.runExclusive(async () => {\n this.assertNotClosedForWrite();\n if (!this.tokenSnapshot.refreshToken?.trim()) {\n throw new Error(\"TokenSession.refresh: нет refreshToken в storage\");\n }\n const result = await this.refreshWithFailurePolicy();\n if (result.outcome === \"refreshed\") {\n return result.resp;\n }\n if (this.lastRefreshError !== undefined) {\n throw this.lastRefreshError;\n }\n throw new Error(\"TokenSession.refresh: refresh failed\");\n });\n }\n\n async ensureFresh(): Promise<void> {\n await this.runExclusive(() => this.ensureFreshImpl());\n }\n\n private assertNotClosedForWrite(): void {\n if (this.closed) {\n throw new Error(SESSION_CLOSED_MESSAGE);\n }\n }\n\n /** Caller must hold `runExclusive`. Do not call public `applyTokenResp` from here. */\n private async applyTokenRespUnlocked(\n tokens: TokenResp,\n opts?: { replace?: boolean },\n ): Promise<void> {\n this.assertNotClosedForWrite();\n const normalized = normalizeTokenResp(tokens);\n assertTokenRespReadyForStorage(normalized, \"TokenSession.applyTokenResp\");\n this.tokenSnapshot = mergeAuthTokens(this.tokenSnapshot, normalized, {\n ...opts,\n // Host TTL survives refresh without expiration only while still fresh (incl. skew); else cleared → opaque until 401.\n keepPrevTtlIfExpiresAfterMs:\n Date.now() + (this.opts.accessTokenSkewMs ?? DEFAULT_SKEW_MS),\n });\n await this.persist(true);\n }\n\n private async clearTokensUnlocked(): Promise<void> {\n this.tokenSnapshot = {\n ...this.tokenSnapshot,\n accessToken: undefined,\n refreshToken: undefined,\n accessExpiresAtMs: undefined,\n };\n await this.persist(true);\n }\n\n /** Caller must hold `runExclusive`. `refreshWithFailurePolicy` skips when no refreshToken; guard here is intentional defense-in-depth. */\n private async refreshUnlocked(): Promise<TokenResp> {\n const refresh = this.tokenSnapshot.refreshToken?.trim();\n if (!refresh) {\n throw new Error(\"TokenSession.refresh: нет refreshToken в storage\");\n }\n const deviceInfo = this.resolveDeviceInfo();\n const resp = await this.identityClient().auth.refresh({\n refreshToken: refresh,\n deviceInfo,\n });\n await this.applyTokenRespUnlocked(resp);\n return resp;\n }\n\n /** Caller must hold `runExclusive`. */\n private async refreshWithFailurePolicy(): Promise<RefreshPolicyResult> {\n if (this.closed) {\n return { outcome: \"unchanged\" };\n }\n const refresh = this.tokenSnapshot.refreshToken?.trim();\n if (!refresh) {\n return { outcome: \"unchanged\" };\n }\n try {\n const resp = await this.refreshUnlocked();\n this.lastRefreshError = undefined;\n return { outcome: \"refreshed\", resp };\n } catch (e: unknown) {\n this.lastRefreshError = e;\n const action = (await this.opts.onRefreshFailure?.(e)) ?? defaultOnRefreshFailure(e);\n if (action === \"throw\") throw e;\n if (action === \"keep\") {\n return { outcome: \"unchanged\" };\n }\n await this.clearTokensUnlocked();\n return { outcome: \"cleared\" };\n }\n }\n\n private async runExclusive<T>(fn: () => Promise<T>): Promise<T> {\n const prev = this.ensureFreshMutex;\n let release!: () => void;\n this.ensureFreshMutex = new Promise<void>((resolve) => {\n release = resolve;\n });\n await prev;\n try {\n return await fn();\n } finally {\n release();\n }\n }\n\n private async ensureFreshImpl(): Promise<void> {\n if (this.closed) return;\n\n const skew = this.opts.accessTokenSkewMs ?? DEFAULT_SKEW_MS;\n const access = this.tokenSnapshot.accessToken?.trim() ?? \"\";\n const expires = this.tokenSnapshot.accessExpiresAtMs ?? 0;\n if (access && this.isAccessStillFresh(access, expires, skew)) {\n return;\n }\n\n const refresh = this.tokenSnapshot.refreshToken?.trim() ?? \"\";\n if (refresh) {\n await this.refreshWithFailurePolicy();\n }\n\n // Пустая / expired сессия допустима: caller сделает loginWithOtp / requestTokenWithCode.\n }\n\n private isAccessStillFresh(access: string, expiresAtMs: number, accessTokenSkewMs: number): boolean {\n const now = Date.now();\n const jwtExpMs = accessExpiresAtMsFromJwt(access);\n if (jwtExpMs !== undefined) {\n const jwtFresh = now < jwtExpMs - accessTokenSkewMs;\n const snapFresh = !(expiresAtMs > 0) || now < expiresAtMs - accessTokenSkewMs;\n return jwtFresh && snapFresh;\n }\n if (accessTokenLooksLikeJwt(access)) {\n // undecodable JWT-shaped → never fresh; see README «JWT vs integration key»\n return false;\n }\n if (!(expiresAtMs > 0)) {\n return true;\n }\n return now < expiresAtMs - accessTokenSkewMs;\n }\n\n private computeStatus(): TokenSessionStatus {\n const skew = this.opts.accessTokenSkewMs ?? DEFAULT_SKEW_MS;\n const access = this.tokenSnapshot.accessToken?.trim() ?? \"\";\n const expires = this.tokenSnapshot.accessExpiresAtMs ?? 0;\n if (access && this.isAccessStillFresh(access, expires, skew)) {\n return \"authenticated\";\n }\n const hasAny =\n Boolean(access) ||\n Boolean(this.tokenSnapshot.refreshToken?.trim()) ||\n (typeof expires === \"number\" && expires > 0);\n if (!hasAny) return \"unauthenticated\";\n return \"expired\";\n }\n\n private emitStatusChange(): void {\n const status = this.computeStatus();\n for (const listener of this.statusListeners) {\n try {\n listener(status);\n } catch {\n // listener errors must not break session\n }\n }\n }\n\n private resolveDeviceInfo(): DeviceInfo {\n if (this.opts.deviceInfo) {\n return { ...this.opts.deviceInfo };\n }\n const deviceId = this.tokenSnapshot.deviceId?.trim();\n return {\n ...(deviceId ? { deviceId } : {}),\n deviceType: DeviceTypeValues.Desktop,\n };\n }\n\n /** Identity-клиент с Basic приложения, без Bearer пользователя. */\n identityClient(): TorrowClient {\n return createIdentityClient(this.opts);\n }\n\n private buildClient(): TorrowClient {\n let fetchImpl = this.opts.fetch;\n if (this.opts.refreshOnUnauthorized !== false) {\n fetchImpl = createFetchWith401Retry({\n fetch: this.opts.fetch,\n getAccessToken: async () => {\n if (this.closed) return undefined;\n return this.tokenSnapshot.accessToken?.trim() || undefined;\n },\n refresh: async () => {\n await this.runExclusive(async () => {\n if (this.closed) {\n throw new Error(SESSION_CLOSED_MESSAGE);\n }\n const result = await this.refreshWithFailurePolicy();\n if (result.outcome !== \"refreshed\") {\n throw (\n this.lastRefreshError ??\n new Error(\"TokenSession 401-retry: refresh failed\")\n );\n }\n });\n },\n });\n }\n\n return new TorrowClient({\n baseUrl: this.opts.baseUrl,\n fetch: fetchImpl,\n defaultHeaders: this.opts.defaultHeaders,\n userAgent: this.opts.userAgent,\n authIdentityBasic: appCredentialsToAuthIdentityBasic(\n resolveAppCredentials(this.opts.appCredentials),\n ),\n getAccessToken: async () => this.getAccessToken(),\n });\n }\n\n private async persist(emitStatus: boolean): Promise<void> {\n await this.opts.tokenStorage.save(this.tokenSnapshot);\n if (emitStatus) this.emitStatusChange();\n }\n}\n","/**\n * Обёртка `fetch` для **`refreshOnUnauthorized`**: на **401** (не `/api/v1/auth/*`)\n * вызывает `refresh` и один раз повторяет запрос с новым Bearer.\n *\n * Ставит `TokenSession` при сборке `client`, если `refreshOnUnauthorized !== false`.\n * Single-flight refresh — снаружи (mutex в `TokenSession`).\n */\nexport type FetchWith401RetryOptions = {\n getAccessToken: () => Promise<string | undefined>;\n refresh: () => Promise<void>;\n fetch?: typeof fetch;\n};\n\nfunction requestUrl(input: RequestInfo | URL): string {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.href;\n return input.url;\n}\n\nfunction isAuthIdentityUrl(url: string): boolean {\n return url.includes(\"/api/v1/auth/\");\n}\n\nexport function createFetchWith401Retry(options: FetchWith401RetryOptions): typeof fetch {\n const baseFetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n\n const wrapped: typeof fetch = async (input, init) => {\n const first = await baseFetch(input, init);\n if (first.status !== 401) return first;\n const url = requestUrl(input);\n if (isAuthIdentityUrl(url)) return first;\n\n await options.refresh();\n const token = await options.getAccessToken();\n const headers = new Headers(init?.headers);\n if (token) {\n headers.set(\"Authorization\", `Bearer ${token}`);\n } else {\n headers.delete(\"Authorization\");\n }\n return baseFetch(input, { ...init, headers });\n };\n\n return wrapped;\n}\n","/**\n * Утилиты ответа auth API → локальный {@link AuthTokenSnapshot}.\n *\n * Нормализация PascalCase/snake_case, merge access/refresh, TTL из JWT,\n * assert `TokenReady` перед persist. Используется `TokenSession` и OTP/link runners.\n */\nimport type { TokenResp } from \"@torrow/sdk-client\";\nimport { isTokenRespReadyStatus } from \"@torrow/sdk-client\";\nimport type { AuthTokenSnapshot } from \"./types.js\";\nimport { tokenResponseFields } from \"./auth-protocol.js\";\n\n/**\n * Нормализация сырого ответа token/refresh (PascalCase / snake_case на некоторых шлюзах).\n */\nexport function normalizeTokenResp(raw: unknown): TokenResp {\n if (raw === null || raw === undefined) return {};\n if (typeof raw === \"string\") {\n const t = raw.trim();\n return t ? { accessToken: t } : {};\n }\n if (typeof raw !== \"object\") return {};\n const o = raw as Record<string, unknown>;\n const { access, refresh, expiration: expRaw, status: statusRaw } = tokenResponseFields(o);\n const out: TokenResp = {};\n if (typeof access === \"string\" && access.trim()) out.accessToken = access.trim();\n if (typeof refresh === \"string\" && refresh.trim()) out.refreshToken = refresh.trim();\n if (typeof expRaw === \"number\" && Number.isFinite(expRaw)) out.expiration = expRaw;\n if (typeof statusRaw === \"string\" && statusRaw.trim()) {\n out.status = statusRaw.trim() as TokenResp[\"status\"];\n }\n return out;\n}\n\n/** Decode one JWT segment (base64url → UTF-8 JSON). Node 18+ and browsers (no Buffer). */\nfunction decodeJwtJsonSegment(segment: string): unknown | undefined {\n try {\n const b64 = segment.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const pad = b64.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (b64.length % 4));\n const binary = atob(b64 + pad);\n const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));\n return JSON.parse(new TextDecoder().decode(bytes)) as unknown;\n } catch {\n return undefined;\n }\n}\n\n/**\n * `exp` JWT → мс; подпись не проверяется.\n * Требует валидный JSON и в header, и в payload — иначе битый access\n * (например `e12yJ…` вместо `eyJ…`) давал бы ложный «свежий» TTL по payload.\n */\nexport function accessExpiresAtMsFromJwt(accessToken: string): number | undefined {\n const parts = accessToken.split(\".\");\n if (parts.length < 2) return undefined;\n if (decodeJwtJsonSegment(parts[0]!) === undefined) return undefined;\n const payload = decodeJwtJsonSegment(parts[1]!);\n if (payload === null || typeof payload !== \"object\") return undefined;\n const exp = (payload as { exp?: unknown }).exp;\n if (typeof exp !== \"number\" || !Number.isFinite(exp)) return undefined;\n return exp * 1000;\n}\n\n/**\n * Heuristic: 3+ dot segments = JWT-shaped wire token.\n * Opaque integration keys must not look like `a.b.c` — not supported as opaque.\n */\nexport function accessTokenLooksLikeJwt(accessToken: string): boolean {\n return accessToken.split(\".\").length >= 3;\n}\n\nexport type MergeAuthTokensOptions = {\n replace?: boolean;\n /**\n * Opaque refresh без `expiration`: prev host TTL сохраняется только если\n * `prev.accessExpiresAtMs` строго больше этого порога (default `Date.now()`).\n * `TokenSession` передаёт `Date.now() + accessTokenSkewMs`.\n */\n keepPrevTtlIfExpiresAfterMs?: number;\n};\n\nexport function mergeAuthTokens(\n prev: AuthTokenSnapshot,\n tokens: TokenResp,\n opts?: MergeAuthTokensOptions,\n): AuthTokenSnapshot {\n const replace = opts?.replace === true;\n const nextAccess = (\n replace\n ? tokens.accessToken?.trim() || \"\"\n : tokens.accessToken?.trim() || prev.accessToken?.trim() || \"\"\n ).trim();\n const refreshToken = replace\n ? tokens.refreshToken?.trim() || \"\"\n : tokens.refreshToken?.trim() || prev.refreshToken?.trim() || \"\";\n\n const ttlSec = tokens.expiration ?? 0;\n let accessExpiresAtMs: number | undefined;\n if (ttlSec > 0) {\n accessExpiresAtMs = Date.now() + ttlSec * 1000;\n } else {\n const keepAfter = opts?.keepPrevTtlIfExpiresAfterMs ?? Date.now();\n const jwtExp = nextAccess ? accessExpiresAtMsFromJwt(nextAccess) : undefined;\n if (typeof jwtExp === \"number\") {\n accessExpiresAtMs = jwtExp;\n } else if (\n !replace &&\n typeof prev.accessExpiresAtMs === \"number\" &&\n prev.accessExpiresAtMs > keepAfter\n ) {\n // Host TTL survives refresh without expiration only while still fresh (incl. caller skew); else cleared → opaque until 401.\n accessExpiresAtMs = prev.accessExpiresAtMs;\n } else {\n accessExpiresAtMs = undefined;\n }\n }\n\n return {\n ...prev,\n accessToken: nextAccess || undefined,\n refreshToken: refreshToken || undefined,\n accessExpiresAtMs,\n };\n}\n\n/** Проверка перед сохранением: только TokenReady / legacy без status. */\nexport function assertTokenRespReadyForStorage(tokens: TokenResp, context: string): TokenResp {\n const status = tokens.status;\n if (!isTokenRespReadyStatus(status)) {\n throw new Error(\n `${context}: TokenResp.status=${status} (ожидался TokenReady). Токены не сохраняем.`,\n );\n }\n if (!tokens.accessToken?.trim() && !tokens.refreshToken?.trim()) {\n throw new Error(`${context}: TokenResp без accessToken и refreshToken.`);\n }\n return tokens;\n}\n","/**\n * Публичный фасад аутентификации **`TorrowAuth`**.\n *\n * Точка входа приложения: `createOtpFlow` / `connect` / `loginWithOtp` / `loginWithLink` /\n * `fromTokens` / `anonymous`. Возвращает {@link TorrowAuthResult}\n * (`{ client, session }`), кроме `anonymous` (только `TorrowClient`).\n *\n * Оркестрация OTP/Telegram — в `otp-login.ts` / `link-login.ts`;\n * persist и refresh — в {@link TokenSession}.\n */\nimport {\n TorrowClient,\n type AccountType,\n type AuthCaptchaType,\n type AuthScope,\n type DeviceInfo,\n type SendCodeAccountType,\n type VerificationMethod,\n} from \"@torrow/sdk-client\";\nimport { accessExpiresAtMsFromJwt } from \"./merge-tokens.js\";\nimport { runLinkLogin } from \"./link-login.js\";\nimport {\n runOtpLogin,\n type OtpSendCodeResult,\n type OtpGetCodeContext,\n} from \"./otp-login.js\";\nimport { TokenSession } from \"./TokenSession.js\";\nimport type { TokenSessionOptions } from \"./types.js\";\nimport { createOtpFlow } from \"./otp-flow.js\";\n\n/** Результат `TorrowAuth.connect` / `loginWithOtp` / `loginWithLink` / `fromTokens`: API-клиент + сессия. */\nexport type TorrowAuthResult = {\n client: TorrowClient;\n session: TokenSession;\n};\n\n/**\n * Общая база опций `TorrowAuth.connect` / `loginWithOtp` / `loginWithLink` / `fromTokens`.\n *\n * Производная от {@link TokenSessionOptions}: те же HTTP-кнопки + storage + app creds,\n * но `deviceInfo` **обязателен**; `onRefreshFailure` пробрасывается в `TokenSession.open`.\n *\n * **Карта:** `TorrowClientConfig` → (Pick HTTP) → `TokenSessionOptions` → **эта база** →\n * `TorrowAuthConnectOptions` / `TorrowAuthLoginWithOtpOptions` / …\n * Приложение обычно передаёт именно `TorrowAuth*Options`, не `TorrowClientConfig`.\n *\n * `appCredentials` опционален: без него — дефолт SDK (`DEFAULT_APP_CREDENTIALS`).\n */\nexport type TorrowAuthSessionBase = Omit<TokenSessionOptions, \"deviceInfo\"> & {\n deviceInfo: DeviceInfo;\n};\n\n/** Опции {@link TorrowAuth.connect}: restore / refresh из `tokenStorage`. */\nexport type TorrowAuthConnectOptions = TorrowAuthSessionBase;\n\n/** Аккаунт для OTP `sendCode` — плоско, как OpenAPI `SendCodeReq`. */\nexport type TorrowAuthAccount = {\n /** `Phone` \\| `Email` → `SendCodeReq.type`. */\n accountType: SendCodeAccountType;\n /** Телефон или email → `SendCodeReq.accountName`. */\n accountName: string;\n};\n\n/** Опции {@link TorrowAuth.loginWithOtp}: connect-база + OTP-колбэки / поля `sendCode`. */\nexport type TorrowAuthLoginWithOtpOptions = TorrowAuthSessionBase & {\n account: TorrowAuthAccount;\n /**\n * Сигнал UI после `sendCode` (`usedMethod`, `codeLength`, …).\n * До Mobile ID poll и до classic `getCode`. См. {@link OtpSendCodeResult}.\n */\n onSendCode: (result: OtpSendCodeResult) => void | Promise<void>;\n /**\n * Вернуть одноразовый код. Не зовётся на happy-path Mobile ID (push без SMS).\n * См. {@link OtpGetCodeContext}.\n */\n getCode: (ctx: OtpGetCodeContext) => Promise<string>;\n /** → `TokenReq.scopes`. По умолчанию `DefaultAuthScopes`. */\n scopes?: AuthScope[];\n /** → `SendCodeReq.language`. По умолчанию `\"ru\"`. */\n language?: string;\n /** → `SendCodeReq.regionStorageId`. По умолчанию `70`. */\n regionStorageId?: number;\n /** → `SendCodeReq.captcha` (если шлюз требует). */\n captcha?: string;\n /** → `SendCodeReq.captchaType`. */\n captchaType?: AuthCaptchaType;\n /** → `SendCodeReq.inviteSessionId`. */\n inviteSessionId?: string;\n /**\n * Hint серверу → `SendCodeReq.preferredMethod`.\n * Факт смотри в `onSendCode.usedMethod`.\n */\n preferredMethod?: VerificationMethod;\n /**\n * Capability клиента → `SendCodeReq.supportedMethods`.\n * Без `MtsMobileId` сервер обычно не выберет Mobile ID.\n */\n supportedMethods?: VerificationMethod[];\n /** Отмена `sendCode` / poll / `requestToken`. */\n signal?: AbortSignal;\n};\n\n/** Опции {@link TorrowAuth.loginWithLink} (Telegram deep-link). */\nexport type TorrowAuthLoginWithLinkOptions = TorrowAuthSessionBase & {\n /** Показать deep-link / QR (`CreateAuthSessionResp.authUrl`). */\n onLink: (authUrl: string) => void | Promise<void>;\n /** По умолчанию `Telegram`. */\n accountType?: AccountType;\n language?: string;\n regionStorageId?: number;\n captcha?: string;\n inviteSessionId?: string;\n scopes?: AuthScope[];\n /** Интервал poll `requestToken` (мс). По умолчанию 2000. */\n pollIntervalMs?: number;\n /** Max wait poll (сек). Иначе TTL сессии из ответа или 180. */\n pollTimeoutSec?: number;\n signal?: AbortSignal;\n};\n\n/** Опции {@link TorrowAuth.fromTokens}: сид токенов от хоста в `tokenStorage` → connect. */\nexport type TorrowAuthFromTokensOptions = TorrowAuthSessionBase & {\n accessToken: string;\n /** Без него refresh недоступен (даже если задан `appCredentials`). */\n refreshToken?: string;\n /**\n * Абсолютное истечение access (мс). Для opaque/integration keys без JWT `exp`.\n * Приоритет **записи в snapshot**: explicit (`> 0` finite) → JWT `exp` → `undefined` (бессрочный ключ).\n * Runtime freshness AND-ит JWT `exp`: будущий explicit TTL не оживит просроченный JWT.\n * Opaque без JWT-формы: explicit TTL или бессрочно без TTL.\n */\n accessExpiresAtMs?: number;\n};\n\nfunction sessionOpenOpts(base: TorrowAuthSessionBase) {\n // Intentional allowlist: login-only fields on TorrowAuth*Options must not leak into TokenSession.open.\n return {\n appCredentials: base.appCredentials,\n tokenStorage: base.tokenStorage,\n deviceInfo: base.deviceInfo,\n baseUrl: base.baseUrl,\n fetch: base.fetch,\n defaultHeaders: base.defaultHeaders,\n userAgent: base.userAgent,\n accessTokenSkewMs: base.accessTokenSkewMs,\n refreshOnUnauthorized: base.refreshOnUnauthorized,\n onRefreshFailure: base.onRefreshFailure,\n };\n}\n\n// Inherits onRefreshFailure (and other sessionOpenOpts fields) via spread — same forwarding as connect/fromTokens.\nfunction sessionOpenOptsForLogin(base: TorrowAuthSessionBase) {\n return { ...sessionOpenOpts(base), skipEnsureFreshOnOpen: true as const };\n}\n\n/**\n * Фасад аутентификации для приложений и виджетов.\n *\n * - {@link TorrowAuth.connect} — restore / refresh\n * - {@link TorrowAuth.createOtpFlow} — раздельные OTP-шаги без сессии\n * - {@link TorrowAuth.loginWithOtp} — OTP (Phone/Email, Mobile ID / classic)\n * - {@link TorrowAuth.loginWithLink} — Telegram deep-link\n * - {@link TorrowAuth.fromTokens} — сид токенов от хоста\n * - {@link TorrowAuth.anonymous} — клиент без Bearer\n */\nexport const TorrowAuth = {\n /** Пошаговый OTP без storage, polling и refresh. */\n createOtpFlow,\n async connect(options: TorrowAuthConnectOptions): Promise<TorrowAuthResult> {\n const session = await TokenSession.open(sessionOpenOpts(options));\n return { client: session.client, session };\n },\n\n async loginWithOtp(options: TorrowAuthLoginWithOtpOptions): Promise<TorrowAuthResult> {\n const accountName = options.account.accountName.trim();\n if (!accountName) {\n throw new Error(\"TorrowAuth.loginWithOtp: account.accountName is empty\");\n }\n const session = await TokenSession.open(sessionOpenOptsForLogin(options));\n const deviceInfo = session.getDeviceInfo();\n const ready = await runOtpLogin(session.identityClient(), {\n accountName,\n type: options.account.accountType,\n deviceInfo,\n onSendCode: options.onSendCode,\n getCode: options.getCode,\n scopes: options.scopes,\n language: options.language,\n regionStorageId: options.regionStorageId,\n captcha: options.captcha,\n captchaType: options.captchaType,\n inviteSessionId: options.inviteSessionId,\n preferredMethod: options.preferredMethod,\n supportedMethods: options.supportedMethods,\n signal: options.signal,\n });\n await session.applyTokenResp(ready, { replace: true });\n return { client: session.client, session };\n },\n\n async loginWithLink(options: TorrowAuthLoginWithLinkOptions): Promise<TorrowAuthResult> {\n const session = await TokenSession.open(sessionOpenOptsForLogin(options));\n await runLinkLogin(session, {\n deviceInfo: session.getDeviceInfo(),\n onLink: options.onLink,\n accountType: options.accountType,\n language: options.language,\n regionStorageId: options.regionStorageId,\n captcha: options.captcha,\n inviteSessionId: options.inviteSessionId,\n scopes: options.scopes,\n pollIntervalMs: options.pollIntervalMs,\n pollTimeoutSec: options.pollTimeoutSec,\n signal: options.signal,\n });\n return { client: session.client, session };\n },\n\n async fromTokens(options: TorrowAuthFromTokensOptions): Promise<TorrowAuthResult> {\n const accessToken = options.accessToken.trim();\n if (!accessToken) {\n throw new Error(\"TorrowAuth.fromTokens: accessToken is empty\");\n }\n const refreshToken = options.refreshToken?.trim() || undefined;\n const prev = await options.tokenStorage.load();\n const explicitTtl = options.accessExpiresAtMs;\n const accessExpiresAtMs =\n typeof explicitTtl === \"number\" && Number.isFinite(explicitTtl) && explicitTtl > 0\n ? explicitTtl\n : accessExpiresAtMsFromJwt(accessToken);\n await options.tokenStorage.save({\n accessToken,\n refreshToken,\n ...(typeof accessExpiresAtMs === \"number\" && accessExpiresAtMs > 0\n ? { accessExpiresAtMs }\n : { accessExpiresAtMs: undefined }),\n deviceId: options.deviceInfo.deviceId?.trim() || prev.deviceId?.trim() || undefined,\n });\n return TorrowAuth.connect(options);\n },\n\n anonymous(\n config: Pick<TokenSessionOptions, \"baseUrl\" | \"fetch\" | \"defaultHeaders\" | \"userAgent\"> = {},\n ): TorrowClient {\n return new TorrowClient({\n ...config,\n getAccessToken: undefined,\n });\n },\n} as const;\n","/**\n * Внутренний оркестратор Telegram deep-link (не публичный export).\n *\n * **`createSession`** → `onLink(authUrl)` → poll **`requestToken`** до ready →\n * persist через `TokenSession`. Вызывается из `TorrowAuth.loginWithLink`.\n */\nimport {\n AccountTypeValues,\n DefaultAuthScopes,\n type AccountType,\n type AuthScope,\n type CreateAuthSessionReq,\n type DeviceInfo,\n type TokenResp,\n} from \"@torrow/sdk-client\";\nimport { isAuthSessionNotReadyYet } from \"./auth-session-not-ready.js\";\nimport type { TokenSession } from \"./TokenSession.js\";\nimport { sleepMs } from \"./sleep.js\";\n\nconst DEFAULT_POLL_INTERVAL_MS = 2000;\nconst DEFAULT_POLL_TIMEOUT_SEC = 180;\nconst DEFAULT_LANGUAGE = \"ru\";\nconst DEFAULT_REGION = 70;\n\nexport type RunLinkLoginOptions = {\n deviceInfo: DeviceInfo;\n /** Показать deep-link / QR (`CreateAuthSessionResp.authUrl`). Код OTP сюда не вводят. */\n onLink: (authUrl: string) => void | Promise<void>;\n /** По умолчанию `Telegram`. */\n accountType?: AccountType;\n /** → `CreateAuthSessionReq.language`. По умолчанию `\"ru\"`. */\n language?: string;\n /** → `CreateAuthSessionReq.regionStorageId`. По умолчанию `70`. */\n regionStorageId?: number;\n captcha?: string;\n inviteSessionId?: string;\n /** → `TokenReq.scopes` при poll. По умолчанию `DefaultAuthScopes`. */\n scopes?: AuthScope[];\n /** Интервал poll (мс). По умолчанию 2000. */\n pollIntervalMs?: number;\n /** Max wait (сек). Иначе `sessionLifeTimeInSeconds` из ответа или 180. */\n pollTimeoutSec?: number;\n signal?: AbortSignal;\n};\n\n/**\n * `createSession` → `onLink` → poll `session.requestTokenWithCode` до TokenReady.\n */\nexport async function runLinkLogin(\n session: TokenSession,\n options: RunLinkLoginOptions,\n): Promise<TokenResp> {\n const client = session.identityClient();\n const accountType = options.accountType ?? AccountTypeValues.Telegram;\n const body: CreateAuthSessionReq = {\n accountType,\n language: options.language?.trim() || DEFAULT_LANGUAGE,\n regionStorageId:\n typeof options.regionStorageId === \"number\" && Number.isFinite(options.regionStorageId)\n ? options.regionStorageId\n : DEFAULT_REGION,\n ...(options.inviteSessionId?.trim()\n ? { inviteSessionId: options.inviteSessionId.trim() }\n : {}),\n ...(options.captcha?.trim() ? { captcha: options.captcha.trim() } : {}),\n ...(options.deviceInfo.deviceId?.trim()\n ? { deviceId: options.deviceInfo.deviceId.trim() }\n : {}),\n };\n\n const createResp = await client.auth.createSession(body, { signal: options.signal });\n const sessionId = createResp.sessionId?.trim();\n const code = createResp.code?.trim();\n const authUrl = createResp.authUrl?.trim();\n if (!sessionId || !code || !authUrl) {\n throw new Error(\n \"runLinkLogin: createSession response incomplete (need sessionId, code, authUrl)\",\n );\n }\n\n await options.onLink(authUrl);\n\n const scopes =\n options.scopes && options.scopes.length > 0 ? options.scopes : [...DefaultAuthScopes];\n const intervalMs =\n typeof options.pollIntervalMs === \"number\" && options.pollIntervalMs > 0\n ? options.pollIntervalMs\n : DEFAULT_POLL_INTERVAL_MS;\n const timeoutSec =\n typeof options.pollTimeoutSec === \"number\" && options.pollTimeoutSec > 0\n ? options.pollTimeoutSec\n : typeof createResp.sessionLifeTimeInSeconds === \"number\" &&\n createResp.sessionLifeTimeInSeconds > 0\n ? createResp.sessionLifeTimeInSeconds\n : DEFAULT_POLL_TIMEOUT_SEC;\n\n const deadline = Date.now() + timeoutSec * 1000;\n\n for (;;) {\n options.signal?.throwIfAborted?.();\n try {\n return await session.requestTokenWithCode({\n sessionId,\n code,\n scopes,\n signal: options.signal,\n });\n } catch (e: unknown) {\n if (!isAuthSessionNotReadyYet(e)) throw e;\n if (Date.now() >= deadline) {\n throw new Error(\n `runLinkLogin: timeout ~${timeoutSec}s waiting for auth session to become ready`,\n );\n }\n await sleepMs(intervalMs, options.signal);\n }\n }\n}\n","/** Sleep with optional AbortSignal; removes abort listener on resolve or abort. */\nexport function sleepMs(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new Error(\"Aborted\"));\n return;\n }\n const onAbort = (): void => {\n clearTimeout(t);\n signal?.removeEventListener(\"abort\", onAbort);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n const t = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n","/**\n * Внутренний оркестратор OTP-логина (не публичный export целиком; типы UI — да).\n *\n * **`sendCode`** → ветка по **`usedMethod`** (Mobile ID poll | classic код) →\n * **`TokenResp`**. UI снаружи: **`onSendCode`** + **`getCode`**.\n * IMAP / stdin / env — в app / `examples/node/_shared`, не здесь.\n *\n * Вызывается из `TorrowAuth.loginWithOtp`; Mobile ID — `mobile-id.ts`.\n */\nimport {\n DefaultAuthScopes,\n SendCodeAccountTypeValues,\n TorrowHttpError,\n VerificationMethodValues,\n TokenStatusValues,\n type AuthCaptchaType,\n type AuthScope,\n type DeviceInfo,\n type SendCodeAccountType,\n type SendCodeReqForOtp,\n type SendCodeResp,\n type TokenResp,\n type VerificationMethod,\n} from \"@torrow/sdk-client\";\nimport { assertTokenRespReadyForStorage, normalizeTokenResp } from \"./merge-tokens.js\";\nimport { pollMobileIdUntilReady, type MobileIdAuthClient } from \"./mobile-id.js\";\nimport { requestAuthToken, sendOtpCode, tokenTransition } from \"./auth-protocol.js\";\n\n/** Как в torrow-apps: сервер часто шлёт `codeLength: 0` для Mobile ID. */\nconst MOBILE_ID_SMS_CODE_LENGTH = 4;\n\nexport type OtpAuthClient = MobileIdAuthClient & {\n auth: MobileIdAuthClient[\"auth\"] & {\n sendCode(body: SendCodeReqForOtp, options?: { signal?: AbortSignal }): Promise<SendCodeResp>;\n };\n};\n\n/**\n * Аргумент **`onSendCode`**: снимок **после** успешного `sendCode` (в основном поля **`SendCodeResp`**).\n * Не путать с телом запроса: в `sendCode` уходят `supportedMethods` / `preferredMethod`\n * (см. {@link RunOtpLoginOptions}); здесь — что сервер **выбрал** и что ещё доступно.\n *\n * Типичный UI: по `usedMethod` показать «ждём SMS» / «код на email» / «подтвердите push»;\n * по `codeLength` — маску поля ввода.\n */\nexport type OtpSendCodeResult = {\n /** Аккаунт из `SendCodeReq` (телефон / email). */\n accountName: string;\n /** `Phone` \\| `Email` из запроса (не путать с `usedMethod`). */\n accountType: SendCodeAccountType;\n /** Id auth-сессии для последующего `requestToken` / Mobile ID poll. */\n sessionId: string;\n /**\n * Фактический канал из **`SendCodeResp.usedMethod`** — ради этого и нужен `onSendCode`.\n * Sms / Email / FlashCall / TelegramGateway / MtsMobileId / …\n */\n usedMethod?: VerificationMethod;\n /** Длина кода для input/маски; у Mobile ID часто `0` (keypad не нужен). */\n codeLength?: number;\n /** Интервал poll при Mobile ID (`SendCodeResp.pollIntervalSeconds`). */\n pollIntervalSeconds?: number;\n /**\n * Из **`SendCodeResp`** (не тело `sendCode`): методы, доступные для этого аккаунта\n * (UX «другой способ»). В запрос передаётся {@link RunOtpLoginOptions.supportedMethods}.\n */\n availableMethods?: VerificationMethod[] | null;\n /** TTL auth-сессии `sendCode` (сек); для Mobile ID — ориентир max wait. */\n sessionLifeTimeInSeconds?: number;\n /** Когда снова можно дернуть `sendCode` (rate-limit hint из ответа). */\n nextAvailableUsageTime?: Date | null;\n};\n\n/**\n * Аргумент **`getCode`**: только когда нужны цифры (stdin / UI / IMAP снаружи).\n *\n * UI-экран уже выбран в **`onSendCode`** (`usedMethod`). Здесь — собрать код\n * (длина из `codeLength`). На happy-path Mobile ID (push) **`getCode` не вызывается**;\n * SMS-fallback после poll — тот же колбэк, без отдельного `reason`.\n *\n * `accountName` / `accountType` не дублируются — они в options и в `onSendCode`.\n */\nexport type OtpGetCodeContext = {\n sessionId: string;\n /** Эффективная длина для валидации (Mobile ID SMS fallback: при `0` с сервера → 4). */\n codeLength?: number;\n /** Канал из `sendCode` (опциональная подсказка; экран уже из `onSendCode`). */\n usedMethod?: VerificationMethod;\n};\n\n/**\n * Опции внутренней OTP-оркестрации (`runOtpLogin`).\n * Публичный вход приложения — обычно `TorrowAuth.loginWithOtp` с теми же колбэками.\n */\nexport type RunOtpLoginOptions = {\n /** Телефон или email → `SendCodeReq.accountName`. */\n accountName: string;\n /** Тип аккаунта в `sendCode`: только `Phone` \\| `Email`. */\n type: SendCodeAccountType;\n /** Обязателен: уходит в `TokenReq.deviceInfo` (и Mobile ID poll). */\n deviceInfo: DeviceInfo;\n /**\n * Обязателен: сигнал UI после `sendCode` (`usedMethod`, `codeLength`, …).\n * Вызывается до Mobile ID poll и до classic `getCode`.\n */\n onSendCode: (result: OtpSendCodeResult) => void | Promise<void>;\n /**\n * Вернуть одноразовый код (stdin / UI / IMAP снаружи).\n * Не вызывается, если Mobile ID завершился push'ем без SMS.\n */\n getCode: (ctx: OtpGetCodeContext) => Promise<string>;\n /**\n * Запрашиваемые scopes → `TokenReq.scopes` (`AuthScopeValues`).\n * По умолчанию {@link DefaultAuthScopes} (`user_api` + `offline_access`).\n */\n scopes?: AuthScope[];\n /** Язык `sendCode`. По умолчанию `\"ru\"`. */\n language?: string;\n /** Регион хранилища → `SendCodeReq.regionStorageId`. По умолчанию `70`. */\n regionStorageId?: number;\n /** Токен captcha для `SendCodeReq.captcha` (если шлюз требует). */\n captcha?: string;\n /** Провайдер captcha → `SendCodeReq.captchaType` (`AuthCaptchaTypeValues`). */\n captchaType?: AuthCaptchaType;\n /** Сессия приглашения → `SendCodeReq.inviteSessionId` (вход по invite). */\n inviteSessionId?: string;\n /**\n * Желаемый способ доставки → `SendCodeReq.preferredMethod`.\n * Hint серверу; факт смотри в `onSendCode` → `usedMethod`.\n */\n preferredMethod?: VerificationMethod;\n /**\n * Capability клиента → `SendCodeReq.supportedMethods`.\n * Без `MtsMobileId` в списке сервер обычно не выберет Mobile ID.\n * `TelegramGateway` — classic OTP (код в TG), не deep-link `createSession`.\n */\n supportedMethods?: VerificationMethod[];\n /** Отмена `sendCode` / poll / `requestToken` (пробрасывается в `fetch`). */\n signal?: AbortSignal;\n};\n\n/** Превышен лимит `sendCode` (`CODE_REQUEST_LIMIT_EXCEEDED`). */\nexport class OtpCodeRequestLimitExceededError extends Error {\n readonly name = \"OtpCodeRequestLimitExceededError\";\n readonly minutes?: string;\n readonly seconds?: string;\n readonly traceUrl?: string;\n readonly apiMessage?: string;\n readonly body: unknown;\n\n constructor(body: unknown) {\n const parsed = parseRateLimitBody(body);\n const wait =\n parsed.min || parsed.sec\n ? `Повтор примерно через ${[parsed.min && `${parsed.min} мин.`, parsed.sec && `${parsed.sec} сек.`]\n .filter(Boolean)\n .join(\" \")}.`\n : \"Точное время ожидания сервер не передал.\";\n super(`sendCode: CODE_REQUEST_LIMIT_EXCEEDED. ${wait}`);\n this.minutes = parsed.min;\n this.seconds = parsed.sec;\n this.traceUrl = parsed.trace;\n this.apiMessage = parsed.message;\n this.body = body;\n }\n}\n\n/** Нет Basic на `/auth/token` (`AUTH_HEADER_MISSING`). */\nexport class OtpAuthHeaderMissingError extends Error {\n readonly name = \"OtpAuthHeaderMissingError\";\n readonly body: unknown;\n readonly systemMessage?: string;\n\n constructor(body: unknown) {\n const sys =\n body !== null &&\n typeof body === \"object\" &&\n typeof (body as { systemMessage?: unknown }).systemMessage === \"string\"\n ? (body as { systemMessage: string }).systemMessage.trim()\n : undefined;\n super(\n [\n \"requestToken: AUTH_HEADER_MISSING — задай appCredentials: { clientId, clientSecret }.\",\n sys ? `Сообщение сервера: ${sys}` : \"\",\n ]\n .filter(Boolean)\n .join(\" \"),\n );\n this.body = body;\n this.systemMessage = sys;\n }\n}\n\nfunction parseRateLimitBody(body: unknown): {\n min?: string;\n sec?: string;\n message?: string;\n trace?: string;\n} {\n if (body === null || typeof body !== \"object\") return {};\n const o = body as {\n message?: unknown;\n parameters?: { minutes?: unknown; seconds?: unknown };\n traceUrl?: unknown;\n };\n const p = o.parameters ?? {};\n return {\n min: p.minutes != null && String(p.minutes) !== \"\" ? String(p.minutes) : undefined,\n sec: p.seconds != null && String(p.seconds) !== \"\" ? String(p.seconds) : undefined,\n message: typeof o.message === \"string\" && o.message.trim() ? o.message.trim() : undefined,\n trace: typeof o.traceUrl === \"string\" && o.traceUrl.trim() ? o.traceUrl.trim() : undefined,\n };\n}\n\nfunction isSendCodeRateLimitedBody(body: unknown): boolean {\n if (body === null || typeof body !== \"object\") return false;\n return (body as Record<string, unknown>).name === \"CODE_REQUEST_LIMIT_EXCEEDED\";\n}\n\nfunction isAuthHeaderMissingBody(body: unknown): boolean {\n if (body === null || typeof body !== \"object\") return false;\n return (body as Record<string, unknown>).name === \"AUTH_HEADER_MISSING\";\n}\n\nfunction httpErrorBody(e: unknown): unknown | undefined {\n if (e instanceof TorrowHttpError) return e.body;\n if (e !== null && typeof e === \"object\" && \"body\" in e) {\n return (e as { body: unknown }).body;\n }\n return undefined;\n}\n\nfunction scopesOrDefault(scopes: AuthScope[] | undefined): AuthScope[] {\n return scopes && scopes.length > 0 ? scopes : [...DefaultAuthScopes];\n}\n\nfunction effectiveCodeLength(raw: number | undefined, fallback: number): number | undefined {\n if (typeof raw === \"number\" && raw > 0) return raw;\n return fallback > 0 ? fallback : undefined;\n}\n\nasync function requestTokenOrThrow(\n client: OtpAuthClient,\n body: Parameters<typeof requestAuthToken>[1],\n signal?: AbortSignal,\n): Promise<TokenResp> {\n try {\n return normalizeTokenResp(await requestAuthToken(client, body, signal));\n } catch (e: unknown) {\n const errBody = httpErrorBody(e);\n if (errBody !== undefined && isAuthHeaderMissingBody(errBody)) {\n throw new OtpAuthHeaderMissingError(errBody);\n }\n throw e;\n }\n}\n\n/**\n * `sendCode` → `onSendCode` → Mobile ID poll **или** classic `getCode` + `requestToken`.\n * Каждый вызов — новый `sendCode`.\n */\nexport async function runOtpLogin(\n client: OtpAuthClient,\n options: RunOtpLoginOptions,\n): Promise<TokenResp> {\n const accountName = options.accountName.trim();\n const accountType = options.type;\n const scopes = scopesOrDefault(options.scopes);\n const signal = options.signal;\n\n let sendResp: SendCodeResp;\n try {\n sendResp = await sendOtpCode(client, options, signal);\n } catch (e: unknown) {\n const errBody = httpErrorBody(e);\n if (errBody !== undefined && isSendCodeRateLimitedBody(errBody)) {\n throw new OtpCodeRequestLimitExceededError(errBody);\n }\n throw e;\n }\n\n const sessionId = sendResp.sessionId?.trim();\n if (!sessionId) {\n throw new Error(\"runOtpLogin: sendCode response had no sessionId\");\n }\n\n const result: OtpSendCodeResult = {\n accountName,\n accountType,\n sessionId,\n usedMethod: sendResp.usedMethod,\n codeLength: sendResp.codeLength,\n pollIntervalSeconds: sendResp.pollIntervalSeconds,\n availableMethods: sendResp.availableMethods,\n sessionLifeTimeInSeconds: sendResp.sessionLifeTimeInSeconds,\n nextAvailableUsageTime: sendResp.nextAvailableUsageTime ?? null,\n };\n await options.onSendCode(result);\n\n if (sendResp.usedMethod === VerificationMethodValues.MtsMobileId) {\n try {\n return await pollMobileIdUntilReady(client, {\n sessionId,\n deviceInfo: options.deviceInfo,\n scopes,\n pollIntervalSeconds: sendResp.pollIntervalSeconds,\n maxWaitSeconds: sendResp.sessionLifeTimeInSeconds,\n signal,\n smsCodeLength:\n typeof sendResp.codeLength === \"number\" && sendResp.codeLength > 0\n ? sendResp.codeLength\n : MOBILE_ID_SMS_CODE_LENGTH,\n obtainSmsCode: async (ctx) => {\n const codeLength = effectiveCodeLength(ctx.codeLength, MOBILE_ID_SMS_CODE_LENGTH);\n return (\n await options.getCode({\n sessionId,\n codeLength,\n usedMethod: sendResp.usedMethod,\n })\n ).trim();\n },\n });\n } catch (e: unknown) {\n const errBody = httpErrorBody(e);\n if (errBody !== undefined && isAuthHeaderMissingBody(errBody)) {\n throw new OtpAuthHeaderMissingError(errBody);\n }\n throw e;\n }\n }\n\n const classicLength = effectiveCodeLength(\n sendResp.codeLength,\n accountType === SendCodeAccountTypeValues.Email ? 6 : 4,\n );\n const code = (\n await options.getCode({\n sessionId,\n codeLength: classicLength,\n usedMethod: sendResp.usedMethod,\n })\n ).trim();\n if (!code) {\n throw new Error(\"runOtpLogin: empty OTP code\");\n }\n\n const resp = await requestTokenOrThrow(\n client,\n {\n sessionId,\n code,\n scopes,\n deviceInfo: options.deviceInfo,\n },\n signal,\n );\n // Строгий flow не допускает legacy без status/только с refresh; старый вход сохраняет это поведение.\n if (tokenTransition(resp.status, true) !== TokenStatusValues.TokenReady) {\n throw new Error(`runOtpLogin: TokenResp.status=${resp.status} (ожидался TokenReady)`);\n }\n return assertTokenRespReadyForStorage(resp, \"runOtpLogin\");\n}\n","/**\n * Внутренний poll MTS Mobile ID (не публичный export).\n *\n * Аналог **MobileIdPollTask** + loop из torrow-apps\n * (`AuthPhoneCodeComponent.startMobileIdPolling`).\n * Ветка после `sendCode`, когда `usedMethod === MtsMobileId`.\n *\n * Не путать с **`session.requestTokenWithCode`** (один вызов: sessionId + OTP-код → токены).\n * Вызывается из `otp-login.ts`.\n */\nimport {\n DefaultAuthScopes,\n TokenStatusValues,\n type AuthScope,\n type DeviceInfo,\n type TokenReq,\n type TokenResp,\n} from \"@torrow/sdk-client\";\nimport { assertTokenRespReadyForStorage, normalizeTokenResp } from \"./merge-tokens.js\";\nimport { sleepMs } from \"./sleep.js\";\nimport { requestAuthToken, tokenTransition } from \"./auth-protocol.js\";\n\nconst DEFAULT_POLL_INTERVAL_SEC = 3;\nconst DEFAULT_MAX_WAIT_SEC = 180;\n\n/** Минимум для poll: **`auth.requestToken`** (обычно `session.client` / identity-only). */\nexport type MobileIdAuthClient = {\n auth: {\n requestToken(body: TokenReq, options?: { signal?: AbortSignal }): Promise<TokenResp>;\n };\n};\n\nexport type MobileIdPollOnceArgs = {\n sessionId: string;\n deviceInfo: DeviceInfo;\n scopes?: AuthScope[];\n /** SMS-fallback; без значения — пустая строка (plain poll), как в torrow-apps. */\n code?: string;\n signal?: AbortSignal;\n};\n\nexport type MobileIdObtainSmsCodeContext = {\n /** Из `SendCodeResp.codeLength`; у Mobile ID часто `0` — UI обычно ждёт 4 цифры. */\n codeLength?: number;\n};\n\nexport type PollMobileIdUntilReadyOptions = {\n sessionId: string;\n deviceInfo: DeviceInfo;\n scopes?: AuthScope[];\n /** `SendCodeResp.pollIntervalSeconds`; иначе **3**. */\n pollIntervalSeconds?: number;\n /** `SendCodeResp.sessionLifeTimeInSeconds`; иначе **180**. */\n maxWaitSeconds?: number;\n signal?: AbortSignal;\n /**\n * Только при **`AwaitingSmsCode`**.\n * После submit, если снова **`Pending`**, poll продолжается **без** кода (как в apps).\n */\n obtainSmsCode?: (ctx: MobileIdObtainSmsCodeContext) => Promise<string>;\n /** Подсказка длины для `obtainSmsCode` (из sendCode). */\n smsCodeLength?: number;\n};\n\nfunction scopesOrDefault(scopes: AuthScope[] | undefined): AuthScope[] {\n return scopes && scopes.length > 0 ? scopes : [...DefaultAuthScopes];\n}\n\n/**\n * Один poll: **`requestToken`** с `code: \"\"` или SMS-кодом.\n * Токены **не** сохраняет — caller решает (как MobileIdPollTask).\n * Internal + unit tests; not re-exported from `@torrow/sdk-auth` barrel.\n */\nexport async function pollMobileIdOnce(\n client: MobileIdAuthClient,\n args: MobileIdPollOnceArgs,\n): Promise<TokenResp> {\n const sessionId = args.sessionId.trim();\n if (!sessionId) {\n throw new Error(\"pollMobileIdOnce: sessionId is empty\");\n }\n const body = {\n sessionId,\n // Empty code = plain poll (server accepts it for Mobile ID); value = SMS-OTP fallback.\n code: args.code ?? \"\",\n scopes: scopesOrDefault(args.scopes),\n deviceInfo: args.deviceInfo,\n };\n return normalizeTokenResp(await requestAuthToken(client, body, args.signal));\n}\n\n/**\n * Poll до **`TokenReady`**: `Pending` → sleep → снова; `AwaitingSmsCode` → `obtainSmsCode` →\n * один poll с кодом; если снова `Pending` — продолжить **без** кода.\n */\nexport async function pollMobileIdUntilReady(\n client: MobileIdAuthClient,\n options: PollMobileIdUntilReadyOptions,\n): Promise<TokenResp> {\n const sessionId = options.sessionId.trim();\n if (!sessionId) {\n throw new Error(\"pollMobileIdUntilReady: sessionId is empty\");\n }\n const intervalSec =\n typeof options.pollIntervalSeconds === \"number\" && options.pollIntervalSeconds > 0\n ? options.pollIntervalSeconds\n : DEFAULT_POLL_INTERVAL_SEC;\n const maxWaitSec =\n typeof options.maxWaitSeconds === \"number\" && options.maxWaitSeconds > 0\n ? options.maxWaitSeconds\n : DEFAULT_MAX_WAIT_SEC;\n const deadline = Date.now() + maxWaitSec * 1000;\n const scopes = scopesOrDefault(options.scopes);\n\n for (;;) {\n if (options.signal?.aborted) {\n throw options.signal.reason ?? new Error(\"Aborted\");\n }\n\n // Plain poll always uses code \"\" (apps MobileIdPollTask.poll without code).\n const resp = await pollMobileIdOnce(client, {\n sessionId,\n deviceInfo: options.deviceInfo,\n scopes,\n signal: options.signal,\n });\n const status = resp.status;\n\n if (tokenTransition(status, true) === TokenStatusValues.TokenReady) {\n return assertTokenRespReadyForStorage(resp, \"pollMobileIdUntilReady\");\n }\n if (status === TokenStatusValues.Failed) {\n throw new Error(\n \"pollMobileIdUntilReady: TokenResp.status=Failed — нужен новый sendCode.\",\n );\n }\n if (status === TokenStatusValues.AwaitingSmsCode) {\n const obtain = options.obtainSmsCode;\n if (!obtain) {\n throw new Error(\n \"pollMobileIdUntilReady: AwaitingSmsCode, но obtainSmsCode не задан.\",\n );\n }\n const code = (\n await obtain({\n codeLength: options.smsCodeLength,\n })\n ).trim();\n if (!code) {\n throw new Error(\"pollMobileIdUntilReady: пустой SMS-код после AwaitingSmsCode.\");\n }\n const afterSms = await pollMobileIdOnce(client, {\n sessionId,\n deviceInfo: options.deviceInfo,\n scopes,\n code,\n signal: options.signal,\n });\n if (tokenTransition(afterSms.status, true) === TokenStatusValues.TokenReady) {\n return assertTokenRespReadyForStorage(afterSms, \"pollMobileIdUntilReady (SMS)\");\n }\n if (afterSms.status === TokenStatusValues.Failed) {\n throw new Error(\n \"pollMobileIdUntilReady: TokenResp.status=Failed после SMS — нужен новый sendCode.\",\n );\n }\n if (afterSms.status === TokenStatusValues.Pending) {\n // Code accepted; resume plain poll without code (apps: startMobileIdPolling again).\n continue;\n }\n if (afterSms.status === TokenStatusValues.AwaitingSmsCode) {\n // Wrong / incomplete — ask again on next loop iteration.\n continue;\n }\n throw new Error(\n `pollMobileIdUntilReady: неизвестный TokenResp.status=${String(afterSms.status)} после SMS.`,\n );\n }\n if (status !== TokenStatusValues.Pending) {\n throw new Error(\n `pollMobileIdUntilReady: неизвестный TokenResp.status=${String(status)}.`,\n );\n }\n if (Date.now() >= deadline) {\n throw new Error(\n `pollMobileIdUntilReady: timeout ~${maxWaitSec}с (poll каждые ${intervalSec}с) — статус Pending.`,\n );\n }\n await sleepMs(intervalSec * 1000, options.signal);\n }\n}\n"],"mappings":"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","names":["TokenStatusValues","OtpPhaseValues","OtpFlowErrorCodeValues","TokenStatusValues","DefaultAuthScopes","TorrowClient","TorrowHttpError","TokenStatusValues","TorrowHttpError","DefaultAuthScopes","TokenStatusValues","TorrowClient","TorrowClient","DefaultAuthScopes","DefaultAuthScopes","DefaultAuthScopes","TorrowHttpError","VerificationMethodValues","TokenStatusValues","DefaultAuthScopes","TokenStatusValues","DefaultAuthScopes","TokenStatusValues","TorrowHttpError","scopesOrDefault","DefaultAuthScopes","VerificationMethodValues","codeLength","TokenStatusValues","TorrowClient"]}
1
+ {"version":3,"sources":["../../../src/auth-session/app-credentials.ts","../../../src/auth-session/otp-errors.ts","../../../src/auth-session/otp-flow.ts","../../../src/auth-session/identity-client.ts","../../../src/auth-session/auth-protocol.ts","../../../src/auth-session/token-storage.ts","../../../src/auth-session/local-storage-token-storage.ts","../../../src/auth-session/TokenSession.ts","../../../src/auth-session/auth-session-not-ready.ts","../../../src/auth-session/session-error.ts","../../../src/auth-session/fetch-with-401-retry.ts","../../../src/auth-session/merge-tokens.ts","../../../src/auth-session/torrow-auth.ts","../../../src/auth-session/link-login.ts","../../../src/auth-session/sleep.ts","../../../src/auth-session/otp-login.ts","../../../src/auth-session/mobile-id.ts"],"sourcesContent":["/** Defaults и преобразование credentials приложения в HTTP Basic. */\nimport type { AuthIdentityBasic } from \"@torrow/sdk-client\";\nimport type { AppCredentials } from \"./types.js\";\n\n/**\n * Дефолтные app credentials SDK для `/api/v1/auth/*`, если {@link TokenSessionOptions.appCredentials}\n * не передан. Своё приложение может переопределить явной парой.\n *\n * Публикуется в npm `@torrow/sdk-auth` (inspectable); свой prod-secret — только override / BFF.\n */\nexport const DEFAULT_APP_CREDENTIALS: AppCredentials = {\n clientId: \"sdk.torrow.net\",\n clientSecret: \"4YM1qYq87faSXaFqXTBcnJrn/jj0AOXl6tTsfubLyCE=\",\n};\n\n/**\n * Подставляет {@link DEFAULT_APP_CREDENTIALS}, если `input` не задан.\n * Неполную явную пару **не** дозаполняет — её отсечёт `appCredentialsToAuthIdentityBasic`.\n *\n * @internal Не экспортируется из `@torrow/sdk-auth`.\n */\nexport function resolveAppCredentials(input?: AppCredentials): AppCredentials {\n return input ?? DEFAULT_APP_CREDENTIALS;\n}\n\n/**\n * Внутренний адаптер слоёв: product DX → HTTP Basic wire.\n *\n * - **auth-пакет:** `AppCredentials` (`clientId` / `clientSecret`)\n * - **sdk-client:** `AuthIdentityBasic` (`username` / `password`) на `TorrowClientConfig.authIdentityBasic`\n *\n * Не экспортируется из `@torrow/sdk-auth`: приложению достаточно `appCredentials`.\n * Low-level `new TorrowClient({ authIdentityBasic })` — собирай `AuthIdentityBasic` сам.\n */\nexport function appCredentialsToAuthIdentityBasic(\n appCredentials: AppCredentials | undefined,\n): AuthIdentityBasic | undefined {\n const clientId = appCredentials?.clientId?.trim();\n const clientSecret = appCredentials?.clientSecret?.trim();\n if (!clientId || !clientSecret) return undefined;\n return { username: clientId, password: clientSecret };\n}\n","import {\n OtpErrorReason, TorrowSdkError, TorrowSdkErrorCategory, TorrowSdkErrorDomain,\n TorrowSdkRequestState, type TorrowSdkSensitiveDetails,\n} from \"@torrow/sdk-client\";\n\n/** Известные wire-коды прежних предметных правил OTP. */\nenum OtpApiErrorName {\n AuthHeaderMissing = \"AUTH_HEADER_MISSING\",\n CodeRequestLimitExceeded = \"CODE_REQUEST_LIMIT_EXCEEDED\",\n}\n\n/** Создаёт безопасный предметный отказ OTP без журнала эффектов. */\nexport function otpFailure(kind: OtpErrorReason | TorrowSdkErrorCategory, cleanup?: TorrowSdkSensitiveDetails[\"cleanup\"], cause?: unknown): TorrowSdkError {\n let category: TorrowSdkErrorCategory;\n let reason: OtpErrorReason | undefined;\n switch (kind) {\n case OtpErrorReason.InvalidChallenge: category = TorrowSdkErrorCategory.InvalidInput; reason = kind; break;\n case OtpErrorReason.UnsupportedMethod: category = TorrowSdkErrorCategory.UnsupportedOperation; reason = kind; break;\n case OtpErrorReason.AuthHeaderMissing: category = TorrowSdkErrorCategory.AuthenticationRequired; reason = kind; break;\n case OtpErrorReason.CodeRequestLimitExceeded: category = TorrowSdkErrorCategory.RateLimited; reason = kind; break;\n default: category = kind;\n }\n return new TorrowSdkError({ category, detail: { domain: TorrowSdkErrorDomain.Otp, ...(reason ? { reason } : {}) }, sensitive: { cleanup, cause } });\n}\n/** Сохраняет прежнее распознавание конкретных auth wire-отказов, не анализируя message. */\nexport function otpHttpFailure(error: unknown): unknown {\n if (!(error instanceof TorrowSdkError)) return error;\n const body = error.getSensitiveDetails().body;\n if (body === null || typeof body !== \"object\") return error;\n const value = body as {name?: unknown; parameters?: {minutes?:unknown;seconds?:unknown}};\n if (value.name === OtpApiErrorName.AuthHeaderMissing) {\n return error.withContext({category:TorrowSdkErrorCategory.AuthenticationRequired,detail:{domain:TorrowSdkErrorDomain.Otp,reason:OtpErrorReason.AuthHeaderMissing}});\n }\n if (value.name !== OtpApiErrorName.CodeRequestLimitExceeded) return error;\n // Только числа или числовые wire-строки: текст сервера нельзя переносить в безопасную диагностику.\n const minutes = duration(value.parameters?.minutes);\n const seconds = duration(value.parameters?.seconds);\n const wait = minutes === undefined && seconds === undefined ? undefined : (minutes ?? 0) * 60 + (seconds ?? 0);\n return error.withContext({category:TorrowSdkErrorCategory.RateLimited,detail:{domain:TorrowSdkErrorDomain.Otp,reason:OtpErrorReason.CodeRequestLimitExceeded,\n ...(wait !== undefined && Number.isFinite(wait) ? {retryAfterSeconds:wait} : {})}});\n}\nfunction duration(value: unknown): number | undefined {\n const number = typeof value === \"number\" ? value : typeof value === \"string\" && /^\\d+(?:\\.\\d+)?$/.test(value) ? Number(value) : undefined;\n return number !== undefined && Number.isFinite(number) && number >= 0 ? number : undefined;\n}\n/** Отмена до HTTP сохраняет исходный reason приватно независимо от его типа. */\nexport function throwIfOtpAborted(signal?: AbortSignal): void {\n if (signal?.aborted) throw new TorrowSdkError({category:TorrowSdkErrorCategory.Cancelled,request:TorrowSdkRequestState.NotStarted,sensitive:{cause:signal.reason}});\n}\n","import { otpFailure, otpHttpFailure, throwIfOtpAborted } from \"./otp-errors.js\";\nimport {\n OtpErrorReason,\n TorrowSdkErrorCategory,\n isAuthCaptchaType,\n isDeviceType,\n isSendCodeAccountType,\n isVerificationMethod,\n TokenStatusValues,\n VerificationMethodValues,\n type DeviceInfo,\n type AuthScope,\n type TokenResp,\n type SendCodeAccountType,\n type VerificationMethod,\n} from \"@torrow/sdk-client\";\nimport type { AppCredentials, TokenSessionOptions } from \"./types.js\";\nimport { createIdentityClient } from \"./identity-client.js\";\nimport {\n requestAuthToken,\n sendOtpCode,\n tokenTransition,\n tokenResponseFields,\n} from \"./auth-protocol.js\";\nimport type { TorrowAuthLoginWithOtpOptions } from \"./torrow-auth.js\";\n\n/**\n * Опции пошагового входа без TokenStorage и пользовательской сессии.\n *\n * HTTP-настройки из {@link TokenSessionOptions} применяются к обоим шагам:\n * - `baseUrl` — адрес шлюза без `/api/v1`; по умолчанию `https://torrow.net`.\n * - `fetch` — реализация HTTP-транспорта; по умолчанию глобальный `fetch`.\n * - `defaultHeaders` — дополнительные заголовки каждого запроса.\n * - `userAgent` — значение заголовка `User-Agent` для диагностики сервера.\n *\n * Между `start` и `complete`, в том числе на разных экземплярах flow,\n * приложение сохраняет одинаковые deviceInfo, scopes и splitTokens.\n */\nexport type TorrowAuthOtpFlowOptions = Pick<\n TokenSessionOptions,\n \"baseUrl\" | \"fetch\" | \"defaultHeaders\" | \"userAgent\"\n> & {\n /**\n * Учётные данные приложения для Basic на `/api/v1/auth/*`.\n * Если не заданы — используется {@link DEFAULT_APP_CREDENTIALS};\n * явная пара полностью заменяет default и требует непустых clientId/clientSecret.\n */\n appCredentials?: AppCredentials;\n /**\n * Обязательное описание устройства для `TokenReq.deviceInfo` при `complete`.\n * Требуются непустой deviceId и поддерживаемый deviceType; flow не генерирует deviceId.\n */\n deviceInfo: DeviceInfo;\n /**\n * Запрашиваемые области доступа → `TokenReq.scopes` при `complete`.\n * Если поле отсутствует или массив пуст — `user_api` и `offline_access`.\n */\n scopes?: AuthScope[];\n /**\n * Разделение access/refresh сервером → `TokenReq.splitTokens` при `complete`.\n * false: полные токены в JSON; используйте для Node, MCP и интеграционных ключей.\n * true: части токенов в JSON, остальные части в HttpOnly cookies;\n * только для браузерного приложения с настроенной передачей cookies.\n * SDK не управляет cookies и не восстанавливает полные токены из частей.\n * Явные true/false передаются без изменения; отсутствующее поле не отправляется.\n */\n splitTokens?: boolean;\n};\n/**\n * Вход отправки кода; поля наследуются из {@link TorrowAuthLoginWithOtpOptions}.\n * account задаёт Phone/Email и получателя; language — язык (default ru),\n * regionStorageId — регион (default 70), captcha/captchaType — ответ и провайдер captcha,\n * inviteSessionId — приглашение. preferredMethod — пожелание серверу,\n * supportedMethods — способы, которые приложение умеет обработать, в порядке приоритета.\n * Для телефона рекомендуется TelegramGateway → MtsMobileId → Sms.\n * UI и получение captcha остаются в приложении.\n */\nexport type OtpStartInput = Pick<\n TorrowAuthLoginWithOtpOptions,\n | \"account\"\n | \"language\"\n | \"regionStorageId\"\n | \"captcha\"\n | \"captchaType\"\n | \"inviteSessionId\"\n | \"preferredMethod\"\n | \"supportedMethods\"\n>;\n/** Опции одного HTTP-шага. */\nexport type OtpRequestOptions = {\n /** Отмена текущего HTTP-шага; не отменяет независимый cleanup приложения. */\n signal?: AbortSignal;\n};\n/**\n * Действие приложения для продолжения входа, отдельно от серверного TokenStatus.\n * AwaitingCode — первоначальный ввод classic OTP либо новая фаза ввода SMS-кода\n * после fallback из Pending в той же Mobile ID auth-сессии.\n * Pending — ожидание Mobile ID, complete вызывается без кода.\n */\nexport enum OtpPhaseValues {\n /** Требуется первоначальный OTP или SMS-код после Mobile ID fallback. */\n AwaitingCode = \"AwaitingCode\",\n /** Ожидание Mobile ID; следующий запрос выполняется без кода. */\n Pending = \"Pending\",\n}\n/** JSON-значение фазы; принимает строковый литерал или член {@link OtpPhaseValues}. */\nexport type OtpPhase = `${OtpPhaseValues}`;\n/** Переносимое продолжение; приложение обеспечивает его целостность. */\nexport type OtpChallenge = {\n /** Идентификатор auth-сессии из SendCodeResp; сохраняется между шагами. */\n sessionId: string;\n /** Тип получателя первоначального кода: Phone или Email. */\n accountType: SendCodeAccountType;\n /** Фактический способ подтверждения; при SMS fallback меняется на Sms. */\n usedMethod: VerificationMethod;\n /** Следующее действие приложения: ввод кода или ожидание без кода. */\n phase: OtpPhase;\n /** Локальный deadline в Unix milliseconds, без продления; не гарантирует серверный срок. TTL из SendCodeResp либо 180 секунд от начала start. */\n expiresAtMs: number;\n /** Подсказка длины кода из SendCodeResp; default 6 для classic Email, 4 для Phone/Mobile ID. В переносимом challenge может отсутствовать. */\n codeLength?: number;\n /** Интервал ожидания в секундах для Pending; default 3. SDK сам не выполняет polling. */\n pollIntervalSeconds?: number;\n /** Оставшиеся попытки из SendCodeResp.attemptCountToUseSession; SDK не уменьшает их. Отсутствие означает, что корректная подсказка не получена. */\n attemptsRemaining?: number;\n /** Время следующей отправки из SendCodeResp.nextAvailableUsageTime в Unix milliseconds; отсутствует без корректной подсказки. */\n nextSendAtMs?: number;\n /** Доступные способы из SendCodeResp; отсутствуют, если список отсутствовал или содержал неизвестные значения. */\n availableMethods?: VerificationMethod[];\n};\n/** Параметры одной попытки обмена или ожидания. */\nexport type OtpCompleteInput = {\n /** Переносимое продолжение; проверяется перед запросом, целостность обеспечивает приложение. */\n challenge: OtpChallenge;\n /** Непустой OTP в AwaitingCode; в Pending поле должно отсутствовать. */\n code?: string;\n};\n/** Данные для явного cleanup вызывающим приложением. */\nexport type OtpCleanupData = {\n /** Refresh-секрет для явного отзыва приложением; не предназначен для диагностики. */\n refreshToken: string;\n};\n/**\n * Результат одного шага: серверный статус отдельно от фазы challenge.\n * Pending — сервер ещё не завершил аутентификацию, следующий запрос без кода.\n * AwaitingSmsCode — продолжение Mobile ID-сценария через SMS fallback,\n * если аутентификацию через Mobile ID не удалось завершить. Возвращённый challenge\n * меняет фазу Pending на AwaitingCode и метод на Sms; sessionId и deadline сохраняются.\n * TokenReady — токены выданы; Failed — терминальный отказ без продолжения.\n * Обычный Email/SMS OTP начинается с AwaitingCode после start,\n * без отдельного серверного AwaitingEmailCode.\n */\nexport type OtpCompleteResult =\n | {\n /** Сервер ещё ожидает подтверждения либо запросил SMS fallback. */\n status: TokenStatusValues.Pending | TokenStatusValues.AwaitingSmsCode;\n /** Продолжение с новым действием приложения и прежним deadline. */\n challenge: OtpChallenge;\n /** Неожиданный refresh для явного отзыва; отсутствует, если сервер его не выдал. */\n cleanup?: OtpCleanupData;\n }\n | {\n /** Терминальный отказ сервера; продолжение не возвращается. */\n status: TokenStatusValues.Failed;\n /** Неожиданный refresh для явного отзыва; отсутствует без refresh в ответе. */\n cleanup?: OtpCleanupData;\n }\n | {\n /** Сервер явно подтвердил выдачу токенов. */\n status: TokenStatusValues.TokenReady;\n /**\n * Проверенные поля TokenResp: accessToken обязателен, refreshToken и expiration\n * (TTL в секундах) сохраняются при наличии; splitTokens определяет полноту токенов.\n */\n tokens: Omit<TokenResp, \"status\" | \"accessToken\"> & {\n /** Явный статус готовности из token-ответа. */\n status: TokenStatusValues.TokenReady;\n /** Непустой access token из ответа после нормализации aliases. */\n accessToken: string;\n };\n };\n/** Пошаговый интерфейс: каждый метод выполняет только один upstream-запрос. */\nexport interface OtpFlow {\n /** Отправляет один код и возвращает переносимое продолжение без токенов. */\n start(\n input: OtpStartInput,\n options?: OtpRequestOptions,\n ): Promise<OtpChallenge>;\n /** Проверяет продолжение и выполняет одну попытку обмена/ожидания без сохранения. */\n complete(\n input: OtpCompleteInput,\n options?: OtpRequestOptions,\n ): Promise<OtpCompleteResult>;\n}\n\n/**\n * Создаёт OTP-flow с фиксированными настройками транспорта и token-обмена.\n * Создание не выполняет HTTP; start и complete выполняют по одному запросу.\n * Challenge и токены остаются у приложения, без storage, polling и refresh внутри flow.\n * @param options Настройки HTTP, credentials приложения, устройства и выдачи токенов.\n * @returns Независимые шаги отправки кода и завершения/ожидания аутентификации.\n * @throws TorrowSdkError При некорректных опциях устройства, scopes или splitTokens.\n */\nexport function createOtpFlow(options: TorrowAuthOtpFlowOptions): OtpFlow {\n // Проверяем runtime-вход, поскольку JavaScript-клиент может обойти TypeScript-контракт.\n if (\n !isRecord(options) ||\n !isRecord(options.deviceInfo) ||\n typeof options.deviceInfo.deviceId !== \"string\" ||\n !options.deviceInfo.deviceId.trim() ||\n typeof options.deviceInfo.deviceType !== \"string\" ||\n !isDeviceType(options.deviceInfo.deviceType) ||\n (options.scopes !== undefined &&\n (!Array.isArray(options.scopes) ||\n !options.scopes.every(\n (scope) => typeof scope === \"string\" && scope.trim(),\n ))) ||\n (options.splitTokens !== undefined &&\n typeof options.splitTokens !== \"boolean\")\n )\n throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n // Basic относится к приложению. Копии устройства/scopes фиксируют настройки\n // экземпляра без открытия TokenSession и без хранения auth-продолжения.\n let client;\n try { client = createIdentityClient(options); }\n catch (cause) { throw otpFailure(TorrowSdkErrorCategory.InvalidInput, undefined, cause); }\n const deviceInfo = { ...options.deviceInfo };\n const scopes = options.scopes ? [...options.scopes] : undefined;\n const splitTokens = options.splitTokens;\n return {\n async start(input, requestOptions) {\n // Некорректный ввод и уже отменённый шаг отсеиваем до отправки OTP.\n validateStartInput(input);\n throwIfOtpAborted(requestOptions?.signal);\n // Отсчёт срока начинается до HTTP: время ожидания upstream тоже расходует TTL.\n const startedAt = Date.now();\n const response: unknown = await sendOtpCode(\n client,\n {\n ...input,\n accountName: input.account.accountName,\n type: input.account.accountType,\n },\n requestOptions?.signal,\n ).catch(error => { throw otpHttpFailure(error); });\n // Ответ сервера недоверенный: sessionId и фактический usedMethod обязательны.\n // preferredMethod был только пожеланием и не определяет возвращаемую фазу.\n if (\n !isRecord(response) ||\n typeof response.sessionId !== \"string\" ||\n !response.sessionId.trim()\n )\n throw otpFailure(TorrowSdkErrorCategory.InvalidResponse);\n if (!supportedMethod(response.usedMethod, input.account.accountType))\n throw otpFailure(OtpErrorReason.UnsupportedMethod);\n // Без серверного TTL задаём локальный срок продолжения, а не гарантию срока\n // auth-сессии на сервере. Явно некорректный или уже истёкший срок отклоняем.\n const ttl = response.sessionLifeTimeInSeconds ?? 180;\n if (!positive(ttl)) throw otpFailure(TorrowSdkErrorCategory.InvalidResponse);\n const expiresAtMs = startedAt + ttl * 1000;\n if (!timestamp(expiresAtMs) || expiresAtMs <= Date.now())\n throw otpFailure(TorrowSdkErrorCategory.InvalidResponse);\n // Mobile ID начинается с ожидания без кода, остальные методы — с ввода OTP.\n const mobile = response.usedMethod === VerificationMethodValues.MtsMobileId;\n const nextSendAtMs =\n response.nextAvailableUsageTime instanceof Date\n ? response.nextAvailableUsageTime.getTime()\n : typeof response.nextAvailableUsageTime === \"string\"\n ? Date.parse(response.nextAvailableUsageTime)\n : undefined;\n // Собираем только поля переносимого challenge: даты как Unix milliseconds,\n // необязательные подсказки проверяем, неизвестные поля ответа не копируем.\n return {\n sessionId: response.sessionId.trim(),\n accountType: input.account.accountType,\n usedMethod: response.usedMethod,\n phase: mobile ? OtpPhaseValues.Pending : OtpPhaseValues.AwaitingCode,\n expiresAtMs,\n codeLength: codeLength(response.codeLength)\n ? response.codeLength\n : input.account.accountType === \"Email\" && !mobile\n ? 6\n : 4,\n ...(mobile\n ? {\n pollIntervalSeconds: positive(response.pollIntervalSeconds)\n ? response.pollIntervalSeconds\n : 3,\n }\n : {}),\n ...(attempts(response.attemptCountToUseSession)\n ? { attemptsRemaining: response.attemptCountToUseSession }\n : {}),\n ...(timestamp(nextSendAtMs) ? { nextSendAtMs } : {}),\n ...(methods(response.availableMethods)\n ? { availableMethods: [...response.availableMethods] }\n : {}),\n };\n },\n async complete(input, requestOptions) {\n // Продолжение может прийти из JSON на новом экземпляре flow. Проверяем его\n // структуру и deadline до HTTP; целостность и привязку к пользователю обеспечивает app.\n if (!isRecord(input)) throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n throwIfOtpAborted(requestOptions?.signal);\n const continuation = validateChallenge(input.challenge);\n // Фаза определяет допустимость кода: Pending не должен повторно отправлять\n // ранее введённый SMS-код, AwaitingCode требует нового непустого значения.\n if (\n (continuation.phase === OtpPhaseValues.Pending && input.code !== undefined) ||\n (continuation.phase === OtpPhaseValues.AwaitingCode &&\n (typeof input.code !== \"string\" || !input.code.trim()))\n )\n throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n // Ровно одна попытка token-обмена. Polling, интервалы и повторы выбирает приложение.\n const raw: unknown = await requestAuthToken(\n client,\n {\n sessionId: continuation.sessionId,\n code: input.code?.trim() ?? \"\",\n deviceInfo,\n scopes,\n splitTokens,\n },\n requestOptions?.signal,\n ).catch(error => { throw otpHttpFailure(error); });\n if (!isRecord(raw)) throw otpFailure(TorrowSdkErrorCategory.InvalidResponse);\n // Извлекаем aliases и доступный refresh до строгой проверки ответа:\n // даже повреждённый ответ может потребовать явного cleanup приложением.\n const fields = tokenResponseFields(raw);\n const refreshToken =\n typeof fields.refresh === \"string\" && fields.refresh.trim()\n ? fields.refresh.trim()\n : undefined;\n const cleanup = refreshToken ? { refreshToken } : undefined;\n // Новый flow требует явный известный status, в отличие от legacy-адаптеров.\n // Некорректные token-поля не скрываем нормализацией; cleanup остаётся в ошибке.\n const status = tokenTransition(\n typeof fields.status === \"string\"\n ? fields.status.trim()\n : fields.status,\n );\n if (\n !status ||\n (fields.access != null && typeof fields.access !== \"string\") ||\n (fields.refresh != null && typeof fields.refresh !== \"string\") ||\n (fields.expiration != null &&\n (typeof fields.expiration !== \"number\" ||\n !Number.isFinite(fields.expiration) ||\n fields.expiration < 0))\n )\n throw otpFailure(TorrowSdkErrorCategory.InvalidResponse, cleanup);\n // Ожидание возвращаем вызывающему коду, сохраняя исходный deadline.\n // Это также обрабатывает возврат к Pending после попытки SMS fallback.\n if (status === TokenStatusValues.Pending)\n return {\n status,\n cleanup,\n challenge: {\n ...continuation,\n phase: OtpPhaseValues.Pending,\n usedMethod: VerificationMethodValues.MtsMobileId,\n pollIntervalSeconds: continuation.pollIntervalSeconds ?? 3,\n },\n };\n if (status === TokenStatusValues.AwaitingSmsCode) {\n // Та же auth-сессия продолжается через SMS fallback: меняем фазу и метод,\n // сохраняя sessionId и исходный deadline, без повторной отправки sendCode.\n const { pollIntervalSeconds: _interval, ...challenge } = continuation;\n return {\n status,\n cleanup,\n challenge: {\n ...challenge,\n phase: OtpPhaseValues.AwaitingCode,\n usedMethod: VerificationMethodValues.Sms,\n codeLength: challenge.codeLength || 4,\n },\n };\n }\n // Терминальный отказ не возвращает challenge; возможный refresh требует cleanup.\n if (status === TokenStatusValues.Failed) return { status, cleanup };\n // TokenReady действителен только с непустым access. Возвращаем известные\n // token-поля без сохранения; режим splitTokens определяет их полноту на сервере.\n if (typeof fields.access !== \"string\" || !fields.access.trim())\n throw otpFailure(TorrowSdkErrorCategory.InvalidResponse, cleanup);\n return {\n status,\n tokens: {\n status,\n accessToken: fields.access.trim(),\n ...(refreshToken ? { refreshToken } : {}),\n ...(typeof fields.expiration === \"number\"\n ? { expiration: fields.expiration }\n : {}),\n },\n };\n },\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction positive(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value) && value > 0;\n}\nfunction codeLength(value: unknown): value is number {\n return positive(value) && Number.isInteger(value) && value <= 32;\n}\nfunction attempts(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\nfunction timestamp(value: unknown): value is number {\n return (\n typeof value === \"number\" &&\n Number.isSafeInteger(value) &&\n value >= 0 &&\n value <= 8.64e15\n );\n}\nfunction methods(value: unknown): value is VerificationMethod[] {\n return (\n Array.isArray(value) &&\n value.every(\n (method) => typeof method === \"string\" && isVerificationMethod(method),\n )\n );\n}\nfunction supportedMethod(\n value: unknown,\n accountType: SendCodeAccountType,\n): value is VerificationMethod {\n return (\n typeof value === \"string\" &&\n isVerificationMethod(value) &&\n (value !== VerificationMethodValues.UnknownMethod || accountType === \"Email\")\n );\n}\n\nfunction validateStartInput(input: unknown): void {\n if (\n !isRecord(input) ||\n !isRecord(input.account) ||\n typeof input.account.accountType !== \"string\" ||\n !isSendCodeAccountType(input.account.accountType) ||\n typeof input.account.accountName !== \"string\" ||\n !input.account.accountName.trim()\n )\n throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n for (const field of [\"language\", \"captcha\", \"inviteSessionId\"]) {\n if (input[field] !== undefined && typeof input[field] !== \"string\")\n throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n }\n if (\n (input.regionStorageId !== undefined && !attempts(input.regionStorageId)) ||\n (input.captchaType !== undefined &&\n (typeof input.captchaType !== \"string\" ||\n !isAuthCaptchaType(input.captchaType))) ||\n (input.preferredMethod !== undefined &&\n (typeof input.preferredMethod !== \"string\" ||\n !isVerificationMethod(input.preferredMethod))) ||\n (input.supportedMethods !== undefined && !methods(input.supportedMethods))\n )\n throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n}\n\n/** Проверяет недоверенное продолжение и копирует только поля публичного контракта. */\nfunction validateChallenge(value: unknown): OtpChallenge {\n if (\n !isRecord(value) ||\n typeof value.sessionId !== \"string\" ||\n !value.sessionId.trim() ||\n typeof value.accountType !== \"string\" ||\n !isSendCodeAccountType(value.accountType) ||\n !supportedMethod(value.usedMethod, value.accountType) ||\n (value.phase !== OtpPhaseValues.Pending && value.phase !== OtpPhaseValues.AwaitingCode) ||\n !timestamp(value.expiresAtMs) ||\n value.expiresAtMs <= Date.now() ||\n (value.phase === OtpPhaseValues.Pending) !== (value.usedMethod === VerificationMethodValues.MtsMobileId)\n )\n throw otpFailure(OtpErrorReason.InvalidChallenge);\n if (\n (value.codeLength !== undefined && !codeLength(value.codeLength)) ||\n (value.pollIntervalSeconds !== undefined &&\n !positive(value.pollIntervalSeconds)) ||\n (value.attemptsRemaining !== undefined &&\n !attempts(value.attemptsRemaining)) ||\n (value.nextSendAtMs !== undefined && !timestamp(value.nextSendAtMs)) ||\n (value.availableMethods !== undefined && !methods(value.availableMethods))\n )\n throw otpFailure(OtpErrorReason.InvalidChallenge);\n return {\n sessionId: value.sessionId.trim(),\n accountType: value.accountType,\n usedMethod: value.usedMethod,\n phase: value.phase,\n expiresAtMs: value.expiresAtMs,\n ...(codeLength(value.codeLength) ? { codeLength: value.codeLength } : {}),\n ...(positive(value.pollIntervalSeconds)\n ? { pollIntervalSeconds: value.pollIntervalSeconds }\n : {}),\n ...(attempts(value.attemptsRemaining)\n ? { attemptsRemaining: value.attemptsRemaining }\n : {}),\n ...(timestamp(value.nextSendAtMs)\n ? { nextSendAtMs: value.nextSendAtMs }\n : {}),\n ...(methods(value.availableMethods)\n ? { availableMethods: [...value.availableMethods] }\n : {}),\n };\n}\n","import { TorrowClient } from \"@torrow/sdk-client\";\nimport {\n appCredentialsToAuthIdentityBasic,\n resolveAppCredentials,\n} from \"./app-credentials.js\";\nimport type { TokenSessionOptions } from \"./types.js\";\n\n/** Создаёт транспорт auth с Basic приложения без открытия пользовательской сессии. */\nexport function createIdentityClient(\n options: Pick<\n TokenSessionOptions,\n \"appCredentials\" | \"baseUrl\" | \"fetch\" | \"defaultHeaders\" | \"userAgent\"\n >,\n): TorrowClient {\n const authIdentityBasic = appCredentialsToAuthIdentityBasic(\n resolveAppCredentials(options.appCredentials),\n );\n if (!authIdentityBasic) {\n throw new Error(\n \"TokenSession: неполная appCredentials (нужны непустые clientId и clientSecret). Без опции используется DEFAULT_APP_CREDENTIALS.\",\n );\n }\n return new TorrowClient({\n baseUrl: options.baseUrl,\n fetch: options.fetch,\n defaultHeaders: options.defaultHeaders,\n userAgent: options.userAgent,\n authIdentityBasic,\n });\n}\n","import {\n DefaultAuthScopes,\n isTokenStatus,\n TokenStatusValues,\n type DeviceInfo,\n type AuthScope,\n type SendCodeReqForOtp,\n type SendCodeResp,\n type TokenReq,\n type TokenResp,\n} from \"@torrow/sdk-client\";\n\n/** Поля отправки OTP, общие для пошагового flow и старого callback-входа. */\nexport type OtpSendInput = Omit<SendCodeReqForOtp, \"accountName\"> & {\n accountName: string;\n};\n\n/** Минимальный транспорт auth-протокола; настоящий клиент и HTTP stand-in используют одну форму. */\nexport type AuthProtocolClient = {\n auth: {\n requestToken(\n body: TokenReq,\n options?: { signal?: AbortSignal },\n ): Promise<TokenResp>;\n };\n};\n\n/** Собирает sendCode с существующими defaults SDK. */\nexport function buildOtpSendRequest(input: OtpSendInput): SendCodeReqForOtp {\n const accountName = input.accountName.trim();\n if (!accountName) throw new Error(\"runOtpLogin: accountName is empty\");\n return {\n accountName,\n type: input.type,\n language: input.language?.trim() || \"ru\",\n regionStorageId:\n typeof input.regionStorageId === \"number\" &&\n Number.isFinite(input.regionStorageId)\n ? input.regionStorageId\n : 70,\n ...(input.captcha?.trim() ? { captcha: input.captcha.trim() } : {}),\n ...(input.captchaType ? { captchaType: input.captchaType } : {}),\n ...(input.inviteSessionId?.trim()\n ? { inviteSessionId: input.inviteSessionId.trim() }\n : {}),\n ...(input.preferredMethod\n ? { preferredMethod: input.preferredMethod }\n : {}),\n ...(input.supportedMethods?.length\n ? { supportedMethods: input.supportedMethods }\n : {}),\n };\n}\n\n/** Один sendCode; проверка результата и legacy-исключения принадлежат вызывающему интерфейсу. */\nexport function sendOtpCode(\n client: {\n auth: {\n sendCode(\n body: SendCodeReqForOtp,\n options?: { signal?: AbortSignal },\n ): Promise<SendCodeResp>;\n };\n },\n input: OtpSendInput,\n signal?: AbortSignal,\n): Promise<SendCodeResp> {\n return client.auth.sendCode(buildOtpSendRequest(input), { signal });\n}\n\n/** Параметры token-обмена до применения defaults SDK. */\nexport type AuthTokenRequestInput = {\n /** Идентификатор созданной auth-сессии для обмена кода или polling. */\n sessionId: string;\n /** OTP-код; пустая строка для Mobile ID polling. */\n code: string;\n /** Устройство, для которого выдаются токены. */\n deviceInfo: DeviceInfo;\n /** Области доступа; отсутствие или пустой массив включает DefaultAuthScopes. */\n scopes?: AuthScope[];\n /**\n * Серверное разделение токенов между JSON и HttpOnly cookies.\n * false — полные токены для Node/MCP; true — браузерный режим с настроенными cookies.\n * Отсутствующее поле не отправляется; тип согласован с {@link TokenReq}.\n */\n splitTokens?: TokenReq[\"splitTokens\"];\n};\n\n/** Один token-запрос с общими defaults, без нормализации, persist и повторов. */\nexport function requestAuthToken(\n client: AuthProtocolClient,\n input: AuthTokenRequestInput,\n signal?: AbortSignal,\n): Promise<TokenResp> {\n return client.auth.requestToken(\n {\n sessionId: input.sessionId,\n code: input.code,\n deviceInfo: input.deviceInfo,\n scopes: input.scopes?.length ? input.scopes : [...DefaultAuthScopes],\n ...(input.splitTokens !== undefined\n ? { splitTokens: input.splitTokens }\n : {}),\n },\n { signal },\n );\n}\n\n/** Распознаёт переход протокола; допуск отсутствующего status включается только legacy-адаптацией. */\nexport function tokenTransition(\n status: unknown,\n legacy = false,\n): TokenStatusValues | undefined {\n if (legacy && status == null) return TokenStatusValues.TokenReady;\n return typeof status === \"string\" && isTokenStatus(status)\n ? status\n : undefined;\n}\n\n/** Извлекает поддерживаемые имена полей без потери некорректных значений перед проверкой. */\nexport function tokenResponseFields(o: Record<string, unknown>) {\n return {\n access: o.accessToken ?? o.AccessToken ?? o.access_token,\n refresh: o.refreshToken ?? o.RefreshToken ?? o.refresh_token,\n expiration: o.expiration ?? o.Expiration ?? o.expires_in,\n status: o.status ?? o.Status,\n };\n}\n","/**\n * In-memory реализация {@link TokenStorage}.\n *\n * Тесты, эфемерные процессы, `TorrowAuth.fromTokens` без диска.\n * Для Node-примеров обычно {@link FileTokenStorage}; для браузера —\n * {@link LocalStorageTokenStorage}.\n */\nimport type { AuthTokenSnapshot, TokenStorage } from \"./types.js\";\n\n/** In-memory хранилище токенов сессии. */\nexport class MemoryTokenStorage implements TokenStorage {\n private snapshot: AuthTokenSnapshot;\n\n constructor(initial?: AuthTokenSnapshot) {\n this.snapshot = { ...(initial ?? {}) };\n }\n\n async load(): Promise<AuthTokenSnapshot> {\n return { ...this.snapshot };\n }\n\n async save(snapshot: AuthTokenSnapshot): Promise<void> {\n this.snapshot = {\n accessToken: snapshot.accessToken?.trim() || undefined,\n refreshToken: snapshot.refreshToken?.trim() || undefined,\n accessExpiresAtMs: snapshot.accessExpiresAtMs || undefined,\n deviceId: snapshot.deviceId?.trim() || undefined,\n };\n }\n}\n","/**\n * Persist snapshot в браузерный **`localStorage`** — реализация {@link TokenStorage}.\n *\n * Для виджетов / SPA (`TorrowAuth.fromTokens` / `connect`). В Node без\n * `globalThis.localStorage` конструктор бросит. Не путать с {@link FileTokenStorage}.\n */\nimport type { AuthTokenSnapshot, TokenStorage } from \"./types.js\";\n\ntype LocalStorageLike = {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem?(key: string): void;\n};\n\nfunction resolveLocalStorage(): LocalStorageLike {\n const g = globalThis as { localStorage?: LocalStorageLike };\n if (!g.localStorage || typeof g.localStorage.getItem !== \"function\") {\n throw new Error(\n \"LocalStorageTokenStorage: globalThis.localStorage is not available in this environment\",\n );\n }\n return g.localStorage;\n}\n\n/**\n * Persist snapshot токенов в браузерный `localStorage` (виджет / SPA).\n */\nexport class LocalStorageTokenStorage implements TokenStorage {\n private readonly store: LocalStorageLike;\n\n constructor(\n readonly key: string,\n store?: LocalStorageLike,\n ) {\n const trimmed = key.trim();\n if (!trimmed) {\n throw new Error(\"LocalStorageTokenStorage: empty key\");\n }\n this.key = trimmed;\n this.store = store ?? resolveLocalStorage();\n }\n\n async load(): Promise<AuthTokenSnapshot> {\n try {\n const raw = this.store.getItem(this.key);\n if (!raw?.trim()) return {};\n const parsed = JSON.parse(raw) as unknown;\n if (parsed === null || typeof parsed !== \"object\") return {};\n const o = parsed as AuthTokenSnapshot;\n return {\n accessToken: typeof o.accessToken === \"string\" ? o.accessToken : undefined,\n refreshToken: typeof o.refreshToken === \"string\" ? o.refreshToken : undefined,\n accessExpiresAtMs:\n typeof o.accessExpiresAtMs === \"number\" ? o.accessExpiresAtMs : undefined,\n deviceId: typeof o.deviceId === \"string\" ? o.deviceId : undefined,\n };\n } catch {\n return {};\n }\n }\n\n async save(snapshot: AuthTokenSnapshot): Promise<void> {\n const toSave: AuthTokenSnapshot = {\n accessToken: snapshot.accessToken?.trim() || undefined,\n refreshToken: snapshot.refreshToken?.trim() || undefined,\n accessExpiresAtMs: snapshot.accessExpiresAtMs || undefined,\n deviceId: snapshot.deviceId?.trim() || undefined,\n };\n this.store.setItem(this.key, JSON.stringify(toSave));\n }\n}\n","/**\n * Ядро пользовательской сессии: persist + проактивный refresh + single-flight + 401-retry.\n *\n * Low-level API: {@link TokenSession.open}. Канон для приложений —\n * **`TorrowAuth.connect` / `login*` / `fromTokens`** (см. `torrow-auth.ts`).\n *\n * Логин снаружи: `TorrowAuth.loginWithOtp` / `loginWithLink`, либо\n * `requestTokenWithCode` / `applyTokenResp` на уже открытой сессии.\n * Не путать статус `\"unauthenticated\"` с `TorrowAuth.anonymous` (клиент без сессии).\n */\nimport {\n DefaultAuthScopes,\n TorrowClient,\n TorrowSdkError,\n TorrowSdkErrorCategory,\n TorrowSdkEffectArea,\n TorrowSdkEffectState,\n TorrowSdkRequestState,\n AuthSessionEffectStep,\n AuthSessionErrorReason,\n type TorrowSdkEffect,\n DeviceTypeValues,\n TokenStatusValues,\n type AuthScope,\n type DeviceInfo,\n type TokenResp,\n} from \"@torrow/sdk-client\";\nimport { isAuthSessionNotReadyYet } from \"./auth-session-not-ready.js\";\nimport { sessionFailure, SessionFailureStage } from \"./session-error.js\";\nimport { createFetchWith401Retry } from \"./fetch-with-401-retry.js\";\nimport {\n accessExpiresAtMsFromJwt,\n accessTokenLooksLikeJwt,\n assertTokenRespReadyForStorage,\n mergeAuthTokens,\n normalizeTokenResp,\n} from \"./merge-tokens.js\";\nimport {\n appCredentialsToAuthIdentityBasic,\n resolveAppCredentials,\n} from \"./app-credentials.js\";\nimport type {\n AuthTokenSnapshot,\n RefreshFailureAction,\n TokenSessionOptions,\n TokenSessionStatus,\n TokenSessionStatusListener,\n TokenStorage,\n} from \"./types.js\";\nimport { createIdentityClient } from \"./identity-client.js\";\nimport { requestAuthToken, tokenTransition } from \"./auth-protocol.js\";\n\nconst DEFAULT_SKEW_MS = 30_000;\nconst SESSION_CLOSED_MESSAGE = \"TokenSession: session closed (logout)\";\n\nfunction defaultOnRefreshFailure(err: unknown): RefreshFailureAction {\n if (!(err instanceof TorrowSdkError) || !err.http) return \"keep\";\n const s = err.http?.status;\n if (s === 429 || s === 502 || s === 503 || s === 504) return \"keep\";\n return \"clear\";\n}\n\n/** Результат refresh и существующей политики обработки отказа. */\ntype RefreshPolicyResult =\n | { /** Подтверждённое обновление токенов. */ outcome: \"refreshed\"; /** Успешный ответ выдачи токенов. */ resp: TokenResp }\n | { /** Результат политики без дополнительной очистки. */ outcome: \"unchanged\"; /** Текущая причина без сведений независимых вызовов. */ error?: TorrowSdkError }\n | { /** Результат политики с очисткой токенов. */ outcome: \"cleared\"; /** Текущая причина с подтверждением выполненной очистки. */ error: TorrowSdkError };\n\n/**\n * Пользовательская сессия (класс). См. file overview выше.\n */\nexport class TokenSession {\n private tokenSnapshot: AuthTokenSnapshot = {};\n private ensureFreshMutex: Promise<void> = Promise.resolve();\n private _client: TorrowClient | undefined;\n private readonly statusListeners = new Set<TokenSessionStatusListener>();\n private closed = false;\n /** Set in refreshWithFailurePolicy catch; not cleared on success (see public refresh). */\n private lastRefreshError: unknown;\n\n private constructor(private readonly opts: TokenSessionOptions) {}\n\n /** Открыть сессию; storage/refresh ошибки описывают только выполненную инициализацию. */\n static async open(opts: TokenSessionOptions): Promise<TokenSession> {\n return (await TokenSession.openWithEffects(opts)).session;\n }\n\n /**\n * @internal Инициализация с явными сведениями только текущего вызова для составного login.\n * Не наблюдает fetch/storage и не хранит историю независимых операций.\n */\n static async openWithEffects(opts: TokenSessionOptions): Promise<{\n /** Открытая сессия с прежней семантикой open. */\n session: TokenSession;\n /** Неизменяемый снимок реально выполненных шагов инициализации. */\n effects: readonly TorrowSdkEffect[];\n }> {\n const session = new TokenSession(opts);\n const effects: TorrowSdkEffect[] = [];\n let failureAt = SessionFailureStage.LoadStorage;\n try {\n try { session.tokenSnapshot = await opts.tokenStorage.load(); }\n catch (error) {\n throw sessionFailure(error, { operation: \"TokenSession.open\", failureAt, effects,\n category: TorrowSdkErrorCategory.StorageFailure });\n }\n effects.push({ area: TorrowSdkEffectArea.SessionMemory, step: AuthSessionEffectStep.SetTokens, state: TorrowSdkEffectState.Applied });\n failureAt = SessionFailureStage.InitializeDevice;\n if (!session.tokenSnapshot.deviceId?.trim()) {\n const id = session.resolveDeviceInfo().deviceId?.trim();\n if (id) {\n session.tokenSnapshot = { ...session.tokenSnapshot, deviceId: id };\n await session.persist(false, effects);\n }\n }\n failureAt = SessionFailureStage.BuildClient;\n session._client = session.buildClient();\n if (!opts.skipEnsureFreshOnOpen) {\n failureAt = SessionFailureStage.RefreshTokens;\n await session.runExclusive(() => session.ensureFreshImpl(effects));\n }\n return { session, effects: Object.freeze(effects.map((effect) => Object.freeze({ ...effect }))) };\n } catch (error) { throw sessionFailure(error, { operation: \"TokenSession.open\", failureAt, effects }); }\n }\n\n /** Persist, переданный в `TokenSession.open` / `TorrowAuth.*`. */\n get tokenStorage(): TokenStorage {\n return this.opts.tokenStorage;\n }\n\n get client(): TorrowClient {\n if (!this._client) {\n throw new Error(\"TokenSession: client not initialized (call TokenSession.open)\");\n }\n return this._client;\n }\n\n /** Текущий статус сессии. */\n get status(): TokenSessionStatus {\n return this.computeStatus();\n }\n\n /** Подписка на смену статуса; возвращает unsubscribe. Сразу вызывает listener с текущим статусом. */\n onStatusChange(listener: TokenSessionStatusListener): () => void {\n this.statusListeners.add(listener);\n try {\n listener(this.computeStatus());\n } catch {\n // listener errors must not break session\n }\n return () => {\n this.statusListeners.delete(listener);\n };\n }\n\n /** Текущий снимок токенов сессии (копия {@link AuthTokenSnapshot}). */\n getTokenSnapshot(): AuthTokenSnapshot {\n return { ...this.tokenSnapshot };\n }\n\n /** `deviceInfo` из options open или fallback Desktop + deviceId из tokenSnapshot. */\n getDeviceInfo(): DeviceInfo {\n return this.resolveDeviceInfo();\n }\n\n /** Получить access после проактивного refresh; closed возвращает undefined без записи. */\n async getAccessToken(): Promise<string | undefined> {\n if (this.closed) return undefined;\n try { await this.ensureFresh(); }\n catch (error) {\n throw sessionFailure(error, { operation: \"TokenSession.getAccessToken\", failureAt: SessionFailureStage.RefreshTokens,\n effects: error instanceof TorrowSdkError ? error.effects ?? [] : [] });\n }\n return this.tokenSnapshot.accessToken?.trim() || undefined;\n }\n\n /**\n * Сохранить ответ `requestToken` / `refresh` (только TokenReady).\n * Сериализован с `ensureFresh` / `logout` через mutex. Дефолт merge намеренно\n * (refresh-shaped); логин — `{ replace: true }`. После `logout` — новая сессия.\n */\n async applyTokenResp(tokens: TokenResp, opts?: { /** Заменить snapshot вместо refresh-merge; по умолчанию false. */ replace?: boolean }): Promise<void> {\n const effects: TorrowSdkEffect[] = [];\n await this.runExclusive(async () => {\n try { await this.applyTokenRespUnlocked(tokens, opts, effects); }\n catch (error) { throw sessionFailure(error, { operation: \"TokenSession.applyTokenResp\", failureAt: SessionFailureStage.ApplyTokens, effects }); }\n });\n }\n\n /** Очистить память и сохранить snapshot; storage-сбой не отменяет очистку памяти. */\n async clearTokens(): Promise<void> {\n const effects: TorrowSdkEffect[] = [];\n await this.runExclusive(async () => {\n try { await this.clearTokensUnlocked(effects); }\n catch (error) { throw sessionFailure(error, { operation: \"TokenSession.clearTokens\", failureAt: SessionFailureStage.ClearTokens, effects }); }\n });\n }\n\n /**\n * Revoke refresh (если есть) + очистка storage.\n * Единственное публичное имя выхода из сессии.\n */\n async logout(): Promise<void> {\n const effects: TorrowSdkEffect[] = [];\n await this.runExclusive(async () => {\n this.closed = true;\n effects.push({ area: TorrowSdkEffectArea.SessionMemory, step: AuthSessionEffectStep.CloseSession, state: TorrowSdkEffectState.Applied });\n const refresh = this.tokenSnapshot.refreshToken?.trim();\n if (refresh) {\n let state = TorrowSdkEffectState.Applied;\n let prepared = false;\n try {\n const identity = this.identityClient();\n prepared = true;\n await identity.auth.revoke({ token: refresh });\n }\n catch (error) {\n // Отзыв подавляется, но неизвестный серверный результат не становится «не применено».\n state = !prepared || (error instanceof TorrowSdkError && error.request === TorrowSdkRequestState.NotStarted)\n ? TorrowSdkEffectState.NotApplied : TorrowSdkEffectState.Unknown;\n }\n effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.RevokeTokens, state });\n }\n try { await this.clearTokensUnlocked(effects); }\n catch (error) { throw sessionFailure(error, { operation: \"TokenSession.logout\", failureAt: SessionFailureStage.ClearTokens, effects }); }\n });\n }\n\n /**\n * Получить токены по **id auth-сессии** (`sessionId` из `sendCode` / `createSession`)\n * и **OTP-коду доступа**, затем сохранить их в storage сессии.\n *\n * Один вызов `POST /api/v1/auth/token` (`requestToken`) с `{ sessionId, code }`.\n * При успехе (`TokenReady`) — persist; если сессия ещё не готова —\n * `TorrowSdkError` с причиной `SessionNotReady` (поллинг для deep-link — внутри `TorrowAuth.loginWithLink`).\n *\n * HTTP вне mutex; persist сериализован через {@link applyTokenResp} (не оборачивать\n * весь метод в `runExclusive` — вложенный mutex с публичным `applyTokenResp`).\n */\n async requestTokenWithCode(args: {\n /** Идентификатор ранее созданной серверной auth-сессии. */\n sessionId: string;\n /** Одноразовый код доступа для выдачи токенов. */\n code: string;\n /** Права токенов; при отсутствии используются DefaultAuthScopes. */\n scopes?: AuthScope[];\n /** Отмена единственного серверного запроса выдачи. */\n signal?: AbortSignal;\n }): Promise<TokenResp> {\n const effects: TorrowSdkEffect[] = [];\n let failureAt = SessionFailureStage.ValidateInput;\n try {\n this.assertNotClosedForWrite();\n const sessionId = args.sessionId.trim();\n const code = args.code.trim();\n if (!sessionId || !code) {\n throw sessionFailure(undefined, { operation: \"TokenSession.requestTokenWithCode\", failureAt, effects,\n category: TorrowSdkErrorCategory.InvalidInput });\n }\n const scopes = args.scopes && args.scopes.length > 0 ? args.scopes : [...DefaultAuthScopes];\n const deviceInfo = this.resolveDeviceInfo();\n failureAt = SessionFailureStage.IssueTokens;\n const identity = this.identityClient();\n let resp: TokenResp;\n try {\n resp = normalizeTokenResp(await requestAuthToken(identity, { sessionId, code, scopes, deviceInfo }, args.signal));\n } catch (error) {\n effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.IssueTokens,\n state: error instanceof TorrowSdkError && error.request === TorrowSdkRequestState.NotStarted\n ? TorrowSdkEffectState.NotApplied : TorrowSdkEffectState.Unknown });\n if (isAuthSessionNotReadyYet(error)) {\n throw sessionFailure(error, { operation: \"TokenSession.requestTokenWithCode\", failureAt, effects,\n reason: AuthSessionErrorReason.SessionNotReady });\n }\n throw error;\n }\n failureAt = SessionFailureStage.ValidateTokens;\n if (tokenTransition(resp.status, true) !== TokenStatusValues.TokenReady) {\n effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.IssueTokens, state: TorrowSdkEffectState.Unknown });\n throw sessionFailure(undefined, { operation: \"TokenSession.requestTokenWithCode\", failureAt, effects,\n category: TorrowSdkErrorCategory.InvalidState, reason: AuthSessionErrorReason.SessionNotReady });\n }\n let ready: TokenResp;\n try { ready = assertTokenRespReadyForStorage(resp, \"TokenSession.requestTokenWithCode\"); }\n catch (error) {\n effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.IssueTokens, state: TorrowSdkEffectState.Unknown });\n throw sessionFailure(error, { operation: \"TokenSession.requestTokenWithCode\", failureAt, effects,\n category: TorrowSdkErrorCategory.InvalidResponse });\n }\n effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.IssueTokens, state: TorrowSdkEffectState.Applied });\n // HTTP остаётся вне mutex; только изменение памяти и persist сериализованы.\n failureAt = SessionFailureStage.ApplyTokens;\n await this.runExclusive(() => this.applyTokenRespUnlocked(ready, { replace: true }, effects));\n return ready;\n } catch (error) {\n throw sessionFailure(error, { operation: \"TokenSession.requestTokenWithCode\", failureAt, effects });\n }\n }\n\n /**\n * Явный refresh (single-flight через общий mutex сессии).\n * Сериализован с `logout` / `ensureFresh` / `applyTokenResp`.\n * При гонке с `logout()`: promise может resolve с `TokenResp`, но snapshot уже очищен —\n * не используй return value; смотри `session.status` / `getTokenSnapshot()`.\n */\n async refresh(): Promise<TokenResp> {\n const effects: TorrowSdkEffect[] = [];\n return this.runExclusive(async () => {\n try {\n this.assertNotClosedForWrite();\n if (!this.tokenSnapshot.refreshToken?.trim()) {\n throw sessionFailure(undefined, { operation: \"TokenSession.refresh\", failureAt: SessionFailureStage.ValidateState, effects,\n category: TorrowSdkErrorCategory.InvalidState });\n }\n const result = await this.refreshWithFailurePolicy(effects);\n if (result.outcome === \"refreshed\") return result.resp;\n throw result.error ?? this.lastRefreshError;\n } catch (error) {\n throw sessionFailure(error, { operation: \"TokenSession.refresh\", failureAt: SessionFailureStage.RefreshTokens, effects });\n }\n });\n }\n\n /** Обновить просроченный access; политика keep/clear сохраняет прежнее resolve-поведение. */\n async ensureFresh(): Promise<void> {\n const effects: TorrowSdkEffect[] = [];\n await this.runExclusive(async () => {\n try { await this.ensureFreshImpl(effects); }\n catch (error) { throw sessionFailure(error, { operation: \"TokenSession.ensureFresh\", failureAt: SessionFailureStage.RefreshTokens, effects }); }\n });\n }\n\n private assertNotClosedForWrite(): void {\n if (this.closed) {\n throw sessionFailure(new Error(SESSION_CLOSED_MESSAGE), { operation: \"TokenSession\", failureAt: SessionFailureStage.ValidateState, effects: [],\n category: TorrowSdkErrorCategory.InvalidState });\n }\n }\n\n /** Caller must hold `runExclusive`. Do not call public `applyTokenResp` from here. */\n private async applyTokenRespUnlocked(\n tokens: TokenResp,\n opts?: { /** Заменить snapshot вместо refresh-merge; по умолчанию false. */ replace?: boolean },\n effects: TorrowSdkEffect[] = [],\n ): Promise<void> {\n this.assertNotClosedForWrite();\n const normalized = normalizeTokenResp(tokens);\n try { assertTokenRespReadyForStorage(normalized, \"TokenSession.applyTokenResp\"); }\n catch (error) {\n throw sessionFailure(error, { operation: \"TokenSession\", failureAt: SessionFailureStage.ValidateTokens, effects,\n category: TorrowSdkErrorCategory.InvalidResponse });\n }\n this.tokenSnapshot = mergeAuthTokens(this.tokenSnapshot, normalized, {\n ...opts,\n // Host TTL survives refresh without expiration only while still fresh (incl. skew); else cleared → opaque until 401.\n keepPrevTtlIfExpiresAfterMs:\n Date.now() + (this.opts.accessTokenSkewMs ?? DEFAULT_SKEW_MS),\n });\n effects.push({ area: TorrowSdkEffectArea.SessionMemory, step: AuthSessionEffectStep.SetTokens, state: TorrowSdkEffectState.Applied });\n await this.persist(true, effects);\n }\n\n private async clearTokensUnlocked(effects: TorrowSdkEffect[] = []): Promise<void> {\n this.tokenSnapshot = {\n ...this.tokenSnapshot,\n accessToken: undefined,\n refreshToken: undefined,\n accessExpiresAtMs: undefined,\n };\n effects.push({ area: TorrowSdkEffectArea.SessionMemory, step: AuthSessionEffectStep.ClearTokens, state: TorrowSdkEffectState.Applied });\n await this.persist(true, effects, true);\n }\n\n /** Caller must hold `runExclusive`. `refreshWithFailurePolicy` skips when no refreshToken; guard here is intentional defense-in-depth. */\n private async refreshUnlocked(effects: TorrowSdkEffect[]): Promise<TokenResp> {\n const refresh = this.tokenSnapshot.refreshToken?.trim();\n if (!refresh) {\n throw sessionFailure(undefined, { operation: \"TokenSession\", failureAt: SessionFailureStage.ValidateState, effects,\n category: TorrowSdkErrorCategory.InvalidState });\n }\n const deviceInfo = this.resolveDeviceInfo();\n const identity = this.identityClient();\n let resp: TokenResp;\n try {\n resp = await identity.auth.refresh({ refreshToken: refresh, deviceInfo });\n } catch (error) {\n effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.RefreshTokens,\n state: error instanceof TorrowSdkError && error.request === TorrowSdkRequestState.NotStarted\n ? TorrowSdkEffectState.NotApplied : TorrowSdkEffectState.Unknown });\n throw error;\n }\n // Только допустимый ответ подтверждает серверный шаг; проверка не усиливает legacy контракт.\n const normalized = normalizeTokenResp(resp);\n try { assertTokenRespReadyForStorage(normalized, \"TokenSession.refresh\"); }\n catch (error) {\n effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.RefreshTokens, state: TorrowSdkEffectState.Unknown });\n throw sessionFailure(error, { operation: \"TokenSession\", failureAt: SessionFailureStage.ValidateTokens, effects,\n category: TorrowSdkErrorCategory.InvalidResponse });\n }\n effects.push({ area: TorrowSdkEffectArea.Remote, step: AuthSessionEffectStep.RefreshTokens, state: TorrowSdkEffectState.Applied });\n await this.applyTokenRespUnlocked(resp, undefined, effects);\n return resp;\n }\n\n /** Caller must hold `runExclusive`; сведения принадлежат только текущей операции. */\n private async refreshWithFailurePolicy(effects: TorrowSdkEffect[] = []): Promise<RefreshPolicyResult> {\n if (this.closed || !this.tokenSnapshot.refreshToken?.trim()) return { outcome: \"unchanged\" };\n try {\n const resp = await this.refreshUnlocked(effects);\n this.lastRefreshError = undefined;\n return { outcome: \"refreshed\", resp };\n } catch (cause: unknown) {\n const error = sessionFailure(cause, { operation: \"TokenSession\", failureAt: SessionFailureStage.RefreshTokens, effects });\n // Повторное использование причины не приписывает новому вызову прошлые изменения.\n this.lastRefreshError = error.withContext({ effects: undefined, operation: undefined, failureAt: undefined });\n let action: RefreshFailureAction;\n try { action = (await this.opts.onRefreshFailure?.(error)) ?? defaultOnRefreshFailure(error); }\n catch (callbackError) {\n throw sessionFailure(callbackError, { operation: \"TokenSession\", failureAt: SessionFailureStage.RefreshFailureCallback, effects,\n category: TorrowSdkErrorCategory.Unknown, reason: AuthSessionErrorReason.CallbackFailed });\n }\n if (action === \"throw\") throw error;\n if (action === \"keep\") return { outcome: \"unchanged\", error };\n await this.clearTokensUnlocked(effects);\n return { outcome: \"cleared\", error: error.withContext({ effects }) };\n }\n }\n\n private async runExclusive<T>(fn: () => Promise<T>): Promise<T> {\n const prev = this.ensureFreshMutex;\n let release!: () => void;\n this.ensureFreshMutex = new Promise<void>((resolve) => {\n release = resolve;\n });\n await prev;\n try {\n return await fn();\n } finally {\n release();\n }\n }\n\n private async ensureFreshImpl(effects: TorrowSdkEffect[] = []): Promise<void> {\n if (this.closed) return;\n\n const skew = this.opts.accessTokenSkewMs ?? DEFAULT_SKEW_MS;\n const access = this.tokenSnapshot.accessToken?.trim() ?? \"\";\n const expires = this.tokenSnapshot.accessExpiresAtMs ?? 0;\n if (access && this.isAccessStillFresh(access, expires, skew)) {\n return;\n }\n\n const refresh = this.tokenSnapshot.refreshToken?.trim() ?? \"\";\n if (refresh) {\n await this.refreshWithFailurePolicy(effects);\n }\n\n // Пустая / expired сессия допустима: caller сделает loginWithOtp / requestTokenWithCode.\n }\n\n private isAccessStillFresh(access: string, expiresAtMs: number, accessTokenSkewMs: number): boolean {\n const now = Date.now();\n const jwtExpMs = accessExpiresAtMsFromJwt(access);\n if (jwtExpMs !== undefined) {\n const jwtFresh = now < jwtExpMs - accessTokenSkewMs;\n const snapFresh = !(expiresAtMs > 0) || now < expiresAtMs - accessTokenSkewMs;\n return jwtFresh && snapFresh;\n }\n if (accessTokenLooksLikeJwt(access)) {\n // undecodable JWT-shaped → never fresh; see README «JWT vs integration key»\n return false;\n }\n if (!(expiresAtMs > 0)) {\n return true;\n }\n return now < expiresAtMs - accessTokenSkewMs;\n }\n\n private computeStatus(): TokenSessionStatus {\n const skew = this.opts.accessTokenSkewMs ?? DEFAULT_SKEW_MS;\n const access = this.tokenSnapshot.accessToken?.trim() ?? \"\";\n const expires = this.tokenSnapshot.accessExpiresAtMs ?? 0;\n if (access && this.isAccessStillFresh(access, expires, skew)) {\n return \"authenticated\";\n }\n const hasAny =\n Boolean(access) ||\n Boolean(this.tokenSnapshot.refreshToken?.trim()) ||\n (typeof expires === \"number\" && expires > 0);\n if (!hasAny) return \"unauthenticated\";\n return \"expired\";\n }\n\n private emitStatusChange(): void {\n const status = this.computeStatus();\n for (const listener of this.statusListeners) {\n try {\n listener(status);\n } catch {\n // listener errors must not break session\n }\n }\n }\n\n private resolveDeviceInfo(): DeviceInfo {\n if (this.opts.deviceInfo) {\n return { ...this.opts.deviceInfo };\n }\n const deviceId = this.tokenSnapshot.deviceId?.trim();\n return {\n ...(deviceId ? { deviceId } : {}),\n deviceType: DeviceTypeValues.Desktop,\n };\n }\n\n /** Identity-клиент с Basic приложения, без Bearer пользователя. */\n identityClient(): TorrowClient {\n return createIdentityClient(this.opts);\n }\n\n private buildClient(): TorrowClient {\n let fetchImpl = this.opts.fetch;\n if (this.opts.refreshOnUnauthorized !== false) {\n fetchImpl = createFetchWith401Retry({\n fetch: this.opts.fetch,\n getAccessToken: async () => {\n if (this.closed) return undefined;\n return this.tokenSnapshot.accessToken?.trim() || undefined;\n },\n refresh: async () => {\n await this.runExclusive(async () => {\n if (this.closed) {\n this.assertNotClosedForWrite();\n }\n const result = await this.refreshWithFailurePolicy();\n if (result.outcome !== \"refreshed\") {\n throw (\n this.lastRefreshError ??\n new Error(\"TokenSession 401-retry: refresh failed\")\n );\n }\n });\n },\n });\n }\n\n return new TorrowClient({\n baseUrl: this.opts.baseUrl,\n fetch: fetchImpl,\n defaultHeaders: this.opts.defaultHeaders,\n userAgent: this.opts.userAgent,\n authIdentityBasic: appCredentialsToAuthIdentityBasic(\n resolveAppCredentials(this.opts.appCredentials),\n ),\n getAccessToken: async () => this.getAccessToken(),\n });\n }\n\n private async persist(emitStatus: boolean, effects: TorrowSdkEffect[] = [], clear = false): Promise<void> {\n const effect: TorrowSdkEffect = { area: TorrowSdkEffectArea.Storage,\n step: clear ? AuthSessionEffectStep.ClearStorage : AuthSessionEffectStep.SaveTokens,\n state: TorrowSdkEffectState.Unknown };\n effects.push(effect);\n try { await this.opts.tokenStorage.save(this.tokenSnapshot); }\n catch (error) {\n throw sessionFailure(error, { operation: \"TokenSession\", failureAt: clear ? SessionFailureStage.ClearStorage : SessionFailureStage.SaveTokens, effects,\n category: TorrowSdkErrorCategory.StorageFailure });\n }\n effects[effects.length - 1] = { ...effect, state: TorrowSdkEffectState.Applied };\n if (emitStatus) this.emitStatusChange();\n }\n}\n","import { AuthSessionErrorReason, TorrowSdkError, TorrowSdkErrorDomain } from \"@torrow/sdk-client\";\n\n/** Распознавание ожидающего подтверждения auth через явные исходные HTTP данные. */\nexport function isAuthSessionNotReadyYet(error: unknown): boolean {\n if (!(error instanceof TorrowSdkError)) return false;\n if (error.detail?.domain === TorrowSdkErrorDomain.AuthSession && error.detail.reason === AuthSessionErrorReason.SessionNotReady) return true;\n if (!error.http) return false;\n const body = error.getSensitiveDetails().body;\n const text = [error.http.apiErrorName, typeof body === \"string\" ? body : JSON.stringify(body ?? \"\")].join(\" \").toLowerCase();\n // Существующая предметная эвристика нужна polling; безопасный message не содержит серверный текст.\n return text.includes(\"session is not ready\") || text.includes(\"unauthorized_client\");\n}\n","import {\n type AuthSessionErrorReason,\n type AuthSessionErrorDetail,\n TorrowSdkError,\n TorrowSdkErrorCategory,\n TorrowSdkErrorDomain,\n type TorrowSdkEffect,\n} from \"@torrow/sdk-client\";\n\n/** Безопасные стадии выбранных составных операций auth. */\nexport enum SessionFailureStage {\n /** Отправка OTP. */ SendCode = \"SendCode\",\n /** Создание серверной auth-сессии по ссылке. */ CreateAuthSession = \"CreateAuthSession\",\n /** Callback после подтверждённой отправки OTP. */ OnSendCode = \"OnSendCode\",\n /** Получение кода от приложения. */ GetCode = \"GetCode\",\n /** Показ ссылки приложением. */ OnLink = \"OnLink\",\n /** Ожидание выдачи токенов. */ PollTokens = \"PollTokens\",\n /** Проверка ответа login. */ ValidateResponse = \"ValidateResponse\",\n /** Проверка аргументов вызывающего кода. */ ValidateInput = \"ValidateInput\",\n /** Проверка состояния/закрытия сессии. */ ValidateState = \"ValidateState\",\n /** Загрузка текущего снимка. */ LoadStorage = \"LoadStorage\",\n /** Добавление идентификатора устройства. */ InitializeDevice = \"InitializeDevice\",\n /** Создание клиентского транспорта. */ BuildClient = \"BuildClient\",\n /** Выдача токенов сервером. */ IssueTokens = \"IssueTokens\",\n /** Проверка ответа перед применением. */ ValidateTokens = \"ValidateTokens\",\n /** Применение выданных токенов. */ ApplyTokens = \"ApplyTokens\",\n /** Обновление токенов сервером. */ RefreshTokens = \"RefreshTokens\",\n /** Callback политики refresh. */ RefreshFailureCallback = \"RefreshFailureCallback\",\n /** Очистка памяти токенов. */ ClearTokens = \"ClearTokens\",\n /** Сохранение снимка. */ SaveTokens = \"SaveTokens\",\n /** Сохранение очищенного снимка. */ ClearStorage = \"ClearStorage\",\n /** Открытие пользовательской сессии. */ OpenSession = \"OpenSession\",\n /** Сохранение токенов хоста до connect. */ SeedStorage = \"SeedStorage\",\n /** Подключение после сохранения host tokens. */ Connect = \"Connect\",\n}\n\n/** Локальная диагностика выбранной операции; не контекст всего SDK. */\nexport interface SessionFailureContext {\n /** Стабильное имя публичного метода. */\n operation: string;\n /** Стадия текущего отказа без аргументов и секретов. */\n failureAt: SessionFailureStage;\n /** Только предметные изменения текущего вызова. */\n effects: readonly TorrowSdkEffect[];\n /** Уточнение локальной причины; сетевой отказ сохраняет свою категорию. */\n reason?: AuthSessionErrorReason;\n /** Переопределение причины для локального/storage отказа. */\n category?: TorrowSdkErrorCategory;\n}\n\n/** Новый безопасный снимок ошибки с локальными сведениями текущей операции. */\nexport function sessionFailure(error: unknown, context: SessionFailureContext): TorrowSdkError {\n const detail: AuthSessionErrorDetail = { domain: TorrowSdkErrorDomain.AuthSession, ...(context.reason ? { reason: context.reason } : {}) };\n // Локальная причина (например storage после refresh) не наследует HTTP успешного шага.\n if (context.category !== undefined) {\n return new TorrowSdkError({ ...context, category: context.category, detail,\n sensitive: { cause: error } });\n }\n if (error instanceof TorrowSdkError) {\n return error.withContext({ operation: context.operation, failureAt: error.operation?.startsWith(\"TokenSession\") ? error.failureAt ?? context.failureAt : context.failureAt,\n effects: context.effects, ...(context.reason ? { detail } : {}) });\n }\n return new TorrowSdkError({ category: TorrowSdkErrorCategory.Unknown,\n operation: context.operation, failureAt: context.failureAt, effects: context.effects,\n detail, sensitive: { cause: error } });\n}\n","/**\n * Обёртка `fetch` для **`refreshOnUnauthorized`**: на **401** (не `/api/v1/auth/*`)\n * вызывает `refresh` и один раз повторяет запрос с новым Bearer.\n *\n * Ставит `TokenSession` при сборке `client`, если `refreshOnUnauthorized !== false`.\n * Single-flight refresh — снаружи (mutex в `TokenSession`).\n */\nexport type FetchWith401RetryOptions = {\n getAccessToken: () => Promise<string | undefined>;\n refresh: () => Promise<void>;\n fetch?: typeof fetch;\n};\n\nfunction requestUrl(input: RequestInfo | URL): string {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.href;\n return input.url;\n}\n\nfunction isAuthIdentityUrl(url: string): boolean {\n return url.includes(\"/api/v1/auth/\");\n}\n\nexport function createFetchWith401Retry(options: FetchWith401RetryOptions): typeof fetch {\n const baseFetch = options.fetch ?? globalThis.fetch.bind(globalThis);\n\n const wrapped: typeof fetch = async (input, init) => {\n const first = await baseFetch(input, init);\n if (first.status !== 401) return first;\n const url = requestUrl(input);\n if (isAuthIdentityUrl(url)) return first;\n\n await options.refresh();\n const token = await options.getAccessToken();\n const headers = new Headers(init?.headers);\n if (token) {\n headers.set(\"Authorization\", `Bearer ${token}`);\n } else {\n headers.delete(\"Authorization\");\n }\n return baseFetch(input, { ...init, headers });\n };\n\n return wrapped;\n}\n","/**\n * Утилиты ответа auth API → локальный {@link AuthTokenSnapshot}.\n *\n * Нормализация PascalCase/snake_case, merge access/refresh, TTL из JWT,\n * assert `TokenReady` перед persist. Используется `TokenSession` и OTP/link runners.\n */\nimport type { TokenResp } from \"@torrow/sdk-client\";\nimport { isTokenRespReadyStatus } from \"@torrow/sdk-client\";\nimport type { AuthTokenSnapshot } from \"./types.js\";\nimport { tokenResponseFields } from \"./auth-protocol.js\";\n\n/**\n * Нормализация сырого ответа token/refresh (PascalCase / snake_case на некоторых шлюзах).\n */\nexport function normalizeTokenResp(raw: unknown): TokenResp {\n if (raw === null || raw === undefined) return {};\n if (typeof raw === \"string\") {\n const t = raw.trim();\n return t ? { accessToken: t } : {};\n }\n if (typeof raw !== \"object\") return {};\n const o = raw as Record<string, unknown>;\n const { access, refresh, expiration: expRaw, status: statusRaw } = tokenResponseFields(o);\n const out: TokenResp = {};\n if (typeof access === \"string\" && access.trim()) out.accessToken = access.trim();\n if (typeof refresh === \"string\" && refresh.trim()) out.refreshToken = refresh.trim();\n if (typeof expRaw === \"number\" && Number.isFinite(expRaw)) out.expiration = expRaw;\n if (typeof statusRaw === \"string\" && statusRaw.trim()) {\n out.status = statusRaw.trim() as TokenResp[\"status\"];\n }\n return out;\n}\n\n/** Decode one JWT segment (base64url → UTF-8 JSON). Node 18+ and browsers (no Buffer). */\nfunction decodeJwtJsonSegment(segment: string): unknown | undefined {\n try {\n const b64 = segment.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const pad = b64.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (b64.length % 4));\n const binary = atob(b64 + pad);\n const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));\n return JSON.parse(new TextDecoder().decode(bytes)) as unknown;\n } catch {\n return undefined;\n }\n}\n\n/**\n * `exp` JWT → мс; подпись не проверяется.\n * Требует валидный JSON и в header, и в payload — иначе битый access\n * (например `e12yJ…` вместо `eyJ…`) давал бы ложный «свежий» TTL по payload.\n */\nexport function accessExpiresAtMsFromJwt(accessToken: string): number | undefined {\n const parts = accessToken.split(\".\");\n if (parts.length < 2) return undefined;\n if (decodeJwtJsonSegment(parts[0]!) === undefined) return undefined;\n const payload = decodeJwtJsonSegment(parts[1]!);\n if (payload === null || typeof payload !== \"object\") return undefined;\n const exp = (payload as { exp?: unknown }).exp;\n if (typeof exp !== \"number\" || !Number.isFinite(exp)) return undefined;\n return exp * 1000;\n}\n\n/**\n * Heuristic: 3+ dot segments = JWT-shaped wire token.\n * Opaque integration keys must not look like `a.b.c` — not supported as opaque.\n */\nexport function accessTokenLooksLikeJwt(accessToken: string): boolean {\n return accessToken.split(\".\").length >= 3;\n}\n\nexport type MergeAuthTokensOptions = {\n replace?: boolean;\n /**\n * Opaque refresh без `expiration`: prev host TTL сохраняется только если\n * `prev.accessExpiresAtMs` строго больше этого порога (default `Date.now()`).\n * `TokenSession` передаёт `Date.now() + accessTokenSkewMs`.\n */\n keepPrevTtlIfExpiresAfterMs?: number;\n};\n\nexport function mergeAuthTokens(\n prev: AuthTokenSnapshot,\n tokens: TokenResp,\n opts?: MergeAuthTokensOptions,\n): AuthTokenSnapshot {\n const replace = opts?.replace === true;\n const nextAccess = (\n replace\n ? tokens.accessToken?.trim() || \"\"\n : tokens.accessToken?.trim() || prev.accessToken?.trim() || \"\"\n ).trim();\n const refreshToken = replace\n ? tokens.refreshToken?.trim() || \"\"\n : tokens.refreshToken?.trim() || prev.refreshToken?.trim() || \"\";\n\n const ttlSec = tokens.expiration ?? 0;\n let accessExpiresAtMs: number | undefined;\n if (ttlSec > 0) {\n accessExpiresAtMs = Date.now() + ttlSec * 1000;\n } else {\n const keepAfter = opts?.keepPrevTtlIfExpiresAfterMs ?? Date.now();\n const jwtExp = nextAccess ? accessExpiresAtMsFromJwt(nextAccess) : undefined;\n if (typeof jwtExp === \"number\") {\n accessExpiresAtMs = jwtExp;\n } else if (\n !replace &&\n typeof prev.accessExpiresAtMs === \"number\" &&\n prev.accessExpiresAtMs > keepAfter\n ) {\n // Host TTL survives refresh without expiration only while still fresh (incl. caller skew); else cleared → opaque until 401.\n accessExpiresAtMs = prev.accessExpiresAtMs;\n } else {\n accessExpiresAtMs = undefined;\n }\n }\n\n return {\n ...prev,\n accessToken: nextAccess || undefined,\n refreshToken: refreshToken || undefined,\n accessExpiresAtMs,\n };\n}\n\n/** Проверка перед сохранением: только TokenReady / legacy без status. */\nexport function assertTokenRespReadyForStorage(tokens: TokenResp, context: string): TokenResp {\n const status = tokens.status;\n if (!isTokenRespReadyStatus(status)) {\n throw new Error(\n `${context}: TokenResp.status=${status} (ожидался TokenReady). Токены не сохраняем.`,\n );\n }\n if (!tokens.accessToken?.trim() && !tokens.refreshToken?.trim()) {\n throw new Error(`${context}: TokenResp без accessToken и refreshToken.`);\n }\n return tokens;\n}\n","/**\n * Публичный фасад аутентификации **`TorrowAuth`**.\n *\n * Точка входа приложения: `createOtpFlow` / `connect` / `loginWithOtp` / `loginWithLink` /\n * `fromTokens` / `anonymous`. Возвращает {@link TorrowAuthResult}\n * (`{ client, session }`), кроме `anonymous` (только `TorrowClient`).\n *\n * Оркестрация OTP/Telegram — в `otp-login.ts` / `link-login.ts`;\n * persist и refresh — в {@link TokenSession}.\n */\nimport {\n TorrowClient,\n TorrowSdkError,\n TorrowSdkErrorCategory,\n TorrowSdkEffectArea,\n TorrowSdkEffectState,\n AuthSessionEffectStep,\n type TorrowSdkEffect,\n type AccountType,\n type AuthCaptchaType,\n type AuthScope,\n type DeviceInfo,\n type SendCodeAccountType,\n type VerificationMethod,\n} from \"@torrow/sdk-client\";\nimport { sessionFailure, SessionFailureStage } from \"./session-error.js\";\nimport { accessExpiresAtMsFromJwt } from \"./merge-tokens.js\";\nimport { runLinkLogin } from \"./link-login.js\";\nimport {\n runOtpLoginWithEffects,\n type OtpSendCodeResult,\n type OtpGetCodeContext,\n} from \"./otp-login.js\";\nimport { TokenSession } from \"./TokenSession.js\";\nimport type { TokenSessionOptions } from \"./types.js\";\nimport { createOtpFlow } from \"./otp-flow.js\";\n\n/** Результат `TorrowAuth.connect` / `loginWithOtp` / `loginWithLink` / `fromTokens`: API-клиент + сессия. */\nexport type TorrowAuthResult = {\n client: TorrowClient;\n session: TokenSession;\n};\n\n/**\n * Общая база опций `TorrowAuth.connect` / `loginWithOtp` / `loginWithLink` / `fromTokens`.\n *\n * Производная от {@link TokenSessionOptions}: те же HTTP-кнопки + storage + app creds,\n * но `deviceInfo` **обязателен**; `onRefreshFailure` пробрасывается в `TokenSession.open`.\n *\n * **Карта:** `TorrowClientConfig` → (Pick HTTP) → `TokenSessionOptions` → **эта база** →\n * `TorrowAuthConnectOptions` / `TorrowAuthLoginWithOtpOptions` / …\n * Приложение обычно передаёт именно `TorrowAuth*Options`, не `TorrowClientConfig`.\n *\n * `appCredentials` опционален: без него — дефолт SDK (`DEFAULT_APP_CREDENTIALS`).\n */\nexport type TorrowAuthSessionBase = Omit<TokenSessionOptions, \"deviceInfo\"> & {\n deviceInfo: DeviceInfo;\n};\n\n/** Опции {@link TorrowAuth.connect}: restore / refresh из `tokenStorage`. */\nexport type TorrowAuthConnectOptions = TorrowAuthSessionBase;\n\n/** Аккаунт для OTP `sendCode` — плоско, как OpenAPI `SendCodeReq`. */\nexport type TorrowAuthAccount = {\n /** `Phone` \\| `Email` → `SendCodeReq.type`. */\n accountType: SendCodeAccountType;\n /** Телефон или email → `SendCodeReq.accountName`. */\n accountName: string;\n};\n\n/** Опции {@link TorrowAuth.loginWithOtp}: connect-база + OTP-колбэки / поля `sendCode`. */\nexport type TorrowAuthLoginWithOtpOptions = TorrowAuthSessionBase & {\n account: TorrowAuthAccount;\n /**\n * Сигнал UI после `sendCode` (`usedMethod`, `codeLength`, …).\n * До Mobile ID poll и до classic `getCode`. См. {@link OtpSendCodeResult}.\n */\n onSendCode: (result: OtpSendCodeResult) => void | Promise<void>;\n /**\n * Вернуть одноразовый код. Не зовётся на happy-path Mobile ID (push без SMS).\n * См. {@link OtpGetCodeContext}.\n */\n getCode: (ctx: OtpGetCodeContext) => Promise<string>;\n /** → `TokenReq.scopes`. По умолчанию `DefaultAuthScopes`. */\n scopes?: AuthScope[];\n /** → `SendCodeReq.language`. По умолчанию `\"ru\"`. */\n language?: string;\n /** → `SendCodeReq.regionStorageId`. По умолчанию `70`. */\n regionStorageId?: number;\n /** → `SendCodeReq.captcha` (если шлюз требует). */\n captcha?: string;\n /** → `SendCodeReq.captchaType`. */\n captchaType?: AuthCaptchaType;\n /** → `SendCodeReq.inviteSessionId`. */\n inviteSessionId?: string;\n /**\n * Hint серверу → `SendCodeReq.preferredMethod`.\n * Факт смотри в `onSendCode.usedMethod`.\n */\n preferredMethod?: VerificationMethod;\n /**\n * Capability клиента → `SendCodeReq.supportedMethods`.\n * Порядок задаёт приоритет: для телефона рекомендуется `TelegramGateway`, `MtsMobileId`, `Sms`.\n * Без `MtsMobileId` сервер обычно не выберет Mobile ID.\n */\n supportedMethods?: VerificationMethod[];\n /** Отмена `sendCode` / poll / `requestToken`. */\n signal?: AbortSignal;\n};\n\n/** Опции {@link TorrowAuth.loginWithLink} (Telegram deep-link). */\nexport type TorrowAuthLoginWithLinkOptions = TorrowAuthSessionBase & {\n /** Показать deep-link / QR (`CreateAuthSessionResp.authUrl`). */\n onLink: (authUrl: string) => void | Promise<void>;\n /** По умолчанию `Telegram`. */\n accountType?: AccountType;\n language?: string;\n regionStorageId?: number;\n captcha?: string;\n inviteSessionId?: string;\n scopes?: AuthScope[];\n /** Интервал poll `requestToken` (мс). По умолчанию 2000. */\n pollIntervalMs?: number;\n /** Max wait poll (сек). Иначе TTL сессии из ответа или 180. */\n pollTimeoutSec?: number;\n signal?: AbortSignal;\n};\n\n/** Опции {@link TorrowAuth.fromTokens}: сид токенов от хоста в `tokenStorage` → connect. */\nexport type TorrowAuthFromTokensOptions = TorrowAuthSessionBase & {\n accessToken: string;\n /** Без него refresh недоступен (даже если задан `appCredentials`). */\n refreshToken?: string;\n /**\n * Абсолютное истечение access (мс). Для opaque/integration keys без JWT `exp`.\n * Приоритет **записи в snapshot**: explicit (`> 0` finite) → JWT `exp` → `undefined` (бессрочный ключ).\n * Runtime freshness AND-ит JWT `exp`: будущий explicit TTL не оживит просроченный JWT.\n * Opaque без JWT-формы: explicit TTL или бессрочно без TTL.\n */\n accessExpiresAtMs?: number;\n};\n\nfunction sessionOpenOpts(base: TorrowAuthSessionBase) {\n // Intentional allowlist: login-only fields on TorrowAuth*Options must not leak into TokenSession.open.\n return {\n appCredentials: base.appCredentials,\n tokenStorage: base.tokenStorage,\n deviceInfo: base.deviceInfo,\n baseUrl: base.baseUrl,\n fetch: base.fetch,\n defaultHeaders: base.defaultHeaders,\n userAgent: base.userAgent,\n accessTokenSkewMs: base.accessTokenSkewMs,\n refreshOnUnauthorized: base.refreshOnUnauthorized,\n onRefreshFailure: base.onRefreshFailure,\n };\n}\n\n// Inherits onRefreshFailure (and other sessionOpenOpts fields) via spread — same forwarding as connect/fromTokens.\nfunction sessionOpenOptsForLogin(base: TorrowAuthSessionBase) {\n return { ...sessionOpenOpts(base), skipEnsureFreshOnOpen: true as const };\n}\n\n/** Вложенный отказ сохраняет известную auth-стадию; чужое значение не заменяет текущую. */\nfunction loginFailureStage(error: unknown, current: SessionFailureStage): SessionFailureStage {\n return error instanceof TorrowSdkError\n ? Object.values(SessionFailureStage).find(stage => stage === error.failureAt) ?? current\n : current;\n}\n\n/**\n * Фасад аутентификации для приложений и виджетов.\n *\n * - {@link TorrowAuth.connect} — restore / refresh\n * - {@link TorrowAuth.createOtpFlow} — раздельные OTP-шаги без сессии\n * - {@link TorrowAuth.loginWithOtp} — OTP (Phone/Email, Mobile ID / classic)\n * - {@link TorrowAuth.loginWithLink} — Telegram deep-link\n * - {@link TorrowAuth.fromTokens} — сид токенов от хоста\n * - {@link TorrowAuth.anonymous} — клиент без Bearer\n */\nexport const TorrowAuth = {\n /** Пошаговый OTP без storage, polling и refresh. */\n createOtpFlow,\n /** Загрузить сессию; общий отказ сохраняет текущую диагностику open/refresh. */\n async connect(options: TorrowAuthConnectOptions): Promise<TorrowAuthResult> {\n try {\n const session = await TokenSession.open(sessionOpenOpts(options));\n return { client: session.client, session };\n } catch (error) {\n throw sessionFailure(error, { operation: \"TorrowAuth.connect\", failureAt: SessionFailureStage.OpenSession,\n effects: error instanceof TorrowSdkError ? error.effects ?? [] : [] });\n }\n },\n\n async loginWithOtp(options: TorrowAuthLoginWithOtpOptions): Promise<TorrowAuthResult> {\n const effects: TorrowSdkEffect[] = [];\n let failureAt = SessionFailureStage.ValidateInput;\n try {\n const accountName = options.account.accountName.trim();\n if (!accountName) throw sessionFailure(undefined, {operation:\"TorrowAuth.loginWithOtp\",failureAt,effects,\n category:TorrowSdkErrorCategory.InvalidInput});\n failureAt = SessionFailureStage.OpenSession;\n const opened = await TokenSession.openWithEffects(sessionOpenOptsForLogin(options));\n const session = opened.session;\n effects.push(...opened.effects);\n const deviceInfo = session.getDeviceInfo();\n failureAt = SessionFailureStage.SendCode;\n const ready = await runOtpLoginWithEffects(session.identityClient(), {\n accountName,\n type: options.account.accountType,\n deviceInfo,\n onSendCode: options.onSendCode,\n getCode: options.getCode,\n scopes: options.scopes,\n language: options.language,\n regionStorageId: options.regionStorageId,\n captcha: options.captcha,\n captchaType: options.captchaType,\n inviteSessionId: options.inviteSessionId,\n preferredMethod: options.preferredMethod,\n supportedMethods: options.supportedMethods,\n signal: options.signal,\n });\n effects.push(...ready.effects);\n failureAt = SessionFailureStage.ApplyTokens;\n await session.applyTokenResp(ready.tokens, { replace: true });\n return { client: session.client, session };\n } catch (error) {\n throw sessionFailure(error, {operation:\"TorrowAuth.loginWithOtp\",failureAt:loginFailureStage(error, failureAt),\n effects:[...effects,...(error instanceof TorrowSdkError ? error.effects ?? [] : [])]});\n }\n },\n\n async loginWithLink(options: TorrowAuthLoginWithLinkOptions): Promise<TorrowAuthResult> {\n const effects: TorrowSdkEffect[] = [];\n let failureAt = SessionFailureStage.OpenSession;\n try {\n const opened = await TokenSession.openWithEffects(sessionOpenOptsForLogin(options));\n const session = opened.session;\n effects.push(...opened.effects);\n failureAt = SessionFailureStage.CreateAuthSession;\n await runLinkLogin(session, {\n deviceInfo: session.getDeviceInfo(),\n onLink: options.onLink,\n accountType: options.accountType,\n language: options.language,\n regionStorageId: options.regionStorageId,\n captcha: options.captcha,\n inviteSessionId: options.inviteSessionId,\n scopes: options.scopes,\n pollIntervalMs: options.pollIntervalMs,\n pollTimeoutSec: options.pollTimeoutSec,\n signal: options.signal,\n });\n return { client: session.client, session };\n } catch (error) {\n throw sessionFailure(error, {operation:\"TorrowAuth.loginWithLink\",failureAt:loginFailureStage(error, failureAt),\n effects:[...effects,...(error instanceof TorrowSdkError ? error.effects ?? [] : [])]});\n }\n },\n\n /** Сохранить host tokens, затем connect; последующий отказ не отменяет Applied сохранение. */\n async fromTokens(options: TorrowAuthFromTokensOptions): Promise<TorrowAuthResult> {\n const effects: TorrowSdkEffect[] = [];\n let failureAt = SessionFailureStage.ValidateInput;\n try {\n const accessToken = options.accessToken.trim();\n if (!accessToken) {\n throw sessionFailure(undefined, { operation: \"TorrowAuth.fromTokens\", failureAt, effects,\n category: TorrowSdkErrorCategory.InvalidInput });\n }\n const refreshToken = options.refreshToken?.trim() || undefined;\n failureAt = SessionFailureStage.LoadStorage;\n let prev;\n try { prev = await options.tokenStorage.load(); }\n catch (error) {\n throw sessionFailure(error, { operation: \"TorrowAuth.fromTokens\", failureAt, effects,\n category: TorrowSdkErrorCategory.StorageFailure });\n }\n const explicitTtl = options.accessExpiresAtMs;\n const accessExpiresAtMs = typeof explicitTtl === \"number\" && Number.isFinite(explicitTtl) && explicitTtl > 0\n ? explicitTtl : accessExpiresAtMsFromJwt(accessToken);\n failureAt = SessionFailureStage.SeedStorage;\n effects.push({ area: TorrowSdkEffectArea.Storage, step: AuthSessionEffectStep.SaveTokens, state: TorrowSdkEffectState.Unknown });\n try {\n await options.tokenStorage.save({ accessToken, refreshToken,\n accessExpiresAtMs: typeof accessExpiresAtMs === \"number\" && accessExpiresAtMs > 0 ? accessExpiresAtMs : undefined,\n deviceId: options.deviceInfo.deviceId?.trim() || prev.deviceId?.trim() || undefined });\n } catch (error) {\n throw sessionFailure(error, { operation: \"TorrowAuth.fromTokens\", failureAt, effects,\n category: TorrowSdkErrorCategory.StorageFailure });\n }\n effects[0] = { ...effects[0]!, state: TorrowSdkEffectState.Applied };\n failureAt = SessionFailureStage.Connect;\n // Seed сохранён: await позволяет сохранить подтверждение при asynchronous rejection.\n try { return await TorrowAuth.connect(options); }\n catch (error) {\n if (error instanceof TorrowSdkError) effects.push(...error.effects ?? []);\n throw error;\n }\n } catch (error) { throw sessionFailure(error, { operation: \"TorrowAuth.fromTokens\", failureAt, effects }); }\n },\n\n anonymous(\n config: Pick<TokenSessionOptions, \"baseUrl\" | \"fetch\" | \"defaultHeaders\" | \"userAgent\"> = {},\n ): TorrowClient {\n return new TorrowClient({\n ...config,\n getAccessToken: undefined,\n });\n },\n} as const;\n","import { sessionFailure, SessionFailureStage } from \"./session-error.js\";\nimport { throwIfOtpAborted } from \"./otp-errors.js\";\n/**\n * Внутренний оркестратор Telegram deep-link (не публичный export).\n *\n * **`createSession`** → `onLink(authUrl)` → poll **`requestToken`** до ready →\n * persist через `TokenSession`. Вызывается из `TorrowAuth.loginWithLink`.\n */\nimport {\n TorrowSdkError,\n TorrowSdkErrorCategory,\n TorrowSdkRequestState,\n TorrowSdkEffectArea,\n TorrowSdkEffectState,\n AuthSessionEffectStep,\n AuthSessionErrorReason,\n type TorrowSdkEffect,\n AccountTypeValues,\n DefaultAuthScopes,\n type AccountType,\n type AuthScope,\n type CreateAuthSessionReq,\n type DeviceInfo,\n type TokenResp,\n} from \"@torrow/sdk-client\";\nimport { isAuthSessionNotReadyYet } from \"./auth-session-not-ready.js\";\nimport type { TokenSession } from \"./TokenSession.js\";\nimport { sleepMs } from \"./sleep.js\";\n\nconst DEFAULT_POLL_INTERVAL_MS = 2000;\nconst DEFAULT_POLL_TIMEOUT_SEC = 180;\nconst DEFAULT_LANGUAGE = \"ru\";\nconst DEFAULT_REGION = 70;\n\nexport type RunLinkLoginOptions = {\n deviceInfo: DeviceInfo;\n /** Показать deep-link / QR (`CreateAuthSessionResp.authUrl`). Код OTP сюда не вводят. */\n onLink: (authUrl: string) => void | Promise<void>;\n /** По умолчанию `Telegram`. */\n accountType?: AccountType;\n /** → `CreateAuthSessionReq.language`. По умолчанию `\"ru\"`. */\n language?: string;\n /** → `CreateAuthSessionReq.regionStorageId`. По умолчанию `70`. */\n regionStorageId?: number;\n captcha?: string;\n inviteSessionId?: string;\n /** → `TokenReq.scopes` при poll. По умолчанию `DefaultAuthScopes`. */\n scopes?: AuthScope[];\n /** Интервал poll (мс). По умолчанию 2000. */\n pollIntervalMs?: number;\n /** Max wait (сек). Иначе `sessionLifeTimeInSeconds` из ответа или 180. */\n pollTimeoutSec?: number;\n signal?: AbortSignal;\n};\n\n/**\n * `createSession` → `onLink` → poll `session.requestTokenWithCode` до TokenReady.\n */\nexport async function runLinkLogin(\n session: TokenSession,\n options: RunLinkLoginOptions,\n): Promise<TokenResp> {\n const effects: TorrowSdkEffect[] = [];\n let failureAt = SessionFailureStage.CreateAuthSession;\n try {\n throwIfOtpAborted(options.signal);\n const client = session.identityClient();\n const accountType = options.accountType ?? AccountTypeValues.Telegram;\n const body: CreateAuthSessionReq = {\n accountType,\n language: options.language?.trim() || DEFAULT_LANGUAGE,\n regionStorageId:\n typeof options.regionStorageId === \"number\" && Number.isFinite(options.regionStorageId)\n ? options.regionStorageId\n : DEFAULT_REGION,\n ...(options.inviteSessionId?.trim()\n ? { inviteSessionId: options.inviteSessionId.trim() }\n : {}),\n ...(options.captcha?.trim() ? { captcha: options.captcha.trim() } : {}),\n ...(options.deviceInfo.deviceId?.trim()\n ? { deviceId: options.deviceInfo.deviceId.trim() }\n : {}),\n };\n\n let createResp;\n try { createResp = await client.auth.createSession(body, { signal: options.signal }); }\n catch (error) {\n effects.push({area:TorrowSdkEffectArea.Remote,step:AuthSessionEffectStep.CreateAuthSession,\n state:error instanceof TorrowSdkError && error.request === TorrowSdkRequestState.NotStarted ? TorrowSdkEffectState.NotApplied : TorrowSdkEffectState.Unknown});\n throw error;\n }\n effects.push({area:TorrowSdkEffectArea.Remote,step:AuthSessionEffectStep.CreateAuthSession,state:TorrowSdkEffectState.Unknown});\n failureAt = SessionFailureStage.ValidateResponse;\n const sessionId = createResp.sessionId?.trim();\n const code = createResp.code?.trim();\n const authUrl = createResp.authUrl?.trim();\n if (!sessionId || !code || !authUrl) {\n throw sessionFailure(undefined, {operation:\"TorrowAuth.loginWithLink\",failureAt,effects:[],category:TorrowSdkErrorCategory.InvalidResponse});\n }\n\n effects[effects.length - 1] = {area:TorrowSdkEffectArea.Remote,step:AuthSessionEffectStep.CreateAuthSession,state:TorrowSdkEffectState.Applied};\n failureAt = SessionFailureStage.OnLink;\n await options.onLink(authUrl);\n\n const scopes =\n options.scopes && options.scopes.length > 0 ? options.scopes : [...DefaultAuthScopes];\n const intervalMs =\n typeof options.pollIntervalMs === \"number\" && options.pollIntervalMs > 0\n ? options.pollIntervalMs\n : DEFAULT_POLL_INTERVAL_MS;\n const timeoutSec =\n typeof options.pollTimeoutSec === \"number\" && options.pollTimeoutSec > 0\n ? options.pollTimeoutSec\n : typeof createResp.sessionLifeTimeInSeconds === \"number\" &&\n createResp.sessionLifeTimeInSeconds > 0\n ? createResp.sessionLifeTimeInSeconds\n : DEFAULT_POLL_TIMEOUT_SEC;\n\n const deadline = Date.now() + timeoutSec * 1000;\n\n failureAt = SessionFailureStage.PollTokens;\n for (;;) {\n throwIfOtpAborted(options.signal);\n try {\n return await session.requestTokenWithCode({\n sessionId,\n code,\n scopes,\n signal: options.signal,\n });\n } catch (e: unknown) {\n if (!isAuthSessionNotReadyYet(e)) throw e;\n if (Date.now() >= deadline) {\n throw sessionFailure(e, {operation:\"TorrowAuth.loginWithLink\",failureAt,effects:[],category:TorrowSdkErrorCategory.InvalidState,reason:AuthSessionErrorReason.SessionNotReady});\n }\n await sleepMs(intervalMs, options.signal);\n }\n }\n } catch (cause) {\n const error = options.signal?.aborted && !(cause instanceof TorrowSdkError)\n ? new TorrowSdkError({category:TorrowSdkErrorCategory.Cancelled,sensitive:{cause}}) : cause;\n const callback = failureAt === SessionFailureStage.OnLink;\n throw sessionFailure(error, {operation:\"TorrowAuth.loginWithLink\",failureAt,\n effects:[...effects,...(failureAt === SessionFailureStage.PollTokens && error instanceof TorrowSdkError ? error.effects ?? [] : [])],\n ...(callback ? {category:TorrowSdkErrorCategory.Unknown,reason:AuthSessionErrorReason.CallbackFailed} : {})});\n }\n}\n","/** Sleep with optional AbortSignal; removes abort listener on resolve or abort. */\nexport function sleepMs(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new Error(\"Aborted\"));\n return;\n }\n const onAbort = (): void => {\n clearTimeout(t);\n signal?.removeEventListener(\"abort\", onAbort);\n reject(signal?.reason ?? new Error(\"Aborted\"));\n };\n const t = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n","import { sessionFailure, SessionFailureStage } from \"./session-error.js\";\nimport { otpFailure, otpHttpFailure, throwIfOtpAborted } from \"./otp-errors.js\";\n/**\n * Внутренний оркестратор OTP-логина (не публичный export целиком; типы UI — да).\n *\n * **`sendCode`** → ветка по **`usedMethod`** (Mobile ID poll | classic код) →\n * **`TokenResp`**. UI снаружи: **`onSendCode`** + **`getCode`**.\n * IMAP / stdin / env — в app / `examples/node/_shared`, не здесь.\n *\n * Вызывается из `TorrowAuth.loginWithOtp`; Mobile ID — `mobile-id.ts`.\n */\nimport {\n AuthSessionEffectStep,\n AuthSessionErrorReason,\n TorrowSdkEffectArea,\n TorrowSdkEffectState,\n TorrowSdkErrorCategory,\n TorrowSdkRequestState,\n type TorrowSdkEffect,\n DefaultAuthScopes,\n SendCodeAccountTypeValues,\n TorrowSdkError,\n VerificationMethodValues,\n TokenStatusValues,\n type AuthCaptchaType,\n type AuthScope,\n type DeviceInfo,\n type SendCodeAccountType,\n type SendCodeReqForOtp,\n type SendCodeResp,\n type TokenResp,\n type VerificationMethod,\n} from \"@torrow/sdk-client\";\nimport { assertTokenRespReadyForStorage, normalizeTokenResp } from \"./merge-tokens.js\";\nimport { pollMobileIdUntilReady, type MobileIdAuthClient } from \"./mobile-id.js\";\nimport { requestAuthToken, sendOtpCode, tokenTransition } from \"./auth-protocol.js\";\n\n/** Как в torrow-apps: сервер часто шлёт `codeLength: 0` для Mobile ID. */\nconst MOBILE_ID_SMS_CODE_LENGTH = 4;\n\nexport type OtpAuthClient = MobileIdAuthClient & {\n auth: MobileIdAuthClient[\"auth\"] & {\n sendCode(body: SendCodeReqForOtp, options?: { signal?: AbortSignal }): Promise<SendCodeResp>;\n };\n};\n\n/**\n * Аргумент **`onSendCode`**: снимок **после** успешного `sendCode` (в основном поля **`SendCodeResp`**).\n * Не путать с телом запроса: в `sendCode` уходят `supportedMethods` / `preferredMethod`\n * (см. {@link RunOtpLoginOptions}); здесь — что сервер **выбрал** и что ещё доступно.\n *\n * Типичный UI: по `usedMethod` показать «ждём SMS» / «код на email» / «подтвердите push»;\n * по `codeLength` — маску поля ввода.\n */\nexport type OtpSendCodeResult = {\n /** Аккаунт из `SendCodeReq` (телефон / email). */\n accountName: string;\n /** `Phone` \\| `Email` из запроса (не путать с `usedMethod`). */\n accountType: SendCodeAccountType;\n /** Id auth-сессии для последующего `requestToken` / Mobile ID poll. */\n sessionId: string;\n /**\n * Фактический канал из **`SendCodeResp.usedMethod`** — ради этого и нужен `onSendCode`.\n * Sms / Email / FlashCall / TelegramGateway / MtsMobileId / …\n */\n usedMethod?: VerificationMethod;\n /** Длина кода для input/маски; у Mobile ID часто `0` (keypad не нужен). */\n codeLength?: number;\n /** Интервал poll при Mobile ID (`SendCodeResp.pollIntervalSeconds`). */\n pollIntervalSeconds?: number;\n /**\n * Из **`SendCodeResp`** (не тело `sendCode`): методы, доступные для этого аккаунта\n * (UX «другой способ»). В запрос передаётся {@link RunOtpLoginOptions.supportedMethods}.\n */\n availableMethods?: VerificationMethod[] | null;\n /** TTL auth-сессии `sendCode` (сек); для Mobile ID — ориентир max wait. */\n sessionLifeTimeInSeconds?: number;\n /** Когда снова можно дернуть `sendCode` (rate-limit hint из ответа). */\n nextAvailableUsageTime?: Date | null;\n};\n\n/**\n * Аргумент **`getCode`**: только когда нужны цифры (stdin / UI / IMAP снаружи).\n *\n * UI-экран уже выбран в **`onSendCode`** (`usedMethod`). Здесь — собрать код\n * (длина из `codeLength`). На happy-path Mobile ID (push) **`getCode` не вызывается**;\n * SMS-fallback после poll — тот же колбэк, без отдельного `reason`.\n *\n * `accountName` / `accountType` не дублируются — они в options и в `onSendCode`.\n */\nexport type OtpGetCodeContext = {\n sessionId: string;\n /** Эффективная длина для валидации (Mobile ID SMS fallback: при `0` с сервера → 4). */\n codeLength?: number;\n /** Канал из `sendCode` (опциональная подсказка; экран уже из `onSendCode`). */\n usedMethod?: VerificationMethod;\n};\n\n/**\n * Опции внутренней OTP-оркестрации (`runOtpLogin`).\n * Публичный вход приложения — обычно `TorrowAuth.loginWithOtp` с теми же колбэками.\n */\nexport type RunOtpLoginOptions = {\n /** Телефон или email → `SendCodeReq.accountName`. */\n accountName: string;\n /** Тип аккаунта в `sendCode`: только `Phone` \\| `Email`. */\n type: SendCodeAccountType;\n /** Обязателен: уходит в `TokenReq.deviceInfo` (и Mobile ID poll). */\n deviceInfo: DeviceInfo;\n /**\n * Обязателен: сигнал UI после `sendCode` (`usedMethod`, `codeLength`, …).\n * Вызывается до Mobile ID poll и до classic `getCode`.\n */\n onSendCode: (result: OtpSendCodeResult) => void | Promise<void>;\n /**\n * Вернуть одноразовый код (stdin / UI / IMAP снаружи).\n * Не вызывается, если Mobile ID завершился push'ем без SMS.\n */\n getCode: (ctx: OtpGetCodeContext) => Promise<string>;\n /**\n * Запрашиваемые scopes → `TokenReq.scopes` (`AuthScopeValues`).\n * По умолчанию {@link DefaultAuthScopes} (`user_api` + `offline_access`).\n */\n scopes?: AuthScope[];\n /** Язык `sendCode`. По умолчанию `\"ru\"`. */\n language?: string;\n /** Регион хранилища → `SendCodeReq.regionStorageId`. По умолчанию `70`. */\n regionStorageId?: number;\n /** Токен captcha для `SendCodeReq.captcha` (если шлюз требует). */\n captcha?: string;\n /** Провайдер captcha → `SendCodeReq.captchaType` (`AuthCaptchaTypeValues`). */\n captchaType?: AuthCaptchaType;\n /** Сессия приглашения → `SendCodeReq.inviteSessionId` (вход по invite). */\n inviteSessionId?: string;\n /**\n * Желаемый способ доставки → `SendCodeReq.preferredMethod`.\n * Hint серверу; факт смотри в `onSendCode` → `usedMethod`.\n */\n preferredMethod?: VerificationMethod;\n /**\n * Capability клиента → `SendCodeReq.supportedMethods`.\n * Порядок задаёт приоритет: для телефона рекомендуется `TelegramGateway`, `MtsMobileId`, `Sms`.\n * Без `MtsMobileId` в списке сервер обычно не выберет Mobile ID.\n * `TelegramGateway` — classic OTP (код в TG), не deep-link `createSession`.\n */\n supportedMethods?: VerificationMethod[];\n /** Отмена `sendCode` / poll / `requestToken` (пробрасывается в `fetch`). */\n signal?: AbortSignal;\n};\n\nfunction scopesOrDefault(scopes: AuthScope[] | undefined): AuthScope[] {\n return scopes && scopes.length > 0 ? scopes : [...DefaultAuthScopes];\n}\n\nfunction effectiveCodeLength(raw: number | undefined, fallback: number): number | undefined {\n if (typeof raw === \"number\" && raw > 0) return raw;\n return fallback > 0 ? fallback : undefined;\n}\n\nasync function requestTokenOrThrow(\n client: OtpAuthClient, body: Parameters<typeof requestAuthToken>[1], signal?: AbortSignal,\n): Promise<TokenResp> {\n let raw;\n try { raw = await requestAuthToken(client, body, signal); }\n catch (error) { throw otpHttpFailure(error); }\n try { return normalizeTokenResp(raw); }\n catch (cause) { throw otpFailure(TorrowSdkErrorCategory.InvalidResponse, undefined, cause); }\n}\n\n/**\n * `sendCode` → `onSendCode` → Mobile ID poll **или** classic `getCode` + `requestToken`.\n * Каждый вызов — новый `sendCode`.\n */\nexport async function runOtpLogin(client: OtpAuthClient, options: RunOtpLoginOptions): Promise<TokenResp> {\n return (await runOtpLoginWithEffects(client, options)).tokens;\n}\n/** Локальный результат составного входа, без контекста прочих SDK-вызовов. */\nexport async function runOtpLoginWithEffects(client: OtpAuthClient, options: RunOtpLoginOptions): Promise<{\n /** Готовые токены с прежней legacy-нормализацией. */ tokens: TokenResp;\n /** Только серверные шаги текущего входа. */ effects: readonly TorrowSdkEffect[];\n}> {\n const effects: TorrowSdkEffect[] = [];\n let failureAt = SessionFailureStage.SendCode;\n try {\n throwIfOtpAborted(options.signal);\n const accountName = options.accountName.trim();\n const accountType = options.type;\n const scopes = scopesOrDefault(options.scopes);\n const signal = options.signal;\n\n let sendResp: SendCodeResp;\n try { sendResp = await sendOtpCode(client, options, signal); }\n catch (error) {\n effects.push({area:TorrowSdkEffectArea.Remote,step:AuthSessionEffectStep.SendCode,\n state:error instanceof TorrowSdkError && error.request === TorrowSdkRequestState.NotStarted ? TorrowSdkEffectState.NotApplied : TorrowSdkEffectState.Unknown});\n throw otpHttpFailure(error);\n }\n effects.push({area:TorrowSdkEffectArea.Remote,step:AuthSessionEffectStep.SendCode,state:TorrowSdkEffectState.Unknown});\n failureAt = SessionFailureStage.ValidateResponse;\n const sessionId = sendResp.sessionId?.trim();\n if (!sessionId) {\n throw otpFailure(TorrowSdkErrorCategory.InvalidResponse);\n }\n\n effects[effects.length - 1] = {area:TorrowSdkEffectArea.Remote,step:AuthSessionEffectStep.SendCode,state:TorrowSdkEffectState.Applied};\n const result: OtpSendCodeResult = {\n accountName,\n accountType,\n sessionId,\n usedMethod: sendResp.usedMethod,\n codeLength: sendResp.codeLength,\n pollIntervalSeconds: sendResp.pollIntervalSeconds,\n availableMethods: sendResp.availableMethods,\n sessionLifeTimeInSeconds: sendResp.sessionLifeTimeInSeconds,\n nextAvailableUsageTime: sendResp.nextAvailableUsageTime ?? null,\n };\n failureAt = SessionFailureStage.OnSendCode;\n await options.onSendCode(result);\n\n if (sendResp.usedMethod === VerificationMethodValues.MtsMobileId) {\n try {\n failureAt = SessionFailureStage.PollTokens;\n const tokens = await pollMobileIdUntilReady(client, {\n sessionId,\n deviceInfo: options.deviceInfo,\n scopes,\n pollIntervalSeconds: sendResp.pollIntervalSeconds,\n maxWaitSeconds: sendResp.sessionLifeTimeInSeconds,\n signal,\n smsCodeLength:\n typeof sendResp.codeLength === \"number\" && sendResp.codeLength > 0\n ? sendResp.codeLength\n : MOBILE_ID_SMS_CODE_LENGTH,\n obtainSmsCode: async (ctx) => {\n const codeLength = effectiveCodeLength(ctx.codeLength, MOBILE_ID_SMS_CODE_LENGTH);\n failureAt = SessionFailureStage.GetCode;\n const code = (\n await options.getCode({\n sessionId,\n codeLength,\n usedMethod: sendResp.usedMethod,\n })\n ).trim();\n failureAt = SessionFailureStage.PollTokens;\n return code;\n },\n });\n effects.push({area:TorrowSdkEffectArea.Remote,step:AuthSessionEffectStep.IssueTokens,state:TorrowSdkEffectState.Applied});\n return {tokens,effects};\n } catch (error) { throw otpHttpFailure(error); }\n }\n\n const classicLength = effectiveCodeLength(\n sendResp.codeLength,\n accountType === SendCodeAccountTypeValues.Email ? 6 : 4,\n );\n failureAt = SessionFailureStage.GetCode;\n const code = (\n await options.getCode({\n sessionId,\n codeLength: classicLength,\n usedMethod: sendResp.usedMethod,\n })\n ).trim();\n failureAt = SessionFailureStage.ValidateInput;\n if (!code) {\n throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n }\n\n failureAt = SessionFailureStage.IssueTokens;\n const resp = await requestTokenOrThrow(\n client,\n {\n sessionId,\n code,\n scopes,\n deviceInfo: options.deviceInfo,\n },\n signal,\n );\n // Строгий flow не допускает legacy без status/только с refresh; старый вход сохраняет это поведение.\n if (tokenTransition(resp.status, true) !== TokenStatusValues.TokenReady) {\n throw otpFailure(TorrowSdkErrorCategory.InvalidResponse);\n }\n let ready;\n try { ready = assertTokenRespReadyForStorage(resp, \"runOtpLogin\"); }\n catch (cause) { throw otpFailure(TorrowSdkErrorCategory.InvalidResponse, undefined, cause); }\n effects.push({area:TorrowSdkEffectArea.Remote,step:AuthSessionEffectStep.IssueTokens,state:TorrowSdkEffectState.Applied});\n return {tokens:ready,effects};\n } catch (cause) {\n const error = options.signal?.aborted && !(cause instanceof TorrowSdkError)\n ? new TorrowSdkError({category:TorrowSdkErrorCategory.Cancelled,sensitive:{cause}}) : cause;\n const tokenStages: readonly SessionFailureStage[] = [SessionFailureStage.IssueTokens, SessionFailureStage.PollTokens];\n if (tokenStages.includes(failureAt)) {\n effects.push({area:TorrowSdkEffectArea.Remote,step:AuthSessionEffectStep.IssueTokens,\n state:error instanceof TorrowSdkError && error.request === TorrowSdkRequestState.NotStarted ? TorrowSdkEffectState.NotApplied : TorrowSdkEffectState.Unknown});\n }\n const callback = failureAt === SessionFailureStage.GetCode || failureAt === SessionFailureStage.OnSendCode;\n throw sessionFailure(error, {operation:\"TorrowAuth.loginWithOtp\",failureAt,effects,\n ...(callback && !(options.signal?.aborted) ? {category:TorrowSdkErrorCategory.Unknown,reason:AuthSessionErrorReason.CallbackFailed} : {})});\n }\n}\n","import { otpFailure, throwIfOtpAborted } from \"./otp-errors.js\";\n/**\n * Внутренний poll MTS Mobile ID (не публичный export).\n *\n * Аналог **MobileIdPollTask** + loop из torrow-apps\n * (`AuthPhoneCodeComponent.startMobileIdPolling`).\n * Ветка после `sendCode`, когда `usedMethod === MtsMobileId`.\n *\n * Не путать с **`session.requestTokenWithCode`** (один вызов: sessionId + OTP-код → токены).\n * Вызывается из `otp-login.ts`.\n */\nimport {\n TorrowSdkError,\n TorrowSdkErrorCategory,\n TorrowSdkErrorDomain,\n DefaultAuthScopes,\n TokenStatusValues,\n type AuthScope,\n type DeviceInfo,\n type TokenReq,\n type TokenResp,\n} from \"@torrow/sdk-client\";\nimport { assertTokenRespReadyForStorage, normalizeTokenResp } from \"./merge-tokens.js\";\nimport { sleepMs } from \"./sleep.js\";\nimport { requestAuthToken, tokenTransition } from \"./auth-protocol.js\";\n\nconst DEFAULT_POLL_INTERVAL_SEC = 3;\nconst DEFAULT_MAX_WAIT_SEC = 180;\n\n/** Минимум для poll: **`auth.requestToken`** (обычно `session.client` / identity-only). */\nexport type MobileIdAuthClient = {\n auth: {\n requestToken(body: TokenReq, options?: { signal?: AbortSignal }): Promise<TokenResp>;\n };\n};\n\nexport type MobileIdPollOnceArgs = {\n sessionId: string;\n deviceInfo: DeviceInfo;\n scopes?: AuthScope[];\n /** SMS-fallback; без значения — пустая строка (plain poll), как в torrow-apps. */\n code?: string;\n signal?: AbortSignal;\n};\n\nexport type MobileIdObtainSmsCodeContext = {\n /** Из `SendCodeResp.codeLength`; у Mobile ID часто `0` — UI обычно ждёт 4 цифры. */\n codeLength?: number;\n};\n\nexport type PollMobileIdUntilReadyOptions = {\n sessionId: string;\n deviceInfo: DeviceInfo;\n scopes?: AuthScope[];\n /** `SendCodeResp.pollIntervalSeconds`; иначе **3**. */\n pollIntervalSeconds?: number;\n /** `SendCodeResp.sessionLifeTimeInSeconds`; иначе **180**. */\n maxWaitSeconds?: number;\n signal?: AbortSignal;\n /**\n * Только при **`AwaitingSmsCode`**.\n * После submit, если снова **`Pending`**, poll продолжается **без** кода (как в apps).\n */\n obtainSmsCode?: (ctx: MobileIdObtainSmsCodeContext) => Promise<string>;\n /** Подсказка длины для `obtainSmsCode` (из sendCode). */\n smsCodeLength?: number;\n};\n\nfunction scopesOrDefault(scopes: AuthScope[] | undefined): AuthScope[] {\n return scopes && scopes.length > 0 ? scopes : [...DefaultAuthScopes];\n}\n\n/**\n * Один poll: **`requestToken`** с `code: \"\"` или SMS-кодом.\n * Токены **не** сохраняет — caller решает (как MobileIdPollTask).\n * Internal + unit tests; not re-exported from `@torrow/sdk-auth` barrel.\n */\nexport async function pollMobileIdOnce(\n client: MobileIdAuthClient,\n args: MobileIdPollOnceArgs,\n): Promise<TokenResp> {\n const sessionId = args.sessionId.trim();\n if (!sessionId) {\n throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n }\n const body = {\n sessionId,\n // Empty code = plain poll (server accepts it for Mobile ID); value = SMS-OTP fallback.\n code: args.code ?? \"\",\n scopes: scopesOrDefault(args.scopes),\n deviceInfo: args.deviceInfo,\n };\n const raw = await requestAuthToken(client, body, args.signal);\n try { return normalizeTokenResp(raw); }\n catch (cause) { throw otpFailure(TorrowSdkErrorCategory.InvalidResponse, undefined, cause); }\n}\n\n/**\n * Poll до **`TokenReady`**: `Pending` → sleep → снова; `AwaitingSmsCode` → `obtainSmsCode` →\n * один poll с кодом; если снова `Pending` — продолжить **без** кода.\n */\nexport async function pollMobileIdUntilReady(\n client: MobileIdAuthClient,\n options: PollMobileIdUntilReadyOptions,\n): Promise<TokenResp> {\n const sessionId = options.sessionId.trim();\n if (!sessionId) {\n throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n }\n const intervalSec =\n typeof options.pollIntervalSeconds === \"number\" && options.pollIntervalSeconds > 0\n ? options.pollIntervalSeconds\n : DEFAULT_POLL_INTERVAL_SEC;\n const maxWaitSec =\n typeof options.maxWaitSeconds === \"number\" && options.maxWaitSeconds > 0\n ? options.maxWaitSeconds\n : DEFAULT_MAX_WAIT_SEC;\n const deadline = Date.now() + maxWaitSec * 1000;\n const scopes = scopesOrDefault(options.scopes);\n\n for (;;) {\n throwIfOtpAborted(options.signal);\n\n // Plain poll always uses code \"\" (apps MobileIdPollTask.poll without code).\n const resp = await pollMobileIdOnce(client, {\n sessionId,\n deviceInfo: options.deviceInfo,\n scopes,\n signal: options.signal,\n });\n const status = resp.status;\n\n if (tokenTransition(status, true) === TokenStatusValues.TokenReady) {\n return readyTokens(resp);\n }\n if (status === TokenStatusValues.Failed) {\n throw mobileInvalidState();\n }\n if (status === TokenStatusValues.AwaitingSmsCode) {\n const obtain = options.obtainSmsCode;\n if (!obtain) {\n throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n }\n const code = (\n await obtain({\n codeLength: options.smsCodeLength,\n })\n ).trim();\n if (!code) {\n throw otpFailure(TorrowSdkErrorCategory.InvalidInput);\n }\n const afterSms = await pollMobileIdOnce(client, {\n sessionId,\n deviceInfo: options.deviceInfo,\n scopes,\n code,\n signal: options.signal,\n });\n if (tokenTransition(afterSms.status, true) === TokenStatusValues.TokenReady) {\n return readyTokens(afterSms);\n }\n if (afterSms.status === TokenStatusValues.Failed) {\n throw mobileInvalidState();\n }\n if (afterSms.status === TokenStatusValues.Pending) {\n // Code accepted; resume plain poll without code (apps: startMobileIdPolling again).\n continue;\n }\n if (afterSms.status === TokenStatusValues.AwaitingSmsCode) {\n // Wrong / incomplete — ask again on next loop iteration.\n continue;\n }\n throw otpFailure(TorrowSdkErrorCategory.InvalidResponse);\n }\n if (status !== TokenStatusValues.Pending) {\n throw otpFailure(TorrowSdkErrorCategory.InvalidResponse);\n }\n if (Date.now() >= deadline) {\n throw mobileInvalidState();\n }\n await sleepMs(intervalSec * 1000, options.signal);\n }\n}\n\n/** Проверка прежней готовности ответа; исключение не переносит токены в message. */\nfunction readyTokens(response: TokenResp): TokenResp {\n try { return assertTokenRespReadyForStorage(response, \"pollMobileIdUntilReady\"); }\n catch (cause) { throw otpFailure(TorrowSdkErrorCategory.InvalidResponse, undefined, cause); }\n}\n/** Сервер завершил вход отказом либо исчерпан прежний deadline polling. */\nfunction mobileInvalidState(): TorrowSdkError {\n return new TorrowSdkError({category:TorrowSdkErrorCategory.InvalidState,\n detail:{domain:TorrowSdkErrorDomain.AuthSession}});\n}\n"],"mappings":"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","names":["OtpErrorReason","TorrowSdkErrorCategory","TokenStatusValues","OtpPhaseValues","TorrowSdkErrorCategory","OtpErrorReason","TokenStatusValues","DefaultAuthScopes","TorrowClient","TorrowSdkError","TorrowSdkErrorCategory","TorrowSdkRequestState","AuthSessionErrorReason","TokenStatusValues","TorrowSdkError","TorrowSdkErrorDomain","TorrowSdkError","TorrowSdkErrorCategory","TorrowSdkErrorDomain","SessionFailureStage","TorrowSdkError","TorrowSdkErrorCategory","TorrowSdkRequestState","DefaultAuthScopes","AuthSessionErrorReason","TokenStatusValues","TorrowClient","TorrowClient","TorrowSdkError","TorrowSdkErrorCategory","TorrowSdkEffectArea","TorrowSdkEffectState","AuthSessionEffectStep","TorrowSdkError","TorrowSdkErrorCategory","TorrowSdkRequestState","TorrowSdkEffectArea","TorrowSdkEffectState","AuthSessionEffectStep","AuthSessionErrorReason","DefaultAuthScopes","TorrowSdkEffectArea","AuthSessionEffectStep","TorrowSdkError","TorrowSdkRequestState","TorrowSdkEffectState","TorrowSdkErrorCategory","DefaultAuthScopes","AuthSessionErrorReason","AuthSessionEffectStep","AuthSessionErrorReason","TorrowSdkEffectArea","TorrowSdkEffectState","TorrowSdkErrorCategory","TorrowSdkRequestState","DefaultAuthScopes","TorrowSdkError","VerificationMethodValues","TokenStatusValues","TorrowSdkError","TorrowSdkErrorCategory","TorrowSdkErrorDomain","DefaultAuthScopes","TokenStatusValues","DefaultAuthScopes","TorrowSdkErrorCategory","TokenStatusValues","TorrowSdkError","TorrowSdkErrorDomain","scopesOrDefault","DefaultAuthScopes","TorrowSdkErrorCategory","scopesOrDefault","TorrowSdkEffectArea","AuthSessionEffectStep","TorrowSdkError","TorrowSdkRequestState","TorrowSdkEffectState","TorrowSdkErrorCategory","VerificationMethodValues","codeLength","code","TokenStatusValues","AuthSessionErrorReason","TorrowSdkError","TorrowSdkErrorCategory","TorrowSdkEffectArea","AuthSessionEffectStep","TorrowSdkEffectState","TorrowClient"]}