@coopenomics/extension-kit 2026.9.22 → 2026.9.24-2

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.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { createParamDecorator, UnauthorizedException, SetMetadata, applyDecorators, Injectable, Inject, ForbiddenException, HttpException, HttpStatus, BadRequestException } from '@nestjs/common';
1
+ import { HttpException, HttpStatus, BadGatewayException, ServiceUnavailableException, BadRequestException, UnauthorizedException, ForbiddenException, NotFoundException, ConflictException, UnprocessableEntityException, InternalServerErrorException, createParamDecorator, SetMetadata, applyDecorators, Injectable, Inject } from '@nestjs/common';
2
2
  import { GqlExecutionContext, Directive, Field, InputType, registerEnumType, Int, IntersectionType, OmitType, ObjectType } from '@nestjs/graphql';
3
+ import { te, t } from '@coopenomics/i18n/server';
3
4
  import { Reflector } from '@nestjs/core';
4
5
  import { AuthGuard } from '@nestjs/passport';
5
6
  import { timingSafeEqual } from 'node:crypto';
@@ -10,11 +11,155 @@ import { GraphQLJSON } from 'graphql-type-json';
10
11
  import { Classes } from '@coopenomics/sdk';
11
12
  import { Name } from '@wharfkit/antelope';
12
13
  import moment from 'moment';
14
+ import crypto, { createHash } from 'crypto';
13
15
  import moment$1 from 'moment-timezone';
14
16
  import 'moment/locale/ru';
15
- import crypto from 'crypto';
16
17
  import { _Common } from 'cooptypes';
17
18
 
19
+ const DOMAIN_ERROR = Symbol.for("@coopenomics/extension-kit/DomainError");
20
+ function domainErrorMessage(code, params = {}) {
21
+ const key = `errors.${code}`;
22
+ if (!te(key) && typeof params.message === "string")
23
+ return params.message;
24
+ return t(key, params);
25
+ }
26
+ function body(code, params, status) {
27
+ return { statusCode: status, code, params, message: domainErrorMessage(code, params) };
28
+ }
29
+ function brand(target, code, params) {
30
+ target.code = code;
31
+ target.params = params;
32
+ target[DOMAIN_ERROR] = true;
33
+ target.name = "DomainError";
34
+ }
35
+ class DomainError extends HttpException {
36
+ static [Symbol.hasInstance](value) {
37
+ return !!value && value[DOMAIN_ERROR] === true;
38
+ }
39
+ constructor(code, params = {}, status = HttpStatus.BAD_REQUEST) {
40
+ super(body(code, params, status), status);
41
+ brand(this, code, params);
42
+ }
43
+ /** Ключ сообщения в словаре. */
44
+ get messageKey() {
45
+ return `errors.${this.code}`;
46
+ }
47
+ static badRequest(code, params = {}) {
48
+ return new DomainBadRequest(code, params);
49
+ }
50
+ static unauthorized(code, params = {}) {
51
+ return new DomainUnauthorized(code, params);
52
+ }
53
+ static forbidden(code, params = {}) {
54
+ return new DomainForbidden(code, params);
55
+ }
56
+ static notFound(code, params = {}) {
57
+ return new DomainNotFound(code, params);
58
+ }
59
+ static conflict(code, params = {}) {
60
+ return new DomainConflict(code, params);
61
+ }
62
+ static unprocessable(code, params = {}) {
63
+ return new DomainUnprocessable(code, params);
64
+ }
65
+ static tooManyRequests(code, params = {}) {
66
+ return new DomainError(code, params, HttpStatus.TOO_MANY_REQUESTS);
67
+ }
68
+ static internal(code, params = {}) {
69
+ return new DomainInternal(code, params);
70
+ }
71
+ static badGateway(code, params = {}) {
72
+ return new DomainBadGateway(code, params);
73
+ }
74
+ static serviceUnavailable(code, params = {}) {
75
+ return new DomainServiceUnavailable(code, params);
76
+ }
77
+ }
78
+ class DomainBadGateway extends BadGatewayException {
79
+ constructor(code, params) {
80
+ super(body(code, params, HttpStatus.BAD_GATEWAY));
81
+ brand(this, code, params);
82
+ }
83
+ get messageKey() {
84
+ return `errors.${this.code}`;
85
+ }
86
+ }
87
+ class DomainServiceUnavailable extends ServiceUnavailableException {
88
+ constructor(code, params) {
89
+ super(body(code, params, HttpStatus.SERVICE_UNAVAILABLE));
90
+ brand(this, code, params);
91
+ }
92
+ get messageKey() {
93
+ return `errors.${this.code}`;
94
+ }
95
+ }
96
+ class DomainBadRequest extends BadRequestException {
97
+ constructor(code, params) {
98
+ super(body(code, params, HttpStatus.BAD_REQUEST));
99
+ brand(this, code, params);
100
+ }
101
+ get messageKey() {
102
+ return `errors.${this.code}`;
103
+ }
104
+ }
105
+ class DomainUnauthorized extends UnauthorizedException {
106
+ constructor(code, params) {
107
+ super(body(code, params, HttpStatus.UNAUTHORIZED));
108
+ brand(this, code, params);
109
+ }
110
+ get messageKey() {
111
+ return `errors.${this.code}`;
112
+ }
113
+ }
114
+ class DomainForbidden extends ForbiddenException {
115
+ constructor(code, params) {
116
+ super(body(code, params, HttpStatus.FORBIDDEN));
117
+ brand(this, code, params);
118
+ }
119
+ get messageKey() {
120
+ return `errors.${this.code}`;
121
+ }
122
+ }
123
+ class DomainNotFound extends NotFoundException {
124
+ constructor(code, params) {
125
+ super(body(code, params, HttpStatus.NOT_FOUND));
126
+ brand(this, code, params);
127
+ }
128
+ get messageKey() {
129
+ return `errors.${this.code}`;
130
+ }
131
+ }
132
+ class DomainConflict extends ConflictException {
133
+ constructor(code, params) {
134
+ super(body(code, params, HttpStatus.CONFLICT));
135
+ brand(this, code, params);
136
+ }
137
+ get messageKey() {
138
+ return `errors.${this.code}`;
139
+ }
140
+ }
141
+ class DomainUnprocessable extends UnprocessableEntityException {
142
+ constructor(code, params) {
143
+ super(body(code, params, HttpStatus.UNPROCESSABLE_ENTITY));
144
+ brand(this, code, params);
145
+ }
146
+ get messageKey() {
147
+ return `errors.${this.code}`;
148
+ }
149
+ }
150
+ class DomainInternal extends InternalServerErrorException {
151
+ constructor(code, params) {
152
+ super(body(code, params, HttpStatus.INTERNAL_SERVER_ERROR));
153
+ brand(this, code, params);
154
+ }
155
+ get messageKey() {
156
+ return `errors.${this.code}`;
157
+ }
158
+ }
159
+ function validationMessage(key, params = {}) {
160
+ return (args) => t(key, { property: args.property, value: String(args.value ?? ""), ...params });
161
+ }
162
+
18
163
  const ROLES_DENY_SELF_KEY = "roles_deny_self";
19
164
  const ROLES_ANY_STATUS_KEY = "roles_any_status";
20
165
  function AuthRoles(roles, options = {}) {
@@ -31,7 +176,7 @@ const CurrentUser = createParamDecorator((data, context) => {
31
176
  const ctx = GqlExecutionContext.create(context);
32
177
  const request = ctx.getContext().req;
33
178
  if (!request?.user) {
34
- throw new UnauthorizedException("\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C \u043D\u0435 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u043E\u0432\u0430\u043D");
179
+ throw DomainError.unauthorized("KIT_USER_NOT_AUTHORIZED");
35
180
  }
36
181
  return request?.user;
37
182
  });
@@ -114,7 +259,7 @@ let OptionalGqlJwtAuthGuard = class extends AuthGuard("jwt") {
114
259
  if (err instanceof Error) {
115
260
  throw err;
116
261
  }
117
- throw new UnauthorizedException("\u0421\u0435\u0441\u0441\u0438\u044F \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u0430, \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u0430\u044F \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u044F");
262
+ throw DomainError.unauthorized("KIT_SESSION_ENDED");
118
263
  }
119
264
  };
120
265
  OptionalGqlJwtAuthGuard = __decorateClass$g([
@@ -164,11 +309,11 @@ let RolesGuard = class {
164
309
  if (allowedRoles.includes(user.role)) {
165
310
  const anyStatus = this.reflector.get(ROLES_ANY_STATUS_KEY, context.getHandler()) === true;
166
311
  if (user.role === PARTICIPANT_ROLE && !anyStatus && user.status !== ACTIVE_USER_STATUS) {
167
- throw new ForbiddenException("\u0414\u043E\u0441\u0442\u0443\u043F \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u043F\u0430\u0439\u0449\u0438\u043A\u043E\u0432 \u043A\u043E\u043E\u043F\u0435\u0440\u0430\u0442\u0438\u0432\u0430");
312
+ throw DomainError.forbidden("KIT_MEMBERS_ONLY");
168
313
  }
169
314
  return true;
170
315
  }
171
- throw new UnauthorizedException(`\u041D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E \u043F\u0440\u0430\u0432 \u0434\u043E\u0441\u0442\u0443\u043F\u0430`);
316
+ throw DomainError.unauthorized("KIT_INSUFFICIENT_RIGHTS");
172
317
  }
173
318
  };
174
319
  RolesGuard = __decorateClass$g([
@@ -184,12 +329,10 @@ let ActiveUserStatusGuard = class {
184
329
  }
185
330
  const user = request.user;
186
331
  if (!user?.status) {
187
- throw new ForbiddenException("\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C");
332
+ throw DomainError.forbidden("KIT_AUTHORIZED_USER_REQUIRED");
188
333
  }
189
334
  if (user.status !== ACTIVE_USER_STATUS) {
190
- throw new ForbiddenException(
191
- "\u0414\u043E\u0441\u0442\u0443\u043F \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u043F\u0430\u0439\u0449\u0438\u043A\u043E\u0432 \u0432 \u0441\u0442\u0430\u0442\u0443\u0441\u0435 \xABactive\xBB"
192
- );
335
+ throw DomainError.forbidden("KIT_ACTIVE_MEMBERS_ONLY");
193
336
  }
194
337
  return true;
195
338
  }
@@ -254,6 +397,7 @@ const BucketRegistry = {
254
397
  const existing = REGISTERED.get(cls);
255
398
  if (existing && existing.name !== spec.name) {
256
399
  throw new Error(
400
+ // i18n-ignore: ошибка регистрации бакета при старте, до пайщика не доходит
257
401
  `BucketRegistry: \u043A\u043B\u0430\u0441\u0441 ${cls.name} \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D \u043F\u043E\u0434 \u0431\u0430\u043A\u0435\u0442\u043E\u043C '${existing.name}', \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u0430\u044F \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u044F \u043F\u043E\u0434 '${spec.name}' \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D\u0430`
258
402
  );
259
403
  }
@@ -290,6 +434,7 @@ function bucketProvidersFor(fileStoragePortToken, consumers) {
290
434
  const spec = BucketRegistry.get(cls);
291
435
  if (!spec) {
292
436
  throw new Error(
437
+ // i18n-ignore: ошибка объявления хранилища при старте, до пайщика не доходит
293
438
  `bucketProvidersFor: \u043A\u043B\u0430\u0441\u0441 ${cls.name} \u043D\u0435 \u043F\u043E\u043C\u0435\u0447\u0435\u043D @UseBucket \u2014 \u043E\u0431\u044A\u044F\u0432\u0438\u0442\u0435 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435 \u0440\u044F\u0434\u043E\u043C \u0441 \u0441\u0435\u0440\u0432\u0438\u0441\u043E\u043C`
294
439
  );
295
440
  }
@@ -647,7 +792,7 @@ __decorateClass$a([
647
792
  Field(() => String, { description: "\u0421\u0440\u043E\u043A \u0438\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F (\xAB\u0432 \u0441\u0440\u043E\u043A \u0434\u043E\xBB), \u0444\u043E\u0440\u043C\u0430\u0442 DD.MM.YYYY" }),
648
793
  IsString(),
649
794
  Matches(/^\d{2}\.\d{2}\.\d{4}$/, {
650
- message: "\u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u0441\u0440\u043E\u043A \u0438\u0441\u043F\u043E\u043B\u043D\u0435\u043D\u0438\u044F \u0440\u0430\u0441\u0445\u043E\u0434\u0430 \u0432 \u0444\u043E\u0440\u043C\u0430\u0442\u0435 \u0414\u0414.\u041C\u041C.\u0413\u0413\u0413\u0413"
795
+ message: validationMessage("kit.expenseStatement.dueDateFormat")
651
796
  })
652
797
  ], ExpenseProposalHeaderInputDTO.prototype, "deadline", 2);
653
798
  __decorateClass$a([
@@ -874,6 +1019,7 @@ let entities;
874
1019
  function registerExtensionEntities(list) {
875
1020
  if (entities) {
876
1021
  throw new Error(
1022
+ // i18n-ignore: ошибка сборки приложения при старте, до пайщика не доходит
877
1023
  "\u0421\u043E\u0441\u0442\u0430\u0432 \u0442\u0430\u0431\u043B\u0438\u0446 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0439 \u0443\u0436\u0435 \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D: registerExtensionEntities() \u0432\u044B\u0437\u044B\u0432\u0430\u0435\u0442\u0441\u044F \u043E\u0434\u0438\u043D \u0440\u0430\u0437 \u043F\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435"
878
1024
  );
879
1025
  }
@@ -886,6 +1032,23 @@ function resetExtensionEntities() {
886
1032
  entities = void 0;
887
1033
  }
888
1034
 
1035
+ let migrations;
1036
+ function registerExtensionDatabaseMigrations(list) {
1037
+ if (migrations) {
1038
+ throw new Error(
1039
+ // i18n-ignore: ошибка сборки composition root для разработчика, до пайщика не доходит
1040
+ "\u041C\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0442\u0430\u0431\u043B\u0438\u0446 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0439 \u0443\u0436\u0435 \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D\u044B: registerExtensionDatabaseMigrations() \u0432\u044B\u0437\u044B\u0432\u0430\u0435\u0442\u0441\u044F \u043E\u0434\u0438\u043D \u0440\u0430\u0437 \u043F\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435"
1041
+ );
1042
+ }
1043
+ migrations = [...list];
1044
+ }
1045
+ function extensionDatabaseMigrations() {
1046
+ return migrations ?? [];
1047
+ }
1048
+ function resetExtensionDatabaseMigrations() {
1049
+ migrations = void 0;
1050
+ }
1051
+
889
1052
  var __defProp$7 = Object.defineProperty;
890
1053
  var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
891
1054
  var __decorateClass$7 = (decorators, target, key, kind) => {
@@ -897,6 +1060,7 @@ var __decorateClass$7 = (decorators, target, key, kind) => {
897
1060
  __defProp$7(target, key, result);
898
1061
  return result;
899
1062
  };
1063
+ const SORT_FIELD_PATTERN = /^([A-Za-z_][A-Za-z0-9_]*(:(asc|desc))?)?$/i;
900
1064
  let PaginationInputDTO = class {
901
1065
  };
902
1066
  __decorateClass$7([
@@ -906,7 +1070,9 @@ __decorateClass$7([
906
1070
  Field(() => Int, { description: "\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432 \u043D\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0435", defaultValue: 10 })
907
1071
  ], PaginationInputDTO.prototype, "limit", 2);
908
1072
  __decorateClass$7([
909
- Field(() => String, { nullable: true, description: '\u041A\u043B\u044E\u0447 \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0438 (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440, "name")' })
1073
+ Field(() => String, { nullable: true, description: '\u041A\u043B\u044E\u0447 \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0438 (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440, "name")' }),
1074
+ IsOptional(),
1075
+ Matches(SORT_FIELD_PATTERN, { message: validationMessage("kit.pagination.invalidSortField") })
910
1076
  ], PaginationInputDTO.prototype, "sortBy", 2);
911
1077
  __decorateClass$7([
912
1078
  Field(() => String, {
@@ -980,13 +1146,16 @@ class PaginationUtils {
980
1146
  static validatePaginationOptions(options) {
981
1147
  const { page = 1, limit = 10, sortBy, sortOrder = "ASC" } = options;
982
1148
  if (page < 1) {
983
- throw new Error("\u041D\u043E\u043C\u0435\u0440 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B \u0434\u043E\u043B\u0436\u0435\u043D \u0431\u044B\u0442\u044C \u0431\u043E\u043B\u044C\u0448\u0435 0");
1149
+ throw DomainError.internal("KIT_PAGE_NUMBER_INVALID");
984
1150
  }
985
1151
  if (limit < 1 || limit > 1e3) {
986
- throw new Error("\u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u044D\u043B\u0435\u043C\u0435\u043D\u0442\u043E\u0432 \u043D\u0430 \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043E\u0442 1 \u0434\u043E 1000");
1152
+ throw DomainError.internal("KIT_PAGE_LIMIT_INVALID");
987
1153
  }
988
1154
  if (sortOrder !== "ASC" && sortOrder !== "DESC") {
989
- throw new Error("\u041D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0438 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C ASC \u0438\u043B\u0438 DESC");
1155
+ throw DomainError.internal("KIT_SORT_ORDER_INVALID");
1156
+ }
1157
+ if (sortBy !== void 0 && sortBy !== null && !SORT_FIELD_PATTERN.test(sortBy)) {
1158
+ throw DomainError.internal("KIT_SORT_FIELD_INVALID");
990
1159
  }
991
1160
  return {
992
1161
  page,
@@ -1116,6 +1285,7 @@ function configurePlatformSettings(value) {
1116
1285
  function platformSettings() {
1117
1286
  if (!settings) {
1118
1287
  throw new Error(
1288
+ // i18n-ignore: ошибка сборки приложения при старте, до пайщика не доходит
1119
1289
  "\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043A\u043E\u043D\u0442\u0443\u0440\u0430 \u043D\u0435 \u0437\u0430\u0434\u0430\u043D\u044B: composition root \u043E\u0431\u044F\u0437\u0430\u043D \u0432\u044B\u0437\u0432\u0430\u0442\u044C configurePlatformSettings() \u043F\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435"
1120
1290
  );
1121
1291
  }
@@ -1333,19 +1503,6 @@ DocumentAggregateDTO = __decorateClass$1([
1333
1503
  ObjectType("DocumentAggregate")
1334
1504
  ], DocumentAggregateDTO);
1335
1505
 
1336
- class HttpApiError extends HttpException {
1337
- constructor(statusCode, message, isOperational = true, stack, subcode) {
1338
- super(message, statusCode);
1339
- this.isOperational = isOperational;
1340
- this.subcode = subcode;
1341
- if (stack) {
1342
- this.stack = stack;
1343
- } else {
1344
- Error.captureStackTrace(this, this.constructor);
1345
- }
1346
- }
1347
- }
1348
-
1349
1506
  class CurrencyValidationUtil {
1350
1507
  /**
1351
1508
  * Проверяет, что сумма содержит правильный символ валюты
@@ -1365,10 +1522,10 @@ class CurrencyValidationUtil {
1365
1522
  * @param fieldName Название поля для сообщения об ошибке (по умолчанию "сумма")
1366
1523
  * @throws Error если символ валюты неправильный
1367
1524
  */
1368
- static validateCurrencySymbol(amount, fieldName = "\u0441\u0443\u043C\u043C\u0430") {
1525
+ static validateCurrencySymbol(amount, fieldName = t("kit.currency.defaultFieldName")) {
1369
1526
  if (!this.hasValidCurrencySymbol(amount)) {
1370
1527
  const expectedSymbol = platformSettings().blockchain.rootGovernSymbol;
1371
- throw new Error(`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0441\u0438\u043C\u0432\u043E\u043B \u0432\u0430\u043B\u044E\u0442\u044B \u0432 ${fieldName}. \u041E\u0436\u0438\u0434\u0430\u043B\u0441\u044F: ${expectedSymbol}`);
1528
+ throw DomainError.internal("KIT_CURRENCY_SYMBOL_INVALID", { fieldName, expectedSymbol });
1372
1529
  }
1373
1530
  }
1374
1531
  /**
@@ -1412,18 +1569,12 @@ class CurrencyValidationUtil {
1412
1569
  async function verifySignedDocumentAgainstStoredDraft(loadGeneratedByDocHash, signed, metaVerifications) {
1413
1570
  const generated = await loadGeneratedByDocHash(signed.doc_hash);
1414
1571
  if (!generated) {
1415
- throw new HttpApiError(
1416
- HttpStatus.BAD_REQUEST,
1417
- `\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0439 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442 \u0441 \u0445\u0435\u0448\u0435\u043C ${signed.doc_hash} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D. \u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0439\u0442\u0435 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442.`
1418
- );
1572
+ throw DomainError.badRequest("KIT_GENERATED_DOCUMENT_NOT_FOUND", { docHash: signed.doc_hash });
1419
1573
  }
1420
1574
  const comparison = await Classes.Document.compareDocuments(signed, generated);
1421
1575
  if (!comparison.isValid) {
1422
- const differences = Object.entries(comparison.differences).map(([field, values]) => `${field}: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C "${values.expected}", \u043F\u043E\u043B\u0443\u0447\u0435\u043D\u043E "${values.actual}"`).join("; ");
1423
- throw new HttpApiError(
1424
- HttpStatus.BAD_REQUEST,
1425
- `\u0421\u0432\u0435\u0440\u043A\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u0430\u043D\u043D\u043E\u0433\u043E \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430 \u0441 \u0447\u0435\u0440\u043D\u043E\u0432\u0438\u043A\u043E\u043C \u043D\u0435 \u043F\u0440\u043E\u0448\u043B\u0430: ${differences}. \u0412\u043E\u0437\u043C\u043E\u0436\u043D\u0430 \u043F\u043E\u0434\u043C\u0435\u043D\u0430 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430.`
1426
- );
1576
+ const differences = Object.entries(comparison.differences).map(([field, values]) => t("kit.documentVerification.fieldDifference", { field, expected: values.expected, actual: values.actual })).join("; ");
1577
+ throw DomainError.badRequest("KIT_DOCUMENT_DRAFT_MISMATCH", { differences });
1427
1578
  }
1428
1579
  if (!metaVerifications || metaVerifications.length === 0) {
1429
1580
  return;
@@ -1433,10 +1584,7 @@ async function verifySignedDocumentAgainstStoredDraft(loadGeneratedByDocHash, si
1433
1584
  const raw = meta[field];
1434
1585
  const actualStr = typeof raw === "string" ? raw.trim() : raw !== void 0 && raw !== null ? String(raw).trim() : "";
1435
1586
  if (!actualStr) {
1436
- throw new HttpApiError(
1437
- HttpStatus.BAD_REQUEST,
1438
- `\u0412 \u043C\u0435\u0442\u0430\u0434\u0430\u043D\u043D\u044B\u0445 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430 \u043E\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043F\u043E\u043B\u0435 \xAB${field}\xBB, \u0442\u0440\u0435\u0431\u0443\u0435\u043C\u043E\u0435 \u0434\u043B\u044F \u0441\u0432\u0435\u0440\u043A\u0438.`
1439
- );
1587
+ throw DomainError.badRequest("KIT_DOCUMENT_META_FIELD_MISSING", { field });
1440
1588
  }
1441
1589
  const expectedTrimmed = expected.trim();
1442
1590
  switch (mode) {
@@ -1444,39 +1592,27 @@ async function verifySignedDocumentAgainstStoredDraft(loadGeneratedByDocHash, si
1444
1592
  const parsedExpected = CurrencyValidationUtil.extractAmountValue(expectedTrimmed);
1445
1593
  const parsedActual = CurrencyValidationUtil.extractAmountValue(actualStr);
1446
1594
  if (Number.isNaN(parsedExpected) || Number.isNaN(parsedActual)) {
1447
- throw new HttpApiError(
1448
- HttpStatus.BAD_REQUEST,
1449
- `\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u0441\u0443\u043C\u043C\u044B \u0432 \u043F\u043E\u043B\u0435 \xAB${field}\xBB \u0438\u043B\u0438 \u0432 \u043E\u0436\u0438\u0434\u0430\u0435\u043C\u043E\u043C \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0438.`
1450
- );
1595
+ throw DomainError.badRequest("KIT_DOCUMENT_AMOUNT_FORMAT_INVALID", { field });
1451
1596
  }
1452
1597
  if (CurrencyValidationUtil.formatAmount(parsedExpected) !== CurrencyValidationUtil.formatAmount(parsedActual)) {
1453
- throw new HttpApiError(
1454
- HttpStatus.BAD_REQUEST,
1455
- `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u044F \xAB${field}\xBB \u0432 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0435 (${actualStr}) \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0435\u0442 \u0441 \u043E\u0436\u0438\u0434\u0430\u0435\u043C\u044B\u043C (${expectedTrimmed}). \u0412\u043E\u0437\u043C\u043E\u0436\u043D\u0430 \u043F\u043E\u0434\u043C\u0435\u043D\u0430 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430.`
1456
- );
1598
+ throw DomainError.badRequest("KIT_DOCUMENT_FIELD_MISMATCH", { field, actual: actualStr, expected: expectedTrimmed });
1457
1599
  }
1458
1600
  break;
1459
1601
  }
1460
1602
  case "string_trim": {
1461
1603
  if (actualStr !== expectedTrimmed) {
1462
- throw new HttpApiError(
1463
- HttpStatus.BAD_REQUEST,
1464
- `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u044F \xAB${field}\xBB \u0432 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0435 (${actualStr}) \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0435\u0442 \u0441 \u043E\u0436\u0438\u0434\u0430\u0435\u043C\u044B\u043C (${expectedTrimmed}). \u0412\u043E\u0437\u043C\u043E\u0436\u043D\u0430 \u043F\u043E\u0434\u043C\u0435\u043D\u0430 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430.`
1465
- );
1604
+ throw DomainError.badRequest("KIT_DOCUMENT_FIELD_MISMATCH", { field, actual: actualStr, expected: expectedTrimmed });
1466
1605
  }
1467
1606
  break;
1468
1607
  }
1469
1608
  case "hex_case_insensitive": {
1470
1609
  if (actualStr.toLowerCase() !== expectedTrimmed.toLowerCase()) {
1471
- throw new HttpApiError(
1472
- HttpStatus.BAD_REQUEST,
1473
- `\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043F\u043E\u043B\u044F \xAB${field}\xBB \u0432 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0435 (${actualStr}) \u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0435\u0442 \u0441 \u043E\u0436\u0438\u0434\u0430\u0435\u043C\u044B\u043C (${expectedTrimmed}). \u0412\u043E\u0437\u043C\u043E\u0436\u043D\u0430 \u043F\u043E\u0434\u043C\u0435\u043D\u0430 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430.`
1474
- );
1610
+ throw DomainError.badRequest("KIT_DOCUMENT_FIELD_MISMATCH", { field, actual: actualStr, expected: expectedTrimmed });
1475
1611
  }
1476
1612
  break;
1477
1613
  }
1478
1614
  default:
1479
- throw new HttpApiError(HttpStatus.INTERNAL_SERVER_ERROR, `\u041D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u044B\u0439 \u0440\u0435\u0436\u0438\u043C \u0441\u0440\u0430\u0432\u043D\u0435\u043D\u0438\u044F meta: ${String(mode)}`);
1615
+ throw DomainError.internal("KIT_DOCUMENT_COMPARE_MODE_UNKNOWN", { mode: String(mode) });
1480
1616
  }
1481
1617
  }
1482
1618
  }
@@ -1533,12 +1669,12 @@ let DomainToBlockchainUtils = class {
1533
1669
  formatQuantityWithPrecision(quantity) {
1534
1670
  const parts = quantity.split(" ");
1535
1671
  if (parts.length !== 2) {
1536
- throw new Error(`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 quantity: ${quantity}. \u041E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F "\u0447\u0438\u0441\u043B\u043E \u0441\u0438\u043C\u0432\u043E\u043B"`);
1672
+ throw DomainError.internal("KIT_QUANTITY_FORMAT_INVALID", { quantity });
1537
1673
  }
1538
1674
  const [amount, symbol] = parts;
1539
1675
  const numericAmount = parseFloat(amount);
1540
1676
  if (isNaN(numericAmount)) {
1541
- throw new Error(`\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E\u0435 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0432 quantity: ${amount}`);
1677
+ throw DomainError.internal("KIT_QUANTITY_AMOUNT_INVALID", { amount });
1542
1678
  }
1543
1679
  const { rootSymbol, rootPrecision, rootGovernSymbol, rootGovernPrecision } = platformSettings().blockchain;
1544
1680
  let precision;
@@ -1547,7 +1683,7 @@ let DomainToBlockchainUtils = class {
1547
1683
  } else if (symbol === rootGovernSymbol) {
1548
1684
  precision = rootGovernPrecision;
1549
1685
  } else {
1550
- throw new Error(`\u041D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0439 \u0441\u0438\u043C\u0432\u043E\u043B: ${symbol}. \u041F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044E\u0442\u0441\u044F \u0442\u043E\u043B\u044C\u043A\u043E: ${rootSymbol}, ${rootGovernSymbol}`);
1686
+ throw DomainError.internal("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
1551
1687
  }
1552
1688
  return `${numericAmount.toFixed(precision)} ${symbol}`;
1553
1689
  }
@@ -1555,10 +1691,10 @@ let DomainToBlockchainUtils = class {
1555
1691
  formatNumericStringToAssetString(numericString, precision, symbol) {
1556
1692
  const numericValue = parseFloat(numericString);
1557
1693
  if (isNaN(numericValue)) {
1558
- throw new Error(`\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E\u0435 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: ${numericString}`);
1694
+ throw DomainError.internal("KIT_NUMBER_INVALID", { value: numericString });
1559
1695
  }
1560
1696
  if (numericValue < 0) {
1561
- throw new Error(`\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u043D\u0435 \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043E\u0442\u0440\u0438\u0446\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u043C: ${numericString}`);
1697
+ throw DomainError.internal("KIT_NUMBER_NEGATIVE", { value: numericString });
1562
1698
  }
1563
1699
  return `${numericValue.toFixed(precision)} ${symbol}`;
1564
1700
  }
@@ -1589,6 +1725,19 @@ DomainToBlockchainUtils = __decorateClass([
1589
1725
  Injectable()
1590
1726
  ], DomainToBlockchainUtils);
1591
1727
 
1728
+ const DIGEST_RE = /^[0-9a-f]{64}$/;
1729
+ function chainTextDigest(text) {
1730
+ if (!text)
1731
+ return "";
1732
+ return createHash("sha256").update(text, "utf8").digest("hex");
1733
+ }
1734
+ function isChainTextDigest(value) {
1735
+ return typeof value === "string" && DIGEST_RE.test(value);
1736
+ }
1737
+ function resolveChainText(chainValue, current) {
1738
+ return isChainTextDigest(chainValue) ? current ?? "" : chainValue;
1739
+ }
1740
+
1592
1741
  class AssetUtils {
1593
1742
  /**
1594
1743
  * Парсит строку ассета и возвращает числовое значение и символ
@@ -1675,17 +1824,17 @@ class AmountFormatterUtils {
1675
1824
  */
1676
1825
  static formatAmount(amountStr) {
1677
1826
  if (!amountStr || typeof amountStr !== "string") {
1678
- throw new Error(`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u0441\u0443\u043C\u043C\u044B: ${amountStr}. \u041E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F "\u0447\u0438\u0441\u043B\u043E" \u0438\u043B\u0438 "\u0447\u0438\u0441\u043B\u043E \u0432\u0430\u043B\u044E\u0442\u0430"`);
1827
+ throw DomainError.internal("KIT_AMOUNT_FORMAT_INVALID", { amount: amountStr });
1679
1828
  }
1680
1829
  const parts = amountStr.trim().split(/\s+/);
1681
1830
  if (parts.length < 1 || !parts[0]) {
1682
- throw new Error(`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 \u0441\u0443\u043C\u043C\u044B: ${amountStr}. \u041E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F "\u0447\u0438\u0441\u043B\u043E" \u0438\u043B\u0438 "\u0447\u0438\u0441\u043B\u043E \u0432\u0430\u043B\u044E\u0442\u0430"`);
1831
+ throw DomainError.internal("KIT_AMOUNT_FORMAT_INVALID", { amount: amountStr });
1683
1832
  }
1684
1833
  const amountPart = parts[0].replace(",", ".");
1685
1834
  const currency = parts.length >= 2 ? parts.slice(1).join(" ") : "";
1686
1835
  const amount = parseFloat(amountPart);
1687
1836
  if (isNaN(amount)) {
1688
- throw new Error(`\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E\u0435 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0432 \u0441\u0443\u043C\u043C\u0435: ${parts[0]}`);
1837
+ throw DomainError.internal("KIT_AMOUNT_NUMBER_INVALID", { value: parts[0] });
1689
1838
  }
1690
1839
  const formattedAmount = amount.toLocaleString("ru-RU", {
1691
1840
  minimumFractionDigits: 2,
@@ -1730,7 +1879,7 @@ function checkPaymentAmount(incomeAmount, expectedAmount, tolerancePercentage) {
1730
1879
  if (incomeAmount < expectedAmount - tolerance) {
1731
1880
  return {
1732
1881
  status: "error",
1733
- message: `\u041D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E \u0441\u0440\u0435\u0434\u0441\u0442\u0432, \u043F\u043E\u0441\u0442\u0443\u043F\u0438\u043B\u043E: ${incomeAmount}, \u043E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F: ${expectedAmount}`
1882
+ message: t("kit.payments.insufficientFunds", { income: incomeAmount, expected: expectedAmount })
1734
1883
  };
1735
1884
  }
1736
1885
  return {
@@ -1761,7 +1910,7 @@ class QuantityUtils {
1761
1910
  } else if (symbol === rootGovernSymbol) {
1762
1911
  return rootGovernPrecision;
1763
1912
  } else {
1764
- throw new Error(`\u041D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0439 \u0441\u0438\u043C\u0432\u043E\u043B: ${symbol}. \u041F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044E\u0442\u0441\u044F \u0442\u043E\u043B\u044C\u043A\u043E: ${rootSymbol}, ${rootGovernSymbol}`);
1913
+ throw DomainError.internal("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
1765
1914
  }
1766
1915
  }
1767
1916
  /**
@@ -1771,7 +1920,7 @@ class QuantityUtils {
1771
1920
  static validateSymbol(symbol) {
1772
1921
  if (!this.isSupportedSymbol(symbol)) {
1773
1922
  const { rootSymbol, rootGovernSymbol } = platformSettings().blockchain;
1774
- throw new Error(`\u041D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0439 \u0441\u0438\u043C\u0432\u043E\u043B: ${symbol}. \u041F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044E\u0442\u0441\u044F \u0442\u043E\u043B\u044C\u043A\u043E: ${rootSymbol}, ${rootGovernSymbol}`);
1923
+ throw DomainError.internal("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
1775
1924
  }
1776
1925
  }
1777
1926
  /**
@@ -1783,7 +1932,7 @@ class QuantityUtils {
1783
1932
  static formatQuantityForBlockchain(amount, symbol) {
1784
1933
  this.validateSymbol(symbol);
1785
1934
  if (isNaN(amount) || amount < 0) {
1786
- throw new Error(`\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E\u0435 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: ${amount}`);
1935
+ throw DomainError.internal("KIT_NUMBER_INVALID", { value: amount });
1787
1936
  }
1788
1937
  const precision = this.getPrecisionForSymbol(symbol);
1789
1938
  const formattedAmount = amount.toFixed(precision);
@@ -1798,7 +1947,7 @@ class QuantityUtils {
1798
1947
  static combineQuantityAndSymbol(amount, symbol) {
1799
1948
  this.validateSymbol(symbol);
1800
1949
  if (isNaN(amount) || amount < 0) {
1801
- throw new Error(`\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E\u0435 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: ${amount}`);
1950
+ throw DomainError.internal("KIT_NUMBER_INVALID", { value: amount });
1802
1951
  }
1803
1952
  return `${amount} ${symbol}`;
1804
1953
  }
@@ -1810,12 +1959,12 @@ class QuantityUtils {
1810
1959
  static parseQuantityString(quantity) {
1811
1960
  const parts = quantity.split(" ");
1812
1961
  if (parts.length !== 2) {
1813
- throw new Error(`\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442 quantity: ${quantity}. \u041E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F "\u0447\u0438\u0441\u043B\u043E \u0441\u0438\u043C\u0432\u043E\u043B"`);
1962
+ throw DomainError.internal("KIT_QUANTITY_FORMAT_INVALID", { quantity });
1814
1963
  }
1815
1964
  const [amountStr, symbol] = parts;
1816
1965
  const amount = parseFloat(amountStr);
1817
1966
  if (isNaN(amount)) {
1818
- throw new Error(`\u041D\u0435\u043A\u043E\u0440\u0440\u0435\u043A\u0442\u043D\u043E\u0435 \u0447\u0438\u0441\u043B\u043E\u0432\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0432 quantity: ${amountStr}`);
1967
+ throw DomainError.internal("KIT_QUANTITY_AMOUNT_INVALID", { amount: amountStr });
1819
1968
  }
1820
1969
  this.validateSymbol(symbol);
1821
1970
  return { amount, symbol };
@@ -1878,14 +2027,29 @@ class DateUtils {
1878
2027
  }
1879
2028
  }
1880
2029
 
1881
- async function waitAfterTransactBeforeChainTableRead() {
1882
- const ms = platformSettings().blockchain.postTransactChainReadDelayMs;
1883
- if (typeof ms !== "number" || !Number.isFinite(ms) || ms <= 0) {
1884
- return;
1885
- }
1886
- await new Promise((resolve) => {
1887
- setTimeout(resolve, ms);
2030
+ function waitForEvent(source, event, match, timeoutMs) {
2031
+ let settle;
2032
+ const promise = new Promise((resolve) => {
2033
+ settle = resolve;
1888
2034
  });
2035
+ let done = false;
2036
+ let timer;
2037
+ const listener = (payload) => {
2038
+ if (match(payload))
2039
+ finish(payload);
2040
+ };
2041
+ function finish(value) {
2042
+ if (done)
2043
+ return;
2044
+ done = true;
2045
+ if (timer)
2046
+ clearTimeout(timer);
2047
+ source.off(event, listener);
2048
+ settle(value);
2049
+ }
2050
+ source.on(event, listener);
2051
+ timer = setTimeout(() => finish(null), timeoutMs);
2052
+ return { promise, cancel: () => finish(null) };
1889
2053
  }
1890
2054
 
1891
2055
  function getAppliedBlockNum(transactResult) {
@@ -1912,11 +2076,52 @@ function sanitizeUserText(value) {
1912
2076
  return _Common.Text.sanitizeEditorMarkdown(value);
1913
2077
  }
1914
2078
 
2079
+ function resolveSortColumn(repository, sortBy, fallback) {
2080
+ if (!sortBy)
2081
+ return fallback;
2082
+ const isKnownColumn = repository.metadata.columns.some(
2083
+ (column) => column.propertyName === sortBy || column.databaseName === sortBy
2084
+ );
2085
+ return isKnownColumn ? sortBy : fallback;
2086
+ }
2087
+
2088
+ class HttpApiError extends HttpException {
2089
+ constructor(statusCode, message, isOperational = true, stack, subcode) {
2090
+ super(message, statusCode);
2091
+ this.isOperational = isOperational;
2092
+ this.subcode = subcode;
2093
+ if (stack) {
2094
+ this.stack = stack;
2095
+ } else {
2096
+ Error.captureStackTrace(this, this.constructor);
2097
+ }
2098
+ }
2099
+ }
2100
+
2101
+ const ASSERT_PREFIX = /assertion failure with message: (.+?)(?:\n|$)/;
2102
+ const CODED = /^([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+):\s*(.*)$/s;
2103
+ function parseChainAssert(message) {
2104
+ const assert = message.match(ASSERT_PREFIX);
2105
+ const body = (assert ? assert[1] : message).trim();
2106
+ const coded = body.match(CODED);
2107
+ return coded ? { code: coded[1], text: coded[2].trim() } : { text: body };
2108
+ }
2109
+ function chainErrorCode(error) {
2110
+ if (error instanceof DomainError) {
2111
+ if (error.code !== "CHAIN_ASSERT" && error.code !== "CHAIN_ERROR")
2112
+ return error.code;
2113
+ return typeof error.params.message === "string" ? parseChainAssert(error.params.message).code : void 0;
2114
+ }
2115
+ const raw = error?.message ?? String(error);
2116
+ return parseChainAssert(raw).code;
2117
+ }
1915
2118
  function rethrowChainError(error) {
1916
2119
  const raw = error?.message ?? String(error);
1917
- const match = raw.match(/assertion failure with message: (.+?)(?:\n|$)/);
1918
- const clean = match ? match[1].trim() : raw;
1919
- throw new BadRequestException(clean);
2120
+ const match = raw.match(ASSERT_PREFIX);
2121
+ if (!match)
2122
+ throw DomainError.badRequest("CHAIN_ERROR", { message: raw });
2123
+ const { code, text } = parseChainAssert(raw);
2124
+ throw DomainError.badRequest(code ?? "CHAIN_ASSERT", { message: text });
1920
2125
  }
1921
2126
 
1922
- export { ActiveUserStatusGuard, AmountFormatterUtils, AssetUtils, AuthRoles, BaseExtensionModule, BucketRegistry, CandidateFilterInputDTO, CandidateOutputDTO, CandidateStatus, CurrencyValidationUtil, CurrentUser, DEFAULT_DOCUMENT_VERSION, DateUtils, DocumentAggregateDTO, DomainToBlockchainUtils, EMPTY_HASH, EXTENSION_APP_TERMINATE_EVENT, EXTENSION_REPOSITORY, EXTENSION_SECRET_SET, EXTENSION_SECRET_UNSET, ExpenseProposalStatementGenerateDocumentInputDTO, ExpenseProposalStatementSignedDocumentInputDTO, ExpenseProposalStatementSignedMetaDocumentInputDTO, ExtensionAvailability, ExtensionConfigSuppliedBy, ExtensionDomainEntity, GenerateDocumentInputDTO, GenerateDocumentOptionsInputDTO, GenerateMetaDocumentInputDTO, GeneratedDocumentDTO, GqlJwtAuthGuard, HttpApiError, HttpJwtAuthGuard, IPNProvider, InjectBucket, LOG_EXTENSION_REPOSITORY, LangType, LogExtensionDomainEntity, MetaDocumentInputDTO, ONBOARDING_EXPIRY_DAYS, ONBOARDING_EXPIRY_MS, OptionalCurrentUser, OptionalGqlJwtAuthGuard, PaginationInputDTO, PaginationResult, PaginationUtils, PaymentProvider, PollingProvider, QuantityUtils, ROLES_ANY_STATUS_KEY, ROLES_DENY_SELF_KEY, RolesGuard, SignatureInfoDTO, SignatureInfoInputDTO, SignedDigitalDocumentDTO, SignedDigitalDocumentInputDTO, TransactionDTO, UseBucket, bucketProvidersFor, bucketTokenFor, buildPaginationResult, checkPaymentAmount, checkPaymentSymbol, computeOnboardingExpiresAt, configureExtensionAuth, configurePlatformSettings, createPaginationResult, extensionEntities, generateHashFromString, generateRandomHash, generateUniqueHash, getAmountPlusFee, getAppliedBlockNum, hasServerSecret, isExtensionAvailable, mergeSecretConfig, paginationInputToOffset, platformSettings, providerSuppliedPaths, redactSecretConfig, registerExtensionEntities, resetExtensionEntities, rethrowChainError, sanitizeUserText, verifySignedDocumentAgainstStoredDraft, waitAfterTransactBeforeChainTableRead };
2127
+ export { ActiveUserStatusGuard, AmountFormatterUtils, AssetUtils, AuthRoles, BaseExtensionModule, BucketRegistry, CandidateFilterInputDTO, CandidateOutputDTO, CandidateStatus, CurrencyValidationUtil, CurrentUser, DEFAULT_DOCUMENT_VERSION, DateUtils, DocumentAggregateDTO, DomainError, DomainToBlockchainUtils, EMPTY_HASH, EXTENSION_APP_TERMINATE_EVENT, EXTENSION_REPOSITORY, EXTENSION_SECRET_SET, EXTENSION_SECRET_UNSET, ExpenseProposalStatementGenerateDocumentInputDTO, ExpenseProposalStatementSignedDocumentInputDTO, ExpenseProposalStatementSignedMetaDocumentInputDTO, ExtensionAvailability, ExtensionConfigSuppliedBy, ExtensionDomainEntity, GenerateDocumentInputDTO, GenerateDocumentOptionsInputDTO, GenerateMetaDocumentInputDTO, GeneratedDocumentDTO, GqlJwtAuthGuard, HttpApiError, HttpJwtAuthGuard, IPNProvider, InjectBucket, LOG_EXTENSION_REPOSITORY, LangType, LogExtensionDomainEntity, MetaDocumentInputDTO, ONBOARDING_EXPIRY_DAYS, ONBOARDING_EXPIRY_MS, OptionalCurrentUser, OptionalGqlJwtAuthGuard, PaginationInputDTO, PaginationResult, PaginationUtils, PaymentProvider, PollingProvider, QuantityUtils, ROLES_ANY_STATUS_KEY, ROLES_DENY_SELF_KEY, RolesGuard, SORT_FIELD_PATTERN, SignatureInfoDTO, SignatureInfoInputDTO, SignedDigitalDocumentDTO, SignedDigitalDocumentInputDTO, TransactionDTO, UseBucket, bucketProvidersFor, bucketTokenFor, buildPaginationResult, chainErrorCode, chainTextDigest, checkPaymentAmount, checkPaymentSymbol, computeOnboardingExpiresAt, configureExtensionAuth, configurePlatformSettings, createPaginationResult, domainErrorMessage, extensionDatabaseMigrations, extensionEntities, generateHashFromString, generateRandomHash, generateUniqueHash, getAmountPlusFee, getAppliedBlockNum, hasServerSecret, isChainTextDigest, isExtensionAvailable, mergeSecretConfig, paginationInputToOffset, parseChainAssert, platformSettings, providerSuppliedPaths, redactSecretConfig, registerExtensionDatabaseMigrations, registerExtensionEntities, resetExtensionDatabaseMigrations, resetExtensionEntities, resolveChainText, resolveSortColumn, rethrowChainError, sanitizeUserText, validationMessage, verifySignedDocumentAgainstStoredDraft, waitForEvent };