@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.cjs CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const common = require('@nestjs/common');
4
4
  const graphql = require('@nestjs/graphql');
5
+ const server = require('@coopenomics/i18n/server');
5
6
  const core = require('@nestjs/core');
6
7
  const passport = require('@nestjs/passport');
7
8
  const node_crypto = require('node:crypto');
@@ -12,16 +13,160 @@ const graphqlTypeJson = require('graphql-type-json');
12
13
  const sdk = require('@coopenomics/sdk');
13
14
  const antelope = require('@wharfkit/antelope');
14
15
  const moment = require('moment');
16
+ const crypto = require('crypto');
15
17
  const moment$1 = require('moment-timezone');
16
18
  require('moment/locale/ru');
17
- const crypto = require('crypto');
18
19
  const cooptypes = require('cooptypes');
19
20
 
20
21
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
21
22
 
22
23
  const moment__default = /*#__PURE__*/_interopDefaultCompat(moment);
23
- const moment__default$1 = /*#__PURE__*/_interopDefaultCompat(moment$1);
24
24
  const crypto__default = /*#__PURE__*/_interopDefaultCompat(crypto);
25
+ const moment__default$1 = /*#__PURE__*/_interopDefaultCompat(moment$1);
26
+
27
+ const DOMAIN_ERROR = Symbol.for("@coopenomics/extension-kit/DomainError");
28
+ function domainErrorMessage(code, params = {}) {
29
+ const key = `errors.${code}`;
30
+ if (!server.te(key) && typeof params.message === "string")
31
+ return params.message;
32
+ return server.t(key, params);
33
+ }
34
+ function body(code, params, status) {
35
+ return { statusCode: status, code, params, message: domainErrorMessage(code, params) };
36
+ }
37
+ function brand(target, code, params) {
38
+ target.code = code;
39
+ target.params = params;
40
+ target[DOMAIN_ERROR] = true;
41
+ target.name = "DomainError";
42
+ }
43
+ class DomainError extends common.HttpException {
44
+ static [Symbol.hasInstance](value) {
45
+ return !!value && value[DOMAIN_ERROR] === true;
46
+ }
47
+ constructor(code, params = {}, status = common.HttpStatus.BAD_REQUEST) {
48
+ super(body(code, params, status), status);
49
+ brand(this, code, params);
50
+ }
51
+ /** Ключ сообщения в словаре. */
52
+ get messageKey() {
53
+ return `errors.${this.code}`;
54
+ }
55
+ static badRequest(code, params = {}) {
56
+ return new DomainBadRequest(code, params);
57
+ }
58
+ static unauthorized(code, params = {}) {
59
+ return new DomainUnauthorized(code, params);
60
+ }
61
+ static forbidden(code, params = {}) {
62
+ return new DomainForbidden(code, params);
63
+ }
64
+ static notFound(code, params = {}) {
65
+ return new DomainNotFound(code, params);
66
+ }
67
+ static conflict(code, params = {}) {
68
+ return new DomainConflict(code, params);
69
+ }
70
+ static unprocessable(code, params = {}) {
71
+ return new DomainUnprocessable(code, params);
72
+ }
73
+ static tooManyRequests(code, params = {}) {
74
+ return new DomainError(code, params, common.HttpStatus.TOO_MANY_REQUESTS);
75
+ }
76
+ static internal(code, params = {}) {
77
+ return new DomainInternal(code, params);
78
+ }
79
+ static badGateway(code, params = {}) {
80
+ return new DomainBadGateway(code, params);
81
+ }
82
+ static serviceUnavailable(code, params = {}) {
83
+ return new DomainServiceUnavailable(code, params);
84
+ }
85
+ }
86
+ class DomainBadGateway extends common.BadGatewayException {
87
+ constructor(code, params) {
88
+ super(body(code, params, common.HttpStatus.BAD_GATEWAY));
89
+ brand(this, code, params);
90
+ }
91
+ get messageKey() {
92
+ return `errors.${this.code}`;
93
+ }
94
+ }
95
+ class DomainServiceUnavailable extends common.ServiceUnavailableException {
96
+ constructor(code, params) {
97
+ super(body(code, params, common.HttpStatus.SERVICE_UNAVAILABLE));
98
+ brand(this, code, params);
99
+ }
100
+ get messageKey() {
101
+ return `errors.${this.code}`;
102
+ }
103
+ }
104
+ class DomainBadRequest extends common.BadRequestException {
105
+ constructor(code, params) {
106
+ super(body(code, params, common.HttpStatus.BAD_REQUEST));
107
+ brand(this, code, params);
108
+ }
109
+ get messageKey() {
110
+ return `errors.${this.code}`;
111
+ }
112
+ }
113
+ class DomainUnauthorized extends common.UnauthorizedException {
114
+ constructor(code, params) {
115
+ super(body(code, params, common.HttpStatus.UNAUTHORIZED));
116
+ brand(this, code, params);
117
+ }
118
+ get messageKey() {
119
+ return `errors.${this.code}`;
120
+ }
121
+ }
122
+ class DomainForbidden extends common.ForbiddenException {
123
+ constructor(code, params) {
124
+ super(body(code, params, common.HttpStatus.FORBIDDEN));
125
+ brand(this, code, params);
126
+ }
127
+ get messageKey() {
128
+ return `errors.${this.code}`;
129
+ }
130
+ }
131
+ class DomainNotFound extends common.NotFoundException {
132
+ constructor(code, params) {
133
+ super(body(code, params, common.HttpStatus.NOT_FOUND));
134
+ brand(this, code, params);
135
+ }
136
+ get messageKey() {
137
+ return `errors.${this.code}`;
138
+ }
139
+ }
140
+ class DomainConflict extends common.ConflictException {
141
+ constructor(code, params) {
142
+ super(body(code, params, common.HttpStatus.CONFLICT));
143
+ brand(this, code, params);
144
+ }
145
+ get messageKey() {
146
+ return `errors.${this.code}`;
147
+ }
148
+ }
149
+ class DomainUnprocessable extends common.UnprocessableEntityException {
150
+ constructor(code, params) {
151
+ super(body(code, params, common.HttpStatus.UNPROCESSABLE_ENTITY));
152
+ brand(this, code, params);
153
+ }
154
+ get messageKey() {
155
+ return `errors.${this.code}`;
156
+ }
157
+ }
158
+ class DomainInternal extends common.InternalServerErrorException {
159
+ constructor(code, params) {
160
+ super(body(code, params, common.HttpStatus.INTERNAL_SERVER_ERROR));
161
+ brand(this, code, params);
162
+ }
163
+ get messageKey() {
164
+ return `errors.${this.code}`;
165
+ }
166
+ }
167
+ function validationMessage(key, params = {}) {
168
+ return (args) => server.t(key, { property: args.property, value: String(args.value ?? ""), ...params });
169
+ }
25
170
 
26
171
  const ROLES_DENY_SELF_KEY = "roles_deny_self";
27
172
  const ROLES_ANY_STATUS_KEY = "roles_any_status";
@@ -39,7 +184,7 @@ const CurrentUser = common.createParamDecorator((data, context) => {
39
184
  const ctx = graphql.GqlExecutionContext.create(context);
40
185
  const request = ctx.getContext().req;
41
186
  if (!request?.user) {
42
- throw new common.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");
187
+ throw DomainError.unauthorized("KIT_USER_NOT_AUTHORIZED");
43
188
  }
44
189
  return request?.user;
45
190
  });
@@ -122,7 +267,7 @@ exports.OptionalGqlJwtAuthGuard = class OptionalGqlJwtAuthGuard extends passport
122
267
  if (err instanceof Error) {
123
268
  throw err;
124
269
  }
125
- throw new common.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");
270
+ throw DomainError.unauthorized("KIT_SESSION_ENDED");
126
271
  }
127
272
  };
128
273
  exports.OptionalGqlJwtAuthGuard = __decorateClass$g([
@@ -172,11 +317,11 @@ exports.RolesGuard = class RolesGuard {
172
317
  if (allowedRoles.includes(user.role)) {
173
318
  const anyStatus = this.reflector.get(ROLES_ANY_STATUS_KEY, context.getHandler()) === true;
174
319
  if (user.role === PARTICIPANT_ROLE && !anyStatus && user.status !== ACTIVE_USER_STATUS) {
175
- throw new common.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");
320
+ throw DomainError.forbidden("KIT_MEMBERS_ONLY");
176
321
  }
177
322
  return true;
178
323
  }
179
- throw new common.UnauthorizedException(`\u041D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043E\u0447\u043D\u043E \u043F\u0440\u0430\u0432 \u0434\u043E\u0441\u0442\u0443\u043F\u0430`);
324
+ throw DomainError.unauthorized("KIT_INSUFFICIENT_RIGHTS");
180
325
  }
181
326
  };
182
327
  exports.RolesGuard = __decorateClass$g([
@@ -192,12 +337,10 @@ exports.ActiveUserStatusGuard = class ActiveUserStatusGuard {
192
337
  }
193
338
  const user = request.user;
194
339
  if (!user?.status) {
195
- throw new common.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");
340
+ throw DomainError.forbidden("KIT_AUTHORIZED_USER_REQUIRED");
196
341
  }
197
342
  if (user.status !== ACTIVE_USER_STATUS) {
198
- throw new common.ForbiddenException(
199
- "\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"
200
- );
343
+ throw DomainError.forbidden("KIT_ACTIVE_MEMBERS_ONLY");
201
344
  }
202
345
  return true;
203
346
  }
@@ -262,6 +405,7 @@ const BucketRegistry = {
262
405
  const existing = REGISTERED.get(cls);
263
406
  if (existing && existing.name !== spec.name) {
264
407
  throw new Error(
408
+ // i18n-ignore: ошибка регистрации бакета при старте, до пайщика не доходит
265
409
  `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`
266
410
  );
267
411
  }
@@ -298,6 +442,7 @@ function bucketProvidersFor(fileStoragePortToken, consumers) {
298
442
  const spec = BucketRegistry.get(cls);
299
443
  if (!spec) {
300
444
  throw new Error(
445
+ // i18n-ignore: ошибка объявления хранилища при старте, до пайщика не доходит
301
446
  `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`
302
447
  );
303
448
  }
@@ -655,7 +800,7 @@ __decorateClass$a([
655
800
  graphql.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" }),
656
801
  classValidator.IsString(),
657
802
  classValidator.Matches(/^\d{2}\.\d{2}\.\d{4}$/, {
658
- 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"
803
+ message: validationMessage("kit.expenseStatement.dueDateFormat")
659
804
  })
660
805
  ], ExpenseProposalHeaderInputDTO.prototype, "deadline", 2);
661
806
  __decorateClass$a([
@@ -882,6 +1027,7 @@ let entities;
882
1027
  function registerExtensionEntities(list) {
883
1028
  if (entities) {
884
1029
  throw new Error(
1030
+ // i18n-ignore: ошибка сборки приложения при старте, до пайщика не доходит
885
1031
  "\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"
886
1032
  );
887
1033
  }
@@ -894,6 +1040,23 @@ function resetExtensionEntities() {
894
1040
  entities = void 0;
895
1041
  }
896
1042
 
1043
+ let migrations;
1044
+ function registerExtensionDatabaseMigrations(list) {
1045
+ if (migrations) {
1046
+ throw new Error(
1047
+ // i18n-ignore: ошибка сборки composition root для разработчика, до пайщика не доходит
1048
+ "\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"
1049
+ );
1050
+ }
1051
+ migrations = [...list];
1052
+ }
1053
+ function extensionDatabaseMigrations() {
1054
+ return migrations ?? [];
1055
+ }
1056
+ function resetExtensionDatabaseMigrations() {
1057
+ migrations = void 0;
1058
+ }
1059
+
897
1060
  var __defProp$7 = Object.defineProperty;
898
1061
  var __getOwnPropDesc$7 = Object.getOwnPropertyDescriptor;
899
1062
  var __decorateClass$7 = (decorators, target, key, kind) => {
@@ -905,6 +1068,7 @@ var __decorateClass$7 = (decorators, target, key, kind) => {
905
1068
  __defProp$7(target, key, result);
906
1069
  return result;
907
1070
  };
1071
+ const SORT_FIELD_PATTERN = /^([A-Za-z_][A-Za-z0-9_]*(:(asc|desc))?)?$/i;
908
1072
  exports.PaginationInputDTO = class PaginationInputDTO {
909
1073
  };
910
1074
  __decorateClass$7([
@@ -914,7 +1078,9 @@ __decorateClass$7([
914
1078
  graphql.Field(() => graphql.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 })
915
1079
  ], exports.PaginationInputDTO.prototype, "limit", 2);
916
1080
  __decorateClass$7([
917
- graphql.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")' })
1081
+ graphql.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")' }),
1082
+ classValidator.IsOptional(),
1083
+ classValidator.Matches(SORT_FIELD_PATTERN, { message: validationMessage("kit.pagination.invalidSortField") })
918
1084
  ], exports.PaginationInputDTO.prototype, "sortBy", 2);
919
1085
  __decorateClass$7([
920
1086
  graphql.Field(() => String, {
@@ -988,13 +1154,16 @@ class PaginationUtils {
988
1154
  static validatePaginationOptions(options) {
989
1155
  const { page = 1, limit = 10, sortBy, sortOrder = "ASC" } = options;
990
1156
  if (page < 1) {
991
- 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");
1157
+ throw DomainError.internal("KIT_PAGE_NUMBER_INVALID");
992
1158
  }
993
1159
  if (limit < 1 || limit > 1e3) {
994
- 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");
1160
+ throw DomainError.internal("KIT_PAGE_LIMIT_INVALID");
995
1161
  }
996
1162
  if (sortOrder !== "ASC" && sortOrder !== "DESC") {
997
- 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");
1163
+ throw DomainError.internal("KIT_SORT_ORDER_INVALID");
1164
+ }
1165
+ if (sortBy !== void 0 && sortBy !== null && !SORT_FIELD_PATTERN.test(sortBy)) {
1166
+ throw DomainError.internal("KIT_SORT_FIELD_INVALID");
998
1167
  }
999
1168
  return {
1000
1169
  page,
@@ -1124,6 +1293,7 @@ function configurePlatformSettings(value) {
1124
1293
  function platformSettings() {
1125
1294
  if (!settings) {
1126
1295
  throw new Error(
1296
+ // i18n-ignore: ошибка сборки приложения при старте, до пайщика не доходит
1127
1297
  "\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"
1128
1298
  );
1129
1299
  }
@@ -1341,19 +1511,6 @@ exports.DocumentAggregateDTO = __decorateClass$1([
1341
1511
  graphql.ObjectType("DocumentAggregate")
1342
1512
  ], exports.DocumentAggregateDTO);
1343
1513
 
1344
- class HttpApiError extends common.HttpException {
1345
- constructor(statusCode, message, isOperational = true, stack, subcode) {
1346
- super(message, statusCode);
1347
- this.isOperational = isOperational;
1348
- this.subcode = subcode;
1349
- if (stack) {
1350
- this.stack = stack;
1351
- } else {
1352
- Error.captureStackTrace(this, this.constructor);
1353
- }
1354
- }
1355
- }
1356
-
1357
1514
  class CurrencyValidationUtil {
1358
1515
  /**
1359
1516
  * Проверяет, что сумма содержит правильный символ валюты
@@ -1373,10 +1530,10 @@ class CurrencyValidationUtil {
1373
1530
  * @param fieldName Название поля для сообщения об ошибке (по умолчанию "сумма")
1374
1531
  * @throws Error если символ валюты неправильный
1375
1532
  */
1376
- static validateCurrencySymbol(amount, fieldName = "\u0441\u0443\u043C\u043C\u0430") {
1533
+ static validateCurrencySymbol(amount, fieldName = server.t("kit.currency.defaultFieldName")) {
1377
1534
  if (!this.hasValidCurrencySymbol(amount)) {
1378
1535
  const expectedSymbol = platformSettings().blockchain.rootGovernSymbol;
1379
- 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}`);
1536
+ throw DomainError.internal("KIT_CURRENCY_SYMBOL_INVALID", { fieldName, expectedSymbol });
1380
1537
  }
1381
1538
  }
1382
1539
  /**
@@ -1420,18 +1577,12 @@ class CurrencyValidationUtil {
1420
1577
  async function verifySignedDocumentAgainstStoredDraft(loadGeneratedByDocHash, signed, metaVerifications) {
1421
1578
  const generated = await loadGeneratedByDocHash(signed.doc_hash);
1422
1579
  if (!generated) {
1423
- throw new HttpApiError(
1424
- common.HttpStatus.BAD_REQUEST,
1425
- `\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.`
1426
- );
1580
+ throw DomainError.badRequest("KIT_GENERATED_DOCUMENT_NOT_FOUND", { docHash: signed.doc_hash });
1427
1581
  }
1428
1582
  const comparison = await sdk.Classes.Document.compareDocuments(signed, generated);
1429
1583
  if (!comparison.isValid) {
1430
- 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("; ");
1431
- throw new HttpApiError(
1432
- common.HttpStatus.BAD_REQUEST,
1433
- `\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.`
1434
- );
1584
+ const differences = Object.entries(comparison.differences).map(([field, values]) => server.t("kit.documentVerification.fieldDifference", { field, expected: values.expected, actual: values.actual })).join("; ");
1585
+ throw DomainError.badRequest("KIT_DOCUMENT_DRAFT_MISMATCH", { differences });
1435
1586
  }
1436
1587
  if (!metaVerifications || metaVerifications.length === 0) {
1437
1588
  return;
@@ -1441,10 +1592,7 @@ async function verifySignedDocumentAgainstStoredDraft(loadGeneratedByDocHash, si
1441
1592
  const raw = meta[field];
1442
1593
  const actualStr = typeof raw === "string" ? raw.trim() : raw !== void 0 && raw !== null ? String(raw).trim() : "";
1443
1594
  if (!actualStr) {
1444
- throw new HttpApiError(
1445
- common.HttpStatus.BAD_REQUEST,
1446
- `\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.`
1447
- );
1595
+ throw DomainError.badRequest("KIT_DOCUMENT_META_FIELD_MISSING", { field });
1448
1596
  }
1449
1597
  const expectedTrimmed = expected.trim();
1450
1598
  switch (mode) {
@@ -1452,39 +1600,27 @@ async function verifySignedDocumentAgainstStoredDraft(loadGeneratedByDocHash, si
1452
1600
  const parsedExpected = CurrencyValidationUtil.extractAmountValue(expectedTrimmed);
1453
1601
  const parsedActual = CurrencyValidationUtil.extractAmountValue(actualStr);
1454
1602
  if (Number.isNaN(parsedExpected) || Number.isNaN(parsedActual)) {
1455
- throw new HttpApiError(
1456
- common.HttpStatus.BAD_REQUEST,
1457
- `\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.`
1458
- );
1603
+ throw DomainError.badRequest("KIT_DOCUMENT_AMOUNT_FORMAT_INVALID", { field });
1459
1604
  }
1460
1605
  if (CurrencyValidationUtil.formatAmount(parsedExpected) !== CurrencyValidationUtil.formatAmount(parsedActual)) {
1461
- throw new HttpApiError(
1462
- common.HttpStatus.BAD_REQUEST,
1463
- `\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.`
1464
- );
1606
+ throw DomainError.badRequest("KIT_DOCUMENT_FIELD_MISMATCH", { field, actual: actualStr, expected: expectedTrimmed });
1465
1607
  }
1466
1608
  break;
1467
1609
  }
1468
1610
  case "string_trim": {
1469
1611
  if (actualStr !== expectedTrimmed) {
1470
- throw new HttpApiError(
1471
- common.HttpStatus.BAD_REQUEST,
1472
- `\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.`
1473
- );
1612
+ throw DomainError.badRequest("KIT_DOCUMENT_FIELD_MISMATCH", { field, actual: actualStr, expected: expectedTrimmed });
1474
1613
  }
1475
1614
  break;
1476
1615
  }
1477
1616
  case "hex_case_insensitive": {
1478
1617
  if (actualStr.toLowerCase() !== expectedTrimmed.toLowerCase()) {
1479
- throw new HttpApiError(
1480
- common.HttpStatus.BAD_REQUEST,
1481
- `\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.`
1482
- );
1618
+ throw DomainError.badRequest("KIT_DOCUMENT_FIELD_MISMATCH", { field, actual: actualStr, expected: expectedTrimmed });
1483
1619
  }
1484
1620
  break;
1485
1621
  }
1486
1622
  default:
1487
- throw new HttpApiError(common.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)}`);
1623
+ throw DomainError.internal("KIT_DOCUMENT_COMPARE_MODE_UNKNOWN", { mode: String(mode) });
1488
1624
  }
1489
1625
  }
1490
1626
  }
@@ -1541,12 +1677,12 @@ exports.DomainToBlockchainUtils = class DomainToBlockchainUtils {
1541
1677
  formatQuantityWithPrecision(quantity) {
1542
1678
  const parts = quantity.split(" ");
1543
1679
  if (parts.length !== 2) {
1544
- 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"`);
1680
+ throw DomainError.internal("KIT_QUANTITY_FORMAT_INVALID", { quantity });
1545
1681
  }
1546
1682
  const [amount, symbol] = parts;
1547
1683
  const numericAmount = parseFloat(amount);
1548
1684
  if (isNaN(numericAmount)) {
1549
- 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}`);
1685
+ throw DomainError.internal("KIT_QUANTITY_AMOUNT_INVALID", { amount });
1550
1686
  }
1551
1687
  const { rootSymbol, rootPrecision, rootGovernSymbol, rootGovernPrecision } = platformSettings().blockchain;
1552
1688
  let precision;
@@ -1555,7 +1691,7 @@ exports.DomainToBlockchainUtils = class DomainToBlockchainUtils {
1555
1691
  } else if (symbol === rootGovernSymbol) {
1556
1692
  precision = rootGovernPrecision;
1557
1693
  } else {
1558
- 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}`);
1694
+ throw DomainError.internal("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
1559
1695
  }
1560
1696
  return `${numericAmount.toFixed(precision)} ${symbol}`;
1561
1697
  }
@@ -1563,10 +1699,10 @@ exports.DomainToBlockchainUtils = class DomainToBlockchainUtils {
1563
1699
  formatNumericStringToAssetString(numericString, precision, symbol) {
1564
1700
  const numericValue = parseFloat(numericString);
1565
1701
  if (isNaN(numericValue)) {
1566
- 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}`);
1702
+ throw DomainError.internal("KIT_NUMBER_INVALID", { value: numericString });
1567
1703
  }
1568
1704
  if (numericValue < 0) {
1569
- 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}`);
1705
+ throw DomainError.internal("KIT_NUMBER_NEGATIVE", { value: numericString });
1570
1706
  }
1571
1707
  return `${numericValue.toFixed(precision)} ${symbol}`;
1572
1708
  }
@@ -1597,6 +1733,19 @@ exports.DomainToBlockchainUtils = __decorateClass([
1597
1733
  common.Injectable()
1598
1734
  ], exports.DomainToBlockchainUtils);
1599
1735
 
1736
+ const DIGEST_RE = /^[0-9a-f]{64}$/;
1737
+ function chainTextDigest(text) {
1738
+ if (!text)
1739
+ return "";
1740
+ return crypto.createHash("sha256").update(text, "utf8").digest("hex");
1741
+ }
1742
+ function isChainTextDigest(value) {
1743
+ return typeof value === "string" && DIGEST_RE.test(value);
1744
+ }
1745
+ function resolveChainText(chainValue, current) {
1746
+ return isChainTextDigest(chainValue) ? current ?? "" : chainValue;
1747
+ }
1748
+
1600
1749
  class AssetUtils {
1601
1750
  /**
1602
1751
  * Парсит строку ассета и возвращает числовое значение и символ
@@ -1683,17 +1832,17 @@ class AmountFormatterUtils {
1683
1832
  */
1684
1833
  static formatAmount(amountStr) {
1685
1834
  if (!amountStr || typeof amountStr !== "string") {
1686
- 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"`);
1835
+ throw DomainError.internal("KIT_AMOUNT_FORMAT_INVALID", { amount: amountStr });
1687
1836
  }
1688
1837
  const parts = amountStr.trim().split(/\s+/);
1689
1838
  if (parts.length < 1 || !parts[0]) {
1690
- 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"`);
1839
+ throw DomainError.internal("KIT_AMOUNT_FORMAT_INVALID", { amount: amountStr });
1691
1840
  }
1692
1841
  const amountPart = parts[0].replace(",", ".");
1693
1842
  const currency = parts.length >= 2 ? parts.slice(1).join(" ") : "";
1694
1843
  const amount = parseFloat(amountPart);
1695
1844
  if (isNaN(amount)) {
1696
- 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]}`);
1845
+ throw DomainError.internal("KIT_AMOUNT_NUMBER_INVALID", { value: parts[0] });
1697
1846
  }
1698
1847
  const formattedAmount = amount.toLocaleString("ru-RU", {
1699
1848
  minimumFractionDigits: 2,
@@ -1738,7 +1887,7 @@ function checkPaymentAmount(incomeAmount, expectedAmount, tolerancePercentage) {
1738
1887
  if (incomeAmount < expectedAmount - tolerance) {
1739
1888
  return {
1740
1889
  status: "error",
1741
- 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}`
1890
+ message: server.t("kit.payments.insufficientFunds", { income: incomeAmount, expected: expectedAmount })
1742
1891
  };
1743
1892
  }
1744
1893
  return {
@@ -1769,7 +1918,7 @@ class QuantityUtils {
1769
1918
  } else if (symbol === rootGovernSymbol) {
1770
1919
  return rootGovernPrecision;
1771
1920
  } else {
1772
- 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}`);
1921
+ throw DomainError.internal("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
1773
1922
  }
1774
1923
  }
1775
1924
  /**
@@ -1779,7 +1928,7 @@ class QuantityUtils {
1779
1928
  static validateSymbol(symbol) {
1780
1929
  if (!this.isSupportedSymbol(symbol)) {
1781
1930
  const { rootSymbol, rootGovernSymbol } = platformSettings().blockchain;
1782
- 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}`);
1931
+ throw DomainError.internal("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
1783
1932
  }
1784
1933
  }
1785
1934
  /**
@@ -1791,7 +1940,7 @@ class QuantityUtils {
1791
1940
  static formatQuantityForBlockchain(amount, symbol) {
1792
1941
  this.validateSymbol(symbol);
1793
1942
  if (isNaN(amount) || amount < 0) {
1794
- 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}`);
1943
+ throw DomainError.internal("KIT_NUMBER_INVALID", { value: amount });
1795
1944
  }
1796
1945
  const precision = this.getPrecisionForSymbol(symbol);
1797
1946
  const formattedAmount = amount.toFixed(precision);
@@ -1806,7 +1955,7 @@ class QuantityUtils {
1806
1955
  static combineQuantityAndSymbol(amount, symbol) {
1807
1956
  this.validateSymbol(symbol);
1808
1957
  if (isNaN(amount) || amount < 0) {
1809
- 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}`);
1958
+ throw DomainError.internal("KIT_NUMBER_INVALID", { value: amount });
1810
1959
  }
1811
1960
  return `${amount} ${symbol}`;
1812
1961
  }
@@ -1818,12 +1967,12 @@ class QuantityUtils {
1818
1967
  static parseQuantityString(quantity) {
1819
1968
  const parts = quantity.split(" ");
1820
1969
  if (parts.length !== 2) {
1821
- 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"`);
1970
+ throw DomainError.internal("KIT_QUANTITY_FORMAT_INVALID", { quantity });
1822
1971
  }
1823
1972
  const [amountStr, symbol] = parts;
1824
1973
  const amount = parseFloat(amountStr);
1825
1974
  if (isNaN(amount)) {
1826
- 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}`);
1975
+ throw DomainError.internal("KIT_QUANTITY_AMOUNT_INVALID", { amount: amountStr });
1827
1976
  }
1828
1977
  this.validateSymbol(symbol);
1829
1978
  return { amount, symbol };
@@ -1886,14 +2035,29 @@ class DateUtils {
1886
2035
  }
1887
2036
  }
1888
2037
 
1889
- async function waitAfterTransactBeforeChainTableRead() {
1890
- const ms = platformSettings().blockchain.postTransactChainReadDelayMs;
1891
- if (typeof ms !== "number" || !Number.isFinite(ms) || ms <= 0) {
1892
- return;
1893
- }
1894
- await new Promise((resolve) => {
1895
- setTimeout(resolve, ms);
2038
+ function waitForEvent(source, event, match, timeoutMs) {
2039
+ let settle;
2040
+ const promise = new Promise((resolve) => {
2041
+ settle = resolve;
1896
2042
  });
2043
+ let done = false;
2044
+ let timer;
2045
+ const listener = (payload) => {
2046
+ if (match(payload))
2047
+ finish(payload);
2048
+ };
2049
+ function finish(value) {
2050
+ if (done)
2051
+ return;
2052
+ done = true;
2053
+ if (timer)
2054
+ clearTimeout(timer);
2055
+ source.off(event, listener);
2056
+ settle(value);
2057
+ }
2058
+ source.on(event, listener);
2059
+ timer = setTimeout(() => finish(null), timeoutMs);
2060
+ return { promise, cancel: () => finish(null) };
1897
2061
  }
1898
2062
 
1899
2063
  function getAppliedBlockNum(transactResult) {
@@ -1920,11 +2084,52 @@ function sanitizeUserText(value) {
1920
2084
  return cooptypes._Common.Text.sanitizeEditorMarkdown(value);
1921
2085
  }
1922
2086
 
2087
+ function resolveSortColumn(repository, sortBy, fallback) {
2088
+ if (!sortBy)
2089
+ return fallback;
2090
+ const isKnownColumn = repository.metadata.columns.some(
2091
+ (column) => column.propertyName === sortBy || column.databaseName === sortBy
2092
+ );
2093
+ return isKnownColumn ? sortBy : fallback;
2094
+ }
2095
+
2096
+ class HttpApiError extends common.HttpException {
2097
+ constructor(statusCode, message, isOperational = true, stack, subcode) {
2098
+ super(message, statusCode);
2099
+ this.isOperational = isOperational;
2100
+ this.subcode = subcode;
2101
+ if (stack) {
2102
+ this.stack = stack;
2103
+ } else {
2104
+ Error.captureStackTrace(this, this.constructor);
2105
+ }
2106
+ }
2107
+ }
2108
+
2109
+ const ASSERT_PREFIX = /assertion failure with message: (.+?)(?:\n|$)/;
2110
+ const CODED = /^([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+):\s*(.*)$/s;
2111
+ function parseChainAssert(message) {
2112
+ const assert = message.match(ASSERT_PREFIX);
2113
+ const body = (assert ? assert[1] : message).trim();
2114
+ const coded = body.match(CODED);
2115
+ return coded ? { code: coded[1], text: coded[2].trim() } : { text: body };
2116
+ }
2117
+ function chainErrorCode(error) {
2118
+ if (error instanceof DomainError) {
2119
+ if (error.code !== "CHAIN_ASSERT" && error.code !== "CHAIN_ERROR")
2120
+ return error.code;
2121
+ return typeof error.params.message === "string" ? parseChainAssert(error.params.message).code : void 0;
2122
+ }
2123
+ const raw = error?.message ?? String(error);
2124
+ return parseChainAssert(raw).code;
2125
+ }
1923
2126
  function rethrowChainError(error) {
1924
2127
  const raw = error?.message ?? String(error);
1925
- const match = raw.match(/assertion failure with message: (.+?)(?:\n|$)/);
1926
- const clean = match ? match[1].trim() : raw;
1927
- throw new common.BadRequestException(clean);
2128
+ const match = raw.match(ASSERT_PREFIX);
2129
+ if (!match)
2130
+ throw DomainError.badRequest("CHAIN_ERROR", { message: raw });
2131
+ const { code, text } = parseChainAssert(raw);
2132
+ throw DomainError.badRequest(code ?? "CHAIN_ASSERT", { message: text });
1928
2133
  }
1929
2134
 
1930
2135
  exports.AmountFormatterUtils = AmountFormatterUtils;
@@ -1936,6 +2141,7 @@ exports.CurrencyValidationUtil = CurrencyValidationUtil;
1936
2141
  exports.CurrentUser = CurrentUser;
1937
2142
  exports.DEFAULT_DOCUMENT_VERSION = DEFAULT_DOCUMENT_VERSION;
1938
2143
  exports.DateUtils = DateUtils;
2144
+ exports.DomainError = DomainError;
1939
2145
  exports.EMPTY_HASH = EMPTY_HASH;
1940
2146
  exports.EXTENSION_APP_TERMINATE_EVENT = EXTENSION_APP_TERMINATE_EVENT;
1941
2147
  exports.EXTENSION_REPOSITORY = EXTENSION_REPOSITORY;
@@ -1957,16 +2163,21 @@ exports.PaginationUtils = PaginationUtils;
1957
2163
  exports.QuantityUtils = QuantityUtils;
1958
2164
  exports.ROLES_ANY_STATUS_KEY = ROLES_ANY_STATUS_KEY;
1959
2165
  exports.ROLES_DENY_SELF_KEY = ROLES_DENY_SELF_KEY;
2166
+ exports.SORT_FIELD_PATTERN = SORT_FIELD_PATTERN;
1960
2167
  exports.UseBucket = UseBucket;
1961
2168
  exports.bucketProvidersFor = bucketProvidersFor;
1962
2169
  exports.bucketTokenFor = bucketTokenFor;
1963
2170
  exports.buildPaginationResult = buildPaginationResult;
2171
+ exports.chainErrorCode = chainErrorCode;
2172
+ exports.chainTextDigest = chainTextDigest;
1964
2173
  exports.checkPaymentAmount = checkPaymentAmount;
1965
2174
  exports.checkPaymentSymbol = checkPaymentSymbol;
1966
2175
  exports.computeOnboardingExpiresAt = computeOnboardingExpiresAt;
1967
2176
  exports.configureExtensionAuth = configureExtensionAuth;
1968
2177
  exports.configurePlatformSettings = configurePlatformSettings;
1969
2178
  exports.createPaginationResult = createPaginationResult;
2179
+ exports.domainErrorMessage = domainErrorMessage;
2180
+ exports.extensionDatabaseMigrations = extensionDatabaseMigrations;
1970
2181
  exports.extensionEntities = extensionEntities;
1971
2182
  exports.generateHashFromString = generateHashFromString;
1972
2183
  exports.generateRandomHash = generateRandomHash;
@@ -1974,15 +2185,22 @@ exports.generateUniqueHash = generateUniqueHash;
1974
2185
  exports.getAmountPlusFee = getAmountPlusFee;
1975
2186
  exports.getAppliedBlockNum = getAppliedBlockNum;
1976
2187
  exports.hasServerSecret = hasServerSecret;
2188
+ exports.isChainTextDigest = isChainTextDigest;
1977
2189
  exports.isExtensionAvailable = isExtensionAvailable;
1978
2190
  exports.mergeSecretConfig = mergeSecretConfig;
1979
2191
  exports.paginationInputToOffset = paginationInputToOffset;
2192
+ exports.parseChainAssert = parseChainAssert;
1980
2193
  exports.platformSettings = platformSettings;
1981
2194
  exports.providerSuppliedPaths = providerSuppliedPaths;
1982
2195
  exports.redactSecretConfig = redactSecretConfig;
2196
+ exports.registerExtensionDatabaseMigrations = registerExtensionDatabaseMigrations;
1983
2197
  exports.registerExtensionEntities = registerExtensionEntities;
2198
+ exports.resetExtensionDatabaseMigrations = resetExtensionDatabaseMigrations;
1984
2199
  exports.resetExtensionEntities = resetExtensionEntities;
2200
+ exports.resolveChainText = resolveChainText;
2201
+ exports.resolveSortColumn = resolveSortColumn;
1985
2202
  exports.rethrowChainError = rethrowChainError;
1986
2203
  exports.sanitizeUserText = sanitizeUserText;
2204
+ exports.validationMessage = validationMessage;
1987
2205
  exports.verifySignedDocumentAgainstStoredDraft = verifySignedDocumentAgainstStoredDraft;
1988
- exports.waitAfterTransactBeforeChainTableRead = waitAfterTransactBeforeChainTableRead;
2206
+ exports.waitForEvent = waitForEvent;