itd-api 0.0.10 → 0.0.11
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/{chunk-BN4AC3DP.cjs → chunk-QD4UHJFF.cjs} +111 -23
- package/dist/chunk-QD4UHJFF.cjs.map +1 -0
- package/dist/{chunk-SMV7TF5P.js → chunk-TB7HW3VX.js} +111 -23
- package/dist/chunk-TB7HW3VX.js.map +1 -0
- package/dist/{index-CSjDNGCE.d.cts → index-CrlTO7sR.d.cts} +47 -10
- package/dist/{index-CSjDNGCE.d.ts → index-CrlTO7sR.d.ts} +47 -10
- 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/package.json +1 -1
- package/dist/chunk-BN4AC3DP.cjs.map +0 -1
- package/dist/chunk-SMV7TF5P.js.map +0 -1
|
@@ -674,18 +674,24 @@ function decodeBase64Url(segment) {
|
|
|
674
674
|
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
675
675
|
return new TextDecoder().decode(bytes);
|
|
676
676
|
}
|
|
677
|
-
function
|
|
677
|
+
function readTokenIdentity(token) {
|
|
678
678
|
try {
|
|
679
679
|
const payload = token.split(".")[1];
|
|
680
|
-
if (!payload) return
|
|
680
|
+
if (!payload) return {};
|
|
681
681
|
const parsed = JSON.parse(decodeBase64Url(payload));
|
|
682
|
-
if (typeof parsed !== "object" || parsed === null) return
|
|
683
|
-
const
|
|
684
|
-
return
|
|
682
|
+
if (typeof parsed !== "object" || parsed === null) return {};
|
|
683
|
+
const { sub, sid } = parsed;
|
|
684
|
+
return {
|
|
685
|
+
...typeof sub === "string" && sub.length > 0 ? { subject: sub } : {},
|
|
686
|
+
...typeof sid === "string" && sid.length > 0 ? { sessionId: sid } : {}
|
|
687
|
+
};
|
|
685
688
|
} catch {
|
|
686
|
-
return
|
|
689
|
+
return {};
|
|
687
690
|
}
|
|
688
691
|
}
|
|
692
|
+
function readTokenSubject(token) {
|
|
693
|
+
return readTokenIdentity(token).subject;
|
|
694
|
+
}
|
|
689
695
|
|
|
690
696
|
// src/core/runtime.ts
|
|
691
697
|
var RuntimeMode = Object.freeze({
|
|
@@ -878,6 +884,7 @@ var AuthManager = class {
|
|
|
878
884
|
#send;
|
|
879
885
|
#jar;
|
|
880
886
|
#emitter;
|
|
887
|
+
#hooks;
|
|
881
888
|
/** `undefined` — сессия ещё не читалась из хранилища. */
|
|
882
889
|
#session;
|
|
883
890
|
/**
|
|
@@ -889,8 +896,12 @@ var AuthManager = class {
|
|
|
889
896
|
#refreshing = null;
|
|
890
897
|
/** Общий промис входа по логину и паролю. */
|
|
891
898
|
#signingIn = null;
|
|
892
|
-
/**
|
|
899
|
+
/** Fallback-область для изоляции состояния плагинов, когда JWT-идентичность недоступна. */
|
|
893
900
|
#authScope = nextAuthScope();
|
|
901
|
+
/** Последний токен внешнего источника; `undefined` означает, что источник ещё не читался. */
|
|
902
|
+
#externalToken;
|
|
903
|
+
/** Идентификаторы последнего токена внешнего источника для синхронных потребителей. */
|
|
904
|
+
#externalIdentity;
|
|
894
905
|
/**
|
|
895
906
|
* Идентификатор устройства.
|
|
896
907
|
*
|
|
@@ -900,10 +911,11 @@ var AuthManager = class {
|
|
|
900
911
|
#deviceId;
|
|
901
912
|
/** Общий промис первичной выдачи `deviceId` — чтобы параллельные запросы получили один. */
|
|
902
913
|
#deviceIdLoading = null;
|
|
903
|
-
constructor(config, send, jar) {
|
|
914
|
+
constructor(config, send, jar, hooks = {}) {
|
|
904
915
|
this.#config = config;
|
|
905
916
|
this.#send = send;
|
|
906
917
|
this.#jar = jar;
|
|
918
|
+
this.#hooks = hooks;
|
|
907
919
|
this.#emitter = new Emitter(
|
|
908
920
|
(error) => reportListenerError(config.logger, "\u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0438", error)
|
|
909
921
|
);
|
|
@@ -916,13 +928,63 @@ var AuthManager = class {
|
|
|
916
928
|
get once() {
|
|
917
929
|
return this.#emitter.once.bind(this.#emitter);
|
|
918
930
|
}
|
|
919
|
-
/**
|
|
931
|
+
/**
|
|
932
|
+
* Непрозрачная fallback-область авторизации.
|
|
933
|
+
*
|
|
934
|
+
* Изолирует состояние плагинов, когда идентичность аккаунта из JWT недоступна (непрозрачный
|
|
935
|
+
* токен): значение уникально для владельца и сменяется при замене или очистке сессии.
|
|
936
|
+
*/
|
|
920
937
|
getAuthScope() {
|
|
921
938
|
return this.#authScope;
|
|
922
939
|
}
|
|
940
|
+
/** Загружает сессию и возвращает идентификаторы аккаунта и сессии для плагинов. */
|
|
941
|
+
async getAuthIdentity() {
|
|
942
|
+
const session = await this.#loadSession();
|
|
943
|
+
if (session?.accessToken) return this.#identityForToken(session.accessToken);
|
|
944
|
+
const auth = this.#config.auth;
|
|
945
|
+
if (typeof auth === "object" && auth !== null && "getToken" in auth) {
|
|
946
|
+
const token = await this.#readExternalToken(() => auth.getToken());
|
|
947
|
+
return this.#identityForToken(token ?? void 0);
|
|
948
|
+
}
|
|
949
|
+
return {};
|
|
950
|
+
}
|
|
951
|
+
/** Идентификаторы аккаунта и сессии без чтения хранилища. @internal */
|
|
952
|
+
getCurrentAuthIdentity() {
|
|
953
|
+
const session = this.#session === void 0 ? this.#sessionFromConfig(this.#config.auth) : this.#session;
|
|
954
|
+
return session?.accessToken ? this.#identityForToken(session.accessToken) : this.#externalIdentity ?? {};
|
|
955
|
+
}
|
|
923
956
|
#rotateAuthScope() {
|
|
924
957
|
this.#authScope = nextAuthScope();
|
|
925
958
|
}
|
|
959
|
+
#identityForToken(accessToken) {
|
|
960
|
+
const token = accessToken ? readTokenIdentity(accessToken) : {};
|
|
961
|
+
return {
|
|
962
|
+
...token.subject ? { userId: token.subject } : {},
|
|
963
|
+
...token.sessionId ? { sessionId: token.sessionId } : {}
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
async #readExternalToken(getToken) {
|
|
967
|
+
const token = await getToken() ?? null;
|
|
968
|
+
const current = this.#identityForToken(token ?? void 0);
|
|
969
|
+
if (this.#externalToken !== void 0 && this.#externalToken !== token) {
|
|
970
|
+
this.#rotateAuthScope();
|
|
971
|
+
const previous = this.#externalIdentity ?? {};
|
|
972
|
+
const changed = previous.userId !== void 0 && current.userId !== void 0 ? previous.userId !== current.userId : true;
|
|
973
|
+
if (changed) this.#hooks.onAccountChange?.();
|
|
974
|
+
}
|
|
975
|
+
this.#externalToken = token;
|
|
976
|
+
this.#externalIdentity = current;
|
|
977
|
+
return token;
|
|
978
|
+
}
|
|
979
|
+
/** Меняет fallback и завершает realtime, только если фактически сменился аккаунт. */
|
|
980
|
+
#transitionAuth(accessToken, rotateFallback = true) {
|
|
981
|
+
const knownPrevious = this.#session !== void 0;
|
|
982
|
+
const previous = this.#identityForToken(this.#session?.accessToken);
|
|
983
|
+
const current = this.#identityForToken(accessToken);
|
|
984
|
+
const changed = previous.userId !== void 0 && current.userId !== void 0 ? previous.userId !== current.userId : previous.userId !== current.userId || rotateFallback;
|
|
985
|
+
if (rotateFallback || changed) this.#rotateAuthScope();
|
|
986
|
+
if (knownPrevious && changed) this.#hooks.onAccountChange?.();
|
|
987
|
+
}
|
|
926
988
|
/**
|
|
927
989
|
* Есть ли признак живой refresh-сессии.
|
|
928
990
|
*
|
|
@@ -983,7 +1045,7 @@ var AuthManager = class {
|
|
|
983
1045
|
const auth = this.#config.auth;
|
|
984
1046
|
if (!auth) return null;
|
|
985
1047
|
if (typeof auth === "object" && "getToken" in auth) {
|
|
986
|
-
return
|
|
1048
|
+
return this.#readExternalToken(() => auth.getToken());
|
|
987
1049
|
}
|
|
988
1050
|
if (typeof auth === "object" && "email" in auth) {
|
|
989
1051
|
return this.#signInWithCredentials(auth);
|
|
@@ -1038,7 +1100,8 @@ var AuthManager = class {
|
|
|
1038
1100
|
}
|
|
1039
1101
|
/** Сохраняет токен, полученный извне, — например после подтверждения OTP. */
|
|
1040
1102
|
async setAccessToken(accessToken) {
|
|
1041
|
-
this.#
|
|
1103
|
+
await this.#loadSession();
|
|
1104
|
+
this.#transitionAuth(accessToken);
|
|
1042
1105
|
await this.#saveSession({ ...this.#session ?? {}, accessToken, obtainedAt: Date.now() });
|
|
1043
1106
|
this.#emitter.emit("tokens", { accessToken });
|
|
1044
1107
|
}
|
|
@@ -1059,7 +1122,8 @@ var AuthManager = class {
|
|
|
1059
1122
|
}
|
|
1060
1123
|
/** Заменяет сессию и связанные с ней cookie целиком. */
|
|
1061
1124
|
async setSession(session) {
|
|
1062
|
-
this.#
|
|
1125
|
+
await this.#loadSession();
|
|
1126
|
+
this.#transitionAuth(session.accessToken);
|
|
1063
1127
|
this.#jar.clear();
|
|
1064
1128
|
this.#jar.deserialize(session.cookies);
|
|
1065
1129
|
if (session.deviceId) this.#deviceId = session.deviceId;
|
|
@@ -1074,8 +1138,9 @@ var AuthManager = class {
|
|
|
1074
1138
|
* новую запись в списке сессий.
|
|
1075
1139
|
*/
|
|
1076
1140
|
async clear() {
|
|
1141
|
+
await this.#loadSession();
|
|
1142
|
+
this.#transitionAuth(void 0);
|
|
1077
1143
|
this.#session = null;
|
|
1078
|
-
this.#rotateAuthScope();
|
|
1079
1144
|
this.#jar.clear();
|
|
1080
1145
|
await this.#config.storage.clear();
|
|
1081
1146
|
if (this.#deviceId) await this.#saveSession({ deviceId: this.#deviceId });
|
|
@@ -1172,6 +1237,7 @@ var AuthManager = class {
|
|
|
1172
1237
|
REFRESH_COOKIE,
|
|
1173
1238
|
this.#config.baseUrl + REFRESH_COOKIE_PATH
|
|
1174
1239
|
);
|
|
1240
|
+
this.#transitionAuth(accessToken, false);
|
|
1175
1241
|
await this.#saveSession({
|
|
1176
1242
|
...this.#session ?? {},
|
|
1177
1243
|
accessToken,
|
|
@@ -1182,8 +1248,8 @@ var AuthManager = class {
|
|
|
1182
1248
|
return accessToken;
|
|
1183
1249
|
} catch (error) {
|
|
1184
1250
|
if (error instanceof ItdApiError) {
|
|
1251
|
+
this.#transitionAuth(void 0);
|
|
1185
1252
|
this.#session = null;
|
|
1186
|
-
this.#rotateAuthScope();
|
|
1187
1253
|
this.#jar.clear();
|
|
1188
1254
|
await this.#config.storage.clear();
|
|
1189
1255
|
const relogged = await this.#reloginOrNull();
|
|
@@ -1251,7 +1317,7 @@ var AuthManager = class {
|
|
|
1251
1317
|
"\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."
|
|
1252
1318
|
);
|
|
1253
1319
|
}
|
|
1254
|
-
this.#
|
|
1320
|
+
this.#transitionAuth(accessToken);
|
|
1255
1321
|
await this.#saveSession({ accessToken, obtainedAt: Date.now() });
|
|
1256
1322
|
this.#emitter.emit("tokens", { accessToken });
|
|
1257
1323
|
this.#emitter.emit("signIn", { accessToken });
|
|
@@ -1318,7 +1384,7 @@ function normalizeBaseUrl(baseUrl) {
|
|
|
1318
1384
|
}
|
|
1319
1385
|
|
|
1320
1386
|
// src/core/version.ts
|
|
1321
|
-
var LIBRARY_VERSION = "0.0.
|
|
1387
|
+
var LIBRARY_VERSION = "0.0.11";
|
|
1322
1388
|
|
|
1323
1389
|
// src/core/config.ts
|
|
1324
1390
|
var DEFAULT_BASE_URL = "https://xn--d1ah4a.com";
|
|
@@ -3292,6 +3358,18 @@ var ItdRealtime = class {
|
|
|
3292
3358
|
get transport() {
|
|
3293
3359
|
return this.#transport.name;
|
|
3294
3360
|
}
|
|
3361
|
+
/** Базовый URL клиента, создавшего поток. @internal */
|
|
3362
|
+
get baseUrl() {
|
|
3363
|
+
return this.#deps.baseUrl;
|
|
3364
|
+
}
|
|
3365
|
+
/** Идентификаторы аккаунта и сессии клиента, создавшего поток. @internal */
|
|
3366
|
+
getAuthIdentity() {
|
|
3367
|
+
return this.#deps.getAuthIdentity?.();
|
|
3368
|
+
}
|
|
3369
|
+
/** Непрозрачная область авторизации создавшего поток клиента. @internal */
|
|
3370
|
+
getAuthScope() {
|
|
3371
|
+
return this.#deps.getAuthScope?.();
|
|
3372
|
+
}
|
|
3295
3373
|
/** Подписывается на событие потока. @returns функция отписки */
|
|
3296
3374
|
on(event, listener) {
|
|
3297
3375
|
return this.#emitter.on(event, listener);
|
|
@@ -6168,7 +6246,9 @@ var ItdClient = class _ItdClient {
|
|
|
6168
6246
|
} : void 0;
|
|
6169
6247
|
const authPipeline = composePipeline([pluginsLayer, retriesLayer], transport.send);
|
|
6170
6248
|
const authHandler = (request) => authRetry && request.retry === void 0 ? authPipeline({ ...request, retry: authRetry }) : authPipeline(request);
|
|
6171
|
-
authManager = new AuthManager(config, authHandler, this.#jar
|
|
6249
|
+
authManager = new AuthManager(config, authHandler, this.#jar, {
|
|
6250
|
+
onAccountChange: () => this.#disconnectStreams()
|
|
6251
|
+
});
|
|
6172
6252
|
this.#authManager = authManager;
|
|
6173
6253
|
const middlewares = [];
|
|
6174
6254
|
if (queues) {
|
|
@@ -6247,7 +6327,8 @@ var ItdClient = class _ItdClient {
|
|
|
6247
6327
|
this.#plugins.add(plugin, {
|
|
6248
6328
|
baseUrl: this.#config.baseUrl,
|
|
6249
6329
|
logger: this.#config.logger,
|
|
6250
|
-
getAuthScope: () => this.#authManager.getAuthScope()
|
|
6330
|
+
getAuthScope: () => this.#authManager.getAuthScope(),
|
|
6331
|
+
getAuthIdentity: () => this.#authManager.getAuthIdentity()
|
|
6251
6332
|
});
|
|
6252
6333
|
return this;
|
|
6253
6334
|
}
|
|
@@ -6307,7 +6388,8 @@ var ItdClient = class _ItdClient {
|
|
|
6307
6388
|
* Создаёт поток уведомлений в реальном времени.
|
|
6308
6389
|
*
|
|
6309
6390
|
* Каждый вызов даёт новый независимый поток; обычно он нужен один на приложение.
|
|
6310
|
-
* Соединение поднимается методом `connect()` и держится само.
|
|
6391
|
+
* Соединение поднимается методом `connect()` и держится само. Замена авторизации на токен
|
|
6392
|
+
* другого пользователя завершает все потоки клиента; смена только сессии их не затрагивает.
|
|
6311
6393
|
*
|
|
6312
6394
|
* @example
|
|
6313
6395
|
* ```ts
|
|
@@ -6328,6 +6410,8 @@ var ItdClient = class _ItdClient {
|
|
|
6328
6410
|
baseUrl: this.#config.baseUrl,
|
|
6329
6411
|
fetch: this.#config.fetch,
|
|
6330
6412
|
baseHeaders: (url) => this.#transport.platformHeaders(url),
|
|
6413
|
+
getAuthIdentity: () => this.#authManager.getCurrentAuthIdentity(),
|
|
6414
|
+
getAuthScope: () => this.#authManager.getAuthScope(),
|
|
6331
6415
|
getToken: () => this.#authManager.getAccessToken(),
|
|
6332
6416
|
refresh: () => this.#authManager.onUnauthorized(),
|
|
6333
6417
|
fetchUnreadCount: () => this.notifications.count(),
|
|
@@ -6357,10 +6441,14 @@ var ItdClient = class _ItdClient {
|
|
|
6357
6441
|
* ```
|
|
6358
6442
|
*/
|
|
6359
6443
|
async close() {
|
|
6360
|
-
|
|
6361
|
-
this.#streams.clear();
|
|
6444
|
+
this.#disconnectStreams();
|
|
6362
6445
|
if (this.#ownsQueues) this.#queues?.stop();
|
|
6363
6446
|
}
|
|
6447
|
+
/** Завершает потоки до того, как запросы начнут использовать другой аккаунт. */
|
|
6448
|
+
#disconnectStreams() {
|
|
6449
|
+
for (const stream of [...this.#streams]) stream.disconnect();
|
|
6450
|
+
this.#streams.clear();
|
|
6451
|
+
}
|
|
6364
6452
|
/** Позволяет использовать клиент с `await using`. */
|
|
6365
6453
|
[Symbol.asyncDispose]() {
|
|
6366
6454
|
return this.close();
|
|
@@ -7295,5 +7383,5 @@ exports.scopedTokenStorage = scopedTokenStorage;
|
|
|
7295
7383
|
exports.statusDays = statusDays;
|
|
7296
7384
|
exports.toDate = toDate;
|
|
7297
7385
|
exports.utcStampToIso = utcStampToIso;
|
|
7298
|
-
//# sourceMappingURL=chunk-
|
|
7299
|
-
//# sourceMappingURL=chunk-
|
|
7386
|
+
//# sourceMappingURL=chunk-QD4UHJFF.cjs.map
|
|
7387
|
+
//# sourceMappingURL=chunk-QD4UHJFF.cjs.map
|