itd-api 0.9.1 → 0.9.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 +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 +198 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -65
- package/dist/index.d.ts +11 -65
- package/dist/index.js +199 -39
- 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-BCn5LRnT.d.cts} +3 -5
- package/dist/shared/{events-D6y0Hxyz.js → events-BIBO2Jif.js} +4 -4
- package/dist/shared/{events-D6y0Hxyz.js.map → events-BIBO2Jif.js.map} +1 -1
- package/dist/shared/{events-BqFkwwTw.cjs → events-BsRm_Fba.cjs} +3 -9
- package/dist/shared/{events-BqFkwwTw.cjs.map → events-BsRm_Fba.cjs.map} +1 -1
- package/dist/shared/{events-BQyH5-S0.d.cts → events-t1uGgpkf.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-sb1VJhUA.js → render--ZXmYEf-.js} +127 -96
- package/dist/shared/render--ZXmYEf-.js.map +1 -0
- package/dist/shared/{render-CZ_-F2Tl.d.ts → render-BdnBV-aT.d.ts} +82 -7
- package/dist/shared/{render-BU1_wex8.d.cts → render-DA83gS0f.d.cts} +82 -7
- package/dist/shared/{render-DAEeCMk9.cjs → render-DqVzx68r.cjs} +125 -100
- package/dist/shared/render-DqVzx68r.cjs.map +1 -0
- 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-CZZcyPI0.d.ts → url-Dbj6V6fk.d.cts} +4 -2
- package/dist/shared/{url-CZZcyPI0.d.cts → url-Dbj6V6fk.d.ts} +4 -2
- package/dist/shared/{url-CzHqE-Fg.cjs → url-Doc0XXW_.cjs} +274 -86
- package/dist/shared/url-Doc0XXW_.cjs.map +1 -0
- package/dist/shared/{url-Dsr4c5r5.js → url-i2f-X1hX.js} +274 -86
- package/dist/shared/url-i2f-X1hX.js.map +1 -0
- package/dist/web/index.cjs +1 -1
- package/dist/web/index.js +1 -1
- package/package.json +4 -4
- 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-i2f-X1hX.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--ZXmYEf-.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-BIBO2Jif.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 при запросе сброса пароля"));
|
|
@@ -119,12 +119,18 @@ function validateAuth(auth) {
|
|
|
119
119
|
return auth;
|
|
120
120
|
}
|
|
121
121
|
if (typeof auth !== "object" || auth === null) throw new ItdConfigError(`auth должен быть строкой с токеном или объектом, получено: ${typeof auth}`);
|
|
122
|
+
if ([
|
|
123
|
+
"getToken" in auth,
|
|
124
|
+
"accessToken" in auth || "refreshToken" in auth,
|
|
125
|
+
"email" in auth || "password" in auth
|
|
126
|
+
].filter(Boolean).length > 1) throw new ItdConfigError("auth должен описывать ровно один способ авторизации: getToken, accessToken или email/password");
|
|
122
127
|
if ("getToken" in auth) {
|
|
123
128
|
if (typeof auth.getToken !== "function") throw new ItdConfigError("auth.getToken должен быть функцией");
|
|
124
129
|
return { ...auth };
|
|
125
130
|
}
|
|
126
131
|
if ("accessToken" in auth) {
|
|
127
132
|
if (typeof auth.accessToken !== "string" || auth.accessToken.trim() === "") throw new ItdConfigError("auth.accessToken должен быть непустой строкой");
|
|
133
|
+
if (auth.refreshToken !== void 0 && (typeof auth.refreshToken !== "string" || auth.refreshToken.trim() === "")) throw new ItdConfigError("auth.refreshToken должен быть непустой строкой");
|
|
128
134
|
return { ...auth };
|
|
129
135
|
}
|
|
130
136
|
if ("email" in auth || "password" in auth) {
|
|
@@ -212,10 +218,67 @@ const AUTH_PATHS = {
|
|
|
212
218
|
const TURNSTILE_SITE_KEY = "0x4AAAAAACHhxczw6fJGwPBg";
|
|
213
219
|
/** Убирает поле, которое сохраняли промежуточные версии с поддержкой `getUserId()`. */
|
|
214
220
|
function withoutLegacyUserId(session) {
|
|
215
|
-
const clean =
|
|
221
|
+
const clean = copySession(session);
|
|
216
222
|
delete clean.userId;
|
|
217
223
|
return clean;
|
|
218
224
|
}
|
|
225
|
+
function normalizeSessionValue(value, source, mode = "strict", logger) {
|
|
226
|
+
const tolerant = mode === "stored";
|
|
227
|
+
if (!isRecord(value)) {
|
|
228
|
+
if (tolerant) {
|
|
229
|
+
logger?.warn(`${source} вернула не объект сессии; сохранённая запись пропущена`);
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
throw new ItdConfigError(`${source} должна быть объектом сессии`);
|
|
233
|
+
}
|
|
234
|
+
const record = value;
|
|
235
|
+
const normalized = { ...record };
|
|
236
|
+
delete normalized.userId;
|
|
237
|
+
const normalizeString = (field) => {
|
|
238
|
+
const item = record[field];
|
|
239
|
+
if (item === void 0) {
|
|
240
|
+
delete normalized[field];
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (item !== void 0 && (typeof item !== "string" || item.trim() === "")) {
|
|
244
|
+
if (tolerant) {
|
|
245
|
+
logger?.warn(`${source}.${field} повреждена; поле сохранённой сессии пропущено`);
|
|
246
|
+
delete normalized[field];
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
throw new ItdConfigError(`${source}.${field} должна быть непустой строкой`);
|
|
250
|
+
}
|
|
251
|
+
normalized[field] = item;
|
|
252
|
+
};
|
|
253
|
+
normalizeString("accessToken");
|
|
254
|
+
normalizeString("refreshToken");
|
|
255
|
+
normalizeString("deviceId");
|
|
256
|
+
if (record.obtainedAt === void 0) delete normalized.obtainedAt;
|
|
257
|
+
else if (typeof record.obtainedAt !== "number" || !Number.isFinite(record.obtainedAt)) {
|
|
258
|
+
if (!tolerant) throw new ItdConfigError(`${source}.obtainedAt должна быть конечным числом`);
|
|
259
|
+
else {
|
|
260
|
+
logger?.warn(`${source}.obtainedAt повреждена; поле сохранённой сессии пропущено`);
|
|
261
|
+
delete normalized.obtainedAt;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (record.cookies === void 0) delete normalized.cookies;
|
|
265
|
+
else if (!Array.isArray(record.cookies)) {
|
|
266
|
+
if (tolerant) {
|
|
267
|
+
logger?.warn(`${source}.cookies повреждена; поле сохранённой сессии пропущено`);
|
|
268
|
+
delete normalized.cookies;
|
|
269
|
+
} else throw new ItdConfigError(`${source}.cookies должна быть массивом`);
|
|
270
|
+
} else {
|
|
271
|
+
const valid = record.cookies.filter((cookie) => typeof cookie === "string" && isSerializedCookieEntry(cookie));
|
|
272
|
+
if (valid.length > 0) normalized.cookies = valid;
|
|
273
|
+
else delete normalized.cookies;
|
|
274
|
+
}
|
|
275
|
+
return normalized;
|
|
276
|
+
}
|
|
277
|
+
function normalizeSession(value, source) {
|
|
278
|
+
const normalized = normalizeSessionValue(value, source);
|
|
279
|
+
if (!normalized) throw new ItdConfigError(`${source} должна быть объектом сессии`);
|
|
280
|
+
return normalized;
|
|
281
|
+
}
|
|
219
282
|
let authScopeSequence = 0;
|
|
220
283
|
function nextAuthScope() {
|
|
221
284
|
authScopeSequence += 1;
|
|
@@ -270,6 +333,9 @@ var AuthManager = class {
|
|
|
270
333
|
* воскресить уже очищенную сессию поверх `signOut`.
|
|
271
334
|
*/
|
|
272
335
|
#authEpoch = 0;
|
|
336
|
+
#externalReadGeneration = 0;
|
|
337
|
+
#disposed = false;
|
|
338
|
+
#persistence = Promise.resolve();
|
|
273
339
|
constructor(config, http, jar, hooks = {}) {
|
|
274
340
|
this.#config = config;
|
|
275
341
|
this.#http = http;
|
|
@@ -279,14 +345,23 @@ var AuthManager = class {
|
|
|
279
345
|
}
|
|
280
346
|
/** Подписка на события авторизации. */
|
|
281
347
|
get on() {
|
|
282
|
-
return
|
|
348
|
+
return ((...args) => {
|
|
349
|
+
this.#assertActive();
|
|
350
|
+
return this.#emitter.on(...args);
|
|
351
|
+
});
|
|
283
352
|
}
|
|
284
353
|
/** Подписка на одно срабатывание. */
|
|
285
354
|
get once() {
|
|
286
|
-
return
|
|
355
|
+
return ((...args) => {
|
|
356
|
+
this.#assertActive();
|
|
357
|
+
return this.#emitter.once(...args);
|
|
358
|
+
});
|
|
287
359
|
}
|
|
288
360
|
/** Снимает служебные подписки при окончательном освобождении владельца. @internal */
|
|
289
361
|
dispose() {
|
|
362
|
+
if (this.#disposed) return;
|
|
363
|
+
this.#disposed = true;
|
|
364
|
+
this.#externalReadGeneration += 1;
|
|
290
365
|
this.#invalidateInFlight();
|
|
291
366
|
this.#emitter.removeAllListeners();
|
|
292
367
|
}
|
|
@@ -301,6 +376,7 @@ var AuthManager = class {
|
|
|
301
376
|
}
|
|
302
377
|
/** Загружает сессию и возвращает идентификаторы аккаунта и сессии для плагинов. */
|
|
303
378
|
async getAuthIdentity() {
|
|
379
|
+
this.#assertActive();
|
|
304
380
|
const session = await this.#loadSession();
|
|
305
381
|
if (session?.accessToken) return this.#identityForToken(session.accessToken);
|
|
306
382
|
const auth = this.#config.auth;
|
|
@@ -321,6 +397,30 @@ var AuthManager = class {
|
|
|
321
397
|
/** Отмечает смену владельца авторизации — обесценивает результат идущего обновления. */
|
|
322
398
|
#invalidateInFlight() {
|
|
323
399
|
this.#authEpoch += 1;
|
|
400
|
+
this.#externalReadGeneration += 1;
|
|
401
|
+
}
|
|
402
|
+
/** Ревизия владельца сессии для условного commit внешнего auth-flow. @internal */
|
|
403
|
+
revision() {
|
|
404
|
+
this.#assertActive();
|
|
405
|
+
return this.#authEpoch;
|
|
406
|
+
}
|
|
407
|
+
/** Создаёт изолированный jar для сетевого auth-flow. @internal */
|
|
408
|
+
createCookieFlow(inherit = true) {
|
|
409
|
+
this.#assertActive();
|
|
410
|
+
return inherit ? this.#jar.clone() : new CookieJar();
|
|
411
|
+
}
|
|
412
|
+
/** Применяет cookies только если владелец сессии не сменился. @internal */
|
|
413
|
+
commitCookieFlow(cookies, expectedRevision) {
|
|
414
|
+
this.#assertActive();
|
|
415
|
+
if (this.#authEpoch !== expectedRevision) return false;
|
|
416
|
+
this.#jar.replaceWith(cookies);
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
#assertActive() {
|
|
420
|
+
if (this.#disposed) throw new ItdStateError("Менеджер авторизации уже освобождён");
|
|
421
|
+
}
|
|
422
|
+
#currentAccessToken() {
|
|
423
|
+
return this.#session?.accessToken ?? null;
|
|
324
424
|
}
|
|
325
425
|
#identityForToken(accessToken) {
|
|
326
426
|
const token = accessToken ? readTokenIdentity(accessToken) : {};
|
|
@@ -330,7 +430,11 @@ var AuthManager = class {
|
|
|
330
430
|
};
|
|
331
431
|
}
|
|
332
432
|
async #readExternalToken(getToken) {
|
|
433
|
+
this.#assertActive();
|
|
434
|
+
const generation = ++this.#externalReadGeneration;
|
|
333
435
|
const token = await getToken() ?? null;
|
|
436
|
+
this.#assertActive();
|
|
437
|
+
if (generation !== this.#externalReadGeneration) return token;
|
|
334
438
|
const current = this.#identityForToken(token ?? void 0);
|
|
335
439
|
if (this.#externalToken !== void 0 && this.#externalToken !== token) {
|
|
336
440
|
this.#rotateAuthScope();
|
|
@@ -361,6 +465,7 @@ var AuthManager = class {
|
|
|
361
465
|
* ответ был бы `false` даже при полностью рабочей сохранённой сессии.
|
|
362
466
|
*/
|
|
363
467
|
async hasRefreshSession() {
|
|
468
|
+
this.#assertActive();
|
|
364
469
|
await this.#loadSession();
|
|
365
470
|
return this.#hasRefreshSession();
|
|
366
471
|
}
|
|
@@ -377,6 +482,7 @@ var AuthManager = class {
|
|
|
377
482
|
* обязаны завершиться до захвата слота очереди.
|
|
378
483
|
*/
|
|
379
484
|
async prepare() {
|
|
485
|
+
this.#assertActive();
|
|
380
486
|
await this.token();
|
|
381
487
|
}
|
|
382
488
|
/**
|
|
@@ -396,6 +502,7 @@ var AuthManager = class {
|
|
|
396
502
|
* через `itd.auth.refresh()`, а библиотека просто пробрасывает {@link ItdAuthError}.
|
|
397
503
|
*/
|
|
398
504
|
recover() {
|
|
505
|
+
this.#assertActive();
|
|
399
506
|
return this.#config.autoRefresh ? this.onUnauthorized() : Promise.resolve(false);
|
|
400
507
|
}
|
|
401
508
|
/**
|
|
@@ -406,6 +513,7 @@ var AuthManager = class {
|
|
|
406
513
|
* по новой сессии на каждый старт.
|
|
407
514
|
*/
|
|
408
515
|
deviceId() {
|
|
516
|
+
this.#assertActive();
|
|
409
517
|
if (this.#deviceId) return Promise.resolve(this.#deviceId);
|
|
410
518
|
this.#deviceIdLoading ??= this.#resolveDeviceId().finally(() => {
|
|
411
519
|
this.#deviceIdLoading = null;
|
|
@@ -413,9 +521,12 @@ var AuthManager = class {
|
|
|
413
521
|
return this.#deviceIdLoading;
|
|
414
522
|
}
|
|
415
523
|
async #resolveDeviceId() {
|
|
524
|
+
const revision = this.#authEpoch;
|
|
416
525
|
const session = await this.#loadSession();
|
|
526
|
+
this.#assertActive();
|
|
417
527
|
const deviceId = this.#config.deviceId ?? session?.deviceId ?? createDeviceId();
|
|
418
528
|
this.#deviceId = deviceId;
|
|
529
|
+
if (this.#authEpoch !== revision) return deviceId;
|
|
419
530
|
if (session?.deviceId !== deviceId) await this.#saveSession({
|
|
420
531
|
...session ?? {},
|
|
421
532
|
deviceId
|
|
@@ -429,6 +540,7 @@ var AuthManager = class {
|
|
|
429
540
|
* и пароль, первый же запрос сам заведёт сессию.
|
|
430
541
|
*/
|
|
431
542
|
async token() {
|
|
543
|
+
this.#assertActive();
|
|
432
544
|
const session = await this.#loadSession();
|
|
433
545
|
if (session?.accessToken) return session.accessToken;
|
|
434
546
|
const auth = this.#config.auth;
|
|
@@ -443,6 +555,7 @@ var AuthManager = class {
|
|
|
443
555
|
* @returns `true`, если токен обновлён и запрос имеет смысл повторить
|
|
444
556
|
*/
|
|
445
557
|
async onUnauthorized() {
|
|
558
|
+
this.#assertActive();
|
|
446
559
|
try {
|
|
447
560
|
if (await this.#refreshDeduplicated() !== null) return true;
|
|
448
561
|
this.#emitter.emit("authError", { error: this.#noRefreshSessionError() });
|
|
@@ -478,24 +591,33 @@ var AuthManager = class {
|
|
|
478
591
|
* @throws {ItdAuthError} если обновить сессию не удалось
|
|
479
592
|
*/
|
|
480
593
|
async refresh() {
|
|
594
|
+
this.#assertActive();
|
|
481
595
|
const token = await this.#refreshDeduplicated();
|
|
482
596
|
if (token === null) throw this.#noRefreshSessionError();
|
|
483
597
|
return token;
|
|
484
598
|
}
|
|
485
599
|
/** Сохраняет токен, полученный извне, — например после подтверждения OTP. */
|
|
486
|
-
async setAccessToken(accessToken) {
|
|
600
|
+
async setAccessToken(accessToken, expectedRevision = this.#authEpoch, cookies) {
|
|
601
|
+
this.#assertActive();
|
|
602
|
+
if (typeof accessToken !== "string" || accessToken.trim() === "") throw new ItdConfigError("accessToken должен быть непустой строкой");
|
|
487
603
|
await this.#loadSession();
|
|
604
|
+
this.#assertActive();
|
|
605
|
+
if (this.#authEpoch !== expectedRevision) return false;
|
|
606
|
+
if (cookies) this.#jar.replaceWith(cookies);
|
|
488
607
|
this.#invalidateInFlight();
|
|
489
608
|
this.#transitionAuth(accessToken);
|
|
490
|
-
|
|
609
|
+
const saved = this.#saveSession({
|
|
491
610
|
...this.#session ?? {},
|
|
492
611
|
accessToken,
|
|
493
612
|
obtainedAt: this.#config.clock.now()
|
|
494
613
|
});
|
|
495
614
|
this.#emitter.emit("tokens", { accessToken });
|
|
615
|
+
await saved;
|
|
616
|
+
return true;
|
|
496
617
|
}
|
|
497
618
|
/** Текущая сессия целиком. Полезно, чтобы сохранить её самому. */
|
|
498
619
|
async getSession() {
|
|
620
|
+
this.#assertActive();
|
|
499
621
|
const session = await this.#loadSession();
|
|
500
622
|
return session ? copySession(session) : null;
|
|
501
623
|
}
|
|
@@ -506,20 +628,24 @@ var AuthManager = class {
|
|
|
506
628
|
* токена идентификатор прежнего владельца остаться не может.
|
|
507
629
|
*/
|
|
508
630
|
async getUserId() {
|
|
631
|
+
this.#assertActive();
|
|
509
632
|
const session = await this.#loadSession();
|
|
510
633
|
return session?.accessToken ? readTokenSubject(session.accessToken) : void 0;
|
|
511
634
|
}
|
|
512
635
|
/** Заменяет сессию и связанные с ней cookie целиком. */
|
|
513
636
|
async setSession(session) {
|
|
637
|
+
this.#assertActive();
|
|
638
|
+
const normalized = normalizeSession(session, "session");
|
|
514
639
|
await this.#loadSession();
|
|
640
|
+
this.#assertActive();
|
|
515
641
|
this.#invalidateInFlight();
|
|
516
|
-
this.#transitionAuth(
|
|
642
|
+
this.#transitionAuth(normalized.accessToken);
|
|
517
643
|
this.#jar.clear();
|
|
518
|
-
this.#jar.deserialize(
|
|
519
|
-
if (
|
|
520
|
-
this.#session =
|
|
644
|
+
this.#jar.deserialize(normalized.cookies);
|
|
645
|
+
if (normalized.deviceId) this.#deviceId = normalized.deviceId;
|
|
646
|
+
this.#session = normalized;
|
|
521
647
|
this.#seedRefreshCookie();
|
|
522
|
-
await this.#saveSession(
|
|
648
|
+
await this.#saveSession(normalized);
|
|
523
649
|
}
|
|
524
650
|
/**
|
|
525
651
|
* Забывает сессию и cookie. Сетевой запрос не выполняется.
|
|
@@ -528,14 +654,19 @@ var AuthManager = class {
|
|
|
528
654
|
* новую запись в списке сессий.
|
|
529
655
|
*/
|
|
530
656
|
async clear() {
|
|
657
|
+
this.#assertActive();
|
|
531
658
|
await this.#loadSession();
|
|
659
|
+
this.#assertActive();
|
|
532
660
|
this.#invalidateInFlight();
|
|
533
661
|
this.#transitionAuth(void 0);
|
|
534
|
-
this.#session = null;
|
|
535
662
|
this.#jar.clear();
|
|
536
|
-
|
|
537
|
-
|
|
663
|
+
const retained = this.#deviceId ? { deviceId: this.#deviceId } : null;
|
|
664
|
+
this.#session = retained;
|
|
538
665
|
this.#emitter.emit("signOut", void 0);
|
|
666
|
+
await this.#enqueuePersistence(async () => {
|
|
667
|
+
await this.#config.storage.clear();
|
|
668
|
+
if (retained) await this.#config.storage.set(copySession(retained));
|
|
669
|
+
});
|
|
539
670
|
}
|
|
540
671
|
#loadSession() {
|
|
541
672
|
if (this.#session !== void 0) return Promise.resolve(this.#session);
|
|
@@ -546,7 +677,8 @@ var AuthManager = class {
|
|
|
546
677
|
}
|
|
547
678
|
async #performLoad() {
|
|
548
679
|
const loaded = await this.#config.storage.get() ?? null;
|
|
549
|
-
|
|
680
|
+
this.#assertActive();
|
|
681
|
+
const stored = loaded === null ? null : normalizeSessionValue(loaded, "storage.get()", "stored", this.#config.logger);
|
|
550
682
|
if (stored?.cookies) this.#jar.deserialize(stored.cookies);
|
|
551
683
|
const fromConfig = this.#sessionFromConfig(this.#config.auth);
|
|
552
684
|
this.#session = stored && fromConfig ? {
|
|
@@ -568,7 +700,7 @@ var AuthManager = class {
|
|
|
568
700
|
if (!this.#config.useCookieJar) return;
|
|
569
701
|
const refreshToken = this.#session?.refreshToken;
|
|
570
702
|
if (!refreshToken) return;
|
|
571
|
-
if (this.#jar.has("refresh_token")) return;
|
|
703
|
+
if (this.#jar.has("refresh_token", this.#config.baseUrl + "/api/v1/auth")) return;
|
|
572
704
|
this.#jar.set(this.#config.baseUrl, REFRESH_COOKIE, refreshToken, REFRESH_COOKIE_PATH);
|
|
573
705
|
}
|
|
574
706
|
#sessionFromConfig(auth) {
|
|
@@ -587,13 +719,19 @@ var AuthManager = class {
|
|
|
587
719
|
async #saveSession(session) {
|
|
588
720
|
const cookies = this.#config.useCookieJar ? this.#jar.serialize() : void 0;
|
|
589
721
|
const deviceId = session.deviceId ?? this.#deviceId;
|
|
722
|
+
const { cookies: _staleCookies, ...withoutCookies } = copySession(session);
|
|
590
723
|
const next = withoutLegacyUserId({
|
|
591
|
-
...
|
|
724
|
+
...withoutCookies,
|
|
592
725
|
...cookies?.length ? { cookies } : {},
|
|
593
726
|
...deviceId ? { deviceId } : {}
|
|
594
727
|
});
|
|
595
728
|
this.#session = next;
|
|
596
|
-
await this.#config.storage.set(next);
|
|
729
|
+
await this.#enqueuePersistence(() => this.#config.storage.set(copySession(next)));
|
|
730
|
+
}
|
|
731
|
+
#enqueuePersistence(operation) {
|
|
732
|
+
const pending = this.#persistence.then(operation, operation);
|
|
733
|
+
this.#persistence = pending.catch(() => {});
|
|
734
|
+
return pending;
|
|
597
735
|
}
|
|
598
736
|
/**
|
|
599
737
|
* Обновление с дедупликацией.
|
|
@@ -609,34 +747,41 @@ var AuthManager = class {
|
|
|
609
747
|
return promise;
|
|
610
748
|
}
|
|
611
749
|
async #performRefresh() {
|
|
750
|
+
this.#assertActive();
|
|
612
751
|
await this.#loadSession();
|
|
613
752
|
if (!this.#hasRefreshSession()) return this.#reloginOrNull();
|
|
614
753
|
const epoch = this.#authEpoch;
|
|
754
|
+
const flowCookies = this.#jar.clone();
|
|
615
755
|
try {
|
|
616
756
|
const accessToken = await this.#http.execute(AUTH_REFRESH, {
|
|
617
757
|
path: AUTH_PATHS.refresh,
|
|
618
758
|
skipAuth: true,
|
|
619
|
-
skipAuthRefresh: true
|
|
759
|
+
skipAuthRefresh: true,
|
|
760
|
+
cookieJar: flowCookies
|
|
620
761
|
});
|
|
621
|
-
if (!accessToken) return this.#reloginOrNull();
|
|
622
762
|
if (this.#authEpoch !== epoch) return this.#session?.accessToken ?? null;
|
|
763
|
+
this.#jar.replaceWith(flowCookies);
|
|
623
764
|
const rotated = this.#jar.getValue(REFRESH_COOKIE, this.#config.baseUrl + REFRESH_COOKIE_PATH);
|
|
624
765
|
this.#transitionAuth(accessToken, false);
|
|
625
|
-
|
|
766
|
+
const saved = this.#saveSession({
|
|
626
767
|
...this.#session ?? {},
|
|
627
768
|
accessToken,
|
|
628
769
|
...rotated ? { refreshToken: rotated } : {},
|
|
629
770
|
obtainedAt: this.#config.clock.now()
|
|
630
771
|
});
|
|
631
772
|
this.#emitter.emit("tokens", { accessToken });
|
|
773
|
+
await saved;
|
|
632
774
|
return accessToken;
|
|
633
775
|
} catch (error) {
|
|
634
|
-
if (error instanceof
|
|
776
|
+
if (error instanceof ItdAuthError) {
|
|
635
777
|
if (this.#authEpoch !== epoch) return this.#session?.accessToken ?? null;
|
|
778
|
+
this.#invalidateInFlight();
|
|
779
|
+
const clearedRevision = this.#authEpoch;
|
|
636
780
|
this.#transitionAuth(void 0);
|
|
637
781
|
this.#session = null;
|
|
638
782
|
this.#jar.clear();
|
|
639
|
-
await this.#config.storage.clear();
|
|
783
|
+
await this.#enqueuePersistence(() => this.#config.storage.clear());
|
|
784
|
+
if (this.#authEpoch !== clearedRevision) return this.#currentAccessToken();
|
|
640
785
|
const relogged = await this.#reloginOrNull();
|
|
641
786
|
if (relogged !== null) return relogged;
|
|
642
787
|
throw error;
|
|
@@ -650,7 +795,8 @@ var AuthManager = class {
|
|
|
650
795
|
if (!this.#config.reloginOnRefreshFailure || !auth || typeof auth !== "object" || !("email" in auth)) return null;
|
|
651
796
|
try {
|
|
652
797
|
return await this.#signInWithCredentials(auth);
|
|
653
|
-
} catch {
|
|
798
|
+
} catch (error) {
|
|
799
|
+
this.#config.logger?.warn("Автоматический повторный вход не удался", error);
|
|
654
800
|
return null;
|
|
655
801
|
}
|
|
656
802
|
}
|
|
@@ -662,7 +808,8 @@ var AuthManager = class {
|
|
|
662
808
|
*/
|
|
663
809
|
#signInWithCredentials(credentials) {
|
|
664
810
|
if (this.#signingIn) return this.#signingIn;
|
|
665
|
-
const
|
|
811
|
+
const revision = this.#authEpoch;
|
|
812
|
+
const promise = this.#performSignIn(credentials, revision).finally(() => {
|
|
666
813
|
this.#signingIn = null;
|
|
667
814
|
});
|
|
668
815
|
this.#signingIn = promise;
|
|
@@ -682,8 +829,9 @@ var AuthManager = class {
|
|
|
682
829
|
if (credentials.turnstileToken) return credentials.turnstileToken;
|
|
683
830
|
throw new ItdConfigError("Вход по email и паролю требует токен капчи Cloudflare Turnstile: без него сервер отвечает 422. Передайте auth.getTurnstileToken (источник свежего токена) либо разовый auth.turnstileToken. Ключ виджета — TURNSTILE_SITE_KEY. В Node токен умеет добывать отдельный пакет: npm i @itd-api/turnstile, затем getTurnstileToken: createTurnstileSolver().");
|
|
684
831
|
}
|
|
685
|
-
async #performSignIn(credentials) {
|
|
832
|
+
async #performSignIn(credentials, revision) {
|
|
686
833
|
const turnstileToken = await this.#resolveTurnstileToken(credentials);
|
|
834
|
+
const flowCookies = new CookieJar();
|
|
687
835
|
const result = await this.#http.execute(AUTH_SIGN_IN, {
|
|
688
836
|
path: AUTH_PATHS.signIn,
|
|
689
837
|
body: {
|
|
@@ -692,18 +840,23 @@ var AuthManager = class {
|
|
|
692
840
|
turnstileToken
|
|
693
841
|
},
|
|
694
842
|
skipAuth: true,
|
|
695
|
-
skipAuthRefresh: true
|
|
843
|
+
skipAuthRefresh: true,
|
|
844
|
+
cookieJar: flowCookies
|
|
696
845
|
});
|
|
697
846
|
if (result.status !== SignInStatus.Authenticated) throw new ItdConfigError("Вход по email и паролю требует подтверждения кодом из письма. Автоматический вход невозможен: воспользуйтесь itd.auth.signInWithOtp() и передайте полученный accessToken в конфигурацию клиента.");
|
|
698
847
|
const accessToken = result.accessToken;
|
|
848
|
+
this.#assertActive();
|
|
849
|
+
if (this.#authEpoch !== revision) return this.#session?.accessToken ?? null;
|
|
850
|
+
this.#jar.replaceWith(flowCookies);
|
|
699
851
|
this.#invalidateInFlight();
|
|
700
852
|
this.#transitionAuth(accessToken);
|
|
701
|
-
|
|
853
|
+
const saved = this.#saveSession({
|
|
702
854
|
accessToken,
|
|
703
855
|
obtainedAt: this.#config.clock.now()
|
|
704
856
|
});
|
|
705
857
|
this.#emitter.emit("tokens", { accessToken });
|
|
706
858
|
this.#emitter.emit("signIn", { accessToken });
|
|
859
|
+
await saved;
|
|
707
860
|
return accessToken;
|
|
708
861
|
}
|
|
709
862
|
};
|
|
@@ -762,14 +915,18 @@ var AuthResource = class extends BaseResource {
|
|
|
762
915
|
* @param credentials email, пароль и обязательный токен капчи — см. {@link CaptchaCredentials}
|
|
763
916
|
*/
|
|
764
917
|
async signIn(credentials, options = {}) {
|
|
918
|
+
const revision = this.#auth.revision();
|
|
919
|
+
const cookies = this.#auth.createCookieFlow(false);
|
|
765
920
|
const result = await this.http.execute(AUTH_SIGN_IN, {
|
|
766
921
|
path: AUTH_PATHS.signIn,
|
|
767
922
|
body: credentials,
|
|
768
923
|
skipAuth: true,
|
|
769
924
|
skipAuthRefresh: true,
|
|
925
|
+
cookieJar: cookies,
|
|
770
926
|
...options
|
|
771
927
|
});
|
|
772
|
-
if (result.status === SignInStatus.Authenticated) await this.#auth.setAccessToken(result.accessToken);
|
|
928
|
+
if (result.status === SignInStatus.Authenticated) await this.#auth.setAccessToken(result.accessToken, revision, cookies);
|
|
929
|
+
else this.#auth.commitCookieFlow(cookies, revision);
|
|
773
930
|
return result;
|
|
774
931
|
}
|
|
775
932
|
/**
|
|
@@ -778,14 +935,17 @@ var AuthResource = class extends BaseResource {
|
|
|
778
935
|
* Полученный токен сохраняется в клиенте автоматически.
|
|
779
936
|
*/
|
|
780
937
|
async verifyOtp(input, options = {}) {
|
|
938
|
+
const revision = this.#auth.revision();
|
|
939
|
+
const cookies = this.#auth.createCookieFlow();
|
|
781
940
|
const accessToken = await this.http.execute(AUTH_VERIFY_OTP, {
|
|
782
941
|
path: AUTH_PATHS.verifyOtp,
|
|
783
942
|
body: input,
|
|
784
943
|
skipAuth: true,
|
|
785
944
|
skipAuthRefresh: true,
|
|
945
|
+
cookieJar: cookies,
|
|
786
946
|
...options
|
|
787
947
|
});
|
|
788
|
-
await this.#auth.setAccessToken(accessToken);
|
|
948
|
+
await this.#auth.setAccessToken(accessToken, revision, cookies);
|
|
789
949
|
return accessToken;
|
|
790
950
|
}
|
|
791
951
|
/** Отправляет код подтверждения повторно. */
|
|
@@ -1704,7 +1864,7 @@ var ItdAccounts = class ItdAccounts {
|
|
|
1704
1864
|
})));
|
|
1705
1865
|
const added = [];
|
|
1706
1866
|
for (const { name, session } of sessions) {
|
|
1707
|
-
if (!isRestorableSession(session)) continue;
|
|
1867
|
+
if (!isRestorableSession(session, this.#base.baseUrl ?? "https://xn--d1ah4a.com")) continue;
|
|
1708
1868
|
if (this.#clients.has(name) || this.#removing.has(name)) continue;
|
|
1709
1869
|
this.addAccount(name);
|
|
1710
1870
|
added.push(name);
|
|
@@ -1957,6 +2117,6 @@ function createAccounts(options = {}) {
|
|
|
1957
2117
|
return new ItdAccounts(options);
|
|
1958
2118
|
}
|
|
1959
2119
|
//#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,
|
|
2120
|
+
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
2121
|
|
|
1962
2122
|
//# sourceMappingURL=index.js.map
|