itd-api 0.7.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -27
- package/dist/events/index.cjs +53 -0
- package/dist/events/index.d.cts +3 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/events/index.js +5 -0
- package/dist/index.cjs +234 -200
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -64
- package/dist/index.d.ts +70 -64
- package/dist/index.js +202 -179
- package/dist/index.js.map +1 -1
- package/dist/node/index.cjs +6 -5
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +2 -2
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.js +5 -4
- package/dist/node/index.js.map +1 -1
- package/dist/rest/index.cjs +32 -26
- package/dist/rest/index.cjs.map +1 -1
- package/dist/rest/index.d.cts +9 -7
- package/dist/rest/index.d.ts +9 -7
- package/dist/rest/index.js +19 -21
- package/dist/rest/index.js.map +1 -1
- package/dist/shared/{errors-DfU8M5eS.cjs → errors-BmP3TKoW.cjs} +3 -3
- package/dist/shared/{errors-DfU8M5eS.cjs.map → errors-BmP3TKoW.cjs.map} +1 -1
- package/dist/shared/{errors-Bhrd2fJd.js → errors-GI10kZxk.js} +3 -3
- package/dist/shared/{errors-Bhrd2fJd.js.map → errors-GI10kZxk.js.map} +1 -1
- package/dist/shared/{websocket-BLR8eVJV.js → events-C5wnTXPT.js} +770 -490
- package/dist/shared/events-C5wnTXPT.js.map +1 -0
- package/dist/shared/events-CqtkP65d.d.ts +698 -0
- package/dist/shared/events-DGAWlCaI.d.cts +698 -0
- package/dist/shared/{websocket-C_eI4H2o.cjs → events-FZfnEez0.cjs} +834 -518
- package/dist/shared/events-FZfnEez0.cjs.map +1 -0
- package/dist/shared/{storage-BPJR_k4-.cjs → key-value-store-B5GYVEYZ.cjs} +2 -86
- package/dist/shared/key-value-store-B5GYVEYZ.cjs.map +1 -0
- package/dist/shared/{storage-D86edNCB.js → key-value-store-Bt2PSMxY.js} +3 -69
- package/dist/shared/key-value-store-Bt2PSMxY.js.map +1 -0
- package/dist/shared/key-value-store-COZaNHZS.d.cts +69 -0
- package/dist/shared/key-value-store-COZaNHZS.d.ts +69 -0
- package/dist/shared/{multi-storage-DccjD7Ww.d.ts → multi-storage-BEL7tcKn.d.cts} +3 -2
- package/dist/shared/{multi-storage--yTEqiod.cjs → multi-storage-CDKLa956.cjs} +11 -10
- package/dist/shared/{multi-storage--yTEqiod.cjs.map → multi-storage-CDKLa956.cjs.map} +1 -1
- package/dist/shared/{multi-storage-CjAPB5Kq.d.cts → multi-storage-LkljOSzA.d.ts} +3 -2
- package/dist/shared/{multi-storage-CkvTUC5m.js → multi-storage-SoW05uzO.js} +5 -4
- package/dist/shared/{multi-storage-CkvTUC5m.js.map → multi-storage-SoW05uzO.js.map} +1 -1
- package/dist/shared/{options-Dg5N3r1V.cjs → options-DOJYtoti.cjs} +8 -6
- package/dist/shared/options-DOJYtoti.cjs.map +1 -0
- package/dist/shared/{options-DtATYdLr.js → options-DhgGg7Ms.js} +8 -6
- package/dist/shared/options-DhgGg7Ms.js.map +1 -0
- package/dist/shared/{cookies-DZwFq6kr.cjs → redact-BP00URmQ.cjs} +312 -2
- package/dist/shared/redact-BP00URmQ.cjs.map +1 -0
- package/dist/shared/{cookies-tX2sNwxb.js → redact-Ba5iSVYZ.js} +235 -3
- package/dist/shared/redact-Ba5iSVYZ.js.map +1 -0
- package/dist/shared/{render-CgwKdOzu.d.ts → render-B4qis7Sb.d.ts} +775 -313
- package/dist/shared/{render-DO0F5YSm.d.cts → render-DDwy2Ame.d.cts} +775 -313
- package/dist/shared/{render-mcuELiYi.cjs → render-Dqi8Owqk.cjs} +1464 -662
- package/dist/shared/render-Dqi8Owqk.cjs.map +1 -0
- package/dist/shared/{render-C6HRPs10.js → render-hiWFh2s4.js} +1366 -618
- package/dist/shared/render-hiWFh2s4.js.map +1 -0
- package/dist/shared/storage-BQrtKon9.d.cts +86 -0
- package/dist/shared/storage-CTRGT5wF.d.ts +86 -0
- package/dist/shared/storage-CfHKzVHf.js +71 -0
- package/dist/shared/storage-CfHKzVHf.js.map +1 -0
- package/dist/shared/storage-QNVxzpDB.cjs +88 -0
- package/dist/shared/storage-QNVxzpDB.cjs.map +1 -0
- package/dist/shared/{url-BaMCQpYH.cjs → url-Bsb3xE5U.cjs} +577 -466
- package/dist/shared/url-Bsb3xE5U.cjs.map +1 -0
- package/dist/shared/{url-IU0xN9wX.js → url-DP33mp3s.js} +508 -409
- package/dist/shared/url-DP33mp3s.js.map +1 -0
- package/dist/shared/{url-DTfZ2toq.d.ts → url-XBsMdAcv.d.cts} +1313 -1200
- package/dist/shared/{url-DTfZ2toq.d.cts → url-XBsMdAcv.d.ts} +1313 -1200
- package/dist/web/index.cjs +5 -4
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts +1 -1
- package/dist/web/index.d.ts +1 -1
- package/dist/web/index.js +3 -2
- package/dist/web/index.js.map +1 -1
- package/package.json +16 -15
- package/dist/realtime/index.cjs +0 -165
- package/dist/realtime/index.cjs.map +0 -1
- package/dist/realtime/index.d.cts +0 -51
- package/dist/realtime/index.d.ts +0 -51
- package/dist/realtime/index.js +0 -120
- package/dist/realtime/index.js.map +0 -1
- package/dist/shared/auth-provider-CG8oCQ9F.cjs +0 -108
- package/dist/shared/auth-provider-CG8oCQ9F.cjs.map +0 -1
- package/dist/shared/auth-provider-mYqxsSVa.js +0 -91
- package/dist/shared/auth-provider-mYqxsSVa.js.map +0 -1
- package/dist/shared/cookies-DZwFq6kr.cjs.map +0 -1
- package/dist/shared/cookies-tX2sNwxb.js.map +0 -1
- package/dist/shared/options-Dg5N3r1V.cjs.map +0 -1
- package/dist/shared/options-DtATYdLr.js.map +0 -1
- package/dist/shared/render-C6HRPs10.js.map +0 -1
- package/dist/shared/render-mcuELiYi.cjs.map +0 -1
- package/dist/shared/storage-BPJR_k4-.cjs.map +0 -1
- package/dist/shared/storage-C_eICCep.d.cts +0 -152
- package/dist/shared/storage-C_eICCep.d.ts +0 -152
- package/dist/shared/storage-D86edNCB.js.map +0 -1
- package/dist/shared/url-BaMCQpYH.cjs.map +0 -1
- package/dist/shared/url-IU0xN9wX.js.map +0 -1
- package/dist/shared/websocket-BLR8eVJV.js.map +0 -1
- package/dist/shared/websocket-C_eI4H2o.cjs.map +0 -1
- package/dist/shared/websocket-DF7XIMiX.d.cts +0 -562
- package/dist/shared/websocket-DYKBr8HF.d.ts +0 -562
package/dist/index.js
CHANGED
|
@@ -1,11 +1,64 @@
|
|
|
1
|
-
import { $ as
|
|
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-
|
|
3
|
-
import {
|
|
4
|
-
import { C as
|
|
5
|
-
import {
|
|
6
|
-
import { c as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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-DP33mp3s.js";
|
|
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 { _ as RuntimeMode, c as REFRESH_COOKIE, l as REFRESH_COOKIE_PATH, v as createDeviceId } from "./shared/redact-Ba5iSVYZ.js";
|
|
4
|
+
import { A as toDate, C as ALLOWED_MIME_TYPES, D as comment, E as VIDEO_MIME_TYPES, F as Paginator, I as mapPage, L as createFeatureHost, M as NotificationsResource, N as BaseResource, O as isBuilder, P as PaginationMode, R as ExtensibleOperationCatalog, S as parseMarkdown, T as IMAGE_MIME_TYPES, _ as post, a as createMemoryShopOrderAccessStorage, b as markup, c as ShopConsentKind, d as ShopProductStatus, f as isMyProfile, g as report, h as createResources, i as MemoryShopOrderAccessStorage, j as utcStampToIso, k as createStatusFeature, l as ShopOrderStatus, m as fromUrl, n as renderSpans, o as createShopOrderAccessStorage, p as fromStream, r as createShopFeature, s as statusDays, t as SpanRenderFormat, u as ShopProductCategory, v as poll, w as AUDIO_MIME_TYPES, x as parseHtml, y as autoSpans } from "./shared/render-hiWFh2s4.js";
|
|
5
|
+
import { c as FileTransferMode } from "./shared/options-DhgGg7Ms.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, n as EventRouter, o as EVENT_OPERATIONS, p as EventChannel, r as NotificationEventsClient, s as NotificationEvents, t as EventComposer, u as setNotificationEventsConnectGuard, v as reportListenerError } from "./shared/events-C5wnTXPT.js";
|
|
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-CfHKzVHf.js";
|
|
9
|
+
import { a as scopedTokenStorage, i as isRestorableSession, n as controlledTokenStorage, r as createMultiTokenStorage, t as MemoryMultiTokenStorage } from "./shared/multi-storage-SoW05uzO.js";
|
|
10
|
+
//#region src/notifications-api.ts
|
|
11
|
+
var ClientNotifications = class extends NotificationsResource {
|
|
12
|
+
#createEvents;
|
|
13
|
+
#events;
|
|
14
|
+
constructor(http, createEvents) {
|
|
15
|
+
super(http);
|
|
16
|
+
this.#createEvents = createEvents;
|
|
17
|
+
}
|
|
18
|
+
get events() {
|
|
19
|
+
this.#events ??= this.#createEvents(this);
|
|
20
|
+
return this.#events;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
/** Создаёт API уведомлений полного клиента. @internal */
|
|
24
|
+
function createNotificationsApi(http, createEvents) {
|
|
25
|
+
return new ClientNotifications(http, createEvents);
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/operations/auth.ts
|
|
29
|
+
/** Чем закончился запрос входа. */
|
|
30
|
+
const SignInStatus = Object.freeze({
|
|
31
|
+
Authenticated: "authenticated",
|
|
32
|
+
OtpRequired: "otp_required"
|
|
33
|
+
});
|
|
34
|
+
function requireString(body, key, message) {
|
|
35
|
+
const value = pickString(body, key);
|
|
36
|
+
if (!value) throw new ItdConfigError(message);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
const AUTH_CHECK = passthroughOperation("auth.check");
|
|
40
|
+
const AUTH_SIGN_UP = defineBuiltInOperation("auth.signUp", (body) => requireString(body, "flowToken", "Сервер не вернул flowToken при регистрации"));
|
|
41
|
+
const AUTH_SIGN_IN = defineBuiltInOperation("auth.signIn", (body) => {
|
|
42
|
+
const accessToken = pickString(body, "accessToken");
|
|
43
|
+
return accessToken ? {
|
|
44
|
+
status: SignInStatus.Authenticated,
|
|
45
|
+
accessToken
|
|
46
|
+
} : {
|
|
47
|
+
status: SignInStatus.OtpRequired,
|
|
48
|
+
flowToken: pickString(body, "flowToken")
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
const AUTH_VERIFY_OTP = defineBuiltInOperation("auth.verifyOtp", (body) => requireString(body, "accessToken", "Сервер не вернул accessToken после подтверждения кода"));
|
|
52
|
+
const AUTH_REFRESH = defineBuiltInOperation("auth.refresh", (body) => pickString(body, "accessToken"));
|
|
53
|
+
const AUTH_RESEND_OTP = voidOperation("auth.resendOtp");
|
|
54
|
+
const AUTH_LOGOUT = voidOperation("auth.logout");
|
|
55
|
+
const AUTH_FORGOT_PASSWORD = defineBuiltInOperation("auth.forgotPassword", (body) => requireString(body, "flowToken", "Сервер не вернул flowToken при запросе сброса пароля"));
|
|
56
|
+
const AUTH_RESET_PASSWORD = voidOperation("auth.resetPassword");
|
|
57
|
+
const AUTH_CHANGE_PASSWORD = voidOperation("auth.changePassword");
|
|
58
|
+
const AUTH_SESSIONS = defineBuiltInOperation("auth.sessions", (body) => pickArray(body, "sessions"));
|
|
59
|
+
const AUTH_REVOKE_SESSION = voidOperation("auth.revokeSession");
|
|
60
|
+
const AUTH_REVOKE_OTHER_SESSIONS = voidOperation("auth.revokeOtherSessions");
|
|
61
|
+
//#endregion
|
|
9
62
|
//#region src/session/jwt.ts
|
|
10
63
|
/**
|
|
11
64
|
* Разбирает строку base64url.
|
|
@@ -122,7 +175,7 @@ function resolveSessionConfig(options, runtime) {
|
|
|
122
175
|
* достижимой из любой сборки, включая анонимную.
|
|
123
176
|
*/
|
|
124
177
|
function createItdAuth(options, deps) {
|
|
125
|
-
return new AuthManager(resolveSessionConfig(options, deps.config), deps.
|
|
178
|
+
return new AuthManager(resolveSessionConfig(options, deps.config), deps.http, deps.cookies, { onAccountChange: deps.onAccountChange });
|
|
126
179
|
}
|
|
127
180
|
/** Пути эндпоинтов авторизации. */
|
|
128
181
|
const AUTH_PATHS = {
|
|
@@ -157,11 +210,6 @@ const AUTH_PATHS = {
|
|
|
157
210
|
* ```
|
|
158
211
|
*/
|
|
159
212
|
const TURNSTILE_SITE_KEY = "0x4AAAAAACHhxczw6fJGwPBg";
|
|
160
|
-
function readAccessToken(payload) {
|
|
161
|
-
if (typeof payload !== "object" || payload === null) return void 0;
|
|
162
|
-
const token = payload.accessToken;
|
|
163
|
-
return typeof token === "string" && token.length > 0 ? token : void 0;
|
|
164
|
-
}
|
|
165
213
|
/** Убирает поле, которое сохраняли промежуточные версии с поддержкой `getUserId()`. */
|
|
166
214
|
function withoutLegacyUserId(session) {
|
|
167
215
|
const clean = { ...session };
|
|
@@ -183,7 +231,7 @@ function nextAuthScope() {
|
|
|
183
231
|
*/
|
|
184
232
|
var AuthManager = class {
|
|
185
233
|
#config;
|
|
186
|
-
#
|
|
234
|
+
#http;
|
|
187
235
|
#jar;
|
|
188
236
|
#emitter;
|
|
189
237
|
#hooks;
|
|
@@ -222,9 +270,9 @@ var AuthManager = class {
|
|
|
222
270
|
* воскресить уже очищенную сессию поверх `signOut`.
|
|
223
271
|
*/
|
|
224
272
|
#authEpoch = 0;
|
|
225
|
-
constructor(config,
|
|
273
|
+
constructor(config, http, jar, hooks = {}) {
|
|
226
274
|
this.#config = config;
|
|
227
|
-
this.#
|
|
275
|
+
this.#http = http;
|
|
228
276
|
this.#jar = jar;
|
|
229
277
|
this.#hooks = hooks;
|
|
230
278
|
this.#emitter = new Emitter((error) => reportListenerError(config.logger, "авторизации", error));
|
|
@@ -237,7 +285,7 @@ var AuthManager = class {
|
|
|
237
285
|
get once() {
|
|
238
286
|
return this.#emitter.once.bind(this.#emitter);
|
|
239
287
|
}
|
|
240
|
-
/** Снимает
|
|
288
|
+
/** Снимает служебные подписки при окончательном освобождении владельца. @internal */
|
|
241
289
|
dispose() {
|
|
242
290
|
this.#invalidateInFlight();
|
|
243
291
|
this.#emitter.removeAllListeners();
|
|
@@ -293,7 +341,7 @@ var AuthManager = class {
|
|
|
293
341
|
this.#externalIdentity = current;
|
|
294
342
|
return token;
|
|
295
343
|
}
|
|
296
|
-
/**
|
|
344
|
+
/** Обновляет резервный идентификатор и закрывает события при смене аккаунта. */
|
|
297
345
|
#transitionAuth(accessToken, rotateFallback = true) {
|
|
298
346
|
const knownPrevious = this.#session !== void 0;
|
|
299
347
|
const previous = this.#identityForToken(this.#session?.accessToken);
|
|
@@ -565,13 +613,11 @@ var AuthManager = class {
|
|
|
565
613
|
if (!this.#hasRefreshSession()) return this.#reloginOrNull();
|
|
566
614
|
const epoch = this.#authEpoch;
|
|
567
615
|
try {
|
|
568
|
-
const accessToken =
|
|
569
|
-
operationId: "auth.refresh",
|
|
570
|
-
method: operationMethod("auth.refresh"),
|
|
616
|
+
const accessToken = await this.#http.execute(AUTH_REFRESH, {
|
|
571
617
|
path: AUTH_PATHS.refresh,
|
|
572
618
|
skipAuth: true,
|
|
573
619
|
skipAuthRefresh: true
|
|
574
|
-
})
|
|
620
|
+
});
|
|
575
621
|
if (!accessToken) return this.#reloginOrNull();
|
|
576
622
|
if (this.#authEpoch !== epoch) return this.#session?.accessToken ?? null;
|
|
577
623
|
const rotated = this.#jar.getValue(REFRESH_COOKIE, this.#config.baseUrl + REFRESH_COOKIE_PATH);
|
|
@@ -638,9 +684,7 @@ var AuthManager = class {
|
|
|
638
684
|
}
|
|
639
685
|
async #performSignIn(credentials) {
|
|
640
686
|
const turnstileToken = await this.#resolveTurnstileToken(credentials);
|
|
641
|
-
const
|
|
642
|
-
operationId: "auth.signIn",
|
|
643
|
-
method: operationMethod("auth.signIn"),
|
|
687
|
+
const result = await this.#http.execute(AUTH_SIGN_IN, {
|
|
644
688
|
path: AUTH_PATHS.signIn,
|
|
645
689
|
body: {
|
|
646
690
|
email: credentials.email,
|
|
@@ -649,8 +693,9 @@ var AuthManager = class {
|
|
|
649
693
|
},
|
|
650
694
|
skipAuth: true,
|
|
651
695
|
skipAuthRefresh: true
|
|
652
|
-
})
|
|
653
|
-
if (
|
|
696
|
+
});
|
|
697
|
+
if (result.status !== SignInStatus.Authenticated) throw new ItdConfigError("Вход по email и паролю требует подтверждения кодом из письма. Автоматический вход невозможен: воспользуйтесь itd.auth.signInWithOtp() и передайте полученный accessToken в конфигурацию клиента.");
|
|
698
|
+
const accessToken = result.accessToken;
|
|
654
699
|
this.#invalidateInFlight();
|
|
655
700
|
this.#transitionAuth(accessToken);
|
|
656
701
|
await this.#saveSession({
|
|
@@ -664,11 +709,12 @@ var AuthManager = class {
|
|
|
664
709
|
};
|
|
665
710
|
//#endregion
|
|
666
711
|
//#region src/resources/auth.ts
|
|
667
|
-
/**
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
712
|
+
/**
|
|
713
|
+
* Результат входа.
|
|
714
|
+
*
|
|
715
|
+
* Сервер может как сразу выдать токен, так и потребовать код подтверждения — размеченное
|
|
716
|
+
* объединение делает оба случая явными.
|
|
717
|
+
*/
|
|
672
718
|
/**
|
|
673
719
|
* Авторизация, сессии и пароли.
|
|
674
720
|
*
|
|
@@ -686,7 +732,7 @@ var AuthResource = class extends BaseResource {
|
|
|
686
732
|
* Без авторизации возвращает `authenticated: false` и `user: null`.
|
|
687
733
|
*/
|
|
688
734
|
check(options = {}) {
|
|
689
|
-
return this.http.
|
|
735
|
+
return this.http.execute(AUTH_CHECK, {
|
|
690
736
|
path: "/api/profile",
|
|
691
737
|
...options
|
|
692
738
|
});
|
|
@@ -696,17 +742,14 @@ var AuthResource = class extends BaseResource {
|
|
|
696
742
|
*
|
|
697
743
|
* @returns `flowToken`, который нужно передать в {@link verifyOtp}
|
|
698
744
|
*/
|
|
699
|
-
|
|
700
|
-
|
|
745
|
+
signUp(credentials, options = {}) {
|
|
746
|
+
return this.http.execute(AUTH_SIGN_UP, {
|
|
701
747
|
path: AUTH_PATHS.signUp,
|
|
702
748
|
body: credentials,
|
|
703
749
|
skipAuth: true,
|
|
704
750
|
skipAuthRefresh: true,
|
|
705
751
|
...options
|
|
706
752
|
});
|
|
707
|
-
const flowToken = pickString(body, "flowToken");
|
|
708
|
-
if (!flowToken) throw new ItdConfigError("Сервер не вернул flowToken при регистрации");
|
|
709
|
-
return flowToken;
|
|
710
753
|
}
|
|
711
754
|
/**
|
|
712
755
|
* Выполняет вход.
|
|
@@ -719,25 +762,15 @@ var AuthResource = class extends BaseResource {
|
|
|
719
762
|
* @param credentials email, пароль и обязательный токен капчи — см. {@link CaptchaCredentials}
|
|
720
763
|
*/
|
|
721
764
|
async signIn(credentials, options = {}) {
|
|
722
|
-
const
|
|
765
|
+
const result = await this.http.execute(AUTH_SIGN_IN, {
|
|
723
766
|
path: AUTH_PATHS.signIn,
|
|
724
767
|
body: credentials,
|
|
725
768
|
skipAuth: true,
|
|
726
769
|
skipAuthRefresh: true,
|
|
727
770
|
...options
|
|
728
771
|
});
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
await this.#auth.setAccessToken(accessToken);
|
|
732
|
-
return {
|
|
733
|
-
status: SignInStatus.Authenticated,
|
|
734
|
-
accessToken
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
return {
|
|
738
|
-
status: SignInStatus.OtpRequired,
|
|
739
|
-
flowToken: pickString(body, "flowToken")
|
|
740
|
-
};
|
|
772
|
+
if (result.status === SignInStatus.Authenticated) await this.#auth.setAccessToken(result.accessToken);
|
|
773
|
+
return result;
|
|
741
774
|
}
|
|
742
775
|
/**
|
|
743
776
|
* Подтверждает вход кодом из письма.
|
|
@@ -745,21 +778,19 @@ var AuthResource = class extends BaseResource {
|
|
|
745
778
|
* Полученный токен сохраняется в клиенте автоматически.
|
|
746
779
|
*/
|
|
747
780
|
async verifyOtp(input, options = {}) {
|
|
748
|
-
const
|
|
781
|
+
const accessToken = await this.http.execute(AUTH_VERIFY_OTP, {
|
|
749
782
|
path: AUTH_PATHS.verifyOtp,
|
|
750
783
|
body: input,
|
|
751
784
|
skipAuth: true,
|
|
752
785
|
skipAuthRefresh: true,
|
|
753
786
|
...options
|
|
754
787
|
});
|
|
755
|
-
const accessToken = pickString(body, "accessToken");
|
|
756
|
-
if (!accessToken) throw new ItdConfigError("Сервер не вернул accessToken после подтверждения кода");
|
|
757
788
|
await this.#auth.setAccessToken(accessToken);
|
|
758
789
|
return accessToken;
|
|
759
790
|
}
|
|
760
791
|
/** Отправляет код подтверждения повторно. */
|
|
761
792
|
resendOtp(input, options = {}) {
|
|
762
|
-
return this.
|
|
793
|
+
return this.voidOperation(AUTH_RESEND_OTP, {
|
|
763
794
|
path: AUTH_PATHS.resendOtp,
|
|
764
795
|
body: input,
|
|
765
796
|
skipAuth: true,
|
|
@@ -828,7 +859,7 @@ var AuthResource = class extends BaseResource {
|
|
|
828
859
|
}
|
|
829
860
|
/** Завершает текущую сессию на сервере и очищает локальную. */
|
|
830
861
|
async logout(options = {}) {
|
|
831
|
-
await this.
|
|
862
|
+
await this.voidOperation(AUTH_LOGOUT, {
|
|
832
863
|
path: AUTH_PATHS.logout,
|
|
833
864
|
skipAuthRefresh: true,
|
|
834
865
|
...options
|
|
@@ -856,17 +887,14 @@ var AuthResource = class extends BaseResource {
|
|
|
856
887
|
*
|
|
857
888
|
* @returns `flowToken`, который нужно передать в {@link resetPassword}
|
|
858
889
|
*/
|
|
859
|
-
|
|
860
|
-
|
|
890
|
+
forgotPassword(input, options = {}) {
|
|
891
|
+
return this.http.execute(AUTH_FORGOT_PASSWORD, {
|
|
861
892
|
path: AUTH_PATHS.forgotPassword,
|
|
862
893
|
body: input,
|
|
863
894
|
skipAuth: true,
|
|
864
895
|
skipAuthRefresh: true,
|
|
865
896
|
...options
|
|
866
897
|
});
|
|
867
|
-
const flowToken = pickString(body, "flowToken");
|
|
868
|
-
if (!flowToken) throw new ItdConfigError("Сервер не вернул flowToken при запросе сброса пароля");
|
|
869
|
-
return flowToken;
|
|
870
898
|
}
|
|
871
899
|
/**
|
|
872
900
|
* Устанавливает новый пароль по коду из письма.
|
|
@@ -875,7 +903,7 @@ var AuthResource = class extends BaseResource {
|
|
|
875
903
|
* при нехватке любого отвечает `422`.
|
|
876
904
|
*/
|
|
877
905
|
resetPassword(input, options = {}) {
|
|
878
|
-
return this.
|
|
906
|
+
return this.voidOperation(AUTH_RESET_PASSWORD, {
|
|
879
907
|
path: AUTH_PATHS.resetPassword,
|
|
880
908
|
body: input,
|
|
881
909
|
skipAuth: true,
|
|
@@ -923,7 +951,7 @@ var AuthResource = class extends BaseResource {
|
|
|
923
951
|
* ```
|
|
924
952
|
*/
|
|
925
953
|
changePassword(input, options = {}) {
|
|
926
|
-
return this.
|
|
954
|
+
return this.voidOperation(AUTH_CHANGE_PASSWORD, {
|
|
927
955
|
path: AUTH_PATHS.changePassword,
|
|
928
956
|
body: {
|
|
929
957
|
currentPassword: input.currentPassword,
|
|
@@ -933,23 +961,22 @@ var AuthResource = class extends BaseResource {
|
|
|
933
961
|
});
|
|
934
962
|
}
|
|
935
963
|
/** Загружает список активных сессий. У текущей поле `isCurrent` равно `true`. */
|
|
936
|
-
|
|
937
|
-
|
|
964
|
+
sessions(options = {}) {
|
|
965
|
+
return this.http.execute(AUTH_SESSIONS, {
|
|
938
966
|
path: AUTH_PATHS.sessions,
|
|
939
967
|
...options
|
|
940
968
|
});
|
|
941
|
-
return pickArray(body, "sessions");
|
|
942
969
|
}
|
|
943
970
|
/** Завершает указанную сессию. */
|
|
944
971
|
revokeSession(sessionId, options = {}) {
|
|
945
|
-
return this.
|
|
972
|
+
return this.voidOperation(AUTH_REVOKE_SESSION, {
|
|
946
973
|
path: `${AUTH_PATHS.sessions}/${encodeURIComponent(sessionId)}`,
|
|
947
974
|
...options
|
|
948
975
|
});
|
|
949
976
|
}
|
|
950
977
|
/** Завершает все сессии, кроме текущей. */
|
|
951
978
|
revokeOtherSessions(options = {}) {
|
|
952
|
-
return this.
|
|
979
|
+
return this.voidOperation(AUTH_REVOKE_OTHER_SESSIONS, {
|
|
953
980
|
path: AUTH_PATHS.sessions,
|
|
954
981
|
...options
|
|
955
982
|
});
|
|
@@ -1013,8 +1040,6 @@ function createManagedClient(options, internals) {
|
|
|
1013
1040
|
var ItdClient = class ItdClient {
|
|
1014
1041
|
#runtime;
|
|
1015
1042
|
#features;
|
|
1016
|
-
/** Порождённые потоки уведомлений — чтобы `close()` мог закрыть их разом. */
|
|
1017
|
-
#streams = /* @__PURE__ */ new Set();
|
|
1018
1043
|
/** Общий результат терминальной очистки для идемпотентных повторных вызовов. */
|
|
1019
1044
|
#disposePromise;
|
|
1020
1045
|
/** Ресурсы конвейера запросов; создаются при первом обращении. */
|
|
@@ -1074,34 +1099,76 @@ var ItdClient = class ItdClient {
|
|
|
1074
1099
|
get telemetry() {
|
|
1075
1100
|
return this.#resources.telemetry;
|
|
1076
1101
|
}
|
|
1102
|
+
/** Каталог, доставка и заказы магазина ИТД. */
|
|
1103
|
+
get shop() {
|
|
1104
|
+
return this.#resources.shop;
|
|
1105
|
+
}
|
|
1077
1106
|
constructor(options = {}) {
|
|
1107
|
+
const notificationEventsOptions = resolveNotificationEventsOptions(options.events?.notifications);
|
|
1078
1108
|
const internals = CLIENT_INTERNALS.get(options) ?? {};
|
|
1079
1109
|
const catalog = new ExtensibleOperationCatalog(ITD_CATALOG);
|
|
1110
|
+
let featureHost;
|
|
1080
1111
|
this.#runtime = createClientRuntime(options, {
|
|
1081
1112
|
catalog,
|
|
1082
1113
|
queues: internals.queues,
|
|
1083
1114
|
assertActive: (action) => assertClientActive(this, action),
|
|
1084
1115
|
auth: (deps) => createItdAuth(options, {
|
|
1085
1116
|
...deps,
|
|
1086
|
-
onAccountChange: () =>
|
|
1117
|
+
onAccountChange: () => featureHost?.stop()
|
|
1087
1118
|
})
|
|
1088
1119
|
});
|
|
1089
|
-
this.#features =
|
|
1090
|
-
http: this.#runtime.http,
|
|
1091
|
-
services: this.#runtime.services,
|
|
1092
|
-
serviceOverrides: this.#runtime.config.services,
|
|
1120
|
+
this.#features = featureHost = createFeatureHost(this.#runtime, {
|
|
1093
1121
|
catalog,
|
|
1094
|
-
|
|
1095
|
-
clock: this.#runtime.config.clock,
|
|
1096
|
-
logger: this.#runtime.config.logger,
|
|
1097
|
-
assertActive: (action) => assertClientActive(this, action),
|
|
1098
|
-
registerBucket: (name, definition) => this.#runtime.registerRateLimitBucket(name, definition)
|
|
1122
|
+
assertActive: (action) => assertClientActive(this, action)
|
|
1099
1123
|
});
|
|
1100
1124
|
const status = this.#features.install(createStatusFeature());
|
|
1101
1125
|
this.#resources = createResources({
|
|
1102
1126
|
http: this.#runtime.http,
|
|
1103
|
-
|
|
1104
|
-
status
|
|
1127
|
+
files: this.#features.files,
|
|
1128
|
+
status,
|
|
1129
|
+
createNotifications: (http) => createNotificationsApi(http, (notifications) => {
|
|
1130
|
+
let events;
|
|
1131
|
+
let lifecycleGeneration = 0;
|
|
1132
|
+
let lifecycleActive = false;
|
|
1133
|
+
let unregister;
|
|
1134
|
+
const releaseAfterDrain = (generation) => {
|
|
1135
|
+
events.drain().then(() => {
|
|
1136
|
+
if (lifecycleActive || generation !== lifecycleGeneration) return;
|
|
1137
|
+
unregister?.();
|
|
1138
|
+
unregister = void 0;
|
|
1139
|
+
});
|
|
1140
|
+
};
|
|
1141
|
+
events = new NotificationEvents({
|
|
1142
|
+
connection: this.#runtime.connection(),
|
|
1143
|
+
request: ({ operationId, path, query, signal }) => this.#runtime.http.execute(EVENT_OPERATIONS[operationId], {
|
|
1144
|
+
path,
|
|
1145
|
+
query,
|
|
1146
|
+
signal
|
|
1147
|
+
}),
|
|
1148
|
+
getAuthIdentity: () => this.#runtime.auth.getCurrentAuthIdentity(),
|
|
1149
|
+
getAuthScope: () => this.#runtime.auth.getAuthScope(),
|
|
1150
|
+
fetchUnreadCount: (signal) => notifications.count({ signal }),
|
|
1151
|
+
onConnect: () => {
|
|
1152
|
+
lifecycleGeneration += 1;
|
|
1153
|
+
unregister ??= this.#features.manage({
|
|
1154
|
+
kind: `notifications:${events.transport}`,
|
|
1155
|
+
stop: () => {
|
|
1156
|
+
if (lifecycleActive) events.disconnect();
|
|
1157
|
+
},
|
|
1158
|
+
drain: () => events.drain()
|
|
1159
|
+
});
|
|
1160
|
+
lifecycleActive = true;
|
|
1161
|
+
},
|
|
1162
|
+
onClose: () => {
|
|
1163
|
+
lifecycleActive = false;
|
|
1164
|
+
releaseAfterDrain(++lifecycleGeneration);
|
|
1165
|
+
},
|
|
1166
|
+
logger: this.#runtime.config.logger,
|
|
1167
|
+
clock: this.#runtime.config.clock
|
|
1168
|
+
}, notificationEventsOptions);
|
|
1169
|
+
setNotificationEventsConnectGuard(events, () => assertClientActive(this, "подключить канал событий уведомлений"));
|
|
1170
|
+
return events;
|
|
1171
|
+
})
|
|
1105
1172
|
});
|
|
1106
1173
|
CLIENT_PLUGIN_REGISTRIES.set(this, this.#runtime.plugins);
|
|
1107
1174
|
}
|
|
@@ -1145,19 +1212,19 @@ var ItdClient = class ItdClient {
|
|
|
1145
1212
|
return this.#runtime.rateLimitState();
|
|
1146
1213
|
}
|
|
1147
1214
|
/**
|
|
1148
|
-
* Устанавливает предметный модуль
|
|
1215
|
+
* Устанавливает предметный модуль с общей сессией и обработкой запросов клиента.
|
|
1149
1216
|
*
|
|
1150
|
-
* Сервисы, операции и бакеты
|
|
1151
|
-
*
|
|
1217
|
+
* Сервисы, операции и бакеты регистрируются до синхронного `setup()`. Возвращает
|
|
1218
|
+
* типизированный API модуля.
|
|
1152
1219
|
*/
|
|
1153
1220
|
install(feature) {
|
|
1154
1221
|
return this.#features.install(feature);
|
|
1155
1222
|
}
|
|
1156
1223
|
/**
|
|
1157
|
-
* Устанавливает
|
|
1224
|
+
* Устанавливает модуль и добавляет его API в свойство клиента только для чтения.
|
|
1158
1225
|
*
|
|
1159
1226
|
* Возвращаемое пересечение сохраняет тип уже подключённых свойств, поэтому вызовы можно
|
|
1160
|
-
* объединять в цепочку: `new ItdClient().withFeature('
|
|
1227
|
+
* объединять в цепочку: `new ItdClient().withFeature('pixelBattle', pixelBattleFeature)`.
|
|
1161
1228
|
*/
|
|
1162
1229
|
withFeature(key, feature) {
|
|
1163
1230
|
assertClientActive(this, "установить feature");
|
|
@@ -1173,20 +1240,18 @@ var ItdClient = class ItdClient {
|
|
|
1173
1240
|
});
|
|
1174
1241
|
return this;
|
|
1175
1242
|
}
|
|
1176
|
-
/** Имена установленных
|
|
1243
|
+
/** Имена установленных модулей в порядке установки. */
|
|
1177
1244
|
featureNames() {
|
|
1178
1245
|
return this.#features.names();
|
|
1179
1246
|
}
|
|
1180
|
-
/** Установлен ли
|
|
1247
|
+
/** Установлен ли модуль с таким именем. */
|
|
1181
1248
|
hasFeature(name) {
|
|
1182
1249
|
return this.#features.has(name);
|
|
1183
1250
|
}
|
|
1184
1251
|
/**
|
|
1185
1252
|
* Подключает плагин.
|
|
1186
1253
|
*
|
|
1187
|
-
* Плагин может
|
|
1188
|
-
* транспортной попытки. Оба контракта охватывают все методы клиента. Подключать плагин
|
|
1189
|
-
* можно в любой момент, но обычно это делают сразу после создания клиента.
|
|
1254
|
+
* Плагин может регистрировать обёртки операций и перехватчики сетевых попыток.
|
|
1190
1255
|
*
|
|
1191
1256
|
* @throws {ItdConfigError} если плагин задан неверно или уже подключён
|
|
1192
1257
|
* @throws {ItdStateError} если клиент уже освобождён через {@link dispose}
|
|
@@ -1289,85 +1354,27 @@ var ItdClient = class ItdClient {
|
|
|
1289
1354
|
return this.#runtime.auth.on(event, listener);
|
|
1290
1355
|
}
|
|
1291
1356
|
/**
|
|
1292
|
-
* Создаёт поток уведомлений в реальном времени.
|
|
1293
|
-
*
|
|
1294
|
-
* Каждый вызов даёт новый независимый поток; обычно он нужен один на приложение.
|
|
1295
|
-
* Соединение поднимается методом `connect()` и держится само. Замена авторизации на токен
|
|
1296
|
-
* другого пользователя завершает все потоки клиента; смена только сессии их не затрагивает.
|
|
1297
|
-
*
|
|
1298
|
-
* @example
|
|
1299
|
-
* ```ts
|
|
1300
|
-
* import { NotificationType } from 'itd-api';
|
|
1301
|
-
*
|
|
1302
|
-
* const stream = itd.realtime();
|
|
1303
|
-
*
|
|
1304
|
-
* stream.onNotification(NotificationType.PostComment, async ({ update }) => {
|
|
1305
|
-
* await handleComment(update.data.notification);
|
|
1306
|
-
* });
|
|
1307
|
-
* stream.on('unreadCount', (count) => setBadge(count));
|
|
1308
|
-
*
|
|
1309
|
-
* await stream.connect();
|
|
1310
|
-
* ```
|
|
1311
|
-
*
|
|
1312
|
-
* @throws {ItdStateError} если клиент уже освобождён через {@link dispose}
|
|
1313
|
-
*/
|
|
1314
|
-
realtime(options = {}) {
|
|
1315
|
-
assertClientActive(this, "создать realtime-поток");
|
|
1316
|
-
let stream;
|
|
1317
|
-
stream = new ItdRealtime({
|
|
1318
|
-
baseUrl: this.#runtime.config.baseUrl,
|
|
1319
|
-
fetch: this.#runtime.config.fetch,
|
|
1320
|
-
request: ({ operationId, path, query, signal }) => this.#runtime.http.operation(operationId, {
|
|
1321
|
-
path,
|
|
1322
|
-
query,
|
|
1323
|
-
signal
|
|
1324
|
-
}),
|
|
1325
|
-
baseHeaders: (url) => this.#runtime.platformHeaders(url),
|
|
1326
|
-
getAuthIdentity: () => this.#runtime.auth.getCurrentAuthIdentity(),
|
|
1327
|
-
getAuthScope: () => this.#runtime.auth.getAuthScope(),
|
|
1328
|
-
getToken: () => this.#runtime.auth.token(),
|
|
1329
|
-
refresh: () => this.#runtime.auth.onUnauthorized(),
|
|
1330
|
-
fetchUnreadCount: () => this.notifications.count(),
|
|
1331
|
-
onConnect: () => this.#streams.add(stream),
|
|
1332
|
-
onClose: () => this.#streams.delete(stream),
|
|
1333
|
-
logger: this.#runtime.config.logger,
|
|
1334
|
-
clock: this.#runtime.config.clock
|
|
1335
|
-
}, options);
|
|
1336
|
-
setRealtimeConnectGuard(stream, () => assertClientActive(this, "подключить realtime-поток"));
|
|
1337
|
-
this.#streams.add(stream);
|
|
1338
|
-
return stream;
|
|
1339
|
-
}
|
|
1340
|
-
/**
|
|
1341
1357
|
* Освобождает ресурсы клиента: закрывает все потоки уведомлений, отправляет открытые
|
|
1342
1358
|
* накопители {@link telemetry}, затем останавливает очередь запросов.
|
|
1343
1359
|
*
|
|
1344
|
-
* Метод дожидается активных обработчиков
|
|
1345
|
-
*
|
|
1346
|
-
* повторным `connect()`.
|
|
1360
|
+
* Метод дожидается завершения активных обработчиков и освобождения ресурсов остановленных
|
|
1361
|
+
* событийных соединений, но не дольше `shutdownTimeout`. После вызова клиентом можно
|
|
1362
|
+
* пользоваться снова; ранее созданный поток можно запустить повторным `connect()`.
|
|
1347
1363
|
*
|
|
1348
1364
|
* Общая очередь, полученная от {@link ItdAccounts}, не останавливается: её гасит сам
|
|
1349
1365
|
* контейнер, когда закрывает все аккаунты разом.
|
|
1350
1366
|
*
|
|
1351
1367
|
* Терминальное освобождение — это {@link dispose}.
|
|
1352
1368
|
*
|
|
1353
|
-
* @throws {ItdStateError} если
|
|
1369
|
+
* @throws {ItdStateError} если остановка событийного соединения не завершилась за отведённый срок
|
|
1354
1370
|
*/
|
|
1355
1371
|
async close() {
|
|
1356
1372
|
return this.#close(false);
|
|
1357
1373
|
}
|
|
1358
1374
|
async #close(disposeCleanup) {
|
|
1359
|
-
const streams = this.#disconnectStreams();
|
|
1360
1375
|
if (disposeCleanup) this.#resources.prepareTelemetryClose();
|
|
1361
|
-
const { shutdownTimeout, clock } = this.#runtime.config;
|
|
1362
|
-
const deadline = createDeadline(shutdownTimeout, clock);
|
|
1363
|
-
let stuck = [];
|
|
1364
1376
|
const errors = [];
|
|
1365
1377
|
try {
|
|
1366
|
-
try {
|
|
1367
|
-
stuck = (await Promise.all(streams.map(async (stream) => await deadline.wait(stream.drain()) ? void 0 : stream))).filter((stream) => stream !== void 0);
|
|
1368
|
-
} catch (error) {
|
|
1369
|
-
errors.push(error);
|
|
1370
|
-
}
|
|
1371
1378
|
try {
|
|
1372
1379
|
await this.#features.close();
|
|
1373
1380
|
} catch (error) {
|
|
@@ -1379,10 +1386,8 @@ var ItdClient = class ItdClient {
|
|
|
1379
1386
|
errors.push(error);
|
|
1380
1387
|
}
|
|
1381
1388
|
} finally {
|
|
1382
|
-
deadline.cancel();
|
|
1383
1389
|
this.#runtime.close();
|
|
1384
1390
|
}
|
|
1385
|
-
if (stuck.length > 0) errors.push(new ItdStateError(`обработчики потоков (${stuck.map((stream) => stream.transport).join(", ")}) не завершились за ${shutdownTimeout} мс; ожидание прекращено`));
|
|
1386
1391
|
if (errors.length === 1) throw errors[0];
|
|
1387
1392
|
if (errors.length > 1) throw new AggregateError(errors, "Не удалось закрыть клиент");
|
|
1388
1393
|
}
|
|
@@ -1392,10 +1397,11 @@ var ItdClient = class ItdClient {
|
|
|
1392
1397
|
*
|
|
1393
1398
|
* Терминальное состояние устанавливается сразу при первом вызове. После этого новые
|
|
1394
1399
|
* запросы, подключение плагинов, регистрация сервисов и создание или повторный запуск
|
|
1395
|
-
*
|
|
1400
|
+
* событийных каналов завершаются с {@link ItdStateError}. Повторные вызовы возвращают
|
|
1396
1401
|
* тот же результат очистки.
|
|
1397
1402
|
*
|
|
1398
|
-
* Ожидание
|
|
1403
|
+
* Ожидание остановки событийных соединений и операций плагинов ограничено
|
|
1404
|
+
* `shutdownTimeout`.
|
|
1399
1405
|
*
|
|
1400
1406
|
* @example
|
|
1401
1407
|
* ```ts
|
|
@@ -1429,13 +1435,6 @@ var ItdClient = class ItdClient {
|
|
|
1429
1435
|
}
|
|
1430
1436
|
if (errors.length > 0) throw new AggregateError(errors, "Не удалось освободить клиент");
|
|
1431
1437
|
}
|
|
1432
|
-
/** Завершает потоки до того, как запросы начнут использовать другой аккаунт. */
|
|
1433
|
-
#disconnectStreams() {
|
|
1434
|
-
const streams = [...this.#streams];
|
|
1435
|
-
for (const stream of streams) stream.disconnect();
|
|
1436
|
-
this.#streams.clear();
|
|
1437
|
-
return streams;
|
|
1438
|
-
}
|
|
1439
1438
|
/** Позволяет использовать клиент с `await using`. */
|
|
1440
1439
|
[Symbol.asyncDispose]() {
|
|
1441
1440
|
return this.dispose();
|
|
@@ -1501,6 +1500,25 @@ function createClient(options = {}) {
|
|
|
1501
1500
|
function validateAccountName(name) {
|
|
1502
1501
|
if (typeof name !== "string" || name.trim() === "") throw new ItdConfigError("имя аккаунта должно быть непустой строкой");
|
|
1503
1502
|
}
|
|
1503
|
+
/** Проверяет фабрики до создания первого аккаунта. */
|
|
1504
|
+
function resolveAccountFeatures(features) {
|
|
1505
|
+
if (features === void 0) return [];
|
|
1506
|
+
if (!Array.isArray(features)) throw new ItdConfigError("features должен быть массивом фабрик подключаемых модулей");
|
|
1507
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1508
|
+
return features.map((feature, index) => {
|
|
1509
|
+
if (typeof feature !== "object" || feature === null || typeof feature.create !== "function") throw new ItdConfigError(`features[${index}] должен предоставлять create()`);
|
|
1510
|
+
if (feature.key !== void 0) {
|
|
1511
|
+
if (typeof feature.key !== "string" || feature.key.trim() === "" || feature.key !== feature.key.trim()) throw new ItdConfigError(`features[${index}].key должен быть непустой строкой без пробелов`);
|
|
1512
|
+
if (keys.has(feature.key)) throw new ItdConfigError(`Свойство подключаемого модуля «${feature.key}» объявлено повторно`);
|
|
1513
|
+
keys.add(feature.key);
|
|
1514
|
+
}
|
|
1515
|
+
const create = feature.create.bind(feature);
|
|
1516
|
+
return Object.freeze({
|
|
1517
|
+
...feature.key === void 0 ? {} : { key: feature.key },
|
|
1518
|
+
create
|
|
1519
|
+
});
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1504
1522
|
const DISPOSED_ACCOUNTS = /* @__PURE__ */ new WeakSet();
|
|
1505
1523
|
function assertAccountsActive(accounts, action) {
|
|
1506
1524
|
if (!DISPOSED_ACCOUNTS.has(accounts)) return;
|
|
@@ -1552,6 +1570,8 @@ var ItdAccounts = class ItdAccounts {
|
|
|
1552
1570
|
#eventUnsubscribers = /* @__PURE__ */ new Map();
|
|
1553
1571
|
/** Плагины для всех: и для уже заведённых аккаунтов, и для будущих. */
|
|
1554
1572
|
#plugins;
|
|
1573
|
+
/** Фабрики модулей для новых и восстанавливаемых аккаунтов. */
|
|
1574
|
+
#features;
|
|
1555
1575
|
/** Имена плагинов, чья асинхронная очистка ещё не завершилась. */
|
|
1556
1576
|
#removingPlugins = /* @__PURE__ */ new Set();
|
|
1557
1577
|
/** Общая очередь. `undefined`, когда у каждого аккаунта своя. */
|
|
@@ -1562,11 +1582,12 @@ var ItdAccounts = class ItdAccounts {
|
|
|
1562
1582
|
/** Общий результат терминальной очистки для идемпотентных повторных вызовов. */
|
|
1563
1583
|
#disposePromise;
|
|
1564
1584
|
constructor(options = {}) {
|
|
1565
|
-
const { storage, plugins, rateLimitScope, ...base } = options;
|
|
1585
|
+
const { storage, plugins, features, rateLimitScope, ...base } = options;
|
|
1566
1586
|
if (rateLimitScope !== void 0 && rateLimitScope !== "account" && rateLimitScope !== "shared") throw new ItdConfigError("rateLimitScope должен быть 'account' или 'shared'");
|
|
1567
1587
|
this.#base = base;
|
|
1568
1588
|
this.#storage = storage ?? new MemoryMultiTokenStorage();
|
|
1569
1589
|
this.#plugins = orderPluginDefinitions(plugins ?? []);
|
|
1590
|
+
this.#features = resolveAccountFeatures(features);
|
|
1570
1591
|
this.#rateLimitScope = rateLimitScope ?? "shared";
|
|
1571
1592
|
const rateLimit = this.#rateLimitScope === "shared" ? resolveRateLimit(base.rateLimit, ITD_CATALOG) : void 0;
|
|
1572
1593
|
this.#queues = rateLimit ? new RequestQueuePool(rateLimit, base.clock ?? systemClock) : void 0;
|
|
@@ -1593,7 +1614,7 @@ var ItdAccounts = class ItdAccounts {
|
|
|
1593
1614
|
/**
|
|
1594
1615
|
* Заводит аккаунт.
|
|
1595
1616
|
*
|
|
1596
|
-
* Возвращается обычный `ItdClient` — со всеми ресурсами, плагинами и
|
|
1617
|
+
* Возвращается обычный `ItdClient` — со всеми ресурсами, плагинами и каналом событий.
|
|
1597
1618
|
* Хранилище ему подставляется само: срез общего по имени аккаунта.
|
|
1598
1619
|
*
|
|
1599
1620
|
* Опция `auth` не обязательна: когда сессия этого аккаунта уже лежит в хранилище,
|
|
@@ -1619,10 +1640,15 @@ var ItdAccounts = class ItdAccounts {
|
|
|
1619
1640
|
try {
|
|
1620
1641
|
client = createManagedClient(this.#mergeOptions(options, storageControl.storage), this.#queues ? { queues: this.#queues } : {});
|
|
1621
1642
|
for (const plugin of this.#plugins) client.use(plugin);
|
|
1643
|
+
for (const feature of this.#features) {
|
|
1644
|
+
const manifest = feature.create();
|
|
1645
|
+
if (feature.key === void 0) client.install(manifest);
|
|
1646
|
+
else client.withFeature(feature.key, manifest);
|
|
1647
|
+
}
|
|
1622
1648
|
} catch (error) {
|
|
1623
1649
|
storageControl.revoke();
|
|
1624
1650
|
if (client) client.dispose().catch((cleanupError) => {
|
|
1625
|
-
this.#
|
|
1651
|
+
this.#reportCleanup("неудачного добавления аккаунта", cleanupError);
|
|
1626
1652
|
});
|
|
1627
1653
|
throw error;
|
|
1628
1654
|
}
|
|
@@ -1746,7 +1772,7 @@ var ItdAccounts = class ItdAccounts {
|
|
|
1746
1772
|
}
|
|
1747
1773
|
} catch (error) {
|
|
1748
1774
|
for (const client of installed.reverse()) client.unuse(plugin.name).catch((cleanupError) => {
|
|
1749
|
-
this.#
|
|
1775
|
+
this.#reportCleanup(`отката плагина «${plugin.name}»`, cleanupError);
|
|
1750
1776
|
});
|
|
1751
1777
|
throw error;
|
|
1752
1778
|
}
|
|
@@ -1862,6 +1888,7 @@ var ItdAccounts = class ItdAccounts {
|
|
|
1862
1888
|
Promise.resolve().then(() => this.#queues?.clear())
|
|
1863
1889
|
]);
|
|
1864
1890
|
this.#plugins.splice(0);
|
|
1891
|
+
this.#features.splice(0);
|
|
1865
1892
|
this.#removingPlugins.clear();
|
|
1866
1893
|
const errors = results.filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
1867
1894
|
if (errors.length > 0) throw new AggregateError(errors, "Не удалось освободить аккаунты");
|
|
@@ -1871,11 +1898,7 @@ var ItdAccounts = class ItdAccounts {
|
|
|
1871
1898
|
return this.dispose();
|
|
1872
1899
|
}
|
|
1873
1900
|
static {
|
|
1874
|
-
|
|
1875
|
-
const prototype = ItdAccounts.prototype;
|
|
1876
|
-
prototype[Symbol.for("Symbol.asyncDispose")] = prototype.undefined;
|
|
1877
|
-
delete prototype.undefined;
|
|
1878
|
-
}
|
|
1901
|
+
installAsyncDisposeFallback(ItdAccounts);
|
|
1879
1902
|
}
|
|
1880
1903
|
/**
|
|
1881
1904
|
* Собирает опции клиента: общие, поверх них — аккаунтные, и обязательно свой срез
|
|
@@ -1900,9 +1923,9 @@ var ItdAccounts = class ItdAccounts {
|
|
|
1900
1923
|
storage
|
|
1901
1924
|
};
|
|
1902
1925
|
}
|
|
1903
|
-
/** Не теряет ошибку фонового
|
|
1904
|
-
#
|
|
1905
|
-
const message = `Не удалось
|
|
1926
|
+
/** Не теряет ошибку фонового освобождения ресурсов, которого синхронный API не ждёт. */
|
|
1927
|
+
#reportCleanup(scope, error) {
|
|
1928
|
+
const message = `Не удалось освободить ресурсы после ${scope}`;
|
|
1906
1929
|
if (this.#logger) this.#logger.error(message, error);
|
|
1907
1930
|
else console.error(`[itd-api] ${message}`, error);
|
|
1908
1931
|
}
|
|
@@ -1934,6 +1957,6 @@ function createAccounts(options = {}) {
|
|
|
1934
1957
|
return new ItdAccounts(options);
|
|
1935
1958
|
}
|
|
1936
1959
|
//#endregion
|
|
1937
|
-
export { ALLOWED_MIME_TYPES, AUDIO_MIME_TYPES, AccessType, AttachmentType, BUCKET_LIMITS, CommentSort, DEFAULT_BASE_URL, DEFAULT_RATE_LIMIT_BUCKET, 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,
|
|
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, createMemoryShopOrderAccessStorage, 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 };
|
|
1938
1961
|
|
|
1939
1962
|
//# sourceMappingURL=index.js.map
|