@torrow/sdk-client 0.7.0 → 0.8.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/dist/index.js CHANGED
@@ -679,7 +679,7 @@ var HttpClient = class {
679
679
  this.config = config;
680
680
  }
681
681
  config;
682
- async requestJson(init) {
682
+ async requestJson(init, onDispatch) {
683
683
  const { fetch: fetchFn = globalThis.fetch } = this.config;
684
684
  const url = this.buildUrl(init.path, init.query);
685
685
  const authHeaders = await buildAuthHeaders(this.config);
@@ -696,12 +696,15 @@ var HttpClient = class {
696
696
  if (needsBody) {
697
697
  headers["Content-Type"] = JSON_MIME;
698
698
  }
699
- const res = await fetchFn(url, {
699
+ const requestInit = {
700
700
  method: init.method,
701
701
  headers,
702
702
  body: needsBody ? stringifyJsonBody(init.body) : void 0,
703
703
  signal: init.signal
704
- });
704
+ };
705
+ if (onDispatch) init.signal?.throwIfAborted();
706
+ onDispatch?.();
707
+ const res = await fetchFn(url, requestInit);
705
708
  const requestId = res.headers.get("x-request-id") ?? void 0;
706
709
  const traceIdentifier = res.headers.get("trace-identifier") ?? void 0;
707
710
  const text = await res.text();
@@ -1210,7 +1213,8 @@ var TorrowItemCoreFacade = class {
1210
1213
  }
1211
1214
  /**
1212
1215
  * `PUT /api/v1/{domain}/{torrowId}/participation/requestaccess` — запрос доступа (не-владелец).
1213
- * Body: **`profileId`**. State → **`Requested`**; владельцу announcement **`RequestAccessToItem`**.
1216
+ * Body: **`profileId`**. Новый участник → **`Requested`**; некоторые существующие состояния → **`Accepted`**.
1217
+ * Проверять возвращённый participantState; после Accepted заново прочитать элемент и проверить полноту содержимого.
1214
1218
  * Владелец своего item вызвать не может. Дальше: {@link grantAccess} / {@link declineAccess} / {@link revokeAccessRequest}.
1215
1219
  */
1216
1220
  requestAccess(torrowId, profileId, options) {
@@ -3092,7 +3096,6 @@ var SERVICE_IMPLICIT_TAG_SUFFIX = {
3092
3096
  autoStartTelegramNotificationBot: "AutoStartTelegramNotificationBot",
3093
3097
  autoStartTelegramNotificationBotTypo: "AutoStartTelegramNotifcationBot",
3094
3098
  autoStartConnectNotifications: "AutoStartConnectNotifications",
3095
- hideMessengerConnectUiLegacy: "HideTelegramNotificationBot",
3096
3099
  hideMessengerConnectUi: "HideMessengerNotifications",
3097
3100
  /**
3098
3101
  * Резерв под серверную договоренность. Формат в `tags`: `NNN:payment_redirect_timeout_sec`.
@@ -3100,6 +3103,7 @@ var SERVICE_IMPLICIT_TAG_SUFFIX = {
3100
3103
  */
3101
3104
  paymentRedirectTimeoutSec: ":payment_redirect_timeout_sec"
3102
3105
  };
3106
+ var OBSOLETE_MESSENGER_CONNECT_UI_TAG = "HideTelegramNotificationBot";
3103
3107
  var SERVICE_IMPLICIT_TAG_REGISTRY = {
3104
3108
  orderName: {
3105
3109
  field: "orderName",
@@ -3145,13 +3149,8 @@ var SERVICE_IMPLICIT_TAG_REGISTRY = {
3145
3149
  },
3146
3150
  hideMessengerConnectUi: {
3147
3151
  field: "hideMessengerConnectUi",
3148
- kind: "containsFlag",
3149
- tokens: [SERVICE_IMPLICIT_TAG_SUFFIX.hideMessengerConnectUi, SERVICE_IMPLICIT_TAG_SUFFIX.hideMessengerConnectUiLegacy]
3150
- },
3151
- hideTelegramNotificationBot: {
3152
- field: "hideTelegramNotificationBot",
3153
- kind: "containsFlag",
3154
- tokens: [SERVICE_IMPLICIT_TAG_SUFFIX.hideMessengerConnectUiLegacy]
3152
+ kind: "exactFlag",
3153
+ tokens: [SERVICE_IMPLICIT_TAG_SUFFIX.hideMessengerConnectUi]
3155
3154
  },
3156
3155
  autoStartNotificationDelaySeconds: {
3157
3156
  field: "autoStartNotificationDelaySeconds",
@@ -3184,8 +3183,8 @@ function splitBySuffix(tag, suffix, caseInsensitive = false) {
3184
3183
  }
3185
3184
  function firstPrefixBySuffix(tags, suffix, options) {
3186
3185
  for (const tag of tags) {
3187
- const prefix = splitBySuffix(tag, suffix, options?.caseInsensitive ?? false);
3188
- if (prefix !== void 0) return prefix;
3186
+ const prefix2 = splitBySuffix(tag, suffix, options?.caseInsensitive ?? false);
3187
+ if (prefix2 !== void 0) return prefix2;
3189
3188
  }
3190
3189
  return void 0;
3191
3190
  }
@@ -3239,8 +3238,8 @@ function parseServiceImplicitTagParams(tags) {
3239
3238
  if (value !== void 0) mutable[config.field] = value;
3240
3239
  }
3241
3240
  }
3242
- if (config.kind === "containsFlag") {
3243
- const hasAny = safeTags.some((tag) => config.tokens.some((token) => tag.includes(token)));
3241
+ if (config.kind === "exactFlag") {
3242
+ const hasAny = safeTags.some((tag) => config.tokens.some((token) => tag === token));
3244
3243
  if (hasAny) mutable[config.field] = true;
3245
3244
  }
3246
3245
  }
@@ -3315,9 +3314,6 @@ function buildServiceImplicitTags(params, options) {
3315
3314
  if (params.hideMessengerConnectUi) {
3316
3315
  tags.push(SERVICE_IMPLICIT_TAG_REGISTRY.hideMessengerConnectUi.tokens[0]);
3317
3316
  }
3318
- if (params.hideTelegramNotificationBot) {
3319
- tags.push(SERVICE_IMPLICIT_TAG_REGISTRY.hideTelegramNotificationBot.tokens[0]);
3320
- }
3321
3317
  return tags;
3322
3318
  }
3323
3319
  function isImplicitServiceTag(tag) {
@@ -3328,8 +3324,8 @@ function isImplicitServiceTag(tag) {
3328
3324
  );
3329
3325
  if (matched) return true;
3330
3326
  }
3331
- if (config.kind === "containsFlag") {
3332
- if (config.tokens.some((token) => tag.includes(token))) return true;
3327
+ if (config.kind === "exactFlag") {
3328
+ if (config.tokens.some((token) => tag === token)) return true;
3333
3329
  }
3334
3330
  if (config.kind === "autoStart") {
3335
3331
  const isAutoStart = config.suffixes.some((suffix) => tag.match(new RegExp(`^\\d+:${suffix}$`)) !== null);
@@ -3345,15 +3341,17 @@ function isImplicitServiceTag(tag) {
3345
3341
  }
3346
3342
  function mergeServiceImplicitTags(baseTags, params, options) {
3347
3343
  const safeBase = baseTags ?? [];
3348
- const preservedNonImplicit = safeBase.filter((tag) => !isImplicitServiceTag(tag));
3344
+ const preservedNonImplicit = safeBase.filter(
3345
+ (tag) => tag !== OBSOLETE_MESSENGER_CONNECT_UI_TAG && !isImplicitServiceTag(tag)
3346
+ );
3349
3347
  const nextImplicit = buildServiceImplicitTags(params, options);
3350
3348
  return [...preservedNonImplicit, ...nextImplicit];
3351
3349
  }
3352
3350
  function parseResourceBehaviorSettings(tags) {
3353
- const parsed = parseServiceImplicitTagParams(tags);
3351
+ const marginTimeSec = toPositiveInt(firstPrefixBySuffix(tags ?? [], SERVICE_IMPLICIT_TAG_SUFFIX.marginTime));
3354
3352
  const lockId = toPositiveInt(firstPrefixBySuffix(tags ?? [], SERVICE_IMPLICIT_TAG_SUFFIX.lockId));
3355
3353
  const result = {};
3356
- if (parsed.marginTimeSec !== void 0) result.marginTimeSec = parsed.marginTimeSec;
3354
+ if (marginTimeSec !== void 0) result.marginTimeSec = marginTimeSec;
3357
3355
  if (lockId !== void 0) result.lockId = lockId;
3358
3356
  return result;
3359
3357
  }
@@ -3591,6 +3589,195 @@ var ResourcesFacade = class extends TorrowItemFacade {
3591
3589
  }
3592
3590
  };
3593
3591
 
3592
+ // ../../src/resources/services/behavior-settings.ts
3593
+ var ServiceBehaviorSettingsErrorCategory = /* @__PURE__ */ ((ServiceBehaviorSettingsErrorCategory2) => {
3594
+ ServiceBehaviorSettingsErrorCategory2["InvalidInput"] = "InvalidInput";
3595
+ ServiceBehaviorSettingsErrorCategory2["AccessDenied"] = "AccessDenied";
3596
+ ServiceBehaviorSettingsErrorCategory2["InsufficientData"] = "InsufficientData";
3597
+ ServiceBehaviorSettingsErrorCategory2["InvalidResponse"] = "InvalidResponse";
3598
+ ServiceBehaviorSettingsErrorCategory2["VersionConflict"] = "VersionConflict";
3599
+ ServiceBehaviorSettingsErrorCategory2["WriteOutcomeUnknown"] = "WriteOutcomeUnknown";
3600
+ ServiceBehaviorSettingsErrorCategory2["RequestNotSent"] = "RequestNotSent";
3601
+ return ServiceBehaviorSettingsErrorCategory2;
3602
+ })(ServiceBehaviorSettingsErrorCategory || {});
3603
+ var ServiceBehaviorSettingsIssueReason = /* @__PURE__ */ ((ServiceBehaviorSettingsIssueReason2) => {
3604
+ ServiceBehaviorSettingsIssueReason2["InvalidValue"] = "InvalidValue";
3605
+ ServiceBehaviorSettingsIssueReason2["AmbiguousValue"] = "AmbiguousValue";
3606
+ return ServiceBehaviorSettingsIssueReason2;
3607
+ })(ServiceBehaviorSettingsIssueReason || {});
3608
+ var ServiceBehaviorSettingsError = class extends Error {
3609
+ /** Предметная категория; не зависит от кодов MCP. */
3610
+ category;
3611
+ /** Исходная ошибка транспорта или сервера, если она существует. */
3612
+ cause;
3613
+ constructor(category, cause) {
3614
+ super(`Service BehaviorSettings: ${category}`);
3615
+ this.name = "ServiceBehaviorSettingsError";
3616
+ this.category = category;
3617
+ this.cause = cause;
3618
+ }
3619
+ };
3620
+ var configs = Object.values(SERVICE_IMPLICIT_TAG_REGISTRY);
3621
+ function isBehaviorVersion(value) {
3622
+ return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 2147483647;
3623
+ }
3624
+ function record(value) {
3625
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3626
+ }
3627
+ function validateBehaviorResponse(value, serviceId) {
3628
+ if (!record(value) || value.id !== serviceId) throw new ServiceBehaviorSettingsError("InvalidResponse" /* InvalidResponse */);
3629
+ const personal = value.personalInfo;
3630
+ if (record(personal) && (personal.itemContentType === "NoRights" /* NoRights */ || personal.itemContentType === "SubscriptionIsNotValid" /* SubscriptionIsNotValid */)) {
3631
+ throw new ServiceBehaviorSettingsError("AccessDenied" /* AccessDenied */);
3632
+ }
3633
+ if (!record(personal) || personal.itemContentType !== "Full" /* Full */ || value.tags == null || !record(value.meta) || !isBehaviorVersion(value.meta.version)) {
3634
+ throw new ServiceBehaviorSettingsError("InsufficientData" /* InsufficientData */);
3635
+ }
3636
+ if (!Array.isArray(value.tags) || !value.tags.every((tag) => typeof tag === "string")) {
3637
+ throw new ServiceBehaviorSettingsError("InvalidResponse" /* InvalidResponse */);
3638
+ }
3639
+ const operations = personal.operationList;
3640
+ if (operations != null && (!Array.isArray(operations) || !operations.every((operation) => typeof operation === "string" || typeof operation === "number" && Number.isSafeInteger(operation)))) {
3641
+ throw new ServiceBehaviorSettingsError("InvalidResponse" /* InvalidResponse */);
3642
+ }
3643
+ return { tags: [...new Set(value.tags)], version: value.meta.version, operations };
3644
+ }
3645
+ function assertBehaviorPermission(operations) {
3646
+ if (!Array.isArray(operations)) throw new ServiceBehaviorSettingsError("InsufficientData" /* InsufficientData */);
3647
+ if (!operations.includes("SetItemTags" /* SetItemTags */)) throw new ServiceBehaviorSettingsError("AccessDenied" /* AccessDenied */);
3648
+ }
3649
+ function suffixPrefix(tag, suffix, ignoreCase = false) {
3650
+ const matches = ignoreCase ? tag.toLowerCase().endsWith(suffix.toLowerCase()) : tag.endsWith(suffix);
3651
+ return matches ? tag.slice(0, -suffix.length) : void 0;
3652
+ }
3653
+ function positive(raw) {
3654
+ const value = Number(raw);
3655
+ return /^\d+$/.test(raw) && Number.isSafeInteger(value) && value > 0 ? value : void 0;
3656
+ }
3657
+ function prefix(tag, config) {
3658
+ if (config.kind === "string" /* String */ || config.kind === "positiveInt" /* PositiveInt */) {
3659
+ for (const suffix of config.suffixes) {
3660
+ const value = suffixPrefix(tag, suffix, config.caseInsensitive);
3661
+ if (value !== void 0) return value;
3662
+ }
3663
+ } else if (config.kind === "exactFlag" /* ExactFlag */) {
3664
+ if (config.tokens.includes(tag) || tag === "HideTelegramNotificationBot" /* LegacyHide */) return "true";
3665
+ } else if (config.kind === "autoStart" /* AutoStart */) {
3666
+ for (const suffix of config.suffixes) {
3667
+ const value = suffixPrefix(tag, `:${suffix}`);
3668
+ if (value !== void 0) return value;
3669
+ }
3670
+ } else if (config.kind === "agreement" /* Agreement */) {
3671
+ for (const suffix of [config.textSuffix, config.linkSuffix]) {
3672
+ const position = tag.lastIndexOf(suffix);
3673
+ if (position >= 0 && /^\d+$/.test(tag.slice(position + suffix.length))) return tag;
3674
+ }
3675
+ }
3676
+ return void 0;
3677
+ }
3678
+ function readBehaviorSettings(tags) {
3679
+ const settings = { hideMessengerConnectUi: false };
3680
+ const issues = [];
3681
+ const target = settings;
3682
+ for (const config of configs) {
3683
+ const matching = [...new Set(tags)].filter((tag) => prefix(tag, config) !== void 0);
3684
+ if (!matching.length) continue;
3685
+ let invalid = false;
3686
+ let ambiguous = false;
3687
+ let result;
3688
+ if (config.kind === "agreement" /* Agreement */) {
3689
+ const entries = /* @__PURE__ */ new Map();
3690
+ for (const tag of matching) {
3691
+ for (const [attribute, suffix] of [["text", config.textSuffix], ["link", config.linkSuffix]]) {
3692
+ const position = tag.lastIndexOf(suffix);
3693
+ if (position < 0 || !/^\d+$/.test(tag.slice(position + suffix.length))) continue;
3694
+ const index = positive(tag.slice(position + suffix.length));
3695
+ let value = tag.slice(0, position).trim();
3696
+ if (attribute === "link") value = value.replace(/^(https?)\/\//, "$1://");
3697
+ if (!index || !value) {
3698
+ invalid = true;
3699
+ continue;
3700
+ }
3701
+ const entry = entries.get(index) ?? { index };
3702
+ if (entry[attribute] !== void 0 && entry[attribute] !== value) ambiguous = true;
3703
+ entry[attribute] = value;
3704
+ entries.set(index, entry);
3705
+ }
3706
+ }
3707
+ result = [...entries.values()].sort((a, b) => a.index - b.index);
3708
+ } else {
3709
+ const values = /* @__PURE__ */ new Set();
3710
+ for (const tag of matching) {
3711
+ const raw = prefix(tag, config);
3712
+ const value = config.kind === "exactFlag" /* ExactFlag */ ? true : config.kind === "string" /* String */ ? raw.trim() || void 0 : positive(raw);
3713
+ if (value === void 0) invalid = true;
3714
+ else values.add(value);
3715
+ }
3716
+ ambiguous = values.size > 1;
3717
+ result = [...values][0];
3718
+ }
3719
+ if (invalid || ambiguous) {
3720
+ issues.push({ field: config.field, reason: invalid ? "InvalidValue" /* InvalidValue */ : "AmbiguousValue" /* AmbiguousValue */ });
3721
+ } else target[config.field] = result;
3722
+ }
3723
+ return { settings, issues };
3724
+ }
3725
+ function normalizeBehaviorInput(input) {
3726
+ function fail() {
3727
+ throw new ServiceBehaviorSettingsError("InvalidInput" /* InvalidInput */);
3728
+ }
3729
+ if (!record(input) || Object.keys(input).some((key) => !Object.hasOwn(SERVICE_IMPLICIT_TAG_REGISTRY, key))) fail();
3730
+ const output = {};
3731
+ for (const config of configs) {
3732
+ if (!Object.hasOwn(input, config.field)) continue;
3733
+ const value = input[config.field];
3734
+ if (config.kind === "string" /* String */) {
3735
+ if (typeof value !== "string" || !value.trim()) fail();
3736
+ output[config.field] = value.trim();
3737
+ } else if (config.kind === "positiveInt" /* PositiveInt */ || config.kind === "autoStart" /* AutoStart */) {
3738
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) fail();
3739
+ output[config.field] = value;
3740
+ } else if (config.kind === "exactFlag" /* ExactFlag */) {
3741
+ if (typeof value !== "boolean") fail();
3742
+ output[config.field] = value;
3743
+ } else {
3744
+ if (!Array.isArray(value)) fail();
3745
+ const seen = /* @__PURE__ */ new Set();
3746
+ output[config.field] = value.map((entry) => {
3747
+ if (!record(entry) || Object.keys(entry).some((key) => !["index", "text", "link"].includes(key)) || typeof entry.index !== "number" || !Number.isSafeInteger(entry.index) || entry.index <= 0 || seen.has(entry.index)) fail();
3748
+ seen.add(entry.index);
3749
+ const normalized = { index: entry.index };
3750
+ for (const attribute of ["text", "link"]) {
3751
+ if (Object.hasOwn(entry, attribute)) {
3752
+ if (typeof entry[attribute] !== "string" || !entry[attribute].trim()) fail();
3753
+ normalized[attribute] = entry[attribute].trim();
3754
+ }
3755
+ }
3756
+ if (!normalized.text && !normalized.link) fail();
3757
+ return normalized;
3758
+ }).sort((a, b) => a.index - b.index);
3759
+ }
3760
+ }
3761
+ return output;
3762
+ }
3763
+ function mergeBehaviorSettings(tags, settings, mergeParameters) {
3764
+ const selected = configs.filter((config) => !mergeParameters || Object.hasOwn(settings, config.field));
3765
+ const output = tags.filter((tag) => !selected.some((config) => prefix(tag, config) !== void 0));
3766
+ for (const config of selected) {
3767
+ const value = settings[config.field];
3768
+ if (value === void 0) continue;
3769
+ if (config.kind === "string" /* String */ || config.kind === "positiveInt" /* PositiveInt */) output.push(`${value}${config.suffixes[0]}`);
3770
+ else if (config.kind === "exactFlag" /* ExactFlag */) {
3771
+ if (value) output.push(config.tokens[0]);
3772
+ } else if (config.kind === "autoStart" /* AutoStart */) output.push(`${value}:${config.suffixes[0]}`);
3773
+ else if (config.kind === "agreement" /* Agreement */) for (const entry of settings.agreementItems ?? []) {
3774
+ if (entry.text) output.push(`${entry.text}${config.textSuffix}${entry.index}`);
3775
+ if (entry.link) output.push(`${entry.link.replace(/^(https?):\/\//, "$1//")}${config.linkSuffix}${entry.index}`);
3776
+ }
3777
+ }
3778
+ return [...new Set(output)];
3779
+ }
3780
+
3594
3781
  // ../../src/resources/services/service-requests.ts
3595
3782
  var BASE8 = "/api/v1/services";
3596
3783
  function toSdkServiceItem(service) {
@@ -3707,56 +3894,80 @@ var ServicesFacade = class extends TorrowItemFacade {
3707
3894
  getById(torrowId, options) {
3708
3895
  return getService(this.http, torrowId, options);
3709
3896
  }
3710
- /** Внутренний helper: получить типизированные настройки поведения услуги из `service.tags`. */
3711
- getBehaviorSettingsFromService(service) {
3712
- const settings = "behaviorSettings" in service && service.behaviorSettings ? service.behaviorSettings : parseServiceImplicitTagParams(service.tags);
3713
- return settings;
3897
+ /** Читает сырой OpenAPI DTO до проекции, чтобы malformed tags не маскировались парсером. */
3898
+ async readBehaviorState(serviceId, options) {
3899
+ if (typeof serviceId !== "string" || !serviceId.trim()) {
3900
+ throw new ServiceBehaviorSettingsError("InvalidInput" /* InvalidInput */);
3901
+ }
3902
+ const value = await this.http.requestJson({
3903
+ method: "GET",
3904
+ path: `${BASE9}/${encodePathSegment(serviceId)}`,
3905
+ query: {
3906
+ profileId: options?.profileId,
3907
+ versionToCheck: options?.versionToCheck,
3908
+ includeDeleted: options?.includeDeleted,
3909
+ includeChildTypes: options?.includeChildTypes,
3910
+ securityToken: options?.securityToken
3911
+ },
3912
+ signal: options?.signal
3913
+ });
3914
+ return validateBehaviorResponse(value, serviceId);
3714
3915
  }
3715
- /** `GET + parse`: получить сервис и извлечь `ServiceBehaviorSettings`. */
3916
+ /** Один GET: версия всей услуги, предметные настройки и диагностика неполного снимка. */
3716
3917
  async getBehaviorSettings(serviceId, options) {
3717
- const service = await this.getById(serviceId, options);
3718
- return this.getBehaviorSettingsFromService(service);
3918
+ const state = await this.readBehaviorState(serviceId, options);
3919
+ return { serviceId, version: state.version, ...readBehaviorSettings(state.tags) };
3719
3920
  }
3720
3921
  /**
3721
- * Обновляет настройки поведения услуги через типизированную структуру.
3722
- * `mergeParameters=true` по умолчанию: patch поверх текущих settings.
3723
- *
3724
- * Персист только через **`PUT .../services/{id}/tags`**: поле `tags` в теле `PUT /services/{id}`
3725
- * сервер игнорирует (`tags` в `TorrowItemKeysExcludedFromItemWriteBody`).
3922
+ * GET → локальный patch → PUT /tags с необязательной проверкой версии; автоматического readback нет.
3923
+ * mergeParameters=true сохраняет пропущенные поля, false заменяет все настройки.
3924
+ * Порядок и одинаковые дубли сервер не сохраняет; no-op определяется множеством строк.
3726
3925
  */
3727
- async updateBehaviorSettings(serviceId, settingsPatch, options) {
3728
- const service = await this.getById(serviceId, {
3729
- profileId: options?.profileId,
3730
- includeDeleted: options?.includeDeleted,
3731
- includeChildTypes: options?.includeChildTypes,
3732
- securityToken: options?.securityToken,
3733
- signal: options?.signal
3734
- });
3735
- const mergeParameters = options?.mergeParameters ?? true;
3736
- const nextSettings = mergeParameters ? {
3737
- ...this.getBehaviorSettingsFromService(service),
3738
- ...settingsPatch
3739
- } : { ...settingsPatch };
3740
- const nextTags = mergeServiceImplicitTags(service.tags, nextSettings);
3741
- await this.updateTags(serviceId, nextTags, {
3742
- versionToCheck: options?.checkVersion ? service.meta?.version ?? void 0 : void 0,
3743
- isNeedAnnouncement: options?.isNeedAnnouncement,
3744
- signal: options?.signal
3745
- });
3746
- return this.getById(serviceId, {
3747
- profileId: options?.profileId,
3748
- includeDeleted: options?.includeDeleted,
3749
- includeChildTypes: options?.includeChildTypes,
3750
- securityToken: options?.securityToken,
3751
- signal: options?.signal
3926
+ async updateBehaviorSettings(serviceId, settingsPatch, options = {}) {
3927
+ if (!options || options.versionToCheck !== void 0 && !isBehaviorVersion(options.versionToCheck) || Object.hasOwn(options, "checkVersion") && options.checkVersion !== true || Object.hasOwn(options, "mergeParameters") && typeof options.mergeParameters !== "boolean" || options.isNeedAnnouncement != null && typeof options.isNeedAnnouncement !== "boolean" || Object.keys(options).some((key) => !["versionToCheck", "checkVersion", "mergeParameters", "profileId", "securityToken", "isNeedAnnouncement", "signal"].includes(key))) {
3928
+ throw new ServiceBehaviorSettingsError("InvalidInput" /* InvalidInput */);
3929
+ }
3930
+ const normalized = normalizeBehaviorInput(settingsPatch);
3931
+ options = { ...options };
3932
+ const state = await this.readBehaviorState(serviceId, {
3933
+ profileId: options.profileId,
3934
+ securityToken: options.securityToken,
3935
+ signal: options.signal
3752
3936
  });
3937
+ assertBehaviorPermission(state.operations);
3938
+ if (options.versionToCheck !== void 0 && state.version !== options.versionToCheck) {
3939
+ throw new ServiceBehaviorSettingsError("VersionConflict" /* VersionConflict */);
3940
+ }
3941
+ const tags = mergeBehaviorSettings(state.tags, normalized, options.mergeParameters ?? true);
3942
+ const projected = readBehaviorSettings(tags);
3943
+ const unchanged = tags.length === state.tags.length && tags.every((tag) => state.tags.includes(tag));
3944
+ if (unchanged) return { serviceId, version: state.version, ...projected, changed: false };
3945
+ let dispatched = false;
3946
+ let result;
3947
+ try {
3948
+ result = await this.http.requestJson({
3949
+ method: "PUT",
3950
+ path: `${BASE9}/${encodePathSegment(serviceId)}/tags`,
3951
+ query: { versionToCheck: options.versionToCheck ?? (options.checkVersion ? state.version : void 0), isNeedAnnouncement: options.isNeedAnnouncement ?? false },
3952
+ body: tags,
3953
+ signal: options.signal
3954
+ }, () => {
3955
+ dispatched = true;
3956
+ });
3957
+ } catch (cause) {
3958
+ if (dispatched && cause instanceof TorrowHttpError && cause.status === 500 && cause.body !== null && typeof cause.body === "object" && "name" in cause.body && cause.body.name === "OBJECT_HAS_WRONG_VERSION" /* VersionConflict */ && "code" in cause.body && (cause.body.code === 208213 /* VersionConflict */ || cause.body.code === String(208213 /* VersionConflict */))) {
3959
+ throw new ServiceBehaviorSettingsError("VersionConflict" /* VersionConflict */, cause);
3960
+ }
3961
+ throw new ServiceBehaviorSettingsError(dispatched ? "WriteOutcomeUnknown" /* WriteOutcomeUnknown */ : "RequestNotSent" /* RequestNotSent */, cause);
3962
+ }
3963
+ if (!result || result.id !== serviceId || !isBehaviorVersion(result.meta?.version)) {
3964
+ throw new ServiceBehaviorSettingsError("WriteOutcomeUnknown" /* WriteOutcomeUnknown */);
3965
+ }
3966
+ return { serviceId, version: result.meta.version, ...projected, changed: true };
3753
3967
  }
3754
- /** Полная установка `ServiceBehaviorSettings` (без merge c текущими параметрами). */
3755
- setBehaviorSettings(serviceId, settings, options) {
3756
- return this.updateBehaviorSettings(serviceId, settings, {
3757
- ...options,
3758
- mergeParameters: false
3759
- });
3968
+ /** Полная замена: пропущенные поля очищаются, посторонние данные сохраняются. */
3969
+ setBehaviorSettings(serviceId, settings, options = {}) {
3970
+ return this.updateBehaviorSettings(serviceId, settings, { ...options, mergeParameters: false });
3760
3971
  }
3761
3972
  /**
3762
3973
  * Запись на услугу — `POST /api/v1/services/{serviceId}/order`.
@@ -7008,6 +7219,9 @@ export {
7008
7219
  SearchFacade,
7009
7220
  SendCodeAccountTypeValueList,
7010
7221
  SendCodeAccountTypeValues,
7222
+ ServiceBehaviorSettingsError,
7223
+ ServiceBehaviorSettingsErrorCategory,
7224
+ ServiceBehaviorSettingsIssueReason,
7011
7225
  ServiceBuilder,
7012
7226
  ServiceCheckInWizardStepParameterName,
7013
7227
  ServiceCheckInWizardStepType,