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
@@ -1,8 +1,8 @@
1
- const require_url = require("./url-BnO1aWsd.cjs");
2
- const require_errors = require("./errors-BmP3TKoW.cjs");
3
- const require_redact = require("./redact-lAmck1Z6.cjs");
4
- const require_options = require("./options-DjEtbVaf.cjs");
5
- const require_key_value_store = require("./key-value-store-B5GYVEYZ.cjs");
1
+ const require_url = require("./url-DTgkCWv5.cjs");
2
+ const require_validate = require("./validate-CFx2dISs.cjs");
3
+ const require_redact = require("./redact-DoC7lSsp.cjs");
4
+ const require_options = require("./options-5tywTYId.cjs");
5
+ const require_key_value_store = require("./key-value-store-DYSPsqwq.cjs");
6
6
  //#region src/core/feature-catalog.ts
7
7
  /**
8
8
  * Изменяемый слой над каталогом конкретного API.
@@ -16,19 +16,16 @@ var ExtensibleOperationCatalog = class {
16
16
  #base;
17
17
  #operations = /* @__PURE__ */ new Map();
18
18
  #operationOwners = /* @__PURE__ */ new Map();
19
- #bucketOwners = /* @__PURE__ */ new Map();
20
- #bucketLimits;
21
- #bucketOverrides;
19
+ /** Бакеты feature: владелец и ограничения. Таблицы базового каталога не изменяются. */
20
+ #buckets = /* @__PURE__ */ new Map();
22
21
  constructor(base) {
23
22
  this.#base = base;
24
- this.#bucketLimits = { ...base.bucketLimits };
25
- this.#bucketOverrides = { ...base.bucketOverrides };
26
23
  }
27
24
  get bucketLimits() {
28
- return this.#bucketLimits;
25
+ return this.#base.bucketLimits;
29
26
  }
30
27
  get bucketOverrides() {
31
- return this.#bucketOverrides;
28
+ return this.#base.bucketOverrides;
32
29
  }
33
30
  get defaultBucket() {
34
31
  return this.#base.defaultBucket;
@@ -36,41 +33,37 @@ var ExtensibleOperationCatalog = class {
36
33
  definitionOf(id) {
37
34
  return this.#operations.get(id) ?? this.#base.definitionOf(id);
38
35
  }
39
- retrySafetyOf(id) {
40
- return this.#operations.get(id)?.retrySafety ?? this.#base.retrySafetyOf(id);
41
- }
42
- methodOf(id) {
43
- return this.#operations.get(id)?.method ?? this.#base.methodOf(id);
44
- }
45
36
  bucketOf(id) {
46
37
  return this.#operations.get(id)?.bucket ?? this.#base.bucketOf(id);
47
38
  }
48
39
  isKnownBucket(name) {
49
- return this.#bucketOwners.has(name) || this.#base.isKnownBucket(name);
40
+ return this.#buckets.has(name) || this.#base.isKnownBucket(name);
41
+ }
42
+ bucketDefinitionOf(name) {
43
+ return this.#buckets.get(name)?.definition ?? this.#base.bucketDefinitionOf(name);
50
44
  }
51
45
  /** Регистрирует принадлежащий feature бакет и возвращает откат регистрации. */
52
46
  registerBucket(owner, name, definition) {
53
- if (this.#base.isKnownBucket(name) || this.#bucketOwners.has(name)) throw new require_errors.ItdConfigError(`feature «${owner}»: бакет «${name}» уже зарегистрирован`);
54
- this.#bucketOwners.set(name, owner);
55
- if (definition.limit !== void 0) this.#bucketLimits[name] = definition.limit;
56
- if (definition.concurrency !== void 0 || definition.rps !== void 0) this.#bucketOverrides[name] = Object.freeze({
57
- ...definition.concurrency === void 0 ? {} : { concurrency: definition.concurrency },
58
- ...definition.rps === void 0 ? {} : { rps: definition.rps }
47
+ if (this.#base.isKnownBucket(name) || this.#buckets.has(name)) throw new require_validate.ItdConfigError(`feature «${owner}»: бакет «${name}» уже зарегистрирован`);
48
+ this.#buckets.set(name, {
49
+ owner,
50
+ definition: Object.freeze({
51
+ ...definition.limit === void 0 ? {} : { limit: definition.limit },
52
+ ...definition.concurrency === void 0 ? {} : { concurrency: definition.concurrency },
53
+ ...definition.rps === void 0 ? {} : { rps: definition.rps }
54
+ })
59
55
  });
60
56
  return () => {
61
- if (this.#bucketOwners.get(name) !== owner) return;
62
- this.#bucketOwners.delete(name);
63
- delete this.#bucketLimits[name];
64
- delete this.#bucketOverrides[name];
57
+ if (this.#buckets.get(name)?.owner === owner) this.#buckets.delete(name);
65
58
  };
66
59
  }
67
60
  /** Регистрирует операцию и возвращает откат регистрации. */
68
61
  registerOperation(owner, id, definition) {
69
62
  const ownerPrefix = `${owner}.`;
70
- if (!id.startsWith(ownerPrefix) || id.length === ownerPrefix.length) throw new require_errors.ItdConfigError(`feature «${owner}»: операция «${id}» должна находиться в пространстве имён «${owner}»`);
63
+ if (!id.startsWith(ownerPrefix) || id.length === ownerPrefix.length) throw new require_validate.ItdConfigError(`feature «${owner}»: операция «${id}» должна находиться в пространстве имён «${owner}»`);
71
64
  const occupiedBy = this.#operationOwners.get(id);
72
- if (occupiedBy !== void 0) throw new require_errors.ItdConfigError(`feature «${owner}»: операция «${id}» уже зарегистрирована feature «${occupiedBy}»`);
73
- if (this.#base.methodOf(id) !== void 0) throw new require_errors.ItdConfigError(`feature «${owner}»: операция «${id}» уже существует в базовом каталоге`);
65
+ if (occupiedBy !== void 0) throw new require_validate.ItdConfigError(`feature «${owner}»: операция «${id}» уже зарегистрирована feature «${occupiedBy}»`);
66
+ if (this.#base.definitionOf(id) !== void 0) throw new require_validate.ItdConfigError(`feature «${owner}»: операция «${id}» уже существует в базовом каталоге`);
74
67
  this.#operationOwners.set(id, owner);
75
68
  this.#operations.set(id, Object.freeze({ ...definition }));
76
69
  return () => {
@@ -111,17 +104,17 @@ async function fetchFile(target, options, context) {
111
104
  try {
112
105
  requested = new URL(target);
113
106
  } catch {
114
- throw new require_errors.ItdConfigError(`«${require_redact.redactUrl(target)}» не разбирается как адрес`);
107
+ throw new require_validate.ItdConfigError(`«${require_redact.redactUrl(target)}» не разбирается как адрес`);
115
108
  }
116
- if (requested.protocol !== "http:" && requested.protocol !== "https:") throw new require_errors.ItdConfigError(`вложение по адресу поддерживает только http и https, получено: ${requested.protocol}`);
109
+ if (requested.protocol !== "http:" && requested.protocol !== "https:") throw new require_validate.ItdConfigError(`вложение по адресу поддерживает только http и https, получено: ${requested.protocol}`);
117
110
  let response;
118
111
  try {
119
112
  response = await context.fetch(requested, { ...context.signal ? { signal: context.signal } : {} });
120
113
  } catch (error) {
121
114
  if (context.signal?.aborted || error instanceof Error && error.name === "AbortError") throw error;
122
115
  const safeUrl = require_redact.redactUrl(requested.href);
123
- throw new require_errors.ItdFileError(`не удалось получить файл по адресу ${safeUrl}`, {
124
- reason: require_errors.ItdFileErrorReason.Network,
116
+ throw new require_validate.ItdFileError(`не удалось получить файл по адресу ${safeUrl}`, {
117
+ reason: require_validate.ItdFileErrorReason.Network,
125
118
  url: safeUrl,
126
119
  retryable: true,
127
120
  cause: require_redact.redactErrorCause(error)
@@ -131,8 +124,8 @@ async function fetchFile(target, options, context) {
131
124
  if (!response.ok) {
132
125
  await response.body?.cancel().catch(() => {});
133
126
  const safeUrl = require_redact.redactUrl(finalUrl.href);
134
- throw new require_errors.ItdFileError(`источник ${safeUrl} ответил статусом ${response.status}`, {
135
- reason: require_errors.ItdFileErrorReason.Http,
127
+ throw new require_validate.ItdFileError(`источник ${safeUrl} ответил статусом ${response.status}`, {
128
+ reason: require_validate.ItdFileErrorReason.Http,
136
129
  url: safeUrl,
137
130
  status: response.status,
138
131
  retryable: response.status === 408 || response.status === 429 || response.status >= 500
@@ -153,11 +146,11 @@ async function fetchFile(target, options, context) {
153
146
  };
154
147
  }
155
148
  /** Читает ответ с контролем размера до создания итогового `Blob`. */
156
- async function responseBlob(response, url, maxBytes, streamBufferBytes, signal) {
149
+ async function responseBlob(response, url, maxBytes, streamBufferBytes, signal, type) {
157
150
  if (!response.body) {
158
151
  const blob = await response.blob();
159
152
  if (maxBytes !== void 0 && blob.size > maxBytes) throw require_options.fileTooLarge(url, maxBytes, blob.size);
160
- return blob;
153
+ return blob.type === type ? blob : new Blob([blob], { type });
161
154
  }
162
155
  const chunks = [];
163
156
  const reader = require_options.boundedFileStream(response.body, {
@@ -176,17 +169,17 @@ async function responseBlob(response, url, maxBytes, streamBufferBytes, signal)
176
169
  } finally {
177
170
  reader.releaseLock();
178
171
  }
179
- return new Blob(chunks.map((chunk) => Uint8Array.from(chunk).buffer));
172
+ return new Blob(chunks, { type });
180
173
  }
181
174
  /** Скачивает файл целиком с ограничением размера. @internal */
182
175
  async function downloadFile(target, options, context) {
183
176
  const resolved = require_options.resolveFileStreamOptions(options, require_options.DEFAULT_URL_FILE_MAX_BYTES);
184
177
  const { response, url } = await fetchFile(target, options, context);
185
- const blob = await responseBlob(response, url.href, resolved.maxBytes, resolved.streamBufferBytes, context.signal);
186
178
  const contentType = normalizeMimeType(options.contentType ?? response.headers.get("content-type") ?? void 0);
179
+ const file = await responseBlob(response, url.href, resolved.maxBytes, resolved.streamBufferBytes, context.signal, contentType ?? "");
187
180
  const filename = options.filename ?? filenameFromUrl(url);
188
181
  return {
189
- file: new Blob([blob], { type: contentType ?? "" }),
182
+ file,
190
183
  ...filename ? { filename } : {},
191
184
  ...contentType ? { contentType } : {}
192
185
  };
@@ -197,8 +190,8 @@ async function openUrlFile(target, options, context) {
197
190
  const { response, url, size } = await fetchFile(target, options, context);
198
191
  if (!response.body) {
199
192
  const safeUrl = require_redact.redactUrl(url.href);
200
- throw new require_errors.ItdFileError(`источник ${safeUrl} не предоставил потоковое тело`, {
201
- reason: require_errors.ItdFileErrorReason.StreamUnavailable,
193
+ throw new require_validate.ItdFileError(`источник ${safeUrl} не предоставил потоковое тело`, {
194
+ reason: require_validate.ItdFileErrorReason.StreamUnavailable,
202
195
  url: safeUrl
203
196
  });
204
197
  }
@@ -245,7 +238,7 @@ var DefaultFileResolver = class {
245
238
  }
246
239
  async resolve(input, options = {}, context = {}) {
247
240
  const attempt = context.attempt ?? 1;
248
- if (!Number.isInteger(attempt) || attempt < 1) throw new require_errors.ItdConfigError(`attempt должен быть целым числом от 1, получено: ${attempt}`);
241
+ if (!Number.isInteger(attempt) || attempt < 1) throw new require_validate.ItdConfigError(`attempt должен быть целым числом от 1, получено: ${attempt}`);
249
242
  const maxBytes = require_options.optionalBytes(options.maxBytes, "maxBytes");
250
243
  if (context.signal?.aborted) throw context.signal.reason ?? new DOMException("Aborted", "AbortError");
251
244
  const fileContext = {
@@ -261,9 +254,9 @@ var DefaultFileResolver = class {
261
254
  }
262
255
  return source;
263
256
  } catch (error) {
264
- if (error instanceof require_errors.ItdError || context.signal?.aborted) throw error;
265
- throw new require_errors.ItdFileError("не удалось получить содержимое вложения", {
266
- reason: require_errors.ItdFileErrorReason.Read,
257
+ if (error instanceof require_validate.ItdError || context.signal?.aborted) throw error;
258
+ throw new require_validate.ItdFileError("не удалось получить содержимое вложения", {
259
+ reason: require_validate.ItdFileErrorReason.Read,
267
260
  retryable: true,
268
261
  cause: require_redact.redactErrorCause(error)
269
262
  });
@@ -271,8 +264,8 @@ var DefaultFileResolver = class {
271
264
  }
272
265
  async #loadBuffer(input, options, context, maxBytes) {
273
266
  const content = await this.#resolveBuffer(input, context);
274
- if (typeof content !== "object" || content === null || !("file" in content)) throw new require_errors.ItdConfigError("буферный источник должен вернуть объект с полем file");
275
- if (!require_redact.isBlob(content.file) && !(content.file instanceof ArrayBuffer) && !ArrayBuffer.isView(content.file)) throw new require_errors.ItdConfigError("поле file должно содержать Blob, ArrayBuffer или Uint8Array");
267
+ if (typeof content !== "object" || content === null || !("file" in content)) throw new require_validate.ItdConfigError("буферный источник должен вернуть объект с полем file");
268
+ if (!require_redact.isBlob(content.file) && !(content.file instanceof ArrayBuffer) && !ArrayBuffer.isView(content.file)) throw new require_validate.ItdConfigError("поле file должно содержать Blob, ArrayBuffer или Uint8Array");
276
269
  const contentType = normalizeMimeType(options.contentType ?? content.contentType) ?? normalizeMimeType(require_redact.isBlob(content.file) ? content.file.type : void 0);
277
270
  const blob = require_redact.isBlob(content.file) && (!contentType || content.file.type === contentType) ? content.file : new Blob([content.file], { type: contentType ?? "" });
278
271
  if (maxBytes !== void 0 && blob.size > maxBytes) throw require_options.fileTooLarge(void 0, maxBytes, blob.size);
@@ -288,7 +281,7 @@ var DefaultFileResolver = class {
288
281
  const opened = await this.#resolveStream(input, context);
289
282
  if (!opened || !require_options.isReadableByteStream(opened.stream)) {
290
283
  await opened?.close?.();
291
- throw new require_errors.ItdConfigError("потоковый источник должен вернуть { stream: ReadableStream }");
284
+ throw new require_validate.ItdConfigError("потоковый источник должен вернуть { stream: ReadableStream }");
292
285
  }
293
286
  let handedOff = false;
294
287
  const closeOpened = closeOnce(async () => {
@@ -297,7 +290,7 @@ var DefaultFileResolver = class {
297
290
  try {
298
291
  require_options.optionalBytes(opened.size, "size");
299
292
  if (maxBytes !== void 0 && opened.size !== void 0 && opened.size > maxBytes) throw require_options.fileTooLarge(void 0, maxBytes, opened.size);
300
- if (options.requireKnownSize === true && opened.size === void 0) throw new require_errors.ItdConfigError("размер потокового вложения должен быть известен до чтения");
293
+ if (options.requireKnownSize === true && opened.size === void 0) throw new require_validate.ItdConfigError("размер потокового вложения должен быть известен до чтения");
301
294
  const limits = require_options.resolveFileStreamOptions({
302
295
  mode: require_options.FileTransferMode.Stream,
303
296
  ...maxBytes !== void 0 ? { maxBytes } : {},
@@ -345,15 +338,15 @@ var DefaultFileResolver = class {
345
338
  file: input,
346
339
  ...require_redact.isFile(input) ? { filename: input.name } : {}
347
340
  };
348
- if (typeof input !== "object" || input === null) throw new require_errors.ItdConfigError(`вложение задано значением типа ${typeof input}; ожидается бинарное значение или объект { file }, { url }, { load }, { open }. Для файла на диске используйте fromPath('./photo.jpg') из itd-api/node`);
341
+ if (typeof input !== "object" || input === null) throw new require_validate.ItdConfigError(`вложение задано значением типа ${typeof input}; ожидается бинарное значение или объект { file }, { url }, { load }, { open }. Для файла на диске используйте fromPath('./photo.jpg') из itd-api/node`);
349
342
  if ("load" in input) return input.load(context);
350
343
  if ("url" in input) {
351
344
  const { url, ...urlOptions } = input;
352
345
  return downloadFile(url, urlOptions, context);
353
346
  }
354
347
  if ("file" in input) return input;
355
- if ("open" in input) throw new require_errors.ItdConfigError("потоковый источник нельзя использовать как буферный");
356
- throw new require_errors.ItdConfigError("вложение не распознано: ожидается { file }, { url }, { load } или { open }");
348
+ if ("open" in input) throw new require_validate.ItdConfigError("потоковый источник нельзя использовать как буферный");
349
+ throw new require_validate.ItdConfigError("вложение не распознано: ожидается { file }, { url }, { load } или { open }");
357
350
  }
358
351
  #resolveStream(input, context) {
359
352
  if (typeof input === "object" && input !== null && "open" in input) return input.open(context);
@@ -361,7 +354,7 @@ var DefaultFileResolver = class {
361
354
  const { url, ...urlOptions } = input;
362
355
  return openUrlFile(url, urlOptions, context);
363
356
  }
364
- throw new require_errors.ItdConfigError("потоковое вложение должно иметь форму { open } или { url, mode }");
357
+ throw new require_validate.ItdConfigError("потоковое вложение должно иметь форму { open } или { url, mode }");
365
358
  }
366
359
  };
367
360
  /** Создаёт механизм подготовки файлов, привязанный к настроенному `fetch` клиента. @internal */
@@ -387,34 +380,120 @@ var FeatureInstallationTransaction = class {
387
380
  }
388
381
  };
389
382
  function requireName(value, kind) {
390
- if (typeof value !== "string" || value.trim() === "") throw new require_errors.ItdConfigError(`${kind} должен иметь непустое имя`);
383
+ if (typeof value !== "string" || value.trim() === "") throw new require_validate.ItdConfigError(`${kind} должен иметь непустое имя`);
391
384
  return value.trim();
392
385
  }
393
386
  function requireFeatureName(value) {
394
387
  const name = requireName(value, "Feature");
395
- if (!/^[a-z][a-z0-9-]*$/.test(name)) throw new require_errors.ItdConfigError(`имя feature «${name}» должно соответствовать [a-z][a-z0-9-]*`);
388
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) throw new require_validate.ItdConfigError(`имя feature «${name}» должно соответствовать [a-z][a-z0-9-]*`);
396
389
  return name;
397
390
  }
398
391
  function validateBucket(owner, localName, bucket) {
399
- if (typeof bucket !== "object" || bucket === null || Array.isArray(bucket)) throw new require_errors.ItdConfigError(`feature «${owner}»: бакет «${localName}» должен быть объектом`);
400
- if (bucket.limit !== void 0 && (!Number.isFinite(bucket.limit) || bucket.limit <= 0)) throw new require_errors.ItdConfigError(`feature «${owner}»: limit бакета «${localName}» должен быть положительным числом`);
401
- if (bucket.concurrency !== void 0 && (!Number.isInteger(bucket.concurrency) || bucket.concurrency < 1)) throw new require_errors.ItdConfigError(`feature «${owner}»: concurrency бакета «${localName}» должен быть целым числом от 1`);
402
- if (bucket.rps !== void 0 && (!Number.isFinite(bucket.rps) || bucket.rps <= 0)) throw new require_errors.ItdConfigError(`feature «${owner}»: rps бакета «${localName}» должен быть положительным числом`);
392
+ if (typeof bucket !== "object" || bucket === null || Array.isArray(bucket)) throw new require_validate.ItdConfigError(`feature «${owner}»: бакет «${localName}» должен быть объектом`);
393
+ if (bucket.limit !== void 0 && (!Number.isFinite(bucket.limit) || bucket.limit <= 0)) throw new require_validate.ItdConfigError(`feature «${owner}»: limit бакета «${localName}» должен быть положительным числом`);
394
+ if (bucket.concurrency !== void 0 && (!Number.isInteger(bucket.concurrency) || bucket.concurrency < 1)) throw new require_validate.ItdConfigError(`feature «${owner}»: concurrency бакета «${localName}» должен быть целым числом от 1`);
395
+ if (bucket.rps !== void 0 && (!Number.isFinite(bucket.rps) || bucket.rps <= 0)) throw new require_validate.ItdConfigError(`feature «${owner}»: rps бакета «${localName}» должен быть положительным числом`);
403
396
  }
404
397
  function validateFeatureDefinition(feature, isInstalled) {
405
- if (typeof feature !== "object" || feature === null) throw new require_errors.ItdConfigError("feature должен быть объектом");
398
+ if (typeof feature !== "object" || feature === null) throw new require_validate.ItdConfigError("feature должен быть объектом");
406
399
  const name = requireFeatureName(feature.name);
407
- if (isInstalled(name)) throw new require_errors.ItdConfigError(`feature «${name}» уже установлен`);
408
- if (typeof feature.setup !== "function") throw new require_errors.ItdConfigError(`feature «${name}» должен предоставлять setup()`);
409
- if (typeof feature.operations !== "object" || feature.operations === null || Array.isArray(feature.operations)) throw new require_errors.ItdConfigError(`feature «${name}» должен объявлять operations`);
410
- if (feature.services !== void 0 && !Array.isArray(feature.services)) throw new require_errors.ItdConfigError(`feature «${name}»: services должен быть массивом`);
411
- if (feature.buckets !== void 0 && (typeof feature.buckets !== "object" || feature.buckets === null || Array.isArray(feature.buckets))) throw new require_errors.ItdConfigError(`feature «${name}»: buckets должен быть объектом`);
400
+ if (isInstalled(name)) throw new require_validate.ItdConfigError(`feature «${name}» уже установлен`);
401
+ if (typeof feature.setup !== "function") throw new require_validate.ItdConfigError(`feature «${name}» должен предоставлять setup()`);
402
+ if (typeof feature.operations !== "object" || feature.operations === null || Array.isArray(feature.operations)) throw new require_validate.ItdConfigError(`feature «${name}» должен объявлять operations`);
403
+ if (feature.services !== void 0 && !Array.isArray(feature.services)) throw new require_validate.ItdConfigError(`feature «${name}»: services должен быть массивом`);
404
+ if (feature.buckets !== void 0 && (typeof feature.buckets !== "object" || feature.buckets === null || Array.isArray(feature.buckets))) throw new require_validate.ItdConfigError(`feature «${name}»: buckets должен быть объектом`);
412
405
  return name;
413
406
  }
414
407
  function validateFeatureInstallation(name, installation) {
415
- if (typeof installation !== "object" || installation === null || !("api" in installation)) throw new require_errors.ItdConfigError(`setup() feature «${name}» должен вернуть объект с полем api`);
416
- if (installation.close !== void 0 && typeof installation.close !== "function") throw new require_errors.ItdConfigError(`feature «${name}»: close должен быть функцией`);
417
- if (installation.dispose !== void 0 && typeof installation.dispose !== "function") throw new require_errors.ItdConfigError(`feature «${name}»: dispose должен быть функцией`);
408
+ if (typeof installation !== "object" || installation === null || !("api" in installation)) throw new require_validate.ItdConfigError(`setup() feature «${name}» должен вернуть объект с полем api`);
409
+ if (installation.close !== void 0 && typeof installation.close !== "function") throw new require_validate.ItdConfigError(`feature «${name}»: close должен быть функцией`);
410
+ if (installation.dispose !== void 0 && typeof installation.dispose !== "function") throw new require_validate.ItdConfigError(`feature «${name}»: dispose должен быть функцией`);
411
+ }
412
+ /** Пишет ошибку отката в журнал; сбой самого журнала не заменяет ошибку установки. */
413
+ function reportRollbackError(logger, message, error) {
414
+ try {
415
+ logger?.error(message, error);
416
+ } catch {}
417
+ }
418
+ /** Останавливает ресурс, зарегистрированный до публикации модуля, при откате установки. */
419
+ function stopUnpublishedResource(resource, release, featureName, logger) {
420
+ try {
421
+ resource.stop();
422
+ } catch (error) {
423
+ reportRollbackError(logger, `Не удалось остановить ресурс «${resource.kind}» при откате feature «${featureName}»`, error);
424
+ } finally {
425
+ release();
426
+ }
427
+ Promise.resolve().then(() => resource.drain()).catch((error) => {
428
+ reportRollbackError(logger, `Не удалось освободить ресурс «${resource.kind}» при откате feature «${featureName}»`, error);
429
+ });
430
+ }
431
+ /** Собирает {@link FeatureContext} модуля: доступ к клиенту с проверкой, что модуль активен. */
432
+ function createFeatureContext(deps, activation) {
433
+ const { name, controller, services, operations, transaction } = activation;
434
+ const inactive = () => new require_validate.ItdStateError(`feature «${name}» не активен`);
435
+ const assertActive = (action) => {
436
+ deps.assertActive(action);
437
+ if (!activation.committed || controller.signal.aborted) throw inactive();
438
+ };
439
+ const requireService = (serviceName) => {
440
+ if (!services.has(serviceName)) throw new require_validate.ItdConfigError(`feature «${name}» не объявляет сервис «${serviceName}»`);
441
+ };
442
+ return Object.freeze({
443
+ featureName: name,
444
+ baseUrl: deps.baseUrl,
445
+ signal: controller.signal,
446
+ clock: deps.clock,
447
+ logger: deps.logger,
448
+ files: Object.freeze({ resolve: (input, options, context) => {
449
+ try {
450
+ assertActive(`открыть файловый источник feature «${name}»`);
451
+ return deps.files.resolve(input, options, context);
452
+ } catch (error) {
453
+ return Promise.reject(error);
454
+ }
455
+ } }),
456
+ request: (operation, options) => {
457
+ try {
458
+ assertActive(`выполнить операцию feature «${name}»`);
459
+ const resolved = operations.get(operation);
460
+ if (!resolved) throw new require_validate.ItdConfigError(`feature «${name}» не объявляет операцию «${operation}»`);
461
+ const { operationId: _operationId, method: _method, service: _service, baseUrl: _baseUrl, retrySafety: _retrySafety, rateLimitBucket: _rateLimitBucket, ...scoped } = options;
462
+ return deps.http.execute(resolved.contract, {
463
+ ...scoped,
464
+ ...resolved.service === void 0 ? {} : { service: resolved.service }
465
+ });
466
+ } catch (error) {
467
+ return Promise.reject(error);
468
+ }
469
+ },
470
+ serviceBaseUrl: (serviceName) => {
471
+ requireService(serviceName);
472
+ return deps.services.resolveBaseUrl(serviceName);
473
+ },
474
+ connection: (serviceName) => {
475
+ requireService(serviceName);
476
+ return deps.connection(serviceName);
477
+ },
478
+ manage: (resource) => {
479
+ deps.assertActive(`зарегистрировать ресурс feature «${name}»`);
480
+ if (controller.signal.aborted) throw inactive();
481
+ const unregister = deps.manage(resource);
482
+ let registered = true;
483
+ const release = () => {
484
+ if (!registered) return;
485
+ registered = false;
486
+ unregister();
487
+ };
488
+ if (!activation.committed) transaction.add(() => {
489
+ if (!registered) return;
490
+ if (activation.committed) release();
491
+ else stopUnpublishedResource(resource, release, name, deps.logger);
492
+ });
493
+ return release;
494
+ },
495
+ assertActive
496
+ });
418
497
  }
419
498
  /** Реестр подключаемых модулей клиента. @internal */
420
499
  var FeatureRegistry = class {
@@ -430,8 +509,8 @@ var FeatureRegistry = class {
430
509
  for (const service of definitions) {
431
510
  const serviceName = requireName(service?.name, `Сервис feature «${featureName}»`);
432
511
  const owner = this.#serviceOwners.get(serviceName);
433
- if (owner !== void 0) throw new require_errors.ItdConfigError(`feature «${featureName}»: сервис «${serviceName}» уже принадлежит feature «${owner}»`);
434
- if (services.has(serviceName)) throw new require_errors.ItdConfigError(`feature «${featureName}»: сервис «${serviceName}» объявлен повторно`);
512
+ if (owner !== void 0) throw new require_validate.ItdConfigError(`feature «${featureName}»: сервис «${serviceName}» уже принадлежит feature «${owner}»`);
513
+ if (services.has(serviceName)) throw new require_validate.ItdConfigError(`feature «${featureName}»: сервис «${serviceName}» объявлен повторно`);
435
514
  if (!this.#deps.services.has(serviceName)) {
436
515
  this.#deps.services.define(service);
437
516
  transaction.add(() => void this.#deps.services.delete(serviceName));
@@ -457,8 +536,6 @@ var FeatureRegistry = class {
457
536
  validateBucket(featureName, localName, bucket);
458
537
  const globalName = `feature:${featureName}/${localName}`;
459
538
  transaction.add(this.#deps.catalog.registerBucket(featureName, globalName, bucket));
460
- const unregisterQueue = this.#deps.registerBucket(globalName, bucket);
461
- if (unregisterQueue) transaction.add(unregisterQueue);
462
539
  bucketNames.set(localName, globalName);
463
540
  }
464
541
  return bucketNames;
@@ -467,22 +544,16 @@ var FeatureRegistry = class {
467
544
  const operations = /* @__PURE__ */ new Map();
468
545
  for (const [rawLocalName, definition] of Object.entries(definitions)) {
469
546
  const localName = requireName(rawLocalName, `Операция feature «${featureName}»`);
470
- if (typeof definition !== "object" || definition === null) throw new require_errors.ItdConfigError(`feature «${featureName}»: операция «${localName}» должна быть объектом`);
471
- if (![
472
- "GET",
473
- "POST",
474
- "PUT",
475
- "PATCH",
476
- "DELETE"
477
- ].includes(definition.method)) throw new require_errors.ItdConfigError(`feature «${featureName}»: операция «${localName}» содержит неизвестный HTTP-метод`);
478
- if (!Object.values(require_url.RetrySafety).includes(definition.retrySafety)) throw new require_errors.ItdConfigError(`feature «${featureName}»: операция «${localName}» содержит неизвестную retrySafety`);
479
- if (definition.service !== void 0 && typeof definition.service !== "string") throw new require_errors.ItdConfigError(`feature «${featureName}»: service операции «${localName}» должен быть строкой`);
480
- if (definition.bucket !== void 0 && typeof definition.bucket !== "string") throw new require_errors.ItdConfigError(`feature «${featureName}»: bucket операции «${localName}» должен быть строкой`);
481
- if (definition.read !== void 0 && typeof definition.read !== "function") throw new require_errors.ItdConfigError(`feature «${featureName}»: read операции «${localName}» должна быть функцией`);
482
- if (definition.annotations !== void 0 && (typeof definition.annotations !== "object" || definition.annotations === null || Array.isArray(definition.annotations))) throw new require_errors.ItdConfigError(`feature «${featureName}»: annotations операции «${localName}» должны быть объектом`);
483
- if (definition.service !== void 0 && !services.has(definition.service)) throw new require_errors.ItdConfigError(`feature «${featureName}»: операция «${localName}» ссылается на необъявленный сервис «${definition.service}»`);
547
+ if (typeof definition !== "object" || definition === null) throw new require_validate.ItdConfigError(`feature «${featureName}»: операция «${localName}» должна быть объектом`);
548
+ if (!Object.values(require_url.OperationMethod).includes(definition.method)) throw new require_validate.ItdConfigError(`feature «${featureName}»: операция «${localName}» содержит неизвестный HTTP-метод`);
549
+ if (!Object.values(require_url.RetrySafety).includes(definition.retrySafety)) throw new require_validate.ItdConfigError(`feature «${featureName}»: операция «${localName}» содержит неизвестную retrySafety`);
550
+ if (definition.service !== void 0 && typeof definition.service !== "string") throw new require_validate.ItdConfigError(`feature «${featureName}»: service операции «${localName}» должен быть строкой`);
551
+ if (definition.bucket !== void 0 && typeof definition.bucket !== "string") throw new require_validate.ItdConfigError(`feature «${featureName}»: bucket операции «${localName}» должен быть строкой`);
552
+ if (definition.read !== void 0 && typeof definition.read !== "function") throw new require_validate.ItdConfigError(`feature «${featureName}»: read операции «${localName}» должна быть функцией`);
553
+ if (definition.annotations !== void 0 && (typeof definition.annotations !== "object" || definition.annotations === null || Array.isArray(definition.annotations))) throw new require_validate.ItdConfigError(`feature «${featureName}»: annotations операции «${localName}» должны быть объектом`);
554
+ if (definition.service !== void 0 && !services.has(definition.service)) throw new require_validate.ItdConfigError(`feature «${featureName}»: операция «${localName}» ссылается на необъявленный сервис «${definition.service}»`);
484
555
  const bucket = definition.bucket === void 0 ? this.#deps.catalog.defaultBucket : bucketNames.get(definition.bucket);
485
- if (bucket === void 0) throw new require_errors.ItdConfigError(`feature «${featureName}»: операция «${localName}» ссылается на необъявленный бакет «${definition.bucket}»`);
556
+ if (bucket === void 0) throw new require_validate.ItdConfigError(`feature «${featureName}»: операция «${localName}» ссылается на необъявленный бакет «${definition.bucket}»`);
486
557
  const operationId = `${featureName}.${localName}`;
487
558
  const contract = require_url.defineOperation(operationId, {
488
559
  method: definition.method,
@@ -505,99 +576,22 @@ var FeatureRegistry = class {
505
576
  }
506
577
  install(feature) {
507
578
  this.#deps.assertActive("установить feature");
508
- if (this.#disposed) throw new require_errors.ItdStateError("Реестр feature уже освобождён");
579
+ if (this.#disposed) throw new require_validate.ItdStateError("Реестр feature уже освобождён");
509
580
  const name = validateFeatureDefinition(feature, (candidate) => this.#features.has(candidate));
510
581
  const transaction = new FeatureInstallationTransaction();
511
582
  const controller = new AbortController();
512
- let committed = false;
513
- const reportRollbackError = (message, error) => {
514
- try {
515
- this.#deps.logger?.error(message, error);
516
- } catch {}
517
- };
518
583
  try {
519
584
  const services = this.#registerServices(name, feature.services ?? [], transaction);
520
585
  const bucketNames = this.#registerBuckets(name, feature.buckets ?? {}, transaction);
521
- const operations = this.#registerOperations(name, feature.operations, services, bucketNames, transaction);
522
- const context = Object.freeze({
523
- featureName: name,
524
- baseUrl: this.#deps.baseUrl,
525
- signal: controller.signal,
526
- clock: this.#deps.clock,
527
- logger: this.#deps.logger,
528
- files: Object.freeze({ resolve: (input, options, context) => {
529
- try {
530
- this.#deps.assertActive(`открыть файловый источник feature «${name}»`);
531
- if (!committed || controller.signal.aborted) throw new require_errors.ItdStateError(`feature «${name}» не активен`);
532
- return this.#deps.files.resolve(input, options, context);
533
- } catch (error) {
534
- return Promise.reject(error);
535
- }
536
- } }),
537
- request: (operation, options) => {
538
- try {
539
- this.#deps.assertActive(`выполнить операцию feature «${name}»`);
540
- if (!committed || controller.signal.aborted) throw new require_errors.ItdStateError(`feature «${name}» не активен`);
541
- const resolved = operations.get(operation);
542
- if (!resolved) throw new require_errors.ItdConfigError(`feature «${name}» не объявляет операцию «${operation}»`);
543
- const scoped = { ...options };
544
- delete scoped.operationId;
545
- delete scoped.method;
546
- delete scoped.service;
547
- delete scoped.baseUrl;
548
- delete scoped.retrySafety;
549
- delete scoped.rateLimitBucket;
550
- return this.#deps.http.execute(resolved.contract, {
551
- ...scoped,
552
- ...resolved.service === void 0 ? {} : { service: resolved.service }
553
- });
554
- } catch (error) {
555
- return Promise.reject(error);
556
- }
557
- },
558
- serviceBaseUrl: (serviceName) => {
559
- if (!services.has(serviceName)) throw new require_errors.ItdConfigError(`feature «${name}» не объявляет сервис «${serviceName}»`);
560
- return this.#deps.services.resolveBaseUrl(serviceName);
561
- },
562
- connection: (serviceName) => {
563
- if (!services.has(serviceName)) throw new require_errors.ItdConfigError(`feature «${name}» не объявляет сервис «${serviceName}»`);
564
- return this.#deps.connection(serviceName);
565
- },
566
- manage: (resource) => {
567
- this.#deps.assertActive(`зарегистрировать ресурс feature «${name}»`);
568
- if (controller.signal.aborted) throw new require_errors.ItdStateError(`feature «${name}» не активен`);
569
- const unregister = this.#deps.manage(resource);
570
- let registered = true;
571
- const release = () => {
572
- if (!registered) return;
573
- registered = false;
574
- unregister();
575
- };
576
- if (!committed) transaction.add(() => {
577
- if (!registered) return;
578
- if (committed) {
579
- release();
580
- return;
581
- }
582
- try {
583
- resource.stop();
584
- } catch (error) {
585
- reportRollbackError(`Не удалось остановить ресурс «${resource.kind}» при откате feature «${name}»`, error);
586
- } finally {
587
- release();
588
- }
589
- Promise.resolve().then(() => resource.drain()).catch((error) => {
590
- reportRollbackError(`Не удалось освободить ресурс «${resource.kind}» при откате feature «${name}»`, error);
591
- });
592
- });
593
- return release;
594
- },
595
- assertActive: (action) => {
596
- this.#deps.assertActive(action);
597
- if (!committed || controller.signal.aborted) throw new require_errors.ItdStateError(`feature «${name}» не активен`);
598
- }
599
- });
600
- const installation = feature.setup(context);
586
+ const activation = {
587
+ name,
588
+ controller,
589
+ services,
590
+ operations: this.#registerOperations(name, feature.operations, services, bucketNames, transaction),
591
+ transaction,
592
+ committed: false
593
+ };
594
+ const installation = feature.setup(createFeatureContext(this.#deps, activation));
601
595
  validateFeatureInstallation(name, installation);
602
596
  this.#features.set(name, {
603
597
  name,
@@ -606,12 +600,12 @@ var FeatureRegistry = class {
606
600
  dispose: installation.dispose,
607
601
  cleanup: transaction.snapshot()
608
602
  });
609
- committed = true;
603
+ activation.committed = true;
610
604
  return installation.api;
611
605
  } catch (error) {
612
- controller.abort(new require_errors.ItdAbortError(`Установка feature «${name}» отменена`));
606
+ controller.abort(new require_validate.ItdAbortError(`Установка feature «${name}» отменена`));
613
607
  transaction.rollback((rollbackError) => {
614
- reportRollbackError(`Не удалось полностью откатить feature «${name}»`, rollbackError);
608
+ reportRollbackError(this.#deps.logger, `Не удалось полностью откатить feature «${name}»`, rollbackError);
615
609
  });
616
610
  throw error;
617
611
  }
@@ -633,7 +627,7 @@ var FeatureRegistry = class {
633
627
  const disposals = [];
634
628
  const features = [...this.#features.values()].reverse();
635
629
  for (const feature of features) {
636
- feature.controller.abort(new require_errors.ItdAbortError(`Feature «${feature.name}» освобождён`));
630
+ feature.controller.abort(new require_validate.ItdAbortError(`Feature «${feature.name}» освобождён`));
637
631
  try {
638
632
  disposals.push(Promise.resolve(feature.dispose?.()).then(() => void 0));
639
633
  } catch (error) {
@@ -658,8 +652,8 @@ var ManagedResourceRegistry = class {
658
652
  #resources = /* @__PURE__ */ new Set();
659
653
  #disposed = false;
660
654
  register(resource) {
661
- if (this.#disposed) throw new require_errors.ItdStateError("Lifecycle клиента уже освобождён");
662
- if (typeof resource !== "object" || resource === null || typeof resource.kind !== "string" || resource.kind.trim() === "" || typeof resource.stop !== "function" || typeof resource.drain !== "function") throw new require_errors.ItdConfigError("Managed resource должен предоставлять kind, stop() и drain()");
655
+ if (this.#disposed) throw new require_validate.ItdStateError("Lifecycle клиента уже освобождён");
656
+ if (typeof resource !== "object" || resource === null || typeof resource.kind !== "string" || resource.kind.trim() === "" || typeof resource.stop !== "function" || typeof resource.drain !== "function") throw new require_validate.ItdConfigError("Managed resource должен предоставлять kind, stop() и drain()");
663
657
  this.#resources.add(resource);
664
658
  let registered = true;
665
659
  return () => {
@@ -714,8 +708,7 @@ var ClientFeatureHost = class {
714
708
  files: this.files,
715
709
  assertActive: options.assertActive,
716
710
  connection: (serviceName) => runtime.connection(serviceName),
717
- manage: (resource) => this.#resources.register(resource),
718
- registerBucket: (name, definition) => runtime.registerRateLimitBucket(name, definition)
711
+ manage: (resource) => this.#resources.register(resource)
719
712
  });
720
713
  }
721
714
  install(feature) {
@@ -755,7 +748,7 @@ var ClientFeatureHost = class {
755
748
  });
756
749
  return await deadline.wait(result) ? void 0 : kind;
757
750
  }))).filter((kind) => kind !== void 0);
758
- if (stuck.length > 0) errors.push(new require_errors.ItdStateError(`ресурсы (${stuck.join(", ")}) не завершились за ${this.#runtime.config.shutdownTimeout} мс`));
751
+ if (stuck.length > 0) errors.push(new require_validate.ItdStateError(`ресурсы (${stuck.join(", ")}) не завершились за ${this.#runtime.config.shutdownTimeout} мс`));
759
752
  } finally {
760
753
  deadline.cancel();
761
754
  }
@@ -772,7 +765,7 @@ var ClientFeatureHost = class {
772
765
  failure = error;
773
766
  });
774
767
  try {
775
- if (!await deadline.wait(settled)) throw new require_errors.ItdStateError(`освобождение подключаемых модулей не завершилось за ${this.#runtime.config.shutdownTimeout} мс`);
768
+ if (!await deadline.wait(settled)) throw new require_validate.ItdStateError(`освобождение подключаемых модулей не завершилось за ${this.#runtime.config.shutdownTimeout} мс`);
776
769
  } finally {
777
770
  deadline.cancel();
778
771
  }
@@ -940,7 +933,7 @@ var Paginator = class {
940
933
  #finished = false;
941
934
  #pagesLoaded = 0;
942
935
  constructor(options) {
943
- if (options.maxPages !== void 0 && (!Number.isInteger(options.maxPages) || options.maxPages < 1)) throw new require_errors.ItdConfigError(`maxPages должен быть целым числом не меньше 1, получено: ${options.maxPages}`);
936
+ if (options.maxPages !== void 0 && (!Number.isInteger(options.maxPages) || options.maxPages < 1)) throw new require_validate.ItdConfigError(`maxPages должен быть целым числом не меньше 1, получено: ${options.maxPages}`);
944
937
  this.#options = options;
945
938
  this.#maxPages = options.maxPages ?? 1e3;
946
939
  this.#state = options.start ?? {};
@@ -1293,7 +1286,7 @@ function toDate(value) {
1293
1286
  //#region src/resources/status.ts
1294
1287
  /** Приводит `last_checked` каждого сервиса к ISO. Остальное остаётся как прислал сервер. */
1295
1288
  function normalizeStatus(body) {
1296
- if (!require_url.isRecord(body) || !Array.isArray(body.services)) return body;
1289
+ if (!require_validate.isRecord(body) || !Array.isArray(body.services)) return body;
1297
1290
  return {
1298
1291
  ...body,
1299
1292
  services: body.services.map((service) => typeof service?.last_checked === "string" ? {
@@ -1372,14 +1365,14 @@ function validateComment(input) {
1372
1365
  const files = input?.files ?? [];
1373
1366
  const hasContent = content.trim() !== "";
1374
1367
  const hasAttachments = attachmentIds.length > 0 || files.length > 0;
1375
- if (!hasContent && !hasAttachments) throw new require_errors.ItdConfigError("Комментарий пуст: нужен текст или вложение");
1368
+ if (!hasContent && !hasAttachments) throw new require_validate.ItdConfigError("Комментарий пуст: нужен текст или вложение");
1376
1369
  return input;
1377
1370
  }
1378
1371
  /** Дополнительная проверка голосового комментария. */
1379
1372
  function validateVoice(state) {
1380
- if (state.content.trim() !== "") throw new require_errors.ItdConfigError("У голосового комментария не может быть текста: API принимает либо текст, либо аудио");
1373
+ if (state.content.trim() !== "") throw new require_validate.ItdConfigError("У голосового комментария не может быть текста: API принимает либо текст, либо аудио");
1381
1374
  const total = state.files.length + state.attachmentIds.length;
1382
- if (total !== 1) throw new require_errors.ItdConfigError(`Голосовой комментарий требует ровно одно аудиовложение, передано: ${total}`);
1375
+ if (total !== 1) throw new require_validate.ItdConfigError(`Голосовой комментарий требует ровно одно аудиовложение, передано: ${total}`);
1383
1376
  }
1384
1377
  /**
1385
1378
  * Билдер комментария и ответа на комментарий.
@@ -1488,7 +1481,7 @@ function comment(content = "") {
1488
1481
  */
1489
1482
  function resolveComment(input, allowReplyTo = false) {
1490
1483
  const resolved = resolveInput(input, () => comment(), validateComment);
1491
- if (!allowReplyTo && resolved.replyToUserId !== void 0) throw new require_errors.ItdConfigError("replyTo применим только к ответу на комментарий (itd.comments.reply). В комментарии к посту адресат не указывается.");
1484
+ if (!allowReplyTo && resolved.replyToUserId !== void 0) throw new require_validate.ItdConfigError("replyTo применим только к ответу на комментарий (itd.comments.reply). В комментарии к посту адресат не указывается.");
1492
1485
  return resolved;
1493
1486
  }
1494
1487
  //#endregion
@@ -1733,8 +1726,8 @@ function isAllowedMime(mimeType) {
1733
1726
  * @throws {ItdConfigError} если тип не поддерживается
1734
1727
  */
1735
1728
  function assertAllowedMime(mimeType, filename) {
1736
- if (!mimeType) throw new require_errors.ItdConfigError(`Не удалось определить тип файла${filename ? ` «${filename}»` : ""}. Укажите его явно: { file, filename, contentType }.`);
1737
- if (!isAllowedMime(mimeType)) throw new require_errors.ItdConfigError(`Тип «${mimeType}» не поддерживается загрузкой. Допустимые: ${ALLOWED_MIME_TYPES.join(", ")}.`);
1729
+ if (!mimeType) throw new require_validate.ItdConfigError(`Не удалось определить тип файла${filename ? ` «${filename}»` : ""}. Укажите его явно: { file, filename, contentType }.`);
1730
+ if (!isAllowedMime(mimeType)) throw new require_validate.ItdConfigError(`Тип «${mimeType}» не поддерживается загрузкой. Допустимые: ${ALLOWED_MIME_TYPES.join(", ")}.`);
1738
1731
  }
1739
1732
  //#endregion
1740
1733
  //#region src/resources/files.ts
@@ -1982,12 +1975,12 @@ var PlatformResource = class extends BaseResource {
1982
1975
  * Возвращает копии, чтобы последующая мутация входного массива не меняла уже собранные данные.
1983
1976
  */
1984
1977
  function validateSpans(content, spans) {
1985
- if (!Array.isArray(spans)) throw new require_errors.ItdConfigError("spans должен быть массивом");
1986
- if (content.length === 0 && spans.length > 0) throw new require_errors.ItdConfigError("Нельзя задать spans без текста: сначала укажите непустой content");
1978
+ if (!Array.isArray(spans)) throw new require_validate.ItdConfigError("spans должен быть массивом");
1979
+ if (content.length === 0 && spans.length > 0) throw new require_validate.ItdConfigError("Нельзя задать spans без текста: сначала укажите непустой content");
1987
1980
  return spans.map((span, index) => {
1988
- if (!span || typeof span !== "object") throw new require_errors.ItdConfigError(`Некорректный span №${index + 1}: ожидался объект`);
1981
+ if (!span || typeof span !== "object") throw new require_validate.ItdConfigError(`Некорректный span №${index + 1}: ожидался объект`);
1989
1982
  const candidate = span;
1990
- if (typeof candidate.type !== "string" || !Number.isInteger(candidate.offset) || !Number.isInteger(candidate.length) || (candidate.offset ?? -1) < 0 || (candidate.length ?? 0) <= 0 || (candidate.offset ?? 0) + (candidate.length ?? 0) > content.length) throw new require_errors.ItdConfigError(`Некорректный span №${index + 1}: offset и length должны указывать на непустой фрагмент content`);
1983
+ if (typeof candidate.type !== "string" || !Number.isInteger(candidate.offset) || !Number.isInteger(candidate.length) || (candidate.offset ?? -1) < 0 || (candidate.length ?? 0) <= 0 || (candidate.offset ?? 0) + (candidate.length ?? 0) > content.length) throw new require_validate.ItdConfigError(`Некорректный span №${index + 1}: offset и length должны указывать на непустой фрагмент content`);
1991
1984
  return { ...candidate };
1992
1985
  });
1993
1986
  }
@@ -2459,7 +2452,7 @@ function parseHtml(source, options = {}) {
2459
2452
  //#endregion
2460
2453
  //#region src/builders/markup.ts
2461
2454
  function validateMarkup(value) {
2462
- if (!value || typeof value.content !== "string" || !Array.isArray(value.spans)) throw new require_errors.ItdConfigError("Разметка должна содержать строку content и массив spans");
2455
+ if (!value || typeof value.content !== "string" || !Array.isArray(value.spans)) throw new require_validate.ItdConfigError("Разметка должна содержать строку content и массив spans");
2463
2456
  return {
2464
2457
  content: value.content,
2465
2458
  spans: validateSpans(value.content, value.spans).sort((left, right) => left.offset - right.offset || right.length - left.length)
@@ -2467,7 +2460,7 @@ function validateMarkup(value) {
2467
2460
  }
2468
2461
  function entityName(value, prefix, label) {
2469
2462
  const name = value.startsWith(prefix) ? value.slice(prefix.length) : value;
2470
- if (name === "" || /\s/u.test(name)) throw new require_errors.ItdConfigError(`${label} должен быть непустым и не содержать пробелов`);
2463
+ if (name === "" || /\s/u.test(name)) throw new require_validate.ItdConfigError(`${label} должен быть непустым и не содержать пробелов`);
2471
2464
  return name;
2472
2465
  }
2473
2466
  /**
@@ -2492,7 +2485,7 @@ var MarkupBuilder = class MarkupBuilder {
2492
2485
  }
2493
2486
  /** Дописывает переводы строк. */
2494
2487
  newline(count = 1) {
2495
- if (!Number.isInteger(count) || count < 0) throw new require_errors.ItdConfigError("Количество переводов строк должно быть целым неотрицательным");
2488
+ if (!Number.isInteger(count) || count < 0) throw new require_validate.ItdConfigError("Количество переводов строк должно быть целым неотрицательным");
2496
2489
  return this.text("\n".repeat(count));
2497
2490
  }
2498
2491
  /** @internal Добавляет готовый фрагмент и сдвигает его spans к текущему концу текста. */
@@ -2501,7 +2494,7 @@ var MarkupBuilder = class MarkupBuilder {
2501
2494
  content: value,
2502
2495
  spans: []
2503
2496
  } : resolveMarkup(value);
2504
- if (fragment.content.length === 0) throw new require_errors.ItdConfigError("Размечаемый фрагмент не может быть пустым");
2497
+ if (fragment.content.length === 0) throw new require_validate.ItdConfigError("Размечаемый фрагмент не может быть пустым");
2505
2498
  const offset = this.#content.length;
2506
2499
  const shifted = fragment.spans.map((span) => ({
2507
2500
  ...span,
@@ -2561,7 +2554,7 @@ var MarkupBuilder = class MarkupBuilder {
2561
2554
  */
2562
2555
  link(content, url) {
2563
2556
  const resolvedUrl = url ?? (typeof content === "string" ? content : void 0);
2564
- if (resolvedUrl === void 0) throw new require_errors.ItdConfigError("Для форматированного текста ссылки нужно явно передать URL");
2557
+ if (resolvedUrl === void 0) throw new require_validate.ItdConfigError("Для форматированного текста ссылки нужно явно передать URL");
2565
2558
  return this.span(content, {
2566
2559
  type: require_url.SpanType.Link,
2567
2560
  url: resolvedUrl
@@ -2707,19 +2700,19 @@ const MAX_OPTION_LENGTH = 100;
2707
2700
  */
2708
2701
  function validatePoll(input) {
2709
2702
  const question = input?.question;
2710
- if (typeof question !== "string" || question.trim() === "") throw new require_errors.ItdConfigError("Опрос требует непустого вопроса");
2711
- if (question.trim().length > MAX_QUESTION_LENGTH) throw new require_errors.ItdConfigError(`Вопрос длиннее ${MAX_QUESTION_LENGTH} символов (передано ${question.trim().length})`);
2703
+ if (typeof question !== "string" || question.trim() === "") throw new require_validate.ItdConfigError("Опрос требует непустого вопроса");
2704
+ if (question.trim().length > MAX_QUESTION_LENGTH) throw new require_validate.ItdConfigError(`Вопрос длиннее ${MAX_QUESTION_LENGTH} символов (передано ${question.trim().length})`);
2712
2705
  const texts = (Array.isArray(input.options) ? input.options : []).map((option, index) => {
2713
2706
  const text = typeof option?.text === "string" ? option.text.trim() : "";
2714
- if (text === "") throw new require_errors.ItdConfigError(`Вариант ответа №${index + 1} пуст — у каждого должен быть текст`);
2715
- if (text.length > MAX_OPTION_LENGTH) throw new require_errors.ItdConfigError(`Вариант ответа №${index + 1} длиннее ${MAX_OPTION_LENGTH} символов (передано ${text.length})`);
2707
+ if (text === "") throw new require_validate.ItdConfigError(`Вариант ответа №${index + 1} пуст — у каждого должен быть текст`);
2708
+ if (text.length > MAX_OPTION_LENGTH) throw new require_validate.ItdConfigError(`Вариант ответа №${index + 1} длиннее ${MAX_OPTION_LENGTH} символов (передано ${text.length})`);
2716
2709
  return text;
2717
2710
  });
2718
- if (texts.length < MIN_OPTIONS) throw new require_errors.ItdConfigError(`Опрос требует минимум ${MIN_OPTIONS} варианта, передано: ${texts.length}`);
2719
- if (texts.length > MAX_OPTIONS) throw new require_errors.ItdConfigError(`Опрос допускает не больше ${MAX_OPTIONS} вариантов, передано: ${texts.length}`);
2711
+ if (texts.length < MIN_OPTIONS) throw new require_validate.ItdConfigError(`Опрос требует минимум ${MIN_OPTIONS} варианта, передано: ${texts.length}`);
2712
+ if (texts.length > MAX_OPTIONS) throw new require_validate.ItdConfigError(`Опрос допускает не больше ${MAX_OPTIONS} вариантов, передано: ${texts.length}`);
2720
2713
  const seen = /* @__PURE__ */ new Set();
2721
2714
  for (const text of texts) {
2722
- if (seen.has(text)) throw new require_errors.ItdConfigError(`Вариант «${text}» повторяется — варианты должны различаться`);
2715
+ if (seen.has(text)) throw new require_validate.ItdConfigError(`Вариант «${text}» повторяется — варианты должны различаться`);
2723
2716
  seen.add(text);
2724
2717
  }
2725
2718
  return {
@@ -2831,10 +2824,10 @@ function validatePost(input) {
2831
2824
  const hasContent = content.trim() !== "";
2832
2825
  const hasAttachments = attachmentIds.length > 0 || files.length > 0;
2833
2826
  const hasPoll = Boolean(input?.poll);
2834
- if (!hasContent && !hasAttachments && !hasPoll) throw new require_errors.ItdConfigError("Пост пуст: нужен текст, вложение или опрос");
2827
+ if (!hasContent && !hasAttachments && !hasPoll) throw new require_validate.ItdConfigError("Пост пуст: нужен текст, вложение или опрос");
2835
2828
  const wallRecipientId = input.wallRecipientId;
2836
2829
  if (wallRecipientId !== void 0 && wallRecipientId !== null) {
2837
- if (!UUID_PATTERN.test(wallRecipientId)) throw new require_errors.ItdConfigError(`wallRecipientId должен быть UUID, а не именем пользователя (получено: «${wallRecipientId}»). Идентификатор можно взять из профиля: (await itd.users.get(username)).id`);
2830
+ if (!UUID_PATTERN.test(wallRecipientId)) throw new require_validate.ItdConfigError(`wallRecipientId должен быть UUID, а не именем пользователя (получено: «${wallRecipientId}»). Идентификатор можно взять из профиля: (await itd.users.get(username)).id`);
2838
2831
  }
2839
2832
  const { poll: inputPoll, ...data } = input;
2840
2833
  return {
@@ -3041,17 +3034,17 @@ function resolvePost(input) {
3041
3034
  return isBuilder(resolved) ? validatePost(resolved.build()) : validatePost(resolved);
3042
3035
  }
3043
3036
  function validatePostUpdate(input) {
3044
- if (!input || typeof input !== "object") throw new require_errors.ItdConfigError("Для обновления поста нужен объект с явно заданным content");
3037
+ if (!input || typeof input !== "object") throw new require_validate.ItdConfigError("Для обновления поста нужен объект с явно заданным content");
3045
3038
  const unsupported = [
3046
3039
  "wallRecipientId",
3047
3040
  "attachmentIds",
3048
3041
  "files",
3049
3042
  "poll"
3050
3043
  ].filter((key) => input[key] !== void 0);
3051
- if (unsupported.length > 0) throw new require_errors.ItdConfigError(`posts.update изменяет только content и spans; не поддерживаются: ${unsupported.join(", ")}`);
3052
- if (input.content === void 0) throw new require_errors.ItdConfigError("posts.update требует явно заданный content; обновление одних spans могло бы стереть текст поста");
3053
- if (typeof input.content !== "string") throw new require_errors.ItdConfigError("content обновляемого поста должен быть строкой");
3054
- if (input.spans !== void 0 && !Array.isArray(input.spans)) throw new require_errors.ItdConfigError("spans обновляемого поста должен быть массивом");
3044
+ if (unsupported.length > 0) throw new require_validate.ItdConfigError(`posts.update изменяет только content и spans; не поддерживаются: ${unsupported.join(", ")}`);
3045
+ if (input.content === void 0) throw new require_validate.ItdConfigError("posts.update требует явно заданный content; обновление одних spans могло бы стереть текст поста");
3046
+ if (typeof input.content !== "string") throw new require_validate.ItdConfigError("content обновляемого поста должен быть строкой");
3047
+ if (input.spans !== void 0 && !Array.isArray(input.spans)) throw new require_validate.ItdConfigError("spans обновляемого поста должен быть массивом");
3055
3048
  return {
3056
3049
  content: input.content,
3057
3050
  ...input.spans !== void 0 ? { spans: validateSpans(input.content, input.spans) } : {}
@@ -3420,9 +3413,9 @@ const TARGET_TYPES = new Set(Object.values(require_url.ReportTargetType));
3420
3413
  * @throws {ItdConfigError} если объект жалобы или причина заданы неверно
3421
3414
  */
3422
3415
  function validateReport(input) {
3423
- if (!input?.targetId || typeof input.targetId !== "string") throw new require_errors.ItdConfigError("Жалоба требует идентификатор объекта (targetId)");
3424
- if (!TARGET_TYPES.has(input.targetType)) throw new require_errors.ItdConfigError(`targetType должен быть одним из ${[...TARGET_TYPES].join(", ")}, получено: ${String(input.targetType)}`);
3425
- if (!REASONS.has(input.reason)) throw new require_errors.ItdConfigError(`Неизвестная причина жалобы «${String(input.reason)}». Допустимые: ${[...REASONS].join(", ")}`);
3416
+ if (!input?.targetId || typeof input.targetId !== "string") throw new require_validate.ItdConfigError("Жалоба требует идентификатор объекта (targetId)");
3417
+ if (!TARGET_TYPES.has(input.targetType)) throw new require_validate.ItdConfigError(`targetType должен быть одним из ${[...TARGET_TYPES].join(", ")}, получено: ${String(input.targetType)}`);
3418
+ if (!REASONS.has(input.reason)) throw new require_validate.ItdConfigError(`Неизвестная причина жалобы «${String(input.reason)}». Допустимые: ${[...REASONS].join(", ")}`);
3426
3419
  return input;
3427
3420
  }
3428
3421
  /**
@@ -3595,7 +3588,7 @@ var ShopProductsResource = class extends BaseResource {
3595
3588
  ...options
3596
3589
  });
3597
3590
  } catch (error) {
3598
- if (require_errors.isItdApiError(error) && error.status === 404) return null;
3591
+ if (require_validate.isItdApiError(error) && error.status === 404) return null;
3599
3592
  throw error;
3600
3593
  }
3601
3594
  }
@@ -3814,20 +3807,20 @@ var SubscriptionResource = class extends BaseResource {
3814
3807
  const DEFAULT_BATCH_SIZE = 50;
3815
3808
  const SYSTEM_CLOCK = Object.freeze({ now: () => Date.now() });
3816
3809
  function assertNonEmpty(value, name) {
3817
- if (value.trim().length === 0) throw new require_errors.ItdConfigError(`${name} должен быть непустой строкой`);
3810
+ if (value.trim().length === 0) throw new require_validate.ItdConfigError(`${name} должен быть непустой строкой`);
3818
3811
  }
3819
3812
  function assertFinite(value, name) {
3820
- if (!Number.isFinite(value)) throw new require_errors.ItdConfigError(`${name} должен быть конечным числом`);
3813
+ if (!Number.isFinite(value)) throw new require_validate.ItdConfigError(`${name} должен быть конечным числом`);
3821
3814
  }
3822
3815
  function assertNonNegative(value, name) {
3823
3816
  assertFinite(value, name);
3824
- if (value < 0) throw new require_errors.ItdConfigError(`${name} должен быть неотрицательным числом`);
3817
+ if (value < 0) throw new require_validate.ItdConfigError(`${name} должен быть неотрицательным числом`);
3825
3818
  }
3826
3819
  function validateDwell(entry) {
3827
3820
  assertNonEmpty(entry.vs, "vs");
3828
3821
  assertFinite(entry.enterAt, "enterAt");
3829
3822
  assertFinite(entry.exitAt, "exitAt");
3830
- if (entry.exitAt < entry.enterAt) throw new require_errors.ItdConfigError("exitAt не может быть раньше enterAt");
3823
+ if (entry.exitAt < entry.enterAt) throw new require_validate.ItdConfigError("exitAt не может быть раньше enterAt");
3831
3824
  if (entry.durationMs !== void 0) assertNonNegative(entry.durationMs, "durationMs");
3832
3825
  return { ...entry };
3833
3826
  }
@@ -3836,7 +3829,7 @@ function validateInteraction(entry) {
3836
3829
  assertNonEmpty(entry.postId, "postId");
3837
3830
  if (entry.mediaIndex !== void 0) {
3838
3831
  assertNonNegative(entry.mediaIndex, "mediaIndex");
3839
- if (!Number.isInteger(entry.mediaIndex)) throw new require_errors.ItdConfigError("mediaIndex должен быть целым числом");
3832
+ if (!Number.isInteger(entry.mediaIndex)) throw new require_validate.ItdConfigError("mediaIndex должен быть целым числом");
3840
3833
  }
3841
3834
  if (entry.positionMs !== void 0) assertNonNegative(entry.positionMs, "positionMs");
3842
3835
  if (entry.durationMs !== void 0) assertNonNegative(entry.durationMs, "durationMs");
@@ -3876,7 +3869,7 @@ var ViewTrackerImpl = class {
3876
3869
  try {
3877
3870
  const exitAt = this.#clock.now();
3878
3871
  assertFinite(exitAt, "clock.now()");
3879
- if (exitAt < this.enteredAt) throw new require_errors.ItdConfigError("часы не могут вернуться назад во время измерения просмотра");
3872
+ if (exitAt < this.enteredAt) throw new require_validate.ItdConfigError("часы не могут вернуться назад во время измерения просмотра");
3880
3873
  this.#result = Promise.resolve(this.#submit({
3881
3874
  ...this.#input,
3882
3875
  enterAt: this.enteredAt,
@@ -3905,7 +3898,7 @@ var TelemetryBatchImpl = class {
3905
3898
  #disposeCleanup = false;
3906
3899
  constructor(sendDwell, sendInteraction, options, requestOptions, onClose) {
3907
3900
  const { maxBatchSize = DEFAULT_BATCH_SIZE, clock = SYSTEM_CLOCK, ...telemetryOptions } = options;
3908
- if (!Number.isInteger(maxBatchSize) || maxBatchSize < 1) throw new require_errors.ItdConfigError("maxBatchSize должен быть целым числом от 1");
3901
+ if (!Number.isInteger(maxBatchSize) || maxBatchSize < 1) throw new require_validate.ItdConfigError("maxBatchSize должен быть целым числом от 1");
3909
3902
  this.#sendDwell = sendDwell;
3910
3903
  this.#sendInteraction = sendInteraction;
3911
3904
  this.#telemetryOptions = telemetryOptions;
@@ -4009,7 +4002,7 @@ var TelemetryBatchImpl = class {
4009
4002
  }
4010
4003
  }
4011
4004
  #assertOpen() {
4012
- if (this.#state !== "open") throw new require_errors.ItdConfigError("накопитель телеметрии уже закрывается или закрыт");
4005
+ if (this.#state !== "open") throw new require_validate.ItdConfigError("накопитель телеметрии уже закрывается или закрыт");
4013
4006
  }
4014
4007
  };
4015
4008
  const TELEMETRY_DISPOSE_CLOSERS = /* @__PURE__ */ new WeakMap();
@@ -4562,15 +4555,15 @@ function fromStream(factory, options = {}) {
4562
4555
  try {
4563
4556
  opened = await factory(context);
4564
4557
  } catch (error) {
4565
- if (error instanceof require_errors.ItdFileError || error instanceof require_errors.ItdConfigError || context.signal?.aborted) throw error;
4566
- throw new require_errors.ItdFileError("не удалось открыть поток вложения", {
4567
- reason: require_errors.ItdFileErrorReason.Read,
4558
+ if (error instanceof require_validate.ItdFileError || error instanceof require_validate.ItdConfigError || context.signal?.aborted) throw error;
4559
+ throw new require_validate.ItdFileError("не удалось открыть поток вложения", {
4560
+ reason: require_validate.ItdFileErrorReason.Read,
4568
4561
  retryable: true,
4569
4562
  cause: require_redact.redactErrorCause(error)
4570
4563
  });
4571
4564
  }
4572
4565
  const content = require_options.isReadableByteStream(opened) ? { stream: opened } : opened;
4573
- if (!content || !require_options.isReadableByteStream(content.stream)) throw new require_errors.ItdConfigError("fromStream должен вернуть ReadableStream или { stream }");
4566
+ if (!content || !require_options.isReadableByteStream(content.stream)) throw new require_validate.ItdConfigError("fromStream должен вернуть ReadableStream или { stream }");
4574
4567
  const size = options.size ?? content.size;
4575
4568
  require_options.optionalBytes(size, "size");
4576
4569
  if (resolved.maxBytes !== void 0 && size !== void 0 && size > resolved.maxBytes) {
@@ -4680,7 +4673,7 @@ var MemoryShopOrderAccessStorage = class {
4680
4673
  /** Создаёт хранилище доступа к заказам поверх `KeyValueStore`. */
4681
4674
  function createShopOrderAccessStorage(store, options = {}) {
4682
4675
  const key = options.key ?? "shop-order-access";
4683
- if (typeof key !== "string" || key.length === 0) throw new require_errors.ItdConfigError("ключ хранилища доступа к заказам должен быть непустой строкой");
4676
+ if (typeof key !== "string" || key.length === 0) throw new require_validate.ItdConfigError("ключ хранилища доступа к заказам должен быть непустой строкой");
4684
4677
  const backend = require_key_value_store.createKeyValueStore(store);
4685
4678
  return {
4686
4679
  async get() {
@@ -4702,7 +4695,7 @@ const Operation = Object.freeze({
4702
4695
  });
4703
4696
  function requireEmail(value) {
4704
4697
  const email = value.trim();
4705
- if (!email?.includes("@")) throw new require_errors.ItdConfigError("укажите адрес почты");
4698
+ if (!email?.includes("@")) throw new require_validate.ItdConfigError("укажите адрес почты");
4706
4699
  return email;
4707
4700
  }
4708
4701
  /** Создаёт подключаемый модуль для доступа к заказам по коду из письма. */
@@ -4756,7 +4749,7 @@ function createShopFeature(options = {}) {
4756
4749
  };
4757
4750
  const requireSession = async () => {
4758
4751
  const session = await readSession();
4759
- if (!session) throw new require_errors.ItdConfigError("доступ к заказам отсутствует или истёк; запросите новый код");
4752
+ if (!session) throw new require_validate.ItdConfigError("доступ к заказам отсутствует или истёк; запросите новый код");
4760
4753
  return session;
4761
4754
  };
4762
4755
  const withAccess = async (run) => {
@@ -4764,7 +4757,7 @@ function createShopFeature(options = {}) {
4764
4757
  try {
4765
4758
  return await run(session.token);
4766
4759
  } catch (error) {
4767
- if (require_errors.isItdApiError(error) && error.status === 401) await storage.clear();
4760
+ if (require_validate.isItdApiError(error) && error.status === 401) await storage.clear();
4768
4761
  throw error;
4769
4762
  }
4770
4763
  };
@@ -4790,7 +4783,7 @@ function createShopFeature(options = {}) {
4790
4783
  pendingEmail = normalized;
4791
4784
  },
4792
4785
  async verifyCode(code, requestOptions = {}) {
4793
- if (!pendingEmail) throw new require_errors.ItdConfigError("сначала запросите код для адреса почты");
4786
+ if (!pendingEmail) throw new require_validate.ItdConfigError("сначала запросите код для адреса почты");
4794
4787
  const verified = await context.request(Operation.VerifyCode, {
4795
4788
  path: "/api/v1/shop/orders/lookup/verify",
4796
4789
  body: {
@@ -5258,4 +5251,4 @@ Object.defineProperty(exports, "utcStampToIso", {
5258
5251
  }
5259
5252
  });
5260
5253
 
5261
- //# sourceMappingURL=render-CYJSDypQ.cjs.map
5254
+ //# sourceMappingURL=render-Dnc84B4M.cjs.map