itd-api 0.10.0 → 0.10.2
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 +2 -2
- package/dist/events/index.d.cts +2 -2
- package/dist/events/index.d.ts +2 -2
- package/dist/events/index.js +2 -2
- package/dist/index.cjs +175 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +216 -132
- package/dist/index.d.ts +216 -132
- package/dist/index.js +175 -60
- package/dist/index.js.map +1 -1
- package/dist/rest/index.cjs +2 -2
- package/dist/rest/index.d.cts +2 -2
- package/dist/rest/index.d.ts +2 -2
- package/dist/rest/index.js +2 -2
- package/dist/shared/{events-B0Wtn4cF.js → events-0I_Pixnq.js} +2 -2
- package/dist/shared/{events-B0Wtn4cF.js.map → events-0I_Pixnq.js.map} +1 -1
- package/dist/shared/{events-BB791aPk.cjs → events-8OLqvKwQ.cjs} +2 -2
- package/dist/shared/{events-BB791aPk.cjs.map → events-8OLqvKwQ.cjs.map} +1 -1
- package/dist/shared/{events-BNGjJVI3.d.cts → events-Cr1Mc2Gl.d.cts} +2 -2
- package/dist/shared/{events-CJO6q93Z.d.ts → events-DXooQkyB.d.ts} +2 -2
- package/dist/shared/{render-C1A57W-5.d.cts → render-BxPM234w.d.cts} +29 -3
- package/dist/shared/{render-BimHjOhw.cjs → render-CnpKoGO8.cjs} +2 -2
- package/dist/shared/{render-BimHjOhw.cjs.map → render-CnpKoGO8.cjs.map} +1 -1
- package/dist/shared/{render-DC5nvqcP.d.ts → render-D69bOsxu.d.ts} +29 -3
- package/dist/shared/{render-CkhvH_yh.js → render-GQw5m1-o.js} +2 -2
- package/dist/shared/{render-CkhvH_yh.js.map → render-GQw5m1-o.js.map} +1 -1
- package/dist/shared/{url-CDVgMIq5.cjs → url-BnaioEsz.cjs} +44 -6
- package/dist/shared/{url-CDVgMIq5.cjs.map → url-BnaioEsz.cjs.map} +1 -1
- package/dist/shared/{url-BA5hEwj2.js → url-D0KJ5pws.js} +39 -7
- package/dist/shared/{url-BA5hEwj2.js.map → url-D0KJ5pws.js.map} +1 -1
- package/dist/shared/{url-DkMueFAT.d.ts → url-Qv7smwHP.d.cts} +40 -7
- package/dist/shared/{url-DkMueFAT.d.cts → url-Qv7smwHP.d.ts} +40 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as SpanType, B as operationRetrySafety, C as ItdErrorCode, D as ReportReason, E as QrLocationPrecision, F as OPERATIONS, H as DEFAULT_RATE_LIMIT_BUCKET, I as defineBuiltInOperation, K as pickArray, L as isBuiltInOperationId, M as ViewSource, N as WallAccess, O as ReportTargetType, P as ITD_CATALOG, R as operationBucket, S as InteractionType, T as NotificationType, U as createClientRuntime, V as BUCKET_LIMITS, W as createApiError, X as pickString, Z as RetrySafety, _ as CaptchaType, _t as installAsyncDisposeFallback, at as assertPluginRemovable, b as FeedTab, c as normalizeNotification, d as canonicalNotificationType, dt as LIBRARY_VERSION, f as isKnownNotificationType, ft as isRecord, g as CaptchaField, gt as systemClock, h as CaptchaChoice, it as RequestQueuePool, j as ViewReason, k as ServiceState, lt as STATUS_SERVICE, m as AttachmentType, mt as RateLimitPacing, n as formatNotificationText, nt as requestAbortError, o as passthroughOperation, ot as orderPluginDefinitions, p as AccessType, pt as requireOptionalBoolean, s as voidOperation, st as DEFAULT_BASE_URL, t as resolveNotificationUrl, tt as createRequestAbortScope, ut as resolveRateLimit, v as CommentSort, w as LikesVisibility, x as IncidentKind, y as EventChannelStatus, z as operationMethod } from "./shared/url-D0KJ5pws.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
3
|
import { c as REFRESH_COOKIE, h as joinUrl, 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-
|
|
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-GQw5m1-o.js";
|
|
5
5
|
import { c as FileTransferMode } from "./shared/options-i4LrKfqx.js";
|
|
6
|
-
import { _ as Emitter, a as WebSocketTransport, c as NotificationEventsTransport, d as NotificationUpdateOrigin, f as NotificationUpdateType, g as runEventMiddleware, h as UnauthorizedStreamError, i as createNotificationEventsClient, l as resolveNotificationEventsOptions, m as EventChannel, n as EventRouter, o as EVENT_OPERATIONS, p as createParser, r as NotificationEventsClient, s as NotificationEvents, t as EventComposer, u as setNotificationEventsConnectGuard, v as reportListenerError } from "./shared/events-
|
|
6
|
+
import { _ as Emitter, a as WebSocketTransport, c as NotificationEventsTransport, d as NotificationUpdateOrigin, f as NotificationUpdateType, g as runEventMiddleware, h as UnauthorizedStreamError, i as createNotificationEventsClient, l as resolveNotificationEventsOptions, m as EventChannel, n as EventRouter, o as EVENT_OPERATIONS, p as createParser, r as NotificationEventsClient, s as NotificationEvents, t as EventComposer, u as setNotificationEventsConnectGuard, v as reportListenerError } from "./shared/events-0I_Pixnq.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
8
|
import { n as copySession, r as createTokenStorage, t as MemoryTokenStorage } from "./shared/storage-DiDe0PZM.js";
|
|
9
9
|
import { a as scopedTokenStorage, i as isRestorableSession, n as controlledTokenStorage, r as createMultiTokenStorage, t as MemoryMultiTokenStorage } from "./shared/multi-storage-C3Ngtop1.js";
|
|
@@ -62,6 +62,7 @@ function requireString(body, key, message) {
|
|
|
62
62
|
}
|
|
63
63
|
const AUTH_CHECK = passthroughOperation("auth.check");
|
|
64
64
|
const AUTH_CAPTCHA_PROVIDER = passthroughOperation("auth.captchaProvider");
|
|
65
|
+
const AUTH_CAPTCHA_PAGE = passthroughOperation("auth.captchaPage");
|
|
65
66
|
const AUTH_SIGN_UP = defineBuiltInOperation("auth.signUp", (body) => requireString(body, "flowToken", "Сервер не вернул flowToken при регистрации"));
|
|
66
67
|
const AUTH_SIGN_IN = defineBuiltInOperation("auth.signIn", (body) => {
|
|
67
68
|
const accessToken = pickString(body, "accessToken");
|
|
@@ -85,27 +86,45 @@ const AUTH_REVOKE_SESSION = voidOperation("auth.revokeSession");
|
|
|
85
86
|
const AUTH_REVOKE_OTHER_SESSIONS = voidOperation("auth.revokeOtherSessions");
|
|
86
87
|
const AUTH_QR_START = passthroughOperation("auth.qrStart");
|
|
87
88
|
const AUTH_QR_CLAIM = passthroughOperation("auth.qrClaim");
|
|
89
|
+
const AUTH_QR_SCAN = passthroughOperation("auth.qrScan");
|
|
90
|
+
const AUTH_QR_APPROVE = voidOperation("auth.qrApprove");
|
|
91
|
+
const AUTH_QR_REJECT = voidOperation("auth.qrReject");
|
|
88
92
|
//#endregion
|
|
89
93
|
//#region src/session/captcha.ts
|
|
90
94
|
function requireCaptchaText(value, field) {
|
|
91
95
|
if (value !== void 0 && (typeof value !== "string" || value.trim() === "")) throw new ItdConfigError(`${field} должен быть непустой строкой`);
|
|
92
96
|
}
|
|
93
97
|
/**
|
|
94
|
-
* Проверяет форму
|
|
98
|
+
* Проверяет форму источника токена и приводит его к объекту.
|
|
95
99
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
100
|
+
* Сам объект сохраняется как есть: `getToken` вызывается на нём, чтобы источнику остался
|
|
101
|
+
* доступен собственный `this`.
|
|
98
102
|
*
|
|
99
103
|
* @throws {ItdConfigError} при некорректных значениях
|
|
100
104
|
*/
|
|
101
|
-
function
|
|
105
|
+
function resolveCaptchaSolver(captcha) {
|
|
106
|
+
if (captcha === void 0) return void 0;
|
|
107
|
+
if (typeof captcha === "function") return { getToken: captcha };
|
|
108
|
+
if (!isRecord(captcha)) throw new ItdConfigError("captcha должен быть функцией либо объектом с getToken");
|
|
109
|
+
const { getToken, type, field } = captcha;
|
|
110
|
+
if (typeof getToken !== "function") throw new ItdConfigError("captcha.getToken должен быть функцией");
|
|
111
|
+
requireCaptchaText(type, "captcha.type");
|
|
112
|
+
requireCaptchaText(field, "captcha.field");
|
|
113
|
+
return captcha;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Проверяет форму готового токена капчи.
|
|
117
|
+
*
|
|
118
|
+
* @param path имя опции или аргумента для текста ошибки
|
|
119
|
+
* @throws {ItdConfigError} если токен или его тип не указаны
|
|
120
|
+
*/
|
|
121
|
+
function validateCaptchaToken(captcha, path) {
|
|
102
122
|
if (captcha === void 0) return;
|
|
103
|
-
if (!isRecord(captcha)) throw new ItdConfigError(
|
|
104
|
-
const { type, token,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
requireCaptchaText(field,
|
|
108
|
-
if (getToken !== void 0 && typeof getToken !== "function") throw new ItdConfigError("auth.captcha.getToken должен быть функцией");
|
|
123
|
+
if (!isRecord(captcha)) throw new ItdConfigError(`${path} должен быть объектом { type, token }`);
|
|
124
|
+
const { type, token, field } = captcha;
|
|
125
|
+
if (typeof token !== "string" || token.trim() === "") throw new ItdConfigError(`${path}.token должен быть непустой строкой`);
|
|
126
|
+
if (typeof type !== "string" || type.trim() === "") throw new ItdConfigError(`${path}.type должен быть непустой строкой`);
|
|
127
|
+
requireCaptchaText(field, `${path}.field`);
|
|
109
128
|
}
|
|
110
129
|
/** Разбирает ответ `GET /api/v1/auth/captcha/provider` для автоматического входа. */
|
|
111
130
|
function readCaptchaProvider(body) {
|
|
@@ -122,43 +141,40 @@ function readCaptchaProvider(body) {
|
|
|
122
141
|
function resolveField(type, configured) {
|
|
123
142
|
const field = configured ?? CAPTCHA_FIELDS[type];
|
|
124
143
|
if (field) return field;
|
|
125
|
-
throw new ItdConfigError(`Неизвестно, в каком поле сервер ждёт токен капчи «${type}». Передайте
|
|
144
|
+
throw new ItdConfigError(`Неизвестно, в каком поле сервер ждёт токен капчи «${type}». Передайте captcha.field либо оставьте captcha.type = ${CaptchaChoice.Auto}, чтобы поле назвал сервер.`);
|
|
126
145
|
}
|
|
127
|
-
/** Спрашивает
|
|
128
|
-
async function
|
|
129
|
-
const token = await
|
|
130
|
-
if (token) return token;
|
|
131
|
-
throw new ItdConfigError(`Источник капчи не вернул токен для провайдера «${type}». Проверьте
|
|
146
|
+
/** Спрашивает токен у источника и убеждается, что он непустой. */
|
|
147
|
+
async function requireSolvedToken(solver, type) {
|
|
148
|
+
const token = await solver.getToken(type);
|
|
149
|
+
if (typeof token === "string" && token.trim() !== "") return token;
|
|
150
|
+
throw new ItdConfigError(`Источник капчи не вернул токен для провайдера «${type}». Проверьте captcha.getToken.`);
|
|
132
151
|
}
|
|
133
152
|
/**
|
|
134
|
-
* Готовит фрагмент тела
|
|
153
|
+
* Готовит фрагмент тела запроса, взяв токен у источника.
|
|
135
154
|
*
|
|
136
155
|
* При `CaptchaChoice.Auto` провайдер спрашивается у сервера — тогда и тип виджета,
|
|
137
|
-
* и имя поля приходят оттуда. Явно названный тип
|
|
156
|
+
* и имя поля приходят оттуда. Явно названный тип обходится без этого запроса.
|
|
138
157
|
*
|
|
139
158
|
* @param askProvider как спросить активного провайдера; вызывается, только если это нужно
|
|
140
|
-
* @throws {ItdConfigError} если
|
|
159
|
+
* @throws {ItdConfigError} если токен получить не удалось
|
|
141
160
|
*/
|
|
142
|
-
async function
|
|
143
|
-
|
|
144
|
-
const choice = captcha.type ?? CaptchaChoice.Auto;
|
|
161
|
+
async function solveCaptchaBody(solver, askProvider) {
|
|
162
|
+
const choice = solver.type ?? CaptchaChoice.Auto;
|
|
145
163
|
if (choice === CaptchaChoice.Auto) {
|
|
146
164
|
const { provider, field } = await askProvider();
|
|
147
|
-
return { [field]: await
|
|
165
|
+
return { [field]: await requireSolvedToken(solver, provider) };
|
|
148
166
|
}
|
|
149
|
-
return { [resolveField(choice,
|
|
167
|
+
return { [resolveField(choice, solver.field)]: await requireSolvedToken(solver, choice) };
|
|
150
168
|
}
|
|
151
169
|
/**
|
|
152
|
-
* Готовит фрагмент тела
|
|
170
|
+
* Готовит фрагмент тела из готового токена.
|
|
153
171
|
*
|
|
154
|
-
* Поле берётся из самого
|
|
172
|
+
* Поле берётся из самого токена либо из умолчания провайдера; запроса к серверу нет.
|
|
155
173
|
*
|
|
156
|
-
* @throws {ItdConfigError} если
|
|
174
|
+
* @throws {ItdConfigError} если токен или его тип не указаны
|
|
157
175
|
*/
|
|
158
176
|
function captchaBody(captcha) {
|
|
159
|
-
|
|
160
|
-
if (typeof captcha.token !== "string" || captcha.token.trim() === "") throw new ItdConfigError("captcha.token должен быть непустой строкой");
|
|
161
|
-
if (typeof captcha.type !== "string" || captcha.type.trim() === "") throw new ItdConfigError("captcha.type должен быть непустой строкой");
|
|
177
|
+
validateCaptchaToken(captcha, "captcha");
|
|
162
178
|
return { [resolveField(captcha.type, captcha.field)]: captcha.token };
|
|
163
179
|
}
|
|
164
180
|
//#endregion
|
|
@@ -236,7 +252,7 @@ function validateAuth(auth) {
|
|
|
236
252
|
const { email, password, captcha } = auth;
|
|
237
253
|
if (typeof email !== "string" || email.trim() === "") throw new ItdConfigError("auth.email должен быть непустой строкой");
|
|
238
254
|
if (typeof password !== "string" || password === "") throw new ItdConfigError("auth.password должен быть непустой строкой");
|
|
239
|
-
|
|
255
|
+
validateCaptchaToken(captcha, "auth.captcha");
|
|
240
256
|
return { ...auth };
|
|
241
257
|
}
|
|
242
258
|
throw new ItdConfigError("auth не распознан. Ожидается строка с accessToken либо объект { accessToken }, { email, password } или { getToken }");
|
|
@@ -265,6 +281,7 @@ function resolveSessionConfig(options, runtime) {
|
|
|
265
281
|
return {
|
|
266
282
|
...runtime,
|
|
267
283
|
auth: validateAuth(options.auth),
|
|
284
|
+
captcha: resolveCaptchaSolver(options.captcha),
|
|
268
285
|
storage: resolveStorage(options.storage),
|
|
269
286
|
deviceId: options.deviceId,
|
|
270
287
|
autoRefresh: options.autoRefresh ?? true,
|
|
@@ -284,6 +301,7 @@ function createItdAuth(options, deps) {
|
|
|
284
301
|
/** Пути эндпоинтов авторизации. */
|
|
285
302
|
const AUTH_PATHS = {
|
|
286
303
|
captchaProvider: "/api/v1/auth/captcha/provider",
|
|
304
|
+
captchaPage: "/api/v1/auth/captcha/page",
|
|
287
305
|
signUp: "/api/v1/auth/sign-up",
|
|
288
306
|
signIn: "/api/v1/auth/sign-in",
|
|
289
307
|
verifyOtp: "/api/v1/auth/verify-otp",
|
|
@@ -296,7 +314,10 @@ const AUTH_PATHS = {
|
|
|
296
314
|
sessions: "/api/v1/auth/sessions",
|
|
297
315
|
qrStart: "/api/v1/auth/qr/start",
|
|
298
316
|
qrClaim: "/api/v1/auth/qr/claim",
|
|
299
|
-
qrStream: "/api/v1/auth/qr/stream"
|
|
317
|
+
qrStream: "/api/v1/auth/qr/stream",
|
|
318
|
+
qrScan: "/api/v1/auth/qr/scan",
|
|
319
|
+
qrApprove: "/api/v1/auth/qr/approve",
|
|
320
|
+
qrReject: "/api/v1/auth/qr/reject"
|
|
300
321
|
};
|
|
301
322
|
/**
|
|
302
323
|
* Публичный ключ Cloudflare Turnstile платформы итд.com.
|
|
@@ -436,6 +457,8 @@ var AuthManager = class {
|
|
|
436
457
|
*/
|
|
437
458
|
#authEpoch = 0;
|
|
438
459
|
#externalReadGeneration = 0;
|
|
460
|
+
/** Разовый токен из `auth.captcha` тратится ровно на один запрос. */
|
|
461
|
+
#configCaptchaSpent = false;
|
|
439
462
|
#disposed = false;
|
|
440
463
|
#persistence = Promise.resolve();
|
|
441
464
|
constructor(config, http, jar, hooks = {}) {
|
|
@@ -963,6 +986,34 @@ var AuthManager = class {
|
|
|
963
986
|
this.#signingIn = promise;
|
|
964
987
|
return promise;
|
|
965
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* Готовит фрагмент тела с токеном капчи для запроса, который её требует.
|
|
991
|
+
*
|
|
992
|
+
* Источники в порядке убывания приоритета: токен, переданный в сам вызов; разовый токен
|
|
993
|
+
* из `auth.captcha`; опция `captcha`. Когда ни одного нет, фрагмент пустой: нужна ли
|
|
994
|
+
* капча этому запросу, решает сервер.
|
|
995
|
+
*
|
|
996
|
+
* @param explicit готовый токен, переданный в вызов
|
|
997
|
+
* @throws {ItdConfigError} если токен неполный или источник его не вернул
|
|
998
|
+
*/
|
|
999
|
+
async captchaBody(explicit) {
|
|
1000
|
+
if (explicit) return captchaBody(explicit);
|
|
1001
|
+
const configured = this.#takeConfiguredCaptcha();
|
|
1002
|
+
if (configured) return captchaBody(configured);
|
|
1003
|
+
const solver = this.#config.captcha;
|
|
1004
|
+
if (solver) return solveCaptchaBody(solver, () => this.#resolveCaptchaProvider());
|
|
1005
|
+
this.#config.logger?.debug("Запрос уходит без токена капчи: не передан аргумент captcha и не задана опция captcha");
|
|
1006
|
+
return {};
|
|
1007
|
+
}
|
|
1008
|
+
/** Отдаёт разовый токен из `auth.captcha`, помечая его потраченным. */
|
|
1009
|
+
#takeConfiguredCaptcha() {
|
|
1010
|
+
if (this.#configCaptchaSpent) return void 0;
|
|
1011
|
+
const auth = this.#config.auth;
|
|
1012
|
+
const captcha = isRecord(auth) ? auth.captcha : void 0;
|
|
1013
|
+
if (!captcha) return void 0;
|
|
1014
|
+
this.#configCaptchaSpent = true;
|
|
1015
|
+
return captcha;
|
|
1016
|
+
}
|
|
966
1017
|
/** Спрашивает у сервера активного провайдера и поле, в котором он ждёт токен. */
|
|
967
1018
|
async #resolveCaptchaProvider() {
|
|
968
1019
|
return readCaptchaProvider(await this.#http.execute(AUTH_CAPTCHA_PROVIDER, {
|
|
@@ -972,7 +1023,7 @@ var AuthManager = class {
|
|
|
972
1023
|
}));
|
|
973
1024
|
}
|
|
974
1025
|
async #performSignIn(credentials, revision) {
|
|
975
|
-
const captcha = await
|
|
1026
|
+
const captcha = await this.captchaBody();
|
|
976
1027
|
const flowCookies = new CookieJar();
|
|
977
1028
|
const result = await this.#http.execute(AUTH_SIGN_IN, {
|
|
978
1029
|
path: AUTH_PATHS.signIn,
|
|
@@ -1153,7 +1204,8 @@ var AuthResource = class extends BaseResource {
|
|
|
1153
1204
|
if (existing) return existing;
|
|
1154
1205
|
const flow = {
|
|
1155
1206
|
cookies: this.#auth.createCookieFlow(false),
|
|
1156
|
-
expiresAt: void 0
|
|
1207
|
+
expiresAt: void 0,
|
|
1208
|
+
captchaRequired: false
|
|
1157
1209
|
};
|
|
1158
1210
|
this.#qrFlows.set(qrId, flow);
|
|
1159
1211
|
return flow;
|
|
@@ -1191,6 +1243,21 @@ var AuthResource = class extends BaseResource {
|
|
|
1191
1243
|
...options
|
|
1192
1244
|
});
|
|
1193
1245
|
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Узнаёт активного провайдера капчи вместе с адресом страницы виджета.
|
|
1248
|
+
*
|
|
1249
|
+
* То же, что {@link captchaProvider}, плюс `url` — готовая страница на домене итд.com,
|
|
1250
|
+
* где ключ виджета действителен. Пригодится встроенному браузеру: открыть её и забрать
|
|
1251
|
+
* токен, ничего не собирая самому.
|
|
1252
|
+
*/
|
|
1253
|
+
captchaPage(options = {}) {
|
|
1254
|
+
return this.http.execute(AUTH_CAPTCHA_PAGE, {
|
|
1255
|
+
path: AUTH_PATHS.captchaPage,
|
|
1256
|
+
skipAuth: true,
|
|
1257
|
+
skipAuthRefresh: true,
|
|
1258
|
+
...options
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1194
1261
|
/** Создаёт короткоживущую сессию QR-входа. */
|
|
1195
1262
|
async startQrLogin(options = {}) {
|
|
1196
1263
|
const cookies = this.#auth.createCookieFlow(false);
|
|
@@ -1204,7 +1271,8 @@ var AuthResource = class extends BaseResource {
|
|
|
1204
1271
|
if (typeof result.qrId === "string" && result.qrId !== "") {
|
|
1205
1272
|
const flow = {
|
|
1206
1273
|
cookies,
|
|
1207
|
-
expiresAt: void 0
|
|
1274
|
+
expiresAt: void 0,
|
|
1275
|
+
captchaRequired: false
|
|
1208
1276
|
};
|
|
1209
1277
|
this.#touchQrFlow(flow, result.expiresIn);
|
|
1210
1278
|
this.#qrFlows.set(result.qrId, flow);
|
|
@@ -1214,20 +1282,21 @@ var AuthResource = class extends BaseResource {
|
|
|
1214
1282
|
/**
|
|
1215
1283
|
* Проверяет QR-сессию и завершает вход после подтверждения на другом устройстве.
|
|
1216
1284
|
*
|
|
1217
|
-
* До подтверждения возвращает промежуточный статус.
|
|
1218
|
-
*
|
|
1219
|
-
* Полученный `accessToken` автоматически сохраняется в клиенте.
|
|
1285
|
+
* До подтверждения возвращает промежуточный статус. Токен капчи добывается, только когда
|
|
1286
|
+
* сервер о ней попросил — статусом `captcha_required` на прошлой проверке, — либо когда
|
|
1287
|
+
* передан в сам вызов. Полученный `accessToken` автоматически сохраняется в клиенте.
|
|
1220
1288
|
*/
|
|
1221
1289
|
async claimQrLogin(input, options = {}) {
|
|
1222
1290
|
const revision = this.#auth.revision();
|
|
1223
1291
|
const flow = this.#qrFlow(input.qrId);
|
|
1224
1292
|
try {
|
|
1225
1293
|
const { captcha, ...rest } = input;
|
|
1294
|
+
const needsCaptcha = captcha !== void 0 || flow.captchaRequired;
|
|
1226
1295
|
const result = await this.http.execute(AUTH_QR_CLAIM, {
|
|
1227
1296
|
path: AUTH_PATHS.qrClaim,
|
|
1228
1297
|
body: {
|
|
1229
1298
|
...rest,
|
|
1230
|
-
...
|
|
1299
|
+
...needsCaptcha ? await this.#auth.captchaBody(captcha) : {}
|
|
1231
1300
|
},
|
|
1232
1301
|
skipAuth: true,
|
|
1233
1302
|
skipAuthRefresh: true,
|
|
@@ -1235,6 +1304,7 @@ var AuthResource = class extends BaseResource {
|
|
|
1235
1304
|
...options
|
|
1236
1305
|
});
|
|
1237
1306
|
this.#touchQrFlow(flow, result.expiresIn);
|
|
1307
|
+
if (result.status === QrLoginStatus.CaptchaRequired) flow.captchaRequired = true;
|
|
1238
1308
|
if (result.status === QrLoginStatus.Authorized) {
|
|
1239
1309
|
await this.#auth.commitAuthFlow(result.accessToken, flow.cookies, revision);
|
|
1240
1310
|
this.#qrFlows.delete(input.qrId);
|
|
@@ -1266,17 +1336,62 @@ var AuthResource = class extends BaseResource {
|
|
|
1266
1336
|
}
|
|
1267
1337
|
}
|
|
1268
1338
|
/**
|
|
1339
|
+
* Отмечает QR-код отсканированным и узнаёт, кого впускают.
|
|
1340
|
+
*
|
|
1341
|
+
* Обратная сторона QR-входа: этот и два соседних метода вызывает устройство, где сессия
|
|
1342
|
+
* уже есть. Показывающая сторона увидит статус `scanned`, но вход ещё не состоится —
|
|
1343
|
+
* его завершают {@link approveQrLogin} или {@link rejectQrLogin}.
|
|
1344
|
+
*
|
|
1345
|
+
* Сервер принимает только сессию, созданную мобильным клиентом. Обычный web access token
|
|
1346
|
+
* отклоняется с кодом `QR_APPROVER_NOT_ALLOWED`, даже если добавить мобильные заголовки
|
|
1347
|
+
* уже после выдачи токена.
|
|
1348
|
+
*
|
|
1349
|
+
* @param input секреты из отсканированного кода — см. {@link QrLoginSecrets}
|
|
1350
|
+
* @returns описание устройства, которое просит вход
|
|
1351
|
+
*/
|
|
1352
|
+
scanQrLogin(input, options = {}) {
|
|
1353
|
+
return this.http.execute(AUTH_QR_SCAN, {
|
|
1354
|
+
path: AUTH_PATHS.qrScan,
|
|
1355
|
+
body: input,
|
|
1356
|
+
...options
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* Подтверждает вход по QR-коду: показавшее код устройство получит access token.
|
|
1361
|
+
*
|
|
1362
|
+
* Осмысленно после {@link scanQrLogin} — человек должен увидеть, что подтверждает.
|
|
1363
|
+
* Требует мобильную сессию, как и `scanQrLogin()`.
|
|
1364
|
+
*/
|
|
1365
|
+
approveQrLogin(input, options = {}) {
|
|
1366
|
+
return this.voidOperation(AUTH_QR_APPROVE, {
|
|
1367
|
+
path: AUTH_PATHS.qrApprove,
|
|
1368
|
+
body: input,
|
|
1369
|
+
...options
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Отклоняет вход по QR-коду: показавшее код устройство получит статус `rejected`.
|
|
1374
|
+
* Требует мобильную сессию, как и `scanQrLogin()`.
|
|
1375
|
+
*/
|
|
1376
|
+
rejectQrLogin(input, options = {}) {
|
|
1377
|
+
return this.voidOperation(AUTH_QR_REJECT, {
|
|
1378
|
+
path: AUTH_PATHS.qrReject,
|
|
1379
|
+
body: input,
|
|
1380
|
+
...options
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1269
1384
|
* Регистрирует аккаунт и запускает подтверждение по коду.
|
|
1270
1385
|
*
|
|
1271
1386
|
* @returns `flowToken`, который нужно передать в {@link verifyOtp}
|
|
1272
1387
|
*/
|
|
1273
|
-
signUp(credentials, options = {}) {
|
|
1388
|
+
async signUp(credentials, options = {}) {
|
|
1274
1389
|
const { captcha, ...rest } = credentials;
|
|
1275
1390
|
return this.http.execute(AUTH_SIGN_UP, {
|
|
1276
1391
|
path: AUTH_PATHS.signUp,
|
|
1277
1392
|
body: {
|
|
1278
1393
|
...rest,
|
|
1279
|
-
...captchaBody(captcha)
|
|
1394
|
+
...await this.#auth.captchaBody(captcha)
|
|
1280
1395
|
},
|
|
1281
1396
|
skipAuth: true,
|
|
1282
1397
|
skipAuthRefresh: true,
|
|
@@ -1291,18 +1406,19 @@ var AuthResource = class extends BaseResource {
|
|
|
1291
1406
|
*
|
|
1292
1407
|
* При успешном входе токен сохраняется в клиенте автоматически.
|
|
1293
1408
|
*
|
|
1294
|
-
* @param credentials email, пароль и
|
|
1409
|
+
* @param credentials email, пароль и токен капчи — см. {@link CredentialsWithCaptcha}
|
|
1295
1410
|
*/
|
|
1296
1411
|
async signIn(credentials, options = {}) {
|
|
1297
1412
|
const { captcha, ...rest } = credentials;
|
|
1298
1413
|
const revision = this.#auth.revision();
|
|
1299
1414
|
const cookies = this.#auth.createCookieFlow(false);
|
|
1415
|
+
const body = {
|
|
1416
|
+
...rest,
|
|
1417
|
+
...await this.#auth.captchaBody(captcha)
|
|
1418
|
+
};
|
|
1300
1419
|
const result = await this.http.execute(AUTH_SIGN_IN, {
|
|
1301
1420
|
path: AUTH_PATHS.signIn,
|
|
1302
|
-
body
|
|
1303
|
-
...rest,
|
|
1304
|
-
...captchaBody(captcha)
|
|
1305
|
-
},
|
|
1421
|
+
body,
|
|
1306
1422
|
skipAuth: true,
|
|
1307
1423
|
skipAuthRefresh: true,
|
|
1308
1424
|
cookieJar: cookies,
|
|
@@ -1430,12 +1546,12 @@ var AuthResource = class extends BaseResource {
|
|
|
1430
1546
|
*
|
|
1431
1547
|
* @returns `flowToken`, который нужно передать в {@link resetPassword}
|
|
1432
1548
|
*/
|
|
1433
|
-
forgotPassword(input, options = {}) {
|
|
1549
|
+
async forgotPassword(input, options = {}) {
|
|
1434
1550
|
return this.http.execute(AUTH_FORGOT_PASSWORD, {
|
|
1435
1551
|
path: AUTH_PATHS.forgotPassword,
|
|
1436
1552
|
body: {
|
|
1437
1553
|
email: input.email,
|
|
1438
|
-
...captchaBody(input.captcha)
|
|
1554
|
+
...await this.#auth.captchaBody(input.captcha)
|
|
1439
1555
|
},
|
|
1440
1556
|
skipAuth: true,
|
|
1441
1557
|
skipAuthRefresh: true,
|
|
@@ -1570,8 +1686,9 @@ function createManagedClient(options, internals) {
|
|
|
1570
1686
|
* const itd = new ItdClient({
|
|
1571
1687
|
* // `auth` не обязателен: когда хранилище уже содержит сессию, токен берётся оттуда,
|
|
1572
1688
|
* // а истёкший продлевается сам. Здесь он нужен на первый запуск.
|
|
1573
|
-
*
|
|
1574
|
-
*
|
|
1689
|
+
* auth: { email, password },
|
|
1690
|
+
* // Капчу входа решает пакет @itd-api/captcha — см. CaptchaSolver.
|
|
1691
|
+
* captcha: createCaptchaSolver(),
|
|
1575
1692
|
* storage: new FileTokenStorage('./.itd-session.json'),
|
|
1576
1693
|
* rateLimit: { concurrency: 4, rps: 8 },
|
|
1577
1694
|
* });
|
|
@@ -2085,6 +2202,8 @@ function assertAccountsActive(accounts, action) {
|
|
|
2085
2202
|
*
|
|
2086
2203
|
* await using accounts = new ItdAccounts({
|
|
2087
2204
|
* storage: new FileMultiTokenStorage('./.itd-sessions.json'),
|
|
2205
|
+
* // Общие опции достаются каждому аккаунту: один источник токена капчи на всех.
|
|
2206
|
+
* captcha: createCaptchaSolver(),
|
|
2088
2207
|
* rateLimit: { concurrency: 4 },
|
|
2089
2208
|
* });
|
|
2090
2209
|
*
|
|
@@ -2092,9 +2211,7 @@ function assertAccountsActive(accounts, action) {
|
|
|
2092
2211
|
* await accounts.restore();
|
|
2093
2212
|
*
|
|
2094
2213
|
* if (!accounts.has('kiow')) {
|
|
2095
|
-
* accounts.addAccount('kiow', {
|
|
2096
|
-
* auth: { email, password, captcha: createCaptchaSolver() },
|
|
2097
|
-
* });
|
|
2214
|
+
* accounts.addAccount('kiow', { auth: { email, password } });
|
|
2098
2215
|
* }
|
|
2099
2216
|
*
|
|
2100
2217
|
* await accounts.account('kiow').posts.create({ content: 'привет' });
|
|
@@ -2173,9 +2290,7 @@ var ItdAccounts = class ItdAccounts {
|
|
|
2173
2290
|
*
|
|
2174
2291
|
* @example
|
|
2175
2292
|
* ```ts
|
|
2176
|
-
* accounts.addAccount('bot', {
|
|
2177
|
-
* auth: { email, password, captcha: createCaptchaSolver() },
|
|
2178
|
-
* });
|
|
2293
|
+
* accounts.addAccount('bot', { auth: { email, password } });
|
|
2179
2294
|
* accounts.addAccount('reader', { auth: '<accessToken>' });
|
|
2180
2295
|
* accounts.addAccount('через-прокси', { fetch: proxyFetch('socks5://…') });
|
|
2181
2296
|
* ```
|
|
@@ -2508,6 +2623,6 @@ function createAccounts(options = {}) {
|
|
|
2508
2623
|
return new ItdAccounts(options);
|
|
2509
2624
|
}
|
|
2510
2625
|
//#endregion
|
|
2511
|
-
export { ALLOWED_MIME_TYPES, AUDIO_MIME_TYPES, AccessType, AttachmentType, BUCKET_LIMITS, CAPTCHA_FIELDS, CaptchaChoice, CaptchaField, CaptchaType, 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, QrLoginStatus, QrLoginStreamStatus, 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 };
|
|
2626
|
+
export { ALLOWED_MIME_TYPES, AUDIO_MIME_TYPES, AccessType, AttachmentType, BUCKET_LIMITS, CAPTCHA_FIELDS, CaptchaChoice, CaptchaField, CaptchaType, 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, QrLocationPrecision, QrLoginStatus, QrLoginStreamStatus, 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 };
|
|
2512
2627
|
|
|
2513
2628
|
//# sourceMappingURL=index.js.map
|