itd-api 0.7.0 → 0.7.1

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.
Files changed (89) hide show
  1. package/dist/index.cjs +465 -9804
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +231 -4950
  4. package/dist/index.d.ts +231 -4950
  5. package/dist/index.js +331 -9670
  6. package/dist/index.js.map +1 -1
  7. package/dist/{node.cjs → node/index.cjs} +16 -14
  8. package/dist/node/index.cjs.map +1 -0
  9. package/dist/{node.d.cts → node/index.d.cts} +4 -3
  10. package/dist/{node.d.ts → node/index.d.ts} +4 -3
  11. package/dist/{node.js → node/index.js} +5 -3
  12. package/dist/node/index.js.map +1 -0
  13. package/dist/realtime/index.cjs +165 -0
  14. package/dist/realtime/index.cjs.map +1 -0
  15. package/dist/realtime/index.d.cts +51 -0
  16. package/dist/realtime/index.d.ts +51 -0
  17. package/dist/realtime/index.js +120 -0
  18. package/dist/realtime/index.js.map +1 -0
  19. package/dist/rest/index.cjs +334 -0
  20. package/dist/rest/index.cjs.map +1 -0
  21. package/dist/rest/index.d.cts +138 -0
  22. package/dist/rest/index.d.ts +138 -0
  23. package/dist/rest/index.js +238 -0
  24. package/dist/rest/index.js.map +1 -0
  25. package/dist/shared/auth-provider-BfogACAb.js +91 -0
  26. package/dist/shared/auth-provider-BfogACAb.js.map +1 -0
  27. package/dist/shared/auth-provider-CTJkKfgy.cjs +108 -0
  28. package/dist/shared/auth-provider-CTJkKfgy.cjs.map +1 -0
  29. package/dist/shared/contracts-BoT7msmq.d.cts +84 -0
  30. package/dist/shared/contracts-BoT7msmq.d.ts +84 -0
  31. package/dist/{multi-storage-Bf84xiO8.cjs → shared/cookies-DZwFq6kr.cjs} +98 -429
  32. package/dist/shared/cookies-DZwFq6kr.cjs.map +1 -0
  33. package/dist/{multi-storage-BUZaLAPO.js → shared/cookies-tX2sNwxb.js} +99 -352
  34. package/dist/shared/cookies-tX2sNwxb.js.map +1 -0
  35. package/dist/{storage-IHdXw52v.js → shared/errors-Bhrd2fJd.js} +2 -229
  36. package/dist/shared/errors-Bhrd2fJd.js.map +1 -0
  37. package/dist/{storage-DnzZPS_9.cjs → shared/errors-DfU8M5eS.cjs} +1 -288
  38. package/dist/shared/errors-DfU8M5eS.cjs.map +1 -0
  39. package/dist/shared/multi-storage--yTEqiod.cjs +150 -0
  40. package/dist/shared/multi-storage--yTEqiod.cjs.map +1 -0
  41. package/dist/shared/multi-storage-CjAPB5Kq.d.cts +72 -0
  42. package/dist/shared/multi-storage-CkvTUC5m.js +121 -0
  43. package/dist/shared/multi-storage-CkvTUC5m.js.map +1 -0
  44. package/dist/shared/multi-storage-DccjD7Ww.d.ts +72 -0
  45. package/dist/shared/options-Dg5N3r1V.cjs +189 -0
  46. package/dist/shared/options-Dg5N3r1V.cjs.map +1 -0
  47. package/dist/shared/options-DtATYdLr.js +142 -0
  48. package/dist/shared/options-DtATYdLr.js.map +1 -0
  49. package/dist/shared/render-DMp_3Nzk.d.cts +2250 -0
  50. package/dist/shared/render-DZrxhC5_.d.ts +2250 -0
  51. package/dist/shared/render-DyeHJNBw.cjs +4147 -0
  52. package/dist/shared/render-DyeHJNBw.cjs.map +1 -0
  53. package/dist/shared/render-vtLixIiU.js +3992 -0
  54. package/dist/shared/render-vtLixIiU.js.map +1 -0
  55. package/dist/shared/storage-BPJR_k4-.cjs +290 -0
  56. package/dist/shared/storage-BPJR_k4-.cjs.map +1 -0
  57. package/dist/{storage-BqMxs76Y.d.ts → shared/storage-C_eICCep.d.cts} +2 -2
  58. package/dist/{storage-BqMxs76Y.d.cts → shared/storage-C_eICCep.d.ts} +2 -2
  59. package/dist/shared/storage-D86edNCB.js +231 -0
  60. package/dist/shared/storage-D86edNCB.js.map +1 -0
  61. package/dist/shared/url-B6-bXHKt.d.cts +2083 -0
  62. package/dist/shared/url-B6-bXHKt.d.ts +2083 -0
  63. package/dist/shared/url-CYXgxqGx.js +3673 -0
  64. package/dist/shared/url-CYXgxqGx.js.map +1 -0
  65. package/dist/shared/url-yjl2c8Ie.cjs +4032 -0
  66. package/dist/shared/url-yjl2c8Ie.cjs.map +1 -0
  67. package/dist/shared/websocket-BMtihD56.d.ts +562 -0
  68. package/dist/shared/websocket-CbzB1Leq.js +1874 -0
  69. package/dist/shared/websocket-CbzB1Leq.js.map +1 -0
  70. package/dist/shared/websocket-D1p32SB2.d.cts +562 -0
  71. package/dist/shared/websocket-sYlynrr0.cjs +1945 -0
  72. package/dist/shared/websocket-sYlynrr0.cjs.map +1 -0
  73. package/dist/{web.cjs → web/index.cjs} +4 -3
  74. package/dist/web/index.cjs.map +1 -0
  75. package/dist/{web.d.cts → web/index.d.cts} +2 -2
  76. package/dist/{web.d.ts → web/index.d.ts} +2 -2
  77. package/dist/{web.js → web/index.js} +3 -2
  78. package/dist/web/index.js.map +1 -0
  79. package/package.json +40 -12
  80. package/dist/multi-storage-BUZaLAPO.js.map +0 -1
  81. package/dist/multi-storage-BUoEoW8f.d.cts +0 -154
  82. package/dist/multi-storage-Bf84xiO8.cjs.map +0 -1
  83. package/dist/multi-storage-CQSlI_kn.d.ts +0 -154
  84. package/dist/node.cjs.map +0 -1
  85. package/dist/node.js.map +0 -1
  86. package/dist/storage-DnzZPS_9.cjs.map +0 -1
  87. package/dist/storage-IHdXw52v.js.map +0 -1
  88. package/dist/web.cjs.map +0 -1
  89. package/dist/web.js.map +0 -1
@@ -1,4 +1,100 @@
1
- const require_storage = require("./storage-DnzZPS_9.cjs");
1
+ const require_errors = require("./errors-DfU8M5eS.cjs");
2
+ //#region src/core/url.ts
3
+ /**
4
+ * Собирает строку запроса.
5
+ *
6
+ * Правила:
7
+ * - `undefined` и `null` пропускаются целиком — не нужно чистить объект перед вызовом;
8
+ * - `boolean` превращается в `true` / `false`;
9
+ * - массив повторяет ключ (`ids=1&ids=2`).
10
+ *
11
+ * @returns строка вида `?a=1&b=2` либо пустая строка, если параметров нет
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * buildQuery({ tab: 'popular', limit: 20, cursor: undefined });
16
+ * // '?tab=popular&limit=20'
17
+ * ```
18
+ */
19
+ function buildQuery(params) {
20
+ if (!params) return "";
21
+ const search = new URLSearchParams();
22
+ for (const [key, value] of Object.entries(params)) {
23
+ if (value === void 0 || value === null) continue;
24
+ if (Array.isArray(value)) {
25
+ for (const item of value) {
26
+ if (item === void 0 || item === null) continue;
27
+ search.append(key, String(item));
28
+ }
29
+ continue;
30
+ }
31
+ search.append(key, String(value));
32
+ }
33
+ const query = search.toString();
34
+ return query ? `?${query}` : "";
35
+ }
36
+ /**
37
+ * Кодирует значение для подстановки в путь.
38
+ *
39
+ * Нужно для сегментов, которые могут содержать что угодно, — прежде всего хэштегов
40
+ * в `/api/hashtags/{tag}/posts`.
41
+ *
42
+ * @throws {ItdConfigError} если значение пустое
43
+ */
44
+ function encodePathSegment(value, name = "параметр пути") {
45
+ if (typeof value !== "string" || value.trim() === "") throw new require_errors.ItdConfigError(`${name} должен быть непустой строкой, получено: ${JSON.stringify(value)}`);
46
+ return encodeURIComponent(value);
47
+ }
48
+ /** Хост из URL в нижнем регистре. Пустая строка, если разобрать не удалось. */
49
+ function hostOf(url) {
50
+ try {
51
+ return new URL(url).hostname.toLowerCase();
52
+ } catch {
53
+ return "";
54
+ }
55
+ }
56
+ /** Origin из URL — схема, хост и порт. Пустая строка, если разобрать не удалось. */
57
+ function originOf(url) {
58
+ try {
59
+ return new URL(url).origin;
60
+ } catch {
61
+ return "";
62
+ }
63
+ }
64
+ /** Тот же хост либо его поддомен. Принимает хосты, а не адреса. */
65
+ function isSameSite(primaryHost, host) {
66
+ if (!primaryHost || !host) return false;
67
+ return host === primaryHost || host.endsWith(`.${primaryHost}`);
68
+ }
69
+ /**
70
+ * Склеивает базовый URL и путь.
71
+ *
72
+ * Завершающий слэш пути сохраняется: он значим для `/api/notifications/`
73
+ * и `/api/v1/subscription/`, без него сервер отвечает ошибкой.
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * joinUrl('https://xn--d1ah4a.com/', '/api/notifications/');
78
+ * // 'https://xn--d1ah4a.com/api/notifications/'
79
+ * ```
80
+ */
81
+ function joinUrl(baseUrl, path) {
82
+ return `${baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
83
+ }
84
+ /** Приводит базовый URL к каноничному виду и проверяет, что он вообще похож на URL. */
85
+ function normalizeBaseUrl(baseUrl) {
86
+ if (typeof baseUrl !== "string" || baseUrl.trim() === "") throw new require_errors.ItdConfigError(`baseUrl должен быть непустой строкой с абсолютным URL, получено: ${JSON.stringify(baseUrl)}`);
87
+ let parsed;
88
+ try {
89
+ parsed = new URL(baseUrl);
90
+ } catch {
91
+ throw new require_errors.ItdConfigError(`baseUrl должен быть абсолютным URL, получено: ${JSON.stringify(baseUrl)}`);
92
+ }
93
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new require_errors.ItdConfigError(`baseUrl должен использовать http или https, получено: ${parsed.protocol}`);
94
+ if (parsed.username || parsed.password || parsed.search || parsed.hash) throw new require_errors.ItdConfigError("baseUrl не должен содержать логин, пароль, query-параметры или fragment");
95
+ return parsed.origin + (parsed.pathname === "/" ? "" : parsed.pathname.replace(/\/$/, ""));
96
+ }
97
+ //#endregion
2
98
  //#region node_modules/set-cookie-parser/lib/set-cookie.js
3
99
  var defaultParseOptions = {
4
100
  decodeValues: true,
@@ -139,102 +235,6 @@ parseSetCookie.parse = parseSetCookie;
139
235
  parseSetCookie.parseString = parseString;
140
236
  parseSetCookie.splitCookiesString = splitCookiesString;
141
237
  //#endregion
142
- //#region src/core/url.ts
143
- /**
144
- * Собирает строку запроса.
145
- *
146
- * Правила:
147
- * - `undefined` и `null` пропускаются целиком — не нужно чистить объект перед вызовом;
148
- * - `boolean` превращается в `true` / `false`;
149
- * - массив повторяет ключ (`ids=1&ids=2`).
150
- *
151
- * @returns строка вида `?a=1&b=2` либо пустая строка, если параметров нет
152
- *
153
- * @example
154
- * ```ts
155
- * buildQuery({ tab: 'popular', limit: 20, cursor: undefined });
156
- * // '?tab=popular&limit=20'
157
- * ```
158
- */
159
- function buildQuery(params) {
160
- if (!params) return "";
161
- const search = new URLSearchParams();
162
- for (const [key, value] of Object.entries(params)) {
163
- if (value === void 0 || value === null) continue;
164
- if (Array.isArray(value)) {
165
- for (const item of value) {
166
- if (item === void 0 || item === null) continue;
167
- search.append(key, String(item));
168
- }
169
- continue;
170
- }
171
- search.append(key, String(value));
172
- }
173
- const query = search.toString();
174
- return query ? `?${query}` : "";
175
- }
176
- /**
177
- * Кодирует значение для подстановки в путь.
178
- *
179
- * Нужно для сегментов, которые могут содержать что угодно, — прежде всего хэштегов
180
- * в `/api/hashtags/{tag}/posts`.
181
- *
182
- * @throws {ItdConfigError} если значение пустое
183
- */
184
- function encodePathSegment(value, name = "параметр пути") {
185
- if (typeof value !== "string" || value.trim() === "") throw new require_storage.ItdConfigError(`${name} должен быть непустой строкой, получено: ${JSON.stringify(value)}`);
186
- return encodeURIComponent(value);
187
- }
188
- /** Хост из URL в нижнем регистре. Пустая строка, если разобрать не удалось. */
189
- function hostOf(url) {
190
- try {
191
- return new URL(url).hostname.toLowerCase();
192
- } catch {
193
- return "";
194
- }
195
- }
196
- /** Origin из URL — схема, хост и порт. Пустая строка, если разобрать не удалось. */
197
- function originOf(url) {
198
- try {
199
- return new URL(url).origin;
200
- } catch {
201
- return "";
202
- }
203
- }
204
- /** Тот же хост либо его поддомен. Принимает хосты, а не адреса. */
205
- function isSameSite(primaryHost, host) {
206
- if (!primaryHost || !host) return false;
207
- return host === primaryHost || host.endsWith(`.${primaryHost}`);
208
- }
209
- /**
210
- * Склеивает базовый URL и путь.
211
- *
212
- * Завершающий слэш пути сохраняется: он значим для `/api/notifications/`
213
- * и `/api/v1/subscription/`, без него сервер отвечает ошибкой.
214
- *
215
- * @example
216
- * ```ts
217
- * joinUrl('https://xn--d1ah4a.com/', '/api/notifications/');
218
- * // 'https://xn--d1ah4a.com/api/notifications/'
219
- * ```
220
- */
221
- function joinUrl(baseUrl, path) {
222
- return `${baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
223
- }
224
- /** Приводит базовый URL к каноничному виду и проверяет, что он вообще похож на URL. */
225
- function normalizeBaseUrl(baseUrl) {
226
- if (typeof baseUrl !== "string" || baseUrl.trim() === "") throw new require_storage.ItdConfigError(`baseUrl должен быть непустой строкой с абсолютным URL, получено: ${JSON.stringify(baseUrl)}`);
227
- let parsed;
228
- try {
229
- parsed = new URL(baseUrl);
230
- } catch {
231
- throw new require_storage.ItdConfigError(`baseUrl должен быть абсолютным URL, получено: ${JSON.stringify(baseUrl)}`);
232
- }
233
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new require_storage.ItdConfigError(`baseUrl должен использовать http или https, получено: ${parsed.protocol}`);
234
- if (parsed.username || parsed.password || parsed.search || parsed.hash) throw new require_storage.ItdConfigError("baseUrl не должен содержать логин, пароль, query-параметры или fragment");
235
- return parsed.origin + (parsed.pathname === "/" ? "" : parsed.pathname.replace(/\/$/, ""));
236
- }
237
- //#endregion
238
238
  //#region src/core/cookies.ts
239
239
  /** Имя cookie-флага «есть refresh-сессия». Ставится сайтом итд.com рядом с refresh-токеном. */
240
240
  const AUTH_FLAG_COOKIE = "is_auth";
@@ -421,259 +421,6 @@ var CookieJar = class {
421
421
  }
422
422
  };
423
423
  //#endregion
424
- //#region src/core/attachments/contracts.ts
425
- /** Способ передачи содержимого вложения. */
426
- const FileTransferMode = Object.freeze({
427
- /** Сначала получить файл целиком, затем отправить его как `Blob`. */
428
- Buffer: "buffer",
429
- /** Передавать данные по мере чтения, не собирая файл целиком в памяти. */
430
- Stream: "stream"
431
- });
432
- /** Предел размера файла, скачиваемого по адресу, — 100 МиБ. */
433
- const DEFAULT_URL_FILE_MAX_BYTES = 104857600;
434
- //#endregion
435
- //#region src/core/attachments/limits.ts
436
- /** Проверяет числовую границу до обращения к источнику. */
437
- function optionalBytes(value, name) {
438
- if (value === void 0) return void 0;
439
- if (!Number.isFinite(value) || value < 0 || !Number.isInteger(value)) throw new require_storage.ItdConfigError(`${name} должен быть неотрицательным целым числом, получено: ${value}`);
440
- return value;
441
- }
442
- /** Создаёт типизированную ошибку превышения размера вложения. */
443
- function fileTooLarge(url, limit, actual) {
444
- const source = url ? ` по адресу ${url}` : "";
445
- return new require_storage.ItdFileError(`файл${source} больше предела в ${limit} байт: ${actual}`, {
446
- reason: require_storage.ItdFileErrorReason.TooLarge,
447
- ...url ? { url } : {},
448
- limit,
449
- actual
450
- });
451
- }
452
- //#endregion
453
- //#region src/core/attachments/bounded-stream.ts
454
- /** Потоки, на которые уже наложены счётчик размера и backpressure. */
455
- const BOUNDED_FILE_STREAMS = /* @__PURE__ */ new WeakSet();
456
- /** Приводит произвольный чанк потока к байтам. */
457
- function asBytes(value) {
458
- return value instanceof Uint8Array ? value : new Uint8Array(value);
459
- }
460
- /** Проверяет значение без обращения к методам потенциально чужого объекта. */
461
- function isReadableByteStream(value) {
462
- return typeof ReadableStream !== "undefined" && value instanceof ReadableStream;
463
- }
464
- /**
465
- * Ограничивает размер и очередь потока. Отмена результирующего потока отменяет исходный.
466
- *
467
- * @internal
468
- */
469
- function boundedFileStream(source, options = {}) {
470
- const maxBytes = optionalBytes(options.maxBytes, "maxBytes");
471
- const streamBufferBytes = optionalBytes(options.streamBufferBytes ?? 4194304, "streamBufferBytes") ?? 4194304;
472
- if (streamBufferBytes === 0) throw new require_storage.ItdConfigError("streamBufferBytes должен быть больше нуля");
473
- const reader = source.getReader();
474
- let total = 0;
475
- let finished = false;
476
- let abortListener;
477
- const cleanup = () => {
478
- if (abortListener) options.signal?.removeEventListener("abort", abortListener);
479
- abortListener = void 0;
480
- };
481
- const stream = new ReadableStream({
482
- start(controller) {
483
- if (!options.signal) return;
484
- abortListener = () => {
485
- finished = true;
486
- reader.cancel(options.signal?.reason).catch(() => {});
487
- controller.error(options.signal?.reason ?? new DOMException("Aborted", "AbortError"));
488
- cleanup();
489
- };
490
- if (options.signal.aborted) abortListener();
491
- else options.signal.addEventListener("abort", abortListener, { once: true });
492
- },
493
- async pull(controller) {
494
- if (finished) return;
495
- try {
496
- const next = await reader.read();
497
- if (finished) return;
498
- if (next.done) {
499
- finished = true;
500
- cleanup();
501
- controller.close();
502
- return;
503
- }
504
- const chunk = asBytes(next.value);
505
- total += chunk.byteLength;
506
- if (maxBytes !== void 0 && total > maxBytes) {
507
- finished = true;
508
- await reader.cancel().catch(() => {});
509
- cleanup();
510
- controller.error(fileTooLarge(options.url, maxBytes, total));
511
- return;
512
- }
513
- controller.enqueue(chunk);
514
- } catch (error) {
515
- finished = true;
516
- cleanup();
517
- if (error instanceof require_storage.ItdFileError) {
518
- controller.error(error);
519
- return;
520
- }
521
- controller.error(new require_storage.ItdFileError("не удалось прочитать поток вложения", {
522
- reason: require_storage.ItdFileErrorReason.Read,
523
- ...options.url ? { url: options.url } : {},
524
- retryable: options.retryableRead ?? false,
525
- cause: error
526
- }));
527
- }
528
- },
529
- async cancel(reason) {
530
- finished = true;
531
- cleanup();
532
- await reader.cancel(reason).catch(() => {});
533
- }
534
- }, {
535
- highWaterMark: streamBufferBytes,
536
- size: (chunk) => chunk.byteLength
537
- });
538
- BOUNDED_FILE_STREAMS.add(stream);
539
- return stream;
540
- }
541
- /** Наложены ли на поток ограничения библиотеки. @internal */
542
- function isBoundedFileStream(stream) {
543
- return BOUNDED_FILE_STREAMS.has(stream);
544
- }
545
- //#endregion
546
- //#region src/core/attachments/options.ts
547
- /** Проверяет и дополняет настройки потока. @internal */
548
- function resolveFileStreamOptions(options, defaultMaxBytes) {
549
- const mode = options.mode ?? FileTransferMode.Buffer;
550
- if (mode !== FileTransferMode.Buffer && mode !== FileTransferMode.Stream) throw new require_storage.ItdConfigError(`mode вложения должен быть 'buffer' или 'stream', получено: ${mode}`);
551
- if (mode === FileTransferMode.Stream && typeof ReadableStream === "undefined") throw new require_storage.ItdConfigError("эта среда не поддерживает ReadableStream; используйте mode: 'buffer'");
552
- const maxBytes = optionalBytes(options.maxBytes ?? defaultMaxBytes, "maxBytes");
553
- const streamBufferBytes = optionalBytes(options.streamBufferBytes ?? 4194304, "streamBufferBytes") ?? 4194304;
554
- if (streamBufferBytes === 0) throw new require_storage.ItdConfigError("streamBufferBytes должен быть больше нуля");
555
- return {
556
- mode,
557
- maxBytes,
558
- streamBufferBytes
559
- };
560
- }
561
- //#endregion
562
- //#region src/core/multi-storage.ts
563
- /**
564
- * Срез мультихранилища как обычное {@link TokenStorage} — в таком виде его получает
565
- * отдельный `ItdClient`, который про соседние аккаунты ничего не знает.
566
- */
567
- function scopedTokenStorage(storage, account) {
568
- return {
569
- get: () => storage.get(account),
570
- set: (session) => storage.set(account, session),
571
- clear: () => storage.clear(account)
572
- };
573
- }
574
- /**
575
- * Можно ли восстановить авторизованный аккаунт из записи.
576
- *
577
- * Один `deviceId` не считается сессией: он намеренно переживает выход, но не позволяет
578
- * выполнить ни одного авторизованного запроса.
579
- *
580
- * @internal
581
- */
582
- function isRestorableSession(session) {
583
- if (!session) return false;
584
- if (session.accessToken || session.refreshToken) return true;
585
- const jar = new CookieJar();
586
- jar.deserialize(session.cookies);
587
- return jar.has(AUTH_FLAG_COOKIE);
588
- }
589
- /** @internal */
590
- function controlledTokenStorage(storage, account) {
591
- let revoked = false;
592
- const pending = /* @__PURE__ */ new Set();
593
- const mutate = (operation) => {
594
- if (revoked) return Promise.resolve();
595
- const promise = Promise.resolve().then(operation);
596
- pending.add(promise);
597
- promise.then(() => pending.delete(promise), () => pending.delete(promise));
598
- return promise;
599
- };
600
- return {
601
- storage: {
602
- get: () => revoked ? null : storage.get(account),
603
- set: (session) => mutate(() => storage.set(account, session)),
604
- clear: () => mutate(() => storage.clear(account))
605
- },
606
- revoke() {
607
- revoked = true;
608
- },
609
- async drain() {
610
- await Promise.all([...pending]);
611
- }
612
- };
613
- }
614
- /**
615
- * Мультихранилище в памяти процесса — вариант по умолчанию.
616
- *
617
- * Сессии теряются при перезапуске. Для долгоживущих ботов возьмите `FileMultiTokenStorage`
618
- * из `itd-api/node` либо соберите своё через {@link createMultiTokenStorage}.
619
- */
620
- var MemoryMultiTokenStorage = class {
621
- #store = new require_storage.MemoryKeyValueStore();
622
- constructor(initial) {
623
- for (const [account, session] of Object.entries(initial ?? {})) this.#store.set(accountKey(account), require_storage.copySession(session));
624
- }
625
- get(account) {
626
- const session = this.#store.get(accountKey(account));
627
- return session ? require_storage.copySession(session) : null;
628
- }
629
- set(account, session) {
630
- this.#store.set(accountKey(account), require_storage.copySession(session));
631
- }
632
- clear(account) {
633
- this.#store.delete(accountKey(account));
634
- }
635
- accounts() {
636
- return this.#store.keys(ACCOUNT_KEY_PREFIX).flatMap((key) => readAccountName(key.slice(ACCOUNT_KEY_PREFIX.length)) ?? []);
637
- }
638
- };
639
- const ACCOUNT_KEY_PREFIX = "accounts/";
640
- function accountKey(account) {
641
- return `${ACCOUNT_KEY_PREFIX}${encodeURIComponent(account)}`;
642
- }
643
- /** Читает имя аккаунта из ключа. `undefined` — ключ не декодируется и запись пропускается. */
644
- function readAccountName(encoded) {
645
- try {
646
- return decodeURIComponent(encoded);
647
- } catch {
648
- console.warn(`[itd-api] запись хранилища пропущена: ключ ${JSON.stringify(encoded)} не декодируется`);
649
- return;
650
- }
651
- }
652
- /**
653
- * Создаёт доменное хранилище нескольких сессий поверх enumerable key-value backend.
654
- *
655
- * Отдельный индекс аккаунтов не используется: `accounts()` перечисляет ключи backend. Поэтому
656
- * запись сессии не может разойтись с индексом, но backend обязан эффективно поддерживать `keys`.
657
- */
658
- function createMultiTokenStorage(store, options = {}) {
659
- const prefix = options.prefix ?? ACCOUNT_KEY_PREFIX;
660
- if (typeof prefix !== "string") throw new require_storage.ItdConfigError("prefix MultiTokenStorage должен быть строкой");
661
- const backend = require_storage.createKeyValueStore(store);
662
- if (!require_storage.isEnumerableKeyValueStore(backend)) throw new require_storage.ItdConfigError("MultiTokenStorage требует KeyValueStore с методом keys()");
663
- const key = (account) => `${prefix}${encodeURIComponent(account)}`;
664
- return {
665
- async get(account) {
666
- const session = await backend.get(key(account));
667
- return session ? require_storage.copySession(session) : null;
668
- },
669
- set: (account, session) => backend.set(key(account), require_storage.copySession(session)),
670
- clear: (account) => backend.delete(key(account)),
671
- async accounts() {
672
- return (await require_storage.collectKeyValueStoreKeys(backend, prefix)).flatMap((value) => readAccountName(value.slice(prefix.length)) ?? []);
673
- }
674
- };
675
- }
676
- //#endregion
677
424
  Object.defineProperty(exports, "AUTH_FLAG_COOKIE", {
678
425
  enumerable: true,
679
426
  get: function() {
@@ -686,24 +433,6 @@ Object.defineProperty(exports, "CookieJar", {
686
433
  return CookieJar;
687
434
  }
688
435
  });
689
- Object.defineProperty(exports, "DEFAULT_URL_FILE_MAX_BYTES", {
690
- enumerable: true,
691
- get: function() {
692
- return DEFAULT_URL_FILE_MAX_BYTES;
693
- }
694
- });
695
- Object.defineProperty(exports, "FileTransferMode", {
696
- enumerable: true,
697
- get: function() {
698
- return FileTransferMode;
699
- }
700
- });
701
- Object.defineProperty(exports, "MemoryMultiTokenStorage", {
702
- enumerable: true,
703
- get: function() {
704
- return MemoryMultiTokenStorage;
705
- }
706
- });
707
436
  Object.defineProperty(exports, "REFRESH_COOKIE", {
708
437
  enumerable: true,
709
438
  get: function() {
@@ -716,66 +445,24 @@ Object.defineProperty(exports, "REFRESH_COOKIE_PATH", {
716
445
  return REFRESH_COOKIE_PATH;
717
446
  }
718
447
  });
719
- Object.defineProperty(exports, "boundedFileStream", {
720
- enumerable: true,
721
- get: function() {
722
- return boundedFileStream;
723
- }
724
- });
725
448
  Object.defineProperty(exports, "buildQuery", {
726
449
  enumerable: true,
727
450
  get: function() {
728
451
  return buildQuery;
729
452
  }
730
453
  });
731
- Object.defineProperty(exports, "controlledTokenStorage", {
732
- enumerable: true,
733
- get: function() {
734
- return controlledTokenStorage;
735
- }
736
- });
737
- Object.defineProperty(exports, "createMultiTokenStorage", {
738
- enumerable: true,
739
- get: function() {
740
- return createMultiTokenStorage;
741
- }
742
- });
743
454
  Object.defineProperty(exports, "encodePathSegment", {
744
455
  enumerable: true,
745
456
  get: function() {
746
457
  return encodePathSegment;
747
458
  }
748
459
  });
749
- Object.defineProperty(exports, "fileTooLarge", {
750
- enumerable: true,
751
- get: function() {
752
- return fileTooLarge;
753
- }
754
- });
755
460
  Object.defineProperty(exports, "hostOf", {
756
461
  enumerable: true,
757
462
  get: function() {
758
463
  return hostOf;
759
464
  }
760
465
  });
761
- Object.defineProperty(exports, "isBoundedFileStream", {
762
- enumerable: true,
763
- get: function() {
764
- return isBoundedFileStream;
765
- }
766
- });
767
- Object.defineProperty(exports, "isReadableByteStream", {
768
- enumerable: true,
769
- get: function() {
770
- return isReadableByteStream;
771
- }
772
- });
773
- Object.defineProperty(exports, "isRestorableSession", {
774
- enumerable: true,
775
- get: function() {
776
- return isRestorableSession;
777
- }
778
- });
779
466
  Object.defineProperty(exports, "isSameSite", {
780
467
  enumerable: true,
781
468
  get: function() {
@@ -794,29 +481,11 @@ Object.defineProperty(exports, "normalizeBaseUrl", {
794
481
  return normalizeBaseUrl;
795
482
  }
796
483
  });
797
- Object.defineProperty(exports, "optionalBytes", {
798
- enumerable: true,
799
- get: function() {
800
- return optionalBytes;
801
- }
802
- });
803
484
  Object.defineProperty(exports, "originOf", {
804
485
  enumerable: true,
805
486
  get: function() {
806
487
  return originOf;
807
488
  }
808
489
  });
809
- Object.defineProperty(exports, "resolveFileStreamOptions", {
810
- enumerable: true,
811
- get: function() {
812
- return resolveFileStreamOptions;
813
- }
814
- });
815
- Object.defineProperty(exports, "scopedTokenStorage", {
816
- enumerable: true,
817
- get: function() {
818
- return scopedTokenStorage;
819
- }
820
- });
821
490
 
822
- //# sourceMappingURL=multi-storage-Bf84xiO8.cjs.map
491
+ //# sourceMappingURL=cookies-DZwFq6kr.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies-DZwFq6kr.cjs","names":["ItdConfigError","#byOrigin","#matching"],"sources":["../../src/core/url.ts","../../node_modules/set-cookie-parser/lib/set-cookie.js","../../src/core/cookies.ts"],"sourcesContent":["import { ItdConfigError } from './errors.js';\n\n/** Значение параметра запроса. `undefined` и `null` в строку не попадают. */\nexport type QueryValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | readonly (string | number | boolean)[];\n\n/** Параметры строки запроса. */\nexport type QueryParams = Record<string, QueryValue>;\n\n/**\n * Собирает строку запроса.\n *\n * Правила:\n * - `undefined` и `null` пропускаются целиком — не нужно чистить объект перед вызовом;\n * - `boolean` превращается в `true` / `false`;\n * - массив повторяет ключ (`ids=1&ids=2`).\n *\n * @returns строка вида `?a=1&b=2` либо пустая строка, если параметров нет\n *\n * @example\n * ```ts\n * buildQuery({ tab: 'popular', limit: 20, cursor: undefined });\n * // '?tab=popular&limit=20'\n * ```\n */\nexport function buildQuery(params?: QueryParams): string {\n if (!params) return '';\n\n const search = new URLSearchParams();\n\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null) continue;\n\n if (Array.isArray(value)) {\n for (const item of value) {\n if (item === undefined || item === null) continue;\n search.append(key, String(item));\n }\n continue;\n }\n\n search.append(key, String(value));\n }\n\n const query = search.toString();\n return query ? `?${query}` : '';\n}\n\n/**\n * Кодирует значение для подстановки в путь.\n *\n * Нужно для сегментов, которые могут содержать что угодно, — прежде всего хэштегов\n * в `/api/hashtags/{tag}/posts`.\n *\n * @throws {ItdConfigError} если значение пустое\n */\nexport function encodePathSegment(value: string, name = 'параметр пути'): string {\n if (typeof value !== 'string' || value.trim() === '') {\n throw new ItdConfigError(\n `${name} должен быть непустой строкой, получено: ${JSON.stringify(value)}`,\n );\n }\n return encodeURIComponent(value);\n}\n\n/** Хост из URL в нижнем регистре. Пустая строка, если разобрать не удалось. */\nexport function hostOf(url: string): string {\n try {\n return new URL(url).hostname.toLowerCase();\n } catch {\n return '';\n }\n}\n\n/** Origin из URL — схема, хост и порт. Пустая строка, если разобрать не удалось. */\nexport function originOf(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return '';\n }\n}\n\n/** Тот же хост либо его поддомен. Принимает хосты, а не адреса. */\nexport function isSameSite(primaryHost: string, host: string): boolean {\n if (!primaryHost || !host) return false;\n return host === primaryHost || host.endsWith(`.${primaryHost}`);\n}\n\n/**\n * Склеивает базовый URL и путь.\n *\n * Завершающий слэш пути сохраняется: он значим для `/api/notifications/`\n * и `/api/v1/subscription/`, без него сервер отвечает ошибкой.\n *\n * @example\n * ```ts\n * joinUrl('https://xn--d1ah4a.com/', '/api/notifications/');\n * // 'https://xn--d1ah4a.com/api/notifications/'\n * ```\n */\nexport function joinUrl(baseUrl: string, path: string): string {\n const base = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n const suffix = path.startsWith('/') ? path : `/${path}`;\n return `${base}${suffix}`;\n}\n\n/** Приводит базовый URL к каноничному виду и проверяет, что он вообще похож на URL. */\nexport function normalizeBaseUrl(baseUrl: string): string {\n if (typeof baseUrl !== 'string' || baseUrl.trim() === '') {\n throw new ItdConfigError(\n `baseUrl должен быть непустой строкой с абсолютным URL, получено: ${JSON.stringify(baseUrl)}`,\n );\n }\n\n let parsed: URL;\n try {\n parsed = new URL(baseUrl);\n } catch {\n throw new ItdConfigError(\n `baseUrl должен быть абсолютным URL, получено: ${JSON.stringify(baseUrl)}`,\n );\n }\n\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new ItdConfigError(\n `baseUrl должен использовать http или https, получено: ${parsed.protocol}`,\n );\n }\n\n if (parsed.username || parsed.password || parsed.search || parsed.hash) {\n throw new ItdConfigError(\n 'baseUrl не должен содержать логин, пароль, query-параметры или fragment',\n );\n }\n\n return parsed.origin + (parsed.pathname === '/' ? '' : parsed.pathname.replace(/\\/$/, ''));\n}\n","var defaultParseOptions = {\n decodeValues: true,\n map: false,\n silent: false,\n split: \"auto\", // auto = split strings but not arrays\n};\n\nfunction isForbiddenKey(key) {\n return typeof key !== \"string\" || key in {};\n}\n\nfunction createNullObj() {\n return Object.create(null);\n}\n\nfunction isNonEmptyString(str) {\n return typeof str === \"string\" && !!str.trim();\n}\n\nfunction parseString(setCookieValue, options) {\n var parts = setCookieValue.split(\";\").filter(isNonEmptyString);\n\n var nameValuePairStr = parts.shift();\n if (!nameValuePairStr) {\n return null;\n }\n var parsed = parseNameValuePair(nameValuePairStr);\n var name = parsed.name;\n var value = parsed.value;\n\n options = options\n ? Object.assign({}, defaultParseOptions, options)\n : defaultParseOptions;\n\n if (isForbiddenKey(name)) {\n return null;\n }\n\n try {\n value = options.decodeValues ? decodeURIComponent(value) : value; // decode cookie value\n } catch (e) {\n console.error(\n \"set-cookie-parser: failed to decode cookie value. Set options.decodeValues=false to disable decoding.\",\n e\n );\n }\n\n var cookie = createNullObj();\n cookie.name = name;\n cookie.value = value;\n\n parts.forEach(function (part) {\n var sides = part.split(\"=\");\n var key = sides.shift().trim().toLowerCase();\n if (isForbiddenKey(key)) {\n return;\n }\n var value = sides.join(\"=\").trim();\n if (key === \"expires\") {\n cookie.expires = new Date(value);\n } else if (key === \"max-age\") {\n var n = parseInt(value, 10);\n if (!Number.isNaN(n)) cookie.maxAge = n;\n } else if (key === \"secure\") {\n cookie.secure = true;\n } else if (key === \"httponly\") {\n cookie.httpOnly = true;\n } else if (key === \"samesite\") {\n cookie.sameSite = value;\n } else if (key === \"partitioned\") {\n cookie.partitioned = true;\n } else if (key) {\n cookie[key] = value;\n }\n });\n\n return cookie;\n}\n\nfunction parseNameValuePair(nameValuePairStr) {\n // Parses name-value-pair according to rfc6265bis draft\n\n var name = \"\";\n var value = \"\";\n var nameValueArr = nameValuePairStr.split(\"=\");\n if (nameValueArr.length > 1) {\n name = nameValueArr.shift();\n value = nameValueArr.join(\"=\"); // everything after the first =, joined by a \"=\" if there was more than one part\n } else {\n value = nameValuePairStr;\n }\n\n return { name: name, value: value };\n}\n\nfunction parseSetCookie(input, options) {\n options = options\n ? Object.assign({}, defaultParseOptions, options)\n : defaultParseOptions;\n\n if (!input) {\n if (!options.map) {\n return [];\n } else {\n return createNullObj();\n }\n }\n\n if (input.headers) {\n if (typeof input.headers.getSetCookie === \"function\") {\n // for fetch responses - they combine headers of the same type in the headers array,\n // but getSetCookie returns an uncombined array\n input = input.headers.getSetCookie();\n } else if (input.headers[\"set-cookie\"]) {\n // fast-path for node.js (which automatically normalizes header names to lower-case)\n input = input.headers[\"set-cookie\"];\n } else {\n // slow-path for other environments - see #25\n var sch =\n input.headers[\n Object.keys(input.headers).find(function (key) {\n return key.toLowerCase() === \"set-cookie\";\n })\n ];\n // warn if called on a request-like object with a cookie header rather than a set-cookie header - see #34, 36\n if (!sch && input.headers.cookie && !options.silent) {\n console.warn(\n \"Warning: set-cookie-parser appears to have been called on a request object. It is designed to parse Set-Cookie headers from responses, not Cookie headers from requests. Set the option {silent: true} to suppress this warning.\"\n );\n }\n input = sch;\n }\n }\n\n var split = options.split;\n var isArray = Array.isArray(input);\n\n if (split === \"auto\") {\n split = !isArray;\n }\n\n if (!isArray) {\n input = [input];\n }\n\n input = input.filter(isNonEmptyString);\n\n if (split) {\n input = input.map(splitCookiesString).flat();\n }\n\n if (!options.map) {\n return input\n .map(function (str) {\n return parseString(str, options);\n })\n .filter(Boolean);\n } else {\n var cookies = createNullObj();\n return input.reduce(function (cookies, str) {\n var cookie = parseString(str, options);\n if (cookie && !isForbiddenKey(cookie.name)) {\n cookies[cookie.name] = cookie;\n }\n return cookies;\n }, cookies);\n }\n}\n\n/*\n Set-Cookie header field-values are sometimes comma joined in one string. This splits them without choking on commas\n that are within a single set-cookie field-value, such as in the Expires portion.\n\n This is uncommon, but explicitly allowed - see https://tools.ietf.org/html/rfc2616#section-4.2\n Node.js does this for every header *except* set-cookie - see https://github.com/nodejs/node/blob/d5e363b77ebaf1caf67cd7528224b651c86815c1/lib/_http_incoming.js#L128\n React Native's fetch does this for *every* header, including set-cookie.\n\n Based on: https://github.com/google/j2objc/commit/16820fdbc8f76ca0c33472810ce0cb03d20efe25\n Credits to: https://github.com/tomball for original and https://github.com/chrusart for JavaScript implementation\n*/\nfunction splitCookiesString(cookiesString) {\n if (Array.isArray(cookiesString)) {\n return cookiesString;\n }\n if (typeof cookiesString !== \"string\") {\n return [];\n }\n\n var cookiesStrings = [];\n var pos = 0;\n var start;\n var ch;\n var lastComma;\n var nextStart;\n var cookiesSeparatorFound;\n\n function skipWhitespace() {\n while (pos < cookiesString.length && /\\s/.test(cookiesString.charAt(pos))) {\n pos += 1;\n }\n return pos < cookiesString.length;\n }\n\n function notSpecialChar() {\n ch = cookiesString.charAt(pos);\n\n return ch !== \"=\" && ch !== \";\" && ch !== \",\";\n }\n\n while (pos < cookiesString.length) {\n start = pos;\n cookiesSeparatorFound = false;\n\n while (skipWhitespace()) {\n ch = cookiesString.charAt(pos);\n if (ch === \",\") {\n // ',' is a cookie separator if we have later first '=', not ';' or ','\n lastComma = pos;\n pos += 1;\n\n skipWhitespace();\n nextStart = pos;\n\n while (pos < cookiesString.length && notSpecialChar()) {\n pos += 1;\n }\n\n // currently special character\n if (pos < cookiesString.length && cookiesString.charAt(pos) === \"=\") {\n // we found cookies separator\n cookiesSeparatorFound = true;\n // pos is inside the next cookie, so back up and return it.\n pos = nextStart;\n cookiesStrings.push(cookiesString.substring(start, lastComma));\n start = pos;\n } else {\n // in param ',' or param separator ';',\n // we continue from that comma\n pos = lastComma + 1;\n }\n } else {\n pos += 1;\n }\n }\n\n if (!cookiesSeparatorFound || pos >= cookiesString.length) {\n cookiesStrings.push(cookiesString.substring(start, cookiesString.length));\n }\n }\n\n return cookiesStrings;\n}\n\n// named export for CJS\nparseSetCookie.parseSetCookie = parseSetCookie;\n// for backwards compatibility\nparseSetCookie.parse = parseSetCookie;\nparseSetCookie.parseString = parseString;\nparseSetCookie.splitCookiesString = splitCookiesString;\n\n// EXPORTS\n// (this section is replaced by build-cjs.js)\n\n// named export for ESM\nexport { parseSetCookie };\n// for backwards compatibility\nexport default parseSetCookie;\nexport { parseSetCookie as parse, parseString, splitCookiesString };\n","import { parse as parseSetCookie, splitCookiesString } from 'set-cookie-parser';\nimport { originOf } from './url.js';\n\n/** Cookie, сохранённая в jar. */\ninterface StoredCookie {\n name: string;\n value: string;\n path: string;\n /** Момент истечения, мс с начала эпохи. `undefined` — сессионная cookie. */\n expires: number | undefined;\n secure: boolean;\n}\n\n/** Имя cookie-флага «есть refresh-сессия». Ставится сайтом итд.com рядом с refresh-токеном. */\nexport const AUTH_FLAG_COOKIE = 'is_auth';\n\n/**\n * Имя cookie с refresh-токеном.\n *\n * `POST /api/v1/auth/refresh` читает токен **только отсюда** — тело запроса сервер игнорирует.\n * Cookie помечена `HttpOnly`, поэтому в браузере её не прочитать и не выставить; вне браузера\n * она проходит через jar, и туда же кладётся токен, переданный строкой.\n */\nexport const REFRESH_COOKIE = 'refresh_token';\n\n/** Путь, которым сервер ограничивает {@link REFRESH_COOKIE}. */\nexport const REFRESH_COOKIE_PATH = '/api/v1/auth';\n\n/** Разделитель origin и содержимого cookie при сериализации. В origin пробелов не бывает. */\nconst SERIALIZED_SEPARATOR = ' ';\n\n/** Дата в миллисекунды. Некорректная дата считается отсутствующей, а не `NaN`. */\nfunction toTimestamp(date: Date | undefined): number | undefined {\n if (!date) return undefined;\n const time = date.getTime();\n return Number.isFinite(time) ? time : undefined;\n}\n\nfunction pathOf(url: string): string {\n try {\n return new URL(url).pathname || '/';\n } catch {\n return '/';\n }\n}\n\n/**\n * Подходит ли путь cookie запросу.\n *\n * Правило из RFC 6265: путь cookie должен быть префиксом пути запроса, причём граница\n * должна проходить по сегменту — `/api` не подходит запросу `/apiary`.\n */\nfunction pathMatches(cookiePath: string, requestPath: string): boolean {\n if (cookiePath === '/' || cookiePath === requestPath) return true;\n if (!requestPath.startsWith(cookiePath)) return false;\n return cookiePath.endsWith('/') || requestPath[cookiePath.length] === '/';\n}\n\n/**\n * Минимальное хранилище cookie для сред без своего.\n *\n * Refresh-токен итд.com приходит в `Set-Cookie`, а `fetch` вне браузера cookie не хранит —\n * без jar сессию не продлить. В браузере и React Native не используется: там cookie ведёт\n * сама среда.\n *\n * Реализована практическая часть RFC 6265: origin, путь, срок жизни, флаг `Secure`.\n * Доменные cookie для поддоменов намеренно не поддерживаются — API работает с одного хоста.\n */\nexport class CookieJar {\n readonly #byOrigin = new Map<string, Map<string, StoredCookie>>();\n\n /**\n * Забирает `Set-Cookie` из ответа.\n *\n * Использует `Headers.getSetCookie()`, где он есть (Node 20+, undici). В остальных средах\n * заголовки склеены в одну строку, и её нельзя резать по запятой напрямую: запятая есть\n * внутри `Expires=Wed, 09 Jun 2027 …`. Разделением занимается `set-cookie-parser`.\n */\n setFromResponse(url: string, response: Response): void {\n const headers = response.headers as Headers & { getSetCookie?: () => string[] };\n\n const raw =\n typeof headers.getSetCookie === 'function'\n ? headers.getSetCookie()\n : splitCookiesString(headers.get('set-cookie') ?? '');\n\n if (raw.length > 0) this.setFromStrings(url, raw);\n }\n\n /**\n * Кладёт cookie напрямую, минуя `Set-Cookie`.\n *\n * Нужно ровно в одном случае: пользователь передал refresh-токен строкой, а сервер читает\n * его только из cookie. Значение не кодируется — оно уходит в заголовок как есть.\n */\n set(url: string, name: string, value: string, path = '/'): void {\n this.setFromStrings(url, [`${name}=${value}; Path=${path}`]);\n }\n\n /** Сохраняет cookie из готовых строк `Set-Cookie`. */\n setFromStrings(url: string, setCookieStrings: string[]): void {\n const origin = originOf(url);\n if (!origin) return;\n\n const jar = this.#byOrigin.get(origin) ?? new Map<string, StoredCookie>();\n\n for (const parsed of parseSetCookie(setCookieStrings)) {\n // Битую дату (её легко получить, если заголовки склеены неправильно) считаем\n // отсутствующей: сессионная cookie лучше, чем cookie со сроком NaN, которая\n // не истекает и одновременно нигде не находится.\n const expires = toTimestamp(parsed.expires);\n const maxAgeExpires =\n typeof parsed.maxAge === 'number' && Number.isFinite(parsed.maxAge)\n ? Date.now() + parsed.maxAge * 1000\n : undefined;\n\n // Max-Age приоритетнее Expires — так требует RFC 6265.\n const expiresAt = maxAgeExpires ?? expires;\n\n // Удаление cookie сервером: срок в прошлом либо Max-Age=0.\n if (expiresAt !== undefined && expiresAt <= Date.now()) {\n jar.delete(parsed.name);\n continue;\n }\n\n jar.set(parsed.name, {\n name: parsed.name,\n value: parsed.value,\n path: parsed.path ?? '/',\n expires: expiresAt,\n secure: parsed.secure ?? false,\n });\n }\n\n this.#byOrigin.set(origin, jar);\n }\n\n /**\n * Собирает значение заголовка `Cookie` для запроса.\n *\n * @returns строка вида `a=1; b=2` либо `undefined`, если подходящих cookie нет\n */\n getHeader(url: string): string | undefined {\n const cookies = this.#matching(url);\n if (cookies.length === 0) return undefined;\n return cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join('; ');\n }\n\n /**\n * Значение действующей cookie.\n *\n * Нужно, чтобы забрать обновлённый refresh-токен: сервер ротирует его при каждом\n * продлении сессии, и сохранять надо именно новое значение.\n *\n * @param url если указан, учитываются origin, путь и флаг `Secure`\n */\n getValue(name: string, url?: string): string | undefined {\n if (url) return this.#matching(url).find((cookie) => cookie.name === name)?.value;\n\n const now = Date.now();\n for (const jar of this.#byOrigin.values()) {\n const cookie = jar.get(name);\n if (cookie && (cookie.expires === undefined || cookie.expires > now)) return cookie.value;\n }\n return undefined;\n }\n\n /**\n * Есть ли действующая cookie с таким именем.\n *\n * Используется для проверки флага {@link AUTH_FLAG_COOKIE} перед запросом обновления\n * токена: у анонима refresh-сессии нет, и дёргать API незачем.\n */\n has(name: string, url?: string): boolean {\n if (url) return this.#matching(url).some((cookie) => cookie.name === name);\n\n const now = Date.now();\n for (const jar of this.#byOrigin.values()) {\n const cookie = jar.get(name);\n if (cookie && (cookie.expires === undefined || cookie.expires > now)) return true;\n }\n return false;\n }\n\n /** Сохраняет содержимое jar для записи в {@link TokenStorage}. */\n serialize(): string[] {\n const result: string[] = [];\n const now = Date.now();\n\n for (const [origin, jar] of this.#byOrigin) {\n for (const cookie of jar.values()) {\n if (cookie.expires !== undefined && cookie.expires <= now) continue;\n\n const parts = [`${cookie.name}=${cookie.value}`, `Path=${cookie.path}`];\n if (cookie.expires !== undefined) {\n parts.push(`Expires=${new Date(cookie.expires).toUTCString()}`);\n }\n if (cookie.secure) parts.push('Secure');\n\n result.push(`${origin}${SERIALIZED_SEPARATOR}${parts.join('; ')}`);\n }\n }\n\n return result;\n }\n\n /** Восстанавливает jar из результата {@link serialize}. Некорректные записи молча пропускаются. */\n deserialize(entries: readonly string[] | undefined): void {\n if (!entries) return;\n\n for (const entry of entries) {\n const separatorAt = entry.indexOf(SERIALIZED_SEPARATOR);\n if (separatorAt <= 0) continue;\n\n const origin = entry.slice(0, separatorAt);\n const setCookie = entry.slice(separatorAt + 1);\n if (!originOf(origin)) continue;\n\n this.setFromStrings(origin, [setCookie]);\n }\n }\n\n /** Удаляет все cookie. */\n clear(): void {\n this.#byOrigin.clear();\n }\n\n /** Действующие cookie, подходящие запросу: тот же origin, подходящий путь, не истёкшие. */\n #matching(url: string): StoredCookie[] {\n const origin = originOf(url);\n const jar = this.#byOrigin.get(origin);\n if (!jar) return [];\n\n const isSecureRequest = origin.startsWith('https:');\n const requestPath = pathOf(url);\n const now = Date.now();\n const result: StoredCookie[] = [];\n\n for (const cookie of jar.values()) {\n if (cookie.expires !== undefined && cookie.expires <= now) {\n jar.delete(cookie.name);\n continue;\n }\n if (cookie.secure && !isSecureRequest) continue;\n if (!pathMatches(cookie.path, requestPath)) continue;\n\n result.push(cookie);\n }\n\n return result;\n }\n}\n"],"x_google_ignoreList":[1],"mappings":"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"}