itd-api 0.9.1 → 0.9.3
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/events/index.cjs +3 -4
- package/dist/events/index.d.cts +3 -3
- package/dist/events/index.d.ts +3 -3
- package/dist/events/index.js +4 -4
- package/dist/index.cjs +283 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -72
- package/dist/index.d.ts +16 -72
- package/dist/index.js +284 -90
- package/dist/index.js.map +1 -1
- package/dist/node/index.cjs +3 -3
- package/dist/node/index.js +3 -3
- package/dist/rest/index.cjs +4 -5
- package/dist/rest/index.d.cts +3 -3
- package/dist/rest/index.d.ts +3 -3
- package/dist/rest/index.js +5 -5
- package/dist/shared/{events-BDdJyH2p.d.ts → events-BPxBF0gQ.d.cts} +3 -5
- package/dist/shared/{events-D6y0Hxyz.js → events-BzjI6pn5.js} +4 -4
- package/dist/shared/{events-D6y0Hxyz.js.map → events-BzjI6pn5.js.map} +1 -1
- package/dist/shared/{events-BqFkwwTw.cjs → events-C1FudmSe.cjs} +3 -9
- package/dist/shared/{events-BqFkwwTw.cjs.map → events-C1FudmSe.cjs.map} +1 -1
- package/dist/shared/{events-BQyH5-S0.d.cts → events-DnwfcLxS.d.ts} +3 -5
- package/dist/shared/{multi-storage-SoW05uzO.js → multi-storage-C3Ngtop1.js} +13 -8
- package/dist/shared/multi-storage-C3Ngtop1.js.map +1 -0
- package/dist/shared/{multi-storage-CDKLa956.cjs → multi-storage-s-fPtjoy.cjs} +13 -8
- package/dist/shared/multi-storage-s-fPtjoy.cjs.map +1 -0
- package/dist/shared/{options-DOJYtoti.cjs → options-DjEtbVaf.cjs} +2 -2
- package/dist/shared/{options-DOJYtoti.cjs.map → options-DjEtbVaf.cjs.map} +1 -1
- package/dist/shared/{options-DhgGg7Ms.js → options-i4LrKfqx.js} +2 -2
- package/dist/shared/{options-DhgGg7Ms.js.map → options-i4LrKfqx.js.map} +1 -1
- package/dist/shared/{redact-Ba5iSVYZ.js → redact-DJV0XM3n.js} +40 -15
- package/dist/shared/redact-DJV0XM3n.js.map +1 -0
- package/dist/shared/{redact-BP00URmQ.cjs → redact-lAmck1Z6.cjs} +45 -14
- package/dist/shared/redact-lAmck1Z6.cjs.map +1 -0
- package/dist/shared/{render-BU1_wex8.d.cts → render-C1F_QWYD.d.cts} +82 -19
- package/dist/shared/{render-sb1VJhUA.js → render-CtOpp9Pm.js} +127 -124
- package/dist/shared/render-CtOpp9Pm.js.map +1 -0
- package/dist/shared/{render-DAEeCMk9.cjs → render-Omy1drH1.cjs} +125 -128
- package/dist/shared/render-Omy1drH1.cjs.map +1 -0
- package/dist/shared/{render-CZ_-F2Tl.d.ts → render-uFW2kDxp.d.ts} +82 -19
- package/dist/shared/{storage-QNVxzpDB.cjs → storage-CZJZoaz9.cjs} +4 -2
- package/dist/shared/storage-CZJZoaz9.cjs.map +1 -0
- package/dist/shared/{storage-CfHKzVHf.js → storage-DiDe0PZM.js} +4 -2
- package/dist/shared/storage-DiDe0PZM.js.map +1 -0
- package/dist/shared/{url-Dsr4c5r5.js → url-3Ts6EEHQ.js} +301 -96
- package/dist/shared/url-3Ts6EEHQ.js.map +1 -0
- package/dist/shared/{url-CzHqE-Fg.cjs → url-D6wsXm1T.cjs} +301 -96
- package/dist/shared/url-D6wsXm1T.cjs.map +1 -0
- package/dist/shared/{url-CZZcyPI0.d.cts → url-DubjkRR9.d.cts} +10 -16
- package/dist/shared/{url-CZZcyPI0.d.ts → url-DubjkRR9.d.ts} +10 -16
- package/dist/web/index.cjs +1 -1
- package/dist/web/index.js +1 -1
- package/package.json +5 -5
- package/dist/shared/multi-storage-CDKLa956.cjs.map +0 -1
- package/dist/shared/multi-storage-SoW05uzO.js.map +0 -1
- package/dist/shared/redact-BP00URmQ.cjs.map +0 -1
- package/dist/shared/redact-Ba5iSVYZ.js.map +0 -1
- package/dist/shared/render-DAEeCMk9.cjs.map +0 -1
- package/dist/shared/render-sb1VJhUA.js.map +0 -1
- package/dist/shared/storage-CfHKzVHf.js.map +0 -1
- package/dist/shared/storage-QNVxzpDB.cjs.map +0 -1
- package/dist/shared/url-CzHqE-Fg.cjs.map +0 -1
- package/dist/shared/url-Dsr4c5r5.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { $ as orderPluginDefinitions, A as ITD_CATALOG, C as ReportReason, D as ViewReason, E as SpanType, F as operationMethod, G as pickString, I as operationRetrySafety, K as RetrySafety, L as BUCKET_LIMITS, M as defineBuiltInOperation, N as isBuiltInOperationId, O as ViewSource, P as operationBucket, Q as assertPluginRemovable, R as DEFAULT_RATE_LIMIT_BUCKET, S as NotificationType, T as ServiceState, V as pickArray, Z as RequestQueuePool, _ as FeedTab, at as isRecord, b as ItdErrorCode, c as normalizeNotification, d as canonicalNotificationType, et as DEFAULT_BASE_URL, f as isKnownNotificationType, g as EventChannelStatus, h as CommentSort, it as LIBRARY_VERSION, j as OPERATIONS, k as WallAccess, lt as systemClock, m as AttachmentType, n as formatNotificationText, nt as STATUS_SERVICE, o as passthroughOperation, ot as requireOptionalBoolean, p as AccessType, rt as resolveRateLimit, s as voidOperation, st as RateLimitPacing, t as resolveNotificationUrl, ut as installAsyncDisposeFallback, v as IncidentKind, w as ReportTargetType, x as LikesVisibility, y as InteractionType, z as createClientRuntime } from "./shared/url-
|
|
1
|
+
import { $ as orderPluginDefinitions, A as ITD_CATALOG, C as ReportReason, D as ViewReason, E as SpanType, F as operationMethod, G as pickString, I as operationRetrySafety, K as RetrySafety, L as BUCKET_LIMITS, M as defineBuiltInOperation, N as isBuiltInOperationId, O as ViewSource, P as operationBucket, Q as assertPluginRemovable, R as DEFAULT_RATE_LIMIT_BUCKET, S as NotificationType, T as ServiceState, V as pickArray, Z as RequestQueuePool, _ as FeedTab, at as isRecord, b as ItdErrorCode, c as normalizeNotification, d as canonicalNotificationType, et as DEFAULT_BASE_URL, f as isKnownNotificationType, g as EventChannelStatus, h as CommentSort, it as LIBRARY_VERSION, j as OPERATIONS, k as WallAccess, lt as systemClock, m as AttachmentType, n as formatNotificationText, nt as STATUS_SERVICE, o as passthroughOperation, ot as requireOptionalBoolean, p as AccessType, rt as resolveRateLimit, s as voidOperation, st as RateLimitPacing, t as resolveNotificationUrl, ut as installAsyncDisposeFallback, v as IncidentKind, w as ReportTargetType, x as LikesVisibility, y as InteractionType, z as createClientRuntime } from "./shared/url-3Ts6EEHQ.js";
|
|
2
2
|
import { A as isItdStateError, C as isItdError, D as isItdPhoneVerificationError, E as isItdNotFoundError, O as isItdRateLimitError, S as isItdConflictError, T as isItdForbiddenError, _ as ItdStateError, a as ItdConfigError, b as isItdApiError, c as ItdErrorKind, d as ItdForbiddenError, f as ItdNetworkError, g as ItdServerError, h as ItdRateLimitError, i as ItdAuthError, j as isItdValidationError, k as isItdServerError, l as ItdFileError, m as ItdPhoneVerificationError, n as ItdApiError, o as ItdConflictError, p as ItdNotFoundError, r as ItdApiErrorKind, s as ItdError, t as ItdAbortError, u as ItdFileErrorReason, v as ItdTimeoutError, w as isItdFileError, x as isItdAuthError, y as ItdValidationError } from "./shared/errors-GI10kZxk.js";
|
|
3
|
-
import {
|
|
4
|
-
import { A as
|
|
5
|
-
import { c as FileTransferMode } from "./shared/options-
|
|
6
|
-
import { _ as
|
|
3
|
+
import { c as REFRESH_COOKIE, l as REFRESH_COOKIE_PATH, s as CookieJar, u as isSerializedCookieEntry, v as RuntimeMode, y as createDeviceId } from "./shared/redact-DJV0XM3n.js";
|
|
4
|
+
import { A as utcStampToIso, C as AUDIO_MIME_TYPES, D as isBuilder, E as comment, F as mapPage, I as createFeatureHost, L as ExtensibleOperationCatalog, M as BaseResource, N as PaginationMode, O as createStatusFeature, P as Paginator, S as ALLOWED_MIME_TYPES, T as VIDEO_MIME_TYPES, _ as poll, a as createShopOrderAccessStorage, b as parseHtml, c as ShopOrderStatus, d as isMyProfile, f as fromStream, g as post, h as report, i as MemoryShopOrderAccessStorage, j as NotificationsResource, k as toDate, l as ShopProductCategory, m as createResources, n as renderSpans, o as statusDays, p as fromUrl, r as createShopFeature, s as ShopConsentKind, t as SpanRenderFormat, u as ShopProductStatus, v as autoSpans, w as IMAGE_MIME_TYPES, x as parseMarkdown, y as markup } from "./shared/render-CtOpp9Pm.js";
|
|
5
|
+
import { c as FileTransferMode } from "./shared/options-i4LrKfqx.js";
|
|
6
|
+
import { _ as reportListenerError, a as WebSocketTransport, c as NotificationEventsTransport, d as NotificationUpdateOrigin, f as NotificationUpdateType, g as Emitter, h as runEventMiddleware, i as createNotificationEventsClient, l as resolveNotificationEventsOptions, m as UnauthorizedStreamError, n as EventRouter, o as EVENT_OPERATIONS, p as EventChannel, r as NotificationEventsClient, s as NotificationEvents, t as EventComposer, u as setNotificationEventsConnectGuard } from "./shared/events-BzjI6pn5.js";
|
|
7
7
|
import { a as isEnumerableKeyValueStore, i as createRecordKeyValueStore, o as withCodec, r as createKeyValueStore, s as withNamespace, t as MemoryKeyValueStore } from "./shared/key-value-store-Bt2PSMxY.js";
|
|
8
|
-
import { n as copySession, r as createTokenStorage, t as MemoryTokenStorage } from "./shared/storage-
|
|
9
|
-
import { a as scopedTokenStorage, i as isRestorableSession, n as controlledTokenStorage, r as createMultiTokenStorage, t as MemoryMultiTokenStorage } from "./shared/multi-storage-
|
|
8
|
+
import { n as copySession, r as createTokenStorage, t as MemoryTokenStorage } from "./shared/storage-DiDe0PZM.js";
|
|
9
|
+
import { a as scopedTokenStorage, i as isRestorableSession, n as controlledTokenStorage, r as createMultiTokenStorage, t as MemoryMultiTokenStorage } from "./shared/multi-storage-C3Ngtop1.js";
|
|
10
10
|
//#region src/notifications-api.ts
|
|
11
11
|
var ClientNotifications = class extends NotificationsResource {
|
|
12
12
|
#createEvents;
|
|
@@ -49,7 +49,7 @@ const AUTH_SIGN_IN = defineBuiltInOperation("auth.signIn", (body) => {
|
|
|
49
49
|
};
|
|
50
50
|
});
|
|
51
51
|
const AUTH_VERIFY_OTP = defineBuiltInOperation("auth.verifyOtp", (body) => requireString(body, "accessToken", "Сервер не вернул accessToken после подтверждения кода"));
|
|
52
|
-
const AUTH_REFRESH = defineBuiltInOperation("auth.refresh", (body) =>
|
|
52
|
+
const AUTH_REFRESH = defineBuiltInOperation("auth.refresh", (body) => requireString(body, "accessToken", "Сервер не вернул accessToken при обновлении сессии"));
|
|
53
53
|
const AUTH_RESEND_OTP = voidOperation("auth.resendOtp");
|
|
54
54
|
const AUTH_LOGOUT = voidOperation("auth.logout");
|
|
55
55
|
const AUTH_FORGOT_PASSWORD = defineBuiltInOperation("auth.forgotPassword", (body) => requireString(body, "flowToken", "Сервер не вернул flowToken при запросе сброса пароля"));
|
|
@@ -74,38 +74,34 @@ function decodeBase64Url(segment) {
|
|
|
74
74
|
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
75
75
|
return new TextDecoder().decode(bytes);
|
|
76
76
|
}
|
|
77
|
-
/**
|
|
78
|
-
|
|
79
|
-
*
|
|
80
|
-
* Подпись не проверяется — ключа для проверки у клиента нет. Прочитанные значения служат
|
|
81
|
-
* лишь метками для разделения локального состояния и не дают доступа: его определяет сервер.
|
|
82
|
-
*/
|
|
83
|
-
function readTokenIdentity(token) {
|
|
77
|
+
/** Читает полезную нагрузку JWT без проверки подписи. */
|
|
78
|
+
function readTokenPayload(token) {
|
|
84
79
|
try {
|
|
85
80
|
const payload = token.split(".")[1];
|
|
86
|
-
if (!payload) return
|
|
81
|
+
if (!payload) return void 0;
|
|
87
82
|
const parsed = JSON.parse(decodeBase64Url(payload));
|
|
88
|
-
if (typeof parsed !== "object" || parsed === null) return
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
...typeof sub === "string" && sub.length > 0 ? { subject: sub } : {},
|
|
92
|
-
...typeof sid === "string" && sid.length > 0 ? { sessionId: sid } : {}
|
|
93
|
-
};
|
|
83
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
84
|
+
return parsed;
|
|
94
85
|
} catch {
|
|
95
|
-
return
|
|
86
|
+
return;
|
|
96
87
|
}
|
|
97
88
|
}
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
89
|
+
/** Читает используемые клиентом поля JWT за один раз. */
|
|
90
|
+
function readTokenMetadata(token) {
|
|
91
|
+
const payload = readTokenPayload(token);
|
|
92
|
+
if (!payload) return {};
|
|
93
|
+
const { sub, sid, exp } = payload;
|
|
94
|
+
const expiresAt = typeof exp === "number" && Number.isFinite(exp) ? exp * 1e3 : void 0;
|
|
95
|
+
return {
|
|
96
|
+
...typeof sub === "string" && sub.length > 0 ? { subject: sub } : {},
|
|
97
|
+
...typeof sid === "string" && sid.length > 0 ? { sessionId: sid } : {},
|
|
98
|
+
...expiresAt !== void 0 && Number.isFinite(expiresAt) ? { expiresAt } : {}
|
|
99
|
+
};
|
|
106
100
|
}
|
|
107
101
|
//#endregion
|
|
108
102
|
//#region src/session/auth.ts
|
|
103
|
+
/** Запас до истечения токена на очередь и выполнение запроса. */
|
|
104
|
+
const ACCESS_TOKEN_REFRESH_LEEWAY_MS = 3e4;
|
|
109
105
|
/**
|
|
110
106
|
* Проверяет форму `auth` и сообщает о типичных ошибках понятным текстом.
|
|
111
107
|
*
|
|
@@ -119,12 +115,18 @@ function validateAuth(auth) {
|
|
|
119
115
|
return auth;
|
|
120
116
|
}
|
|
121
117
|
if (typeof auth !== "object" || auth === null) throw new ItdConfigError(`auth должен быть строкой с токеном или объектом, получено: ${typeof auth}`);
|
|
118
|
+
if ([
|
|
119
|
+
"getToken" in auth,
|
|
120
|
+
"accessToken" in auth || "refreshToken" in auth,
|
|
121
|
+
"email" in auth || "password" in auth
|
|
122
|
+
].filter(Boolean).length > 1) throw new ItdConfigError("auth должен описывать ровно один способ авторизации: getToken, accessToken или email/password");
|
|
122
123
|
if ("getToken" in auth) {
|
|
123
124
|
if (typeof auth.getToken !== "function") throw new ItdConfigError("auth.getToken должен быть функцией");
|
|
124
125
|
return { ...auth };
|
|
125
126
|
}
|
|
126
127
|
if ("accessToken" in auth) {
|
|
127
128
|
if (typeof auth.accessToken !== "string" || auth.accessToken.trim() === "") throw new ItdConfigError("auth.accessToken должен быть непустой строкой");
|
|
129
|
+
if (auth.refreshToken !== void 0 && (typeof auth.refreshToken !== "string" || auth.refreshToken.trim() === "")) throw new ItdConfigError("auth.refreshToken должен быть непустой строкой");
|
|
128
130
|
return { ...auth };
|
|
129
131
|
}
|
|
130
132
|
if ("email" in auth || "password" in auth) {
|
|
@@ -212,10 +214,67 @@ const AUTH_PATHS = {
|
|
|
212
214
|
const TURNSTILE_SITE_KEY = "0x4AAAAAACHhxczw6fJGwPBg";
|
|
213
215
|
/** Убирает поле, которое сохраняли промежуточные версии с поддержкой `getUserId()`. */
|
|
214
216
|
function withoutLegacyUserId(session) {
|
|
215
|
-
const clean =
|
|
217
|
+
const clean = copySession(session);
|
|
216
218
|
delete clean.userId;
|
|
217
219
|
return clean;
|
|
218
220
|
}
|
|
221
|
+
function normalizeSessionValue(value, source, mode = "strict", logger) {
|
|
222
|
+
const tolerant = mode === "stored";
|
|
223
|
+
if (!isRecord(value)) {
|
|
224
|
+
if (tolerant) {
|
|
225
|
+
logger?.warn(`${source} вернула не объект сессии; сохранённая запись пропущена`);
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
throw new ItdConfigError(`${source} должна быть объектом сессии`);
|
|
229
|
+
}
|
|
230
|
+
const record = value;
|
|
231
|
+
const normalized = { ...record };
|
|
232
|
+
delete normalized.userId;
|
|
233
|
+
const normalizeString = (field) => {
|
|
234
|
+
const item = record[field];
|
|
235
|
+
if (item === void 0) {
|
|
236
|
+
delete normalized[field];
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (item !== void 0 && (typeof item !== "string" || item.trim() === "")) {
|
|
240
|
+
if (tolerant) {
|
|
241
|
+
logger?.warn(`${source}.${field} повреждена; поле сохранённой сессии пропущено`);
|
|
242
|
+
delete normalized[field];
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
throw new ItdConfigError(`${source}.${field} должна быть непустой строкой`);
|
|
246
|
+
}
|
|
247
|
+
normalized[field] = item;
|
|
248
|
+
};
|
|
249
|
+
normalizeString("accessToken");
|
|
250
|
+
normalizeString("refreshToken");
|
|
251
|
+
normalizeString("deviceId");
|
|
252
|
+
if (record.obtainedAt === void 0) delete normalized.obtainedAt;
|
|
253
|
+
else if (typeof record.obtainedAt !== "number" || !Number.isFinite(record.obtainedAt)) {
|
|
254
|
+
if (!tolerant) throw new ItdConfigError(`${source}.obtainedAt должна быть конечным числом`);
|
|
255
|
+
else {
|
|
256
|
+
logger?.warn(`${source}.obtainedAt повреждена; поле сохранённой сессии пропущено`);
|
|
257
|
+
delete normalized.obtainedAt;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (record.cookies === void 0) delete normalized.cookies;
|
|
261
|
+
else if (!Array.isArray(record.cookies)) {
|
|
262
|
+
if (tolerant) {
|
|
263
|
+
logger?.warn(`${source}.cookies повреждена; поле сохранённой сессии пропущено`);
|
|
264
|
+
delete normalized.cookies;
|
|
265
|
+
} else throw new ItdConfigError(`${source}.cookies должна быть массивом`);
|
|
266
|
+
} else {
|
|
267
|
+
const valid = record.cookies.filter((cookie) => typeof cookie === "string" && isSerializedCookieEntry(cookie));
|
|
268
|
+
if (valid.length > 0) normalized.cookies = valid;
|
|
269
|
+
else delete normalized.cookies;
|
|
270
|
+
}
|
|
271
|
+
return normalized;
|
|
272
|
+
}
|
|
273
|
+
function normalizeSession(value, source) {
|
|
274
|
+
const normalized = normalizeSessionValue(value, source);
|
|
275
|
+
if (!normalized) throw new ItdConfigError(`${source} должна быть объектом сессии`);
|
|
276
|
+
return normalized;
|
|
277
|
+
}
|
|
219
278
|
let authScopeSequence = 0;
|
|
220
279
|
function nextAuthScope() {
|
|
221
280
|
authScopeSequence += 1;
|
|
@@ -242,7 +301,7 @@ var AuthManager = class {
|
|
|
242
301
|
* старте читают хранилище один раз и не заводят каждый свой `deviceId`.
|
|
243
302
|
*/
|
|
244
303
|
#loading = null;
|
|
245
|
-
/**
|
|
304
|
+
/** Текущее обновление: к нему присоединяются запросы той же ревизии. */
|
|
246
305
|
#refreshing = null;
|
|
247
306
|
/** Общий промис входа по логину и паролю. */
|
|
248
307
|
#signingIn = null;
|
|
@@ -252,6 +311,7 @@ var AuthManager = class {
|
|
|
252
311
|
#externalToken;
|
|
253
312
|
/** Идентификаторы последнего токена внешнего источника для синхронных потребителей. */
|
|
254
313
|
#externalIdentity;
|
|
314
|
+
#tokenMetadata;
|
|
255
315
|
/**
|
|
256
316
|
* Идентификатор устройства.
|
|
257
317
|
*
|
|
@@ -265,11 +325,12 @@ var AuthManager = class {
|
|
|
265
325
|
* Счётчик смен владельца авторизации.
|
|
266
326
|
*
|
|
267
327
|
* Растёт при каждой операции, которая заменяет или очищает сессию извне: `clear`,
|
|
268
|
-
* `setSession`, `setAccessToken`, вход.
|
|
269
|
-
* сетевым запросом и сверяет перед записью — иначе запоздавший ответ обновления мог бы
|
|
270
|
-
* воскресить уже очищенную сессию поверх `signOut`.
|
|
328
|
+
* `setSession`, `setAccessToken`, вход. Refresh сверяет снимок до и после сетевого запроса.
|
|
271
329
|
*/
|
|
272
330
|
#authEpoch = 0;
|
|
331
|
+
#externalReadGeneration = 0;
|
|
332
|
+
#disposed = false;
|
|
333
|
+
#persistence = Promise.resolve();
|
|
273
334
|
constructor(config, http, jar, hooks = {}) {
|
|
274
335
|
this.#config = config;
|
|
275
336
|
this.#http = http;
|
|
@@ -279,15 +340,25 @@ var AuthManager = class {
|
|
|
279
340
|
}
|
|
280
341
|
/** Подписка на события авторизации. */
|
|
281
342
|
get on() {
|
|
282
|
-
return
|
|
343
|
+
return ((...args) => {
|
|
344
|
+
this.#assertActive();
|
|
345
|
+
return this.#emitter.on(...args);
|
|
346
|
+
});
|
|
283
347
|
}
|
|
284
348
|
/** Подписка на одно срабатывание. */
|
|
285
349
|
get once() {
|
|
286
|
-
return
|
|
350
|
+
return ((...args) => {
|
|
351
|
+
this.#assertActive();
|
|
352
|
+
return this.#emitter.once(...args);
|
|
353
|
+
});
|
|
287
354
|
}
|
|
288
355
|
/** Снимает служебные подписки при окончательном освобождении владельца. @internal */
|
|
289
356
|
dispose() {
|
|
357
|
+
if (this.#disposed) return;
|
|
358
|
+
this.#disposed = true;
|
|
359
|
+
this.#externalReadGeneration += 1;
|
|
290
360
|
this.#invalidateInFlight();
|
|
361
|
+
this.#tokenMetadata = void 0;
|
|
291
362
|
this.#emitter.removeAllListeners();
|
|
292
363
|
}
|
|
293
364
|
/**
|
|
@@ -301,6 +372,7 @@ var AuthManager = class {
|
|
|
301
372
|
}
|
|
302
373
|
/** Загружает сессию и возвращает идентификаторы аккаунта и сессии для плагинов. */
|
|
303
374
|
async getAuthIdentity() {
|
|
375
|
+
this.#assertActive();
|
|
304
376
|
const session = await this.#loadSession();
|
|
305
377
|
if (session?.accessToken) return this.#identityForToken(session.accessToken);
|
|
306
378
|
const auth = this.#config.auth;
|
|
@@ -321,16 +393,57 @@ var AuthManager = class {
|
|
|
321
393
|
/** Отмечает смену владельца авторизации — обесценивает результат идущего обновления. */
|
|
322
394
|
#invalidateInFlight() {
|
|
323
395
|
this.#authEpoch += 1;
|
|
396
|
+
this.#externalReadGeneration += 1;
|
|
397
|
+
}
|
|
398
|
+
/** Ревизия владельца сессии для условного commit внешнего auth-flow. @internal */
|
|
399
|
+
revision() {
|
|
400
|
+
this.#assertActive();
|
|
401
|
+
return this.#authEpoch;
|
|
402
|
+
}
|
|
403
|
+
/** Создаёт изолированный jar для сетевого auth-flow. @internal */
|
|
404
|
+
createCookieFlow(inherit = true) {
|
|
405
|
+
this.#assertActive();
|
|
406
|
+
return inherit ? this.#jar.clone() : new CookieJar();
|
|
407
|
+
}
|
|
408
|
+
/** Применяет cookies только если владелец сессии не сменился. @internal */
|
|
409
|
+
commitCookieFlow(cookies, expectedRevision) {
|
|
410
|
+
this.#assertActive();
|
|
411
|
+
if (this.#authEpoch !== expectedRevision) return false;
|
|
412
|
+
this.#jar.replaceWith(cookies);
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
#assertActive() {
|
|
416
|
+
if (this.#disposed) throw new ItdStateError("Менеджер авторизации уже освобождён");
|
|
417
|
+
}
|
|
418
|
+
#currentAccessToken() {
|
|
419
|
+
return this.#session?.accessToken ?? null;
|
|
324
420
|
}
|
|
325
421
|
#identityForToken(accessToken) {
|
|
326
|
-
const token =
|
|
422
|
+
const token = this.#metadataForToken(accessToken);
|
|
327
423
|
return {
|
|
328
424
|
...token.subject ? { userId: token.subject } : {},
|
|
329
425
|
...token.sessionId ? { sessionId: token.sessionId } : {}
|
|
330
426
|
};
|
|
331
427
|
}
|
|
428
|
+
#metadataForToken(accessToken) {
|
|
429
|
+
if (!accessToken) {
|
|
430
|
+
this.#tokenMetadata = void 0;
|
|
431
|
+
return {};
|
|
432
|
+
}
|
|
433
|
+
if (this.#tokenMetadata?.accessToken === accessToken) return this.#tokenMetadata.value;
|
|
434
|
+
const value = readTokenMetadata(accessToken);
|
|
435
|
+
this.#tokenMetadata = {
|
|
436
|
+
accessToken,
|
|
437
|
+
value
|
|
438
|
+
};
|
|
439
|
+
return value;
|
|
440
|
+
}
|
|
332
441
|
async #readExternalToken(getToken) {
|
|
442
|
+
this.#assertActive();
|
|
443
|
+
const generation = ++this.#externalReadGeneration;
|
|
333
444
|
const token = await getToken() ?? null;
|
|
445
|
+
this.#assertActive();
|
|
446
|
+
if (generation !== this.#externalReadGeneration) return token;
|
|
334
447
|
const current = this.#identityForToken(token ?? void 0);
|
|
335
448
|
if (this.#externalToken !== void 0 && this.#externalToken !== token) {
|
|
336
449
|
this.#rotateAuthScope();
|
|
@@ -361,6 +474,7 @@ var AuthManager = class {
|
|
|
361
474
|
* ответ был бы `false` даже при полностью рабочей сохранённой сессии.
|
|
362
475
|
*/
|
|
363
476
|
async hasRefreshSession() {
|
|
477
|
+
this.#assertActive();
|
|
364
478
|
await this.#loadSession();
|
|
365
479
|
return this.#hasRefreshSession();
|
|
366
480
|
}
|
|
@@ -370,14 +484,33 @@ var AuthManager = class {
|
|
|
370
484
|
if (this.#jar.has("is_auth", this.#config.baseUrl)) return true;
|
|
371
485
|
return Boolean(this.#session?.refreshToken);
|
|
372
486
|
}
|
|
373
|
-
/**
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
487
|
+
/** Готовит внутреннюю сессию до retry исходного запроса. */
|
|
488
|
+
async preflight(allowRefresh) {
|
|
489
|
+
this.#assertActive();
|
|
490
|
+
let accessToken = (await this.#loadSession())?.accessToken;
|
|
491
|
+
if (!accessToken) {
|
|
492
|
+
const auth = this.#config.auth;
|
|
493
|
+
if (typeof auth === "object" && auth !== null && "email" in auth) accessToken = await this.#signInWithCredentials(auth) ?? void 0;
|
|
494
|
+
}
|
|
495
|
+
if (!accessToken || !allowRefresh || !this.#config.autoRefresh) return;
|
|
496
|
+
const expiresAt = this.#metadataForToken(accessToken).expiresAt;
|
|
497
|
+
if (expiresAt === void 0 || expiresAt > this.#config.clock.now() + ACCESS_TOKEN_REFRESH_LEEWAY_MS || !this.#hasRefreshSession()) return;
|
|
498
|
+
try {
|
|
499
|
+
await this.#refreshDeduplicated({
|
|
500
|
+
epoch: this.#authEpoch,
|
|
501
|
+
accessToken
|
|
502
|
+
});
|
|
503
|
+
} catch (error) {
|
|
504
|
+
this.#emitter.emit("authError", { error });
|
|
505
|
+
throw error;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
/** Перед каждой попыткой перечитывает внешний источник токена. */
|
|
379
509
|
async prepare() {
|
|
380
|
-
|
|
510
|
+
this.#assertActive();
|
|
511
|
+
if ((await this.#loadSession())?.accessToken) return;
|
|
512
|
+
const auth = this.#config.auth;
|
|
513
|
+
if (typeof auth === "object" && auth !== null && "getToken" in auth) await this.#readExternalToken(() => auth.getToken());
|
|
381
514
|
}
|
|
382
515
|
/**
|
|
383
516
|
* Заголовки уже подготовленной авторизации без чтения storage или вызова внешнего источника.
|
|
@@ -396,6 +529,7 @@ var AuthManager = class {
|
|
|
396
529
|
* через `itd.auth.refresh()`, а библиотека просто пробрасывает {@link ItdAuthError}.
|
|
397
530
|
*/
|
|
398
531
|
recover() {
|
|
532
|
+
this.#assertActive();
|
|
399
533
|
return this.#config.autoRefresh ? this.onUnauthorized() : Promise.resolve(false);
|
|
400
534
|
}
|
|
401
535
|
/**
|
|
@@ -406,6 +540,7 @@ var AuthManager = class {
|
|
|
406
540
|
* по новой сессии на каждый старт.
|
|
407
541
|
*/
|
|
408
542
|
deviceId() {
|
|
543
|
+
this.#assertActive();
|
|
409
544
|
if (this.#deviceId) return Promise.resolve(this.#deviceId);
|
|
410
545
|
this.#deviceIdLoading ??= this.#resolveDeviceId().finally(() => {
|
|
411
546
|
this.#deviceIdLoading = null;
|
|
@@ -413,9 +548,12 @@ var AuthManager = class {
|
|
|
413
548
|
return this.#deviceIdLoading;
|
|
414
549
|
}
|
|
415
550
|
async #resolveDeviceId() {
|
|
551
|
+
const revision = this.#authEpoch;
|
|
416
552
|
const session = await this.#loadSession();
|
|
553
|
+
this.#assertActive();
|
|
417
554
|
const deviceId = this.#config.deviceId ?? session?.deviceId ?? createDeviceId();
|
|
418
555
|
this.#deviceId = deviceId;
|
|
556
|
+
if (this.#authEpoch !== revision) return deviceId;
|
|
419
557
|
if (session?.deviceId !== deviceId) await this.#saveSession({
|
|
420
558
|
...session ?? {},
|
|
421
559
|
deviceId
|
|
@@ -429,6 +567,7 @@ var AuthManager = class {
|
|
|
429
567
|
* и пароль, первый же запрос сам заведёт сессию.
|
|
430
568
|
*/
|
|
431
569
|
async token() {
|
|
570
|
+
this.#assertActive();
|
|
432
571
|
const session = await this.#loadSession();
|
|
433
572
|
if (session?.accessToken) return session.accessToken;
|
|
434
573
|
const auth = this.#config.auth;
|
|
@@ -443,6 +582,7 @@ var AuthManager = class {
|
|
|
443
582
|
* @returns `true`, если токен обновлён и запрос имеет смысл повторить
|
|
444
583
|
*/
|
|
445
584
|
async onUnauthorized() {
|
|
585
|
+
this.#assertActive();
|
|
446
586
|
try {
|
|
447
587
|
if (await this.#refreshDeduplicated() !== null) return true;
|
|
448
588
|
this.#emitter.emit("authError", { error: this.#noRefreshSessionError() });
|
|
@@ -478,24 +618,33 @@ var AuthManager = class {
|
|
|
478
618
|
* @throws {ItdAuthError} если обновить сессию не удалось
|
|
479
619
|
*/
|
|
480
620
|
async refresh() {
|
|
621
|
+
this.#assertActive();
|
|
481
622
|
const token = await this.#refreshDeduplicated();
|
|
482
623
|
if (token === null) throw this.#noRefreshSessionError();
|
|
483
624
|
return token;
|
|
484
625
|
}
|
|
485
626
|
/** Сохраняет токен, полученный извне, — например после подтверждения OTP. */
|
|
486
|
-
async setAccessToken(accessToken) {
|
|
627
|
+
async setAccessToken(accessToken, expectedRevision = this.#authEpoch, cookies) {
|
|
628
|
+
this.#assertActive();
|
|
629
|
+
if (typeof accessToken !== "string" || accessToken.trim() === "") throw new ItdConfigError("accessToken должен быть непустой строкой");
|
|
487
630
|
await this.#loadSession();
|
|
631
|
+
this.#assertActive();
|
|
632
|
+
if (this.#authEpoch !== expectedRevision) return false;
|
|
633
|
+
if (cookies) this.#jar.replaceWith(cookies);
|
|
488
634
|
this.#invalidateInFlight();
|
|
489
635
|
this.#transitionAuth(accessToken);
|
|
490
|
-
|
|
636
|
+
const saved = this.#saveSession({
|
|
491
637
|
...this.#session ?? {},
|
|
492
638
|
accessToken,
|
|
493
639
|
obtainedAt: this.#config.clock.now()
|
|
494
640
|
});
|
|
495
641
|
this.#emitter.emit("tokens", { accessToken });
|
|
642
|
+
await saved;
|
|
643
|
+
return true;
|
|
496
644
|
}
|
|
497
645
|
/** Текущая сессия целиком. Полезно, чтобы сохранить её самому. */
|
|
498
646
|
async getSession() {
|
|
647
|
+
this.#assertActive();
|
|
499
648
|
const session = await this.#loadSession();
|
|
500
649
|
return session ? copySession(session) : null;
|
|
501
650
|
}
|
|
@@ -506,20 +655,24 @@ var AuthManager = class {
|
|
|
506
655
|
* токена идентификатор прежнего владельца остаться не может.
|
|
507
656
|
*/
|
|
508
657
|
async getUserId() {
|
|
658
|
+
this.#assertActive();
|
|
509
659
|
const session = await this.#loadSession();
|
|
510
|
-
return session?.accessToken ?
|
|
660
|
+
return session?.accessToken ? this.#identityForToken(session.accessToken).userId : void 0;
|
|
511
661
|
}
|
|
512
662
|
/** Заменяет сессию и связанные с ней cookie целиком. */
|
|
513
663
|
async setSession(session) {
|
|
664
|
+
this.#assertActive();
|
|
665
|
+
const normalized = normalizeSession(session, "session");
|
|
514
666
|
await this.#loadSession();
|
|
667
|
+
this.#assertActive();
|
|
515
668
|
this.#invalidateInFlight();
|
|
516
|
-
this.#transitionAuth(
|
|
669
|
+
this.#transitionAuth(normalized.accessToken);
|
|
517
670
|
this.#jar.clear();
|
|
518
|
-
this.#jar.deserialize(
|
|
519
|
-
if (
|
|
520
|
-
this.#session =
|
|
671
|
+
this.#jar.deserialize(normalized.cookies);
|
|
672
|
+
if (normalized.deviceId) this.#deviceId = normalized.deviceId;
|
|
673
|
+
this.#session = normalized;
|
|
521
674
|
this.#seedRefreshCookie();
|
|
522
|
-
await this.#saveSession(
|
|
675
|
+
await this.#saveSession(normalized);
|
|
523
676
|
}
|
|
524
677
|
/**
|
|
525
678
|
* Забывает сессию и cookie. Сетевой запрос не выполняется.
|
|
@@ -528,14 +681,19 @@ var AuthManager = class {
|
|
|
528
681
|
* новую запись в списке сессий.
|
|
529
682
|
*/
|
|
530
683
|
async clear() {
|
|
684
|
+
this.#assertActive();
|
|
531
685
|
await this.#loadSession();
|
|
686
|
+
this.#assertActive();
|
|
532
687
|
this.#invalidateInFlight();
|
|
533
688
|
this.#transitionAuth(void 0);
|
|
534
|
-
this.#session = null;
|
|
535
689
|
this.#jar.clear();
|
|
536
|
-
|
|
537
|
-
|
|
690
|
+
const retained = this.#deviceId ? { deviceId: this.#deviceId } : null;
|
|
691
|
+
this.#session = retained;
|
|
538
692
|
this.#emitter.emit("signOut", void 0);
|
|
693
|
+
await this.#enqueuePersistence(async () => {
|
|
694
|
+
await this.#config.storage.clear();
|
|
695
|
+
if (retained) await this.#config.storage.set(copySession(retained));
|
|
696
|
+
});
|
|
539
697
|
}
|
|
540
698
|
#loadSession() {
|
|
541
699
|
if (this.#session !== void 0) return Promise.resolve(this.#session);
|
|
@@ -546,7 +704,8 @@ var AuthManager = class {
|
|
|
546
704
|
}
|
|
547
705
|
async #performLoad() {
|
|
548
706
|
const loaded = await this.#config.storage.get() ?? null;
|
|
549
|
-
|
|
707
|
+
this.#assertActive();
|
|
708
|
+
const stored = loaded === null ? null : normalizeSessionValue(loaded, "storage.get()", "stored", this.#config.logger);
|
|
550
709
|
if (stored?.cookies) this.#jar.deserialize(stored.cookies);
|
|
551
710
|
const fromConfig = this.#sessionFromConfig(this.#config.auth);
|
|
552
711
|
this.#session = stored && fromConfig ? {
|
|
@@ -568,7 +727,7 @@ var AuthManager = class {
|
|
|
568
727
|
if (!this.#config.useCookieJar) return;
|
|
569
728
|
const refreshToken = this.#session?.refreshToken;
|
|
570
729
|
if (!refreshToken) return;
|
|
571
|
-
if (this.#jar.has("refresh_token")) return;
|
|
730
|
+
if (this.#jar.has("refresh_token", this.#config.baseUrl + "/api/v1/auth")) return;
|
|
572
731
|
this.#jar.set(this.#config.baseUrl, REFRESH_COOKIE, refreshToken, REFRESH_COOKIE_PATH);
|
|
573
732
|
}
|
|
574
733
|
#sessionFromConfig(auth) {
|
|
@@ -587,56 +746,76 @@ var AuthManager = class {
|
|
|
587
746
|
async #saveSession(session) {
|
|
588
747
|
const cookies = this.#config.useCookieJar ? this.#jar.serialize() : void 0;
|
|
589
748
|
const deviceId = session.deviceId ?? this.#deviceId;
|
|
749
|
+
const { cookies: _staleCookies, ...withoutCookies } = copySession(session);
|
|
590
750
|
const next = withoutLegacyUserId({
|
|
591
|
-
...
|
|
751
|
+
...withoutCookies,
|
|
592
752
|
...cookies?.length ? { cookies } : {},
|
|
593
753
|
...deviceId ? { deviceId } : {}
|
|
594
754
|
});
|
|
595
755
|
this.#session = next;
|
|
596
|
-
await this.#config.storage.set(next);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const
|
|
606
|
-
|
|
756
|
+
await this.#enqueuePersistence(() => this.#config.storage.set(copySession(next)));
|
|
757
|
+
}
|
|
758
|
+
#enqueuePersistence(operation) {
|
|
759
|
+
const pending = this.#persistence.then(operation, operation);
|
|
760
|
+
this.#persistence = pending.catch(() => {});
|
|
761
|
+
return pending;
|
|
762
|
+
}
|
|
763
|
+
/** Дедуплицирует refresh одной ревизии; следующая ждёт завершения предыдущей. */
|
|
764
|
+
#refreshDeduplicated(expectation = { epoch: this.#authEpoch }) {
|
|
765
|
+
const current = this.#refreshing;
|
|
766
|
+
if (current) {
|
|
767
|
+
if (current.epoch === expectation.epoch) return current.promise;
|
|
768
|
+
return current.promise.then(() => this.#refreshDeduplicated(expectation), () => this.#refreshDeduplicated(expectation));
|
|
769
|
+
}
|
|
770
|
+
const promise = this.#performRefresh(expectation).finally(() => {
|
|
771
|
+
if (this.#refreshing?.promise === promise) this.#refreshing = null;
|
|
607
772
|
});
|
|
608
|
-
this.#refreshing =
|
|
773
|
+
this.#refreshing = {
|
|
774
|
+
epoch: expectation.epoch,
|
|
775
|
+
promise
|
|
776
|
+
};
|
|
609
777
|
return promise;
|
|
610
778
|
}
|
|
611
|
-
|
|
779
|
+
#matchesRefreshExpectation(expectation) {
|
|
780
|
+
return this.#authEpoch === expectation.epoch && (expectation.accessToken === void 0 || this.#session?.accessToken === expectation.accessToken);
|
|
781
|
+
}
|
|
782
|
+
async #performRefresh(expectation) {
|
|
783
|
+
this.#assertActive();
|
|
612
784
|
await this.#loadSession();
|
|
785
|
+
if (!this.#matchesRefreshExpectation(expectation)) return this.#currentAccessToken();
|
|
613
786
|
if (!this.#hasRefreshSession()) return this.#reloginOrNull();
|
|
614
|
-
const
|
|
787
|
+
const flowCookies = this.#jar.clone();
|
|
615
788
|
try {
|
|
616
789
|
const accessToken = await this.#http.execute(AUTH_REFRESH, {
|
|
617
790
|
path: AUTH_PATHS.refresh,
|
|
618
791
|
skipAuth: true,
|
|
619
|
-
skipAuthRefresh: true
|
|
792
|
+
skipAuthRefresh: true,
|
|
793
|
+
cookieJar: flowCookies
|
|
620
794
|
});
|
|
621
|
-
if (!
|
|
622
|
-
|
|
795
|
+
if (!this.#matchesRefreshExpectation(expectation)) return this.#session?.accessToken ?? null;
|
|
796
|
+
this.#jar.replaceWith(flowCookies);
|
|
623
797
|
const rotated = this.#jar.getValue(REFRESH_COOKIE, this.#config.baseUrl + REFRESH_COOKIE_PATH);
|
|
624
798
|
this.#transitionAuth(accessToken, false);
|
|
625
|
-
|
|
799
|
+
const saved = this.#saveSession({
|
|
626
800
|
...this.#session ?? {},
|
|
627
801
|
accessToken,
|
|
628
802
|
...rotated ? { refreshToken: rotated } : {},
|
|
629
803
|
obtainedAt: this.#config.clock.now()
|
|
630
804
|
});
|
|
631
805
|
this.#emitter.emit("tokens", { accessToken });
|
|
806
|
+
await saved;
|
|
807
|
+
if (this.#authEpoch !== expectation.epoch) return this.#currentAccessToken();
|
|
632
808
|
return accessToken;
|
|
633
809
|
} catch (error) {
|
|
634
|
-
if (
|
|
635
|
-
|
|
810
|
+
if (this.#authEpoch !== expectation.epoch) return this.#currentAccessToken();
|
|
811
|
+
if (error instanceof ItdAuthError) {
|
|
812
|
+
this.#invalidateInFlight();
|
|
813
|
+
const clearedRevision = this.#authEpoch;
|
|
636
814
|
this.#transitionAuth(void 0);
|
|
637
815
|
this.#session = null;
|
|
638
816
|
this.#jar.clear();
|
|
639
|
-
await this.#config.storage.clear();
|
|
817
|
+
await this.#enqueuePersistence(() => this.#config.storage.clear());
|
|
818
|
+
if (this.#authEpoch !== clearedRevision) return this.#currentAccessToken();
|
|
640
819
|
const relogged = await this.#reloginOrNull();
|
|
641
820
|
if (relogged !== null) return relogged;
|
|
642
821
|
throw error;
|
|
@@ -650,7 +829,8 @@ var AuthManager = class {
|
|
|
650
829
|
if (!this.#config.reloginOnRefreshFailure || !auth || typeof auth !== "object" || !("email" in auth)) return null;
|
|
651
830
|
try {
|
|
652
831
|
return await this.#signInWithCredentials(auth);
|
|
653
|
-
} catch {
|
|
832
|
+
} catch (error) {
|
|
833
|
+
this.#config.logger?.warn("Автоматический повторный вход не удался", error);
|
|
654
834
|
return null;
|
|
655
835
|
}
|
|
656
836
|
}
|
|
@@ -662,7 +842,8 @@ var AuthManager = class {
|
|
|
662
842
|
*/
|
|
663
843
|
#signInWithCredentials(credentials) {
|
|
664
844
|
if (this.#signingIn) return this.#signingIn;
|
|
665
|
-
const
|
|
845
|
+
const revision = this.#authEpoch;
|
|
846
|
+
const promise = this.#performSignIn(credentials, revision).finally(() => {
|
|
666
847
|
this.#signingIn = null;
|
|
667
848
|
});
|
|
668
849
|
this.#signingIn = promise;
|
|
@@ -682,8 +863,9 @@ var AuthManager = class {
|
|
|
682
863
|
if (credentials.turnstileToken) return credentials.turnstileToken;
|
|
683
864
|
throw new ItdConfigError("Вход по email и паролю требует токен капчи Cloudflare Turnstile: без него сервер отвечает 422. Передайте auth.getTurnstileToken (источник свежего токена) либо разовый auth.turnstileToken. Ключ виджета — TURNSTILE_SITE_KEY. В Node токен умеет добывать отдельный пакет: npm i @itd-api/turnstile, затем getTurnstileToken: createTurnstileSolver().");
|
|
684
865
|
}
|
|
685
|
-
async #performSignIn(credentials) {
|
|
866
|
+
async #performSignIn(credentials, revision) {
|
|
686
867
|
const turnstileToken = await this.#resolveTurnstileToken(credentials);
|
|
868
|
+
const flowCookies = new CookieJar();
|
|
687
869
|
const result = await this.#http.execute(AUTH_SIGN_IN, {
|
|
688
870
|
path: AUTH_PATHS.signIn,
|
|
689
871
|
body: {
|
|
@@ -692,18 +874,23 @@ var AuthManager = class {
|
|
|
692
874
|
turnstileToken
|
|
693
875
|
},
|
|
694
876
|
skipAuth: true,
|
|
695
|
-
skipAuthRefresh: true
|
|
877
|
+
skipAuthRefresh: true,
|
|
878
|
+
cookieJar: flowCookies
|
|
696
879
|
});
|
|
697
880
|
if (result.status !== SignInStatus.Authenticated) throw new ItdConfigError("Вход по email и паролю требует подтверждения кодом из письма. Автоматический вход невозможен: воспользуйтесь itd.auth.signInWithOtp() и передайте полученный accessToken в конфигурацию клиента.");
|
|
698
881
|
const accessToken = result.accessToken;
|
|
882
|
+
this.#assertActive();
|
|
883
|
+
if (this.#authEpoch !== revision) return this.#session?.accessToken ?? null;
|
|
884
|
+
this.#jar.replaceWith(flowCookies);
|
|
699
885
|
this.#invalidateInFlight();
|
|
700
886
|
this.#transitionAuth(accessToken);
|
|
701
|
-
|
|
887
|
+
const saved = this.#saveSession({
|
|
702
888
|
accessToken,
|
|
703
889
|
obtainedAt: this.#config.clock.now()
|
|
704
890
|
});
|
|
705
891
|
this.#emitter.emit("tokens", { accessToken });
|
|
706
892
|
this.#emitter.emit("signIn", { accessToken });
|
|
893
|
+
await saved;
|
|
707
894
|
return accessToken;
|
|
708
895
|
}
|
|
709
896
|
};
|
|
@@ -762,14 +949,18 @@ var AuthResource = class extends BaseResource {
|
|
|
762
949
|
* @param credentials email, пароль и обязательный токен капчи — см. {@link CaptchaCredentials}
|
|
763
950
|
*/
|
|
764
951
|
async signIn(credentials, options = {}) {
|
|
952
|
+
const revision = this.#auth.revision();
|
|
953
|
+
const cookies = this.#auth.createCookieFlow(false);
|
|
765
954
|
const result = await this.http.execute(AUTH_SIGN_IN, {
|
|
766
955
|
path: AUTH_PATHS.signIn,
|
|
767
956
|
body: credentials,
|
|
768
957
|
skipAuth: true,
|
|
769
958
|
skipAuthRefresh: true,
|
|
959
|
+
cookieJar: cookies,
|
|
770
960
|
...options
|
|
771
961
|
});
|
|
772
|
-
if (result.status === SignInStatus.Authenticated) await this.#auth.setAccessToken(result.accessToken);
|
|
962
|
+
if (result.status === SignInStatus.Authenticated) await this.#auth.setAccessToken(result.accessToken, revision, cookies);
|
|
963
|
+
else this.#auth.commitCookieFlow(cookies, revision);
|
|
773
964
|
return result;
|
|
774
965
|
}
|
|
775
966
|
/**
|
|
@@ -778,14 +969,17 @@ var AuthResource = class extends BaseResource {
|
|
|
778
969
|
* Полученный токен сохраняется в клиенте автоматически.
|
|
779
970
|
*/
|
|
780
971
|
async verifyOtp(input, options = {}) {
|
|
972
|
+
const revision = this.#auth.revision();
|
|
973
|
+
const cookies = this.#auth.createCookieFlow();
|
|
781
974
|
const accessToken = await this.http.execute(AUTH_VERIFY_OTP, {
|
|
782
975
|
path: AUTH_PATHS.verifyOtp,
|
|
783
976
|
body: input,
|
|
784
977
|
skipAuth: true,
|
|
785
978
|
skipAuthRefresh: true,
|
|
979
|
+
cookieJar: cookies,
|
|
786
980
|
...options
|
|
787
981
|
});
|
|
788
|
-
await this.#auth.setAccessToken(accessToken);
|
|
982
|
+
await this.#auth.setAccessToken(accessToken, revision, cookies);
|
|
789
983
|
return accessToken;
|
|
790
984
|
}
|
|
791
985
|
/** Отправляет код подтверждения повторно. */
|
|
@@ -1704,7 +1898,7 @@ var ItdAccounts = class ItdAccounts {
|
|
|
1704
1898
|
})));
|
|
1705
1899
|
const added = [];
|
|
1706
1900
|
for (const { name, session } of sessions) {
|
|
1707
|
-
if (!isRestorableSession(session)) continue;
|
|
1901
|
+
if (!isRestorableSession(session, this.#base.baseUrl ?? "https://xn--d1ah4a.com")) continue;
|
|
1708
1902
|
if (this.#clients.has(name) || this.#removing.has(name)) continue;
|
|
1709
1903
|
this.addAccount(name);
|
|
1710
1904
|
added.push(name);
|
|
@@ -1957,6 +2151,6 @@ function createAccounts(options = {}) {
|
|
|
1957
2151
|
return new ItdAccounts(options);
|
|
1958
2152
|
}
|
|
1959
2153
|
//#endregion
|
|
1960
|
-
export { ALLOWED_MIME_TYPES, AUDIO_MIME_TYPES, AccessType, AttachmentType, BUCKET_LIMITS, CommentSort, DEFAULT_BASE_URL, DEFAULT_RATE_LIMIT_BUCKET, EventChannel, EventChannelStatus, EventComposer, EventRouter, FeedTab, FileTransferMode, IMAGE_MIME_TYPES, IncidentKind, InteractionType, ItdAbortError, ItdAccounts, ItdApiError, ItdApiErrorKind, ItdAuthError, ItdClient, ItdConfigError, ItdConflictError, ItdError, ItdErrorCode, ItdErrorKind, ItdFileError, ItdFileErrorReason, ItdForbiddenError, ItdNetworkError, ItdNotFoundError, ItdPhoneVerificationError, ItdRateLimitError, ItdServerError, ItdStateError, ItdTimeoutError, ItdValidationError, LIBRARY_VERSION, LikesVisibility, MemoryKeyValueStore, MemoryMultiTokenStorage, MemoryShopOrderAccessStorage, MemoryTokenStorage, NotificationEvents, NotificationEventsClient, NotificationEventsTransport, NotificationType, NotificationUpdateOrigin, NotificationUpdateType, OPERATIONS, PaginationMode, Paginator, RateLimitPacing, ReportReason, ReportTargetType, RetrySafety, RuntimeMode, STATUS_SERVICE, ServiceState, ShopConsentKind, ShopOrderStatus, ShopProductCategory, ShopProductStatus, SignInStatus, SpanRenderFormat, SpanType, TURNSTILE_SITE_KEY, UnauthorizedStreamError, VIDEO_MIME_TYPES, ViewReason, ViewSource, WallAccess, WebSocketTransport, autoSpans, canonicalNotificationType, comment, createAccounts, createClient, createKeyValueStore,
|
|
2154
|
+
export { ALLOWED_MIME_TYPES, AUDIO_MIME_TYPES, AccessType, AttachmentType, BUCKET_LIMITS, CommentSort, DEFAULT_BASE_URL, DEFAULT_RATE_LIMIT_BUCKET, EventChannel, EventChannelStatus, EventComposer, EventRouter, FeedTab, FileTransferMode, IMAGE_MIME_TYPES, IncidentKind, InteractionType, ItdAbortError, ItdAccounts, ItdApiError, ItdApiErrorKind, ItdAuthError, ItdClient, ItdConfigError, ItdConflictError, ItdError, ItdErrorCode, ItdErrorKind, ItdFileError, ItdFileErrorReason, ItdForbiddenError, ItdNetworkError, ItdNotFoundError, ItdPhoneVerificationError, ItdRateLimitError, ItdServerError, ItdStateError, ItdTimeoutError, ItdValidationError, LIBRARY_VERSION, LikesVisibility, MemoryKeyValueStore, MemoryMultiTokenStorage, MemoryShopOrderAccessStorage, MemoryTokenStorage, NotificationEvents, NotificationEventsClient, NotificationEventsTransport, NotificationType, NotificationUpdateOrigin, NotificationUpdateType, OPERATIONS, PaginationMode, Paginator, RateLimitPacing, ReportReason, ReportTargetType, RetrySafety, RuntimeMode, STATUS_SERVICE, ServiceState, ShopConsentKind, ShopOrderStatus, ShopProductCategory, ShopProductStatus, SignInStatus, SpanRenderFormat, SpanType, TURNSTILE_SITE_KEY, UnauthorizedStreamError, VIDEO_MIME_TYPES, ViewReason, ViewSource, WallAccess, WebSocketTransport, autoSpans, canonicalNotificationType, comment, createAccounts, createClient, createKeyValueStore, createMultiTokenStorage, createNotificationEventsClient, createRecordKeyValueStore, createShopFeature, createShopOrderAccessStorage, createTokenStorage, formatNotificationText, fromStream, fromUrl, isBuilder, isBuiltInOperationId, isEnumerableKeyValueStore, isItdApiError, isItdAuthError, isItdConflictError, isItdError, isItdFileError, isItdForbiddenError, isItdNotFoundError, isItdPhoneVerificationError, isItdRateLimitError, isItdServerError, isItdStateError, isItdValidationError, isKnownNotificationType, isMyProfile, mapPage, markup, normalizeNotification, operationBucket, operationMethod, operationRetrySafety, parseHtml, parseMarkdown, poll, post, renderSpans, report, resolveNotificationUrl, runEventMiddleware, scopedTokenStorage, statusDays, systemClock, toDate, utcStampToIso, withCodec, withNamespace };
|
|
1961
2155
|
|
|
1962
2156
|
//# sourceMappingURL=index.js.map
|