itd-api 0.7.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -27
- package/dist/events/index.cjs +53 -0
- package/dist/events/index.d.cts +3 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/events/index.js +5 -0
- package/dist/index.cjs +234 -200
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -64
- package/dist/index.d.ts +70 -64
- package/dist/index.js +202 -179
- package/dist/index.js.map +1 -1
- package/dist/node/index.cjs +6 -5
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +2 -2
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.js +5 -4
- package/dist/node/index.js.map +1 -1
- package/dist/rest/index.cjs +32 -26
- package/dist/rest/index.cjs.map +1 -1
- package/dist/rest/index.d.cts +9 -7
- package/dist/rest/index.d.ts +9 -7
- package/dist/rest/index.js +19 -21
- package/dist/rest/index.js.map +1 -1
- package/dist/shared/{errors-DfU8M5eS.cjs → errors-BmP3TKoW.cjs} +3 -3
- package/dist/shared/{errors-DfU8M5eS.cjs.map → errors-BmP3TKoW.cjs.map} +1 -1
- package/dist/shared/{errors-Bhrd2fJd.js → errors-GI10kZxk.js} +3 -3
- package/dist/shared/{errors-Bhrd2fJd.js.map → errors-GI10kZxk.js.map} +1 -1
- package/dist/shared/{websocket-BLR8eVJV.js → events-C5wnTXPT.js} +770 -490
- package/dist/shared/events-C5wnTXPT.js.map +1 -0
- package/dist/shared/events-CqtkP65d.d.ts +698 -0
- package/dist/shared/events-DGAWlCaI.d.cts +698 -0
- package/dist/shared/{websocket-C_eI4H2o.cjs → events-FZfnEez0.cjs} +834 -518
- package/dist/shared/events-FZfnEez0.cjs.map +1 -0
- package/dist/shared/{storage-BPJR_k4-.cjs → key-value-store-B5GYVEYZ.cjs} +2 -86
- package/dist/shared/key-value-store-B5GYVEYZ.cjs.map +1 -0
- package/dist/shared/{storage-D86edNCB.js → key-value-store-Bt2PSMxY.js} +3 -69
- package/dist/shared/key-value-store-Bt2PSMxY.js.map +1 -0
- package/dist/shared/key-value-store-COZaNHZS.d.cts +69 -0
- package/dist/shared/key-value-store-COZaNHZS.d.ts +69 -0
- package/dist/shared/{multi-storage-DccjD7Ww.d.ts → multi-storage-BEL7tcKn.d.cts} +3 -2
- package/dist/shared/{multi-storage--yTEqiod.cjs → multi-storage-CDKLa956.cjs} +11 -10
- package/dist/shared/{multi-storage--yTEqiod.cjs.map → multi-storage-CDKLa956.cjs.map} +1 -1
- package/dist/shared/{multi-storage-CjAPB5Kq.d.cts → multi-storage-LkljOSzA.d.ts} +3 -2
- package/dist/shared/{multi-storage-CkvTUC5m.js → multi-storage-SoW05uzO.js} +5 -4
- package/dist/shared/{multi-storage-CkvTUC5m.js.map → multi-storage-SoW05uzO.js.map} +1 -1
- package/dist/shared/{options-Dg5N3r1V.cjs → options-DOJYtoti.cjs} +8 -6
- package/dist/shared/options-DOJYtoti.cjs.map +1 -0
- package/dist/shared/{options-DtATYdLr.js → options-DhgGg7Ms.js} +8 -6
- package/dist/shared/options-DhgGg7Ms.js.map +1 -0
- package/dist/shared/{cookies-DZwFq6kr.cjs → redact-BP00URmQ.cjs} +312 -2
- package/dist/shared/redact-BP00URmQ.cjs.map +1 -0
- package/dist/shared/{cookies-tX2sNwxb.js → redact-Ba5iSVYZ.js} +235 -3
- package/dist/shared/redact-Ba5iSVYZ.js.map +1 -0
- package/dist/shared/{render-CgwKdOzu.d.ts → render-B4qis7Sb.d.ts} +775 -313
- package/dist/shared/{render-DO0F5YSm.d.cts → render-DDwy2Ame.d.cts} +775 -313
- package/dist/shared/{render-mcuELiYi.cjs → render-Dqi8Owqk.cjs} +1464 -662
- package/dist/shared/render-Dqi8Owqk.cjs.map +1 -0
- package/dist/shared/{render-C6HRPs10.js → render-hiWFh2s4.js} +1366 -618
- package/dist/shared/render-hiWFh2s4.js.map +1 -0
- package/dist/shared/storage-BQrtKon9.d.cts +86 -0
- package/dist/shared/storage-CTRGT5wF.d.ts +86 -0
- package/dist/shared/storage-CfHKzVHf.js +71 -0
- package/dist/shared/storage-CfHKzVHf.js.map +1 -0
- package/dist/shared/storage-QNVxzpDB.cjs +88 -0
- package/dist/shared/storage-QNVxzpDB.cjs.map +1 -0
- package/dist/shared/{url-BaMCQpYH.cjs → url-Bsb3xE5U.cjs} +577 -466
- package/dist/shared/url-Bsb3xE5U.cjs.map +1 -0
- package/dist/shared/{url-IU0xN9wX.js → url-DP33mp3s.js} +508 -409
- package/dist/shared/url-DP33mp3s.js.map +1 -0
- package/dist/shared/{url-DTfZ2toq.d.ts → url-XBsMdAcv.d.cts} +1313 -1200
- package/dist/shared/{url-DTfZ2toq.d.cts → url-XBsMdAcv.d.ts} +1313 -1200
- package/dist/web/index.cjs +5 -4
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts +1 -1
- package/dist/web/index.d.ts +1 -1
- package/dist/web/index.js +3 -2
- package/dist/web/index.js.map +1 -1
- package/package.json +16 -15
- package/dist/realtime/index.cjs +0 -165
- package/dist/realtime/index.cjs.map +0 -1
- package/dist/realtime/index.d.cts +0 -51
- package/dist/realtime/index.d.ts +0 -51
- package/dist/realtime/index.js +0 -120
- package/dist/realtime/index.js.map +0 -1
- package/dist/shared/auth-provider-CG8oCQ9F.cjs +0 -108
- package/dist/shared/auth-provider-CG8oCQ9F.cjs.map +0 -1
- package/dist/shared/auth-provider-mYqxsSVa.js +0 -91
- package/dist/shared/auth-provider-mYqxsSVa.js.map +0 -1
- package/dist/shared/cookies-DZwFq6kr.cjs.map +0 -1
- package/dist/shared/cookies-tX2sNwxb.js.map +0 -1
- package/dist/shared/options-Dg5N3r1V.cjs.map +0 -1
- package/dist/shared/options-DtATYdLr.js.map +0 -1
- package/dist/shared/render-C6HRPs10.js.map +0 -1
- package/dist/shared/render-mcuELiYi.cjs.map +0 -1
- package/dist/shared/storage-BPJR_k4-.cjs.map +0 -1
- package/dist/shared/storage-C_eICCep.d.cts +0 -152
- package/dist/shared/storage-C_eICCep.d.ts +0 -152
- package/dist/shared/storage-D86edNCB.js.map +0 -1
- package/dist/shared/url-BaMCQpYH.cjs.map +0 -1
- package/dist/shared/url-IU0xN9wX.js.map +0 -1
- package/dist/shared/websocket-BLR8eVJV.js.map +0 -1
- package/dist/shared/websocket-C_eI4H2o.cjs.map +0 -1
- package/dist/shared/websocket-DF7XIMiX.d.cts +0 -562
- package/dist/shared/websocket-DYKBr8HF.d.ts +0 -562
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_errors = require("./errors-
|
|
2
|
-
const
|
|
1
|
+
const require_errors = require("./errors-BmP3TKoW.cjs");
|
|
2
|
+
const require_redact = require("./redact-BP00URmQ.cjs");
|
|
3
3
|
//#region src/core/async-dispose.ts
|
|
4
4
|
/**
|
|
5
5
|
* Подставляет метод `await using` там, где `Symbol.asyncDispose` отсутствует.
|
|
@@ -51,120 +51,6 @@ function createDeadline(timeout, clock = systemClock) {
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
//#endregion
|
|
54
|
-
//#region src/core/runtime.ts
|
|
55
|
-
/**
|
|
56
|
-
* Как библиотека обращается с cookie.
|
|
57
|
-
*
|
|
58
|
-
* - `browser` — cookie ведёт браузер, запросы уходят с `credentials: 'include'`;
|
|
59
|
-
* - `server` — cookie ведёт встроенный jar, заголовок `Cookie` подставляется вручную;
|
|
60
|
-
* - `auto` — определяется по среде исполнения (значение по умолчанию).
|
|
61
|
-
*/
|
|
62
|
-
const RuntimeMode = Object.freeze({
|
|
63
|
-
/** Определяется по среде исполнения. Значение по умолчанию. */
|
|
64
|
-
Auto: "auto",
|
|
65
|
-
/** Cookie ведёт браузер, запросы уходят с `credentials: 'include'`. */
|
|
66
|
-
Browser: "browser",
|
|
67
|
-
/** Cookie ведёт встроенный jar, заголовок `Cookie` подставляется вручную. */
|
|
68
|
-
Server: "server"
|
|
69
|
-
});
|
|
70
|
-
/** Распознанная среда исполнения. */
|
|
71
|
-
const DetectedRuntime = Object.freeze({
|
|
72
|
-
Browser: "browser",
|
|
73
|
-
/** Есть `window`, но нет `document`; cookie ведёт нативный сетевой слой. */
|
|
74
|
-
ReactNative: "react-native",
|
|
75
|
-
Server: "server"
|
|
76
|
-
});
|
|
77
|
-
/**
|
|
78
|
-
* Определяет среду исполнения.
|
|
79
|
-
*
|
|
80
|
-
* React Native выделен отдельно намеренно: там есть `window`, но нет `document`, а cookie
|
|
81
|
-
* хранит нативный сетевой слой (`NSHTTPCookieStorage` / `OkHttp`). Свой jar в этой среде
|
|
82
|
-
* привёл бы к отправке cookie дважды.
|
|
83
|
-
*/
|
|
84
|
-
function detectRuntime() {
|
|
85
|
-
if (globalThis.navigator?.product === "ReactNative") return DetectedRuntime.ReactNative;
|
|
86
|
-
if (typeof document !== "undefined") return DetectedRuntime.Browser;
|
|
87
|
-
return DetectedRuntime.Server;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Нужно ли вести собственный cookie-jar.
|
|
91
|
-
*
|
|
92
|
-
* Только в серверных средах: в браузере `Set-Cookie` из JS не читается и не нужен,
|
|
93
|
-
* в React Native cookie ведёт нативный слой.
|
|
94
|
-
*/
|
|
95
|
-
function shouldUseCookieJar(mode) {
|
|
96
|
-
if (mode === RuntimeMode.Browser) return false;
|
|
97
|
-
if (mode === RuntimeMode.Server) return true;
|
|
98
|
-
return detectRuntime() === DetectedRuntime.Server;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Нужно ли отправлять запросы с `credentials: 'include'`.
|
|
102
|
-
*
|
|
103
|
-
* Только в браузере: именно так refresh-cookie попадает в запрос `POST /api/v1/auth/refresh`.
|
|
104
|
-
* Требует настроенного CORS на стороне итд.com — если его нет, укажите свой прокси в `baseUrl`.
|
|
105
|
-
*/
|
|
106
|
-
function shouldSendCredentials(mode) {
|
|
107
|
-
if (mode === RuntimeMode.Browser) return true;
|
|
108
|
-
if (mode === RuntimeMode.Server) return false;
|
|
109
|
-
return detectRuntime() === DetectedRuntime.Browser;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Возвращает реализацию `fetch`.
|
|
113
|
-
*
|
|
114
|
-
* @param custom реализация из конфигурации клиента, если пользователь её передал
|
|
115
|
-
* @throws {ItdConfigError} если `fetch` недоступен — например, на Node ниже 18
|
|
116
|
-
*/
|
|
117
|
-
function resolveFetch(custom) {
|
|
118
|
-
if (custom !== void 0) {
|
|
119
|
-
if (typeof custom !== "function") throw new require_errors.ItdConfigError("fetch должен быть функцией");
|
|
120
|
-
return custom;
|
|
121
|
-
}
|
|
122
|
-
if (typeof globalThis.fetch === "function") return globalThis.fetch.bind(globalThis);
|
|
123
|
-
throw new require_errors.ItdConfigError("В этой среде нет глобального fetch. Обновитесь до Node 18+ либо передайте свою реализацию через опцию fetch.");
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Проверки бинарных типов, безопасные в любой среде.
|
|
127
|
-
*
|
|
128
|
-
* Обычный `instanceof` здесь недостаточен: конструктора может не быть вовсе, и тогда
|
|
129
|
-
* проверка падает с `ReferenceError`, а не возвращает `false`. `Blob` есть в Node с 18.0,
|
|
130
|
-
* но `File` стал глобальным только в Node 20 — при заявленной поддержке Node 18 голый
|
|
131
|
-
* `instanceof File` ронял бы любую загрузку.
|
|
132
|
-
*/
|
|
133
|
-
function isBlob(value) {
|
|
134
|
-
return typeof Blob !== "undefined" && value instanceof Blob;
|
|
135
|
-
}
|
|
136
|
-
/** @see {@link isBlob} */
|
|
137
|
-
function isFile(value) {
|
|
138
|
-
return typeof File !== "undefined" && value instanceof File;
|
|
139
|
-
}
|
|
140
|
-
/** Доступно ли потоковое чтение тела ответа — от этого зависит, сработает ли SSE. */
|
|
141
|
-
function supportsStreamingBody() {
|
|
142
|
-
return typeof ReadableStream !== "undefined" && typeof TextDecoder !== "undefined";
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Создаёт идентификатор устройства для заголовка `X-Device-Id` — UUID v4.
|
|
146
|
-
*
|
|
147
|
-
* `crypto.randomUUID` есть в Node 19+, браузерах и Deno, но отсутствует в Node 18 вне
|
|
148
|
-
* защищённого контекста, поэтому предусмотрен запасной путь.
|
|
149
|
-
*/
|
|
150
|
-
function createDeviceId() {
|
|
151
|
-
const webCrypto = globalThis.crypto;
|
|
152
|
-
if (typeof webCrypto?.randomUUID === "function") return webCrypto.randomUUID();
|
|
153
|
-
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
154
|
-
if (typeof webCrypto?.getRandomValues === "function") webCrypto.getRandomValues(bytes);
|
|
155
|
-
else for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
156
|
-
bytes[6] = bytes[6] & 15 | 64;
|
|
157
|
-
bytes[8] = bytes[8] & 63 | 128;
|
|
158
|
-
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
159
|
-
return [
|
|
160
|
-
hex.slice(0, 8),
|
|
161
|
-
hex.slice(8, 12),
|
|
162
|
-
hex.slice(12, 16),
|
|
163
|
-
hex.slice(16, 20),
|
|
164
|
-
hex.slice(20, 32)
|
|
165
|
-
].join("-");
|
|
166
|
-
}
|
|
167
|
-
//#endregion
|
|
168
54
|
//#region src/core/scheduling/pacing.ts
|
|
169
55
|
/** Реакция на остаток лимита из заголовков ответа. */
|
|
170
56
|
const RateLimitPacing = Object.freeze({
|
|
@@ -199,7 +85,7 @@ function requireOptionalBoolean(value, name) {
|
|
|
199
85
|
//#endregion
|
|
200
86
|
//#region src/core/version.ts
|
|
201
87
|
/** Версия библиотеки. Попадает в `User-Agent`. */
|
|
202
|
-
const LIBRARY_VERSION = "0.
|
|
88
|
+
const LIBRARY_VERSION = "0.9.0";
|
|
203
89
|
//#endregion
|
|
204
90
|
//#region src/core/config.ts
|
|
205
91
|
/** Базовый URL API итд.com. Домен записан в punycode: `итд.com`. */
|
|
@@ -318,6 +204,10 @@ function requireConcurrency(value, name) {
|
|
|
318
204
|
if (!Number.isInteger(value) || value < 1) throw new require_errors.ItdConfigError(`${name} должен быть целым числом от 1, получено: ${value}`);
|
|
319
205
|
return value;
|
|
320
206
|
}
|
|
207
|
+
function requireRps(value, name) {
|
|
208
|
+
if (!Number.isFinite(value) || value <= 0) throw new require_errors.ItdConfigError(`${name} должен быть положительным числом, получено: ${value}`);
|
|
209
|
+
return value;
|
|
210
|
+
}
|
|
321
211
|
function resolvePacing(pacing) {
|
|
322
212
|
const known = Object.values(RateLimitPacing);
|
|
323
213
|
if (pacing !== void 0 && !known.includes(pacing)) throw new require_errors.ItdConfigError(`rateLimit.pacing должен быть одним из ${known.join(", ")}, получено: ${pacing}`);
|
|
@@ -340,9 +230,11 @@ function resolveBucketOverrides(overrides, bucket, catalog) {
|
|
|
340
230
|
assertKnownBucket(name, "rateLimit.bucketOverrides", bucket, catalog);
|
|
341
231
|
if (override.concurrency !== void 0) requireConcurrency(override.concurrency, `rateLimit.bucketOverrides.${name}.concurrency`);
|
|
342
232
|
if (override.limit !== void 0 && (!Number.isFinite(override.limit) || override.limit <= 0)) throw new require_errors.ItdConfigError(`rateLimit.bucketOverrides.${name}.limit должен быть положительным числом, получено: ${override.limit}`);
|
|
233
|
+
if (override.rps !== void 0) requireRps(override.rps, `rateLimit.bucketOverrides.${name}.rps`);
|
|
343
234
|
const built = builtIn[name];
|
|
344
235
|
resolved[name] = Object.freeze({
|
|
345
236
|
concurrency: override.concurrency ?? built?.concurrency,
|
|
237
|
+
rps: override.rps ?? built?.rps,
|
|
346
238
|
limit: override.limit ?? built?.limit
|
|
347
239
|
});
|
|
348
240
|
}
|
|
@@ -375,7 +267,7 @@ function resolveRateLimit(rateLimit, catalog) {
|
|
|
375
267
|
};
|
|
376
268
|
if (!rateLimit) return defaults;
|
|
377
269
|
const concurrency = requireConcurrency(rateLimit.concurrency ?? 6, "rateLimit.concurrency");
|
|
378
|
-
if (rateLimit.rps !== void 0
|
|
270
|
+
if (rateLimit.rps !== void 0) requireRps(rateLimit.rps, "rateLimit.rps");
|
|
379
271
|
const retryDelays = rateLimit.retryDelays ?? defaults.retryDelays;
|
|
380
272
|
if (!Array.isArray(retryDelays)) throw new require_errors.ItdConfigError("rateLimit.retryDelays должен быть массивом чисел");
|
|
381
273
|
for (const delay of retryDelays) requirePositive(delay, "rateLimit.retryDelays");
|
|
@@ -424,16 +316,16 @@ function resolveServices(services) {
|
|
|
424
316
|
*/
|
|
425
317
|
function resolveRuntimeConfig(options, catalog) {
|
|
426
318
|
if (!isRecord$1(options)) throw new require_errors.ItdConfigError("опции клиента должны быть объектом");
|
|
427
|
-
const mode = options.mode ?? RuntimeMode.Auto;
|
|
428
|
-
if (!Object.values(RuntimeMode).includes(mode)) throw new require_errors.ItdConfigError(`mode должен быть одним из ${Object.values(RuntimeMode).join(", ")}, получено: ${mode}`);
|
|
319
|
+
const mode = options.mode ?? require_redact.RuntimeMode.Auto;
|
|
320
|
+
if (!Object.values(require_redact.RuntimeMode).includes(mode)) throw new require_errors.ItdConfigError(`mode должен быть одним из ${Object.values(require_redact.RuntimeMode).join(", ")}, получено: ${mode}`);
|
|
429
321
|
const timeout = requirePositive(options.timeout ?? 3e4, "timeout");
|
|
430
322
|
const shutdownTimeout = requirePositive(options.shutdownTimeout ?? 1e4, "shutdownTimeout");
|
|
431
323
|
if (options.clock !== void 0 && (typeof options.clock !== "object" || options.clock === null || typeof options.clock.now !== "function" || typeof options.clock.schedule !== "function")) throw new require_errors.ItdConfigError("clock должен предоставлять методы now() и schedule()");
|
|
432
324
|
if (options.userAgent !== void 0 && options.userAgent !== false && typeof options.userAgent !== "string") throw new require_errors.ItdConfigError("userAgent должен быть строкой или false");
|
|
433
325
|
return {
|
|
434
|
-
baseUrl:
|
|
326
|
+
baseUrl: require_redact.normalizeBaseUrl(options.baseUrl ?? "https://xn--d1ah4a.com"),
|
|
435
327
|
services: resolveServices(options.services),
|
|
436
|
-
fetch: resolveFetch(options.fetch),
|
|
328
|
+
fetch: require_redact.resolveFetch(options.fetch),
|
|
437
329
|
clock: options.clock ?? systemClock,
|
|
438
330
|
timeout,
|
|
439
331
|
shutdownTimeout,
|
|
@@ -443,8 +335,8 @@ function resolveRuntimeConfig(options, catalog) {
|
|
|
443
335
|
headers: resolveHeaders(options.headers),
|
|
444
336
|
userAgent: options.userAgent === false ? void 0 : options.userAgent ?? DEFAULT_USER_AGENT,
|
|
445
337
|
mode,
|
|
446
|
-
useCookieJar: shouldUseCookieJar(mode),
|
|
447
|
-
sendCredentials: shouldSendCredentials(mode),
|
|
338
|
+
useCookieJar: require_redact.shouldUseCookieJar(mode),
|
|
339
|
+
sendCredentials: require_redact.shouldSendCredentials(mode),
|
|
448
340
|
logger: resolveLogger(options.logger)
|
|
449
341
|
};
|
|
450
342
|
}
|
|
@@ -660,7 +552,10 @@ var PluginRegistry = class {
|
|
|
660
552
|
};
|
|
661
553
|
const installed = plugin.install({
|
|
662
554
|
...context,
|
|
663
|
-
operations: {
|
|
555
|
+
operations: {
|
|
556
|
+
get: this.#options.operationMetadata,
|
|
557
|
+
use: (transformer) => register(transformers, transformer, "operations")
|
|
558
|
+
},
|
|
664
559
|
attempts: { use: (interceptor) => register(interceptors, interceptor, "attempts") }
|
|
665
560
|
});
|
|
666
561
|
if (installed !== void 0 && typeof installed !== "function") throw new require_errors.ItdConfigError(`install() плагина «${plugin.name}» должен вернуть функцию или void`);
|
|
@@ -699,8 +594,9 @@ var PluginRegistry = class {
|
|
|
699
594
|
this.#removing.add(name);
|
|
700
595
|
const deadline = createDeadline(this.#options.shutdownTimeout, this.#options.clock);
|
|
701
596
|
const cleanup = this.#trackCleanup((async () => {
|
|
702
|
-
const
|
|
703
|
-
if (
|
|
597
|
+
const expirationErrors = await this.#release(entry, deadline);
|
|
598
|
+
if (expirationErrors.length === 1) throw expirationErrors[0];
|
|
599
|
+
if (expirationErrors.length > 1) throw new AggregateError(expirationErrors, `Не удалось отключить плагин «${name}»`);
|
|
704
600
|
})());
|
|
705
601
|
try {
|
|
706
602
|
await cleanup;
|
|
@@ -728,8 +624,7 @@ var PluginRegistry = class {
|
|
|
728
624
|
const previous = await Promise.allSettled(previousCleanups);
|
|
729
625
|
for (const result of previous) if (result.status === "rejected") errors.push(result.reason);
|
|
730
626
|
for (const entry of entries) try {
|
|
731
|
-
|
|
732
|
-
if (expired) errors.push(expired);
|
|
627
|
+
errors.push(...await this.#release(entry, deadline));
|
|
733
628
|
} catch (error) {
|
|
734
629
|
errors.push(error);
|
|
735
630
|
} finally {
|
|
@@ -755,13 +650,29 @@ var PluginRegistry = class {
|
|
|
755
650
|
* @param execute выполнение логической операции, вызываемое самым внутренним transformer
|
|
756
651
|
*/
|
|
757
652
|
async run(request, execute) {
|
|
758
|
-
const
|
|
653
|
+
const ordered = this.#ordered;
|
|
654
|
+
if (ordered.length === 0) return execute(request);
|
|
655
|
+
const entries = [...ordered];
|
|
759
656
|
for (const entry of entries) entry.activeRequests += 1;
|
|
760
657
|
const interceptorScope = entries.flatMap((entry) => entry.interceptors.map((interceptor) => ({
|
|
761
658
|
plugin: entry.plugin.name,
|
|
762
659
|
interceptor
|
|
763
660
|
})));
|
|
764
|
-
const
|
|
661
|
+
const operationId = request.operationId;
|
|
662
|
+
const method = request.method.toUpperCase();
|
|
663
|
+
const retrySafety = request.retrySafety;
|
|
664
|
+
const scoped = (current) => {
|
|
665
|
+
const prepared = current.retrySafety === void 0 ? {
|
|
666
|
+
...current,
|
|
667
|
+
retrySafety
|
|
668
|
+
} : current;
|
|
669
|
+
return interceptorScope.length === 0 ? prepared : withAttemptInterceptorScope(prepared, interceptorScope);
|
|
670
|
+
};
|
|
671
|
+
const validateContract = (prepared) => {
|
|
672
|
+
if (prepared.operationId !== operationId) throw new require_errors.ItdConfigError(`operation transformer не может заменить operationId «${operationId}» на «${prepared.operationId}»`);
|
|
673
|
+
if (prepared.method.toUpperCase() !== method) throw new require_errors.ItdConfigError(`operation transformer не может заменить метод «${method}» операции «${operationId}» на «${prepared.method}»`);
|
|
674
|
+
if (prepared.retrySafety !== void 0 && prepared.retrySafety !== retrySafety) throw new require_errors.ItdConfigError(`operation transformer не может заменить retrySafety операции «${operationId}»`);
|
|
675
|
+
};
|
|
765
676
|
const chain = entries.flatMap((entry) => entry.transformers.map((transformer) => ({
|
|
766
677
|
plugin: entry.plugin.name,
|
|
767
678
|
transformer
|
|
@@ -770,9 +681,13 @@ var PluginRegistry = class {
|
|
|
770
681
|
return transformer(scoped(current), (prepared) => {
|
|
771
682
|
if (called) throw new require_errors.ItdConfigError(`operation transformer плагина «${plugin}» вызвал next() больше одного раза`);
|
|
772
683
|
called = true;
|
|
684
|
+
validateContract(prepared);
|
|
773
685
|
return next(scoped(prepared));
|
|
774
686
|
});
|
|
775
|
-
}, (current) =>
|
|
687
|
+
}, (current) => {
|
|
688
|
+
validateContract(current);
|
|
689
|
+
return execute(scoped(current));
|
|
690
|
+
});
|
|
776
691
|
try {
|
|
777
692
|
return await chain(scoped(request));
|
|
778
693
|
} finally {
|
|
@@ -789,14 +704,23 @@ var PluginRegistry = class {
|
|
|
789
704
|
/**
|
|
790
705
|
* Дожидается операций плагина и освобождает его ресурсы.
|
|
791
706
|
*
|
|
792
|
-
*
|
|
707
|
+
* Функция очистки запускается после ожидания активных операций. Обе стадии ограничены
|
|
708
|
+
* общим сроком.
|
|
793
709
|
*
|
|
794
|
-
* @returns
|
|
710
|
+
* @returns ошибки истёкшего срока для незавершённых стадий
|
|
795
711
|
*/
|
|
796
712
|
async #release(entry, deadline) {
|
|
797
|
-
const
|
|
798
|
-
await entry.
|
|
799
|
-
|
|
713
|
+
const errors = [];
|
|
714
|
+
if (!await deadline.wait(this.#waitForDrain(entry))) errors.push(new require_errors.ItdStateError(`плагин «${entry.plugin.name}» не завершил операции за ${this.#options.shutdownTimeout} мс`));
|
|
715
|
+
let teardownFailed = false;
|
|
716
|
+
let teardownFailure;
|
|
717
|
+
const teardown = Promise.resolve().then(() => entry.teardown?.()).catch((error) => {
|
|
718
|
+
teardownFailed = true;
|
|
719
|
+
teardownFailure = error;
|
|
720
|
+
});
|
|
721
|
+
if (!await deadline.wait(teardown)) errors.push(new require_errors.ItdStateError(`плагин «${entry.plugin.name}» не завершил teardown за ${this.#options.shutdownTimeout} мс`));
|
|
722
|
+
else if (teardownFailed) throw teardownFailure;
|
|
723
|
+
return errors;
|
|
800
724
|
}
|
|
801
725
|
#waitForDrain(entry) {
|
|
802
726
|
if (entry.activeRequests === 0) return Promise.resolve();
|
|
@@ -821,148 +745,118 @@ function queueAbortError() {
|
|
|
821
745
|
function queueStoppedError() {
|
|
822
746
|
return new require_errors.ItdAbortError("Клиент закрыт, запрос отменён");
|
|
823
747
|
}
|
|
748
|
+
/** Длина окна лимита на сервере. */
|
|
749
|
+
const RATE_LIMIT_WINDOW = 6e4;
|
|
824
750
|
/**
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
* Нужна прежде всего ботам: без неё цикл по сотне постов уходит в API одним залпом
|
|
828
|
-
* и упирается в `RATE_LIMIT_EXCEEDED`.
|
|
751
|
+
* Пауза при исчерпании бакета неизвестной ёмкости.
|
|
829
752
|
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
753
|
+
* Ёмкость приходит в заголовке вместе с остатком, поэтому случай возможен только у чужого
|
|
754
|
+
* прокси, который прислал `remaining` без `limit`.
|
|
755
|
+
*/
|
|
756
|
+
const UNKNOWN_CAPACITY_PAUSE = 1e3;
|
|
757
|
+
/**
|
|
758
|
+
* Единый планировщик одного направления.
|
|
832
759
|
*
|
|
833
|
-
*
|
|
760
|
+
* Он выбирает готовый бакет и только перед фактическим запуском атомарно занимает общий
|
|
761
|
+
* и локальный слоты. Одно ближайшее пробуждение обслуживает все бакеты направления.
|
|
834
762
|
*/
|
|
835
|
-
var
|
|
763
|
+
var DestinationScheduler = class {
|
|
836
764
|
#concurrency;
|
|
837
|
-
/** Минимальный промежуток между стартами, мс. `0` — без ограничения частоты. */
|
|
838
765
|
#minGap;
|
|
839
|
-
#
|
|
840
|
-
#
|
|
766
|
+
#clock;
|
|
767
|
+
#buckets = [];
|
|
841
768
|
#active = 0;
|
|
842
|
-
/** Момент, раньше которого следующий запрос стартовать не должен. */
|
|
843
769
|
#nextSlot = 0;
|
|
844
|
-
#
|
|
770
|
+
#cursor = 0;
|
|
845
771
|
#cancelTimer;
|
|
846
|
-
|
|
772
|
+
#wakeupAt;
|
|
773
|
+
constructor(options, clock) {
|
|
847
774
|
this.#concurrency = options.concurrency;
|
|
848
775
|
this.#minGap = options.rps ? 1e3 / options.rps : 0;
|
|
849
|
-
this.#onDispatch = options.onDispatch;
|
|
850
776
|
this.#clock = clock;
|
|
851
777
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
return this.#active;
|
|
778
|
+
register(bucket) {
|
|
779
|
+
this.#buckets.push(bucket);
|
|
855
780
|
}
|
|
856
|
-
/**
|
|
857
|
-
|
|
858
|
-
|
|
781
|
+
/** Пересчитывает ближайший запуск после изменения очереди или ограничения. */
|
|
782
|
+
changed() {
|
|
783
|
+
this.#drain();
|
|
859
784
|
}
|
|
860
|
-
/**
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
*/
|
|
865
|
-
schedule(task, signal) {
|
|
866
|
-
if (signal?.aborted) return Promise.reject(queueAbortError());
|
|
867
|
-
return new Promise((resolve, reject) => {
|
|
868
|
-
let queued;
|
|
869
|
-
const detach = () => signal?.removeEventListener("abort", onAbort);
|
|
870
|
-
const onAbort = () => {
|
|
871
|
-
const index = this.#waiting.indexOf(queued);
|
|
872
|
-
if (index < 0) return;
|
|
873
|
-
this.#waiting.splice(index, 1);
|
|
874
|
-
queued.cancel(queueAbortError());
|
|
875
|
-
this.#drain();
|
|
876
|
-
};
|
|
877
|
-
queued = {
|
|
878
|
-
run: () => {
|
|
879
|
-
detach();
|
|
880
|
-
this.#active += 1;
|
|
881
|
-
this.#onDispatch?.();
|
|
882
|
-
Promise.resolve().then(task).then(resolve, reject).finally(() => {
|
|
883
|
-
this.#active -= 1;
|
|
884
|
-
this.#drain();
|
|
885
|
-
});
|
|
886
|
-
},
|
|
887
|
-
cancel: (reason) => {
|
|
888
|
-
detach();
|
|
889
|
-
reject(reason);
|
|
890
|
-
}
|
|
891
|
-
};
|
|
892
|
-
this.#waiting.push(queued);
|
|
893
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
894
|
-
if (signal?.aborted) onAbort();
|
|
895
|
-
else this.#drain();
|
|
896
|
-
});
|
|
785
|
+
/** Перепланирует пробуждение после изменения времени готовности бакета. */
|
|
786
|
+
timingChanged() {
|
|
787
|
+
this.#cancelWakeup();
|
|
788
|
+
this.#drain();
|
|
897
789
|
}
|
|
898
|
-
/**
|
|
899
|
-
* Останавливает очередь: снимает отложенную паузу и отклоняет ещё не начатые задачи
|
|
900
|
-
* ошибкой `ItdAbortError`. Уже выполняющиеся задачи доводятся до конца.
|
|
901
|
-
*/
|
|
790
|
+
/** Отклоняет ожидающие задачи, сохраняя паузы и темп для повторного запуска клиента. */
|
|
902
791
|
stop() {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
this.#cancelTimer = void 0;
|
|
906
|
-
}
|
|
907
|
-
const pending = this.#waiting.splice(0, this.#waiting.length);
|
|
908
|
-
for (const task of pending) task.cancel(queueStoppedError());
|
|
909
|
-
}
|
|
910
|
-
/** Придерживает очередь на заданное время: ждут все её задачи, а не только одна. */
|
|
911
|
-
pause(ms) {
|
|
912
|
-
if (ms <= 0) return;
|
|
913
|
-
this.#nextSlot = Math.max(this.#nextSlot, this.#clock.now() + ms);
|
|
792
|
+
this.#cancelWakeup();
|
|
793
|
+
for (const bucket of this.#buckets) bucket.cancelWaiting(queueStoppedError(), false);
|
|
914
794
|
}
|
|
915
|
-
/** Запускает столько ожидающих задач, сколько позволяют ограничения. */
|
|
916
795
|
#drain() {
|
|
917
|
-
if (this.#waiting.length === 0) {
|
|
918
|
-
if (this.#cancelTimer) {
|
|
919
|
-
this.#cancelTimer();
|
|
920
|
-
this.#cancelTimer = void 0;
|
|
921
|
-
}
|
|
922
|
-
return;
|
|
923
|
-
}
|
|
924
796
|
if (this.#active >= this.#concurrency) return;
|
|
925
|
-
if (this.#cancelTimer) return;
|
|
926
797
|
const now = this.#clock.now();
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
798
|
+
let selected;
|
|
799
|
+
let selectedIndex = -1;
|
|
800
|
+
let earliest = Number.POSITIVE_INFINITY;
|
|
801
|
+
for (let offset = 0; offset < this.#buckets.length; offset += 1) {
|
|
802
|
+
const index = (this.#cursor + offset) % this.#buckets.length;
|
|
803
|
+
const bucket = this.#buckets[index];
|
|
804
|
+
if (!bucket?.canStart) continue;
|
|
805
|
+
const readyAt = Math.max(this.#nextSlot, bucket.readyAt);
|
|
806
|
+
if (readyAt < earliest) earliest = readyAt;
|
|
807
|
+
if (readyAt <= now) {
|
|
808
|
+
selected = bucket;
|
|
809
|
+
selectedIndex = index;
|
|
810
|
+
break;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (!selected) {
|
|
814
|
+
if (Number.isFinite(earliest)) this.#armWakeup(Math.max(0, earliest - now));
|
|
815
|
+
else this.#cancelWakeup();
|
|
932
816
|
return;
|
|
933
817
|
}
|
|
934
|
-
|
|
935
|
-
|
|
818
|
+
this.#cursor = (selectedIndex + 1) % this.#buckets.length;
|
|
819
|
+
this.#active += 1;
|
|
936
820
|
if (this.#minGap > 0) this.#nextSlot = now + this.#minGap;
|
|
937
|
-
|
|
821
|
+
selected.dispatch(now, () => {
|
|
822
|
+
this.#active -= 1;
|
|
823
|
+
this.changed();
|
|
824
|
+
});
|
|
938
825
|
this.#drain();
|
|
939
826
|
}
|
|
827
|
+
#armWakeup(delay) {
|
|
828
|
+
const wakeupAt = this.#clock.now() + delay;
|
|
829
|
+
if (this.#cancelTimer && (this.#wakeupAt ?? Number.POSITIVE_INFINITY) <= wakeupAt) return;
|
|
830
|
+
this.#cancelWakeup();
|
|
831
|
+
this.#wakeupAt = wakeupAt;
|
|
832
|
+
this.#cancelTimer = this.#clock.schedule(() => {
|
|
833
|
+
this.#cancelTimer = void 0;
|
|
834
|
+
this.#wakeupAt = void 0;
|
|
835
|
+
this.#drain();
|
|
836
|
+
}, delay);
|
|
837
|
+
}
|
|
838
|
+
#cancelWakeup() {
|
|
839
|
+
this.#cancelTimer?.();
|
|
840
|
+
this.#cancelTimer = void 0;
|
|
841
|
+
this.#wakeupAt = void 0;
|
|
842
|
+
}
|
|
940
843
|
};
|
|
941
|
-
/** Длина окна лимита на сервере. */
|
|
942
|
-
const RATE_LIMIT_WINDOW = 6e4;
|
|
943
|
-
/**
|
|
944
|
-
* Пауза при исчерпании бакета неизвестной ёмкости.
|
|
945
|
-
*
|
|
946
|
-
* Ёмкость приходит в заголовке вместе с остатком, поэтому случай возможен только у чужого
|
|
947
|
-
* прокси, который прислал `remaining` без `limit`.
|
|
948
|
-
*/
|
|
949
|
-
const UNKNOWN_CAPACITY_PAUSE = 1e3;
|
|
950
844
|
/**
|
|
951
|
-
*
|
|
845
|
+
* Состояние и очередь одного серверного бакета.
|
|
952
846
|
*
|
|
953
|
-
*
|
|
954
|
-
*
|
|
955
|
-
* захвата общего слота: притормозивший счётчик не занимает общую ёмкость.
|
|
847
|
+
* Класс хранит локальную конкурентность, темп и серверную квоту, а решение о старте
|
|
848
|
+
* делегирует общему планировщику направления.
|
|
956
849
|
*
|
|
957
850
|
* @internal
|
|
958
851
|
*/
|
|
959
852
|
var BucketQueue = class {
|
|
960
853
|
#destination;
|
|
961
854
|
#bucket;
|
|
962
|
-
#
|
|
963
|
-
#shared;
|
|
855
|
+
#scheduler;
|
|
964
856
|
#clock;
|
|
965
857
|
#pacing;
|
|
858
|
+
#concurrency;
|
|
859
|
+
#minGap;
|
|
966
860
|
/** Ровный темп. Требует раздельных бакетов: без них ёмкость счётчика неизвестна. */
|
|
967
861
|
#smooth;
|
|
968
862
|
/**
|
|
@@ -979,6 +873,9 @@ var BucketQueue = class {
|
|
|
979
873
|
/** Последнее, что сказал сервер. Живёт и в режиме `off` — ради `rateLimitState()`. */
|
|
980
874
|
#limit;
|
|
981
875
|
#remaining;
|
|
876
|
+
#active = 0;
|
|
877
|
+
#nextSlot = 0;
|
|
878
|
+
#waiting = [];
|
|
982
879
|
/**
|
|
983
880
|
* Оценка остатка для режима `smooth`.
|
|
984
881
|
*
|
|
@@ -987,24 +884,19 @@ var BucketQueue = class {
|
|
|
987
884
|
*/
|
|
988
885
|
#tokens = 1;
|
|
989
886
|
#tokensAt;
|
|
990
|
-
|
|
991
|
-
* Номер поколения очереди. `stop()` увеличивает его, отсекая задачи, которые уже взяли
|
|
992
|
-
* слот бакета, но до общей очереди ещё не дошли.
|
|
993
|
-
*/
|
|
994
|
-
#generation = 0;
|
|
995
|
-
constructor(destination, bucket, shared, options, clock, featureDefinition) {
|
|
887
|
+
constructor(destination, bucket, scheduler, options, clock, featureDefinition) {
|
|
996
888
|
this.#destination = destination;
|
|
997
889
|
this.#bucket = bucket;
|
|
998
|
-
this.#
|
|
890
|
+
this.#scheduler = scheduler;
|
|
999
891
|
this.#clock = clock;
|
|
1000
892
|
this.#pacing = options.pacing;
|
|
1001
893
|
this.#smooth = options.buckets && options.pacing === RateLimitPacing.Smooth;
|
|
1002
894
|
this.#flatPause = options.buckets ? void 0 : options.retryDelays[0] ?? 0;
|
|
1003
895
|
this.#seedLimit = options.buckets ? featureDefinition?.limit ?? seedLimit(bucket, options) : void 0;
|
|
1004
|
-
this.#
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
896
|
+
this.#concurrency = options.buckets ? featureDefinition?.concurrency ?? options.bucketOverrides[bucket]?.concurrency ?? options.bucketConcurrency : options.concurrency;
|
|
897
|
+
const rps = options.buckets ? featureDefinition?.rps ?? options.bucketOverrides[bucket]?.rps : void 0;
|
|
898
|
+
this.#minGap = rps ? 1e3 / rps : 0;
|
|
899
|
+
scheduler.register(this);
|
|
1008
900
|
}
|
|
1009
901
|
/** Имя счётчика. При `buckets: false` — всегда `default`, каким бы ни был запрос. */
|
|
1010
902
|
get bucket() {
|
|
@@ -1012,19 +904,63 @@ var BucketQueue = class {
|
|
|
1012
904
|
}
|
|
1013
905
|
/** Запросов бакета прошло в общую очередь и ещё не завершилось. */
|
|
1014
906
|
get active() {
|
|
1015
|
-
return this.#
|
|
907
|
+
return this.#active;
|
|
1016
908
|
}
|
|
1017
909
|
/** Запросов бакета ждёт своей очереди. */
|
|
1018
910
|
get pending() {
|
|
1019
|
-
return this.#
|
|
911
|
+
return this.#waiting.length;
|
|
1020
912
|
}
|
|
1021
|
-
/**
|
|
913
|
+
/** Есть ли ожидающая задача, для которой свободен локальный слот. */
|
|
914
|
+
get canStart() {
|
|
915
|
+
return this.#waiting.length > 0 && this.#active < this.#concurrency;
|
|
916
|
+
}
|
|
917
|
+
/** Момент, раньше которого локальное ограничение не разрешает следующий старт. */
|
|
918
|
+
get readyAt() {
|
|
919
|
+
return this.#nextSlot;
|
|
920
|
+
}
|
|
921
|
+
/** Ставит запрос в очередь единого планировщика направления. */
|
|
1022
922
|
schedule(task, signal) {
|
|
1023
|
-
|
|
1024
|
-
return
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
923
|
+
if (signal?.aborted) return Promise.reject(queueAbortError());
|
|
924
|
+
return new Promise((resolve, reject) => {
|
|
925
|
+
let queued;
|
|
926
|
+
const detach = () => signal?.removeEventListener("abort", onAbort);
|
|
927
|
+
const onAbort = () => {
|
|
928
|
+
const index = this.#waiting.indexOf(queued);
|
|
929
|
+
if (index < 0) return;
|
|
930
|
+
this.#waiting.splice(index, 1);
|
|
931
|
+
queued.cancel(queueAbortError());
|
|
932
|
+
this.#scheduler.timingChanged();
|
|
933
|
+
};
|
|
934
|
+
queued = {
|
|
935
|
+
start: (complete) => {
|
|
936
|
+
detach();
|
|
937
|
+
Promise.resolve().then(task).then(resolve, reject).finally(complete);
|
|
938
|
+
},
|
|
939
|
+
cancel: (reason) => {
|
|
940
|
+
detach();
|
|
941
|
+
reject(reason);
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
this.#waiting.push(queued);
|
|
945
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
946
|
+
if (signal?.aborted) onAbort();
|
|
947
|
+
else this.#scheduler.changed();
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
/** Запускает первую задачу; вызывается только планировщиком направления. */
|
|
951
|
+
dispatch(now, complete) {
|
|
952
|
+
const task = this.#waiting.shift();
|
|
953
|
+
if (!task) {
|
|
954
|
+
complete();
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
this.#active += 1;
|
|
958
|
+
if (this.#smooth) this.#spend();
|
|
959
|
+
if (this.#minGap > 0) this.#nextSlot = Math.max(this.#nextSlot, now + this.#minGap);
|
|
960
|
+
task.start(() => {
|
|
961
|
+
this.#active -= 1;
|
|
962
|
+
complete();
|
|
963
|
+
});
|
|
1028
964
|
}
|
|
1029
965
|
/**
|
|
1030
966
|
* Учитывает заголовки ответа.
|
|
@@ -1040,7 +976,7 @@ var BucketQueue = class {
|
|
|
1040
976
|
if (this.#pacing === RateLimitPacing.Off || remaining === void 0) return 0;
|
|
1041
977
|
if (this.#flatPause !== void 0) {
|
|
1042
978
|
if (remaining > 0) return 0;
|
|
1043
|
-
this.#
|
|
979
|
+
this.#hold(this.#flatPause);
|
|
1044
980
|
return this.#flatPause;
|
|
1045
981
|
}
|
|
1046
982
|
const capacity = this.#capacity();
|
|
@@ -1048,11 +984,13 @@ var BucketQueue = class {
|
|
|
1048
984
|
if (capacity === void 0) return 0;
|
|
1049
985
|
this.#refill(capacity);
|
|
1050
986
|
if (remaining < this.#tokens) this.#tokens = remaining;
|
|
1051
|
-
|
|
987
|
+
const wait = this.#armPause(capacity);
|
|
988
|
+
this.#scheduler.timingChanged();
|
|
989
|
+
return wait;
|
|
1052
990
|
}
|
|
1053
991
|
if (remaining > 0) return 0;
|
|
1054
992
|
const wait = capacity === void 0 ? UNKNOWN_CAPACITY_PAUSE : Math.ceil(RATE_LIMIT_WINDOW / Math.max(capacity, 1));
|
|
1055
|
-
this.#
|
|
993
|
+
this.#hold(wait);
|
|
1056
994
|
return wait;
|
|
1057
995
|
}
|
|
1058
996
|
/** Придерживает бакет на названное время — путь ответа `429`. Оценка остатка обнуляется. */
|
|
@@ -1061,7 +999,7 @@ var BucketQueue = class {
|
|
|
1061
999
|
this.#tokens = 0;
|
|
1062
1000
|
this.#tokensAt = this.#clock.now();
|
|
1063
1001
|
}
|
|
1064
|
-
this.#
|
|
1002
|
+
this.#hold(ms);
|
|
1065
1003
|
}
|
|
1066
1004
|
/** Снимок для `rateLimitState()`. */
|
|
1067
1005
|
state() {
|
|
@@ -1070,17 +1008,19 @@ var BucketQueue = class {
|
|
|
1070
1008
|
bucket: this.#bucket,
|
|
1071
1009
|
limit: this.#limit,
|
|
1072
1010
|
remaining: this.#remaining,
|
|
1073
|
-
active: this.#
|
|
1074
|
-
pending: this.#
|
|
1011
|
+
active: this.#active,
|
|
1012
|
+
pending: this.#waiting.length
|
|
1075
1013
|
};
|
|
1076
1014
|
}
|
|
1077
|
-
/**
|
|
1078
|
-
* Останавливает уровень бакета. Общая очередь направления гасится пулом.
|
|
1079
|
-
*
|
|
1080
|
-
*/
|
|
1015
|
+
/** Отклоняет ещё не начатые задачи этого бакета. */
|
|
1081
1016
|
stop() {
|
|
1082
|
-
this
|
|
1083
|
-
|
|
1017
|
+
this.cancelWaiting(queueStoppedError());
|
|
1018
|
+
}
|
|
1019
|
+
/** Отклоняет ожидающие задачи; используется также при остановке всего направления. */
|
|
1020
|
+
cancelWaiting(reason, notify = true) {
|
|
1021
|
+
const pending = this.#waiting.splice(0, this.#waiting.length);
|
|
1022
|
+
for (const task of pending) task.cancel(reason);
|
|
1023
|
+
if (notify) this.#scheduler.timingChanged();
|
|
1084
1024
|
}
|
|
1085
1025
|
/** Лимит бакета: сказанный сервером, иначе табличный. */
|
|
1086
1026
|
#capacity() {
|
|
@@ -1107,9 +1047,15 @@ var BucketQueue = class {
|
|
|
1107
1047
|
#armPause(capacity) {
|
|
1108
1048
|
if (this.#tokens >= 1) return 0;
|
|
1109
1049
|
const wait = Math.ceil((1 - this.#tokens) * RATE_LIMIT_WINDOW / capacity);
|
|
1110
|
-
this.#
|
|
1050
|
+
this.#nextSlot = Math.max(this.#nextSlot, this.#clock.now() + wait);
|
|
1111
1051
|
return wait;
|
|
1112
1052
|
}
|
|
1053
|
+
/** Отодвигает ближайший допустимый старт и перепланирует общее пробуждение. */
|
|
1054
|
+
#hold(ms) {
|
|
1055
|
+
if (ms <= 0) return;
|
|
1056
|
+
this.#nextSlot = Math.max(this.#nextSlot, this.#clock.now() + ms);
|
|
1057
|
+
this.#scheduler.timingChanged();
|
|
1058
|
+
}
|
|
1113
1059
|
};
|
|
1114
1060
|
/** Лимит бакета до первого ответа: поправка пользователя важнее табличного значения. */
|
|
1115
1061
|
function seedLimit(bucket, options) {
|
|
@@ -1146,11 +1092,12 @@ var RequestQueuePool = class {
|
|
|
1146
1092
|
defineBucket(name, definition) {
|
|
1147
1093
|
const normalized = Object.freeze({
|
|
1148
1094
|
...definition.limit === void 0 ? {} : { limit: definition.limit },
|
|
1149
|
-
...definition.concurrency === void 0 ? {} : { concurrency: definition.concurrency }
|
|
1095
|
+
...definition.concurrency === void 0 ? {} : { concurrency: definition.concurrency },
|
|
1096
|
+
...definition.rps === void 0 ? {} : { rps: definition.rps }
|
|
1150
1097
|
});
|
|
1151
1098
|
const existing = this.#featureBuckets.get(name);
|
|
1152
1099
|
if (existing) {
|
|
1153
|
-
if (existing.definition.limit !== normalized.limit || existing.definition.concurrency !== normalized.concurrency) throw new require_errors.ItdConfigError(`Бакет feature «${name}» уже зарегистрирован с другими ограничениями`);
|
|
1100
|
+
if (existing.definition.limit !== normalized.limit || existing.definition.concurrency !== normalized.concurrency || existing.definition.rps !== normalized.rps) throw new require_errors.ItdConfigError(`Бакет feature «${name}» уже зарегистрирован с другими ограничениями`);
|
|
1154
1101
|
existing.references += 1;
|
|
1155
1102
|
} else this.#featureBuckets.set(name, {
|
|
1156
1103
|
definition: normalized,
|
|
@@ -1174,14 +1121,14 @@ var RequestQueuePool = class {
|
|
|
1174
1121
|
let entry = this.#destinations.get(destination);
|
|
1175
1122
|
if (!entry) {
|
|
1176
1123
|
entry = {
|
|
1177
|
-
|
|
1124
|
+
scheduler: new DestinationScheduler(this.#options, this.#clock),
|
|
1178
1125
|
buckets: /* @__PURE__ */ new Map()
|
|
1179
1126
|
};
|
|
1180
1127
|
this.#destinations.set(destination, entry);
|
|
1181
1128
|
}
|
|
1182
1129
|
let queue = entry.buckets.get(name);
|
|
1183
1130
|
if (!queue) {
|
|
1184
|
-
queue = new BucketQueue(destination, name, entry.
|
|
1131
|
+
queue = new BucketQueue(destination, name, entry.scheduler, this.#options, this.#clock, this.#featureBuckets.get(name)?.definition);
|
|
1185
1132
|
entry.buckets.set(name, queue);
|
|
1186
1133
|
}
|
|
1187
1134
|
return queue;
|
|
@@ -1193,14 +1140,11 @@ var RequestQueuePool = class {
|
|
|
1193
1140
|
return states;
|
|
1194
1141
|
}
|
|
1195
1142
|
/**
|
|
1196
|
-
* Останавливает
|
|
1197
|
-
*
|
|
1143
|
+
* Останавливает планировщики: ожидающие задачи отклоняются, а состояние счётчиков
|
|
1144
|
+
* и отложенные паузы сохраняются до следующего запуска — путь `close()`.
|
|
1198
1145
|
*/
|
|
1199
1146
|
stop() {
|
|
1200
|
-
for (const entry of this.#destinations.values())
|
|
1201
|
-
for (const queue of entry.buckets.values()) queue.stop();
|
|
1202
|
-
entry.shared.stop();
|
|
1203
|
-
}
|
|
1147
|
+
for (const entry of this.#destinations.values()) entry.scheduler.stop();
|
|
1204
1148
|
}
|
|
1205
1149
|
/** Останавливает очереди и забывает всё, что известно о счётчиках, — путь `dispose()`. */
|
|
1206
1150
|
clear() {
|
|
@@ -1240,7 +1184,7 @@ var ServiceRegistry = class {
|
|
|
1240
1184
|
#primaryHost;
|
|
1241
1185
|
/** @param primaryBaseUrl базовый URL клиента */
|
|
1242
1186
|
constructor(primaryBaseUrl) {
|
|
1243
|
-
this.#primaryHost = primaryBaseUrl ?
|
|
1187
|
+
this.#primaryHost = primaryBaseUrl ? require_redact.hostOf(primaryBaseUrl) : "";
|
|
1244
1188
|
}
|
|
1245
1189
|
/**
|
|
1246
1190
|
* Регистрирует сервис. Имя очищается от краевых пробелов, базовый URL приводится
|
|
@@ -1253,7 +1197,7 @@ var ServiceRegistry = class {
|
|
|
1253
1197
|
if (typeof raw !== "string" || raw.trim() === "") throw new require_errors.ItdConfigError("У сервиса должно быть непустое имя");
|
|
1254
1198
|
const name = raw.trim();
|
|
1255
1199
|
if (this.#services.has(name)) throw new require_errors.ItdConfigError(`Сервис «${name}» уже зарегистрирован и не может быть заменён`);
|
|
1256
|
-
const baseUrl =
|
|
1200
|
+
const baseUrl = require_redact.normalizeBaseUrl(definition.baseUrl);
|
|
1257
1201
|
if (definition.auth !== void 0 && typeof definition.auth !== "boolean") throw new require_errors.ItdConfigError(`services.${name}.auth должен быть boolean`);
|
|
1258
1202
|
let headers;
|
|
1259
1203
|
if (definition.headers !== void 0) {
|
|
@@ -1265,7 +1209,7 @@ var ServiceRegistry = class {
|
|
|
1265
1209
|
...definition,
|
|
1266
1210
|
name,
|
|
1267
1211
|
baseUrl,
|
|
1268
|
-
auth: definition.auth ??
|
|
1212
|
+
auth: definition.auth ?? require_redact.isSameSite(this.#primaryHost, require_redact.hostOf(baseUrl)),
|
|
1269
1213
|
...headers ? { headers } : {}
|
|
1270
1214
|
};
|
|
1271
1215
|
this.#services.set(name, Object.freeze(normalized));
|
|
@@ -1330,7 +1274,7 @@ var ServiceRegistry = class {
|
|
|
1330
1274
|
* @internal
|
|
1331
1275
|
*/
|
|
1332
1276
|
isPrimarySite(baseUrl) {
|
|
1333
|
-
return
|
|
1277
|
+
return require_redact.isSameSite(this.#primaryHost, require_redact.hostOf(baseUrl));
|
|
1334
1278
|
}
|
|
1335
1279
|
};
|
|
1336
1280
|
//#endregion
|
|
@@ -1392,14 +1336,14 @@ function markDisposeCleanupRequest(request) {
|
|
|
1392
1336
|
[DISPOSE_CLEANUP_REQUEST]: true
|
|
1393
1337
|
};
|
|
1394
1338
|
}
|
|
1395
|
-
/** Разрешено ли запросу завершать внутреннюю очистку после
|
|
1339
|
+
/** Разрешено ли запросу завершать внутреннюю очистку после `dispose()`. @internal */
|
|
1396
1340
|
function isDisposeCleanupRequest(request) {
|
|
1397
1341
|
return request[DISPOSE_CLEANUP_REQUEST] === true;
|
|
1398
1342
|
}
|
|
1399
1343
|
/**
|
|
1400
1344
|
* Собирает слои в один обработчик.
|
|
1401
1345
|
*
|
|
1402
|
-
* Первый слой
|
|
1346
|
+
* Первый слой становится внешним.
|
|
1403
1347
|
*
|
|
1404
1348
|
* @example
|
|
1405
1349
|
* ```ts
|
|
@@ -1427,24 +1371,17 @@ function withLayerHeaders(request, headers) {
|
|
|
1427
1371
|
/**
|
|
1428
1372
|
* Точка входа ресурсов в конвейер запросов.
|
|
1429
1373
|
*
|
|
1430
|
-
* Принимает
|
|
1431
|
-
* во внутреннем runtime клиента, — и отдаёт ресурсам методы `request`/`operation`.
|
|
1374
|
+
* Принимает готовую цепочку обработки и предоставляет ресурсам методы `request`/`execute`.
|
|
1432
1375
|
* О слоях и их порядке ресурсы не знают.
|
|
1433
1376
|
*/
|
|
1434
1377
|
var HttpClient = class {
|
|
1435
1378
|
#handler;
|
|
1379
|
+
#plugins;
|
|
1436
1380
|
#baseUrl;
|
|
1437
|
-
#catalog;
|
|
1438
1381
|
constructor(deps) {
|
|
1439
1382
|
this.#handler = deps.handler;
|
|
1383
|
+
this.#plugins = deps.plugins;
|
|
1440
1384
|
this.#baseUrl = deps.baseUrl;
|
|
1441
|
-
this.#catalog = deps.catalog;
|
|
1442
|
-
}
|
|
1443
|
-
/** Метод операции из каталога. Отсутствие описания — ошибка сборки клиента, не запроса. */
|
|
1444
|
-
#methodOf(operationId) {
|
|
1445
|
-
const method = this.#catalog.methodOf(operationId);
|
|
1446
|
-
if (method === void 0) throw new require_errors.ItdConfigError(`каталог операций не знает «${operationId}»`);
|
|
1447
|
-
return method;
|
|
1448
1385
|
}
|
|
1449
1386
|
/** Базовый URL, к которому обращается клиент. */
|
|
1450
1387
|
get baseUrl() {
|
|
@@ -1460,23 +1397,34 @@ var HttpClient = class {
|
|
|
1460
1397
|
* @throws {ItdNetworkError} если запрос не дошёл до сервера
|
|
1461
1398
|
*/
|
|
1462
1399
|
request(options) {
|
|
1463
|
-
|
|
1400
|
+
const request = identifyRequest(options);
|
|
1401
|
+
return this.#plugins.run(request, (prepared) => this.#handler(prepared));
|
|
1464
1402
|
}
|
|
1465
|
-
/** Выполняет
|
|
1466
|
-
operation
|
|
1467
|
-
return this
|
|
1403
|
+
/** Выполняет контракт операции; `next()` плагина возвращает результат после `read`. */
|
|
1404
|
+
execute(operation, options) {
|
|
1405
|
+
return this.#run(operation, options);
|
|
1406
|
+
}
|
|
1407
|
+
#run(operation, options) {
|
|
1408
|
+
const request = identifyRequest({
|
|
1468
1409
|
...options,
|
|
1469
|
-
operationId,
|
|
1470
|
-
method:
|
|
1410
|
+
operationId: operation.id,
|
|
1411
|
+
method: operation.method,
|
|
1412
|
+
retrySafety: options.retrySafety ?? operation.retrySafety
|
|
1471
1413
|
});
|
|
1414
|
+
return this.#plugins.run(request, async (prepared) => operation.read(await this.#handler(prepared), prepared));
|
|
1472
1415
|
}
|
|
1473
1416
|
/** Выполняет внутреннюю операцию финализации после начала `ItdClient.dispose()`. @internal */
|
|
1474
|
-
cleanupOperation(
|
|
1475
|
-
|
|
1417
|
+
cleanupOperation(operation, options) {
|
|
1418
|
+
const request = identifyRequest({
|
|
1476
1419
|
...options,
|
|
1477
|
-
operationId,
|
|
1478
|
-
method:
|
|
1479
|
-
|
|
1420
|
+
operationId: operation.id,
|
|
1421
|
+
method: operation.method,
|
|
1422
|
+
retrySafety: options.retrySafety ?? operation.retrySafety
|
|
1423
|
+
});
|
|
1424
|
+
return this.#plugins.run(request, async (prepared) => {
|
|
1425
|
+
const cleanupRequest = markDisposeCleanupRequest(prepared);
|
|
1426
|
+
return operation.read(await this.#handler(cleanupRequest), prepared);
|
|
1427
|
+
});
|
|
1480
1428
|
}
|
|
1481
1429
|
};
|
|
1482
1430
|
//#endregion
|
|
@@ -1501,6 +1449,50 @@ const RetrySafety = Object.freeze({
|
|
|
1501
1449
|
/** Повтор может создать ещё один побочный эффект. */
|
|
1502
1450
|
Unsafe: "unsafe"
|
|
1503
1451
|
});
|
|
1452
|
+
function snapshotAnnotation(value, operationId, copies) {
|
|
1453
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return value;
|
|
1454
|
+
if (typeof value === "function") throw new require_errors.ItdConfigError(`annotations операции «${operationId}» не должны содержать функции`);
|
|
1455
|
+
const existing = copies.get(value);
|
|
1456
|
+
if (existing !== void 0) return existing;
|
|
1457
|
+
if (Array.isArray(value)) {
|
|
1458
|
+
const copy = [];
|
|
1459
|
+
copies.set(value, copy);
|
|
1460
|
+
for (const item of value) copy.push(snapshotAnnotation(item, operationId, copies));
|
|
1461
|
+
return Object.freeze(copy);
|
|
1462
|
+
}
|
|
1463
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1464
|
+
if (prototype !== Object.prototype && prototype !== null) throw new require_errors.ItdConfigError(`annotations операции «${operationId}» должны состоять из обычных объектов, массивов и примитивов`);
|
|
1465
|
+
const copy = Object.create(prototype);
|
|
1466
|
+
copies.set(value, copy);
|
|
1467
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
1468
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
1469
|
+
if (!descriptor?.enumerable) continue;
|
|
1470
|
+
if (!("value" in descriptor)) throw new require_errors.ItdConfigError(`annotations операции «${operationId}» не должны содержать getters или setters`);
|
|
1471
|
+
Object.defineProperty(copy, key, {
|
|
1472
|
+
value: snapshotAnnotation(descriptor.value, operationId, copies),
|
|
1473
|
+
enumerable: true,
|
|
1474
|
+
writable: true,
|
|
1475
|
+
configurable: true
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
return Object.freeze(copy);
|
|
1479
|
+
}
|
|
1480
|
+
/** Создаёт неизменяемый контракт результата. @internal */
|
|
1481
|
+
function defineOperation(id, metadata, read) {
|
|
1482
|
+
const annotations = metadata.annotations === void 0 ? void 0 : snapshotAnnotation(metadata.annotations, id, /* @__PURE__ */ new Map());
|
|
1483
|
+
return Object.freeze({
|
|
1484
|
+
id,
|
|
1485
|
+
...metadata,
|
|
1486
|
+
...annotations === void 0 ? {} : { annotations },
|
|
1487
|
+
read
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
/** Возвращает разобранное тело без дополнительного преобразования. @internal */
|
|
1491
|
+
function identityResult(body) {
|
|
1492
|
+
return body;
|
|
1493
|
+
}
|
|
1494
|
+
/** Отбрасывает служебное тело операции без результата. @internal */
|
|
1495
|
+
function voidResult() {}
|
|
1504
1496
|
//#endregion
|
|
1505
1497
|
//#region src/core/scheduling/retry.ts
|
|
1506
1498
|
/** Методы raw-запроса, безопасность которых гарантирована самим HTTP-контрактом. */
|
|
@@ -1610,9 +1602,6 @@ function createQueueMiddleware(schedule) {
|
|
|
1610
1602
|
* разу, иначе, например, текст поста зашифруется дважды. Здесь же к операции привязывается
|
|
1611
1603
|
* snapshot attempt interceptors; сами они выполняются транспортом на каждой попытке.
|
|
1612
1604
|
*/
|
|
1613
|
-
function createPluginsMiddleware(plugins) {
|
|
1614
|
-
return (request, next) => plugins.run(request, next);
|
|
1615
|
-
}
|
|
1616
1605
|
/**
|
|
1617
1606
|
* Слой сервисов.
|
|
1618
1607
|
*
|
|
@@ -1626,7 +1615,7 @@ function createServicesMiddleware(registry) {
|
|
|
1626
1615
|
const service = request.service === void 0 ? void 0 : registry.require(request.service);
|
|
1627
1616
|
let prepared = request;
|
|
1628
1617
|
if (request.baseUrl !== void 0) {
|
|
1629
|
-
const baseUrl =
|
|
1618
|
+
const baseUrl = require_redact.normalizeBaseUrl(request.baseUrl);
|
|
1630
1619
|
if (baseUrl !== request.baseUrl) prepared = {
|
|
1631
1620
|
...prepared,
|
|
1632
1621
|
baseUrl
|
|
@@ -1770,90 +1759,6 @@ function createRetryMiddleware(deps) {
|
|
|
1770
1759
|
};
|
|
1771
1760
|
}
|
|
1772
1761
|
//#endregion
|
|
1773
|
-
//#region src/core/redact.ts
|
|
1774
|
-
/** Заголовки, значение которых нельзя писать в лог целиком. */
|
|
1775
|
-
const SECRET_HEADERS = /* @__PURE__ */ new Set([
|
|
1776
|
-
"authorization",
|
|
1777
|
-
"cookie",
|
|
1778
|
-
"set-cookie",
|
|
1779
|
-
"x-api-key"
|
|
1780
|
-
]);
|
|
1781
|
-
/** Поля тела запроса, значение которых нельзя писать в лог. */
|
|
1782
|
-
const SECRET_FIELDS = /* @__PURE__ */ new Set([
|
|
1783
|
-
"password",
|
|
1784
|
-
"oldpassword",
|
|
1785
|
-
"newpassword",
|
|
1786
|
-
"accesstoken",
|
|
1787
|
-
"refreshtoken",
|
|
1788
|
-
"currentpassword",
|
|
1789
|
-
"flowtoken",
|
|
1790
|
-
"token",
|
|
1791
|
-
"turnstiletoken",
|
|
1792
|
-
"otp"
|
|
1793
|
-
]);
|
|
1794
|
-
/** Query-параметры, содержащие секреты. */
|
|
1795
|
-
const SECRET_QUERY_PARAMS = /* @__PURE__ */ new Set(["token", "access_token"]);
|
|
1796
|
-
/**
|
|
1797
|
-
* Прячет середину секрета, оставляя концы для сопоставления.
|
|
1798
|
-
*
|
|
1799
|
-
* @example
|
|
1800
|
-
* ```ts
|
|
1801
|
-
* maskSecret('eyJhbGciOiJIUzI1NiJ9.abc'); // 'eyJh…(24)…abc'
|
|
1802
|
-
* ```
|
|
1803
|
-
*/
|
|
1804
|
-
function maskSecret(value) {
|
|
1805
|
-
if (value.length <= 8) return "…";
|
|
1806
|
-
return `${value.slice(0, 4)}…(${value.length})…${value.slice(-3)}`;
|
|
1807
|
-
}
|
|
1808
|
-
/** Маскирует секретные query-параметры URL перед записью в логи и ошибки. */
|
|
1809
|
-
function redactUrl(url) {
|
|
1810
|
-
try {
|
|
1811
|
-
const parsed = new URL(url);
|
|
1812
|
-
const entries = [...parsed.searchParams.entries()];
|
|
1813
|
-
parsed.search = "";
|
|
1814
|
-
for (const [name, value] of entries) parsed.searchParams.append(name, SECRET_QUERY_PARAMS.has(name.toLowerCase()) ? maskSecret(value) : value);
|
|
1815
|
-
return parsed.toString();
|
|
1816
|
-
} catch {
|
|
1817
|
-
return url.replace(/([?&](?:token|access_token)=)[^&#]*/gi, "$1[скрыто]");
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
/**
|
|
1821
|
-
* Готовит заголовки к записи в лог.
|
|
1822
|
-
*
|
|
1823
|
-
* `Authorization` и `Cookie` маскируются: логи часто уезжают в системы сбора,
|
|
1824
|
-
* и токен доступа в них попадать не должен.
|
|
1825
|
-
*/
|
|
1826
|
-
function redactHeaders(headers) {
|
|
1827
|
-
const result = {};
|
|
1828
|
-
headers.forEach((value, name) => {
|
|
1829
|
-
if (SECRET_HEADERS.has(name.toLowerCase())) {
|
|
1830
|
-
const spaceAt = value.indexOf(" ");
|
|
1831
|
-
result[name] = spaceAt > 0 ? `${value.slice(0, spaceAt)} ${maskSecret(value.slice(spaceAt + 1))}` : maskSecret(value);
|
|
1832
|
-
return;
|
|
1833
|
-
}
|
|
1834
|
-
result[name] = value;
|
|
1835
|
-
});
|
|
1836
|
-
return result;
|
|
1837
|
-
}
|
|
1838
|
-
/**
|
|
1839
|
-
* Готовит тело запроса к записи в лог: пароли, токены и коды OTP заменяются заглушкой.
|
|
1840
|
-
*
|
|
1841
|
-
* Обходит вложенные объекты и массивы. `FormData` и бинарные тела не раскрываются вовсе.
|
|
1842
|
-
*/
|
|
1843
|
-
function redactBody(body) {
|
|
1844
|
-
if (body === null || body === void 0) return body;
|
|
1845
|
-
if (typeof FormData !== "undefined" && body instanceof FormData) return "[FormData]";
|
|
1846
|
-
if (isBlob(body)) return "[Blob]";
|
|
1847
|
-
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) return "[binary]";
|
|
1848
|
-
if (Array.isArray(body)) return body.map(redactBody);
|
|
1849
|
-
if (typeof body === "object") {
|
|
1850
|
-
const result = {};
|
|
1851
|
-
for (const [key, value] of Object.entries(body)) result[key] = SECRET_FIELDS.has(key.toLowerCase()) ? "[скрыто]" : redactBody(value);
|
|
1852
|
-
return result;
|
|
1853
|
-
}
|
|
1854
|
-
return body;
|
|
1855
|
-
}
|
|
1856
|
-
//#endregion
|
|
1857
1762
|
//#region src/core/unwrap.ts
|
|
1858
1763
|
/**
|
|
1859
1764
|
* Снимает внешнюю обёртку `{ data: … }`.
|
|
@@ -2102,7 +2007,7 @@ function safeRawBody(body) {
|
|
|
2102
2007
|
if (!isRecord(body) || body.type !== "validation" || !isRecord(body.found)) return body;
|
|
2103
2008
|
return {
|
|
2104
2009
|
...body,
|
|
2105
|
-
found: redactBody(body.found)
|
|
2010
|
+
found: require_redact.redactBody(body.found)
|
|
2106
2011
|
};
|
|
2107
2012
|
}
|
|
2108
2013
|
/**
|
|
@@ -2149,7 +2054,7 @@ function setHeader(headers, name, value) {
|
|
|
2149
2054
|
/** Тело, которое отправляется как есть, без сериализации в JSON. */
|
|
2150
2055
|
function isRawBody(body) {
|
|
2151
2056
|
if (typeof body !== "object" || body === null) return typeof body === "string";
|
|
2152
|
-
return typeof FormData !== "undefined" && body instanceof FormData || isBlob(body) || typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams || typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body instanceof ArrayBuffer || ArrayBuffer.isView(body);
|
|
2057
|
+
return typeof FormData !== "undefined" && body instanceof FormData || require_redact.isBlob(body) || typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams || typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body instanceof ArrayBuffer || ArrayBuffer.isView(body);
|
|
2153
2058
|
}
|
|
2154
2059
|
/**
|
|
2155
2060
|
* Читает тело ответа один раз.
|
|
@@ -2295,8 +2200,8 @@ var Transport = class {
|
|
|
2295
2200
|
};
|
|
2296
2201
|
await dispatchRequestHook(this.#config.hooks, "onRequest", context);
|
|
2297
2202
|
this.#config.logger?.debug(`→ ${method} ${request.path}`, {
|
|
2298
|
-
headers: redactHeaders(headers),
|
|
2299
|
-
body: request.bodyFactory ? "[повторяемое тело]" : redactBody(request.body)
|
|
2203
|
+
headers: require_redact.redactHeaders(headers),
|
|
2204
|
+
body: request.bodyFactory ? "[повторяемое тело]" : require_redact.redactBody(request.body)
|
|
2300
2205
|
});
|
|
2301
2206
|
let response;
|
|
2302
2207
|
try {
|
|
@@ -2438,7 +2343,7 @@ var Transport = class {
|
|
|
2438
2343
|
*/
|
|
2439
2344
|
buildUrl(request) {
|
|
2440
2345
|
const base = request.baseUrl ?? this.#config.baseUrl;
|
|
2441
|
-
return
|
|
2346
|
+
return require_redact.joinUrl(base, request.path) + require_redact.buildQuery(request.query);
|
|
2442
2347
|
}
|
|
2443
2348
|
/**
|
|
2444
2349
|
* Собирает общие заголовки клиента: `User-Agent`, идентификатор устройства,
|
|
@@ -2489,7 +2394,7 @@ var Transport = class {
|
|
|
2489
2394
|
};
|
|
2490
2395
|
//#endregion
|
|
2491
2396
|
//#region src/core/execution/client-runtime.ts
|
|
2492
|
-
/** Имена стадий
|
|
2397
|
+
/** Имена стадий обработки запроса в порядке выполнения. @internal */
|
|
2493
2398
|
const ClientRuntimeStage = Object.freeze({
|
|
2494
2399
|
OperationPlugins: "operation_plugins",
|
|
2495
2400
|
Services: "services",
|
|
@@ -2507,14 +2412,15 @@ function createServiceRegistry(config) {
|
|
|
2507
2412
|
for (const service of config.services) services.define(service);
|
|
2508
2413
|
return services;
|
|
2509
2414
|
}
|
|
2510
|
-
/** Собирает
|
|
2415
|
+
/** Собирает внутренние компоненты и цепочку запросов клиента. @internal */
|
|
2511
2416
|
function createClientRuntime(options, internals) {
|
|
2512
2417
|
const catalog = internals.catalog;
|
|
2513
2418
|
const config = resolveRuntimeConfig(options, catalog);
|
|
2514
|
-
const jar = new
|
|
2419
|
+
const jar = new require_redact.CookieJar();
|
|
2515
2420
|
const plugins = new PluginRegistry({
|
|
2516
2421
|
shutdownTimeout: config.shutdownTimeout,
|
|
2517
|
-
clock: config.clock
|
|
2422
|
+
clock: config.clock,
|
|
2423
|
+
operationMetadata: (operationId) => catalog.definitionOf(operationId)
|
|
2518
2424
|
});
|
|
2519
2425
|
const services = createServiceRegistry(config);
|
|
2520
2426
|
const lifetime = new AbortController();
|
|
@@ -2538,7 +2444,7 @@ function createClientRuntime(options, internals) {
|
|
|
2538
2444
|
}) ?? catalog.bucketOf(request.operationId);
|
|
2539
2445
|
};
|
|
2540
2446
|
const queueKeyFor = (request) => requestQueueKey(request, (target) => ({
|
|
2541
|
-
destination:
|
|
2447
|
+
destination: require_redact.originOf(transport.buildUrl(target)) || void 0,
|
|
2542
2448
|
bucket: bucketFor(target)
|
|
2543
2449
|
}));
|
|
2544
2450
|
const queueFor = (request) => {
|
|
@@ -2563,10 +2469,6 @@ function createClientRuntime(options, internals) {
|
|
|
2563
2469
|
lifetimeSignal: lifetime.signal
|
|
2564
2470
|
});
|
|
2565
2471
|
const stages = [
|
|
2566
|
-
{
|
|
2567
|
-
name: ClientRuntimeStage.OperationPlugins,
|
|
2568
|
-
middleware: createPluginsMiddleware(plugins)
|
|
2569
|
-
},
|
|
2570
2472
|
{
|
|
2571
2473
|
name: ClientRuntimeStage.Services,
|
|
2572
2474
|
middleware: createServicesMiddleware(services)
|
|
@@ -2618,17 +2520,21 @@ function createClientRuntime(options, internals) {
|
|
|
2618
2520
|
}
|
|
2619
2521
|
return handler(request);
|
|
2620
2522
|
};
|
|
2523
|
+
const http = new HttpClient({
|
|
2524
|
+
handler: clientHandler,
|
|
2525
|
+
plugins,
|
|
2526
|
+
baseUrl: config.baseUrl
|
|
2527
|
+
});
|
|
2621
2528
|
auth = internals.auth({
|
|
2622
2529
|
config,
|
|
2623
|
-
|
|
2530
|
+
http,
|
|
2624
2531
|
cookies: jar
|
|
2625
2532
|
});
|
|
2626
|
-
const
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
const stageOrder = Object.freeze([...stages.map(({ name }) => name), ClientRuntimeStage.Transport]);
|
|
2533
|
+
const stageOrder = Object.freeze([
|
|
2534
|
+
ClientRuntimeStage.OperationPlugins,
|
|
2535
|
+
...stages.map(({ name }) => name),
|
|
2536
|
+
ClientRuntimeStage.Transport
|
|
2537
|
+
]);
|
|
2632
2538
|
return {
|
|
2633
2539
|
config,
|
|
2634
2540
|
http,
|
|
@@ -2637,6 +2543,24 @@ function createClientRuntime(options, internals) {
|
|
|
2637
2543
|
plugins,
|
|
2638
2544
|
services,
|
|
2639
2545
|
stageOrder,
|
|
2546
|
+
connection: (serviceName) => {
|
|
2547
|
+
const service = serviceName === void 0 ? void 0 : services.require(serviceName);
|
|
2548
|
+
const baseUrl = service?.baseUrl ?? config.baseUrl;
|
|
2549
|
+
return Object.freeze({
|
|
2550
|
+
baseUrl,
|
|
2551
|
+
authorize: service?.auth ?? true,
|
|
2552
|
+
fetch: config.fetch,
|
|
2553
|
+
clock: config.clock,
|
|
2554
|
+
logger: config.logger,
|
|
2555
|
+
baseHeaders: async (url) => {
|
|
2556
|
+
const headers = await transport.platformHeaders(url);
|
|
2557
|
+
for (const [name, value] of Object.entries(service?.headers ?? {})) headers.set(name, value);
|
|
2558
|
+
return headers;
|
|
2559
|
+
},
|
|
2560
|
+
getToken: () => auth.token(),
|
|
2561
|
+
refreshAuth: () => auth.recover()
|
|
2562
|
+
});
|
|
2563
|
+
},
|
|
2640
2564
|
platformHeaders: (url) => transport.platformHeaders(url),
|
|
2641
2565
|
rateLimitState: () => queues?.states() ?? [],
|
|
2642
2566
|
registerRateLimitBucket: (name, definition) => queues?.defineBucket(name, definition),
|
|
@@ -2684,7 +2608,16 @@ const BUCKET_LIMITS = Object.freeze({
|
|
|
2684
2608
|
"posts.create": 5,
|
|
2685
2609
|
"users.updateMe": 3,
|
|
2686
2610
|
"reports.create": 3,
|
|
2687
|
-
"verification.submit": 3
|
|
2611
|
+
"verification.submit": 3,
|
|
2612
|
+
shop: 150,
|
|
2613
|
+
"shop.delivery.cities": 60,
|
|
2614
|
+
"shop.delivery.calculate": 45,
|
|
2615
|
+
"shop.delivery.points": 30,
|
|
2616
|
+
"shop.consents.record": 15,
|
|
2617
|
+
"shop.orders.pay": 13,
|
|
2618
|
+
"shop.orders.verifyAccessCode": 13,
|
|
2619
|
+
"shop.orders.create": 12,
|
|
2620
|
+
"shop.orders.requestAccessCode": 4
|
|
2688
2621
|
});
|
|
2689
2622
|
/** Счётчик, из которого списывается путь без собственного правила на сервере. */
|
|
2690
2623
|
const DEFAULT_RATE_LIMIT_BUCKET = "default";
|
|
@@ -3039,12 +2972,12 @@ const OPERATIONS = freezeOperations({
|
|
|
3039
2972
|
method: "PUT",
|
|
3040
2973
|
retrySafety: RetrySafety.Idempotent
|
|
3041
2974
|
},
|
|
3042
|
-
"
|
|
2975
|
+
"events.notifications.poll.updates": {
|
|
3043
2976
|
method: "GET",
|
|
3044
2977
|
retrySafety: RetrySafety.Safe,
|
|
3045
2978
|
bucket: "notifications"
|
|
3046
2979
|
},
|
|
3047
|
-
"
|
|
2980
|
+
"events.notifications.poll.unread": {
|
|
3048
2981
|
method: "GET",
|
|
3049
2982
|
retrySafety: RetrySafety.Safe,
|
|
3050
2983
|
bucket: "notifications"
|
|
@@ -3135,8 +3068,75 @@ const OPERATIONS = freezeOperations({
|
|
|
3135
3068
|
"telemetry.interaction": {
|
|
3136
3069
|
method: "POST",
|
|
3137
3070
|
retrySafety: RetrySafety.Unsafe
|
|
3071
|
+
},
|
|
3072
|
+
"shop.products.list": {
|
|
3073
|
+
method: "GET",
|
|
3074
|
+
retrySafety: RetrySafety.Safe,
|
|
3075
|
+
bucket: "shop"
|
|
3076
|
+
},
|
|
3077
|
+
"shop.products.get": {
|
|
3078
|
+
method: "GET",
|
|
3079
|
+
retrySafety: RetrySafety.Safe,
|
|
3080
|
+
bucket: "shop"
|
|
3081
|
+
},
|
|
3082
|
+
"shop.delivery.countries": {
|
|
3083
|
+
method: "GET",
|
|
3084
|
+
retrySafety: RetrySafety.Safe,
|
|
3085
|
+
bucket: "shop"
|
|
3086
|
+
},
|
|
3087
|
+
"shop.delivery.cities": {
|
|
3088
|
+
method: "GET",
|
|
3089
|
+
retrySafety: RetrySafety.Safe,
|
|
3090
|
+
bucket: "shop.delivery.cities"
|
|
3091
|
+
},
|
|
3092
|
+
"shop.delivery.points": {
|
|
3093
|
+
method: "GET",
|
|
3094
|
+
retrySafety: RetrySafety.Safe,
|
|
3095
|
+
bucket: "shop.delivery.points"
|
|
3096
|
+
},
|
|
3097
|
+
"shop.delivery.calculate": {
|
|
3098
|
+
method: "POST",
|
|
3099
|
+
retrySafety: RetrySafety.Safe,
|
|
3100
|
+
bucket: "shop.delivery.calculate"
|
|
3101
|
+
},
|
|
3102
|
+
"shop.orders.create": {
|
|
3103
|
+
method: "POST",
|
|
3104
|
+
retrySafety: RetrySafety.Idempotent,
|
|
3105
|
+
bucket: "shop.orders.create"
|
|
3106
|
+
},
|
|
3107
|
+
"shop.orders.list": {
|
|
3108
|
+
method: "GET",
|
|
3109
|
+
retrySafety: RetrySafety.Safe
|
|
3110
|
+
},
|
|
3111
|
+
"shop.orders.get": {
|
|
3112
|
+
method: "GET",
|
|
3113
|
+
retrySafety: RetrySafety.Safe
|
|
3114
|
+
},
|
|
3115
|
+
"shop.orders.pay": {
|
|
3116
|
+
method: "POST",
|
|
3117
|
+
retrySafety: RetrySafety.Unsafe,
|
|
3118
|
+
bucket: "shop.orders.pay"
|
|
3119
|
+
},
|
|
3120
|
+
"shop.orders.requestAccessCode": {
|
|
3121
|
+
method: "POST",
|
|
3122
|
+
retrySafety: RetrySafety.Unsafe,
|
|
3123
|
+
bucket: "shop.orders.requestAccessCode"
|
|
3124
|
+
},
|
|
3125
|
+
"shop.orders.verifyAccessCode": {
|
|
3126
|
+
method: "POST",
|
|
3127
|
+
retrySafety: RetrySafety.Unsafe,
|
|
3128
|
+
bucket: "shop.orders.verifyAccessCode"
|
|
3129
|
+
},
|
|
3130
|
+
"shop.consents.record": {
|
|
3131
|
+
method: "POST",
|
|
3132
|
+
retrySafety: RetrySafety.Unsafe,
|
|
3133
|
+
bucket: "shop.consents.record"
|
|
3138
3134
|
}
|
|
3139
3135
|
});
|
|
3136
|
+
/** Создаёт контракт результата встроенной операции с проверяемым ID. */
|
|
3137
|
+
function defineBuiltInOperation(id, read = identityResult) {
|
|
3138
|
+
return defineOperation(id, OPERATIONS[id], read);
|
|
3139
|
+
}
|
|
3140
3140
|
const BUILT_IN_IDS = new Set(Object.keys(OPERATIONS));
|
|
3141
3141
|
/** Проверяет принадлежность ID встроенному каталогу. */
|
|
3142
3142
|
function isBuiltInOperationId(value) {
|
|
@@ -3171,6 +3171,7 @@ function operationBucket(id) {
|
|
|
3171
3171
|
* @internal
|
|
3172
3172
|
*/
|
|
3173
3173
|
const ITD_CATALOG = Object.freeze({
|
|
3174
|
+
definitionOf: (id) => isBuiltInOperationId(id) ? OPERATIONS[id] : void 0,
|
|
3174
3175
|
retrySafetyOf: (id) => isBuiltInOperationId(id) ? operationRetrySafety(id) : void 0,
|
|
3175
3176
|
methodOf: (id) => isBuiltInOperationId(id) ? operationMethod(id) : void 0,
|
|
3176
3177
|
bucketOf: (id) => operationBucket(id),
|
|
@@ -3257,8 +3258,8 @@ const ReportReason = Object.freeze({
|
|
|
3257
3258
|
Fraud: "fraud",
|
|
3258
3259
|
Other: "other"
|
|
3259
3260
|
});
|
|
3260
|
-
/** Состояние
|
|
3261
|
-
const
|
|
3261
|
+
/** Состояние соединения событийного канала. */
|
|
3262
|
+
const EventChannelStatus = Object.freeze({
|
|
3262
3263
|
Connecting: "connecting",
|
|
3263
3264
|
Connected: "connected",
|
|
3264
3265
|
Error: "error",
|
|
@@ -3427,6 +3428,7 @@ const ItdErrorCode = Object.freeze({
|
|
|
3427
3428
|
SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
|
|
3428
3429
|
MISSING_FLOW_TOKEN: "MISSING_FLOW_TOKEN",
|
|
3429
3430
|
PROFILE_USERNAME_TAKEN: "PROFILE_USERNAME_TAKEN",
|
|
3431
|
+
PROFILE_USERNAME_RESERVED: "PROFILE_USERNAME_RESERVED",
|
|
3430
3432
|
PROFILE_RESTRICTION_ACTIVE: "PROFILE_RESTRICTION_ACTIVE",
|
|
3431
3433
|
PROFILE_MODIFICATION_RESTRICTED: "PROFILE_MODIFICATION_RESTRICTED",
|
|
3432
3434
|
CONTENT_MODERATION_FAILED: "CONTENT_MODERATION_FAILED",
|
|
@@ -3577,6 +3579,103 @@ function readUnreadCountEvent(data) {
|
|
|
3577
3579
|
return typeof payload.count === "number" ? payload.count : void 0;
|
|
3578
3580
|
}
|
|
3579
3581
|
//#endregion
|
|
3582
|
+
//#region src/operations/common.ts
|
|
3583
|
+
/** Контракт встроенной операции без дополнительного преобразования результата. */
|
|
3584
|
+
function passthroughOperation(id) {
|
|
3585
|
+
return defineBuiltInOperation(id);
|
|
3586
|
+
}
|
|
3587
|
+
/** Контракт встроенной операции без возвращаемого значения. */
|
|
3588
|
+
function voidOperation(id) {
|
|
3589
|
+
return defineBuiltInOperation(id, voidResult);
|
|
3590
|
+
}
|
|
3591
|
+
//#endregion
|
|
3592
|
+
//#region src/core/auth-provider.ts
|
|
3593
|
+
/**
|
|
3594
|
+
* Идентификатор устройства, общий для провайдеров без сессии.
|
|
3595
|
+
*
|
|
3596
|
+
* Значение своё у каждого провайдера: сервер различает по нему записи в списке сессий,
|
|
3597
|
+
* поэтому делить его между клиентами нельзя. Хранилища нет, так что перезапуск процесса
|
|
3598
|
+
* значение не переживает — в этом и разница с полноценной сессией.
|
|
3599
|
+
*/
|
|
3600
|
+
function localDeviceId() {
|
|
3601
|
+
let deviceId;
|
|
3602
|
+
return () => {
|
|
3603
|
+
deviceId ??= require_redact.createDeviceId();
|
|
3604
|
+
return Promise.resolve(deviceId);
|
|
3605
|
+
};
|
|
3606
|
+
}
|
|
3607
|
+
/**
|
|
3608
|
+
* Авторизации нет: заголовок не подставляется, ответ `401` не восстанавливается.
|
|
3609
|
+
*
|
|
3610
|
+
* @example
|
|
3611
|
+
* ```ts
|
|
3612
|
+
* const api = createRestClient(); // публичные эндпоинты доступны и без токена
|
|
3613
|
+
* ```
|
|
3614
|
+
*/
|
|
3615
|
+
function anonymousAuth() {
|
|
3616
|
+
return {
|
|
3617
|
+
token: () => Promise.resolve(null),
|
|
3618
|
+
prepare: () => Promise.resolve(),
|
|
3619
|
+
currentHeaders: () => ({}),
|
|
3620
|
+
recover: () => Promise.resolve(false),
|
|
3621
|
+
deviceId: localDeviceId(),
|
|
3622
|
+
dispose: () => {}
|
|
3623
|
+
};
|
|
3624
|
+
}
|
|
3625
|
+
/**
|
|
3626
|
+
* Готовый Bearer-токен: ни хранилища, ни продления.
|
|
3627
|
+
*
|
|
3628
|
+
* Ответ `401` уходит вызывающему коду как есть — обновить токен провайдеру нечем.
|
|
3629
|
+
* Для сессии, которая продлевает себя сама, нужен полный клиент.
|
|
3630
|
+
*
|
|
3631
|
+
* @example
|
|
3632
|
+
* ```ts
|
|
3633
|
+
* const api = createRestClient({ auth: bearerToken(process.env.ITD_TOKEN) });
|
|
3634
|
+
* ```
|
|
3635
|
+
*/
|
|
3636
|
+
function bearerToken(accessToken) {
|
|
3637
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
3638
|
+
return {
|
|
3639
|
+
token: () => Promise.resolve(accessToken),
|
|
3640
|
+
prepare: () => Promise.resolve(),
|
|
3641
|
+
currentHeaders: () => headers,
|
|
3642
|
+
recover: () => Promise.resolve(false),
|
|
3643
|
+
deviceId: localDeviceId(),
|
|
3644
|
+
dispose: () => {}
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
/**
|
|
3648
|
+
* Токен из внешнего источника — хранилища секретов, кэша, соседнего сервиса.
|
|
3649
|
+
*
|
|
3650
|
+
* Источник спрашивается на стадии подготовки, до входа в очередь: там ожидание безопасно,
|
|
3651
|
+
* а слот транспорта ещё не занят. Значение держится до следующей подготовки, потому что
|
|
3652
|
+
* подстановка заголовков обязана быть синхронной.
|
|
3653
|
+
*
|
|
3654
|
+
* @example
|
|
3655
|
+
* ```ts
|
|
3656
|
+
* const api = createRestClient({ auth: tokenProvider(() => vault.read('itd')) });
|
|
3657
|
+
* ```
|
|
3658
|
+
*/
|
|
3659
|
+
function tokenProvider(getToken) {
|
|
3660
|
+
let cached = null;
|
|
3661
|
+
const read = async () => {
|
|
3662
|
+
cached = await getToken();
|
|
3663
|
+
return cached;
|
|
3664
|
+
};
|
|
3665
|
+
return {
|
|
3666
|
+
token: read,
|
|
3667
|
+
prepare: async () => {
|
|
3668
|
+
await read();
|
|
3669
|
+
},
|
|
3670
|
+
currentHeaders: () => cached ? { Authorization: `Bearer ${cached}` } : {},
|
|
3671
|
+
recover: () => Promise.resolve(false),
|
|
3672
|
+
deviceId: localDeviceId(),
|
|
3673
|
+
dispose: () => {
|
|
3674
|
+
cached = null;
|
|
3675
|
+
}
|
|
3676
|
+
};
|
|
3677
|
+
}
|
|
3678
|
+
//#endregion
|
|
3580
3679
|
//#region src/notifications/text.ts
|
|
3581
3680
|
/** Имя, которое подставляется, если участник неизвестен. */
|
|
3582
3681
|
const UNKNOWN_ACTOR = "Пользователь";
|
|
@@ -3750,6 +3849,12 @@ Object.defineProperty(exports, "DEFAULT_STATUS_BASE_URL", {
|
|
|
3750
3849
|
return DEFAULT_STATUS_BASE_URL;
|
|
3751
3850
|
}
|
|
3752
3851
|
});
|
|
3852
|
+
Object.defineProperty(exports, "EventChannelStatus", {
|
|
3853
|
+
enumerable: true,
|
|
3854
|
+
get: function() {
|
|
3855
|
+
return EventChannelStatus;
|
|
3856
|
+
}
|
|
3857
|
+
});
|
|
3753
3858
|
Object.defineProperty(exports, "FeedTab", {
|
|
3754
3859
|
enumerable: true,
|
|
3755
3860
|
get: function() {
|
|
@@ -3810,12 +3915,6 @@ Object.defineProperty(exports, "RateLimitPacing", {
|
|
|
3810
3915
|
return RateLimitPacing;
|
|
3811
3916
|
}
|
|
3812
3917
|
});
|
|
3813
|
-
Object.defineProperty(exports, "RealtimeStatus", {
|
|
3814
|
-
enumerable: true,
|
|
3815
|
-
get: function() {
|
|
3816
|
-
return RealtimeStatus;
|
|
3817
|
-
}
|
|
3818
|
-
});
|
|
3819
3918
|
Object.defineProperty(exports, "ReportReason", {
|
|
3820
3919
|
enumerable: true,
|
|
3821
3920
|
get: function() {
|
|
@@ -3840,12 +3939,6 @@ Object.defineProperty(exports, "RetrySafety", {
|
|
|
3840
3939
|
return RetrySafety;
|
|
3841
3940
|
}
|
|
3842
3941
|
});
|
|
3843
|
-
Object.defineProperty(exports, "RuntimeMode", {
|
|
3844
|
-
enumerable: true,
|
|
3845
|
-
get: function() {
|
|
3846
|
-
return RuntimeMode;
|
|
3847
|
-
}
|
|
3848
|
-
});
|
|
3849
3942
|
Object.defineProperty(exports, "STATUS_SERVICE", {
|
|
3850
3943
|
enumerable: true,
|
|
3851
3944
|
get: function() {
|
|
@@ -3882,12 +3975,24 @@ Object.defineProperty(exports, "WallAccess", {
|
|
|
3882
3975
|
return WallAccess;
|
|
3883
3976
|
}
|
|
3884
3977
|
});
|
|
3978
|
+
Object.defineProperty(exports, "anonymousAuth", {
|
|
3979
|
+
enumerable: true,
|
|
3980
|
+
get: function() {
|
|
3981
|
+
return anonymousAuth;
|
|
3982
|
+
}
|
|
3983
|
+
});
|
|
3885
3984
|
Object.defineProperty(exports, "assertPluginRemovable", {
|
|
3886
3985
|
enumerable: true,
|
|
3887
3986
|
get: function() {
|
|
3888
3987
|
return assertPluginRemovable;
|
|
3889
3988
|
}
|
|
3890
3989
|
});
|
|
3990
|
+
Object.defineProperty(exports, "bearerToken", {
|
|
3991
|
+
enumerable: true,
|
|
3992
|
+
get: function() {
|
|
3993
|
+
return bearerToken;
|
|
3994
|
+
}
|
|
3995
|
+
});
|
|
3891
3996
|
Object.defineProperty(exports, "canonicalNotificationType", {
|
|
3892
3997
|
enumerable: true,
|
|
3893
3998
|
get: function() {
|
|
@@ -3906,40 +4011,40 @@ Object.defineProperty(exports, "createDeadline", {
|
|
|
3906
4011
|
return createDeadline;
|
|
3907
4012
|
}
|
|
3908
4013
|
});
|
|
3909
|
-
Object.defineProperty(exports, "
|
|
4014
|
+
Object.defineProperty(exports, "defineBuiltInOperation", {
|
|
3910
4015
|
enumerable: true,
|
|
3911
4016
|
get: function() {
|
|
3912
|
-
return
|
|
4017
|
+
return defineBuiltInOperation;
|
|
3913
4018
|
}
|
|
3914
4019
|
});
|
|
3915
|
-
Object.defineProperty(exports, "
|
|
4020
|
+
Object.defineProperty(exports, "defineOperation", {
|
|
3916
4021
|
enumerable: true,
|
|
3917
4022
|
get: function() {
|
|
3918
|
-
return
|
|
4023
|
+
return defineOperation;
|
|
3919
4024
|
}
|
|
3920
4025
|
});
|
|
3921
|
-
Object.defineProperty(exports, "
|
|
4026
|
+
Object.defineProperty(exports, "formatNotificationText", {
|
|
3922
4027
|
enumerable: true,
|
|
3923
4028
|
get: function() {
|
|
3924
|
-
return
|
|
4029
|
+
return formatNotificationText;
|
|
3925
4030
|
}
|
|
3926
4031
|
});
|
|
3927
|
-
Object.defineProperty(exports, "
|
|
4032
|
+
Object.defineProperty(exports, "identityResult", {
|
|
3928
4033
|
enumerable: true,
|
|
3929
4034
|
get: function() {
|
|
3930
|
-
return
|
|
4035
|
+
return identityResult;
|
|
3931
4036
|
}
|
|
3932
4037
|
});
|
|
3933
|
-
Object.defineProperty(exports, "
|
|
4038
|
+
Object.defineProperty(exports, "installAsyncDisposeFallback", {
|
|
3934
4039
|
enumerable: true,
|
|
3935
4040
|
get: function() {
|
|
3936
|
-
return
|
|
4041
|
+
return installAsyncDisposeFallback;
|
|
3937
4042
|
}
|
|
3938
4043
|
});
|
|
3939
|
-
Object.defineProperty(exports, "
|
|
4044
|
+
Object.defineProperty(exports, "isBuiltInOperationId", {
|
|
3940
4045
|
enumerable: true,
|
|
3941
4046
|
get: function() {
|
|
3942
|
-
return
|
|
4047
|
+
return isBuiltInOperationId;
|
|
3943
4048
|
}
|
|
3944
4049
|
});
|
|
3945
4050
|
Object.defineProperty(exports, "isKnownNotificationType", {
|
|
@@ -3960,12 +4065,6 @@ Object.defineProperty(exports, "isRecord$1", {
|
|
|
3960
4065
|
return isRecord$1;
|
|
3961
4066
|
}
|
|
3962
4067
|
});
|
|
3963
|
-
Object.defineProperty(exports, "maskSecret", {
|
|
3964
|
-
enumerable: true,
|
|
3965
|
-
get: function() {
|
|
3966
|
-
return maskSecret;
|
|
3967
|
-
}
|
|
3968
|
-
});
|
|
3969
4068
|
Object.defineProperty(exports, "mergeService", {
|
|
3970
4069
|
enumerable: true,
|
|
3971
4070
|
get: function() {
|
|
@@ -4002,6 +4101,12 @@ Object.defineProperty(exports, "orderPluginDefinitions", {
|
|
|
4002
4101
|
return orderPluginDefinitions;
|
|
4003
4102
|
}
|
|
4004
4103
|
});
|
|
4104
|
+
Object.defineProperty(exports, "passthroughOperation", {
|
|
4105
|
+
enumerable: true,
|
|
4106
|
+
get: function() {
|
|
4107
|
+
return passthroughOperation;
|
|
4108
|
+
}
|
|
4109
|
+
});
|
|
4005
4110
|
Object.defineProperty(exports, "pickArray", {
|
|
4006
4111
|
enumerable: true,
|
|
4007
4112
|
get: function() {
|
|
@@ -4044,12 +4149,6 @@ Object.defineProperty(exports, "readUnreadCountEvent", {
|
|
|
4044
4149
|
return readUnreadCountEvent;
|
|
4045
4150
|
}
|
|
4046
4151
|
});
|
|
4047
|
-
Object.defineProperty(exports, "redactUrl", {
|
|
4048
|
-
enumerable: true,
|
|
4049
|
-
get: function() {
|
|
4050
|
-
return redactUrl;
|
|
4051
|
-
}
|
|
4052
|
-
});
|
|
4053
4152
|
Object.defineProperty(exports, "requireOptionalBoolean", {
|
|
4054
4153
|
enumerable: true,
|
|
4055
4154
|
get: function() {
|
|
@@ -4068,17 +4167,29 @@ Object.defineProperty(exports, "resolveRateLimit", {
|
|
|
4068
4167
|
return resolveRateLimit;
|
|
4069
4168
|
}
|
|
4070
4169
|
});
|
|
4071
|
-
Object.defineProperty(exports, "
|
|
4170
|
+
Object.defineProperty(exports, "systemClock", {
|
|
4171
|
+
enumerable: true,
|
|
4172
|
+
get: function() {
|
|
4173
|
+
return systemClock;
|
|
4174
|
+
}
|
|
4175
|
+
});
|
|
4176
|
+
Object.defineProperty(exports, "tokenProvider", {
|
|
4072
4177
|
enumerable: true,
|
|
4073
4178
|
get: function() {
|
|
4074
|
-
return
|
|
4179
|
+
return tokenProvider;
|
|
4075
4180
|
}
|
|
4076
4181
|
});
|
|
4077
|
-
Object.defineProperty(exports, "
|
|
4182
|
+
Object.defineProperty(exports, "voidOperation", {
|
|
4078
4183
|
enumerable: true,
|
|
4079
4184
|
get: function() {
|
|
4080
|
-
return
|
|
4185
|
+
return voidOperation;
|
|
4186
|
+
}
|
|
4187
|
+
});
|
|
4188
|
+
Object.defineProperty(exports, "voidResult", {
|
|
4189
|
+
enumerable: true,
|
|
4190
|
+
get: function() {
|
|
4191
|
+
return voidResult;
|
|
4081
4192
|
}
|
|
4082
4193
|
});
|
|
4083
4194
|
|
|
4084
|
-
//# sourceMappingURL=url-
|
|
4195
|
+
//# sourceMappingURL=url-Bsb3xE5U.cjs.map
|