itd-api 0.0.10 → 0.1.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 +8 -4
- package/dist/{chunk-SMV7TF5P.js → chunk-6FB4HTKH.js} +768 -194
- package/dist/chunk-6FB4HTKH.js.map +1 -0
- package/dist/{chunk-BN4AC3DP.cjs → chunk-73CISRBG.cjs} +768 -194
- package/dist/chunk-73CISRBG.cjs.map +1 -0
- package/dist/{index-CSjDNGCE.d.cts → index-BZF4K90s.d.cts} +169 -29
- package/dist/{index-CSjDNGCE.d.ts → index-BZF4K90s.d.ts} +169 -29
- package/dist/index.cjs +109 -109
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node.cjs +112 -112
- package/dist/node.d.cts +2 -2
- package/dist/node.d.ts +2 -2
- package/dist/node.js +2 -2
- package/guides/README.md +2 -0
- package/guides/multi-accounts/README.md +2 -1
- package/guides/plugins/README.md +93 -2
- package/guides/reference/README.md +67 -0
- package/guides/reference/accounts.md +101 -0
- package/guides/reference/auth.md +141 -0
- package/guides/reference/builders.md +135 -0
- package/guides/reference/client.md +184 -0
- package/guides/reference/comments.md +58 -0
- package/guides/reference/discovery.md +81 -0
- package/guides/reference/enums.md +103 -0
- package/guides/reference/errors.md +107 -0
- package/guides/reference/files.md +73 -0
- package/guides/reference/models.md +448 -0
- package/guides/reference/notifications.md +77 -0
- package/guides/reference/pagination.md +82 -0
- package/guides/reference/platform.md +47 -0
- package/guides/reference/posts.md +157 -0
- package/guides/reference/realtime.md +78 -0
- package/guides/reference/reports.md +28 -0
- package/guides/reference/subscription.md +41 -0
- package/guides/reference/users.md +146 -0
- package/guides/reference/verification.md +24 -0
- package/package.json +1 -1
- package/dist/chunk-BN4AC3DP.cjs.map +0 -1
- package/dist/chunk-SMV7TF5P.js.map +0 -1
|
@@ -619,8 +619,8 @@ var ItdTimeoutError = class extends ItdError {
|
|
|
619
619
|
}
|
|
620
620
|
};
|
|
621
621
|
var ItdAbortError = class extends ItdError {
|
|
622
|
-
constructor(message = "\u0417\u0430\u043F\u0440\u043E\u0441 \u043E\u0442\u043C\u0435\u043D\u0451\u043D") {
|
|
623
|
-
super(ItdErrorKind.Abort, message);
|
|
622
|
+
constructor(message = "\u0417\u0430\u043F\u0440\u043E\u0441 \u043E\u0442\u043C\u0435\u043D\u0451\u043D", options) {
|
|
623
|
+
super(ItdErrorKind.Abort, message, options);
|
|
624
624
|
this.name = "ItdAbortError";
|
|
625
625
|
}
|
|
626
626
|
};
|
|
@@ -672,18 +672,24 @@ function decodeBase64Url(segment) {
|
|
|
672
672
|
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
673
673
|
return new TextDecoder().decode(bytes);
|
|
674
674
|
}
|
|
675
|
-
function
|
|
675
|
+
function readTokenIdentity(token) {
|
|
676
676
|
try {
|
|
677
677
|
const payload = token.split(".")[1];
|
|
678
|
-
if (!payload) return
|
|
678
|
+
if (!payload) return {};
|
|
679
679
|
const parsed = JSON.parse(decodeBase64Url(payload));
|
|
680
|
-
if (typeof parsed !== "object" || parsed === null) return
|
|
681
|
-
const
|
|
682
|
-
return
|
|
680
|
+
if (typeof parsed !== "object" || parsed === null) return {};
|
|
681
|
+
const { sub, sid } = parsed;
|
|
682
|
+
return {
|
|
683
|
+
...typeof sub === "string" && sub.length > 0 ? { subject: sub } : {},
|
|
684
|
+
...typeof sid === "string" && sid.length > 0 ? { sessionId: sid } : {}
|
|
685
|
+
};
|
|
683
686
|
} catch {
|
|
684
|
-
return
|
|
687
|
+
return {};
|
|
685
688
|
}
|
|
686
689
|
}
|
|
690
|
+
function readTokenSubject(token) {
|
|
691
|
+
return readTokenIdentity(token).subject;
|
|
692
|
+
}
|
|
687
693
|
|
|
688
694
|
// src/core/runtime.ts
|
|
689
695
|
var RuntimeMode = Object.freeze({
|
|
@@ -876,6 +882,7 @@ var AuthManager = class {
|
|
|
876
882
|
#send;
|
|
877
883
|
#jar;
|
|
878
884
|
#emitter;
|
|
885
|
+
#hooks;
|
|
879
886
|
/** `undefined` — сессия ещё не читалась из хранилища. */
|
|
880
887
|
#session;
|
|
881
888
|
/**
|
|
@@ -887,8 +894,12 @@ var AuthManager = class {
|
|
|
887
894
|
#refreshing = null;
|
|
888
895
|
/** Общий промис входа по логину и паролю. */
|
|
889
896
|
#signingIn = null;
|
|
890
|
-
/**
|
|
897
|
+
/** Fallback-область для изоляции состояния плагинов, когда JWT-идентичность недоступна. */
|
|
891
898
|
#authScope = nextAuthScope();
|
|
899
|
+
/** Последний токен внешнего источника; `undefined` означает, что источник ещё не читался. */
|
|
900
|
+
#externalToken;
|
|
901
|
+
/** Идентификаторы последнего токена внешнего источника для синхронных потребителей. */
|
|
902
|
+
#externalIdentity;
|
|
892
903
|
/**
|
|
893
904
|
* Идентификатор устройства.
|
|
894
905
|
*
|
|
@@ -898,10 +909,20 @@ var AuthManager = class {
|
|
|
898
909
|
#deviceId;
|
|
899
910
|
/** Общий промис первичной выдачи `deviceId` — чтобы параллельные запросы получили один. */
|
|
900
911
|
#deviceIdLoading = null;
|
|
901
|
-
|
|
912
|
+
/**
|
|
913
|
+
* Счётчик смен владельца авторизации.
|
|
914
|
+
*
|
|
915
|
+
* Растёт при каждой операции, которая заменяет или очищает сессию извне: `clear`,
|
|
916
|
+
* `setSession`, `setAccessToken`, вход. `#performRefresh` снимает его значение перед
|
|
917
|
+
* сетевым запросом и сверяет перед записью — иначе запоздавший ответ обновления мог бы
|
|
918
|
+
* воскресить уже очищенную сессию поверх `signOut`.
|
|
919
|
+
*/
|
|
920
|
+
#authEpoch = 0;
|
|
921
|
+
constructor(config, send, jar, hooks = {}) {
|
|
902
922
|
this.#config = config;
|
|
903
923
|
this.#send = send;
|
|
904
924
|
this.#jar = jar;
|
|
925
|
+
this.#hooks = hooks;
|
|
905
926
|
this.#emitter = new Emitter(
|
|
906
927
|
(error) => reportListenerError(config.logger, "\u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438", error)
|
|
907
928
|
);
|
|
@@ -914,13 +935,67 @@ var AuthManager = class {
|
|
|
914
935
|
get once() {
|
|
915
936
|
return this.#emitter.once.bind(this.#emitter);
|
|
916
937
|
}
|
|
917
|
-
/**
|
|
938
|
+
/**
|
|
939
|
+
* Непрозрачная fallback-область авторизации.
|
|
940
|
+
*
|
|
941
|
+
* Изолирует состояние плагинов, когда идентичность аккаунта из JWT недоступна (непрозрачный
|
|
942
|
+
* токен): значение уникально для владельца и сменяется при замене или очистке сессии.
|
|
943
|
+
*/
|
|
918
944
|
getAuthScope() {
|
|
919
945
|
return this.#authScope;
|
|
920
946
|
}
|
|
947
|
+
/** Загружает сессию и возвращает идентификаторы аккаунта и сессии для плагинов. */
|
|
948
|
+
async getAuthIdentity() {
|
|
949
|
+
const session = await this.#loadSession();
|
|
950
|
+
if (session?.accessToken) return this.#identityForToken(session.accessToken);
|
|
951
|
+
const auth = this.#config.auth;
|
|
952
|
+
if (typeof auth === "object" && auth !== null && "getToken" in auth) {
|
|
953
|
+
const token = await this.#readExternalToken(() => auth.getToken());
|
|
954
|
+
return this.#identityForToken(token ?? void 0);
|
|
955
|
+
}
|
|
956
|
+
return {};
|
|
957
|
+
}
|
|
958
|
+
/** Идентификаторы аккаунта и сессии без чтения хранилища. @internal */
|
|
959
|
+
getCurrentAuthIdentity() {
|
|
960
|
+
const session = this.#session === void 0 ? this.#sessionFromConfig(this.#config.auth) : this.#session;
|
|
961
|
+
return session?.accessToken ? this.#identityForToken(session.accessToken) : this.#externalIdentity ?? {};
|
|
962
|
+
}
|
|
921
963
|
#rotateAuthScope() {
|
|
922
964
|
this.#authScope = nextAuthScope();
|
|
923
965
|
}
|
|
966
|
+
/** Отмечает смену владельца авторизации — обесценивает результат идущего обновления. */
|
|
967
|
+
#invalidateInFlight() {
|
|
968
|
+
this.#authEpoch += 1;
|
|
969
|
+
}
|
|
970
|
+
#identityForToken(accessToken) {
|
|
971
|
+
const token = accessToken ? readTokenIdentity(accessToken) : {};
|
|
972
|
+
return {
|
|
973
|
+
...token.subject ? { userId: token.subject } : {},
|
|
974
|
+
...token.sessionId ? { sessionId: token.sessionId } : {}
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
async #readExternalToken(getToken) {
|
|
978
|
+
const token = await getToken() ?? null;
|
|
979
|
+
const current = this.#identityForToken(token ?? void 0);
|
|
980
|
+
if (this.#externalToken !== void 0 && this.#externalToken !== token) {
|
|
981
|
+
this.#rotateAuthScope();
|
|
982
|
+
const previous = this.#externalIdentity ?? {};
|
|
983
|
+
const changed = previous.userId !== void 0 && current.userId !== void 0 ? previous.userId !== current.userId : true;
|
|
984
|
+
if (changed) this.#hooks.onAccountChange?.();
|
|
985
|
+
}
|
|
986
|
+
this.#externalToken = token;
|
|
987
|
+
this.#externalIdentity = current;
|
|
988
|
+
return token;
|
|
989
|
+
}
|
|
990
|
+
/** Меняет fallback и завершает realtime, только если фактически сменился аккаунт. */
|
|
991
|
+
#transitionAuth(accessToken, rotateFallback = true) {
|
|
992
|
+
const knownPrevious = this.#session !== void 0;
|
|
993
|
+
const previous = this.#identityForToken(this.#session?.accessToken);
|
|
994
|
+
const current = this.#identityForToken(accessToken);
|
|
995
|
+
const changed = previous.userId !== void 0 && current.userId !== void 0 ? previous.userId !== current.userId : previous.userId !== current.userId || rotateFallback;
|
|
996
|
+
if (rotateFallback || changed) this.#rotateAuthScope();
|
|
997
|
+
if (knownPrevious && changed) this.#hooks.onAccountChange?.();
|
|
998
|
+
}
|
|
924
999
|
/**
|
|
925
1000
|
* Есть ли признак живой refresh-сессии.
|
|
926
1001
|
*
|
|
@@ -981,7 +1056,7 @@ var AuthManager = class {
|
|
|
981
1056
|
const auth = this.#config.auth;
|
|
982
1057
|
if (!auth) return null;
|
|
983
1058
|
if (typeof auth === "object" && "getToken" in auth) {
|
|
984
|
-
return
|
|
1059
|
+
return this.#readExternalToken(() => auth.getToken());
|
|
985
1060
|
}
|
|
986
1061
|
if (typeof auth === "object" && "email" in auth) {
|
|
987
1062
|
return this.#signInWithCredentials(auth);
|
|
@@ -1036,7 +1111,9 @@ var AuthManager = class {
|
|
|
1036
1111
|
}
|
|
1037
1112
|
/** Сохраняет токен, полученный извне, — например после подтверждения OTP. */
|
|
1038
1113
|
async setAccessToken(accessToken) {
|
|
1039
|
-
this.#
|
|
1114
|
+
await this.#loadSession();
|
|
1115
|
+
this.#invalidateInFlight();
|
|
1116
|
+
this.#transitionAuth(accessToken);
|
|
1040
1117
|
await this.#saveSession({ ...this.#session ?? {}, accessToken, obtainedAt: Date.now() });
|
|
1041
1118
|
this.#emitter.emit("tokens", { accessToken });
|
|
1042
1119
|
}
|
|
@@ -1057,7 +1134,9 @@ var AuthManager = class {
|
|
|
1057
1134
|
}
|
|
1058
1135
|
/** Заменяет сессию и связанные с ней cookie целиком. */
|
|
1059
1136
|
async setSession(session) {
|
|
1060
|
-
this.#
|
|
1137
|
+
await this.#loadSession();
|
|
1138
|
+
this.#invalidateInFlight();
|
|
1139
|
+
this.#transitionAuth(session.accessToken);
|
|
1061
1140
|
this.#jar.clear();
|
|
1062
1141
|
this.#jar.deserialize(session.cookies);
|
|
1063
1142
|
if (session.deviceId) this.#deviceId = session.deviceId;
|
|
@@ -1072,8 +1151,10 @@ var AuthManager = class {
|
|
|
1072
1151
|
* новую запись в списке сессий.
|
|
1073
1152
|
*/
|
|
1074
1153
|
async clear() {
|
|
1154
|
+
await this.#loadSession();
|
|
1155
|
+
this.#invalidateInFlight();
|
|
1156
|
+
this.#transitionAuth(void 0);
|
|
1075
1157
|
this.#session = null;
|
|
1076
|
-
this.#rotateAuthScope();
|
|
1077
1158
|
this.#jar.clear();
|
|
1078
1159
|
await this.#config.storage.clear();
|
|
1079
1160
|
if (this.#deviceId) await this.#saveSession({ deviceId: this.#deviceId });
|
|
@@ -1154,6 +1235,7 @@ var AuthManager = class {
|
|
|
1154
1235
|
if (!this.#hasRefreshSession()) {
|
|
1155
1236
|
return this.#reloginOrNull();
|
|
1156
1237
|
}
|
|
1238
|
+
const epoch = this.#authEpoch;
|
|
1157
1239
|
try {
|
|
1158
1240
|
const payload = await this.#send({
|
|
1159
1241
|
method: "POST",
|
|
@@ -1166,10 +1248,14 @@ var AuthManager = class {
|
|
|
1166
1248
|
});
|
|
1167
1249
|
const accessToken = readAccessToken(payload);
|
|
1168
1250
|
if (!accessToken) return this.#reloginOrNull();
|
|
1251
|
+
if (this.#authEpoch !== epoch) {
|
|
1252
|
+
return this.#session?.accessToken ?? null;
|
|
1253
|
+
}
|
|
1169
1254
|
const rotated = this.#jar.getValue(
|
|
1170
1255
|
REFRESH_COOKIE,
|
|
1171
1256
|
this.#config.baseUrl + REFRESH_COOKIE_PATH
|
|
1172
1257
|
);
|
|
1258
|
+
this.#transitionAuth(accessToken, false);
|
|
1173
1259
|
await this.#saveSession({
|
|
1174
1260
|
...this.#session ?? {},
|
|
1175
1261
|
accessToken,
|
|
@@ -1180,8 +1266,11 @@ var AuthManager = class {
|
|
|
1180
1266
|
return accessToken;
|
|
1181
1267
|
} catch (error) {
|
|
1182
1268
|
if (error instanceof ItdApiError) {
|
|
1269
|
+
if (this.#authEpoch !== epoch) {
|
|
1270
|
+
return this.#session?.accessToken ?? null;
|
|
1271
|
+
}
|
|
1272
|
+
this.#transitionAuth(void 0);
|
|
1183
1273
|
this.#session = null;
|
|
1184
|
-
this.#rotateAuthScope();
|
|
1185
1274
|
this.#jar.clear();
|
|
1186
1275
|
await this.#config.storage.clear();
|
|
1187
1276
|
const relogged = await this.#reloginOrNull();
|
|
@@ -1249,7 +1338,8 @@ var AuthManager = class {
|
|
|
1249
1338
|
"\u0412\u0445\u043E\u0434 \u043F\u043E email \u0438 \u043F\u0430\u0440\u043E\u043B\u044E \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u044F \u043A\u043E\u0434\u043E\u043C \u0438\u0437 \u043F\u0438\u0441\u044C\u043C\u0430. \u0410\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u0432\u0445\u043E\u0434 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D: \u0432\u043E\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435\u0441\u044C itd.auth.signInWithOtp() \u0438 \u043F\u0435\u0440\u0435\u0434\u0430\u0439\u0442\u0435 \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044B\u0439 accessToken \u0432 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044E \u043A\u043B\u0438\u0435\u043D\u0442\u0430."
|
|
1250
1339
|
);
|
|
1251
1340
|
}
|
|
1252
|
-
this.#
|
|
1341
|
+
this.#invalidateInFlight();
|
|
1342
|
+
this.#transitionAuth(accessToken);
|
|
1253
1343
|
await this.#saveSession({ accessToken, obtainedAt: Date.now() });
|
|
1254
1344
|
this.#emitter.emit("tokens", { accessToken });
|
|
1255
1345
|
this.#emitter.emit("signIn", { accessToken });
|
|
@@ -1316,7 +1406,7 @@ function normalizeBaseUrl(baseUrl) {
|
|
|
1316
1406
|
}
|
|
1317
1407
|
|
|
1318
1408
|
// src/core/version.ts
|
|
1319
|
-
var LIBRARY_VERSION = "0.0
|
|
1409
|
+
var LIBRARY_VERSION = "0.1.0";
|
|
1320
1410
|
|
|
1321
1411
|
// src/core/config.ts
|
|
1322
1412
|
var DEFAULT_BASE_URL = "https://xn--d1ah4a.com";
|
|
@@ -1601,6 +1691,396 @@ function withLayerHeaders(request, headers) {
|
|
|
1601
1691
|
return { ...request, layerHeaders: { ...request.layerHeaders, ...headers } };
|
|
1602
1692
|
}
|
|
1603
1693
|
|
|
1694
|
+
// src/core/plugins.ts
|
|
1695
|
+
var NO_KEYS = /* @__PURE__ */ new Set();
|
|
1696
|
+
var RESERVED_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
1697
|
+
"signal",
|
|
1698
|
+
"timeout",
|
|
1699
|
+
"headers",
|
|
1700
|
+
"retry",
|
|
1701
|
+
"method",
|
|
1702
|
+
"path",
|
|
1703
|
+
"service",
|
|
1704
|
+
"baseUrl",
|
|
1705
|
+
"query",
|
|
1706
|
+
"body",
|
|
1707
|
+
"skipAuth",
|
|
1708
|
+
"skipAuthRefresh",
|
|
1709
|
+
"skipQueue",
|
|
1710
|
+
"raw"
|
|
1711
|
+
]);
|
|
1712
|
+
var RELATION_FIELDS = ["requires", "conflicts", "before", "after"];
|
|
1713
|
+
var HOOK_FIELDS = ["onRequest", "onResponse", "onError", "onRetry"];
|
|
1714
|
+
function validateNameList(plugin, field) {
|
|
1715
|
+
const values = plugin[field];
|
|
1716
|
+
if (values === void 0) return;
|
|
1717
|
+
if (!Array.isArray(values)) {
|
|
1718
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB: ${field} \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043C\u0430\u0441\u0441\u0438\u0432\u043E\u043C`);
|
|
1719
|
+
}
|
|
1720
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1721
|
+
for (const value of values) {
|
|
1722
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
1723
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB: ${field} \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u0442 \u043F\u0443\u0441\u0442\u043E\u0435 \u0438\u043C\u044F \u043F\u043B\u0430\u0433\u0438\u043D\u0430`);
|
|
1724
|
+
}
|
|
1725
|
+
if (value === plugin.name) {
|
|
1726
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0443\u043A\u0430\u0437\u0430\u0442\u044C \u0441\u0435\u0431\u044F \u0432 ${field}`);
|
|
1727
|
+
}
|
|
1728
|
+
if (seen.has(value)) {
|
|
1729
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB: ${field} \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442 \u0438\u043C\u044F \xAB${value}\xBB`);
|
|
1730
|
+
}
|
|
1731
|
+
seen.add(value);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
function validateHooks(plugin, hooks) {
|
|
1735
|
+
if (typeof hooks !== "object" || hooks === null) {
|
|
1736
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin}\xBB \u043F\u0435\u0440\u0435\u0434\u0430\u043B \u0432 useHooks() \u043D\u0435 \u043E\u0431\u044A\u0435\u043A\u0442`);
|
|
1737
|
+
}
|
|
1738
|
+
for (const field of HOOK_FIELDS) {
|
|
1739
|
+
if (hooks[field] !== void 0 && typeof hooks[field] !== "function") {
|
|
1740
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin}\xBB: useHooks().${field} \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0444\u0443\u043D\u043A\u0446\u0438\u0435\u0439`);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
function validatePluginDefinition(plugin) {
|
|
1745
|
+
if (typeof plugin?.install !== "function") {
|
|
1746
|
+
throw new ItdConfigError("\u041F\u043B\u0430\u0433\u0438\u043D \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043E\u0431\u044A\u0435\u043A\u0442\u043E\u043C \u0441 \u043C\u0435\u0442\u043E\u0434\u043E\u043C install()");
|
|
1747
|
+
}
|
|
1748
|
+
const name = plugin.name;
|
|
1749
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
1750
|
+
throw new ItdConfigError("\u0423 \u043F\u043B\u0430\u0433\u0438\u043D\u0430 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435\u043F\u0443\u0441\u0442\u043E\u0435 \u0438\u043C\u044F");
|
|
1751
|
+
}
|
|
1752
|
+
const keys = plugin.optionKeys ?? [];
|
|
1753
|
+
if (!Array.isArray(keys)) {
|
|
1754
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${name}\xBB: optionKeys \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043C\u0430\u0441\u0441\u0438\u0432\u043E\u043C`);
|
|
1755
|
+
}
|
|
1756
|
+
for (const key of keys) {
|
|
1757
|
+
if (typeof key !== "string" || key.trim() === "") {
|
|
1758
|
+
throw new ItdConfigError(`\u041F\u043B\u0430\u0433\u0438\u043D \xAB${name}\xBB \u0437\u0430\u044F\u0432\u0438\u043B \u043F\u0443\u0441\u0442\u043E\u0435 \u0438\u043C\u044F \u043E\u043F\u0446\u0438\u0438`);
|
|
1759
|
+
}
|
|
1760
|
+
if (RESERVED_OPTION_KEYS.has(key)) {
|
|
1761
|
+
throw new ItdConfigError(
|
|
1762
|
+
`\u041F\u043B\u0430\u0433\u0438\u043D \xAB${name}\xBB \u0437\u0430\u044F\u0432\u0438\u043B \u043E\u043F\u0446\u0438\u044E \xAB${key}\xBB: \u044D\u0442\u043E \u043F\u043E\u043B\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u0430, \u0438\u043C\u044F \u0437\u0430\u043D\u044F\u0442\u043E. \u0417\u0430\u043D\u044F\u0442\u044B\u0435 \u0438\u043C\u0435\u043D\u0430: ${[...RESERVED_OPTION_KEYS].join(", ")}`
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
for (const field of RELATION_FIELDS) validateNameList(plugin, field);
|
|
1767
|
+
}
|
|
1768
|
+
function addEdge(from, to, edges, indegree) {
|
|
1769
|
+
const targets = edges.get(from);
|
|
1770
|
+
if (!targets || targets.has(to)) return;
|
|
1771
|
+
targets.add(to);
|
|
1772
|
+
indegree.set(to, (indegree.get(to) ?? 0) + 1);
|
|
1773
|
+
}
|
|
1774
|
+
function orderPluginDefinitions(plugins) {
|
|
1775
|
+
const entries = [];
|
|
1776
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1777
|
+
for (const [sequence, plugin] of plugins.entries()) {
|
|
1778
|
+
validatePluginDefinition(plugin);
|
|
1779
|
+
if (byName.has(plugin.name)) {
|
|
1780
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB \u0443\u0436\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D`);
|
|
1781
|
+
}
|
|
1782
|
+
const entry = { plugin, sequence };
|
|
1783
|
+
entries.push(entry);
|
|
1784
|
+
byName.set(plugin.name, entry);
|
|
1785
|
+
}
|
|
1786
|
+
for (const { plugin } of entries) {
|
|
1787
|
+
for (const required of plugin.requires ?? []) {
|
|
1788
|
+
if (!byName.has(required)) {
|
|
1789
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D\u0443 \xAB${plugin.name}\xBB \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u043F\u043B\u0430\u0433\u0438\u043D \xAB${required}\xBB`);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
for (const conflict of plugin.conflicts ?? []) {
|
|
1793
|
+
if (byName.has(conflict)) {
|
|
1794
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB \u043D\u0435\u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C \u0441 \u043F\u043B\u0430\u0433\u0438\u043D\u043E\u043C \xAB${conflict}\xBB`);
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
for (const { plugin: other } of entries) {
|
|
1798
|
+
if (other.conflicts?.includes(plugin.name)) {
|
|
1799
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB \u043D\u0435\u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C \u0441 \u043F\u043B\u0430\u0433\u0438\u043D\u043E\u043C \xAB${other.name}\xBB`);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
const edges = /* @__PURE__ */ new Map();
|
|
1804
|
+
const indegree = /* @__PURE__ */ new Map();
|
|
1805
|
+
for (const { plugin } of entries) {
|
|
1806
|
+
edges.set(plugin.name, /* @__PURE__ */ new Set());
|
|
1807
|
+
indegree.set(plugin.name, 0);
|
|
1808
|
+
}
|
|
1809
|
+
for (const { plugin } of entries) {
|
|
1810
|
+
for (const required of plugin.requires ?? []) {
|
|
1811
|
+
addEdge(required, plugin.name, edges, indegree);
|
|
1812
|
+
}
|
|
1813
|
+
for (const target of plugin.before ?? []) {
|
|
1814
|
+
if (byName.has(target)) addEdge(plugin.name, target, edges, indegree);
|
|
1815
|
+
}
|
|
1816
|
+
for (const target of plugin.after ?? []) {
|
|
1817
|
+
if (byName.has(target)) addEdge(target, plugin.name, edges, indegree);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
const ready = entries.filter(({ plugin }) => indegree.get(plugin.name) === 0);
|
|
1821
|
+
ready.sort((a, b) => a.sequence - b.sequence);
|
|
1822
|
+
const ordered = [];
|
|
1823
|
+
while (ready.length > 0) {
|
|
1824
|
+
const current = ready.shift();
|
|
1825
|
+
if (!current) break;
|
|
1826
|
+
ordered.push(current.plugin);
|
|
1827
|
+
for (const target of edges.get(current.plugin.name) ?? []) {
|
|
1828
|
+
const next = (indegree.get(target) ?? 0) - 1;
|
|
1829
|
+
indegree.set(target, next);
|
|
1830
|
+
if (next === 0) {
|
|
1831
|
+
const entry = byName.get(target);
|
|
1832
|
+
if (entry) {
|
|
1833
|
+
ready.push(entry);
|
|
1834
|
+
ready.sort((a, b) => a.sequence - b.sequence);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
if (ordered.length !== entries.length) {
|
|
1840
|
+
const cycle = entries.filter(({ plugin }) => (indegree.get(plugin.name) ?? 0) > 0).map(({ plugin }) => plugin.name);
|
|
1841
|
+
throw new ItdConfigError(`\u0446\u0438\u043A\u043B\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u043F\u043E\u0440\u044F\u0434\u043E\u043A \u043F\u043B\u0430\u0433\u0438\u043D\u043E\u0432: ${cycle.join(" \u2192 ")}`);
|
|
1842
|
+
}
|
|
1843
|
+
return ordered;
|
|
1844
|
+
}
|
|
1845
|
+
function assertPluginRemovable(plugins, name) {
|
|
1846
|
+
const dependent = plugins.find((plugin) => plugin.requires?.includes(name));
|
|
1847
|
+
if (dependent) {
|
|
1848
|
+
throw new ItdConfigError(
|
|
1849
|
+
`\u043D\u0435\u043B\u044C\u0437\u044F \u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043F\u043B\u0430\u0433\u0438\u043D \xAB${name}\xBB: \u043E\u0442 \u043D\u0435\u0433\u043E \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \xAB${dependent.name}\xBB`
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
var REQUEST_HOOK_DISPATCHERS = /* @__PURE__ */ new WeakMap();
|
|
1854
|
+
var PLUGIN_HOOK_SCOPE = /* @__PURE__ */ Symbol("itd-api.plugin-hooks");
|
|
1855
|
+
async function dispatchRequestHook(hooks, field, context, request) {
|
|
1856
|
+
const dispatcher = REQUEST_HOOK_DISPATCHERS.get(hooks);
|
|
1857
|
+
if (dispatcher) {
|
|
1858
|
+
await dispatcher(field, context, request);
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
const hook = hooks[field];
|
|
1862
|
+
await hook?.(context);
|
|
1863
|
+
}
|
|
1864
|
+
var PluginRegistry = class {
|
|
1865
|
+
#entries = /* @__PURE__ */ new Map();
|
|
1866
|
+
#optionKeys = /* @__PURE__ */ new Set();
|
|
1867
|
+
#removing = /* @__PURE__ */ new Set();
|
|
1868
|
+
#cleanups = /* @__PURE__ */ new Set();
|
|
1869
|
+
#ordered = [];
|
|
1870
|
+
/** Сколько плагинов подключено. */
|
|
1871
|
+
get size() {
|
|
1872
|
+
return this.#entries.size;
|
|
1873
|
+
}
|
|
1874
|
+
/** Имена опций активных плагинов. */
|
|
1875
|
+
get optionKeys() {
|
|
1876
|
+
return this.#optionKeys.size === 0 ? NO_KEYS : this.#optionKeys;
|
|
1877
|
+
}
|
|
1878
|
+
/** Имена плагинов в фактическом порядке выполнения. */
|
|
1879
|
+
names() {
|
|
1880
|
+
return this.#ordered.map(({ plugin }) => plugin.name);
|
|
1881
|
+
}
|
|
1882
|
+
/** Подключён ли плагин с таким именем. */
|
|
1883
|
+
has(name) {
|
|
1884
|
+
return this.#entries.has(name);
|
|
1885
|
+
}
|
|
1886
|
+
/** Проверяет добавление без вызова `install()`. @internal */
|
|
1887
|
+
assertCanAdd(plugin) {
|
|
1888
|
+
validatePluginDefinition(plugin);
|
|
1889
|
+
if (this.#removing.has(plugin.name)) {
|
|
1890
|
+
throw new ItdConfigError(
|
|
1891
|
+
`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB \u0435\u0449\u0451 \u043E\u0442\u043A\u043B\u044E\u0447\u0430\u0435\u0442\u0441\u044F; \u0434\u043E\u0436\u0434\u0438\u0442\u0435\u0441\u044C \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F unuse() \u0438\u043B\u0438 dispose()`
|
|
1892
|
+
);
|
|
1893
|
+
}
|
|
1894
|
+
orderPluginDefinitions([...this.#ordered.map((entry) => entry.plugin), plugin]);
|
|
1895
|
+
}
|
|
1896
|
+
/** Проверяет удаление без изменения реестра. @internal */
|
|
1897
|
+
assertCanRemove(name) {
|
|
1898
|
+
const entry = this.#entries.get(name);
|
|
1899
|
+
if (!entry) return;
|
|
1900
|
+
assertPluginRemovable(
|
|
1901
|
+
this.#ordered.map((current) => current.plugin),
|
|
1902
|
+
name
|
|
1903
|
+
);
|
|
1904
|
+
}
|
|
1905
|
+
/**
|
|
1906
|
+
* Подключает плагин.
|
|
1907
|
+
*
|
|
1908
|
+
* @throws {ItdConfigError} если плагин задан неверно, уже подключён, нарушает зависимости
|
|
1909
|
+
* или заявил занятое имя опции
|
|
1910
|
+
*/
|
|
1911
|
+
add(plugin, context) {
|
|
1912
|
+
this.assertCanAdd(plugin);
|
|
1913
|
+
const ordered = orderPluginDefinitions([...this.#ordered.map((entry) => entry.plugin), plugin]);
|
|
1914
|
+
const transformers = [];
|
|
1915
|
+
const hooks = [];
|
|
1916
|
+
const installed = plugin.install({
|
|
1917
|
+
...context,
|
|
1918
|
+
use: (transformer) => {
|
|
1919
|
+
if (typeof transformer !== "function") {
|
|
1920
|
+
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB \u043F\u0435\u0440\u0435\u0434\u0430\u043B \u0432 use() \u043D\u0435 \u0444\u0443\u043D\u043A\u0446\u0438\u044E`);
|
|
1921
|
+
}
|
|
1922
|
+
transformers.push(transformer);
|
|
1923
|
+
},
|
|
1924
|
+
useHooks: (value) => {
|
|
1925
|
+
validateHooks(plugin.name, value);
|
|
1926
|
+
hooks.push({ ...value });
|
|
1927
|
+
}
|
|
1928
|
+
});
|
|
1929
|
+
const teardown = typeof installed === "function" ? installed : void 0;
|
|
1930
|
+
this.#entries.set(plugin.name, {
|
|
1931
|
+
plugin,
|
|
1932
|
+
transformers,
|
|
1933
|
+
hooks,
|
|
1934
|
+
teardown,
|
|
1935
|
+
activeRequests: 0,
|
|
1936
|
+
drain: void 0,
|
|
1937
|
+
finishDrain: void 0
|
|
1938
|
+
});
|
|
1939
|
+
this.#ordered = ordered.map((definition) => {
|
|
1940
|
+
const entry = this.#entries.get(definition.name);
|
|
1941
|
+
if (!entry) throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${definition.name}\xBB \u043D\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D`);
|
|
1942
|
+
return entry;
|
|
1943
|
+
});
|
|
1944
|
+
this.#rebuildOptionKeys();
|
|
1945
|
+
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Отключает плагин и вызывает его функцию очистки.
|
|
1948
|
+
*
|
|
1949
|
+
* Новые запросы перестают видеть плагин сразу. Если его обёртка уже выполняется,
|
|
1950
|
+
* очистка дождётся завершения этого логического запроса.
|
|
1951
|
+
*
|
|
1952
|
+
* @returns `false`, если такого плагина не было
|
|
1953
|
+
*/
|
|
1954
|
+
async remove(name) {
|
|
1955
|
+
const entry = this.#entries.get(name);
|
|
1956
|
+
if (!entry) return false;
|
|
1957
|
+
this.assertCanRemove(name);
|
|
1958
|
+
this.#entries.delete(name);
|
|
1959
|
+
this.#ordered = this.#ordered.filter((current) => current !== entry);
|
|
1960
|
+
this.#rebuildOptionKeys();
|
|
1961
|
+
this.#removing.add(name);
|
|
1962
|
+
const cleanup = this.#trackCleanup(
|
|
1963
|
+
(async () => {
|
|
1964
|
+
await this.#waitForDrain(entry);
|
|
1965
|
+
await entry.teardown?.();
|
|
1966
|
+
})()
|
|
1967
|
+
);
|
|
1968
|
+
try {
|
|
1969
|
+
await cleanup;
|
|
1970
|
+
return true;
|
|
1971
|
+
} finally {
|
|
1972
|
+
this.#removing.delete(name);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Отключает все плагины окончательно.
|
|
1977
|
+
*
|
|
1978
|
+
* Очистка идёт изнутри наружу — в порядке, обратном выполнению обёрток.
|
|
1979
|
+
*/
|
|
1980
|
+
async dispose() {
|
|
1981
|
+
const entries = [...this.#ordered].reverse();
|
|
1982
|
+
const previousCleanups = [...this.#cleanups];
|
|
1983
|
+
this.#entries.clear();
|
|
1984
|
+
this.#ordered = [];
|
|
1985
|
+
this.#optionKeys.clear();
|
|
1986
|
+
for (const { plugin } of entries) this.#removing.add(plugin.name);
|
|
1987
|
+
const cleanup = this.#trackCleanup(
|
|
1988
|
+
(async () => {
|
|
1989
|
+
const errors = [];
|
|
1990
|
+
const previous = await Promise.allSettled(previousCleanups);
|
|
1991
|
+
for (const result of previous) {
|
|
1992
|
+
if (result.status === "rejected") errors.push(result.reason);
|
|
1993
|
+
}
|
|
1994
|
+
for (const entry of entries) {
|
|
1995
|
+
try {
|
|
1996
|
+
await this.#waitForDrain(entry);
|
|
1997
|
+
await entry.teardown?.();
|
|
1998
|
+
} catch (error) {
|
|
1999
|
+
errors.push(error);
|
|
2000
|
+
} finally {
|
|
2001
|
+
this.#removing.delete(entry.plugin.name);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
if (errors.length > 0) {
|
|
2005
|
+
throw new AggregateError(errors, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u044C \u0440\u0435\u0441\u0443\u0440\u0441\u044B \u043F\u043B\u0430\u0433\u0438\u043D\u043E\u0432");
|
|
2006
|
+
}
|
|
2007
|
+
})()
|
|
2008
|
+
);
|
|
2009
|
+
await cleanup;
|
|
2010
|
+
}
|
|
2011
|
+
/**
|
|
2012
|
+
* Объединяет конструкторские хуки с хуками подключаемых плагинов.
|
|
2013
|
+
*
|
|
2014
|
+
* Возвращённый объект динамический: подключение и отключение плагина начинает действовать
|
|
2015
|
+
* со следующего логического запроса без пересоздания транспорта.
|
|
2016
|
+
*/
|
|
2017
|
+
hooks(base) {
|
|
2018
|
+
const hooks = {};
|
|
2019
|
+
REQUEST_HOOK_DISPATCHERS.set(hooks, ((field, context, request) => this.#runHook(field, context, request, base)));
|
|
2020
|
+
return hooks;
|
|
2021
|
+
}
|
|
2022
|
+
/**
|
|
2023
|
+
* Прогоняет запрос через цепочку обёрток.
|
|
2024
|
+
*
|
|
2025
|
+
* Снимок цепочки берётся в начале: `unuse()` влияет на новые запросы, но не обрывает
|
|
2026
|
+
* уже выполняющийся посередине.
|
|
2027
|
+
*
|
|
2028
|
+
* @param execute настоящий запрос, вызывается самой внутренней обёрткой
|
|
2029
|
+
*/
|
|
2030
|
+
async run(request, execute) {
|
|
2031
|
+
const entries = [...this.#ordered];
|
|
2032
|
+
for (const entry of entries) entry.activeRequests += 1;
|
|
2033
|
+
const hookScope = entries.flatMap((entry) => entry.hooks);
|
|
2034
|
+
const scoped = (current) => current[PLUGIN_HOOK_SCOPE] === hookScope ? current : { ...current, [PLUGIN_HOOK_SCOPE]: hookScope };
|
|
2035
|
+
const chain = entries.flatMap((entry) => entry.transformers).reduceRight(
|
|
2036
|
+
(next, transformer) => (current) => transformer(scoped(current), (prepared) => next(scoped(prepared))),
|
|
2037
|
+
(current) => execute(scoped(current))
|
|
2038
|
+
);
|
|
2039
|
+
try {
|
|
2040
|
+
return await chain(scoped(request));
|
|
2041
|
+
} finally {
|
|
2042
|
+
for (const entry of entries) {
|
|
2043
|
+
entry.activeRequests -= 1;
|
|
2044
|
+
if (entry.activeRequests === 0) {
|
|
2045
|
+
entry.finishDrain?.();
|
|
2046
|
+
entry.finishDrain = void 0;
|
|
2047
|
+
entry.drain = void 0;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
#rebuildOptionKeys() {
|
|
2053
|
+
this.#optionKeys.clear();
|
|
2054
|
+
for (const { plugin } of this.#ordered) {
|
|
2055
|
+
for (const key of plugin.optionKeys ?? []) this.#optionKeys.add(key);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
#waitForDrain(entry) {
|
|
2059
|
+
if (entry.activeRequests === 0) return Promise.resolve();
|
|
2060
|
+
entry.drain ??= new Promise((resolve) => {
|
|
2061
|
+
entry.finishDrain = resolve;
|
|
2062
|
+
});
|
|
2063
|
+
return entry.drain;
|
|
2064
|
+
}
|
|
2065
|
+
#trackCleanup(cleanup) {
|
|
2066
|
+
this.#cleanups.add(cleanup);
|
|
2067
|
+
void cleanup.then(
|
|
2068
|
+
() => this.#cleanups.delete(cleanup),
|
|
2069
|
+
() => this.#cleanups.delete(cleanup)
|
|
2070
|
+
);
|
|
2071
|
+
return cleanup;
|
|
2072
|
+
}
|
|
2073
|
+
async #runHook(field, context, request, base) {
|
|
2074
|
+
const baseHook = base[field];
|
|
2075
|
+
await baseHook?.(context);
|
|
2076
|
+
const scope = request[PLUGIN_HOOK_SCOPE] ?? [];
|
|
2077
|
+
for (const hooks of scope) {
|
|
2078
|
+
const hook = hooks[field];
|
|
2079
|
+
await hook?.(context);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
};
|
|
2083
|
+
|
|
1604
2084
|
// src/core/retry.ts
|
|
1605
2085
|
var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
1606
2086
|
function isRetryable(error, method, retryWrites) {
|
|
@@ -1656,10 +2136,7 @@ function createQueueMiddleware(schedule) {
|
|
|
1656
2136
|
return (request, next) => request.skipQueue ? next(request) : schedule(request, () => next(request));
|
|
1657
2137
|
}
|
|
1658
2138
|
function createPluginsMiddleware(plugins) {
|
|
1659
|
-
return (request, next) =>
|
|
1660
|
-
if (plugins.size === 0) return next(request);
|
|
1661
|
-
return plugins.run(request, next);
|
|
1662
|
-
};
|
|
2139
|
+
return (request, next) => plugins.run(request, next);
|
|
1663
2140
|
}
|
|
1664
2141
|
function createServicesMiddleware(registry) {
|
|
1665
2142
|
return async (request, next) => {
|
|
@@ -1731,16 +2208,21 @@ function createRetryMiddleware(deps) {
|
|
|
1731
2208
|
} catch (error) {
|
|
1732
2209
|
const delay = nextDelay(error, attempt, request, method, backoff);
|
|
1733
2210
|
if (delay === void 0) throw error;
|
|
1734
|
-
await
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
2211
|
+
await dispatchRequestHook(
|
|
2212
|
+
deps.hooks,
|
|
2213
|
+
"onRetry",
|
|
2214
|
+
{
|
|
2215
|
+
method,
|
|
2216
|
+
path: request.path,
|
|
2217
|
+
url: deps.buildUrl(request),
|
|
2218
|
+
// Умолчания транспорта добавляются после слоя повторов и сюда не входят.
|
|
2219
|
+
headers: new Headers({ ...request.layerHeaders, ...request.headers }),
|
|
2220
|
+
attempt,
|
|
2221
|
+
error,
|
|
2222
|
+
delay
|
|
2223
|
+
},
|
|
2224
|
+
request
|
|
2225
|
+
);
|
|
1744
2226
|
deps.logger?.debug(
|
|
1745
2227
|
`\u043F\u043E\u0432\u0442\u043E\u0440 ${method} ${request.path}, \u043F\u043E\u043F\u044B\u0442\u043A\u0430 ${attempt + 1} \u0447\u0435\u0440\u0435\u0437 ${delay} \u043C\u0441`
|
|
1746
2228
|
);
|
|
@@ -1750,104 +2232,6 @@ function createRetryMiddleware(deps) {
|
|
|
1750
2232
|
};
|
|
1751
2233
|
}
|
|
1752
2234
|
|
|
1753
|
-
// src/core/plugins.ts
|
|
1754
|
-
var NO_KEYS = /* @__PURE__ */ new Set();
|
|
1755
|
-
var RESERVED_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
1756
|
-
"signal",
|
|
1757
|
-
"timeout",
|
|
1758
|
-
"headers",
|
|
1759
|
-
"retry",
|
|
1760
|
-
"method",
|
|
1761
|
-
"path",
|
|
1762
|
-
"service",
|
|
1763
|
-
"baseUrl",
|
|
1764
|
-
"query",
|
|
1765
|
-
"body",
|
|
1766
|
-
"skipAuth",
|
|
1767
|
-
"skipAuthRefresh",
|
|
1768
|
-
"skipQueue",
|
|
1769
|
-
"raw"
|
|
1770
|
-
]);
|
|
1771
|
-
function validatePluginDefinition(plugin) {
|
|
1772
|
-
if (typeof plugin?.install !== "function") {
|
|
1773
|
-
throw new ItdConfigError("\u041F\u043B\u0430\u0433\u0438\u043D \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u043E\u0431\u044A\u0435\u043A\u0442\u043E\u043C \u0441 \u043C\u0435\u0442\u043E\u0434\u043E\u043C install()");
|
|
1774
|
-
}
|
|
1775
|
-
const name = plugin.name;
|
|
1776
|
-
if (typeof name !== "string" || name.trim() === "") {
|
|
1777
|
-
throw new ItdConfigError("\u0423 \u043F\u043B\u0430\u0433\u0438\u043D\u0430 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043D\u0435\u043F\u0443\u0441\u0442\u043E\u0435 \u0438\u043C\u044F");
|
|
1778
|
-
}
|
|
1779
|
-
const keys = plugin.optionKeys ?? [];
|
|
1780
|
-
for (const key of keys) {
|
|
1781
|
-
if (typeof key !== "string" || key.trim() === "") {
|
|
1782
|
-
throw new ItdConfigError(`\u041F\u043B\u0430\u0433\u0438\u043D \xAB${name}\xBB \u0437\u0430\u044F\u0432\u0438\u043B \u043F\u0443\u0441\u0442\u043E\u0435 \u0438\u043C\u044F \u043E\u043F\u0446\u0438\u0438`);
|
|
1783
|
-
}
|
|
1784
|
-
if (RESERVED_OPTION_KEYS.has(key)) {
|
|
1785
|
-
throw new ItdConfigError(
|
|
1786
|
-
`\u041F\u043B\u0430\u0433\u0438\u043D \xAB${name}\xBB \u0437\u0430\u044F\u0432\u0438\u043B \u043E\u043F\u0446\u0438\u044E \xAB${key}\xBB: \u044D\u0442\u043E \u043F\u043E\u043B\u0435 \u0437\u0430\u043F\u0440\u043E\u0441\u0430, \u0438\u043C\u044F \u0437\u0430\u043D\u044F\u0442\u043E. \u0417\u0430\u043D\u044F\u0442\u044B\u0435 \u0438\u043C\u0435\u043D\u0430: ${[...RESERVED_OPTION_KEYS].join(", ")}`
|
|
1787
|
-
);
|
|
1788
|
-
}
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
var PluginRegistry = class {
|
|
1792
|
-
#transformers = [];
|
|
1793
|
-
#optionKeys = /* @__PURE__ */ new Set();
|
|
1794
|
-
#names = /* @__PURE__ */ new Set();
|
|
1795
|
-
/** Сколько обёрток подключено. Ноль означает, что запрос идёт прежним путём. */
|
|
1796
|
-
get size() {
|
|
1797
|
-
return this.#transformers.length;
|
|
1798
|
-
}
|
|
1799
|
-
/** Имена опций запроса, заявленные плагинами. */
|
|
1800
|
-
get optionKeys() {
|
|
1801
|
-
return this.#optionKeys.size === 0 ? NO_KEYS : this.#optionKeys;
|
|
1802
|
-
}
|
|
1803
|
-
/**
|
|
1804
|
-
* Подключает плагин.
|
|
1805
|
-
*
|
|
1806
|
-
* @throws {ItdConfigError} если плагин задан неверно, уже подключён или заявил занятое
|
|
1807
|
-
* имя опции
|
|
1808
|
-
*/
|
|
1809
|
-
add(plugin, context) {
|
|
1810
|
-
validatePluginDefinition(plugin);
|
|
1811
|
-
const name = plugin.name;
|
|
1812
|
-
if (this.#names.has(name)) {
|
|
1813
|
-
throw new ItdConfigError(`\u041F\u043B\u0430\u0433\u0438\u043D \xAB${name}\xBB \u0443\u0436\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D`);
|
|
1814
|
-
}
|
|
1815
|
-
const keys = plugin.optionKeys ?? [];
|
|
1816
|
-
const before = this.#transformers.length;
|
|
1817
|
-
try {
|
|
1818
|
-
plugin.install({
|
|
1819
|
-
...context,
|
|
1820
|
-
use: (transformer) => {
|
|
1821
|
-
if (typeof transformer !== "function") {
|
|
1822
|
-
throw new ItdConfigError(`\u041F\u043B\u0430\u0433\u0438\u043D \xAB${name}\xBB \u043F\u0435\u0440\u0435\u0434\u0430\u043B \u0432 use() \u043D\u0435 \u0444\u0443\u043D\u043A\u0446\u0438\u044E`);
|
|
1823
|
-
}
|
|
1824
|
-
this.#transformers.push(transformer);
|
|
1825
|
-
}
|
|
1826
|
-
});
|
|
1827
|
-
} catch (error) {
|
|
1828
|
-
this.#transformers.length = before;
|
|
1829
|
-
throw error;
|
|
1830
|
-
}
|
|
1831
|
-
this.#names.add(name);
|
|
1832
|
-
for (const key of keys) this.#optionKeys.add(key);
|
|
1833
|
-
}
|
|
1834
|
-
/**
|
|
1835
|
-
* Прогоняет запрос через цепочку обёрток.
|
|
1836
|
-
*
|
|
1837
|
-
* Цепочка собирается на каждый запрос заново: плагин можно подключить в любой момент,
|
|
1838
|
-
* а обёрток единицы — экономить тут не на чем.
|
|
1839
|
-
*
|
|
1840
|
-
* @param execute настоящий запрос, вызывается самой внутренней обёрткой
|
|
1841
|
-
*/
|
|
1842
|
-
run(request, execute) {
|
|
1843
|
-
const chain = this.#transformers.reduceRight(
|
|
1844
|
-
(next, transformer) => (current) => transformer(current, next),
|
|
1845
|
-
execute
|
|
1846
|
-
);
|
|
1847
|
-
return chain(request);
|
|
1848
|
-
}
|
|
1849
|
-
};
|
|
1850
|
-
|
|
1851
2235
|
// src/core/rate-limit.ts
|
|
1852
2236
|
function queueAbortError() {
|
|
1853
2237
|
return new ItdAbortError("\u0417\u0430\u043F\u0440\u043E\u0441 \u043E\u0442\u043C\u0435\u043D\u0451\u043D \u0432\u043E \u0432\u0440\u0435\u043C\u044F \u043E\u0436\u0438\u0434\u0430\u043D\u0438\u044F \u043E\u0447\u0435\u0440\u0435\u0434\u0438");
|
|
@@ -2445,7 +2829,7 @@ var Transport = class {
|
|
|
2445
2829
|
}
|
|
2446
2830
|
}
|
|
2447
2831
|
const context = { method, path: request.path, url, headers, attempt };
|
|
2448
|
-
await this.#config.hooks
|
|
2832
|
+
await dispatchRequestHook(this.#config.hooks, "onRequest", context, request);
|
|
2449
2833
|
const timeout = request.timeout ?? this.#config.timeout;
|
|
2450
2834
|
const abort = createAbortBundle(request.signal, timeout);
|
|
2451
2835
|
const startedAt = Date.now();
|
|
@@ -2466,7 +2850,12 @@ var Transport = class {
|
|
|
2466
2850
|
} catch (error) {
|
|
2467
2851
|
const duration2 = Date.now() - startedAt;
|
|
2468
2852
|
const failure = this.#toTransportError(error, abort, request, method, timeout);
|
|
2469
|
-
await
|
|
2853
|
+
await dispatchRequestHook(
|
|
2854
|
+
this.#config.hooks,
|
|
2855
|
+
"onError",
|
|
2856
|
+
{ ...context, duration: duration2, error: failure },
|
|
2857
|
+
request
|
|
2858
|
+
);
|
|
2470
2859
|
this.#config.logger?.warn(
|
|
2471
2860
|
`\xD7 ${method} ${request.path} (${duration2} \u043C\u0441): ${failure.message}`
|
|
2472
2861
|
);
|
|
@@ -2478,12 +2867,17 @@ var Transport = class {
|
|
|
2478
2867
|
}
|
|
2479
2868
|
if (this.#config.useCookieJar) this.#deps.cookies?.setFromResponse(url, response);
|
|
2480
2869
|
if (response.ok) {
|
|
2481
|
-
await
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2870
|
+
await dispatchRequestHook(
|
|
2871
|
+
this.#config.hooks,
|
|
2872
|
+
"onResponse",
|
|
2873
|
+
{
|
|
2874
|
+
...context,
|
|
2875
|
+
status: response.status,
|
|
2876
|
+
duration: Date.now() - startedAt,
|
|
2877
|
+
response
|
|
2878
|
+
},
|
|
2879
|
+
request
|
|
2880
|
+
);
|
|
2487
2881
|
}
|
|
2488
2882
|
const payload = await this.#readBodyOrFail(
|
|
2489
2883
|
response,
|
|
@@ -2504,7 +2898,12 @@ var Transport = class {
|
|
|
2504
2898
|
response,
|
|
2505
2899
|
body: payload
|
|
2506
2900
|
});
|
|
2507
|
-
await
|
|
2901
|
+
await dispatchRequestHook(
|
|
2902
|
+
this.#config.hooks,
|
|
2903
|
+
"onError",
|
|
2904
|
+
{ ...context, duration, error },
|
|
2905
|
+
request
|
|
2906
|
+
);
|
|
2508
2907
|
this.#config.logger?.warn(
|
|
2509
2908
|
`\u2190 ${response.status} ${method} ${request.path} (${duration} \u043C\u0441): ${error.message}`
|
|
2510
2909
|
);
|
|
@@ -2525,11 +2924,16 @@ var Transport = class {
|
|
|
2525
2924
|
await response.body?.cancel().catch(() => {
|
|
2526
2925
|
});
|
|
2527
2926
|
const failure = this.#toTransportError(error, abort, request, method, timeout);
|
|
2528
|
-
await
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2927
|
+
await dispatchRequestHook(
|
|
2928
|
+
this.#config.hooks,
|
|
2929
|
+
"onError",
|
|
2930
|
+
{
|
|
2931
|
+
...context,
|
|
2932
|
+
duration: Date.now() - startedAt,
|
|
2933
|
+
error: failure
|
|
2934
|
+
},
|
|
2935
|
+
request
|
|
2936
|
+
);
|
|
2533
2937
|
this.#config.logger?.warn(
|
|
2534
2938
|
`\xD7 ${method} ${request.path}: \u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0442\u044C \u0442\u0435\u043B\u043E \u043E\u0442\u0432\u0435\u0442\u0430 \u2014 ${failure.message}`
|
|
2535
2939
|
);
|
|
@@ -2580,12 +2984,16 @@ var Transport = class {
|
|
|
2580
2984
|
}
|
|
2581
2985
|
/** Превращает исключение `fetch` в понятную ошибку библиотеки. */
|
|
2582
2986
|
#toTransportError(error, abort, request, method, timeout) {
|
|
2583
|
-
const aborted = error instanceof Error && error.name === "AbortError";
|
|
2987
|
+
const aborted = abort.signal.aborted || error instanceof Error && error.name === "AbortError";
|
|
2584
2988
|
if (aborted && abort.timedOut()) {
|
|
2585
2989
|
return new ItdTimeoutError({ timeout, method, path: request.path });
|
|
2586
2990
|
}
|
|
2587
2991
|
if (aborted) {
|
|
2588
|
-
|
|
2992
|
+
const reason = request.signal?.reason;
|
|
2993
|
+
return new ItdAbortError(
|
|
2994
|
+
`\u0417\u0430\u043F\u0440\u043E\u0441 ${method} ${request.path} \u043E\u0442\u043C\u0435\u043D\u0451\u043D`,
|
|
2995
|
+
reason !== void 0 ? { cause: reason } : void 0
|
|
2996
|
+
);
|
|
2589
2997
|
}
|
|
2590
2998
|
return new ItdNetworkError(
|
|
2591
2999
|
`\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C ${method} ${request.path}: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -2883,7 +3291,6 @@ var PollTransport = class {
|
|
|
2883
3291
|
const seen = /* @__PURE__ */ new Set();
|
|
2884
3292
|
let firstRun = true;
|
|
2885
3293
|
let lastUnreadCount;
|
|
2886
|
-
context.onOpen();
|
|
2887
3294
|
while (!context.signal.aborted) {
|
|
2888
3295
|
const token = await context.getToken();
|
|
2889
3296
|
if (!token) throw new UnauthorizedStreamError();
|
|
@@ -2898,6 +3305,7 @@ var PollTransport = class {
|
|
|
2898
3305
|
});
|
|
2899
3306
|
if (response.status === 401) throw new UnauthorizedStreamError();
|
|
2900
3307
|
if (!response.ok) throw new Error(`\u041E\u043F\u0440\u043E\u0441 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0439 \u0432\u0435\u0440\u043D\u0443\u043B \u0441\u0442\u0430\u0442\u0443\u0441 ${response.status}`);
|
|
3308
|
+
context.onOpen();
|
|
2901
3309
|
const body = await response.json();
|
|
2902
3310
|
const payload = typeof body === "object" && body !== null && "data" in body ? body.data : body;
|
|
2903
3311
|
const items = pickArray(payload, "notifications");
|
|
@@ -3143,10 +3551,12 @@ var STREAM_PATH = "/api/notifications/stream";
|
|
|
3143
3551
|
var SseTransport = class {
|
|
3144
3552
|
name = "sse";
|
|
3145
3553
|
#idleTimeout;
|
|
3554
|
+
#handshakeTimeout;
|
|
3146
3555
|
/** Идентификатор последнего события — отправляется при переподключении. */
|
|
3147
3556
|
#lastEventId;
|
|
3148
3557
|
constructor(options = {}) {
|
|
3149
3558
|
this.#idleTimeout = options.idleTimeout ?? 9e4;
|
|
3559
|
+
this.#handshakeTimeout = options.handshakeTimeout ?? 2e4;
|
|
3150
3560
|
}
|
|
3151
3561
|
async connect(context) {
|
|
3152
3562
|
const token = await context.getToken();
|
|
@@ -3157,16 +3567,36 @@ var SseTransport = class {
|
|
|
3157
3567
|
headers.set("Authorization", `Bearer ${token}`);
|
|
3158
3568
|
headers.set("Cache-Control", "no-cache");
|
|
3159
3569
|
if (this.#lastEventId) headers.set("Last-Event-ID", this.#lastEventId);
|
|
3160
|
-
const
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3570
|
+
const controller = new AbortController();
|
|
3571
|
+
const relayAbort = () => controller.abort(context.signal.reason);
|
|
3572
|
+
if (context.signal.aborted) controller.abort(context.signal.reason);
|
|
3573
|
+
else context.signal.addEventListener("abort", relayAbort, { once: true });
|
|
3574
|
+
try {
|
|
3575
|
+
const response = await this.#handshake(url, headers, context, controller);
|
|
3576
|
+
if (response.status === 401) throw new UnauthorizedStreamError();
|
|
3577
|
+
if (!response.ok) throw new Error(`\u041F\u043E\u0442\u043E\u043A \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0439 \u0432\u0435\u0440\u043D\u0443\u043B \u0441\u0442\u0430\u0442\u0443\u0441 ${response.status}`);
|
|
3578
|
+
if (!response.body) throw new Error("\u041E\u0442\u0432\u0435\u0442 \u043F\u043E\u0442\u043E\u043A\u0430 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0439 \u043F\u0443\u0441\u0442");
|
|
3579
|
+
context.onOpen();
|
|
3580
|
+
await this.#read(response.body, context);
|
|
3581
|
+
} finally {
|
|
3582
|
+
context.signal.removeEventListener("abort", relayAbort);
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
/** Выполняет запрос потока, обрывая его, если ответ не пришёл за отведённое время. */
|
|
3586
|
+
async #handshake(url, headers, context, controller) {
|
|
3587
|
+
let expired = false;
|
|
3588
|
+
const timer = this.#handshakeTimeout > 0 ? setTimeout(() => {
|
|
3589
|
+
expired = true;
|
|
3590
|
+
controller.abort(new Error("\u0418\u0441\u0442\u0451\u043A \u0442\u0430\u0439\u043C\u0430\u0443\u0442 \u0440\u0443\u043A\u043E\u043F\u043E\u0436\u0430\u0442\u0438\u044F SSE"));
|
|
3591
|
+
}, this.#handshakeTimeout) : void 0;
|
|
3592
|
+
try {
|
|
3593
|
+
return await context.fetch(url, { method: "GET", headers, signal: controller.signal });
|
|
3594
|
+
} catch (error) {
|
|
3595
|
+
if (expired) throw new Error("\u041F\u043E\u0442\u043E\u043A \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0439 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B: \u0438\u0441\u0442\u0451\u043A \u0442\u0430\u0439\u043C\u0430\u0443\u0442 \u0440\u0443\u043A\u043E\u043F\u043E\u0436\u0430\u0442\u0438\u044F");
|
|
3596
|
+
throw error;
|
|
3597
|
+
} finally {
|
|
3598
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
3599
|
+
}
|
|
3170
3600
|
}
|
|
3171
3601
|
async #read(body, context) {
|
|
3172
3602
|
const reader = body.getReader();
|
|
@@ -3238,6 +3668,7 @@ function validateRealtimeOptions(options) {
|
|
|
3238
3668
|
positiveInteger(options.maxAttempts, "maxAttempts");
|
|
3239
3669
|
duration(options.pollInterval, "pollInterval", 1);
|
|
3240
3670
|
duration(options.idleTimeout, "idleTimeout", 0);
|
|
3671
|
+
duration(options.handshakeTimeout, "handshakeTimeout", 0);
|
|
3241
3672
|
if (options.jitter !== void 0 && !(options.jitter >= 0 && options.jitter <= 1)) {
|
|
3242
3673
|
throw new ItdConfigError(
|
|
3243
3674
|
`realtime.jitter \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0432 \u0434\u0438\u0430\u043F\u0430\u0437\u043E\u043D\u0435 0\u20261, \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043E: ${options.jitter}`
|
|
@@ -3290,6 +3721,18 @@ var ItdRealtime = class {
|
|
|
3290
3721
|
get transport() {
|
|
3291
3722
|
return this.#transport.name;
|
|
3292
3723
|
}
|
|
3724
|
+
/** Базовый URL клиента, создавшего поток. @internal */
|
|
3725
|
+
get baseUrl() {
|
|
3726
|
+
return this.#deps.baseUrl;
|
|
3727
|
+
}
|
|
3728
|
+
/** Идентификаторы аккаунта и сессии клиента, создавшего поток. @internal */
|
|
3729
|
+
getAuthIdentity() {
|
|
3730
|
+
return this.#deps.getAuthIdentity?.();
|
|
3731
|
+
}
|
|
3732
|
+
/** Непрозрачная область авторизации создавшего поток клиента. @internal */
|
|
3733
|
+
getAuthScope() {
|
|
3734
|
+
return this.#deps.getAuthScope?.();
|
|
3735
|
+
}
|
|
3293
3736
|
/** Подписывается на событие потока. @returns функция отписки */
|
|
3294
3737
|
on(event, listener) {
|
|
3295
3738
|
return this.#emitter.on(event, listener);
|
|
@@ -3347,7 +3790,8 @@ var ItdRealtime = class {
|
|
|
3347
3790
|
});
|
|
3348
3791
|
}
|
|
3349
3792
|
return new SseTransport({
|
|
3350
|
-
...this.#options.idleTimeout !== void 0 ? { idleTimeout: this.#options.idleTimeout } : {}
|
|
3793
|
+
...this.#options.idleTimeout !== void 0 ? { idleTimeout: this.#options.idleTimeout } : {},
|
|
3794
|
+
...this.#options.handshakeTimeout !== void 0 ? { handshakeTimeout: this.#options.handshakeTimeout } : {}
|
|
3351
3795
|
});
|
|
3352
3796
|
}
|
|
3353
3797
|
/** Запускает попытку подключения; повторы планирует сам. */
|
|
@@ -3562,6 +4006,11 @@ var Paginator = class {
|
|
|
3562
4006
|
#finished = false;
|
|
3563
4007
|
#pagesLoaded = 0;
|
|
3564
4008
|
constructor(options) {
|
|
4009
|
+
if (options.maxPages !== void 0 && (!Number.isInteger(options.maxPages) || options.maxPages < 1)) {
|
|
4010
|
+
throw new ItdConfigError(
|
|
4011
|
+
`maxPages \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0446\u0435\u043B\u044B\u043C \u0447\u0438\u0441\u043B\u043E\u043C \u043D\u0435 \u043C\u0435\u043D\u044C\u0448\u0435 1, \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043E: ${options.maxPages}`
|
|
4012
|
+
);
|
|
4013
|
+
}
|
|
3565
4014
|
this.#options = options;
|
|
3566
4015
|
this.#maxPages = options.maxPages ?? 1e3;
|
|
3567
4016
|
this.#state = options.start ?? {};
|
|
@@ -3611,6 +4060,7 @@ var Paginator = class {
|
|
|
3611
4060
|
* @param max сколько элементов достаточно; без него перебираются все страницы
|
|
3612
4061
|
*/
|
|
3613
4062
|
async collect(max) {
|
|
4063
|
+
if (max !== void 0 && max <= 0) return [];
|
|
3614
4064
|
const result = [];
|
|
3615
4065
|
for await (const item of this) {
|
|
3616
4066
|
result.push(item);
|
|
@@ -6086,6 +6536,13 @@ var VerificationResource = class extends BaseResource {
|
|
|
6086
6536
|
};
|
|
6087
6537
|
|
|
6088
6538
|
// src/client.ts
|
|
6539
|
+
var CLIENT_PLUGIN_REGISTRIES = /* @__PURE__ */ new WeakMap();
|
|
6540
|
+
function assertClientCanUsePlugin(client, plugin) {
|
|
6541
|
+
CLIENT_PLUGIN_REGISTRIES.get(client)?.assertCanAdd(plugin);
|
|
6542
|
+
}
|
|
6543
|
+
function assertClientCanUnusePlugin(client, name) {
|
|
6544
|
+
CLIENT_PLUGIN_REGISTRIES.get(client)?.assertCanRemove(name);
|
|
6545
|
+
}
|
|
6089
6546
|
var ItdClient = class _ItdClient {
|
|
6090
6547
|
#config;
|
|
6091
6548
|
#http;
|
|
@@ -6123,13 +6580,10 @@ var ItdClient = class _ItdClient {
|
|
|
6123
6580
|
subscription;
|
|
6124
6581
|
/** Сведения о платформе: изменения, анонсы, баннер события. */
|
|
6125
6582
|
platform;
|
|
6126
|
-
/**
|
|
6127
|
-
* Телеметрия просмотров.
|
|
6128
|
-
*
|
|
6129
|
-
* @experimental Недокументированные эндпоинты. Библиотека никогда не отправляет их сама.
|
|
6130
|
-
*/
|
|
6583
|
+
/** Телеметрия просмотров. */
|
|
6131
6584
|
telemetry;
|
|
6132
6585
|
constructor(options = {}, internals = {}) {
|
|
6586
|
+
CLIENT_PLUGIN_REGISTRIES.set(this, this.#plugins);
|
|
6133
6587
|
const config = resolveConfig(options);
|
|
6134
6588
|
this.#config = config;
|
|
6135
6589
|
this.#jar = new CookieJar();
|
|
@@ -6141,18 +6595,22 @@ var ItdClient = class _ItdClient {
|
|
|
6141
6595
|
this.#queues = queues;
|
|
6142
6596
|
this.#ownsQueues = shared === void 0;
|
|
6143
6597
|
let authManager;
|
|
6144
|
-
const
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6598
|
+
const hooks = this.#plugins.hooks(config.hooks);
|
|
6599
|
+
const transport = new Transport(
|
|
6600
|
+
{ ...config, hooks },
|
|
6601
|
+
{
|
|
6602
|
+
cookies: config.useCookieJar ? this.#jar : void 0,
|
|
6603
|
+
getDeviceId: () => authManager.getDeviceId(),
|
|
6604
|
+
onRateLimit: queues && config.rateLimit?.respectHeaders ? (limit, remaining, request) => this.#throttleByHeaders(limit, remaining, request) : void 0
|
|
6605
|
+
}
|
|
6606
|
+
);
|
|
6149
6607
|
this.#transport = transport;
|
|
6150
6608
|
const pluginsLayer = createPluginsMiddleware(this.#plugins);
|
|
6151
6609
|
const retriesLayer = createRetryMiddleware({
|
|
6152
6610
|
retry: config.retry,
|
|
6153
6611
|
rateLimitDelays: config.rateLimit?.retryDelays ?? [],
|
|
6154
6612
|
pauseQueue: queues ? (ms, request) => queues.for(request.service).pause(ms) : void 0,
|
|
6155
|
-
hooks
|
|
6613
|
+
hooks,
|
|
6156
6614
|
logger: config.logger,
|
|
6157
6615
|
buildUrl: (request) => transport.buildUrl(request)
|
|
6158
6616
|
});
|
|
@@ -6166,7 +6624,9 @@ var ItdClient = class _ItdClient {
|
|
|
6166
6624
|
} : void 0;
|
|
6167
6625
|
const authPipeline = composePipeline([pluginsLayer, retriesLayer], transport.send);
|
|
6168
6626
|
const authHandler = (request) => authRetry && request.retry === void 0 ? authPipeline({ ...request, retry: authRetry }) : authPipeline(request);
|
|
6169
|
-
authManager = new AuthManager(config, authHandler, this.#jar
|
|
6627
|
+
authManager = new AuthManager(config, authHandler, this.#jar, {
|
|
6628
|
+
onAccountChange: () => this.#disconnectStreams()
|
|
6629
|
+
});
|
|
6170
6630
|
this.#authManager = authManager;
|
|
6171
6631
|
const middlewares = [];
|
|
6172
6632
|
if (queues) {
|
|
@@ -6245,10 +6705,31 @@ var ItdClient = class _ItdClient {
|
|
|
6245
6705
|
this.#plugins.add(plugin, {
|
|
6246
6706
|
baseUrl: this.#config.baseUrl,
|
|
6247
6707
|
logger: this.#config.logger,
|
|
6248
|
-
getAuthScope: () => this.#authManager.getAuthScope()
|
|
6708
|
+
getAuthScope: () => this.#authManager.getAuthScope(),
|
|
6709
|
+
getAuthIdentity: () => this.#authManager.getAuthIdentity()
|
|
6249
6710
|
});
|
|
6250
6711
|
return this;
|
|
6251
6712
|
}
|
|
6713
|
+
/** Имена подключённых плагинов в фактическом порядке выполнения обёрток. */
|
|
6714
|
+
pluginNames() {
|
|
6715
|
+
return this.#plugins.names();
|
|
6716
|
+
}
|
|
6717
|
+
/** Подключён ли плагин с таким именем. */
|
|
6718
|
+
hasPlugin(name) {
|
|
6719
|
+
return this.#plugins.has(name);
|
|
6720
|
+
}
|
|
6721
|
+
/**
|
|
6722
|
+
* Отключает плагин и освобождает заведённые им ресурсы.
|
|
6723
|
+
*
|
|
6724
|
+
* Новые запросы перестают видеть плагин сразу. Очистка дождётся логического запроса,
|
|
6725
|
+
* который уже проходил через его обёртку.
|
|
6726
|
+
*
|
|
6727
|
+
* @returns `false`, если такого плагина не было
|
|
6728
|
+
* @throws {ItdConfigError} если от плагина зависит другой подключённый плагин
|
|
6729
|
+
*/
|
|
6730
|
+
unuse(name) {
|
|
6731
|
+
return this.#plugins.remove(name);
|
|
6732
|
+
}
|
|
6252
6733
|
/**
|
|
6253
6734
|
* Регистрирует сервис платформы — домен, отличный от основного.
|
|
6254
6735
|
*
|
|
@@ -6305,7 +6786,8 @@ var ItdClient = class _ItdClient {
|
|
|
6305
6786
|
* Создаёт поток уведомлений в реальном времени.
|
|
6306
6787
|
*
|
|
6307
6788
|
* Каждый вызов даёт новый независимый поток; обычно он нужен один на приложение.
|
|
6308
|
-
* Соединение поднимается методом `connect()` и держится само.
|
|
6789
|
+
* Соединение поднимается методом `connect()` и держится само. Замена авторизации на токен
|
|
6790
|
+
* другого пользователя завершает все потоки клиента; смена только сессии их не затрагивает.
|
|
6309
6791
|
*
|
|
6310
6792
|
* @example
|
|
6311
6793
|
* ```ts
|
|
@@ -6326,6 +6808,8 @@ var ItdClient = class _ItdClient {
|
|
|
6326
6808
|
baseUrl: this.#config.baseUrl,
|
|
6327
6809
|
fetch: this.#config.fetch,
|
|
6328
6810
|
baseHeaders: (url) => this.#transport.platformHeaders(url),
|
|
6811
|
+
getAuthIdentity: () => this.#authManager.getCurrentAuthIdentity(),
|
|
6812
|
+
getAuthScope: () => this.#authManager.getAuthScope(),
|
|
6329
6813
|
getToken: () => this.#authManager.getAccessToken(),
|
|
6330
6814
|
refresh: () => this.#authManager.onUnauthorized(),
|
|
6331
6815
|
fetchUnreadCount: () => this.notifications.count(),
|
|
@@ -6351,17 +6835,33 @@ var ItdClient = class _ItdClient {
|
|
|
6351
6835
|
* ```ts
|
|
6352
6836
|
* await using itd = new ItdClient({ auth: token });
|
|
6353
6837
|
* // …работа…
|
|
6354
|
-
* //
|
|
6838
|
+
* // dispose() вызовется сам на выходе из блока
|
|
6355
6839
|
* ```
|
|
6356
6840
|
*/
|
|
6357
6841
|
async close() {
|
|
6358
|
-
|
|
6359
|
-
this.#streams.clear();
|
|
6842
|
+
this.#disconnectStreams();
|
|
6360
6843
|
if (this.#ownsQueues) this.#queues?.stop();
|
|
6361
6844
|
}
|
|
6845
|
+
/**
|
|
6846
|
+
* Окончательно освобождает клиент: выполняет {@link close} и отключает все плагины.
|
|
6847
|
+
*
|
|
6848
|
+
* В отличие от `close()`, после `dispose()` плагины не восстанавливаются автоматически.
|
|
6849
|
+
* Сам клиент остаётся пригоден для обычных запросов; при необходимости плагины можно
|
|
6850
|
+
* подключить заново через {@link use}.
|
|
6851
|
+
*/
|
|
6852
|
+
async dispose() {
|
|
6853
|
+
const results = await Promise.allSettled([this.close(), this.#plugins.dispose()]);
|
|
6854
|
+
const errors = results.filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
6855
|
+
if (errors.length > 0) throw new AggregateError(errors, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u044C \u043A\u043B\u0438\u0435\u043D\u0442");
|
|
6856
|
+
}
|
|
6857
|
+
/** Завершает потоки до того, как запросы начнут использовать другой аккаунт. */
|
|
6858
|
+
#disconnectStreams() {
|
|
6859
|
+
for (const stream of [...this.#streams]) stream.disconnect();
|
|
6860
|
+
this.#streams.clear();
|
|
6861
|
+
}
|
|
6362
6862
|
/** Позволяет использовать клиент с `await using`. */
|
|
6363
6863
|
[Symbol.asyncDispose]() {
|
|
6364
|
-
return this.
|
|
6864
|
+
return this.dispose();
|
|
6365
6865
|
}
|
|
6366
6866
|
static {
|
|
6367
6867
|
if (typeof Symbol.asyncDispose !== "symbol") {
|
|
@@ -6560,10 +7060,13 @@ var ItdAccounts = class _ItdAccounts {
|
|
|
6560
7060
|
#eventUnsubscribers = /* @__PURE__ */ new Map();
|
|
6561
7061
|
/** Плагины для всех: и для уже заведённых аккаунтов, и для будущих. */
|
|
6562
7062
|
#plugins;
|
|
7063
|
+
/** Имена плагинов, чья асинхронная очистка ещё не завершилась. */
|
|
7064
|
+
#removingPlugins = /* @__PURE__ */ new Set();
|
|
6563
7065
|
/** Общая очередь. `undefined`, когда у каждого аккаунта своя. */
|
|
6564
7066
|
#queues;
|
|
6565
7067
|
#rateLimitScope;
|
|
6566
7068
|
#emitter;
|
|
7069
|
+
#logger;
|
|
6567
7070
|
#createClient;
|
|
6568
7071
|
constructor(options = {}, internals = {}) {
|
|
6569
7072
|
const { storage, plugins, rateLimitScope, ...base } = options;
|
|
@@ -6572,19 +7075,13 @@ var ItdAccounts = class _ItdAccounts {
|
|
|
6572
7075
|
}
|
|
6573
7076
|
this.#base = base;
|
|
6574
7077
|
this.#storage = storage ?? new MemoryMultiTokenStorage();
|
|
6575
|
-
this.#plugins = [];
|
|
6576
|
-
for (const plugin of plugins ?? []) {
|
|
6577
|
-
validatePluginDefinition(plugin);
|
|
6578
|
-
if (this.#plugins.some((added) => added.name === plugin.name)) {
|
|
6579
|
-
throw new ItdConfigError(`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB \u0443\u0436\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D`);
|
|
6580
|
-
}
|
|
6581
|
-
this.#plugins.push(plugin);
|
|
6582
|
-
}
|
|
7078
|
+
this.#plugins = orderPluginDefinitions(plugins ?? []);
|
|
6583
7079
|
this.#rateLimitScope = rateLimitScope ?? "account";
|
|
6584
7080
|
this.#createClient = internals.createClient ?? ((clientOptions, clientInternals) => new ItdClient(clientOptions, clientInternals));
|
|
6585
7081
|
const rateLimit = this.#rateLimitScope === "shared" ? resolveRateLimit(base.rateLimit) : void 0;
|
|
6586
7082
|
this.#queues = rateLimit ? new RequestQueuePool(rateLimit) : void 0;
|
|
6587
7083
|
const logger = typeof base.logger === "object" ? base.logger : void 0;
|
|
7084
|
+
this.#logger = logger;
|
|
6588
7085
|
this.#emitter = new Emitter(
|
|
6589
7086
|
(error) => reportListenerError(logger, "\u0430\u043A\u043A\u0430\u0443\u043D\u0442\u043E\u0432", error)
|
|
6590
7087
|
);
|
|
@@ -6650,6 +7147,11 @@ var ItdAccounts = class _ItdAccounts {
|
|
|
6650
7147
|
for (const plugin of this.#plugins) client.use(plugin);
|
|
6651
7148
|
} catch (error) {
|
|
6652
7149
|
storageControl.revoke();
|
|
7150
|
+
if (client) {
|
|
7151
|
+
void client.dispose().catch((cleanupError) => {
|
|
7152
|
+
this.#reportPluginCleanup("\u043D\u0435\u0443\u0434\u0430\u0447\u043D\u043E\u0433\u043E \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0430\u043A\u043A\u0430\u0443\u043D\u0442\u0430", cleanupError);
|
|
7153
|
+
});
|
|
7154
|
+
}
|
|
6653
7155
|
throw error;
|
|
6654
7156
|
}
|
|
6655
7157
|
const unsubscribers = this.#forwardEvents(name, client);
|
|
@@ -6731,7 +7233,7 @@ var ItdAccounts = class _ItdAccounts {
|
|
|
6731
7233
|
try {
|
|
6732
7234
|
const errors = [];
|
|
6733
7235
|
const closing = await Promise.allSettled([
|
|
6734
|
-
client.
|
|
7236
|
+
client.dispose(),
|
|
6735
7237
|
storageControl?.drain() ?? Promise.resolve()
|
|
6736
7238
|
]);
|
|
6737
7239
|
for (const result of closing) {
|
|
@@ -6759,14 +7261,64 @@ var ItdAccounts = class _ItdAccounts {
|
|
|
6759
7261
|
* ```
|
|
6760
7262
|
*/
|
|
6761
7263
|
use(plugin) {
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
7264
|
+
if (this.#removingPlugins.has(plugin?.name)) {
|
|
7265
|
+
throw new ItdConfigError(
|
|
7266
|
+
`\u043F\u043B\u0430\u0433\u0438\u043D \xAB${plugin.name}\xBB \u0435\u0449\u0451 \u043E\u0442\u043A\u043B\u044E\u0447\u0430\u0435\u0442\u0441\u044F; \u0434\u043E\u0436\u0434\u0438\u0442\u0435\u0441\u044C \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0438\u044F accounts.unuse()`
|
|
7267
|
+
);
|
|
6765
7268
|
}
|
|
6766
|
-
|
|
6767
|
-
this.#
|
|
7269
|
+
const ordered = orderPluginDefinitions([...this.#plugins, plugin]);
|
|
7270
|
+
const clients = [...this.#clients.values()];
|
|
7271
|
+
for (const client of clients) assertClientCanUsePlugin(client, plugin);
|
|
7272
|
+
const installed = [];
|
|
7273
|
+
try {
|
|
7274
|
+
for (const client of clients) {
|
|
7275
|
+
client.use(plugin);
|
|
7276
|
+
installed.push(client);
|
|
7277
|
+
}
|
|
7278
|
+
} catch (error) {
|
|
7279
|
+
for (const client of installed.reverse()) {
|
|
7280
|
+
void client.unuse(plugin.name).catch((cleanupError) => {
|
|
7281
|
+
this.#reportPluginCleanup(`\u043E\u0442\u043A\u0430\u0442\u0430 \u043F\u043B\u0430\u0433\u0438\u043D\u0430 \xAB${plugin.name}\xBB`, cleanupError);
|
|
7282
|
+
});
|
|
7283
|
+
}
|
|
7284
|
+
throw error;
|
|
7285
|
+
}
|
|
7286
|
+
this.#plugins.splice(0, this.#plugins.length, ...ordered);
|
|
6768
7287
|
return this;
|
|
6769
7288
|
}
|
|
7289
|
+
/** Имена общих плагинов в фактическом порядке выполнения обёрток. */
|
|
7290
|
+
pluginNames() {
|
|
7291
|
+
return this.#plugins.map((plugin) => plugin.name);
|
|
7292
|
+
}
|
|
7293
|
+
/** Подключён ли общий плагин с таким именем. */
|
|
7294
|
+
hasPlugin(name) {
|
|
7295
|
+
return this.#plugins.some((plugin) => plugin.name === name);
|
|
7296
|
+
}
|
|
7297
|
+
/**
|
|
7298
|
+
* Отключает общий плагин у существующих клиентов и не применяет его к будущим.
|
|
7299
|
+
*
|
|
7300
|
+
* @returns `false`, если такого плагина не было
|
|
7301
|
+
* @throws {ItdConfigError} если от плагина зависит другой общий плагин
|
|
7302
|
+
*/
|
|
7303
|
+
async unuse(name) {
|
|
7304
|
+
const index = this.#plugins.findIndex((plugin) => plugin.name === name);
|
|
7305
|
+
if (index < 0) return false;
|
|
7306
|
+
assertPluginRemovable(this.#plugins, name);
|
|
7307
|
+
const clients = [...this.#clients.values()];
|
|
7308
|
+
for (const client of clients) assertClientCanUnusePlugin(client, name);
|
|
7309
|
+
this.#plugins.splice(index, 1);
|
|
7310
|
+
this.#removingPlugins.add(name);
|
|
7311
|
+
try {
|
|
7312
|
+
const results = await Promise.allSettled(clients.map((client) => client.unuse(name)));
|
|
7313
|
+
const errors = results.filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
7314
|
+
if (errors.length > 0) {
|
|
7315
|
+
throw new AggregateError(errors, `\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u043F\u043B\u0430\u0433\u0438\u043D \xAB${name}\xBB \u0443 \u0432\u0441\u0435\u0445 \u0430\u043A\u043A\u0430\u0443\u043D\u0442\u043E\u0432`);
|
|
7316
|
+
}
|
|
7317
|
+
return true;
|
|
7318
|
+
} finally {
|
|
7319
|
+
this.#removingPlugins.delete(name);
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
6770
7322
|
/**
|
|
6771
7323
|
* Подписывается на события авторизации всех аккаунтов сразу.
|
|
6772
7324
|
*
|
|
@@ -6804,16 +7356,32 @@ var ItdAccounts = class _ItdAccounts {
|
|
|
6804
7356
|
* ```ts
|
|
6805
7357
|
* await using accounts = new ItdAccounts({ storage });
|
|
6806
7358
|
* // …работа…
|
|
6807
|
-
* //
|
|
7359
|
+
* // dispose() вызовется сам на выходе из блока
|
|
6808
7360
|
* ```
|
|
6809
7361
|
*/
|
|
6810
7362
|
async close() {
|
|
6811
7363
|
await Promise.all([...this.#clients.values()].map((client) => client.close()));
|
|
6812
7364
|
this.#queues?.stop();
|
|
6813
7365
|
}
|
|
7366
|
+
/**
|
|
7367
|
+
* Окончательно освобождает контейнер и отключает общие плагины у всех аккаунтов.
|
|
7368
|
+
*
|
|
7369
|
+
* Для временной остановки потоков и очереди без отключения плагинов используйте
|
|
7370
|
+
* {@link close}.
|
|
7371
|
+
*/
|
|
7372
|
+
async dispose() {
|
|
7373
|
+
const results = await Promise.allSettled([
|
|
7374
|
+
...[...this.#clients.values()].map((client) => client.dispose()),
|
|
7375
|
+
Promise.resolve().then(() => this.#queues?.stop())
|
|
7376
|
+
]);
|
|
7377
|
+
this.#plugins.splice(0);
|
|
7378
|
+
this.#removingPlugins.clear();
|
|
7379
|
+
const errors = results.filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
7380
|
+
if (errors.length > 0) throw new AggregateError(errors, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u044C \u0430\u043A\u043A\u0430\u0443\u043D\u0442\u044B");
|
|
7381
|
+
}
|
|
6814
7382
|
/** Позволяет использовать контейнер с `await using`. */
|
|
6815
7383
|
[Symbol.asyncDispose]() {
|
|
6816
|
-
return this.
|
|
7384
|
+
return this.dispose();
|
|
6817
7385
|
}
|
|
6818
7386
|
static {
|
|
6819
7387
|
if (typeof Symbol.asyncDispose !== "symbol") {
|
|
@@ -6839,6 +7407,12 @@ var ItdAccounts = class _ItdAccounts {
|
|
|
6839
7407
|
storage
|
|
6840
7408
|
};
|
|
6841
7409
|
}
|
|
7410
|
+
/** Не теряет ошибку фонового teardown, который синхронный API не может await-нуть. */
|
|
7411
|
+
#reportPluginCleanup(scope, error) {
|
|
7412
|
+
const message = `\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0442\u044C teardown \u043F\u043E\u0441\u043B\u0435 ${scope}`;
|
|
7413
|
+
if (this.#logger) this.#logger.error(message, error);
|
|
7414
|
+
else console.error(`[itd-api] ${message}`, error);
|
|
7415
|
+
}
|
|
6842
7416
|
/** Ретранслирует события клиента наружу, добавляя к ним имя аккаунта. */
|
|
6843
7417
|
#forwardEvents(account, client) {
|
|
6844
7418
|
return [
|
|
@@ -7185,5 +7759,5 @@ function statusDays(service) {
|
|
|
7185
7759
|
}
|
|
7186
7760
|
|
|
7187
7761
|
export { ALLOWED_MIME_TYPES, AUDIO_MIME_TYPES, AUTH_FLAG_COOKIE, AUTH_PATHS, AccessType, AttachmentType, BUILT_IN_SERVICES, CommentSort, DEFAULT_BASE_URL, DEFAULT_STATUS_BASE_URL, DEFAULT_TIMEOUT, DEFAULT_UPLOAD_TIMEOUT, DEFAULT_USER_AGENT, DEVICE_ID_HEADER, DetectedRuntime, FeedTab, IMAGE_MIME_TYPES, IncidentKind, InteractionType, ItdAbortError, ItdAccounts, ItdApiError, ItdApiErrorKind, ItdAuthError, ItdClient, ItdConfigError, ItdConflictError, ItdError, ItdErrorCode, ItdErrorKind, ItdForbiddenError, ItdNetworkError, ItdNotFoundError, ItdPhoneVerificationError, ItdRateLimitError, ItdRealtime, ItdServerError, ItdTimeoutError, ItdValidationError, LIBRARY_VERSION, LikesVisibility, LocalStorageTokenStorage, MAX_RECONNECT_ATTEMPTS, MemoryMultiTokenStorage, MemoryTokenStorage, NOTIFICATION_TYPE_ALIASES, NotificationType, OAuthProvider, PaginationMode, Paginator, RECONNECT_BACKOFF, RECONNECT_JITTER, REFRESH_COOKIE, REFRESH_COOKIE_PATH, REQUEST_OPTION_KEYS, RealtimeStatus, RealtimeTransportKind, ReportReason, ReportTargetType, RuntimeMode, STATUS_SERVICE, STREAM_PATH, ServiceRegistry, ServiceState, SignInStatus, SpanType, TURNSTILE_SITE_KEY, UnauthorizedStreamError, VIDEO_MIME_TYPES, ViewReason, ViewSource, WallAccess, autoSpans, canonicalNotificationType, comment, copySession, createAccounts, createClient, createMultiTokenStorage, createRecordMultiStorage, createTokenStorage, formatNotificationText, isBuilder, isItdApiError, isItdAuthError, isItdConflictError, isItdError, isItdForbiddenError, isItdNotFoundError, isItdPhoneVerificationError, isItdRateLimitError, isItdServerError, isItdValidationError, isKnownNotificationType, isMyProfile, mapPage, markup, normalizeNotification, poll, post, readNotificationEvent, readUnreadCountEvent, renderSpans, report, resolveNotificationUrl, scopedTokenStorage, statusDays, toDate, utcStampToIso };
|
|
7188
|
-
//# sourceMappingURL=chunk-
|
|
7189
|
-
//# sourceMappingURL=chunk-
|
|
7762
|
+
//# sourceMappingURL=chunk-6FB4HTKH.js.map
|
|
7763
|
+
//# sourceMappingURL=chunk-6FB4HTKH.js.map
|