itd-api 0.10.3 → 0.11.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.
Files changed (79) hide show
  1. package/README.md +2 -1
  2. package/dist/events/index.cjs +27 -25
  3. package/dist/events/index.d.cts +3 -3
  4. package/dist/events/index.d.ts +3 -3
  5. package/dist/events/index.js +5 -5
  6. package/dist/index.cjs +136 -116
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +18 -7
  9. package/dist/index.d.ts +18 -7
  10. package/dist/index.js +46 -30
  11. package/dist/index.js.map +1 -1
  12. package/dist/node/index.cjs +13 -13
  13. package/dist/node/index.cjs.map +1 -1
  14. package/dist/node/index.js +5 -5
  15. package/dist/rest/index.cjs +43 -40
  16. package/dist/rest/index.cjs.map +1 -1
  17. package/dist/rest/index.d.cts +3 -3
  18. package/dist/rest/index.d.ts +3 -3
  19. package/dist/rest/index.js +6 -6
  20. package/dist/shared/{events-BVfqWyBw.cjs → events-C6tmj8aW.cjs} +63 -59
  21. package/dist/shared/events-C6tmj8aW.cjs.map +1 -0
  22. package/dist/shared/{events-Cp7ITueA.d.ts → events-CAb9G7Sf.d.ts} +2 -2
  23. package/dist/shared/{events-Cwj7UPAa.js → events-MWNcnfrq.js} +19 -15
  24. package/dist/shared/events-MWNcnfrq.js.map +1 -0
  25. package/dist/shared/{events-D0Wtt-If.d.cts → events-Sc75x6rT.d.cts} +2 -2
  26. package/dist/shared/{key-value-store-B5GYVEYZ.cjs → key-value-store-DYSPsqwq.cjs} +11 -14
  27. package/dist/shared/key-value-store-DYSPsqwq.cjs.map +1 -0
  28. package/dist/shared/{key-value-store-Bt2PSMxY.js → key-value-store-w1Dn5Pjb.js} +5 -8
  29. package/dist/shared/key-value-store-w1Dn5Pjb.js.map +1 -0
  30. package/dist/shared/{multi-storage-C3Ngtop1.js → multi-storage-BLBrGN9Q.js} +6 -6
  31. package/dist/shared/multi-storage-BLBrGN9Q.js.map +1 -0
  32. package/dist/shared/{multi-storage-s-fPtjoy.cjs → multi-storage-C7-Gz6Z7.cjs} +8 -8
  33. package/dist/shared/multi-storage-C7-Gz6Z7.cjs.map +1 -0
  34. package/dist/shared/{options-DjEtbVaf.cjs → options-5tywTYId.cjs} +13 -13
  35. package/dist/shared/{options-DjEtbVaf.cjs.map → options-5tywTYId.cjs.map} +1 -1
  36. package/dist/shared/{options-i4LrKfqx.js → options-BerKicCs.js} +3 -3
  37. package/dist/shared/{options-i4LrKfqx.js.map → options-BerKicCs.js.map} +1 -1
  38. package/dist/shared/{redact-DJV0XM3n.js → redact-C5oxIvcc.js} +79 -18
  39. package/dist/shared/redact-C5oxIvcc.js.map +1 -0
  40. package/dist/shared/{redact-lAmck1Z6.cjs → redact-DoC7lSsp.cjs} +109 -24
  41. package/dist/shared/redact-DoC7lSsp.cjs.map +1 -0
  42. package/dist/shared/{render-DRmuHoEy.d.ts → render-CBroJPcV.d.ts} +178 -252
  43. package/dist/shared/{render-CJO_L2CD.d.cts → render-DM7AhTZW.d.cts} +178 -252
  44. package/dist/shared/{render-BT74nC1l.js → render-DRh5_aRe.js} +128 -135
  45. package/dist/shared/render-DRh5_aRe.js.map +1 -0
  46. package/dist/shared/{render-CYJSDypQ.cjs → render-Dnc84B4M.cjs} +234 -241
  47. package/dist/shared/render-Dnc84B4M.cjs.map +1 -0
  48. package/dist/shared/{storage-DiDe0PZM.js → storage-86wiHeXD.js} +3 -3
  49. package/dist/shared/{storage-DiDe0PZM.js.map → storage-86wiHeXD.js.map} +1 -1
  50. package/dist/shared/{storage-CZJZoaz9.cjs → storage-CIjejZDt.cjs} +4 -4
  51. package/dist/shared/{storage-CZJZoaz9.cjs.map → storage-CIjejZDt.cjs.map} +1 -1
  52. package/dist/shared/{url-BLnX-LYt.d.cts → url-BOi-_mJT.d.cts} +143 -79
  53. package/dist/shared/{url-BLnX-LYt.d.ts → url-BOi-_mJT.d.ts} +143 -79
  54. package/dist/shared/{url-DS0-lrl-.js → url-Bg61sk_Y.js} +741 -598
  55. package/dist/shared/url-Bg61sk_Y.js.map +1 -0
  56. package/dist/shared/{url-BnO1aWsd.cjs → url-DTgkCWv5.cjs} +848 -717
  57. package/dist/shared/url-DTgkCWv5.cjs.map +1 -0
  58. package/dist/shared/{errors-BmP3TKoW.cjs → validate-CFx2dISs.cjs} +84 -5
  59. package/dist/shared/validate-CFx2dISs.cjs.map +1 -0
  60. package/dist/shared/{errors-GI10kZxk.js → validate-D7_kq3g5.js} +55 -6
  61. package/dist/shared/validate-D7_kq3g5.js.map +1 -0
  62. package/dist/web/index.cjs +4 -4
  63. package/dist/web/index.cjs.map +1 -1
  64. package/dist/web/index.js +3 -3
  65. package/package.json +1 -1
  66. package/dist/shared/errors-BmP3TKoW.cjs.map +0 -1
  67. package/dist/shared/errors-GI10kZxk.js.map +0 -1
  68. package/dist/shared/events-BVfqWyBw.cjs.map +0 -1
  69. package/dist/shared/events-Cwj7UPAa.js.map +0 -1
  70. package/dist/shared/key-value-store-B5GYVEYZ.cjs.map +0 -1
  71. package/dist/shared/key-value-store-Bt2PSMxY.js.map +0 -1
  72. package/dist/shared/multi-storage-C3Ngtop1.js.map +0 -1
  73. package/dist/shared/multi-storage-s-fPtjoy.cjs.map +0 -1
  74. package/dist/shared/redact-DJV0XM3n.js.map +0 -1
  75. package/dist/shared/redact-lAmck1Z6.cjs.map +0 -1
  76. package/dist/shared/render-BT74nC1l.js.map +0 -1
  77. package/dist/shared/render-CYJSDypQ.cjs.map +0 -1
  78. package/dist/shared/url-BnO1aWsd.cjs.map +0 -1
  79. package/dist/shared/url-DS0-lrl-.js.map +0 -1
@@ -155,7 +155,12 @@ var ItdApiError = class extends ItdError {
155
155
  hasCode(...codes) {
156
156
  return codes.includes(this.code);
157
157
  }
158
- /** Имеет ли смысл повторить запрос: `429` и серверные ошибки `5xx`. */
158
+ /**
159
+ * Временная ошибка сервера, которую политика повторов считает повторяемой: `429` или `5xx`.
160
+ *
161
+ * Повторит ли библиотека саму операцию, зависит ещё от её `retrySafety`: `5xx` на
162
+ * небезопасной операции не повторяется, `429` — повторяется всегда.
163
+ */
159
164
  get isRetryable() {
160
165
  return this.status === 429 || this.status >= 500;
161
166
  }
@@ -278,20 +283,36 @@ var ItdNetworkError = class extends ItdError {
278
283
  this.path = init.path;
279
284
  }
280
285
  };
281
- /** Истёк таймаут запроса, заданный опцией `timeout`. */
286
+ /** Какой бюджет времени исчерпан. */
287
+ const TimeoutBudget = Object.freeze({
288
+ /** Срок одной транспортной попытки — опция `timeout`. */
289
+ Attempt: "attempt",
290
+ /** Общий срок логической операции — опция `deadline`. */
291
+ Deadline: "deadline"
292
+ });
293
+ /**
294
+ * Истёк срок, заданный опцией `timeout` (одна попытка) или `deadline` (операция целиком).
295
+ *
296
+ * Истёкший `timeout` для safe- и idempotent-операций повторяется; истёкший `deadline` —
297
+ * нет.
298
+ */
282
299
  var ItdTimeoutError = class extends ItdError {
283
- /** Значение таймаута в миллисекундах. */
300
+ /** Истёкший срок в миллисекундах. */
284
301
  timeout;
285
302
  /** HTTP-метод запроса. */
286
303
  method;
287
304
  /** Путь запроса без базового URL. */
288
305
  path;
306
+ /** Какой из двух сроков истёк. */
307
+ budget;
289
308
  constructor(init) {
290
- super(ItdErrorKind.Timeout, `Запрос ${init.method} ${init.path} превысил таймаут ${init.timeout} мс`);
309
+ const budget = init.budget ?? TimeoutBudget.Attempt;
310
+ super(ItdErrorKind.Timeout, budget === TimeoutBudget.Attempt ? `Запрос ${init.method} ${init.path} превысил таймаут попытки ${init.timeout} мс` : `Запрос ${init.method} ${init.path} превысил общий срок ${init.timeout} мс`);
291
311
  this.name = "ItdTimeoutError";
292
312
  this.timeout = init.timeout;
293
313
  this.method = init.method;
294
314
  this.path = init.path;
315
+ this.budget = budget;
295
316
  }
296
317
  };
297
318
  /** Запрос отменён через переданный `AbortSignal`. */
@@ -384,6 +405,34 @@ function isItdServerError(value) {
384
405
  return hasApiKind(value, ItdApiErrorKind.Server);
385
406
  }
386
407
  //#endregion
408
+ //#region src/core/validate.ts
409
+ /**
410
+ * Проверки, общие для резолверов конфигурации и разборщиков ответов.
411
+ *
412
+ * Настройки исполнения и настройки сессии разбираются в разных модулях, но сообщать
413
+ * об ошибке должны одинаково: пользователю всё равно, какой слой отверг его значение.
414
+ */
415
+ /** Обычный объект — не `null`, не массив. Сужает тип: для разбора значений неизвестной формы. */
416
+ function isRecord(value) {
417
+ return typeof value === "object" && value !== null && !Array.isArray(value);
418
+ }
419
+ /**
420
+ * Та же проверка без сужения типа: для уже типизированных настроек, где сужение до индексной
421
+ * сигнатуры стёрло бы объявленные поля.
422
+ */
423
+ function isObjectLike(value) {
424
+ return isRecord(value);
425
+ }
426
+ /** @throws {ItdConfigError} если значение не является неотрицательным конечным числом */
427
+ function requireNonNegative(value, name) {
428
+ if (!Number.isFinite(value) || value < 0) throw new ItdConfigError(`${name} должен быть неотрицательным числом, получено: ${value}`);
429
+ return value;
430
+ }
431
+ /** @throws {ItdConfigError} если значение задано и не является boolean */
432
+ function requireOptionalBoolean(value, name) {
433
+ if (value !== void 0 && typeof value !== "boolean") throw new ItdConfigError(`${name} должен быть boolean`);
434
+ }
435
+ //#endregion
387
436
  Object.defineProperty(exports, "ItdAbortError", {
388
437
  enumerable: true,
389
438
  get: function() {
@@ -498,6 +547,12 @@ Object.defineProperty(exports, "ItdValidationError", {
498
547
  return ItdValidationError;
499
548
  }
500
549
  });
550
+ Object.defineProperty(exports, "TimeoutBudget", {
551
+ enumerable: true,
552
+ get: function() {
553
+ return TimeoutBudget;
554
+ }
555
+ });
501
556
  Object.defineProperty(exports, "isItdApiError", {
502
557
  enumerable: true,
503
558
  get: function() {
@@ -570,5 +625,29 @@ Object.defineProperty(exports, "isItdValidationError", {
570
625
  return isItdValidationError;
571
626
  }
572
627
  });
628
+ Object.defineProperty(exports, "isObjectLike", {
629
+ enumerable: true,
630
+ get: function() {
631
+ return isObjectLike;
632
+ }
633
+ });
634
+ Object.defineProperty(exports, "isRecord", {
635
+ enumerable: true,
636
+ get: function() {
637
+ return isRecord;
638
+ }
639
+ });
640
+ Object.defineProperty(exports, "requireNonNegative", {
641
+ enumerable: true,
642
+ get: function() {
643
+ return requireNonNegative;
644
+ }
645
+ });
646
+ Object.defineProperty(exports, "requireOptionalBoolean", {
647
+ enumerable: true,
648
+ get: function() {
649
+ return requireOptionalBoolean;
650
+ }
651
+ });
573
652
 
574
- //# sourceMappingURL=errors-BmP3TKoW.cjs.map
653
+ //# sourceMappingURL=validate-CFx2dISs.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-CFx2dISs.cjs","names":[],"sources":["../../src/core/errors.ts","../../src/core/validate.ts"],"sourcesContent":["import type { ItdErrorCode } from '../types/enums.js';\n\n/** Бренд, по которому ошибки библиотеки распознаются надёжнее, чем через `instanceof`. */\nconst ITD_ERROR = Symbol.for('itd.error');\n\n/**\n * Категория ошибки. Определяет, какие поля у неё есть.\n *\n * Значение, а не только тип: категорию нужно с чем-то сравнивать в рантайме.\n */\nexport const ItdErrorKind = Object.freeze({\n /** Сервер ответил статусом ≥ 400. */\n Api: 'api',\n /** Запрос не дошёл до сервера. */\n Network: 'network',\n /** Истёк таймаут запроса. */\n Timeout: 'timeout',\n /** Запрос отменён через `AbortSignal`. */\n Abort: 'abort',\n /** Операция невозможна в текущем состоянии объекта. */\n State: 'state',\n /** Не удалось получить или подготовить содержимое вложения. */\n File: 'file',\n /** Некорректная конфигурация или аргументы — обнаружено до обращения к сети. */\n Config: 'config',\n} as const);\nexport type ItdErrorKind = (typeof ItdErrorKind)[keyof typeof ItdErrorKind];\n\n/**\n * Разновидность ошибки API — то же, что класс ошибки, но в виде данных.\n *\n * Существует потому, что `instanceof` подводит, когда в дереве зависимостей оказались\n * две копии пакета или смешаны сборки ESM и CJS: классы тогда разные, хотя ошибка та же.\n * Проверки {@link isItdValidationError} и соседние опираются на это поле, а не на класс.\n */\nexport const ItdApiErrorKind = Object.freeze({\n /** Ни одна из специализаций не подошла. */\n Generic: 'generic',\n Validation: 'validation',\n Auth: 'auth',\n Forbidden: 'forbidden',\n NotFound: 'not_found',\n Conflict: 'conflict',\n RateLimit: 'rate_limit',\n PhoneVerification: 'phone_verification',\n Server: 'server',\n} as const);\nexport type ItdApiErrorKind = (typeof ItdApiErrorKind)[keyof typeof ItdApiErrorKind];\n\n/** Ошибки по полям формы: `{ email: ['уже занят'] }`. */\nexport type ItdFieldErrors = Record<string, string[]>;\n\n/**\n * Базовый класс всех ошибок библиотеки.\n *\n * Ловить его имеет смысл, чтобы отделить проблемы обращения к итд.com от прочих исключений.\n * Для разбора конкретной причины используйте {@link isItdApiError} и поле {@link ItdApiError.code}.\n *\n * @example\n * ```ts\n * try {\n * await itd.posts.like(id);\n * } catch (e) {\n * if (isItdError(e)) console.error('итд.com:', e.message);\n * else throw e;\n * }\n * ```\n */\nexport class ItdError extends Error {\n /** @internal */\n readonly [ITD_ERROR] = true as const;\n\n /** Категория ошибки. */\n readonly kind: ItdErrorKind;\n\n constructor(kind: ItdErrorKind, message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.kind = kind;\n this.name = 'ItdError';\n }\n}\n\n/** Параметры конструктора {@link ItdApiError}. */\nexport interface ItdApiErrorInit {\n /** HTTP-статус ответа. */\n status: number;\n /** Строковый код ошибки из тела ответа. */\n code: ItdErrorCode;\n /** Человекочитаемое сообщение. */\n message: string;\n /** Расширенное описание, если сервер его прислал. */\n detail?: string | undefined;\n /** Заголовок ошибки, если сервер его прислал. */\n title?: string | undefined;\n /** Ошибки по конкретным полям (сведены из `errors` и `violations`). */\n fieldErrors?: ItdFieldErrors | undefined;\n /** Идентификатор запроса из заголовков ответа, если есть. */\n requestId?: string | undefined;\n /** HTTP-метод запроса. */\n method: string;\n /** Путь запроса без базового URL. */\n path: string;\n /** Тело ответа как оно пришло — на случай, если документация разошлась с реальностью. */\n raw: unknown;\n /** Сам объект ответа. Тело уже прочитано. */\n response?: Response | undefined;\n /** Значение `Retry-After` в миллисекундах, если заголовок был. */\n retryAfter?: number | undefined;\n /** Сколько запросов разрешено в окне (`x-ratelimit-limit`). */\n rateLimit?: number | undefined;\n /** Сколько запросов осталось в окне (`x-ratelimit-remaining`). */\n rateLimitRemaining?: number | undefined;\n}\n\n/**\n * Ошибка, возвращённая сервером итд.com (HTTP-статус ≥ 400).\n *\n * API отдаёт ошибки в двух разных формах — `{ error: { … } }` и `{ code, message, violations }`.\n * Библиотека сводит обе к этому классу, поэтому разбирать форму ответа вручную не нужно.\n *\n * @example\n * ```ts\n * try {\n * await itd.users.updateMe({ username: 'занятое_имя' });\n * } catch (e) {\n * if (e instanceof ItdValidationError) {\n * console.log(e.fieldErrors.username); // ['Имя уже занято']\n * }\n * }\n * ```\n */\nexport class ItdApiError extends ItdError {\n /**\n * Разновидность ошибки: та же информация, что и класс, но пригодная для сравнения.\n *\n * Позволяет разбирать ошибку через `switch`, а проверкам вроде {@link isItdAuthError} —\n * работать даже когда в проекте оказались две копии библиотеки.\n */\n readonly apiKind: ItdApiErrorKind;\n /** HTTP-статус ответа. */\n readonly status: number;\n /** Строковый код ошибки, например `VALIDATION_ERROR`. */\n readonly code: ItdErrorCode;\n /** Расширенное описание, если сервер его прислал. */\n readonly detail: string | undefined;\n /** Заголовок ошибки, если сервер его прислал. */\n readonly title: string | undefined;\n /** Ошибки по полям. Пустой объект, если сервер их не прислал. */\n readonly fieldErrors: ItdFieldErrors;\n /** Идентификатор запроса из заголовков ответа. */\n readonly requestId: string | undefined;\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n /** Тело ответа как оно пришло. */\n readonly raw: unknown;\n /** Объект ответа. Тело уже прочитано и повторно прочитано быть не может. */\n readonly response: Response | undefined;\n /** Пауза из заголовка `Retry-After` в миллисекундах. Сервер итд.com его не присылает. */\n readonly retryAfter: number | undefined;\n /**\n * Сколько запросов разрешено в окне — заголовок `x-ratelimit-limit`.\n *\n * Времени сброса окна сервер не сообщает, поэтому точный момент повтора неизвестен.\n */\n readonly rateLimit: number | undefined;\n /** Сколько запросов осталось в окне — заголовок `x-ratelimit-remaining`. */\n readonly rateLimitRemaining: number | undefined;\n\n /**\n * @param apiKind разновидность; подставляется подклассами, снаружи задавать не нужно\n */\n constructor(init: ItdApiErrorInit, apiKind: ItdApiErrorKind = ItdApiErrorKind.Generic) {\n super(ItdErrorKind.Api, init.message);\n this.name = 'ItdApiError';\n this.apiKind = apiKind;\n this.status = init.status;\n this.code = init.code;\n this.detail = init.detail;\n this.title = init.title;\n this.fieldErrors = init.fieldErrors ?? {};\n this.requestId = init.requestId;\n this.method = init.method;\n this.path = init.path;\n this.raw = init.raw;\n this.response = init.response;\n this.retryAfter = init.retryAfter;\n this.rateLimit = init.rateLimit;\n this.rateLimitRemaining = init.rateLimitRemaining;\n }\n\n /**\n * Проверяет код ошибки. Удобнее, чем сравнивать строки вручную.\n *\n * @example\n * ```ts\n * if (err.hasCode('OTP_INVALID', 'MISSING_FLOW_TOKEN')) await restartOtpFlow();\n * ```\n */\n hasCode(...codes: ItdErrorCode[]): boolean {\n return codes.includes(this.code);\n }\n\n /**\n * Временная ошибка сервера, которую политика повторов считает повторяемой: `429` или `5xx`.\n *\n * Повторит ли библиотека саму операцию, зависит ещё от её `retrySafety`: `5xx` на\n * небезопасной операции не повторяется, `429` — повторяется всегда.\n */\n get isRetryable(): boolean {\n return this.status === 429 || this.status >= 500;\n }\n}\n\n/** `400` / `422` — данные не прошли валидацию. Подробности в {@link ItdApiError.fieldErrors}. */\nexport class ItdValidationError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Validation);\n this.name = 'ItdValidationError';\n }\n}\n\n/** `401` — токен отсутствует, истёк или отозван. */\nexport class ItdAuthError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Auth);\n this.name = 'ItdAuthError';\n }\n}\n\n/** `403` — доступ запрещён либо действие ограничено настройками приватности. */\nexport class ItdForbiddenError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Forbidden);\n this.name = 'ItdForbiddenError';\n }\n}\n\n/** `404` — сущность не найдена. */\nexport class ItdNotFoundError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.NotFound);\n this.name = 'ItdNotFoundError';\n }\n}\n\n/** `409` — сущность уже существует. */\nexport class ItdConflictError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Conflict);\n this.name = 'ItdConflictError';\n }\n}\n\n/**\n * `429` — превышен лимит запросов.\n *\n * Если сервер прислал `Retry-After`, пауза доступна в {@link ItdApiError.retryAfter}\n * (в миллисекундах). При включённых ретраях библиотека выдерживает её автоматически.\n */\nexport class ItdRateLimitError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.RateLimit);\n this.name = 'ItdRateLimitError';\n }\n}\n\n/**\n * Действие требует подтверждённого телефона (`PHONE_VERIFICATION_REQUIRED`).\n *\n * Подтверждение проходит через Telegram-бота: ссылка лежит в {@link verificationUrl}.\n */\nexport class ItdPhoneVerificationError extends ItdApiError {\n /** Ссылка на бота подтверждения, если удалось определить идентификатор пользователя. */\n readonly verificationUrl: string | undefined;\n\n constructor(init: ItdApiErrorInit & { userId?: string | undefined }) {\n super(init, ItdApiErrorKind.PhoneVerification);\n this.name = 'ItdPhoneVerificationError';\n this.verificationUrl = init.userId\n ? `https://t.me/itd_verification_bot?start=${encodeURIComponent(init.userId)}`\n : undefined;\n }\n}\n\n/** `5xx` — ошибка на стороне сервера. */\nexport class ItdServerError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Server);\n this.name = 'ItdServerError';\n }\n}\n\n/** Причина ошибки получения вложения. */\nexport const ItdFileErrorReason = Object.freeze({\n /** Сетевой сбой при получении источника. */\n Network: 'network',\n /** Источник ответил ошибочным HTTP-статусом. */\n Http: 'http',\n /** Источник превысил разрешённый размер. */\n TooLarge: 'too_large',\n /** Среда или источник не предоставили поток. */\n StreamUnavailable: 'stream_unavailable',\n /** Поток источника завершился ошибкой. */\n Read: 'read',\n} as const);\nexport type ItdFileErrorReason = (typeof ItdFileErrorReason)[keyof typeof ItdFileErrorReason];\n\n/** Не удалось получить или прочитать содержимое вложения. */\nexport class ItdFileError extends ItdError {\n readonly reason: ItdFileErrorReason;\n /** Адрес источника без секретных параметров запроса, если файл получался по сети. */\n readonly url: string | undefined;\n /** HTTP-статус источника. */\n readonly status: number | undefined;\n /** Разрешённый размер в байтах. */\n readonly limit: number | undefined;\n /** Обнаруженный размер в байтах. */\n readonly actual: number | undefined;\n /** Имеет ли смысл повторить получение источника. */\n readonly retryable: boolean;\n\n constructor(\n message: string,\n init: {\n reason: ItdFileErrorReason;\n url?: string | undefined;\n status?: number | undefined;\n limit?: number | undefined;\n actual?: number | undefined;\n retryable?: boolean | undefined;\n cause?: unknown;\n },\n ) {\n super(ItdErrorKind.File, message, { cause: init.cause });\n this.name = 'ItdFileError';\n this.reason = init.reason;\n this.url = init.url;\n this.status = init.status;\n this.limit = init.limit;\n this.actual = init.actual;\n this.retryable = init.retryable ?? false;\n }\n}\n\n/** Запрос не дошёл до сервера: DNS, обрыв соединения, отсутствие сети. */\nexport class ItdNetworkError extends ItdError {\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n\n constructor(message: string, init: { method: string; path: string; cause?: unknown }) {\n super(ItdErrorKind.Network, message, { cause: init.cause });\n this.name = 'ItdNetworkError';\n this.method = init.method;\n this.path = init.path;\n }\n}\n\n/** Какой бюджет времени исчерпан. */\nexport const TimeoutBudget = Object.freeze({\n /** Срок одной транспортной попытки — опция `timeout`. */\n Attempt: 'attempt',\n /** Общий срок логической операции — опция `deadline`. */\n Deadline: 'deadline',\n} as const);\nexport type TimeoutBudget = (typeof TimeoutBudget)[keyof typeof TimeoutBudget];\n\n/**\n * Истёк срок, заданный опцией `timeout` (одна попытка) или `deadline` (операция целиком).\n *\n * Истёкший `timeout` для safe- и idempotent-операций повторяется; истёкший `deadline` —\n * нет.\n */\nexport class ItdTimeoutError extends ItdError {\n /** Истёкший срок в миллисекундах. */\n readonly timeout: number;\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n /** Какой из двух сроков истёк. */\n readonly budget: TimeoutBudget;\n\n constructor(init: {\n timeout: number;\n method: string;\n path: string;\n budget?: TimeoutBudget | undefined;\n }) {\n const budget = init.budget ?? TimeoutBudget.Attempt;\n super(\n ItdErrorKind.Timeout,\n budget === TimeoutBudget.Attempt\n ? `Запрос ${init.method} ${init.path} превысил таймаут попытки ${init.timeout} мс`\n : `Запрос ${init.method} ${init.path} превысил общий срок ${init.timeout} мс`,\n );\n this.name = 'ItdTimeoutError';\n this.timeout = init.timeout;\n this.method = init.method;\n this.path = init.path;\n this.budget = budget;\n }\n}\n\n/** Запрос отменён через переданный `AbortSignal`. */\nexport class ItdAbortError extends ItdError {\n constructor(message = 'Запрос отменён', options?: { cause?: unknown }) {\n super(ItdErrorKind.Abort, message, options);\n this.name = 'ItdAbortError';\n }\n}\n\n/**\n * Операция невозможна в текущем состоянии объекта.\n *\n * Например, клиент уже окончательно освобождён через `dispose()` и не может выполнять\n * новые запросы или создавать событийные соединения.\n */\nexport class ItdStateError extends ItdError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(ItdErrorKind.State, message, options);\n this.name = 'ItdStateError';\n }\n}\n\n/**\n * Некорректная конфигурация или аргументы — обнаружено до обращения к сети.\n *\n * Этим же классом сообщают о нарушенных инвариантах билдеры: например, опрос\n * с одним вариантом ответа.\n */\nexport class ItdConfigError extends ItdError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(ItdErrorKind.Config, message, options);\n this.name = 'ItdConfigError';\n }\n}\n\n/** Любая ошибка, порождённая этой библиотекой. */\nexport function isItdError(value: unknown): value is ItdError {\n return typeof value === 'object' && value !== null && ITD_ERROR in value;\n}\n\n/** Ошибка, пришедшая от сервера итд.com (статус ≥ 400). */\nexport function isItdApiError(value: unknown): value is ItdApiError {\n return isItdError(value) && value.kind === ItdErrorKind.Api;\n}\n\n/** Ошибка получения или чтения вложения. */\nexport function isItdFileError(value: unknown): value is ItdFileError {\n return isItdError(value) && value.kind === ItdErrorKind.File;\n}\n\n/** Операция невозможна в текущем состоянии объекта. */\nexport function isItdStateError(value: unknown): value is ItdStateError {\n return isItdError(value) && value.kind === ItdErrorKind.State;\n}\n\n/**\n * Проверяет разновидность ошибки API.\n *\n * Намеренно не `instanceof`: две копии пакета в дереве зависимостей или смешение сборок\n * ESM и CJS дают разные классы для одной и той же ошибки, и проверка по классу молча\n * перестаёт срабатывать. Поле {@link ItdApiError.apiKind} от этого не зависит.\n */\nfunction hasApiKind(value: unknown, kind: ItdApiErrorKind): boolean {\n return isItdApiError(value) && value.apiKind === kind;\n}\n\n/** Ошибка валидации: `VALIDATION_ERROR` либо статус `400`/`422`. */\nexport function isItdValidationError(value: unknown): value is ItdValidationError {\n return hasApiKind(value, ItdApiErrorKind.Validation);\n}\n\n/** Ошибка авторизации: истёкший или отозванный токен. */\nexport function isItdAuthError(value: unknown): value is ItdAuthError {\n return hasApiKind(value, ItdApiErrorKind.Auth);\n}\n\n/** Доступ запрещён либо действие ограничено настройками приватности. */\nexport function isItdForbiddenError(value: unknown): value is ItdForbiddenError {\n return hasApiKind(value, ItdApiErrorKind.Forbidden);\n}\n\n/** Сущность не найдена. */\nexport function isItdNotFoundError(value: unknown): value is ItdNotFoundError {\n return hasApiKind(value, ItdApiErrorKind.NotFound);\n}\n\n/** Сущность уже существует. */\nexport function isItdConflictError(value: unknown): value is ItdConflictError {\n return hasApiKind(value, ItdApiErrorKind.Conflict);\n}\n\n/** Превышен лимит запросов. */\nexport function isItdRateLimitError(value: unknown): value is ItdRateLimitError {\n return hasApiKind(value, ItdApiErrorKind.RateLimit);\n}\n\n/** Действие требует подтверждённого телефона. Ссылка — в `verificationUrl`. */\nexport function isItdPhoneVerificationError(value: unknown): value is ItdPhoneVerificationError {\n return hasApiKind(value, ItdApiErrorKind.PhoneVerification);\n}\n\n/** Ошибка на стороне сервера (`5xx`). */\nexport function isItdServerError(value: unknown): value is ItdServerError {\n return hasApiKind(value, ItdApiErrorKind.Server);\n}\n","import { ItdConfigError } from './errors.js';\n\n/**\n * Проверки, общие для резолверов конфигурации и разборщиков ответов.\n *\n * Настройки исполнения и настройки сессии разбираются в разных модулях, но сообщать\n * об ошибке должны одинаково: пользователю всё равно, какой слой отверг его значение.\n */\n\n/** Обычный объект — не `null`, не массив. Сужает тип: для разбора значений неизвестной формы. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Та же проверка без сужения типа: для уже типизированных настроек, где сужение до индексной\n * сигнатуры стёрло бы объявленные поля.\n */\nexport function isObjectLike(value: unknown): boolean {\n return isRecord(value);\n}\n\n/** @throws {ItdConfigError} если значение не является неотрицательным конечным числом */\nexport function requireNonNegative(value: number, name: string): number {\n if (!Number.isFinite(value) || value < 0) {\n throw new ItdConfigError(`${name} должен быть неотрицательным числом, получено: ${value}`);\n }\n return value;\n}\n\n/** @throws {ItdConfigError} если значение задано и не является boolean */\nexport function requireOptionalBoolean(value: unknown, name: string): void {\n if (value !== undefined && typeof value !== 'boolean') {\n throw new ItdConfigError(`${name} должен быть boolean`);\n }\n}\n"],"mappings":"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"}
@@ -155,7 +155,12 @@ var ItdApiError = class extends ItdError {
155
155
  hasCode(...codes) {
156
156
  return codes.includes(this.code);
157
157
  }
158
- /** Имеет ли смысл повторить запрос: `429` и серверные ошибки `5xx`. */
158
+ /**
159
+ * Временная ошибка сервера, которую политика повторов считает повторяемой: `429` или `5xx`.
160
+ *
161
+ * Повторит ли библиотека саму операцию, зависит ещё от её `retrySafety`: `5xx` на
162
+ * небезопасной операции не повторяется, `429` — повторяется всегда.
163
+ */
159
164
  get isRetryable() {
160
165
  return this.status === 429 || this.status >= 500;
161
166
  }
@@ -278,20 +283,36 @@ var ItdNetworkError = class extends ItdError {
278
283
  this.path = init.path;
279
284
  }
280
285
  };
281
- /** Истёк таймаут запроса, заданный опцией `timeout`. */
286
+ /** Какой бюджет времени исчерпан. */
287
+ const TimeoutBudget = Object.freeze({
288
+ /** Срок одной транспортной попытки — опция `timeout`. */
289
+ Attempt: "attempt",
290
+ /** Общий срок логической операции — опция `deadline`. */
291
+ Deadline: "deadline"
292
+ });
293
+ /**
294
+ * Истёк срок, заданный опцией `timeout` (одна попытка) или `deadline` (операция целиком).
295
+ *
296
+ * Истёкший `timeout` для safe- и idempotent-операций повторяется; истёкший `deadline` —
297
+ * нет.
298
+ */
282
299
  var ItdTimeoutError = class extends ItdError {
283
- /** Значение таймаута в миллисекундах. */
300
+ /** Истёкший срок в миллисекундах. */
284
301
  timeout;
285
302
  /** HTTP-метод запроса. */
286
303
  method;
287
304
  /** Путь запроса без базового URL. */
288
305
  path;
306
+ /** Какой из двух сроков истёк. */
307
+ budget;
289
308
  constructor(init) {
290
- super(ItdErrorKind.Timeout, `Запрос ${init.method} ${init.path} превысил таймаут ${init.timeout} мс`);
309
+ const budget = init.budget ?? TimeoutBudget.Attempt;
310
+ super(ItdErrorKind.Timeout, budget === TimeoutBudget.Attempt ? `Запрос ${init.method} ${init.path} превысил таймаут попытки ${init.timeout} мс` : `Запрос ${init.method} ${init.path} превысил общий срок ${init.timeout} мс`);
291
311
  this.name = "ItdTimeoutError";
292
312
  this.timeout = init.timeout;
293
313
  this.method = init.method;
294
314
  this.path = init.path;
315
+ this.budget = budget;
295
316
  }
296
317
  };
297
318
  /** Запрос отменён через переданный `AbortSignal`. */
@@ -384,6 +405,34 @@ function isItdServerError(value) {
384
405
  return hasApiKind(value, ItdApiErrorKind.Server);
385
406
  }
386
407
  //#endregion
387
- export { isItdStateError as A, isItdError as C, isItdPhoneVerificationError as D, isItdNotFoundError as E, isItdRateLimitError as O, isItdConflictError as S, isItdForbiddenError as T, ItdStateError as _, ItdConfigError as a, isItdApiError as b, ItdErrorKind as c, ItdForbiddenError as d, ItdNetworkError as f, ItdServerError as g, ItdRateLimitError as h, ItdAuthError as i, isItdValidationError as j, isItdServerError as k, ItdFileError as l, ItdPhoneVerificationError as m, ItdApiError as n, ItdConflictError as o, ItdNotFoundError as p, ItdApiErrorKind as r, ItdError as s, ItdAbortError as t, ItdFileErrorReason as u, ItdTimeoutError as v, isItdFileError as w, isItdAuthError as x, ItdValidationError as y };
408
+ //#region src/core/validate.ts
409
+ /**
410
+ * Проверки, общие для резолверов конфигурации и разборщиков ответов.
411
+ *
412
+ * Настройки исполнения и настройки сессии разбираются в разных модулях, но сообщать
413
+ * об ошибке должны одинаково: пользователю всё равно, какой слой отверг его значение.
414
+ */
415
+ /** Обычный объект — не `null`, не массив. Сужает тип: для разбора значений неизвестной формы. */
416
+ function isRecord(value) {
417
+ return typeof value === "object" && value !== null && !Array.isArray(value);
418
+ }
419
+ /**
420
+ * Та же проверка без сужения типа: для уже типизированных настроек, где сужение до индексной
421
+ * сигнатуры стёрло бы объявленные поля.
422
+ */
423
+ function isObjectLike(value) {
424
+ return isRecord(value);
425
+ }
426
+ /** @throws {ItdConfigError} если значение не является неотрицательным конечным числом */
427
+ function requireNonNegative(value, name) {
428
+ if (!Number.isFinite(value) || value < 0) throw new ItdConfigError(`${name} должен быть неотрицательным числом, получено: ${value}`);
429
+ return value;
430
+ }
431
+ /** @throws {ItdConfigError} если значение задано и не является boolean */
432
+ function requireOptionalBoolean(value, name) {
433
+ if (value !== void 0 && typeof value !== "boolean") throw new ItdConfigError(`${name} должен быть boolean`);
434
+ }
435
+ //#endregion
436
+ export { isItdForbiddenError as A, ItdValidationError as C, isItdConflictError as D, isItdAuthError as E, isItdStateError as F, isItdValidationError as I, isItdPhoneVerificationError as M, isItdRateLimitError as N, isItdError as O, isItdServerError as P, ItdTimeoutError as S, isItdApiError as T, ItdNotFoundError as _, ItdAbortError as a, ItdServerError as b, ItdAuthError as c, ItdError as d, ItdErrorKind as f, ItdNetworkError as g, ItdForbiddenError as h, requireOptionalBoolean as i, isItdNotFoundError as j, isItdFileError as k, ItdConfigError as l, ItdFileErrorReason as m, isRecord as n, ItdApiError as o, ItdFileError as p, requireNonNegative as r, ItdApiErrorKind as s, isObjectLike as t, ItdConflictError as u, ItdPhoneVerificationError as v, TimeoutBudget as w, ItdStateError as x, ItdRateLimitError as y };
388
437
 
389
- //# sourceMappingURL=errors-GI10kZxk.js.map
438
+ //# sourceMappingURL=validate-D7_kq3g5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-D7_kq3g5.js","names":[],"sources":["../../src/core/errors.ts","../../src/core/validate.ts"],"sourcesContent":["import type { ItdErrorCode } from '../types/enums.js';\n\n/** Бренд, по которому ошибки библиотеки распознаются надёжнее, чем через `instanceof`. */\nconst ITD_ERROR = Symbol.for('itd.error');\n\n/**\n * Категория ошибки. Определяет, какие поля у неё есть.\n *\n * Значение, а не только тип: категорию нужно с чем-то сравнивать в рантайме.\n */\nexport const ItdErrorKind = Object.freeze({\n /** Сервер ответил статусом ≥ 400. */\n Api: 'api',\n /** Запрос не дошёл до сервера. */\n Network: 'network',\n /** Истёк таймаут запроса. */\n Timeout: 'timeout',\n /** Запрос отменён через `AbortSignal`. */\n Abort: 'abort',\n /** Операция невозможна в текущем состоянии объекта. */\n State: 'state',\n /** Не удалось получить или подготовить содержимое вложения. */\n File: 'file',\n /** Некорректная конфигурация или аргументы — обнаружено до обращения к сети. */\n Config: 'config',\n} as const);\nexport type ItdErrorKind = (typeof ItdErrorKind)[keyof typeof ItdErrorKind];\n\n/**\n * Разновидность ошибки API — то же, что класс ошибки, но в виде данных.\n *\n * Существует потому, что `instanceof` подводит, когда в дереве зависимостей оказались\n * две копии пакета или смешаны сборки ESM и CJS: классы тогда разные, хотя ошибка та же.\n * Проверки {@link isItdValidationError} и соседние опираются на это поле, а не на класс.\n */\nexport const ItdApiErrorKind = Object.freeze({\n /** Ни одна из специализаций не подошла. */\n Generic: 'generic',\n Validation: 'validation',\n Auth: 'auth',\n Forbidden: 'forbidden',\n NotFound: 'not_found',\n Conflict: 'conflict',\n RateLimit: 'rate_limit',\n PhoneVerification: 'phone_verification',\n Server: 'server',\n} as const);\nexport type ItdApiErrorKind = (typeof ItdApiErrorKind)[keyof typeof ItdApiErrorKind];\n\n/** Ошибки по полям формы: `{ email: ['уже занят'] }`. */\nexport type ItdFieldErrors = Record<string, string[]>;\n\n/**\n * Базовый класс всех ошибок библиотеки.\n *\n * Ловить его имеет смысл, чтобы отделить проблемы обращения к итд.com от прочих исключений.\n * Для разбора конкретной причины используйте {@link isItdApiError} и поле {@link ItdApiError.code}.\n *\n * @example\n * ```ts\n * try {\n * await itd.posts.like(id);\n * } catch (e) {\n * if (isItdError(e)) console.error('итд.com:', e.message);\n * else throw e;\n * }\n * ```\n */\nexport class ItdError extends Error {\n /** @internal */\n readonly [ITD_ERROR] = true as const;\n\n /** Категория ошибки. */\n readonly kind: ItdErrorKind;\n\n constructor(kind: ItdErrorKind, message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.kind = kind;\n this.name = 'ItdError';\n }\n}\n\n/** Параметры конструктора {@link ItdApiError}. */\nexport interface ItdApiErrorInit {\n /** HTTP-статус ответа. */\n status: number;\n /** Строковый код ошибки из тела ответа. */\n code: ItdErrorCode;\n /** Человекочитаемое сообщение. */\n message: string;\n /** Расширенное описание, если сервер его прислал. */\n detail?: string | undefined;\n /** Заголовок ошибки, если сервер его прислал. */\n title?: string | undefined;\n /** Ошибки по конкретным полям (сведены из `errors` и `violations`). */\n fieldErrors?: ItdFieldErrors | undefined;\n /** Идентификатор запроса из заголовков ответа, если есть. */\n requestId?: string | undefined;\n /** HTTP-метод запроса. */\n method: string;\n /** Путь запроса без базового URL. */\n path: string;\n /** Тело ответа как оно пришло — на случай, если документация разошлась с реальностью. */\n raw: unknown;\n /** Сам объект ответа. Тело уже прочитано. */\n response?: Response | undefined;\n /** Значение `Retry-After` в миллисекундах, если заголовок был. */\n retryAfter?: number | undefined;\n /** Сколько запросов разрешено в окне (`x-ratelimit-limit`). */\n rateLimit?: number | undefined;\n /** Сколько запросов осталось в окне (`x-ratelimit-remaining`). */\n rateLimitRemaining?: number | undefined;\n}\n\n/**\n * Ошибка, возвращённая сервером итд.com (HTTP-статус ≥ 400).\n *\n * API отдаёт ошибки в двух разных формах — `{ error: { … } }` и `{ code, message, violations }`.\n * Библиотека сводит обе к этому классу, поэтому разбирать форму ответа вручную не нужно.\n *\n * @example\n * ```ts\n * try {\n * await itd.users.updateMe({ username: 'занятое_имя' });\n * } catch (e) {\n * if (e instanceof ItdValidationError) {\n * console.log(e.fieldErrors.username); // ['Имя уже занято']\n * }\n * }\n * ```\n */\nexport class ItdApiError extends ItdError {\n /**\n * Разновидность ошибки: та же информация, что и класс, но пригодная для сравнения.\n *\n * Позволяет разбирать ошибку через `switch`, а проверкам вроде {@link isItdAuthError} —\n * работать даже когда в проекте оказались две копии библиотеки.\n */\n readonly apiKind: ItdApiErrorKind;\n /** HTTP-статус ответа. */\n readonly status: number;\n /** Строковый код ошибки, например `VALIDATION_ERROR`. */\n readonly code: ItdErrorCode;\n /** Расширенное описание, если сервер его прислал. */\n readonly detail: string | undefined;\n /** Заголовок ошибки, если сервер его прислал. */\n readonly title: string | undefined;\n /** Ошибки по полям. Пустой объект, если сервер их не прислал. */\n readonly fieldErrors: ItdFieldErrors;\n /** Идентификатор запроса из заголовков ответа. */\n readonly requestId: string | undefined;\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n /** Тело ответа как оно пришло. */\n readonly raw: unknown;\n /** Объект ответа. Тело уже прочитано и повторно прочитано быть не может. */\n readonly response: Response | undefined;\n /** Пауза из заголовка `Retry-After` в миллисекундах. Сервер итд.com его не присылает. */\n readonly retryAfter: number | undefined;\n /**\n * Сколько запросов разрешено в окне — заголовок `x-ratelimit-limit`.\n *\n * Времени сброса окна сервер не сообщает, поэтому точный момент повтора неизвестен.\n */\n readonly rateLimit: number | undefined;\n /** Сколько запросов осталось в окне — заголовок `x-ratelimit-remaining`. */\n readonly rateLimitRemaining: number | undefined;\n\n /**\n * @param apiKind разновидность; подставляется подклассами, снаружи задавать не нужно\n */\n constructor(init: ItdApiErrorInit, apiKind: ItdApiErrorKind = ItdApiErrorKind.Generic) {\n super(ItdErrorKind.Api, init.message);\n this.name = 'ItdApiError';\n this.apiKind = apiKind;\n this.status = init.status;\n this.code = init.code;\n this.detail = init.detail;\n this.title = init.title;\n this.fieldErrors = init.fieldErrors ?? {};\n this.requestId = init.requestId;\n this.method = init.method;\n this.path = init.path;\n this.raw = init.raw;\n this.response = init.response;\n this.retryAfter = init.retryAfter;\n this.rateLimit = init.rateLimit;\n this.rateLimitRemaining = init.rateLimitRemaining;\n }\n\n /**\n * Проверяет код ошибки. Удобнее, чем сравнивать строки вручную.\n *\n * @example\n * ```ts\n * if (err.hasCode('OTP_INVALID', 'MISSING_FLOW_TOKEN')) await restartOtpFlow();\n * ```\n */\n hasCode(...codes: ItdErrorCode[]): boolean {\n return codes.includes(this.code);\n }\n\n /**\n * Временная ошибка сервера, которую политика повторов считает повторяемой: `429` или `5xx`.\n *\n * Повторит ли библиотека саму операцию, зависит ещё от её `retrySafety`: `5xx` на\n * небезопасной операции не повторяется, `429` — повторяется всегда.\n */\n get isRetryable(): boolean {\n return this.status === 429 || this.status >= 500;\n }\n}\n\n/** `400` / `422` — данные не прошли валидацию. Подробности в {@link ItdApiError.fieldErrors}. */\nexport class ItdValidationError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Validation);\n this.name = 'ItdValidationError';\n }\n}\n\n/** `401` — токен отсутствует, истёк или отозван. */\nexport class ItdAuthError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Auth);\n this.name = 'ItdAuthError';\n }\n}\n\n/** `403` — доступ запрещён либо действие ограничено настройками приватности. */\nexport class ItdForbiddenError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Forbidden);\n this.name = 'ItdForbiddenError';\n }\n}\n\n/** `404` — сущность не найдена. */\nexport class ItdNotFoundError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.NotFound);\n this.name = 'ItdNotFoundError';\n }\n}\n\n/** `409` — сущность уже существует. */\nexport class ItdConflictError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Conflict);\n this.name = 'ItdConflictError';\n }\n}\n\n/**\n * `429` — превышен лимит запросов.\n *\n * Если сервер прислал `Retry-After`, пауза доступна в {@link ItdApiError.retryAfter}\n * (в миллисекундах). При включённых ретраях библиотека выдерживает её автоматически.\n */\nexport class ItdRateLimitError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.RateLimit);\n this.name = 'ItdRateLimitError';\n }\n}\n\n/**\n * Действие требует подтверждённого телефона (`PHONE_VERIFICATION_REQUIRED`).\n *\n * Подтверждение проходит через Telegram-бота: ссылка лежит в {@link verificationUrl}.\n */\nexport class ItdPhoneVerificationError extends ItdApiError {\n /** Ссылка на бота подтверждения, если удалось определить идентификатор пользователя. */\n readonly verificationUrl: string | undefined;\n\n constructor(init: ItdApiErrorInit & { userId?: string | undefined }) {\n super(init, ItdApiErrorKind.PhoneVerification);\n this.name = 'ItdPhoneVerificationError';\n this.verificationUrl = init.userId\n ? `https://t.me/itd_verification_bot?start=${encodeURIComponent(init.userId)}`\n : undefined;\n }\n}\n\n/** `5xx` — ошибка на стороне сервера. */\nexport class ItdServerError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Server);\n this.name = 'ItdServerError';\n }\n}\n\n/** Причина ошибки получения вложения. */\nexport const ItdFileErrorReason = Object.freeze({\n /** Сетевой сбой при получении источника. */\n Network: 'network',\n /** Источник ответил ошибочным HTTP-статусом. */\n Http: 'http',\n /** Источник превысил разрешённый размер. */\n TooLarge: 'too_large',\n /** Среда или источник не предоставили поток. */\n StreamUnavailable: 'stream_unavailable',\n /** Поток источника завершился ошибкой. */\n Read: 'read',\n} as const);\nexport type ItdFileErrorReason = (typeof ItdFileErrorReason)[keyof typeof ItdFileErrorReason];\n\n/** Не удалось получить или прочитать содержимое вложения. */\nexport class ItdFileError extends ItdError {\n readonly reason: ItdFileErrorReason;\n /** Адрес источника без секретных параметров запроса, если файл получался по сети. */\n readonly url: string | undefined;\n /** HTTP-статус источника. */\n readonly status: number | undefined;\n /** Разрешённый размер в байтах. */\n readonly limit: number | undefined;\n /** Обнаруженный размер в байтах. */\n readonly actual: number | undefined;\n /** Имеет ли смысл повторить получение источника. */\n readonly retryable: boolean;\n\n constructor(\n message: string,\n init: {\n reason: ItdFileErrorReason;\n url?: string | undefined;\n status?: number | undefined;\n limit?: number | undefined;\n actual?: number | undefined;\n retryable?: boolean | undefined;\n cause?: unknown;\n },\n ) {\n super(ItdErrorKind.File, message, { cause: init.cause });\n this.name = 'ItdFileError';\n this.reason = init.reason;\n this.url = init.url;\n this.status = init.status;\n this.limit = init.limit;\n this.actual = init.actual;\n this.retryable = init.retryable ?? false;\n }\n}\n\n/** Запрос не дошёл до сервера: DNS, обрыв соединения, отсутствие сети. */\nexport class ItdNetworkError extends ItdError {\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n\n constructor(message: string, init: { method: string; path: string; cause?: unknown }) {\n super(ItdErrorKind.Network, message, { cause: init.cause });\n this.name = 'ItdNetworkError';\n this.method = init.method;\n this.path = init.path;\n }\n}\n\n/** Какой бюджет времени исчерпан. */\nexport const TimeoutBudget = Object.freeze({\n /** Срок одной транспортной попытки — опция `timeout`. */\n Attempt: 'attempt',\n /** Общий срок логической операции — опция `deadline`. */\n Deadline: 'deadline',\n} as const);\nexport type TimeoutBudget = (typeof TimeoutBudget)[keyof typeof TimeoutBudget];\n\n/**\n * Истёк срок, заданный опцией `timeout` (одна попытка) или `deadline` (операция целиком).\n *\n * Истёкший `timeout` для safe- и idempotent-операций повторяется; истёкший `deadline` —\n * нет.\n */\nexport class ItdTimeoutError extends ItdError {\n /** Истёкший срок в миллисекундах. */\n readonly timeout: number;\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n /** Какой из двух сроков истёк. */\n readonly budget: TimeoutBudget;\n\n constructor(init: {\n timeout: number;\n method: string;\n path: string;\n budget?: TimeoutBudget | undefined;\n }) {\n const budget = init.budget ?? TimeoutBudget.Attempt;\n super(\n ItdErrorKind.Timeout,\n budget === TimeoutBudget.Attempt\n ? `Запрос ${init.method} ${init.path} превысил таймаут попытки ${init.timeout} мс`\n : `Запрос ${init.method} ${init.path} превысил общий срок ${init.timeout} мс`,\n );\n this.name = 'ItdTimeoutError';\n this.timeout = init.timeout;\n this.method = init.method;\n this.path = init.path;\n this.budget = budget;\n }\n}\n\n/** Запрос отменён через переданный `AbortSignal`. */\nexport class ItdAbortError extends ItdError {\n constructor(message = 'Запрос отменён', options?: { cause?: unknown }) {\n super(ItdErrorKind.Abort, message, options);\n this.name = 'ItdAbortError';\n }\n}\n\n/**\n * Операция невозможна в текущем состоянии объекта.\n *\n * Например, клиент уже окончательно освобождён через `dispose()` и не может выполнять\n * новые запросы или создавать событийные соединения.\n */\nexport class ItdStateError extends ItdError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(ItdErrorKind.State, message, options);\n this.name = 'ItdStateError';\n }\n}\n\n/**\n * Некорректная конфигурация или аргументы — обнаружено до обращения к сети.\n *\n * Этим же классом сообщают о нарушенных инвариантах билдеры: например, опрос\n * с одним вариантом ответа.\n */\nexport class ItdConfigError extends ItdError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(ItdErrorKind.Config, message, options);\n this.name = 'ItdConfigError';\n }\n}\n\n/** Любая ошибка, порождённая этой библиотекой. */\nexport function isItdError(value: unknown): value is ItdError {\n return typeof value === 'object' && value !== null && ITD_ERROR in value;\n}\n\n/** Ошибка, пришедшая от сервера итд.com (статус ≥ 400). */\nexport function isItdApiError(value: unknown): value is ItdApiError {\n return isItdError(value) && value.kind === ItdErrorKind.Api;\n}\n\n/** Ошибка получения или чтения вложения. */\nexport function isItdFileError(value: unknown): value is ItdFileError {\n return isItdError(value) && value.kind === ItdErrorKind.File;\n}\n\n/** Операция невозможна в текущем состоянии объекта. */\nexport function isItdStateError(value: unknown): value is ItdStateError {\n return isItdError(value) && value.kind === ItdErrorKind.State;\n}\n\n/**\n * Проверяет разновидность ошибки API.\n *\n * Намеренно не `instanceof`: две копии пакета в дереве зависимостей или смешение сборок\n * ESM и CJS дают разные классы для одной и той же ошибки, и проверка по классу молча\n * перестаёт срабатывать. Поле {@link ItdApiError.apiKind} от этого не зависит.\n */\nfunction hasApiKind(value: unknown, kind: ItdApiErrorKind): boolean {\n return isItdApiError(value) && value.apiKind === kind;\n}\n\n/** Ошибка валидации: `VALIDATION_ERROR` либо статус `400`/`422`. */\nexport function isItdValidationError(value: unknown): value is ItdValidationError {\n return hasApiKind(value, ItdApiErrorKind.Validation);\n}\n\n/** Ошибка авторизации: истёкший или отозванный токен. */\nexport function isItdAuthError(value: unknown): value is ItdAuthError {\n return hasApiKind(value, ItdApiErrorKind.Auth);\n}\n\n/** Доступ запрещён либо действие ограничено настройками приватности. */\nexport function isItdForbiddenError(value: unknown): value is ItdForbiddenError {\n return hasApiKind(value, ItdApiErrorKind.Forbidden);\n}\n\n/** Сущность не найдена. */\nexport function isItdNotFoundError(value: unknown): value is ItdNotFoundError {\n return hasApiKind(value, ItdApiErrorKind.NotFound);\n}\n\n/** Сущность уже существует. */\nexport function isItdConflictError(value: unknown): value is ItdConflictError {\n return hasApiKind(value, ItdApiErrorKind.Conflict);\n}\n\n/** Превышен лимит запросов. */\nexport function isItdRateLimitError(value: unknown): value is ItdRateLimitError {\n return hasApiKind(value, ItdApiErrorKind.RateLimit);\n}\n\n/** Действие требует подтверждённого телефона. Ссылка — в `verificationUrl`. */\nexport function isItdPhoneVerificationError(value: unknown): value is ItdPhoneVerificationError {\n return hasApiKind(value, ItdApiErrorKind.PhoneVerification);\n}\n\n/** Ошибка на стороне сервера (`5xx`). */\nexport function isItdServerError(value: unknown): value is ItdServerError {\n return hasApiKind(value, ItdApiErrorKind.Server);\n}\n","import { ItdConfigError } from './errors.js';\n\n/**\n * Проверки, общие для резолверов конфигурации и разборщиков ответов.\n *\n * Настройки исполнения и настройки сессии разбираются в разных модулях, но сообщать\n * об ошибке должны одинаково: пользователю всё равно, какой слой отверг его значение.\n */\n\n/** Обычный объект — не `null`, не массив. Сужает тип: для разбора значений неизвестной формы. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Та же проверка без сужения типа: для уже типизированных настроек, где сужение до индексной\n * сигнатуры стёрло бы объявленные поля.\n */\nexport function isObjectLike(value: unknown): boolean {\n return isRecord(value);\n}\n\n/** @throws {ItdConfigError} если значение не является неотрицательным конечным числом */\nexport function requireNonNegative(value: number, name: string): number {\n if (!Number.isFinite(value) || value < 0) {\n throw new ItdConfigError(`${name} должен быть неотрицательным числом, получено: ${value}`);\n }\n return value;\n}\n\n/** @throws {ItdConfigError} если значение задано и не является boolean */\nexport function requireOptionalBoolean(value: unknown, name: string): void {\n if (value !== undefined && typeof value !== 'boolean') {\n throw new ItdConfigError(`${name} должен быть boolean`);\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_errors = require("../shared/errors-BmP3TKoW.cjs");
3
- const require_key_value_store = require("../shared/key-value-store-B5GYVEYZ.cjs");
4
- const require_storage = require("../shared/storage-CZJZoaz9.cjs");
2
+ const require_validate = require("../shared/validate-CFx2dISs.cjs");
3
+ const require_key_value_store = require("../shared/key-value-store-DYSPsqwq.cjs");
4
+ const require_storage = require("../shared/storage-CIjejZDt.cjs");
5
5
  //#region src/web.ts
6
6
  /**
7
7
  * `itd-api/web` — хранилища для браузера.
@@ -52,7 +52,7 @@ var WebStorageKeyValueStore = class {
52
52
  try {
53
53
  return JSON.parse(raw);
54
54
  } catch (error) {
55
- throw new require_errors.ItdConfigError(`Повреждён JSON в ${this.#property} по ключу «${key}»`, { cause: error });
55
+ throw new require_validate.ItdConfigError(`Повреждён JSON в ${this.#property} по ключу «${key}»`, { cause: error });
56
56
  }
57
57
  }
58
58
  set(key, value) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["MemoryKeyValueStore","#property","#storage","#fallback","#degrade","ItdConfigError","#inner","createTokenStorage"],"sources":["../../src/web.ts"],"sourcesContent":["/**\n * `itd-api/web` — хранилища для браузера.\n *\n * Здесь лежит только то, что рассчитано на браузерное окружение. Клиент, аккаунты,\n * билдеры, типы и всё остальное берутся из `itd-api` — одинаково на всех платформах.\n *\n * Отдельная точка входа нужна не сборщику, а вам: браузерные хранилища при\n * недоступном Web Storage молча работают в памяти, и такое поведение стоит выбирать\n * осознанно, а не получать вместе с общим импортом.\n *\n * @example\n * ```ts\n * import { ItdClient } from 'itd-api';\n * import { LocalStorageTokenStorage } from 'itd-api/web';\n *\n * const itd = new ItdClient({ storage: new LocalStorageTokenStorage() });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { ItdConfigError } from './core/errors.js';\nimport { MemoryKeyValueStore } from './core/key-value-store.js';\nimport { createTokenStorage, type ItdSession, type TokenStorage } from './session/storage.js';\n\ntype WebStorageProperty = 'localStorage' | 'sessionStorage';\n\nfunction getWebStorage(property: WebStorageProperty): Storage | undefined {\n try {\n return globalThis[property] ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Общая реализация Web Storage; публичные классы ниже фиксируют его вид. */\nclass WebStorageKeyValueStore<T> {\n #fallback = new MemoryKeyValueStore<T>();\n #storage: Storage | undefined;\n readonly #property: WebStorageProperty;\n\n constructor(property: WebStorageProperty) {\n this.#property = property;\n this.#storage = getWebStorage(property);\n }\n\n get(key: string): T | undefined {\n if (!this.#storage) return this.#fallback.get(key);\n let raw: string | null;\n try {\n raw = this.#storage.getItem(key);\n } catch {\n this.#degrade();\n return this.#fallback.get(key);\n }\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw) as T;\n } catch (error) {\n throw new ItdConfigError(`Повреждён JSON в ${this.#property} по ключу «${key}»`, {\n cause: error,\n });\n }\n }\n\n set(key: string, value: T): void {\n if (this.#storage) {\n try {\n this.#storage.setItem(key, JSON.stringify(value));\n return;\n } catch {\n this.#degrade();\n }\n }\n this.#fallback.set(key, value);\n }\n\n delete(key: string): void {\n if (this.#storage) {\n try {\n this.#storage.removeItem(key);\n return;\n } catch {\n this.#degrade();\n }\n }\n this.#fallback.delete(key);\n }\n\n keys(prefix = ''): Iterable<string> | AsyncIterable<string> {\n if (!this.#storage) return this.#fallback.keys(prefix);\n try {\n const keys: string[] = [];\n for (let index = 0; index < this.#storage.length; index += 1) {\n const key = this.#storage.key(index);\n if (key?.startsWith(prefix)) keys.push(key);\n }\n return keys;\n } catch {\n this.#degrade();\n return this.#fallback.keys(prefix);\n }\n }\n\n #degrade(): void {\n this.#storage = undefined;\n this.#fallback = new MemoryKeyValueStore<T>();\n }\n}\n\n/** JSON key-value backend поверх браузерного `localStorage` с откатом в память. */\nexport class LocalStorageKeyValueStore<T> {\n readonly #inner = new WebStorageKeyValueStore<T>('localStorage');\n\n get(key: string): T | undefined {\n return this.#inner.get(key);\n }\n\n set(key: string, value: T): void {\n this.#inner.set(key, value);\n }\n\n delete(key: string): void {\n this.#inner.delete(key);\n }\n\n keys(prefix = ''): Iterable<string> | AsyncIterable<string> {\n return this.#inner.keys(prefix);\n }\n}\n\n/** JSON key-value backend поверх браузерного `sessionStorage` с откатом в память. */\nexport class SessionStorageKeyValueStore<T> {\n readonly #inner = new WebStorageKeyValueStore<T>('sessionStorage');\n\n get(key: string): T | undefined {\n return this.#inner.get(key);\n }\n\n set(key: string, value: T): void {\n this.#inner.set(key, value);\n }\n\n delete(key: string): void {\n this.#inner.delete(key);\n }\n\n keys(prefix = ''): Iterable<string> | AsyncIterable<string> {\n return this.#inner.keys(prefix);\n }\n}\n\n/**\n * Хранилище поверх `localStorage` браузера.\n *\n * Если `localStorage` недоступен (приватный режим, серверный рендеринг), молча работает\n * как хранилище в памяти — библиотека не должна падать из-за настроек браузера. Сессия\n * в этом случае теряется при перезагрузке страницы; когда это неприемлемо, проверьте\n * доступность сами или возьмите своё хранилище через `createTokenStorage`.\n *\n * Помните, что `localStorage` доступен любому скрипту на странице: не используйте его,\n * если для вашего приложения это неприемлемый риск.\n *\n * Повреждённый JSON считается ошибкой, а не отсутствующей сессией. После ошибки доступа,\n * записи или удаления хранилище переключается на память до конца\n * своего жизненного цикла.\n */\nexport class LocalStorageTokenStorage implements TokenStorage {\n readonly #inner: TokenStorage;\n\n /** @param key ключ в `localStorage`. По умолчанию `itd-api:session`. */\n constructor(key = 'itd-api:session') {\n this.#inner = createTokenStorage(new LocalStorageKeyValueStore<ItdSession>(), { key });\n }\n\n get(): ItdSession | null {\n return this.#inner.get() as ItdSession | null;\n }\n\n set(session: ItdSession): void {\n void this.#inner.set(session);\n }\n\n clear(): void {\n void this.#inner.clear();\n }\n}\n\n/**\n * Хранилище сессии поверх браузерного `sessionStorage`.\n *\n * Данные переживают перезагрузку страницы, но существуют только в пределах текущей\n * browser page session. Если `sessionStorage` недоступен, экземпляр молча работает\n * в памяти; после ошибки доступа, записи или удаления он также остаётся в памяти до конца\n * своего жизненного цикла.\n * Повреждённый JSON считается ошибкой, а не отсутствующей сессией.\n *\n * Как и `localStorage`, это хранилище доступно скриптам страницы и не защищает сессию\n * от XSS. Выбирайте его ради более короткого срока жизни, а не ради изоляции секрета.\n */\nexport class SessionStorageTokenStorage implements TokenStorage {\n readonly #inner: TokenStorage;\n\n /** @param key ключ в `sessionStorage`. По умолчанию `itd-api:session`. */\n constructor(key = 'itd-api:session') {\n this.#inner = createTokenStorage(new SessionStorageKeyValueStore<ItdSession>(), { key });\n }\n\n get(): ItdSession | null {\n return this.#inner.get() as ItdSession | null;\n }\n\n set(session: ItdSession): void {\n void this.#inner.set(session);\n }\n\n clear(): void {\n void this.#inner.clear();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["MemoryKeyValueStore","#property","#storage","#fallback","#degrade","ItdConfigError","#inner","createTokenStorage"],"sources":["../../src/web.ts"],"sourcesContent":["/**\n * `itd-api/web` — хранилища для браузера.\n *\n * Здесь лежит только то, что рассчитано на браузерное окружение. Клиент, аккаунты,\n * билдеры, типы и всё остальное берутся из `itd-api` — одинаково на всех платформах.\n *\n * Отдельная точка входа нужна не сборщику, а вам: браузерные хранилища при\n * недоступном Web Storage молча работают в памяти, и такое поведение стоит выбирать\n * осознанно, а не получать вместе с общим импортом.\n *\n * @example\n * ```ts\n * import { ItdClient } from 'itd-api';\n * import { LocalStorageTokenStorage } from 'itd-api/web';\n *\n * const itd = new ItdClient({ storage: new LocalStorageTokenStorage() });\n * ```\n *\n * @packageDocumentation\n */\n\nimport { ItdConfigError } from './core/errors.js';\nimport { MemoryKeyValueStore } from './core/key-value-store.js';\nimport { createTokenStorage, type ItdSession, type TokenStorage } from './session/storage.js';\n\ntype WebStorageProperty = 'localStorage' | 'sessionStorage';\n\nfunction getWebStorage(property: WebStorageProperty): Storage | undefined {\n try {\n return globalThis[property] ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/** Общая реализация Web Storage; публичные классы ниже фиксируют его вид. */\nclass WebStorageKeyValueStore<T> {\n #fallback = new MemoryKeyValueStore<T>();\n #storage: Storage | undefined;\n readonly #property: WebStorageProperty;\n\n constructor(property: WebStorageProperty) {\n this.#property = property;\n this.#storage = getWebStorage(property);\n }\n\n get(key: string): T | undefined {\n if (!this.#storage) return this.#fallback.get(key);\n let raw: string | null;\n try {\n raw = this.#storage.getItem(key);\n } catch {\n this.#degrade();\n return this.#fallback.get(key);\n }\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw) as T;\n } catch (error) {\n throw new ItdConfigError(`Повреждён JSON в ${this.#property} по ключу «${key}»`, {\n cause: error,\n });\n }\n }\n\n set(key: string, value: T): void {\n if (this.#storage) {\n try {\n this.#storage.setItem(key, JSON.stringify(value));\n return;\n } catch {\n this.#degrade();\n }\n }\n this.#fallback.set(key, value);\n }\n\n delete(key: string): void {\n if (this.#storage) {\n try {\n this.#storage.removeItem(key);\n return;\n } catch {\n this.#degrade();\n }\n }\n this.#fallback.delete(key);\n }\n\n keys(prefix = ''): Iterable<string> | AsyncIterable<string> {\n if (!this.#storage) return this.#fallback.keys(prefix);\n try {\n const keys: string[] = [];\n for (let index = 0; index < this.#storage.length; index += 1) {\n const key = this.#storage.key(index);\n if (key?.startsWith(prefix)) keys.push(key);\n }\n return keys;\n } catch {\n this.#degrade();\n return this.#fallback.keys(prefix);\n }\n }\n\n #degrade(): void {\n this.#storage = undefined;\n this.#fallback = new MemoryKeyValueStore<T>();\n }\n}\n\n/** JSON key-value backend поверх браузерного `localStorage` с откатом в память. */\nexport class LocalStorageKeyValueStore<T> {\n readonly #inner = new WebStorageKeyValueStore<T>('localStorage');\n\n get(key: string): T | undefined {\n return this.#inner.get(key);\n }\n\n set(key: string, value: T): void {\n this.#inner.set(key, value);\n }\n\n delete(key: string): void {\n this.#inner.delete(key);\n }\n\n keys(prefix = ''): Iterable<string> | AsyncIterable<string> {\n return this.#inner.keys(prefix);\n }\n}\n\n/** JSON key-value backend поверх браузерного `sessionStorage` с откатом в память. */\nexport class SessionStorageKeyValueStore<T> {\n readonly #inner = new WebStorageKeyValueStore<T>('sessionStorage');\n\n get(key: string): T | undefined {\n return this.#inner.get(key);\n }\n\n set(key: string, value: T): void {\n this.#inner.set(key, value);\n }\n\n delete(key: string): void {\n this.#inner.delete(key);\n }\n\n keys(prefix = ''): Iterable<string> | AsyncIterable<string> {\n return this.#inner.keys(prefix);\n }\n}\n\n/**\n * Хранилище поверх `localStorage` браузера.\n *\n * Если `localStorage` недоступен (приватный режим, серверный рендеринг), молча работает\n * как хранилище в памяти — библиотека не должна падать из-за настроек браузера. Сессия\n * в этом случае теряется при перезагрузке страницы; когда это неприемлемо, проверьте\n * доступность сами или возьмите своё хранилище через `createTokenStorage`.\n *\n * Помните, что `localStorage` доступен любому скрипту на странице: не используйте его,\n * если для вашего приложения это неприемлемый риск.\n *\n * Повреждённый JSON считается ошибкой, а не отсутствующей сессией. После ошибки доступа,\n * записи или удаления хранилище переключается на память до конца\n * своего жизненного цикла.\n */\nexport class LocalStorageTokenStorage implements TokenStorage {\n readonly #inner: TokenStorage;\n\n /** @param key ключ в `localStorage`. По умолчанию `itd-api:session`. */\n constructor(key = 'itd-api:session') {\n this.#inner = createTokenStorage(new LocalStorageKeyValueStore<ItdSession>(), { key });\n }\n\n get(): ItdSession | null {\n return this.#inner.get() as ItdSession | null;\n }\n\n set(session: ItdSession): void {\n void this.#inner.set(session);\n }\n\n clear(): void {\n void this.#inner.clear();\n }\n}\n\n/**\n * Хранилище сессии поверх браузерного `sessionStorage`.\n *\n * Данные переживают перезагрузку страницы, но существуют только в пределах текущей\n * browser page session. Если `sessionStorage` недоступен, экземпляр молча работает\n * в памяти; после ошибки доступа, записи или удаления он также остаётся в памяти до конца\n * своего жизненного цикла.\n * Повреждённый JSON считается ошибкой, а не отсутствующей сессией.\n *\n * Как и `localStorage`, это хранилище доступно скриптам страницы и не защищает сессию\n * от XSS. Выбирайте его ради более короткого срока жизни, а не ради изоляции секрета.\n */\nexport class SessionStorageTokenStorage implements TokenStorage {\n readonly #inner: TokenStorage;\n\n /** @param key ключ в `sessionStorage`. По умолчанию `itd-api:session`. */\n constructor(key = 'itd-api:session') {\n this.#inner = createTokenStorage(new SessionStorageKeyValueStore<ItdSession>(), { key });\n }\n\n get(): ItdSession | null {\n return this.#inner.get() as ItdSession | null;\n }\n\n set(session: ItdSession): void {\n void this.#inner.set(session);\n }\n\n clear(): void {\n void this.#inner.clear();\n }\n}\n"],"mappings":"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"}
package/dist/web/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { a as ItdConfigError } from "../shared/errors-GI10kZxk.js";
2
- import { t as MemoryKeyValueStore } from "../shared/key-value-store-Bt2PSMxY.js";
3
- import { r as createTokenStorage } from "../shared/storage-DiDe0PZM.js";
1
+ import { l as ItdConfigError } from "../shared/validate-D7_kq3g5.js";
2
+ import { t as MemoryKeyValueStore } from "../shared/key-value-store-w1Dn5Pjb.js";
3
+ import { r as createTokenStorage } from "../shared/storage-86wiHeXD.js";
4
4
  //#region src/web.ts
5
5
  /**
6
6
  * `itd-api/web` — хранилища для браузера.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itd-api",
3
- "version": "0.10.3",
3
+ "version": "0.11.0",
4
4
  "description": "JS/TS SDK & API клиент для соцсети ИТД (итд.com)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors-BmP3TKoW.cjs","names":[],"sources":["../../src/core/errors.ts"],"sourcesContent":["import type { ItdErrorCode } from '../types/enums.js';\n\n/** Бренд, по которому ошибки библиотеки распознаются надёжнее, чем через `instanceof`. */\nconst ITD_ERROR = Symbol.for('itd.error');\n\n/**\n * Категория ошибки. Определяет, какие поля у неё есть.\n *\n * Значение, а не только тип: категорию нужно с чем-то сравнивать в рантайме.\n */\nexport const ItdErrorKind = Object.freeze({\n /** Сервер ответил статусом ≥ 400. */\n Api: 'api',\n /** Запрос не дошёл до сервера. */\n Network: 'network',\n /** Истёк таймаут запроса. */\n Timeout: 'timeout',\n /** Запрос отменён через `AbortSignal`. */\n Abort: 'abort',\n /** Операция невозможна в текущем состоянии объекта. */\n State: 'state',\n /** Не удалось получить или подготовить содержимое вложения. */\n File: 'file',\n /** Некорректная конфигурация или аргументы — обнаружено до обращения к сети. */\n Config: 'config',\n} as const);\nexport type ItdErrorKind = (typeof ItdErrorKind)[keyof typeof ItdErrorKind];\n\n/**\n * Разновидность ошибки API — то же, что класс ошибки, но в виде данных.\n *\n * Существует потому, что `instanceof` подводит, когда в дереве зависимостей оказались\n * две копии пакета или смешаны сборки ESM и CJS: классы тогда разные, хотя ошибка та же.\n * Проверки {@link isItdValidationError} и соседние опираются на это поле, а не на класс.\n */\nexport const ItdApiErrorKind = Object.freeze({\n /** Ни одна из специализаций не подошла. */\n Generic: 'generic',\n Validation: 'validation',\n Auth: 'auth',\n Forbidden: 'forbidden',\n NotFound: 'not_found',\n Conflict: 'conflict',\n RateLimit: 'rate_limit',\n PhoneVerification: 'phone_verification',\n Server: 'server',\n} as const);\nexport type ItdApiErrorKind = (typeof ItdApiErrorKind)[keyof typeof ItdApiErrorKind];\n\n/** Ошибки по полям формы: `{ email: ['уже занят'] }`. */\nexport type ItdFieldErrors = Record<string, string[]>;\n\n/**\n * Базовый класс всех ошибок библиотеки.\n *\n * Ловить его имеет смысл, чтобы отделить проблемы обращения к итд.com от прочих исключений.\n * Для разбора конкретной причины используйте {@link isItdApiError} и поле {@link ItdApiError.code}.\n *\n * @example\n * ```ts\n * try {\n * await itd.posts.like(id);\n * } catch (e) {\n * if (isItdError(e)) console.error('итд.com:', e.message);\n * else throw e;\n * }\n * ```\n */\nexport class ItdError extends Error {\n /** @internal */\n readonly [ITD_ERROR] = true as const;\n\n /** Категория ошибки. */\n readonly kind: ItdErrorKind;\n\n constructor(kind: ItdErrorKind, message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.kind = kind;\n this.name = 'ItdError';\n }\n}\n\n/** Параметры конструктора {@link ItdApiError}. */\nexport interface ItdApiErrorInit {\n /** HTTP-статус ответа. */\n status: number;\n /** Строковый код ошибки из тела ответа. */\n code: ItdErrorCode;\n /** Человекочитаемое сообщение. */\n message: string;\n /** Расширенное описание, если сервер его прислал. */\n detail?: string | undefined;\n /** Заголовок ошибки, если сервер его прислал. */\n title?: string | undefined;\n /** Ошибки по конкретным полям (сведены из `errors` и `violations`). */\n fieldErrors?: ItdFieldErrors | undefined;\n /** Идентификатор запроса из заголовков ответа, если есть. */\n requestId?: string | undefined;\n /** HTTP-метод запроса. */\n method: string;\n /** Путь запроса без базового URL. */\n path: string;\n /** Тело ответа как оно пришло — на случай, если документация разошлась с реальностью. */\n raw: unknown;\n /** Сам объект ответа. Тело уже прочитано. */\n response?: Response | undefined;\n /** Значение `Retry-After` в миллисекундах, если заголовок был. */\n retryAfter?: number | undefined;\n /** Сколько запросов разрешено в окне (`x-ratelimit-limit`). */\n rateLimit?: number | undefined;\n /** Сколько запросов осталось в окне (`x-ratelimit-remaining`). */\n rateLimitRemaining?: number | undefined;\n}\n\n/**\n * Ошибка, возвращённая сервером итд.com (HTTP-статус ≥ 400).\n *\n * API отдаёт ошибки в двух разных формах — `{ error: { … } }` и `{ code, message, violations }`.\n * Библиотека сводит обе к этому классу, поэтому разбирать форму ответа вручную не нужно.\n *\n * @example\n * ```ts\n * try {\n * await itd.users.updateMe({ username: 'занятое_имя' });\n * } catch (e) {\n * if (e instanceof ItdValidationError) {\n * console.log(e.fieldErrors.username); // ['Имя уже занято']\n * }\n * }\n * ```\n */\nexport class ItdApiError extends ItdError {\n /**\n * Разновидность ошибки: та же информация, что и класс, но пригодная для сравнения.\n *\n * Позволяет разбирать ошибку через `switch`, а проверкам вроде {@link isItdAuthError} —\n * работать даже когда в проекте оказались две копии библиотеки.\n */\n readonly apiKind: ItdApiErrorKind;\n /** HTTP-статус ответа. */\n readonly status: number;\n /** Строковый код ошибки, например `VALIDATION_ERROR`. */\n readonly code: ItdErrorCode;\n /** Расширенное описание, если сервер его прислал. */\n readonly detail: string | undefined;\n /** Заголовок ошибки, если сервер его прислал. */\n readonly title: string | undefined;\n /** Ошибки по полям. Пустой объект, если сервер их не прислал. */\n readonly fieldErrors: ItdFieldErrors;\n /** Идентификатор запроса из заголовков ответа. */\n readonly requestId: string | undefined;\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n /** Тело ответа как оно пришло. */\n readonly raw: unknown;\n /** Объект ответа. Тело уже прочитано и повторно прочитано быть не может. */\n readonly response: Response | undefined;\n /** Пауза из заголовка `Retry-After` в миллисекундах. Сервер итд.com его не присылает. */\n readonly retryAfter: number | undefined;\n /**\n * Сколько запросов разрешено в окне — заголовок `x-ratelimit-limit`.\n *\n * Времени сброса окна сервер не сообщает, поэтому точный момент повтора неизвестен.\n */\n readonly rateLimit: number | undefined;\n /** Сколько запросов осталось в окне — заголовок `x-ratelimit-remaining`. */\n readonly rateLimitRemaining: number | undefined;\n\n /**\n * @param apiKind разновидность; подставляется подклассами, снаружи задавать не нужно\n */\n constructor(init: ItdApiErrorInit, apiKind: ItdApiErrorKind = ItdApiErrorKind.Generic) {\n super(ItdErrorKind.Api, init.message);\n this.name = 'ItdApiError';\n this.apiKind = apiKind;\n this.status = init.status;\n this.code = init.code;\n this.detail = init.detail;\n this.title = init.title;\n this.fieldErrors = init.fieldErrors ?? {};\n this.requestId = init.requestId;\n this.method = init.method;\n this.path = init.path;\n this.raw = init.raw;\n this.response = init.response;\n this.retryAfter = init.retryAfter;\n this.rateLimit = init.rateLimit;\n this.rateLimitRemaining = init.rateLimitRemaining;\n }\n\n /**\n * Проверяет код ошибки. Удобнее, чем сравнивать строки вручную.\n *\n * @example\n * ```ts\n * if (err.hasCode('OTP_INVALID', 'MISSING_FLOW_TOKEN')) await restartOtpFlow();\n * ```\n */\n hasCode(...codes: ItdErrorCode[]): boolean {\n return codes.includes(this.code);\n }\n\n /** Имеет ли смысл повторить запрос: `429` и серверные ошибки `5xx`. */\n get isRetryable(): boolean {\n return this.status === 429 || this.status >= 500;\n }\n}\n\n/** `400` / `422` — данные не прошли валидацию. Подробности в {@link ItdApiError.fieldErrors}. */\nexport class ItdValidationError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Validation);\n this.name = 'ItdValidationError';\n }\n}\n\n/** `401` — токен отсутствует, истёк или отозван. */\nexport class ItdAuthError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Auth);\n this.name = 'ItdAuthError';\n }\n}\n\n/** `403` — доступ запрещён либо действие ограничено настройками приватности. */\nexport class ItdForbiddenError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Forbidden);\n this.name = 'ItdForbiddenError';\n }\n}\n\n/** `404` — сущность не найдена. */\nexport class ItdNotFoundError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.NotFound);\n this.name = 'ItdNotFoundError';\n }\n}\n\n/** `409` — сущность уже существует. */\nexport class ItdConflictError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Conflict);\n this.name = 'ItdConflictError';\n }\n}\n\n/**\n * `429` — превышен лимит запросов.\n *\n * Если сервер прислал `Retry-After`, пауза доступна в {@link ItdApiError.retryAfter}\n * (в миллисекундах). При включённых ретраях библиотека выдерживает её автоматически.\n */\nexport class ItdRateLimitError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.RateLimit);\n this.name = 'ItdRateLimitError';\n }\n}\n\n/**\n * Действие требует подтверждённого телефона (`PHONE_VERIFICATION_REQUIRED`).\n *\n * Подтверждение проходит через Telegram-бота: ссылка лежит в {@link verificationUrl}.\n */\nexport class ItdPhoneVerificationError extends ItdApiError {\n /** Ссылка на бота подтверждения, если удалось определить идентификатор пользователя. */\n readonly verificationUrl: string | undefined;\n\n constructor(init: ItdApiErrorInit & { userId?: string | undefined }) {\n super(init, ItdApiErrorKind.PhoneVerification);\n this.name = 'ItdPhoneVerificationError';\n this.verificationUrl = init.userId\n ? `https://t.me/itd_verification_bot?start=${encodeURIComponent(init.userId)}`\n : undefined;\n }\n}\n\n/** `5xx` — ошибка на стороне сервера. */\nexport class ItdServerError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Server);\n this.name = 'ItdServerError';\n }\n}\n\n/** Причина ошибки получения вложения. */\nexport const ItdFileErrorReason = Object.freeze({\n /** Сетевой сбой при получении источника. */\n Network: 'network',\n /** Источник ответил ошибочным HTTP-статусом. */\n Http: 'http',\n /** Источник превысил разрешённый размер. */\n TooLarge: 'too_large',\n /** Среда или источник не предоставили поток. */\n StreamUnavailable: 'stream_unavailable',\n /** Поток источника завершился ошибкой. */\n Read: 'read',\n} as const);\nexport type ItdFileErrorReason = (typeof ItdFileErrorReason)[keyof typeof ItdFileErrorReason];\n\n/** Не удалось получить или прочитать содержимое вложения. */\nexport class ItdFileError extends ItdError {\n readonly reason: ItdFileErrorReason;\n /** Адрес источника без секретных параметров запроса, если файл получался по сети. */\n readonly url: string | undefined;\n /** HTTP-статус источника. */\n readonly status: number | undefined;\n /** Разрешённый размер в байтах. */\n readonly limit: number | undefined;\n /** Обнаруженный размер в байтах. */\n readonly actual: number | undefined;\n /** Имеет ли смысл повторить получение источника. */\n readonly retryable: boolean;\n\n constructor(\n message: string,\n init: {\n reason: ItdFileErrorReason;\n url?: string | undefined;\n status?: number | undefined;\n limit?: number | undefined;\n actual?: number | undefined;\n retryable?: boolean | undefined;\n cause?: unknown;\n },\n ) {\n super(ItdErrorKind.File, message, { cause: init.cause });\n this.name = 'ItdFileError';\n this.reason = init.reason;\n this.url = init.url;\n this.status = init.status;\n this.limit = init.limit;\n this.actual = init.actual;\n this.retryable = init.retryable ?? false;\n }\n}\n\n/** Запрос не дошёл до сервера: DNS, обрыв соединения, отсутствие сети. */\nexport class ItdNetworkError extends ItdError {\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n\n constructor(message: string, init: { method: string; path: string; cause?: unknown }) {\n super(ItdErrorKind.Network, message, { cause: init.cause });\n this.name = 'ItdNetworkError';\n this.method = init.method;\n this.path = init.path;\n }\n}\n\n/** Истёк таймаут запроса, заданный опцией `timeout`. */\nexport class ItdTimeoutError extends ItdError {\n /** Значение таймаута в миллисекундах. */\n readonly timeout: number;\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n\n constructor(init: { timeout: number; method: string; path: string }) {\n super(\n ItdErrorKind.Timeout,\n `Запрос ${init.method} ${init.path} превысил таймаут ${init.timeout} мс`,\n );\n this.name = 'ItdTimeoutError';\n this.timeout = init.timeout;\n this.method = init.method;\n this.path = init.path;\n }\n}\n\n/** Запрос отменён через переданный `AbortSignal`. */\nexport class ItdAbortError extends ItdError {\n constructor(message = 'Запрос отменён', options?: { cause?: unknown }) {\n super(ItdErrorKind.Abort, message, options);\n this.name = 'ItdAbortError';\n }\n}\n\n/**\n * Операция невозможна в текущем состоянии объекта.\n *\n * Например, клиент уже окончательно освобождён через `dispose()` и не может выполнять\n * новые запросы или создавать событийные соединения.\n */\nexport class ItdStateError extends ItdError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(ItdErrorKind.State, message, options);\n this.name = 'ItdStateError';\n }\n}\n\n/**\n * Некорректная конфигурация или аргументы — обнаружено до обращения к сети.\n *\n * Этим же классом сообщают о нарушенных инвариантах билдеры: например, опрос\n * с одним вариантом ответа.\n */\nexport class ItdConfigError extends ItdError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(ItdErrorKind.Config, message, options);\n this.name = 'ItdConfigError';\n }\n}\n\n/** Любая ошибка, порождённая этой библиотекой. */\nexport function isItdError(value: unknown): value is ItdError {\n return typeof value === 'object' && value !== null && ITD_ERROR in value;\n}\n\n/** Ошибка, пришедшая от сервера итд.com (статус ≥ 400). */\nexport function isItdApiError(value: unknown): value is ItdApiError {\n return isItdError(value) && value.kind === ItdErrorKind.Api;\n}\n\n/** Ошибка получения или чтения вложения. */\nexport function isItdFileError(value: unknown): value is ItdFileError {\n return isItdError(value) && value.kind === ItdErrorKind.File;\n}\n\n/** Операция невозможна в текущем состоянии объекта. */\nexport function isItdStateError(value: unknown): value is ItdStateError {\n return isItdError(value) && value.kind === ItdErrorKind.State;\n}\n\n/**\n * Проверяет разновидность ошибки API.\n *\n * Намеренно не `instanceof`: две копии пакета в дереве зависимостей или смешение сборок\n * ESM и CJS дают разные классы для одной и той же ошибки, и проверка по классу молча\n * перестаёт срабатывать. Поле {@link ItdApiError.apiKind} от этого не зависит.\n */\nfunction hasApiKind(value: unknown, kind: ItdApiErrorKind): boolean {\n return isItdApiError(value) && value.apiKind === kind;\n}\n\n/** Ошибка валидации: `VALIDATION_ERROR` либо статус `400`/`422`. */\nexport function isItdValidationError(value: unknown): value is ItdValidationError {\n return hasApiKind(value, ItdApiErrorKind.Validation);\n}\n\n/** Ошибка авторизации: истёкший или отозванный токен. */\nexport function isItdAuthError(value: unknown): value is ItdAuthError {\n return hasApiKind(value, ItdApiErrorKind.Auth);\n}\n\n/** Доступ запрещён либо действие ограничено настройками приватности. */\nexport function isItdForbiddenError(value: unknown): value is ItdForbiddenError {\n return hasApiKind(value, ItdApiErrorKind.Forbidden);\n}\n\n/** Сущность не найдена. */\nexport function isItdNotFoundError(value: unknown): value is ItdNotFoundError {\n return hasApiKind(value, ItdApiErrorKind.NotFound);\n}\n\n/** Сущность уже существует. */\nexport function isItdConflictError(value: unknown): value is ItdConflictError {\n return hasApiKind(value, ItdApiErrorKind.Conflict);\n}\n\n/** Превышен лимит запросов. */\nexport function isItdRateLimitError(value: unknown): value is ItdRateLimitError {\n return hasApiKind(value, ItdApiErrorKind.RateLimit);\n}\n\n/** Действие требует подтверждённого телефона. Ссылка — в `verificationUrl`. */\nexport function isItdPhoneVerificationError(value: unknown): value is ItdPhoneVerificationError {\n return hasApiKind(value, ItdApiErrorKind.PhoneVerification);\n}\n\n/** Ошибка на стороне сервера (`5xx`). */\nexport function isItdServerError(value: unknown): value is ItdServerError {\n return hasApiKind(value, ItdApiErrorKind.Server);\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors-GI10kZxk.js","names":[],"sources":["../../src/core/errors.ts"],"sourcesContent":["import type { ItdErrorCode } from '../types/enums.js';\n\n/** Бренд, по которому ошибки библиотеки распознаются надёжнее, чем через `instanceof`. */\nconst ITD_ERROR = Symbol.for('itd.error');\n\n/**\n * Категория ошибки. Определяет, какие поля у неё есть.\n *\n * Значение, а не только тип: категорию нужно с чем-то сравнивать в рантайме.\n */\nexport const ItdErrorKind = Object.freeze({\n /** Сервер ответил статусом ≥ 400. */\n Api: 'api',\n /** Запрос не дошёл до сервера. */\n Network: 'network',\n /** Истёк таймаут запроса. */\n Timeout: 'timeout',\n /** Запрос отменён через `AbortSignal`. */\n Abort: 'abort',\n /** Операция невозможна в текущем состоянии объекта. */\n State: 'state',\n /** Не удалось получить или подготовить содержимое вложения. */\n File: 'file',\n /** Некорректная конфигурация или аргументы — обнаружено до обращения к сети. */\n Config: 'config',\n} as const);\nexport type ItdErrorKind = (typeof ItdErrorKind)[keyof typeof ItdErrorKind];\n\n/**\n * Разновидность ошибки API — то же, что класс ошибки, но в виде данных.\n *\n * Существует потому, что `instanceof` подводит, когда в дереве зависимостей оказались\n * две копии пакета или смешаны сборки ESM и CJS: классы тогда разные, хотя ошибка та же.\n * Проверки {@link isItdValidationError} и соседние опираются на это поле, а не на класс.\n */\nexport const ItdApiErrorKind = Object.freeze({\n /** Ни одна из специализаций не подошла. */\n Generic: 'generic',\n Validation: 'validation',\n Auth: 'auth',\n Forbidden: 'forbidden',\n NotFound: 'not_found',\n Conflict: 'conflict',\n RateLimit: 'rate_limit',\n PhoneVerification: 'phone_verification',\n Server: 'server',\n} as const);\nexport type ItdApiErrorKind = (typeof ItdApiErrorKind)[keyof typeof ItdApiErrorKind];\n\n/** Ошибки по полям формы: `{ email: ['уже занят'] }`. */\nexport type ItdFieldErrors = Record<string, string[]>;\n\n/**\n * Базовый класс всех ошибок библиотеки.\n *\n * Ловить его имеет смысл, чтобы отделить проблемы обращения к итд.com от прочих исключений.\n * Для разбора конкретной причины используйте {@link isItdApiError} и поле {@link ItdApiError.code}.\n *\n * @example\n * ```ts\n * try {\n * await itd.posts.like(id);\n * } catch (e) {\n * if (isItdError(e)) console.error('итд.com:', e.message);\n * else throw e;\n * }\n * ```\n */\nexport class ItdError extends Error {\n /** @internal */\n readonly [ITD_ERROR] = true as const;\n\n /** Категория ошибки. */\n readonly kind: ItdErrorKind;\n\n constructor(kind: ItdErrorKind, message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.kind = kind;\n this.name = 'ItdError';\n }\n}\n\n/** Параметры конструктора {@link ItdApiError}. */\nexport interface ItdApiErrorInit {\n /** HTTP-статус ответа. */\n status: number;\n /** Строковый код ошибки из тела ответа. */\n code: ItdErrorCode;\n /** Человекочитаемое сообщение. */\n message: string;\n /** Расширенное описание, если сервер его прислал. */\n detail?: string | undefined;\n /** Заголовок ошибки, если сервер его прислал. */\n title?: string | undefined;\n /** Ошибки по конкретным полям (сведены из `errors` и `violations`). */\n fieldErrors?: ItdFieldErrors | undefined;\n /** Идентификатор запроса из заголовков ответа, если есть. */\n requestId?: string | undefined;\n /** HTTP-метод запроса. */\n method: string;\n /** Путь запроса без базового URL. */\n path: string;\n /** Тело ответа как оно пришло — на случай, если документация разошлась с реальностью. */\n raw: unknown;\n /** Сам объект ответа. Тело уже прочитано. */\n response?: Response | undefined;\n /** Значение `Retry-After` в миллисекундах, если заголовок был. */\n retryAfter?: number | undefined;\n /** Сколько запросов разрешено в окне (`x-ratelimit-limit`). */\n rateLimit?: number | undefined;\n /** Сколько запросов осталось в окне (`x-ratelimit-remaining`). */\n rateLimitRemaining?: number | undefined;\n}\n\n/**\n * Ошибка, возвращённая сервером итд.com (HTTP-статус ≥ 400).\n *\n * API отдаёт ошибки в двух разных формах — `{ error: { … } }` и `{ code, message, violations }`.\n * Библиотека сводит обе к этому классу, поэтому разбирать форму ответа вручную не нужно.\n *\n * @example\n * ```ts\n * try {\n * await itd.users.updateMe({ username: 'занятое_имя' });\n * } catch (e) {\n * if (e instanceof ItdValidationError) {\n * console.log(e.fieldErrors.username); // ['Имя уже занято']\n * }\n * }\n * ```\n */\nexport class ItdApiError extends ItdError {\n /**\n * Разновидность ошибки: та же информация, что и класс, но пригодная для сравнения.\n *\n * Позволяет разбирать ошибку через `switch`, а проверкам вроде {@link isItdAuthError} —\n * работать даже когда в проекте оказались две копии библиотеки.\n */\n readonly apiKind: ItdApiErrorKind;\n /** HTTP-статус ответа. */\n readonly status: number;\n /** Строковый код ошибки, например `VALIDATION_ERROR`. */\n readonly code: ItdErrorCode;\n /** Расширенное описание, если сервер его прислал. */\n readonly detail: string | undefined;\n /** Заголовок ошибки, если сервер его прислал. */\n readonly title: string | undefined;\n /** Ошибки по полям. Пустой объект, если сервер их не прислал. */\n readonly fieldErrors: ItdFieldErrors;\n /** Идентификатор запроса из заголовков ответа. */\n readonly requestId: string | undefined;\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n /** Тело ответа как оно пришло. */\n readonly raw: unknown;\n /** Объект ответа. Тело уже прочитано и повторно прочитано быть не может. */\n readonly response: Response | undefined;\n /** Пауза из заголовка `Retry-After` в миллисекундах. Сервер итд.com его не присылает. */\n readonly retryAfter: number | undefined;\n /**\n * Сколько запросов разрешено в окне — заголовок `x-ratelimit-limit`.\n *\n * Времени сброса окна сервер не сообщает, поэтому точный момент повтора неизвестен.\n */\n readonly rateLimit: number | undefined;\n /** Сколько запросов осталось в окне — заголовок `x-ratelimit-remaining`. */\n readonly rateLimitRemaining: number | undefined;\n\n /**\n * @param apiKind разновидность; подставляется подклассами, снаружи задавать не нужно\n */\n constructor(init: ItdApiErrorInit, apiKind: ItdApiErrorKind = ItdApiErrorKind.Generic) {\n super(ItdErrorKind.Api, init.message);\n this.name = 'ItdApiError';\n this.apiKind = apiKind;\n this.status = init.status;\n this.code = init.code;\n this.detail = init.detail;\n this.title = init.title;\n this.fieldErrors = init.fieldErrors ?? {};\n this.requestId = init.requestId;\n this.method = init.method;\n this.path = init.path;\n this.raw = init.raw;\n this.response = init.response;\n this.retryAfter = init.retryAfter;\n this.rateLimit = init.rateLimit;\n this.rateLimitRemaining = init.rateLimitRemaining;\n }\n\n /**\n * Проверяет код ошибки. Удобнее, чем сравнивать строки вручную.\n *\n * @example\n * ```ts\n * if (err.hasCode('OTP_INVALID', 'MISSING_FLOW_TOKEN')) await restartOtpFlow();\n * ```\n */\n hasCode(...codes: ItdErrorCode[]): boolean {\n return codes.includes(this.code);\n }\n\n /** Имеет ли смысл повторить запрос: `429` и серверные ошибки `5xx`. */\n get isRetryable(): boolean {\n return this.status === 429 || this.status >= 500;\n }\n}\n\n/** `400` / `422` — данные не прошли валидацию. Подробности в {@link ItdApiError.fieldErrors}. */\nexport class ItdValidationError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Validation);\n this.name = 'ItdValidationError';\n }\n}\n\n/** `401` — токен отсутствует, истёк или отозван. */\nexport class ItdAuthError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Auth);\n this.name = 'ItdAuthError';\n }\n}\n\n/** `403` — доступ запрещён либо действие ограничено настройками приватности. */\nexport class ItdForbiddenError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Forbidden);\n this.name = 'ItdForbiddenError';\n }\n}\n\n/** `404` — сущность не найдена. */\nexport class ItdNotFoundError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.NotFound);\n this.name = 'ItdNotFoundError';\n }\n}\n\n/** `409` — сущность уже существует. */\nexport class ItdConflictError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Conflict);\n this.name = 'ItdConflictError';\n }\n}\n\n/**\n * `429` — превышен лимит запросов.\n *\n * Если сервер прислал `Retry-After`, пауза доступна в {@link ItdApiError.retryAfter}\n * (в миллисекундах). При включённых ретраях библиотека выдерживает её автоматически.\n */\nexport class ItdRateLimitError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.RateLimit);\n this.name = 'ItdRateLimitError';\n }\n}\n\n/**\n * Действие требует подтверждённого телефона (`PHONE_VERIFICATION_REQUIRED`).\n *\n * Подтверждение проходит через Telegram-бота: ссылка лежит в {@link verificationUrl}.\n */\nexport class ItdPhoneVerificationError extends ItdApiError {\n /** Ссылка на бота подтверждения, если удалось определить идентификатор пользователя. */\n readonly verificationUrl: string | undefined;\n\n constructor(init: ItdApiErrorInit & { userId?: string | undefined }) {\n super(init, ItdApiErrorKind.PhoneVerification);\n this.name = 'ItdPhoneVerificationError';\n this.verificationUrl = init.userId\n ? `https://t.me/itd_verification_bot?start=${encodeURIComponent(init.userId)}`\n : undefined;\n }\n}\n\n/** `5xx` — ошибка на стороне сервера. */\nexport class ItdServerError extends ItdApiError {\n constructor(init: ItdApiErrorInit) {\n super(init, ItdApiErrorKind.Server);\n this.name = 'ItdServerError';\n }\n}\n\n/** Причина ошибки получения вложения. */\nexport const ItdFileErrorReason = Object.freeze({\n /** Сетевой сбой при получении источника. */\n Network: 'network',\n /** Источник ответил ошибочным HTTP-статусом. */\n Http: 'http',\n /** Источник превысил разрешённый размер. */\n TooLarge: 'too_large',\n /** Среда или источник не предоставили поток. */\n StreamUnavailable: 'stream_unavailable',\n /** Поток источника завершился ошибкой. */\n Read: 'read',\n} as const);\nexport type ItdFileErrorReason = (typeof ItdFileErrorReason)[keyof typeof ItdFileErrorReason];\n\n/** Не удалось получить или прочитать содержимое вложения. */\nexport class ItdFileError extends ItdError {\n readonly reason: ItdFileErrorReason;\n /** Адрес источника без секретных параметров запроса, если файл получался по сети. */\n readonly url: string | undefined;\n /** HTTP-статус источника. */\n readonly status: number | undefined;\n /** Разрешённый размер в байтах. */\n readonly limit: number | undefined;\n /** Обнаруженный размер в байтах. */\n readonly actual: number | undefined;\n /** Имеет ли смысл повторить получение источника. */\n readonly retryable: boolean;\n\n constructor(\n message: string,\n init: {\n reason: ItdFileErrorReason;\n url?: string | undefined;\n status?: number | undefined;\n limit?: number | undefined;\n actual?: number | undefined;\n retryable?: boolean | undefined;\n cause?: unknown;\n },\n ) {\n super(ItdErrorKind.File, message, { cause: init.cause });\n this.name = 'ItdFileError';\n this.reason = init.reason;\n this.url = init.url;\n this.status = init.status;\n this.limit = init.limit;\n this.actual = init.actual;\n this.retryable = init.retryable ?? false;\n }\n}\n\n/** Запрос не дошёл до сервера: DNS, обрыв соединения, отсутствие сети. */\nexport class ItdNetworkError extends ItdError {\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n\n constructor(message: string, init: { method: string; path: string; cause?: unknown }) {\n super(ItdErrorKind.Network, message, { cause: init.cause });\n this.name = 'ItdNetworkError';\n this.method = init.method;\n this.path = init.path;\n }\n}\n\n/** Истёк таймаут запроса, заданный опцией `timeout`. */\nexport class ItdTimeoutError extends ItdError {\n /** Значение таймаута в миллисекундах. */\n readonly timeout: number;\n /** HTTP-метод запроса. */\n readonly method: string;\n /** Путь запроса без базового URL. */\n readonly path: string;\n\n constructor(init: { timeout: number; method: string; path: string }) {\n super(\n ItdErrorKind.Timeout,\n `Запрос ${init.method} ${init.path} превысил таймаут ${init.timeout} мс`,\n );\n this.name = 'ItdTimeoutError';\n this.timeout = init.timeout;\n this.method = init.method;\n this.path = init.path;\n }\n}\n\n/** Запрос отменён через переданный `AbortSignal`. */\nexport class ItdAbortError extends ItdError {\n constructor(message = 'Запрос отменён', options?: { cause?: unknown }) {\n super(ItdErrorKind.Abort, message, options);\n this.name = 'ItdAbortError';\n }\n}\n\n/**\n * Операция невозможна в текущем состоянии объекта.\n *\n * Например, клиент уже окончательно освобождён через `dispose()` и не может выполнять\n * новые запросы или создавать событийные соединения.\n */\nexport class ItdStateError extends ItdError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(ItdErrorKind.State, message, options);\n this.name = 'ItdStateError';\n }\n}\n\n/**\n * Некорректная конфигурация или аргументы — обнаружено до обращения к сети.\n *\n * Этим же классом сообщают о нарушенных инвариантах билдеры: например, опрос\n * с одним вариантом ответа.\n */\nexport class ItdConfigError extends ItdError {\n constructor(message: string, options?: { cause?: unknown }) {\n super(ItdErrorKind.Config, message, options);\n this.name = 'ItdConfigError';\n }\n}\n\n/** Любая ошибка, порождённая этой библиотекой. */\nexport function isItdError(value: unknown): value is ItdError {\n return typeof value === 'object' && value !== null && ITD_ERROR in value;\n}\n\n/** Ошибка, пришедшая от сервера итд.com (статус ≥ 400). */\nexport function isItdApiError(value: unknown): value is ItdApiError {\n return isItdError(value) && value.kind === ItdErrorKind.Api;\n}\n\n/** Ошибка получения или чтения вложения. */\nexport function isItdFileError(value: unknown): value is ItdFileError {\n return isItdError(value) && value.kind === ItdErrorKind.File;\n}\n\n/** Операция невозможна в текущем состоянии объекта. */\nexport function isItdStateError(value: unknown): value is ItdStateError {\n return isItdError(value) && value.kind === ItdErrorKind.State;\n}\n\n/**\n * Проверяет разновидность ошибки API.\n *\n * Намеренно не `instanceof`: две копии пакета в дереве зависимостей или смешение сборок\n * ESM и CJS дают разные классы для одной и той же ошибки, и проверка по классу молча\n * перестаёт срабатывать. Поле {@link ItdApiError.apiKind} от этого не зависит.\n */\nfunction hasApiKind(value: unknown, kind: ItdApiErrorKind): boolean {\n return isItdApiError(value) && value.apiKind === kind;\n}\n\n/** Ошибка валидации: `VALIDATION_ERROR` либо статус `400`/`422`. */\nexport function isItdValidationError(value: unknown): value is ItdValidationError {\n return hasApiKind(value, ItdApiErrorKind.Validation);\n}\n\n/** Ошибка авторизации: истёкший или отозванный токен. */\nexport function isItdAuthError(value: unknown): value is ItdAuthError {\n return hasApiKind(value, ItdApiErrorKind.Auth);\n}\n\n/** Доступ запрещён либо действие ограничено настройками приватности. */\nexport function isItdForbiddenError(value: unknown): value is ItdForbiddenError {\n return hasApiKind(value, ItdApiErrorKind.Forbidden);\n}\n\n/** Сущность не найдена. */\nexport function isItdNotFoundError(value: unknown): value is ItdNotFoundError {\n return hasApiKind(value, ItdApiErrorKind.NotFound);\n}\n\n/** Сущность уже существует. */\nexport function isItdConflictError(value: unknown): value is ItdConflictError {\n return hasApiKind(value, ItdApiErrorKind.Conflict);\n}\n\n/** Превышен лимит запросов. */\nexport function isItdRateLimitError(value: unknown): value is ItdRateLimitError {\n return hasApiKind(value, ItdApiErrorKind.RateLimit);\n}\n\n/** Действие требует подтверждённого телефона. Ссылка — в `verificationUrl`. */\nexport function isItdPhoneVerificationError(value: unknown): value is ItdPhoneVerificationError {\n return hasApiKind(value, ItdApiErrorKind.PhoneVerification);\n}\n\n/** Ошибка на стороне сервера (`5xx`). */\nexport function isItdServerError(value: unknown): value is ItdServerError {\n return hasApiKind(value, ItdApiErrorKind.Server);\n}\n"],"mappings":"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"}