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
- import { $ as identityResult, A as SpanType, D as ReportReason, G as isRecord, I as defineBuiltInOperation, J as pickNumber, K as pickArray, O as ReportTargetType, Q as defineOperation, S as InteractionType, X as pickString, Y as pickObject, Z as RetrySafety, c as normalizeNotification, ct as DEFAULT_STATUS_BASE_URL, et as voidResult, ht as createDeadline, lt as STATUS_SERVICE, o as passthroughOperation, q as pickBoolean, rt as mergeService, s as voidOperation } from "./url-DS0-lrl-.js";
2
- import { _ as ItdStateError, a as ItdConfigError, b as isItdApiError, l as ItdFileError, s as ItdError, t as ItdAbortError, u as ItdFileErrorReason } from "./errors-GI10kZxk.js";
3
- import { a as redactUrl, b as isBlob, f as encodePathSegment, r as redactErrorCause, x as isFile, y as createDeviceId } from "./redact-DJV0XM3n.js";
4
- import { a as fileTooLarge, c as FileTransferMode, i as isReadableByteStream, n as boundedFileStream, o as optionalBytes, r as isBoundedFileStream, s as DEFAULT_URL_FILE_MAX_BYTES, t as resolveFileStreamOptions } from "./options-i4LrKfqx.js";
5
- import { r as createKeyValueStore } from "./key-value-store-Bt2PSMxY.js";
1
+ import { $ as identityResult, A as SpanType, D as ReportReason, G as pickArray, I as defineBuiltInOperation, J as pickObject, K as pickBoolean, O as ReportTargetType, Q as defineOperation, S as InteractionType, X as OperationMethod, Y as pickString, Z as RetrySafety, c as normalizeNotification, ct as DEFAULT_STATUS_BASE_URL, et as voidResult, lt as STATUS_SERVICE, o as passthroughOperation, pt as createDeadline, q as pickNumber, rt as mergeService, s as voidOperation } from "./url-Bg61sk_Y.js";
2
+ import { T as isItdApiError, a as ItdAbortError, d as ItdError, l as ItdConfigError, m as ItdFileErrorReason, n as isRecord, p as ItdFileError, x as ItdStateError } from "./validate-D7_kq3g5.js";
3
+ import { a as redactUrl, b as isBlob, f as encodePathSegment, r as redactErrorCause, x as isFile, y as createDeviceId } from "./redact-C5oxIvcc.js";
4
+ import { a as fileTooLarge, c as FileTransferMode, i as isReadableByteStream, n as boundedFileStream, o as optionalBytes, r as isBoundedFileStream, s as DEFAULT_URL_FILE_MAX_BYTES, t as resolveFileStreamOptions } from "./options-BerKicCs.js";
5
+ import { r as createKeyValueStore } from "./key-value-store-w1Dn5Pjb.js";
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,32 +33,28 @@ 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 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 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
  /** Регистрирует операцию и возвращает откат регистрации. */
@@ -70,7 +63,7 @@ var ExtensibleOperationCatalog = class {
70
63
  if (!id.startsWith(ownerPrefix) || id.length === ownerPrefix.length) throw new ItdConfigError(`feature «${owner}»: операция «${id}» должна находиться в пространстве имён «${owner}»`);
71
64
  const occupiedBy = this.#operationOwners.get(id);
72
65
  if (occupiedBy !== void 0) throw new ItdConfigError(`feature «${owner}»: операция «${id}» уже зарегистрирована feature «${occupiedBy}»`);
73
- if (this.#base.methodOf(id) !== void 0) throw new ItdConfigError(`feature «${owner}»: операция «${id}» уже существует в базовом каталоге`);
66
+ if (this.#base.definitionOf(id) !== void 0) throw new ItdConfigError(`feature «${owner}»: операция «${id}» уже существует в базовом каталоге`);
74
67
  this.#operationOwners.set(id, owner);
75
68
  this.#operations.set(id, Object.freeze({ ...definition }));
76
69
  return () => {
@@ -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 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 = 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 = resolveFileStreamOptions(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
  };
@@ -416,6 +409,92 @@ function validateFeatureInstallation(name, installation) {
416
409
  if (installation.close !== void 0 && typeof installation.close !== "function") throw new ItdConfigError(`feature «${name}»: close должен быть функцией`);
417
410
  if (installation.dispose !== void 0 && typeof installation.dispose !== "function") throw new ItdConfigError(`feature «${name}»: dispose должен быть функцией`);
418
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 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 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 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
+ });
497
+ }
419
498
  /** Реестр подключаемых модулей клиента. @internal */
420
499
  var FeatureRegistry = class {
421
500
  #deps;
@@ -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;
@@ -468,13 +545,7 @@ var FeatureRegistry = class {
468
545
  for (const [rawLocalName, definition] of Object.entries(definitions)) {
469
546
  const localName = requireName(rawLocalName, `Операция feature «${featureName}»`);
470
547
  if (typeof definition !== "object" || definition === null) throw new ItdConfigError(`feature «${featureName}»: операция «${localName}» должна быть объектом`);
471
- if (![
472
- "GET",
473
- "POST",
474
- "PUT",
475
- "PATCH",
476
- "DELETE"
477
- ].includes(definition.method)) throw new ItdConfigError(`feature «${featureName}»: операция «${localName}» содержит неизвестный HTTP-метод`);
548
+ if (!Object.values(OperationMethod).includes(definition.method)) throw new ItdConfigError(`feature «${featureName}»: операция «${localName}» содержит неизвестный HTTP-метод`);
478
549
  if (!Object.values(RetrySafety).includes(definition.retrySafety)) throw new ItdConfigError(`feature «${featureName}»: операция «${localName}» содержит неизвестную retrySafety`);
479
550
  if (definition.service !== void 0 && typeof definition.service !== "string") throw new ItdConfigError(`feature «${featureName}»: service операции «${localName}» должен быть строкой`);
480
551
  if (definition.bucket !== void 0 && typeof definition.bucket !== "string") throw new ItdConfigError(`feature «${featureName}»: bucket операции «${localName}» должен быть строкой`);
@@ -509,95 +580,18 @@ var FeatureRegistry = class {
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 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 ItdStateError(`feature «${name}» не активен`);
541
- const resolved = operations.get(operation);
542
- if (!resolved) throw new 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 ItdConfigError(`feature «${name}» не объявляет сервис «${serviceName}»`);
560
- return this.#deps.services.resolveBaseUrl(serviceName);
561
- },
562
- connection: (serviceName) => {
563
- if (!services.has(serviceName)) throw new 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 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 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
606
  controller.abort(new 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
  }
@@ -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) {
@@ -5037,4 +5030,4 @@ function renderSpans(content, spans = [], options = {}) {
5037
5030
  //#endregion
5038
5031
  export { utcStampToIso as A, AUDIO_MIME_TYPES as C, isBuilder as D, comment as E, mapPage as F, createFeatureHost as I, ExtensibleOperationCatalog as L, BaseResource as M, PaginationMode as N, createStatusFeature as O, Paginator as P, ALLOWED_MIME_TYPES as S, VIDEO_MIME_TYPES as T, poll as _, createShopOrderAccessStorage as a, parseHtml as b, ShopOrderStatus as c, isMyProfile as d, fromStream as f, post as g, report as h, MemoryShopOrderAccessStorage as i, NotificationsResource as j, toDate as k, ShopProductCategory as l, createResources as m, renderSpans as n, statusDays as o, fromUrl as p, createShopFeature as r, ShopConsentKind as s, SpanRenderFormat as t, ShopProductStatus as u, autoSpans as v, IMAGE_MIME_TYPES as w, parseMarkdown as x, markup as y };
5039
5032
 
5040
- //# sourceMappingURL=render-BT74nC1l.js.map
5033
+ //# sourceMappingURL=render-DRh5_aRe.js.map