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.
- package/README.md +2 -1
- package/dist/events/index.cjs +27 -25
- package/dist/events/index.d.cts +3 -3
- package/dist/events/index.d.ts +3 -3
- package/dist/events/index.js +5 -5
- package/dist/index.cjs +136 -116
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -7
- package/dist/index.d.ts +18 -7
- package/dist/index.js +46 -30
- package/dist/index.js.map +1 -1
- package/dist/node/index.cjs +13 -13
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.js +5 -5
- package/dist/rest/index.cjs +43 -40
- package/dist/rest/index.cjs.map +1 -1
- package/dist/rest/index.d.cts +3 -3
- package/dist/rest/index.d.ts +3 -3
- package/dist/rest/index.js +6 -6
- package/dist/shared/{events-BVfqWyBw.cjs → events-C6tmj8aW.cjs} +63 -59
- package/dist/shared/events-C6tmj8aW.cjs.map +1 -0
- package/dist/shared/{events-Cp7ITueA.d.ts → events-CAb9G7Sf.d.ts} +2 -2
- package/dist/shared/{events-Cwj7UPAa.js → events-MWNcnfrq.js} +19 -15
- package/dist/shared/events-MWNcnfrq.js.map +1 -0
- package/dist/shared/{events-D0Wtt-If.d.cts → events-Sc75x6rT.d.cts} +2 -2
- package/dist/shared/{key-value-store-B5GYVEYZ.cjs → key-value-store-DYSPsqwq.cjs} +11 -14
- package/dist/shared/key-value-store-DYSPsqwq.cjs.map +1 -0
- package/dist/shared/{key-value-store-Bt2PSMxY.js → key-value-store-w1Dn5Pjb.js} +5 -8
- package/dist/shared/key-value-store-w1Dn5Pjb.js.map +1 -0
- package/dist/shared/{multi-storage-C3Ngtop1.js → multi-storage-BLBrGN9Q.js} +6 -6
- package/dist/shared/multi-storage-BLBrGN9Q.js.map +1 -0
- package/dist/shared/{multi-storage-s-fPtjoy.cjs → multi-storage-C7-Gz6Z7.cjs} +8 -8
- package/dist/shared/multi-storage-C7-Gz6Z7.cjs.map +1 -0
- package/dist/shared/{options-DjEtbVaf.cjs → options-5tywTYId.cjs} +13 -13
- package/dist/shared/{options-DjEtbVaf.cjs.map → options-5tywTYId.cjs.map} +1 -1
- package/dist/shared/{options-i4LrKfqx.js → options-BerKicCs.js} +3 -3
- package/dist/shared/{options-i4LrKfqx.js.map → options-BerKicCs.js.map} +1 -1
- package/dist/shared/{redact-DJV0XM3n.js → redact-C5oxIvcc.js} +79 -18
- package/dist/shared/redact-C5oxIvcc.js.map +1 -0
- package/dist/shared/{redact-lAmck1Z6.cjs → redact-DoC7lSsp.cjs} +109 -24
- package/dist/shared/redact-DoC7lSsp.cjs.map +1 -0
- package/dist/shared/{render-DRmuHoEy.d.ts → render-CBroJPcV.d.ts} +178 -252
- package/dist/shared/{render-CJO_L2CD.d.cts → render-DM7AhTZW.d.cts} +178 -252
- package/dist/shared/{render-BT74nC1l.js → render-DRh5_aRe.js} +128 -135
- package/dist/shared/render-DRh5_aRe.js.map +1 -0
- package/dist/shared/{render-CYJSDypQ.cjs → render-Dnc84B4M.cjs} +234 -241
- package/dist/shared/render-Dnc84B4M.cjs.map +1 -0
- package/dist/shared/{storage-DiDe0PZM.js → storage-86wiHeXD.js} +3 -3
- package/dist/shared/{storage-DiDe0PZM.js.map → storage-86wiHeXD.js.map} +1 -1
- package/dist/shared/{storage-CZJZoaz9.cjs → storage-CIjejZDt.cjs} +4 -4
- package/dist/shared/{storage-CZJZoaz9.cjs.map → storage-CIjejZDt.cjs.map} +1 -1
- package/dist/shared/{url-BLnX-LYt.d.cts → url-BOi-_mJT.d.cts} +143 -79
- package/dist/shared/{url-BLnX-LYt.d.ts → url-BOi-_mJT.d.ts} +143 -79
- package/dist/shared/{url-DS0-lrl-.js → url-Bg61sk_Y.js} +741 -598
- package/dist/shared/url-Bg61sk_Y.js.map +1 -0
- package/dist/shared/{url-BnO1aWsd.cjs → url-DTgkCWv5.cjs} +848 -717
- package/dist/shared/url-DTgkCWv5.cjs.map +1 -0
- package/dist/shared/{errors-BmP3TKoW.cjs → validate-CFx2dISs.cjs} +84 -5
- package/dist/shared/validate-CFx2dISs.cjs.map +1 -0
- package/dist/shared/{errors-GI10kZxk.js → validate-D7_kq3g5.js} +55 -6
- package/dist/shared/validate-D7_kq3g5.js.map +1 -0
- package/dist/web/index.cjs +4 -4
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.js +3 -3
- package/package.json +1 -1
- package/dist/shared/errors-BmP3TKoW.cjs.map +0 -1
- package/dist/shared/errors-GI10kZxk.js.map +0 -1
- package/dist/shared/events-BVfqWyBw.cjs.map +0 -1
- package/dist/shared/events-Cwj7UPAa.js.map +0 -1
- package/dist/shared/key-value-store-B5GYVEYZ.cjs.map +0 -1
- package/dist/shared/key-value-store-Bt2PSMxY.js.map +0 -1
- package/dist/shared/multi-storage-C3Ngtop1.js.map +0 -1
- package/dist/shared/multi-storage-s-fPtjoy.cjs.map +0 -1
- package/dist/shared/redact-DJV0XM3n.js.map +0 -1
- package/dist/shared/redact-lAmck1Z6.cjs.map +0 -1
- package/dist/shared/render-BT74nC1l.js.map +0 -1
- package/dist/shared/render-CYJSDypQ.cjs.map +0 -1
- package/dist/shared/url-BnO1aWsd.cjs.map +0 -1
- 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
|
|
2
|
-
import {
|
|
3
|
-
import { a as redactUrl, b as isBlob, f as encodePathSegment, r as redactErrorCause, x as isFile, y as createDeviceId } from "./redact-
|
|
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-
|
|
5
|
-
import { r as createKeyValueStore } from "./key-value-store-
|
|
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
|
-
|
|
20
|
-
#
|
|
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.#
|
|
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.#
|
|
54
|
-
this.#
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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.#
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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-
|
|
5033
|
+
//# sourceMappingURL=render-DRh5_aRe.js.map
|