@torrow/sdk-auth 0.0.0-stage → 0.7.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.
@@ -0,0 +1,543 @@
1
+ import { A as AppCredentials, T as TokenSessionOptions, a as TokenStorage, b as TokenSessionStatus, c as TokenSessionStatusListener, d as AuthTokenSnapshot } from './types-C4e90uOw.js';
2
+ export { R as RefreshFailureAction } from './types-C4e90uOw.js';
3
+ import { SendCodeAccountType, VerificationMethod, TorrowClient, DeviceInfo, TokenResp, AuthScope, AuthCaptchaType, AccountType, TokenStatusValues } from '@torrow/sdk-client';
4
+
5
+ /** Defaults и преобразование credentials приложения в HTTP Basic. */
6
+
7
+ /**
8
+ * Дефолтные app credentials SDK для `/api/v1/auth/*`, если {@link TokenSessionOptions.appCredentials}
9
+ * не передан. Своё приложение может переопределить явной парой.
10
+ *
11
+ * Публикуется в npm `@torrow/sdk-auth` (inspectable); свой prod-secret — только override / BFF.
12
+ */
13
+ declare const DEFAULT_APP_CREDENTIALS: AppCredentials;
14
+
15
+ /**
16
+ * Внутренний оркестратор OTP-логина (не публичный export целиком; типы UI — да).
17
+ *
18
+ * **`sendCode`** → ветка по **`usedMethod`** (Mobile ID poll | classic код) →
19
+ * **`TokenResp`**. UI снаружи: **`onSendCode`** + **`getCode`**.
20
+ * IMAP / stdin / env — в app / `examples/node/_shared`, не здесь.
21
+ *
22
+ * Вызывается из `TorrowAuth.loginWithOtp`; Mobile ID — `mobile-id.ts`.
23
+ */
24
+
25
+ /**
26
+ * Аргумент **`onSendCode`**: снимок **после** успешного `sendCode` (в основном поля **`SendCodeResp`**).
27
+ * Не путать с телом запроса: в `sendCode` уходят `supportedMethods` / `preferredMethod`
28
+ * (см. {@link RunOtpLoginOptions}); здесь — что сервер **выбрал** и что ещё доступно.
29
+ *
30
+ * Типичный UI: по `usedMethod` показать «ждём SMS» / «код на email» / «подтвердите push»;
31
+ * по `codeLength` — маску поля ввода.
32
+ */
33
+ type OtpSendCodeResult = {
34
+ /** Аккаунт из `SendCodeReq` (телефон / email). */
35
+ accountName: string;
36
+ /** `Phone` \| `Email` из запроса (не путать с `usedMethod`). */
37
+ accountType: SendCodeAccountType;
38
+ /** Id auth-сессии для последующего `requestToken` / Mobile ID poll. */
39
+ sessionId: string;
40
+ /**
41
+ * Фактический канал из **`SendCodeResp.usedMethod`** — ради этого и нужен `onSendCode`.
42
+ * Sms / Email / FlashCall / TelegramGateway / MtsMobileId / …
43
+ */
44
+ usedMethod?: VerificationMethod;
45
+ /** Длина кода для input/маски; у Mobile ID часто `0` (keypad не нужен). */
46
+ codeLength?: number;
47
+ /** Интервал poll при Mobile ID (`SendCodeResp.pollIntervalSeconds`). */
48
+ pollIntervalSeconds?: number;
49
+ /**
50
+ * Из **`SendCodeResp`** (не тело `sendCode`): методы, доступные для этого аккаунта
51
+ * (UX «другой способ»). В запрос передаётся {@link RunOtpLoginOptions.supportedMethods}.
52
+ */
53
+ availableMethods?: VerificationMethod[] | null;
54
+ /** TTL auth-сессии `sendCode` (сек); для Mobile ID — ориентир max wait. */
55
+ sessionLifeTimeInSeconds?: number;
56
+ /** Когда снова можно дернуть `sendCode` (rate-limit hint из ответа). */
57
+ nextAvailableUsageTime?: Date | null;
58
+ };
59
+ /**
60
+ * Аргумент **`getCode`**: только когда нужны цифры (stdin / UI / IMAP снаружи).
61
+ *
62
+ * UI-экран уже выбран в **`onSendCode`** (`usedMethod`). Здесь — собрать код
63
+ * (длина из `codeLength`). На happy-path Mobile ID (push) **`getCode` не вызывается**;
64
+ * SMS-fallback после poll — тот же колбэк, без отдельного `reason`.
65
+ *
66
+ * `accountName` / `accountType` не дублируются — они в options и в `onSendCode`.
67
+ */
68
+ type OtpGetCodeContext = {
69
+ sessionId: string;
70
+ /** Эффективная длина для валидации (Mobile ID SMS fallback: при `0` с сервера → 4). */
71
+ codeLength?: number;
72
+ /** Канал из `sendCode` (опциональная подсказка; экран уже из `onSendCode`). */
73
+ usedMethod?: VerificationMethod;
74
+ };
75
+ /** Превышен лимит `sendCode` (`CODE_REQUEST_LIMIT_EXCEEDED`). */
76
+ declare class OtpCodeRequestLimitExceededError extends Error {
77
+ readonly name = "OtpCodeRequestLimitExceededError";
78
+ readonly minutes?: string;
79
+ readonly seconds?: string;
80
+ readonly traceUrl?: string;
81
+ readonly apiMessage?: string;
82
+ readonly body: unknown;
83
+ constructor(body: unknown);
84
+ }
85
+ /** Нет Basic на `/auth/token` (`AUTH_HEADER_MISSING`). */
86
+ declare class OtpAuthHeaderMissingError extends Error {
87
+ readonly name = "OtpAuthHeaderMissingError";
88
+ readonly body: unknown;
89
+ readonly systemMessage?: string;
90
+ constructor(body: unknown);
91
+ }
92
+
93
+ /**
94
+ * Ядро пользовательской сессии: persist + проактивный refresh + single-flight + 401-retry.
95
+ *
96
+ * Low-level API: {@link TokenSession.open}. Канон для приложений —
97
+ * **`TorrowAuth.connect` / `login*` / `fromTokens`** (см. `torrow-auth.ts`).
98
+ *
99
+ * Логин снаружи: `TorrowAuth.loginWithOtp` / `loginWithLink`, либо
100
+ * `requestTokenWithCode` / `applyTokenResp` на уже открытой сессии.
101
+ * Не путать статус `"unauthenticated"` с `TorrowAuth.anonymous` (клиент без сессии).
102
+ */
103
+
104
+ /**
105
+ * Пользовательская сессия (класс). См. file overview выше.
106
+ */
107
+ declare class TokenSession {
108
+ private readonly opts;
109
+ private tokenSnapshot;
110
+ private ensureFreshMutex;
111
+ private _client;
112
+ private readonly statusListeners;
113
+ private closed;
114
+ /** Set in refreshWithFailurePolicy catch; not cleared on success (see public refresh). */
115
+ private lastRefreshError;
116
+ private constructor();
117
+ static open(opts: TokenSessionOptions): Promise<TokenSession>;
118
+ /** Persist, переданный в `TokenSession.open` / `TorrowAuth.*`. */
119
+ get tokenStorage(): TokenStorage;
120
+ get client(): TorrowClient;
121
+ /** Текущий статус сессии. */
122
+ get status(): TokenSessionStatus;
123
+ /** Подписка на смену статуса; возвращает unsubscribe. Сразу вызывает listener с текущим статусом. */
124
+ onStatusChange(listener: TokenSessionStatusListener): () => void;
125
+ /** Текущий снимок токенов сессии (копия {@link AuthTokenSnapshot}). */
126
+ getTokenSnapshot(): AuthTokenSnapshot;
127
+ /** `deviceInfo` из options open или fallback Desktop + deviceId из tokenSnapshot. */
128
+ getDeviceInfo(): DeviceInfo;
129
+ getAccessToken(): Promise<string | undefined>;
130
+ /**
131
+ * Сохранить ответ `requestToken` / `refresh` (только TokenReady).
132
+ * Сериализован с `ensureFresh` / `logout` через mutex. Дефолт merge намеренно
133
+ * (refresh-shaped); логин — `{ replace: true }`. После `logout` — новая сессия.
134
+ */
135
+ applyTokenResp(tokens: TokenResp, opts?: {
136
+ replace?: boolean;
137
+ }): Promise<void>;
138
+ clearTokens(): Promise<void>;
139
+ /**
140
+ * Revoke refresh (если есть) + очистка storage.
141
+ * Единственное публичное имя выхода из сессии.
142
+ */
143
+ logout(): Promise<void>;
144
+ /**
145
+ * Получить токены по **id auth-сессии** (`sessionId` из `sendCode` / `createSession`)
146
+ * и **OTP-коду доступа**, затем сохранить их в storage сессии.
147
+ *
148
+ * Один вызов `POST /api/v1/auth/token` (`requestToken`) с `{ sessionId, code }`.
149
+ * При успехе (`TokenReady`) — persist; если сессия ещё не готова —
150
+ * {@link AuthSessionNotReadyError} (поллинг для deep-link — внутри `TorrowAuth.loginWithLink`).
151
+ *
152
+ * HTTP вне mutex; persist сериализован через {@link applyTokenResp} (не оборачивать
153
+ * весь метод в `runExclusive` — вложенный mutex с публичным `applyTokenResp`).
154
+ */
155
+ requestTokenWithCode(args: {
156
+ sessionId: string;
157
+ code: string;
158
+ scopes?: AuthScope[];
159
+ signal?: AbortSignal;
160
+ }): Promise<TokenResp>;
161
+ /**
162
+ * Явный refresh (single-flight через общий mutex сессии).
163
+ * Сериализован с `logout` / `ensureFresh` / `applyTokenResp`.
164
+ * При гонке с `logout()`: promise может resolve с `TokenResp`, но snapshot уже очищен —
165
+ * не используй return value; смотри `session.status` / `getTokenSnapshot()`.
166
+ */
167
+ refresh(): Promise<TokenResp>;
168
+ ensureFresh(): Promise<void>;
169
+ private assertNotClosedForWrite;
170
+ /** Caller must hold `runExclusive`. Do not call public `applyTokenResp` from here. */
171
+ private applyTokenRespUnlocked;
172
+ private clearTokensUnlocked;
173
+ /** Caller must hold `runExclusive`. `refreshWithFailurePolicy` skips when no refreshToken; guard here is intentional defense-in-depth. */
174
+ private refreshUnlocked;
175
+ /** Caller must hold `runExclusive`. */
176
+ private refreshWithFailurePolicy;
177
+ private runExclusive;
178
+ private ensureFreshImpl;
179
+ private isAccessStillFresh;
180
+ private computeStatus;
181
+ private emitStatusChange;
182
+ private resolveDeviceInfo;
183
+ /** Identity-клиент с Basic приложения, без Bearer пользователя. */
184
+ identityClient(): TorrowClient;
185
+ private buildClient;
186
+ private persist;
187
+ }
188
+
189
+ /**
190
+ * Публичный фасад аутентификации **`TorrowAuth`**.
191
+ *
192
+ * Точка входа приложения: `createOtpFlow` / `connect` / `loginWithOtp` / `loginWithLink` /
193
+ * `fromTokens` / `anonymous`. Возвращает {@link TorrowAuthResult}
194
+ * (`{ client, session }`), кроме `anonymous` (только `TorrowClient`).
195
+ *
196
+ * Оркестрация OTP/Telegram — в `otp-login.ts` / `link-login.ts`;
197
+ * persist и refresh — в {@link TokenSession}.
198
+ */
199
+
200
+ /** Результат `TorrowAuth.connect` / `loginWithOtp` / `loginWithLink` / `fromTokens`: API-клиент + сессия. */
201
+ type TorrowAuthResult = {
202
+ client: TorrowClient;
203
+ session: TokenSession;
204
+ };
205
+ /**
206
+ * Общая база опций `TorrowAuth.connect` / `loginWithOtp` / `loginWithLink` / `fromTokens`.
207
+ *
208
+ * Производная от {@link TokenSessionOptions}: те же HTTP-кнопки + storage + app creds,
209
+ * но `deviceInfo` **обязателен**; `onRefreshFailure` пробрасывается в `TokenSession.open`.
210
+ *
211
+ * **Карта:** `TorrowClientConfig` → (Pick HTTP) → `TokenSessionOptions` → **эта база** →
212
+ * `TorrowAuthConnectOptions` / `TorrowAuthLoginWithOtpOptions` / …
213
+ * Приложение обычно передаёт именно `TorrowAuth*Options`, не `TorrowClientConfig`.
214
+ *
215
+ * `appCredentials` опционален: без него — дефолт SDK (`DEFAULT_APP_CREDENTIALS`).
216
+ */
217
+ type TorrowAuthSessionBase = Omit<TokenSessionOptions, "deviceInfo"> & {
218
+ deviceInfo: DeviceInfo;
219
+ };
220
+ /** Опции {@link TorrowAuth.connect}: restore / refresh из `tokenStorage`. */
221
+ type TorrowAuthConnectOptions = TorrowAuthSessionBase;
222
+ /** Аккаунт для OTP `sendCode` — плоско, как OpenAPI `SendCodeReq`. */
223
+ type TorrowAuthAccount = {
224
+ /** `Phone` \| `Email` → `SendCodeReq.type`. */
225
+ accountType: SendCodeAccountType;
226
+ /** Телефон или email → `SendCodeReq.accountName`. */
227
+ accountName: string;
228
+ };
229
+ /** Опции {@link TorrowAuth.loginWithOtp}: connect-база + OTP-колбэки / поля `sendCode`. */
230
+ type TorrowAuthLoginWithOtpOptions = TorrowAuthSessionBase & {
231
+ account: TorrowAuthAccount;
232
+ /**
233
+ * Сигнал UI после `sendCode` (`usedMethod`, `codeLength`, …).
234
+ * До Mobile ID poll и до classic `getCode`. См. {@link OtpSendCodeResult}.
235
+ */
236
+ onSendCode: (result: OtpSendCodeResult) => void | Promise<void>;
237
+ /**
238
+ * Вернуть одноразовый код. Не зовётся на happy-path Mobile ID (push без SMS).
239
+ * См. {@link OtpGetCodeContext}.
240
+ */
241
+ getCode: (ctx: OtpGetCodeContext) => Promise<string>;
242
+ /** → `TokenReq.scopes`. По умолчанию `DefaultAuthScopes`. */
243
+ scopes?: AuthScope[];
244
+ /** → `SendCodeReq.language`. По умолчанию `"ru"`. */
245
+ language?: string;
246
+ /** → `SendCodeReq.regionStorageId`. По умолчанию `70`. */
247
+ regionStorageId?: number;
248
+ /** → `SendCodeReq.captcha` (если шлюз требует). */
249
+ captcha?: string;
250
+ /** → `SendCodeReq.captchaType`. */
251
+ captchaType?: AuthCaptchaType;
252
+ /** → `SendCodeReq.inviteSessionId`. */
253
+ inviteSessionId?: string;
254
+ /**
255
+ * Hint серверу → `SendCodeReq.preferredMethod`.
256
+ * Факт смотри в `onSendCode.usedMethod`.
257
+ */
258
+ preferredMethod?: VerificationMethod;
259
+ /**
260
+ * Capability клиента → `SendCodeReq.supportedMethods`.
261
+ * Без `MtsMobileId` сервер обычно не выберет Mobile ID.
262
+ */
263
+ supportedMethods?: VerificationMethod[];
264
+ /** Отмена `sendCode` / poll / `requestToken`. */
265
+ signal?: AbortSignal;
266
+ };
267
+ /** Опции {@link TorrowAuth.loginWithLink} (Telegram deep-link). */
268
+ type TorrowAuthLoginWithLinkOptions = TorrowAuthSessionBase & {
269
+ /** Показать deep-link / QR (`CreateAuthSessionResp.authUrl`). */
270
+ onLink: (authUrl: string) => void | Promise<void>;
271
+ /** По умолчанию `Telegram`. */
272
+ accountType?: AccountType;
273
+ language?: string;
274
+ regionStorageId?: number;
275
+ captcha?: string;
276
+ inviteSessionId?: string;
277
+ scopes?: AuthScope[];
278
+ /** Интервал poll `requestToken` (мс). По умолчанию 2000. */
279
+ pollIntervalMs?: number;
280
+ /** Max wait poll (сек). Иначе TTL сессии из ответа или 180. */
281
+ pollTimeoutSec?: number;
282
+ signal?: AbortSignal;
283
+ };
284
+ /** Опции {@link TorrowAuth.fromTokens}: сид токенов от хоста в `tokenStorage` → connect. */
285
+ type TorrowAuthFromTokensOptions = TorrowAuthSessionBase & {
286
+ accessToken: string;
287
+ /** Без него refresh недоступен (даже если задан `appCredentials`). */
288
+ refreshToken?: string;
289
+ /**
290
+ * Абсолютное истечение access (мс). Для opaque/integration keys без JWT `exp`.
291
+ * Приоритет **записи в snapshot**: explicit (`> 0` finite) → JWT `exp` → `undefined` (бессрочный ключ).
292
+ * Runtime freshness AND-ит JWT `exp`: будущий explicit TTL не оживит просроченный JWT.
293
+ * Opaque без JWT-формы: explicit TTL или бессрочно без TTL.
294
+ */
295
+ accessExpiresAtMs?: number;
296
+ };
297
+ /**
298
+ * Фасад аутентификации для приложений и виджетов.
299
+ *
300
+ * - {@link TorrowAuth.connect} — restore / refresh
301
+ * - {@link TorrowAuth.createOtpFlow} — раздельные OTP-шаги без сессии
302
+ * - {@link TorrowAuth.loginWithOtp} — OTP (Phone/Email, Mobile ID / classic)
303
+ * - {@link TorrowAuth.loginWithLink} — Telegram deep-link
304
+ * - {@link TorrowAuth.fromTokens} — сид токенов от хоста
305
+ * - {@link TorrowAuth.anonymous} — клиент без Bearer
306
+ */
307
+ declare const TorrowAuth: {
308
+ /** Пошаговый OTP без storage, polling и refresh. */
309
+ readonly createOtpFlow: typeof createOtpFlow;
310
+ readonly connect: (options: TorrowAuthConnectOptions) => Promise<TorrowAuthResult>;
311
+ readonly loginWithOtp: (options: TorrowAuthLoginWithOtpOptions) => Promise<TorrowAuthResult>;
312
+ readonly loginWithLink: (options: TorrowAuthLoginWithLinkOptions) => Promise<TorrowAuthResult>;
313
+ readonly fromTokens: (options: TorrowAuthFromTokensOptions) => Promise<TorrowAuthResult>;
314
+ readonly anonymous: (config?: Pick<TokenSessionOptions, "baseUrl" | "fetch" | "defaultHeaders" | "userAgent">) => TorrowClient;
315
+ };
316
+
317
+ /**
318
+ * Опции пошагового входа без TokenStorage и пользовательской сессии.
319
+ *
320
+ * HTTP-настройки из {@link TokenSessionOptions} применяются к обоим шагам:
321
+ * - `baseUrl` — адрес шлюза без `/api/v1`; по умолчанию `https://torrow.net`.
322
+ * - `fetch` — реализация HTTP-транспорта; по умолчанию глобальный `fetch`.
323
+ * - `defaultHeaders` — дополнительные заголовки каждого запроса.
324
+ * - `userAgent` — значение заголовка `User-Agent` для диагностики сервера.
325
+ *
326
+ * Между `start` и `complete`, в том числе на разных экземплярах flow,
327
+ * приложение сохраняет одинаковые deviceInfo, scopes и splitTokens.
328
+ */
329
+ type TorrowAuthOtpFlowOptions = Pick<TokenSessionOptions, "baseUrl" | "fetch" | "defaultHeaders" | "userAgent"> & {
330
+ /**
331
+ * Учётные данные приложения для Basic на `/api/v1/auth/*`.
332
+ * Если не заданы — используется {@link DEFAULT_APP_CREDENTIALS};
333
+ * явная пара полностью заменяет default и требует непустых clientId/clientSecret.
334
+ */
335
+ appCredentials?: AppCredentials;
336
+ /**
337
+ * Обязательное описание устройства для `TokenReq.deviceInfo` при `complete`.
338
+ * Требуются непустой deviceId и поддерживаемый deviceType; flow не генерирует deviceId.
339
+ */
340
+ deviceInfo: DeviceInfo;
341
+ /**
342
+ * Запрашиваемые области доступа → `TokenReq.scopes` при `complete`.
343
+ * Если поле отсутствует или массив пуст — `user_api` и `offline_access`.
344
+ */
345
+ scopes?: AuthScope[];
346
+ /**
347
+ * Разделение access/refresh сервером → `TokenReq.splitTokens` при `complete`.
348
+ * false: полные токены в JSON; используйте для Node, MCP и интеграционных ключей.
349
+ * true: части токенов в JSON, остальные части в HttpOnly cookies;
350
+ * только для браузерного приложения с настроенной передачей cookies.
351
+ * SDK не управляет cookies и не восстанавливает полные токены из частей.
352
+ * Явные true/false передаются без изменения; отсутствующее поле не отправляется.
353
+ */
354
+ splitTokens?: boolean;
355
+ };
356
+ /**
357
+ * Вход отправки кода; поля наследуются из {@link TorrowAuthLoginWithOtpOptions}.
358
+ * account задаёт Phone/Email и получателя; language — язык (default ru),
359
+ * regionStorageId — регион (default 70), captcha/captchaType — ответ и провайдер captcha,
360
+ * inviteSessionId — приглашение. preferredMethod — пожелание серверу,
361
+ * supportedMethods — способы, которые приложение умеет обработать.
362
+ * UI и получение captcha остаются в приложении.
363
+ */
364
+ type OtpStartInput = Pick<TorrowAuthLoginWithOtpOptions, "account" | "language" | "regionStorageId" | "captcha" | "captchaType" | "inviteSessionId" | "preferredMethod" | "supportedMethods">;
365
+ /** Опции одного HTTP-шага. */
366
+ type OtpRequestOptions = {
367
+ /** Отмена текущего HTTP-шага; не отменяет независимый cleanup приложения. */
368
+ signal?: AbortSignal;
369
+ };
370
+ /**
371
+ * Действие приложения для продолжения входа, отдельно от серверного TokenStatus.
372
+ * AwaitingCode — первоначальный ввод classic OTP либо новая фаза ввода SMS-кода
373
+ * после fallback из Pending в той же Mobile ID auth-сессии.
374
+ * Pending — ожидание Mobile ID, complete вызывается без кода.
375
+ */
376
+ declare enum OtpPhaseValues {
377
+ /** Требуется первоначальный OTP или SMS-код после Mobile ID fallback. */
378
+ AwaitingCode = "AwaitingCode",
379
+ /** Ожидание Mobile ID; следующий запрос выполняется без кода. */
380
+ Pending = "Pending"
381
+ }
382
+ /** JSON-значение фазы; принимает строковый литерал или член {@link OtpPhaseValues}. */
383
+ type OtpPhase = `${OtpPhaseValues}`;
384
+ /** Переносимое продолжение; приложение обеспечивает его целостность. */
385
+ type OtpChallenge = {
386
+ /** Идентификатор auth-сессии из SendCodeResp; сохраняется между шагами. */
387
+ sessionId: string;
388
+ /** Тип получателя первоначального кода: Phone или Email. */
389
+ accountType: SendCodeAccountType;
390
+ /** Фактический способ подтверждения; при SMS fallback меняется на Sms. */
391
+ usedMethod: VerificationMethod;
392
+ /** Следующее действие приложения: ввод кода или ожидание без кода. */
393
+ phase: OtpPhase;
394
+ /** Локальный deadline в Unix milliseconds, без продления; не гарантирует серверный срок. TTL из SendCodeResp либо 180 секунд от начала start. */
395
+ expiresAtMs: number;
396
+ /** Подсказка длины кода из SendCodeResp; default 6 для classic Email, 4 для Phone/Mobile ID. В переносимом challenge может отсутствовать. */
397
+ codeLength?: number;
398
+ /** Интервал ожидания в секундах для Pending; default 3. SDK сам не выполняет polling. */
399
+ pollIntervalSeconds?: number;
400
+ /** Оставшиеся попытки из SendCodeResp.attemptCountToUseSession; SDK не уменьшает их. Отсутствие означает, что корректная подсказка не получена. */
401
+ attemptsRemaining?: number;
402
+ /** Время следующей отправки из SendCodeResp.nextAvailableUsageTime в Unix milliseconds; отсутствует без корректной подсказки. */
403
+ nextSendAtMs?: number;
404
+ /** Доступные способы из SendCodeResp; отсутствуют, если список отсутствовал или содержал неизвестные значения. */
405
+ availableMethods?: VerificationMethod[];
406
+ };
407
+ /** Параметры одной попытки обмена или ожидания. */
408
+ type OtpCompleteInput = {
409
+ /** Переносимое продолжение; проверяется перед запросом, целостность обеспечивает приложение. */
410
+ challenge: OtpChallenge;
411
+ /** Непустой OTP в AwaitingCode; в Pending поле должно отсутствовать. */
412
+ code?: string;
413
+ };
414
+ /** Данные для явного cleanup вызывающим приложением. */
415
+ type OtpCleanupData = {
416
+ /** Refresh-секрет для явного отзыва приложением; не предназначен для диагностики. */
417
+ refreshToken: string;
418
+ };
419
+ /** Причины локального отказа; HTTP и отмена пробрасываются без преобразования. */
420
+ declare enum OtpFlowErrorCodeValues {
421
+ /** Некорректные опции, вход start или код для текущей фазы. */
422
+ InvalidArguments = "InvalidArguments",
423
+ /** Повреждённое, противоречивое или просроченное продолжение. */
424
+ InvalidChallenge = "InvalidChallenge",
425
+ /** Ответ не соответствует строгому контракту шага. */
426
+ InvalidResponse = "InvalidResponse",
427
+ /** Фактический способ подтверждения отсутствует или не поддерживается для аккаунта. */
428
+ UnsupportedMethod = "UnsupportedMethod"
429
+ }
430
+ /** Строковое представление безопасной причины отказа. */
431
+ type OtpFlowErrorCode = `${OtpFlowErrorCodeValues}`;
432
+ /** Отказ с безопасной диагностикой и отдельно доступным секретом для cleanup. */
433
+ declare class OtpFlowError extends Error {
434
+ #private;
435
+ /** Безопасная причина локального отказа, без исходного ответа и секретов. */
436
+ readonly code: OtpFlowErrorCode;
437
+ readonly name = "OtpFlowError";
438
+ constructor(
439
+ /** Безопасная причина локального отказа, без исходного ответа и секретов. */
440
+ code: OtpFlowErrorCode, cleanup?: OtpCleanupData);
441
+ /** Секрет доступен явно; не является перечисляемым полем исключения. */
442
+ get cleanup(): OtpCleanupData | undefined;
443
+ }
444
+ /**
445
+ * Результат одного шага: серверный статус отдельно от фазы challenge.
446
+ * Pending — сервер ещё не завершил аутентификацию, следующий запрос без кода.
447
+ * AwaitingSmsCode — продолжение Mobile ID-сценария через SMS fallback,
448
+ * если аутентификацию через Mobile ID не удалось завершить. Возвращённый challenge
449
+ * меняет фазу Pending на AwaitingCode и метод на Sms; sessionId и deadline сохраняются.
450
+ * TokenReady — токены выданы; Failed — терминальный отказ без продолжения.
451
+ * Обычный Email/SMS OTP начинается с AwaitingCode после start,
452
+ * без отдельного серверного AwaitingEmailCode.
453
+ */
454
+ type OtpCompleteResult = {
455
+ /** Сервер ещё ожидает подтверждения либо запросил SMS fallback. */
456
+ status: TokenStatusValues.Pending | TokenStatusValues.AwaitingSmsCode;
457
+ /** Продолжение с новым действием приложения и прежним deadline. */
458
+ challenge: OtpChallenge;
459
+ /** Неожиданный refresh для явного отзыва; отсутствует, если сервер его не выдал. */
460
+ cleanup?: OtpCleanupData;
461
+ } | {
462
+ /** Терминальный отказ сервера; продолжение не возвращается. */
463
+ status: TokenStatusValues.Failed;
464
+ /** Неожиданный refresh для явного отзыва; отсутствует без refresh в ответе. */
465
+ cleanup?: OtpCleanupData;
466
+ } | {
467
+ /** Сервер явно подтвердил выдачу токенов. */
468
+ status: TokenStatusValues.TokenReady;
469
+ /**
470
+ * Проверенные поля TokenResp: accessToken обязателен, refreshToken и expiration
471
+ * (TTL в секундах) сохраняются при наличии; splitTokens определяет полноту токенов.
472
+ */
473
+ tokens: Omit<TokenResp, "status" | "accessToken"> & {
474
+ /** Явный статус готовности из token-ответа. */
475
+ status: TokenStatusValues.TokenReady;
476
+ /** Непустой access token из ответа после нормализации aliases. */
477
+ accessToken: string;
478
+ };
479
+ };
480
+ /** Пошаговый интерфейс: каждый метод выполняет только один upstream-запрос. */
481
+ interface OtpFlow {
482
+ /** Отправляет один код и возвращает переносимое продолжение без токенов. */
483
+ start(input: OtpStartInput, options?: OtpRequestOptions): Promise<OtpChallenge>;
484
+ /** Проверяет продолжение и выполняет одну попытку обмена/ожидания без сохранения. */
485
+ complete(input: OtpCompleteInput, options?: OtpRequestOptions): Promise<OtpCompleteResult>;
486
+ }
487
+ /**
488
+ * Создаёт OTP-flow с фиксированными настройками транспорта и token-обмена.
489
+ * Создание не выполняет HTTP; start и complete выполняют по одному запросу.
490
+ * Challenge и токены остаются у приложения, без storage, polling и refresh внутри flow.
491
+ * @param options Настройки HTTP, credentials приложения, устройства и выдачи токенов.
492
+ * @returns Независимые шаги отправки кода и завершения/ожидания аутентификации.
493
+ * @throws OtpFlowError При некорректных опциях устройства, scopes или splitTokens.
494
+ */
495
+ declare function createOtpFlow(options: TorrowAuthOtpFlowOptions): OtpFlow;
496
+
497
+ /**
498
+ * In-memory реализация {@link TokenStorage}.
499
+ *
500
+ * Тесты, эфемерные процессы, `TorrowAuth.fromTokens` без диска.
501
+ * Для Node-примеров обычно {@link FileTokenStorage}; для браузера —
502
+ * {@link LocalStorageTokenStorage}.
503
+ */
504
+
505
+ /** In-memory хранилище токенов сессии. */
506
+ declare class MemoryTokenStorage implements TokenStorage {
507
+ private snapshot;
508
+ constructor(initial?: AuthTokenSnapshot);
509
+ load(): Promise<AuthTokenSnapshot>;
510
+ save(snapshot: AuthTokenSnapshot): Promise<void>;
511
+ }
512
+
513
+ /**
514
+ * Persist snapshot в браузерный **`localStorage`** — реализация {@link TokenStorage}.
515
+ *
516
+ * Для виджетов / SPA (`TorrowAuth.fromTokens` / `connect`). В Node без
517
+ * `globalThis.localStorage` конструктор бросит. Не путать с {@link FileTokenStorage}.
518
+ */
519
+
520
+ type LocalStorageLike = {
521
+ getItem(key: string): string | null;
522
+ setItem(key: string, value: string): void;
523
+ removeItem?(key: string): void;
524
+ };
525
+ /**
526
+ * Persist snapshot токенов в браузерный `localStorage` (виджет / SPA).
527
+ */
528
+ declare class LocalStorageTokenStorage implements TokenStorage {
529
+ readonly key: string;
530
+ private readonly store;
531
+ constructor(key: string, store?: LocalStorageLike);
532
+ load(): Promise<AuthTokenSnapshot>;
533
+ save(snapshot: AuthTokenSnapshot): Promise<void>;
534
+ }
535
+
536
+ /** Сессия auth ещё не подтверждена (например Telegram до Start у бота). */
537
+ declare class AuthSessionNotReadyError extends Error {
538
+ readonly name = "AuthSessionNotReadyError";
539
+ readonly cause?: unknown;
540
+ constructor(message?: string, cause?: unknown);
541
+ }
542
+
543
+ export { AppCredentials, AuthSessionNotReadyError, AuthTokenSnapshot, DEFAULT_APP_CREDENTIALS, LocalStorageTokenStorage, MemoryTokenStorage, OtpAuthHeaderMissingError, type OtpChallenge, type OtpCleanupData, OtpCodeRequestLimitExceededError, type OtpCompleteInput, type OtpCompleteResult, type OtpFlow, OtpFlowError, type OtpFlowErrorCode, OtpFlowErrorCodeValues, type OtpGetCodeContext, type OtpPhase, OtpPhaseValues, type OtpRequestOptions, type OtpSendCodeResult, type OtpStartInput, TokenSession, TokenSessionOptions, TokenSessionStatus, TokenSessionStatusListener, TokenStorage, TorrowAuth, type TorrowAuthAccount, type TorrowAuthConnectOptions, type TorrowAuthFromTokensOptions, type TorrowAuthLoginWithLinkOptions, type TorrowAuthLoginWithOtpOptions, type TorrowAuthOtpFlowOptions, type TorrowAuthResult, type TorrowAuthSessionBase };