@torrow/sdk-client 0.10.0 → 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 CHANGED
@@ -60,6 +60,12 @@ Network failures use `TorrowSdkError`: `category` is independent of the outcome
60
60
 
61
61
  Selected composite methods provide optional `detail`, `operation`, `failureAt` and immutable `effects`. Missing `effects` means no description is supplied. Local builder/ID/constructor errors retain their existing exception types. Errors do not authorize automatic retry.
62
62
 
63
+ `publicMessage?: string` is an optional explanation for people or AI clients. The eight item `patch` methods supply short Russian messages from SDK templates; other methods may omit it. Use `error.publicMessage ?? "Не удалось выполнить операцию."` for display and `category`/`effects` for decisions. Wording is not a stable machine code and does not prove whether a write happened. Applications can replace it with their own localized text. The standard `Error.message` stays generic.
64
+
65
+ The constructor accepts nonblank strings of at most 512 UTF-16 code units and preserves them unchanged. Invalid values become `undefined` without throwing, coercing or truncating. This checks shape, not confidentiality: callers creating errors or using `withContext` must supply safe text. SDK patch never forwards server/callback messages or item data into this field.
66
+
67
+ `toJSON()` includes the explanation. `withContext({})` preserves it, `withContext({ publicMessage: undefined })` clears it, and a supplied string replaces it under the same validation. The original error and sensitive diagnostics remain intact. Unknown server codes, status and correlation metadata remain available through `http`; raw data stays behind `getSensitiveDetails()`.
68
+
63
69
 
64
70
  ## Частичное обновление item
65
71
 
package/dist/index.d.ts CHANGED
@@ -41638,6 +41638,7 @@ interface TorrowSdkSensitiveDetails {
41638
41638
  /** Безопасный снимок ошибки. Отсутствие effects не доказывает отсутствие изменений. */
41639
41639
  interface TorrowSdkErrorInfo {
41640
41640
  /** Общая причина отказа. */ readonly category: TorrowSdkErrorCategory;
41641
+ /** Необязательное пояснение для человека, не для ветвления. Передающий текст отвечает за его безопасность. */ readonly publicMessage?: string;
41641
41642
  /** Метаданные HTTP, если они известны. */ readonly http?: TorrowSdkHttpDetails;
41642
41643
  /** Предметное уточнение выбранного домена. */ readonly detail?: TorrowSdkErrorDetail;
41643
41644
  /** Начало fetch соответствующего запроса. */ readonly request?: TorrowSdkRequestState;
@@ -41653,6 +41654,7 @@ interface TorrowSdkErrorInit extends TorrowSdkErrorInfo {
41653
41654
  declare class TorrowSdkError extends Error implements TorrowSdkErrorInfo {
41654
41655
  #private;
41655
41656
  /** Общая причина отказа. */ readonly category: TorrowSdkErrorCategory;
41657
+ /** Пояснение без изменения исходной строки: до 512 UTF-16 единиц, не пустое после trim; иначе undefined. */ readonly publicMessage?: string;
41656
41658
  /** Безопасные метаданные ответа. */ readonly http?: TorrowSdkHttpDetails;
41657
41659
  /** Неизменяемое предметное уточнение. */ readonly detail?: TorrowSdkErrorDetail;
41658
41660
  /** Начало fetch; не доставка. */ readonly request?: TorrowSdkRequestState;
package/dist/index.js CHANGED
@@ -89,6 +89,8 @@ var AuthSessionEffectStep = /* @__PURE__ */ ((AuthSessionEffectStep2) => {
89
89
  var TorrowSdkError = class _TorrowSdkError extends Error {
90
90
  /** Общая причина отказа. */
91
91
  category;
92
+ /** Пояснение без изменения исходной строки: до 512 UTF-16 единиц, не пустое после trim; иначе undefined. */
93
+ publicMessage;
92
94
  /** Безопасные метаданные ответа. */
93
95
  http;
94
96
  /** Неизменяемое предметное уточнение. */
@@ -106,6 +108,7 @@ var TorrowSdkError = class _TorrowSdkError extends Error {
106
108
  super(`Torrow SDK: ${init.category}`);
107
109
  this.name = "TorrowSdkError";
108
110
  this.category = init.category;
111
+ this.publicMessage = typeof init.publicMessage === "string" && init.publicMessage.length <= 512 && init.publicMessage.trim().length > 0 ? init.publicMessage : void 0;
109
112
  this.http = init.http && Object.freeze({ ...init.http });
110
113
  this.detail = init.detail && Object.freeze({ ...init.detail });
111
114
  this.request = init.request;
@@ -122,6 +125,7 @@ var TorrowSdkError = class _TorrowSdkError extends Error {
122
125
  toJSON() {
123
126
  return {
124
127
  category: this.category,
128
+ publicMessage: this.publicMessage,
125
129
  http: this.http,
126
130
  detail: this.detail,
127
131
  request: this.request,
@@ -3460,91 +3464,131 @@ function validField(value, rule) {
3460
3464
  }
3461
3465
  return valid && (rule.validate?.(value) ?? true);
3462
3466
  }
3467
+ function patchPublicMessage(category, failureAt) {
3468
+ switch (category) {
3469
+ case "InvalidInput" /* InvalidInput */:
3470
+ return "\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u0447\u0430\u0441\u0442\u0438\u0447\u043D\u043E\u0433\u043E \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430 \u043D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B.";
3471
+ case "InsufficientData" /* InsufficientData */:
3472
+ return "\u0412 \u0441\u043D\u0438\u043C\u043A\u0435 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430 \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E \u0434\u0430\u043D\u043D\u044B\u0445 \u0434\u043B\u044F \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0433\u043E \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F.";
3473
+ case "AccessDenied" /* AccessDenied */:
3474
+ return "\u041D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E \u043F\u0440\u0430\u0432 \u0434\u043B\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u0447\u0430\u0441\u0442\u0438\u0447\u043D\u043E\u0433\u043E \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430.";
3475
+ case "InvalidResponse" /* InvalidResponse */:
3476
+ return failureAt === "ProjectResult" /* ProjectResult */ ? "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u0435\u043E\u0431\u0440\u0430\u0437\u043E\u0432\u0430\u0442\u044C \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043D\u044B\u0439 \u044D\u043B\u0435\u043C\u0435\u043D\u0442 \u0432 \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 SDK." : "\u041E\u0442\u0432\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043D\u0435\u043F\u0440\u0438\u0433\u043E\u0434\u0435\u043D \u0434\u043B\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u0447\u0430\u0441\u0442\u0438\u0447\u043D\u043E\u0433\u043E \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430.";
3477
+ case "AuthenticationRequired" /* AuthenticationRequired */:
3478
+ return "\u0414\u043B\u044F \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u0447\u0430\u0441\u0442\u0438\u0447\u043D\u043E\u0433\u043E \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u0430\u0443\u0442\u0435\u043D\u0442\u0438\u0444\u0438\u043A\u0430\u0446\u0438\u044F.";
3479
+ case "Cancelled" /* Cancelled */:
3480
+ return "\u0412\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u0435 \u0447\u0430\u0441\u0442\u0438\u0447\u043D\u043E\u0433\u043E \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430 \u043E\u0442\u043C\u0435\u043D\u0435\u043D\u043E.";
3481
+ case "RateLimited" /* RateLimited */:
3482
+ return "\u0421\u0435\u0440\u0432\u0435\u0440 \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0438\u043B \u0447\u0430\u0441\u0442\u043E\u0442\u0443 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432 \u043F\u0440\u0438 \u0447\u0430\u0441\u0442\u0438\u0447\u043D\u043E\u043C \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0438 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430.";
3483
+ case "TransportFailure" /* TransportFailure */:
3484
+ return "\u0421\u0431\u043E\u0439 \u0441\u0432\u044F\u0437\u0438 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043E\u043C \u043F\u0440\u0438 \u0447\u0430\u0441\u0442\u0438\u0447\u043D\u043E\u043C \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0438 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430.";
3485
+ case "HttpFailure" /* HttpFailure */:
3486
+ return "\u0421\u0435\u0440\u0432\u0435\u0440 \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u043B \u0437\u0430\u043F\u0440\u043E\u0441 \u043F\u0440\u0438 \u0447\u0430\u0441\u0442\u0438\u0447\u043D\u043E\u043C \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0438 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430.";
3487
+ default:
3488
+ return "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u0447\u0430\u0441\u0442\u0438\u0447\u043D\u043E\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0435 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430.";
3489
+ }
3490
+ }
3463
3491
  async function patchItem(http, itemId, fields, options, adapter) {
3464
3492
  let failureAt = "ValidateInput" /* ValidateInput */;
3465
3493
  let effect = "NotApplied" /* NotApplied */;
3494
+ const trustedErrors = /* @__PURE__ */ new Set();
3495
+ function localError(category, publicMessage) {
3496
+ const error = new TorrowSdkError({
3497
+ category,
3498
+ publicMessage: publicMessage ?? patchPublicMessage(category, failureAt),
3499
+ detail: { domain: "ItemPatch" /* ItemPatch */ }
3500
+ });
3501
+ trustedErrors.add(error);
3502
+ return error;
3503
+ }
3466
3504
  try {
3467
3505
  try {
3468
3506
  if (torrowObjectTypeToCode(getTorrowObjectTypeFromTorrowId(itemId)) !== adapter.objectType) {
3469
- throw itemPatchError("InvalidInput" /* InvalidInput */);
3507
+ throw localError("InvalidInput" /* InvalidInput */);
3470
3508
  }
3471
3509
  } catch {
3472
- throw itemPatchError("InvalidInput" /* InvalidInput */);
3510
+ throw localError("InvalidInput" /* InvalidInput */);
3473
3511
  }
3474
3512
  const opts = options ?? {};
3475
3513
  if (!isRecord(opts) || Object.keys(opts).some((key) => key !== "versionToCheck" && key !== "signal")) {
3476
- throw itemPatchError("InvalidInput" /* InvalidInput */);
3514
+ throw localError("InvalidInput" /* InvalidInput */);
3477
3515
  }
3478
3516
  if (options === null) {
3479
- throw itemPatchError("InvalidInput" /* InvalidInput */);
3517
+ throw localError("InvalidInput" /* InvalidInput */);
3480
3518
  }
3481
3519
  const expectedVersion = opts.versionToCheck;
3482
3520
  const signal = opts.signal;
3483
3521
  if (expectedVersion !== void 0 && !isVersion(expectedVersion) || Object.hasOwn(opts, "versionToCheck") && expectedVersion === void 0 || signal !== void 0 && !isAbortSignal(signal)) {
3484
- throw itemPatchError("InvalidInput" /* InvalidInput */);
3522
+ throw localError("InvalidInput" /* InvalidInput */);
3485
3523
  }
3486
3524
  const selected = copyValue(fields);
3487
3525
  if (!isRecord(selected) || Object.keys(selected).length === 0) {
3488
- throw itemPatchError("InvalidInput" /* InvalidInput */);
3526
+ throw localError("InvalidInput" /* InvalidInput */);
3489
3527
  }
3490
3528
  for (const [key, value] of Object.entries(selected)) {
3491
3529
  if (!Object.hasOwn(adapter.fields, key) || !validField(value, adapter.fields[key])) {
3492
- throw itemPatchError("InvalidInput" /* InvalidInput */);
3530
+ throw localError("InvalidInput" /* InvalidInput */);
3493
3531
  }
3494
3532
  }
3495
3533
  adapter.validateFields?.(selected);
3496
3534
  const readQuery = adapter.readQuery ? { ...adapter.readQuery } : void 0;
3497
3535
  const writeQuery = adapter.writeQuery ? { ...adapter.writeQuery } : void 0;
3498
3536
  if (signal?.aborted) {
3499
- throw itemPatchError("Cancelled" /* Cancelled */);
3537
+ throw localError("Cancelled" /* Cancelled */);
3500
3538
  }
3501
3539
  const path = `${adapter.basePath}/${encodePathSegment(itemId)}`;
3502
3540
  failureAt = "ReadCurrent" /* ReadCurrent */;
3503
3541
  const dto = await http.requestJson({ method: "GET", path, query: readQuery, signal });
3504
3542
  failureAt = "PrepareWrite" /* PrepareWrite */;
3505
3543
  if (!isRecord(dto) || dto.id !== itemId) {
3506
- throw itemPatchError("InvalidResponse" /* InvalidResponse */);
3544
+ throw localError("InvalidResponse" /* InvalidResponse */);
3507
3545
  }
3508
3546
  if (dto.meta == null || dto.personalInfo == null) {
3509
- throw itemPatchError("InsufficientData" /* InsufficientData */);
3547
+ throw localError("InsufficientData" /* InsufficientData */);
3510
3548
  }
3511
3549
  if (!isRecord(dto.meta) || !isRecord(dto.personalInfo)) {
3512
- throw itemPatchError("InvalidResponse" /* InvalidResponse */);
3550
+ throw localError("InvalidResponse" /* InvalidResponse */);
3513
3551
  }
3514
3552
  const contentType = dto.personalInfo.itemContentType;
3515
3553
  if (contentType === "NoRights" /* NoRights */) {
3516
- throw itemPatchError("AccessDenied" /* AccessDenied */);
3554
+ throw localError("AccessDenied" /* AccessDenied */, "\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A \u043F\u043E\u043B\u043D\u043E\u043C\u0443 \u0441\u043D\u0438\u043C\u043A\u0443 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430.");
3517
3555
  }
3518
3556
  if (contentType !== void 0 && contentType !== null && typeof contentType !== "string") {
3519
- throw itemPatchError("InvalidResponse" /* InvalidResponse */);
3557
+ throw localError("InvalidResponse" /* InvalidResponse */);
3520
3558
  }
3521
3559
  if (!isVersion(dto.meta.version) || contentType !== "Full" /* Full */) {
3522
- throw itemPatchError("InsufficientData" /* InsufficientData */);
3560
+ throw localError(
3561
+ "InsufficientData" /* InsufficientData */,
3562
+ "\u0421\u043D\u0438\u043C\u043E\u043A \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430 \u043D\u0435 \u044F\u0432\u043B\u044F\u0435\u0442\u0441\u044F \u043F\u043E\u043B\u043D\u044B\u043C \u0438\u043B\u0438 \u043D\u0435 \u0441\u043E\u0434\u0435\u0440\u0436\u0438\u0442 \u043F\u0440\u0438\u0433\u043E\u0434\u043D\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0434\u043B\u044F \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0433\u043E \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F."
3563
+ );
3523
3564
  }
3524
3565
  const operations = dto.personalInfo.operationList;
3525
3566
  if (operations == null) {
3526
- throw itemPatchError("InsufficientData" /* InsufficientData */);
3567
+ throw localError("InsufficientData" /* InsufficientData */);
3527
3568
  }
3528
3569
  if (!Array.isArray(operations) || operations.some((value) => typeof value !== "string")) {
3529
- throw itemPatchError("InvalidResponse" /* InvalidResponse */);
3570
+ throw localError("InvalidResponse" /* InvalidResponse */);
3530
3571
  }
3531
3572
  if (!operations.includes("UpdateItem" /* UpdateItem */)) {
3532
- throw itemPatchError("AccessDenied" /* AccessDenied */);
3573
+ throw localError("AccessDenied" /* AccessDenied */, "\u0412 \u0441\u043D\u0438\u043C\u043A\u0435 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043F\u0440\u0430\u0432\u043E UpdateItem.");
3533
3574
  }
3534
3575
  if (Object.hasOwn(adapter.fields, "name")) {
3535
3576
  if (dto.name == null) {
3536
- throw itemPatchError("InsufficientData" /* InsufficientData */);
3577
+ throw localError("InsufficientData" /* InsufficientData */);
3537
3578
  }
3538
3579
  if (typeof dto.name !== "string" || dto.name.length === 0) {
3539
- throw itemPatchError("InvalidResponse" /* InvalidResponse */);
3580
+ throw localError("InvalidResponse" /* InvalidResponse */);
3540
3581
  }
3541
3582
  }
3542
3583
  adapter.assertSnapshot?.(dto);
3543
3584
  if (expectedVersion !== void 0 && expectedVersion !== dto.meta.version) {
3544
- throw itemPatchError("VersionConflict" /* VersionConflict */);
3585
+ throw localError(
3586
+ "VersionConflict" /* VersionConflict */,
3587
+ "\u041E\u0436\u0438\u0434\u0430\u0435\u043C\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430 \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0435\u0442 \u0441 \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043D\u043E\u0433\u043E \u0441\u043D\u0438\u043C\u043A\u0430."
3588
+ );
3545
3589
  }
3546
3590
  if (signal?.aborted) {
3547
- throw itemPatchError("Cancelled" /* Cancelled */);
3591
+ throw localError("Cancelled" /* Cancelled */);
3548
3592
  }
3549
3593
  const unchanged = Object.entries(selected).every(([key, value]) => {
3550
3594
  const equal = adapter.equalField?.(key, dto[key], value, dto);
@@ -3587,13 +3631,18 @@ async function patchItem(http, itemId, fields, options, adapter) {
3587
3631
  } catch (cause) {
3588
3632
  if (dispatched && cause instanceof TorrowSdkError && adapter.casRefusal?.(cause)) {
3589
3633
  effect = "NotApplied" /* NotApplied */;
3590
- throw cause.withContext({ category: "VersionConflict" /* VersionConflict */ });
3634
+ const error = cause.withContext({
3635
+ category: "VersionConflict" /* VersionConflict */,
3636
+ publicMessage: "\u0421\u0435\u0440\u0432\u0435\u0440 \u043E\u0442\u043A\u043B\u043E\u043D\u0438\u043B \u0437\u0430\u043F\u0438\u0441\u044C \u0438\u0437-\u0437\u0430 \u043A\u043E\u043D\u043A\u0443\u0440\u0435\u043D\u0442\u043D\u043E\u0433\u043E \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0432\u0435\u0440\u0441\u0438\u0438 \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u0430."
3637
+ });
3638
+ trustedErrors.add(error);
3639
+ throw error;
3591
3640
  }
3592
3641
  throw cause;
3593
3642
  }
3594
3643
  failureAt = "ValidateWriteResponse" /* ValidateWriteResponse */;
3595
3644
  if (!isRecord(ack) || ack.id !== itemId) {
3596
- throw itemPatchError("InvalidResponse" /* InvalidResponse */);
3645
+ throw localError("InvalidResponse" /* InvalidResponse */);
3597
3646
  }
3598
3647
  effect = "Applied" /* Applied */;
3599
3648
  failureAt = "ProjectResult" /* ProjectResult */;
@@ -3601,6 +3650,7 @@ async function patchItem(http, itemId, fields, options, adapter) {
3601
3650
  } catch (cause) {
3602
3651
  const error = cause instanceof TorrowSdkError ? cause : new TorrowSdkError({ category: "Unknown" /* Unknown */, sensitive: { cause } });
3603
3652
  throw error.withContext({
3653
+ publicMessage: trustedErrors.has(error) ? error.publicMessage : patchPublicMessage(error.category, failureAt),
3604
3654
  operation: adapter.operation,
3605
3655
  failureAt,
3606
3656
  detail: { domain: "ItemPatch" /* ItemPatch */ },