@coopenomics/extension-kit 2026.9.24-3 → 2026.9.30
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 +121 -21
- package/dist/index.d.cts +96 -32
- package/dist/index.d.mts +96 -32
- package/dist/index.d.ts +96 -32
- package/dist/index.mjs +118 -23
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -1086,7 +1086,9 @@ __decorateClass$7([
|
|
|
1086
1086
|
graphql.Field(() => String, {
|
|
1087
1087
|
description: '\u041D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0438 ("ASC" \u0438\u043B\u0438 "DESC")',
|
|
1088
1088
|
defaultValue: "ASC"
|
|
1089
|
-
})
|
|
1089
|
+
}),
|
|
1090
|
+
classValidator.IsOptional(),
|
|
1091
|
+
classValidator.IsIn(["ASC", "DESC"], { message: validationMessage("kit.pagination.invalidSortOrder") })
|
|
1090
1092
|
], exports.PaginationInputDTO.prototype, "sortOrder", 2);
|
|
1091
1093
|
exports.PaginationInputDTO = __decorateClass$7([
|
|
1092
1094
|
graphql.InputType("PaginationInput")
|
|
@@ -1154,16 +1156,16 @@ class PaginationUtils {
|
|
|
1154
1156
|
static validatePaginationOptions(options) {
|
|
1155
1157
|
const { page = 1, limit = 10, sortBy, sortOrder = "ASC" } = options;
|
|
1156
1158
|
if (page < 1) {
|
|
1157
|
-
throw DomainError.
|
|
1159
|
+
throw DomainError.badRequest("KIT_PAGE_NUMBER_INVALID");
|
|
1158
1160
|
}
|
|
1159
1161
|
if (limit < 1 || limit > 1e3) {
|
|
1160
|
-
throw DomainError.
|
|
1162
|
+
throw DomainError.badRequest("KIT_PAGE_LIMIT_INVALID");
|
|
1161
1163
|
}
|
|
1162
1164
|
if (sortOrder !== "ASC" && sortOrder !== "DESC") {
|
|
1163
|
-
throw DomainError.
|
|
1165
|
+
throw DomainError.badRequest("KIT_SORT_ORDER_INVALID");
|
|
1164
1166
|
}
|
|
1165
1167
|
if (sortBy !== void 0 && sortBy !== null && !SORT_FIELD_PATTERN.test(sortBy)) {
|
|
1166
|
-
throw DomainError.
|
|
1168
|
+
throw DomainError.badRequest("KIT_SORT_FIELD_INVALID");
|
|
1167
1169
|
}
|
|
1168
1170
|
return {
|
|
1169
1171
|
page,
|
|
@@ -1533,7 +1535,7 @@ class CurrencyValidationUtil {
|
|
|
1533
1535
|
static validateCurrencySymbol(amount, fieldName = server.t("kit.currency.defaultFieldName")) {
|
|
1534
1536
|
if (!this.hasValidCurrencySymbol(amount)) {
|
|
1535
1537
|
const expectedSymbol = platformSettings().blockchain.rootGovernSymbol;
|
|
1536
|
-
throw DomainError.
|
|
1538
|
+
throw DomainError.badRequest("KIT_CURRENCY_SYMBOL_INVALID", { fieldName, expectedSymbol });
|
|
1537
1539
|
}
|
|
1538
1540
|
}
|
|
1539
1541
|
/**
|
|
@@ -1678,12 +1680,12 @@ exports.DomainToBlockchainUtils = class DomainToBlockchainUtils {
|
|
|
1678
1680
|
formatQuantityWithPrecision(quantity) {
|
|
1679
1681
|
const parts = quantity.split(" ");
|
|
1680
1682
|
if (parts.length !== 2) {
|
|
1681
|
-
throw DomainError.
|
|
1683
|
+
throw DomainError.badRequest("KIT_QUANTITY_FORMAT_INVALID", { quantity });
|
|
1682
1684
|
}
|
|
1683
1685
|
const [amount, symbol] = parts;
|
|
1684
1686
|
const numericAmount = parseFloat(amount);
|
|
1685
1687
|
if (isNaN(numericAmount)) {
|
|
1686
|
-
throw DomainError.
|
|
1688
|
+
throw DomainError.badRequest("KIT_QUANTITY_AMOUNT_INVALID", { amount });
|
|
1687
1689
|
}
|
|
1688
1690
|
const { rootSymbol, rootPrecision, rootGovernSymbol, rootGovernPrecision } = platformSettings().blockchain;
|
|
1689
1691
|
let precision;
|
|
@@ -1692,7 +1694,7 @@ exports.DomainToBlockchainUtils = class DomainToBlockchainUtils {
|
|
|
1692
1694
|
} else if (symbol === rootGovernSymbol) {
|
|
1693
1695
|
precision = rootGovernPrecision;
|
|
1694
1696
|
} else {
|
|
1695
|
-
throw DomainError.
|
|
1697
|
+
throw DomainError.badRequest("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
|
|
1696
1698
|
}
|
|
1697
1699
|
return `${numericAmount.toFixed(precision)} ${symbol}`;
|
|
1698
1700
|
}
|
|
@@ -1700,10 +1702,10 @@ exports.DomainToBlockchainUtils = class DomainToBlockchainUtils {
|
|
|
1700
1702
|
formatNumericStringToAssetString(numericString, precision, symbol) {
|
|
1701
1703
|
const numericValue = parseFloat(numericString);
|
|
1702
1704
|
if (isNaN(numericValue)) {
|
|
1703
|
-
throw DomainError.
|
|
1705
|
+
throw DomainError.badRequest("KIT_NUMBER_INVALID", { value: numericString });
|
|
1704
1706
|
}
|
|
1705
1707
|
if (numericValue < 0) {
|
|
1706
|
-
throw DomainError.
|
|
1708
|
+
throw DomainError.badRequest("KIT_NUMBER_NEGATIVE", { value: numericString });
|
|
1707
1709
|
}
|
|
1708
1710
|
return `${numericValue.toFixed(precision)} ${symbol}`;
|
|
1709
1711
|
}
|
|
@@ -1734,7 +1736,7 @@ exports.DomainToBlockchainUtils = __decorateClass([
|
|
|
1734
1736
|
common.Injectable()
|
|
1735
1737
|
], exports.DomainToBlockchainUtils);
|
|
1736
1738
|
|
|
1737
|
-
const DIGEST_RE = /^[0-9a-f]{64}
|
|
1739
|
+
const DIGEST_RE = /^[0-9a-f]{64}$/i;
|
|
1738
1740
|
function chainTextDigest(text) {
|
|
1739
1741
|
if (!text)
|
|
1740
1742
|
return "";
|
|
@@ -1743,6 +1745,9 @@ function chainTextDigest(text) {
|
|
|
1743
1745
|
function isChainTextDigest(value) {
|
|
1744
1746
|
return typeof value === "string" && DIGEST_RE.test(value);
|
|
1745
1747
|
}
|
|
1748
|
+
function isChainTextOf(text, chainValue) {
|
|
1749
|
+
return chainTextDigest(text) === (chainValue ?? "").toLowerCase();
|
|
1750
|
+
}
|
|
1746
1751
|
function resolveChainText(chainValue, current) {
|
|
1747
1752
|
return isChainTextDigest(chainValue) ? current ?? "" : chainValue;
|
|
1748
1753
|
}
|
|
@@ -1833,17 +1838,17 @@ class AmountFormatterUtils {
|
|
|
1833
1838
|
*/
|
|
1834
1839
|
static formatAmount(amountStr) {
|
|
1835
1840
|
if (!amountStr || typeof amountStr !== "string") {
|
|
1836
|
-
throw DomainError.
|
|
1841
|
+
throw DomainError.badRequest("KIT_AMOUNT_FORMAT_INVALID", { amount: amountStr });
|
|
1837
1842
|
}
|
|
1838
1843
|
const parts = amountStr.trim().split(/\s+/);
|
|
1839
1844
|
if (parts.length < 1 || !parts[0]) {
|
|
1840
|
-
throw DomainError.
|
|
1845
|
+
throw DomainError.badRequest("KIT_AMOUNT_FORMAT_INVALID", { amount: amountStr });
|
|
1841
1846
|
}
|
|
1842
1847
|
const amountPart = parts[0].replace(",", ".");
|
|
1843
1848
|
const currency = parts.length >= 2 ? parts.slice(1).join(" ") : "";
|
|
1844
1849
|
const amount = parseFloat(amountPart);
|
|
1845
1850
|
if (isNaN(amount)) {
|
|
1846
|
-
throw DomainError.
|
|
1851
|
+
throw DomainError.badRequest("KIT_AMOUNT_NUMBER_INVALID", { value: parts[0] });
|
|
1847
1852
|
}
|
|
1848
1853
|
const formattedAmount = amount.toLocaleString("ru-RU", {
|
|
1849
1854
|
minimumFractionDigits: 2,
|
|
@@ -1919,7 +1924,7 @@ class QuantityUtils {
|
|
|
1919
1924
|
} else if (symbol === rootGovernSymbol) {
|
|
1920
1925
|
return rootGovernPrecision;
|
|
1921
1926
|
} else {
|
|
1922
|
-
throw DomainError.
|
|
1927
|
+
throw DomainError.badRequest("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
|
|
1923
1928
|
}
|
|
1924
1929
|
}
|
|
1925
1930
|
/**
|
|
@@ -1929,7 +1934,7 @@ class QuantityUtils {
|
|
|
1929
1934
|
static validateSymbol(symbol) {
|
|
1930
1935
|
if (!this.isSupportedSymbol(symbol)) {
|
|
1931
1936
|
const { rootSymbol, rootGovernSymbol } = platformSettings().blockchain;
|
|
1932
|
-
throw DomainError.
|
|
1937
|
+
throw DomainError.badRequest("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
|
|
1933
1938
|
}
|
|
1934
1939
|
}
|
|
1935
1940
|
/**
|
|
@@ -1941,7 +1946,7 @@ class QuantityUtils {
|
|
|
1941
1946
|
static formatQuantityForBlockchain(amount, symbol) {
|
|
1942
1947
|
this.validateSymbol(symbol);
|
|
1943
1948
|
if (isNaN(amount) || amount < 0) {
|
|
1944
|
-
throw DomainError.
|
|
1949
|
+
throw DomainError.badRequest("KIT_NUMBER_INVALID", { value: amount });
|
|
1945
1950
|
}
|
|
1946
1951
|
const precision = this.getPrecisionForSymbol(symbol);
|
|
1947
1952
|
const formattedAmount = amount.toFixed(precision);
|
|
@@ -1956,7 +1961,7 @@ class QuantityUtils {
|
|
|
1956
1961
|
static combineQuantityAndSymbol(amount, symbol) {
|
|
1957
1962
|
this.validateSymbol(symbol);
|
|
1958
1963
|
if (isNaN(amount) || amount < 0) {
|
|
1959
|
-
throw DomainError.
|
|
1964
|
+
throw DomainError.badRequest("KIT_NUMBER_INVALID", { value: amount });
|
|
1960
1965
|
}
|
|
1961
1966
|
return `${amount} ${symbol}`;
|
|
1962
1967
|
}
|
|
@@ -1968,12 +1973,12 @@ class QuantityUtils {
|
|
|
1968
1973
|
static parseQuantityString(quantity) {
|
|
1969
1974
|
const parts = quantity.split(" ");
|
|
1970
1975
|
if (parts.length !== 2) {
|
|
1971
|
-
throw DomainError.
|
|
1976
|
+
throw DomainError.badRequest("KIT_QUANTITY_FORMAT_INVALID", { quantity });
|
|
1972
1977
|
}
|
|
1973
1978
|
const [amountStr, symbol] = parts;
|
|
1974
1979
|
const amount = parseFloat(amountStr);
|
|
1975
1980
|
if (isNaN(amount)) {
|
|
1976
|
-
throw DomainError.
|
|
1981
|
+
throw DomainError.badRequest("KIT_QUANTITY_AMOUNT_INVALID", { amount: amountStr });
|
|
1977
1982
|
}
|
|
1978
1983
|
this.validateSymbol(symbol);
|
|
1979
1984
|
return { amount, symbol };
|
|
@@ -2094,6 +2099,79 @@ function resolveSortColumn(repository, sortBy, fallback) {
|
|
|
2094
2099
|
return isKnownColumn ? sortBy : fallback;
|
|
2095
2100
|
}
|
|
2096
2101
|
|
|
2102
|
+
class LocalChangesCollector {
|
|
2103
|
+
constructor(isWatched, publish) {
|
|
2104
|
+
this.isWatched = isWatched;
|
|
2105
|
+
this.publish = publish;
|
|
2106
|
+
this.pending = /* @__PURE__ */ new WeakMap();
|
|
2107
|
+
}
|
|
2108
|
+
afterInsert(event) {
|
|
2109
|
+
this.collect(event.metadata, event.queryRunner, event.entity);
|
|
2110
|
+
}
|
|
2111
|
+
afterUpdate(event) {
|
|
2112
|
+
this.collect(event.metadata, event.queryRunner, { ...event.databaseEntity ?? {}, ...event.entity ?? {} });
|
|
2113
|
+
}
|
|
2114
|
+
afterRemove(event) {
|
|
2115
|
+
const row = event.databaseEntity ?? event.entity;
|
|
2116
|
+
this.collect(event.metadata, event.queryRunner, row, event.entityId);
|
|
2117
|
+
}
|
|
2118
|
+
afterTransactionCommit(event) {
|
|
2119
|
+
const changes = this.pending.get(event.queryRunner);
|
|
2120
|
+
if (!changes)
|
|
2121
|
+
return;
|
|
2122
|
+
this.pending.delete(event.queryRunner);
|
|
2123
|
+
changes.forEach((c) => void this.publish(c.table, c.primary_key, c.row));
|
|
2124
|
+
}
|
|
2125
|
+
afterTransactionRollback(event) {
|
|
2126
|
+
this.pending.delete(event.queryRunner);
|
|
2127
|
+
}
|
|
2128
|
+
collect(metadata, queryRunner, row, entityId) {
|
|
2129
|
+
const table = metadata.tableName;
|
|
2130
|
+
if (!this.isWatched(table))
|
|
2131
|
+
return;
|
|
2132
|
+
const change = { table, primary_key: primaryKeyOf(metadata, row, entityId), row };
|
|
2133
|
+
if (queryRunner?.isTransactionActive) {
|
|
2134
|
+
const list = this.pending.get(queryRunner) ?? [];
|
|
2135
|
+
list.push(change);
|
|
2136
|
+
this.pending.set(queryRunner, list);
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
void this.publish(change.table, change.primary_key, change.row);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
function primaryKeyOf(metadata, row, entityId) {
|
|
2143
|
+
if (row) {
|
|
2144
|
+
const ids = metadata.primaryColumns.map((c) => c.getEntityValue(row)).filter((v) => v !== void 0 && v !== null);
|
|
2145
|
+
if (ids.length)
|
|
2146
|
+
return ids.map(String).join(":");
|
|
2147
|
+
}
|
|
2148
|
+
if (entityId !== void 0 && entityId !== null) {
|
|
2149
|
+
return typeof entityId === "object" ? Object.values(entityId).map(String).join(":") : String(entityId);
|
|
2150
|
+
}
|
|
2151
|
+
return "";
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
const numericColumnTransformer = {
|
|
2155
|
+
to: (value) => value,
|
|
2156
|
+
from: (value) => value === null || value === void 0 ? value : Number(value)
|
|
2157
|
+
};
|
|
2158
|
+
|
|
2159
|
+
function SafeMarkup(validationOptions) {
|
|
2160
|
+
return function(object, propertyName) {
|
|
2161
|
+
classValidator.registerDecorator({
|
|
2162
|
+
name: "safeMarkup",
|
|
2163
|
+
target: object.constructor,
|
|
2164
|
+
propertyName,
|
|
2165
|
+
options: { message: validationMessage("kit.markup.executable"), ...validationOptions },
|
|
2166
|
+
validator: {
|
|
2167
|
+
validate(value) {
|
|
2168
|
+
return typeof value !== "string" || !cooptypes._Common.Text.hasExecutableMarkup(value);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
});
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2097
2175
|
class HttpApiError extends common.HttpException {
|
|
2098
2176
|
constructor(statusCode, message, isOperational = true, stack, subcode) {
|
|
2099
2177
|
super(message, statusCode);
|
|
@@ -2109,6 +2187,7 @@ class HttpApiError extends common.HttpException {
|
|
|
2109
2187
|
|
|
2110
2188
|
const ASSERT_PREFIX = /assertion failure with message: (.+?)(?:\n|$)/;
|
|
2111
2189
|
const CODED = /^([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+):\s*(.*)$/s;
|
|
2190
|
+
const ENCODING = /^Encoding error at root<[^>]*>((?:\.[^.<:]+<[^>]*>)*):/;
|
|
2112
2191
|
function parseChainAssert(message) {
|
|
2113
2192
|
const assert = message.match(ASSERT_PREFIX);
|
|
2114
2193
|
const body = (assert ? assert[1] : message).trim();
|
|
@@ -2124,7 +2203,23 @@ function chainErrorCode(error) {
|
|
|
2124
2203
|
const raw = error?.message ?? String(error);
|
|
2125
2204
|
return parseChainAssert(raw).code;
|
|
2126
2205
|
}
|
|
2206
|
+
function chainRefusalOf(error) {
|
|
2207
|
+
if (error instanceof DomainError)
|
|
2208
|
+
return error;
|
|
2209
|
+
const raw = error?.message ?? String(error);
|
|
2210
|
+
const encoding = raw.match(ENCODING);
|
|
2211
|
+
if (encoding) {
|
|
2212
|
+
const field = encoding[1].split(".").filter(Boolean).map((part) => part.replace(/<[^>]*>$/, "")).join(".");
|
|
2213
|
+
return DomainError.badRequest("CHAIN_ACTION_ARGUMENT_INVALID", { field });
|
|
2214
|
+
}
|
|
2215
|
+
if (!ASSERT_PREFIX.test(raw))
|
|
2216
|
+
return null;
|
|
2217
|
+
const { code, text } = parseChainAssert(raw);
|
|
2218
|
+
return DomainError.badRequest(code ?? "CHAIN_ASSERT", { message: text });
|
|
2219
|
+
}
|
|
2127
2220
|
function rethrowChainError(error) {
|
|
2221
|
+
if (error instanceof DomainError)
|
|
2222
|
+
throw error;
|
|
2128
2223
|
const raw = error?.message ?? String(error);
|
|
2129
2224
|
const match = raw.match(ASSERT_PREFIX);
|
|
2130
2225
|
if (!match)
|
|
@@ -2155,6 +2250,7 @@ exports.HttpApiError = HttpApiError;
|
|
|
2155
2250
|
exports.InjectBucket = InjectBucket;
|
|
2156
2251
|
exports.LOG_EXTENSION_REPOSITORY = LOG_EXTENSION_REPOSITORY;
|
|
2157
2252
|
exports.LangType = LangType;
|
|
2253
|
+
exports.LocalChangesCollector = LocalChangesCollector;
|
|
2158
2254
|
exports.LogExtensionDomainEntity = LogExtensionDomainEntity;
|
|
2159
2255
|
exports.ONBOARDING_EXPIRY_DAYS = ONBOARDING_EXPIRY_DAYS;
|
|
2160
2256
|
exports.ONBOARDING_EXPIRY_MS = ONBOARDING_EXPIRY_MS;
|
|
@@ -2165,11 +2261,13 @@ exports.QuantityUtils = QuantityUtils;
|
|
|
2165
2261
|
exports.ROLES_ANY_STATUS_KEY = ROLES_ANY_STATUS_KEY;
|
|
2166
2262
|
exports.ROLES_DENY_SELF_KEY = ROLES_DENY_SELF_KEY;
|
|
2167
2263
|
exports.SORT_FIELD_PATTERN = SORT_FIELD_PATTERN;
|
|
2264
|
+
exports.SafeMarkup = SafeMarkup;
|
|
2168
2265
|
exports.UseBucket = UseBucket;
|
|
2169
2266
|
exports.bucketProvidersFor = bucketProvidersFor;
|
|
2170
2267
|
exports.bucketTokenFor = bucketTokenFor;
|
|
2171
2268
|
exports.buildPaginationResult = buildPaginationResult;
|
|
2172
2269
|
exports.chainErrorCode = chainErrorCode;
|
|
2270
|
+
exports.chainRefusalOf = chainRefusalOf;
|
|
2173
2271
|
exports.chainTextDigest = chainTextDigest;
|
|
2174
2272
|
exports.checkPaymentAmount = checkPaymentAmount;
|
|
2175
2273
|
exports.checkPaymentSymbol = checkPaymentSymbol;
|
|
@@ -2187,8 +2285,10 @@ exports.getAmountPlusFee = getAmountPlusFee;
|
|
|
2187
2285
|
exports.getAppliedBlockNum = getAppliedBlockNum;
|
|
2188
2286
|
exports.hasServerSecret = hasServerSecret;
|
|
2189
2287
|
exports.isChainTextDigest = isChainTextDigest;
|
|
2288
|
+
exports.isChainTextOf = isChainTextOf;
|
|
2190
2289
|
exports.isExtensionAvailable = isExtensionAvailable;
|
|
2191
2290
|
exports.mergeSecretConfig = mergeSecretConfig;
|
|
2291
|
+
exports.numericColumnTransformer = numericColumnTransformer;
|
|
2192
2292
|
exports.paginationInputToOffset = paginationInputToOffset;
|
|
2193
2293
|
exports.parseChainAssert = parseChainAssert;
|
|
2194
2294
|
exports.platformSettings = platformSettings;
|
package/dist/index.d.cts
CHANGED
|
@@ -5,7 +5,8 @@ import { Reflector } from '@nestjs/core';
|
|
|
5
5
|
import { Observable } from 'rxjs';
|
|
6
6
|
import { ZodObject } from 'zod';
|
|
7
7
|
import { Cooperative } from 'cooptypes';
|
|
8
|
-
import { MigrationInterface, ObjectLiteral, Repository } from 'typeorm';
|
|
8
|
+
import { MigrationInterface, ObjectLiteral, Repository, EntitySubscriberInterface, InsertEvent, UpdateEvent, RemoveEvent, TransactionCommitEvent, TransactionRollbackEvent, ValueTransformer } from 'typeorm';
|
|
9
|
+
import { ValidationOptions } from 'class-validator';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Пути внутри объекта, по которым пайщик признаётся «своим» для поля.
|
|
@@ -1320,6 +1321,8 @@ declare class DomainToBlockchainUtils {
|
|
|
1320
1321
|
declare function chainTextDigest(text: string | null | undefined): string;
|
|
1321
1322
|
/** Значение из цепи — хеш текста, а не сам текст. */
|
|
1322
1323
|
declare function isChainTextDigest(value: string | null | undefined): value is string;
|
|
1324
|
+
/** Хеш в цепи — хеш именно этого текста (регистр хеша не важен). */
|
|
1325
|
+
declare function isChainTextOf(text: string | null | undefined, chainValue: string | null | undefined): boolean;
|
|
1323
1326
|
/**
|
|
1324
1327
|
* Значение поля из цепи для записи в базу: хеш текст в базе не заменяет.
|
|
1325
1328
|
* Строки, записанные до выноса текстов, несут сам текст и копируются как раньше.
|
|
@@ -1631,6 +1634,58 @@ declare function sanitizeUserText<T extends string | null | undefined>(value: T)
|
|
|
1631
1634
|
*/
|
|
1632
1635
|
declare function resolveSortColumn<T extends ObjectLiteral>(repository: Repository<T>, sortBy: string | undefined, fallback: string): string;
|
|
1633
1636
|
|
|
1637
|
+
/** Публикация сигнала ленты изменений: таблица базы, ключ строки, строка. */
|
|
1638
|
+
type LocalChangePublisher = (table: string, primary_key: string, row: Record<string, unknown> | undefined) => unknown;
|
|
1639
|
+
/**
|
|
1640
|
+
* Сигналы ленты изменений для данных узла вне цепи — общая часть подписчика
|
|
1641
|
+
* TypeORM для ядра и расширений со своей базой.
|
|
1642
|
+
*
|
|
1643
|
+
* Любая запись в наблюдаемую таблицу — через репозиторий, менеджер или
|
|
1644
|
+
* построитель запросов — порождает сигнал «перечитай». Сигнал уходит после
|
|
1645
|
+
* фиксации транзакции: стол по нему читает уже записанное, а откаченная запись
|
|
1646
|
+
* сигнала не даёт. Сырой `query()` подписчик не видит — такую запись
|
|
1647
|
+
* публикует сам её автор.
|
|
1648
|
+
*
|
|
1649
|
+
* Из `typeorm` здесь только типы: метаданные и события приходят от источника
|
|
1650
|
+
* данных вызывающей стороны.
|
|
1651
|
+
*/
|
|
1652
|
+
declare class LocalChangesCollector implements EntitySubscriberInterface {
|
|
1653
|
+
private readonly isWatched;
|
|
1654
|
+
private readonly publish;
|
|
1655
|
+
private readonly pending;
|
|
1656
|
+
constructor(isWatched: (table: string) => boolean, publish: LocalChangePublisher);
|
|
1657
|
+
afterInsert(event: InsertEvent<any>): void;
|
|
1658
|
+
afterUpdate(event: UpdateEvent<any>): void;
|
|
1659
|
+
afterRemove(event: RemoveEvent<any>): void;
|
|
1660
|
+
afterTransactionCommit(event: TransactionCommitEvent): void;
|
|
1661
|
+
afterTransactionRollback(event: TransactionRollbackEvent): void;
|
|
1662
|
+
private collect;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
/**
|
|
1666
|
+
* Трансформер колонок `numeric`, которые в домене — число: Postgres отдаёт
|
|
1667
|
+
* numeric строкой, а сущности и арифметика работают с `number`. Годится для
|
|
1668
|
+
* количеств (0.5 кг) и сумм, которые домен держит числом (сумма платежа);
|
|
1669
|
+
* денежные поля, которые домен держит строкой актива, его не используют.
|
|
1670
|
+
*
|
|
1671
|
+
* Один экземпляр на ядро и расширения — из пакета (из typeorm здесь только тип).
|
|
1672
|
+
*/
|
|
1673
|
+
declare const numericColumnTransformer: ValueTransformer;
|
|
1674
|
+
|
|
1675
|
+
/**
|
|
1676
|
+
* Текст, который уходит в подписываемый документ (повестка собрания, проект
|
|
1677
|
+
* решения совета), без исполняемой разметки.
|
|
1678
|
+
*
|
|
1679
|
+
* Вёрстка допускается: проекты решений на утверждение совета несут абзацы,
|
|
1680
|
+
* таблицы и стили документа. Отклоняются только скрипты, встраиваемые рамки и
|
|
1681
|
+
* объекты, формы, обработчики событий и опасные ссылки — правило одно с
|
|
1682
|
+
* очисткой редактора (`_Common.Text.hasExecutableMarkup` в cooptypes).
|
|
1683
|
+
*
|
|
1684
|
+
* Отказ, а не очистка: документ подписывается по хэшу, и переписанный текст
|
|
1685
|
+
* был бы не тем, что ввёл человек (решение владельца 25.09.2026, C28-80).
|
|
1686
|
+
*/
|
|
1687
|
+
declare function SafeMarkup(validationOptions?: ValidationOptions): (object: object, propertyName: string) => void;
|
|
1688
|
+
|
|
1634
1689
|
/**
|
|
1635
1690
|
* Ошибка API с кодом состояния HTTP.
|
|
1636
1691
|
*
|
|
@@ -1647,35 +1702,6 @@ declare class HttpApiError extends HttpException {
|
|
|
1647
1702
|
constructor(statusCode: HttpStatus, message: string, isOperational?: boolean, stack?: string, subcode?: any);
|
|
1648
1703
|
}
|
|
1649
1704
|
|
|
1650
|
-
/**
|
|
1651
|
-
* Разбор отказа контракта. Контракт пишет причину как `КОД: текст`
|
|
1652
|
-
* (`GATEWAY_OUTCOME_NOT_FOUND: Объект возврата не существует…`); старые
|
|
1653
|
-
* отказы — одним текстом, без кода.
|
|
1654
|
-
*/
|
|
1655
|
-
declare function parseChainAssert(message: string): {
|
|
1656
|
-
code?: string;
|
|
1657
|
-
text: string;
|
|
1658
|
-
};
|
|
1659
|
-
/**
|
|
1660
|
-
* Код отказа контракта — из DomainError, в который отказ уже превращён,
|
|
1661
|
-
* или из сырой ошибки цепи. Отказ без кода — undefined. Сравнивать отказы
|
|
1662
|
-
* контракта — по этому коду, а не по тексту: текст переводится и правится.
|
|
1663
|
-
*/
|
|
1664
|
-
declare function chainErrorCode(error: unknown): string | undefined;
|
|
1665
|
-
/**
|
|
1666
|
-
* Пробросить отказ цепи человеку.
|
|
1667
|
-
*
|
|
1668
|
-
* `eosio::check` возвращает сообщение обёрнутым в `assertion failure with
|
|
1669
|
-
* message: ...`; пайщику нужна причина, а не обёртка виртуальной машины.
|
|
1670
|
-
* Живёт в каркасе, потому что в цепь ходит не одно расширение: вторая копия
|
|
1671
|
-
* этой чистки разошлась бы с первой на первом же новом формате ошибки.
|
|
1672
|
-
*
|
|
1673
|
-
* Отказ с кодом (`КОД: текст`) уходит с этим кодом, текст — из словаря,
|
|
1674
|
-
* а без записи в словаре — текст контракта. Отказ без кода — `CHAIN_ASSERT`,
|
|
1675
|
-
* прочий сбой цепи — `CHAIN_ERROR`, текст — параметром `message`.
|
|
1676
|
-
*/
|
|
1677
|
-
declare function rethrowChainError(error: unknown): never;
|
|
1678
|
-
|
|
1679
1705
|
type DomainErrorParams = Record<string, string | number | boolean | null | undefined>;
|
|
1680
1706
|
interface Branded {
|
|
1681
1707
|
code: string;
|
|
@@ -1733,5 +1759,43 @@ declare function validationMessage(key: string, params?: DomainErrorParams): (ar
|
|
|
1733
1759
|
constraints?: unknown[];
|
|
1734
1760
|
}) => string;
|
|
1735
1761
|
|
|
1736
|
-
|
|
1737
|
-
|
|
1762
|
+
/**
|
|
1763
|
+
* Разбор отказа контракта. Контракт пишет причину как `КОД: текст`
|
|
1764
|
+
* (`GATEWAY_OUTCOME_NOT_FOUND: Объект возврата не существует…`); старые
|
|
1765
|
+
* отказы — одним текстом, без кода.
|
|
1766
|
+
*/
|
|
1767
|
+
declare function parseChainAssert(message: string): {
|
|
1768
|
+
code?: string;
|
|
1769
|
+
text: string;
|
|
1770
|
+
};
|
|
1771
|
+
/**
|
|
1772
|
+
* Код отказа контракта — из DomainError, в который отказ уже превращён,
|
|
1773
|
+
* или из сырой ошибки цепи. Отказ без кода — undefined. Сравнивать отказы
|
|
1774
|
+
* контракта — по этому коду, а не по тексту: текст переводится и правится.
|
|
1775
|
+
*/
|
|
1776
|
+
declare function chainErrorCode(error: unknown): string | undefined;
|
|
1777
|
+
/**
|
|
1778
|
+
* Отказ контракта (`eosio::check`) как отказ с кодом; иной сбой цепи — null.
|
|
1779
|
+
*
|
|
1780
|
+
* Отказ контракта — ответ на действие пайщика, а не сбой сервера. Клиент цепи
|
|
1781
|
+
* (@wharfkit/session) превращает его в простой Error, и без перевода он уходил
|
|
1782
|
+
* ответом 500 без кода — в журнал ошибок как поломка (C28-80). Переводит
|
|
1783
|
+
* единственная отправка в цепь, поэтому код получают все мутации сразу.
|
|
1784
|
+
*/
|
|
1785
|
+
declare function chainRefusalOf(error: unknown): DomainError | null;
|
|
1786
|
+
/**
|
|
1787
|
+
* Пробросить отказ цепи человеку.
|
|
1788
|
+
*
|
|
1789
|
+
* `eosio::check` возвращает сообщение обёрнутым в `assertion failure with
|
|
1790
|
+
* message: ...`; пайщику нужна причина, а не обёртка виртуальной машины.
|
|
1791
|
+
* Живёт в каркасе, потому что в цепь ходит не одно расширение: вторая копия
|
|
1792
|
+
* этой чистки разошлась бы с первой на первом же новом формате ошибки.
|
|
1793
|
+
*
|
|
1794
|
+
* Отказ с кодом (`КОД: текст`) уходит с этим кодом, текст — из словаря,
|
|
1795
|
+
* а без записи в словаре — текст контракта. Отказ без кода — `CHAIN_ASSERT`,
|
|
1796
|
+
* прочий сбой цепи — `CHAIN_ERROR`, текст — параметром `message`.
|
|
1797
|
+
*/
|
|
1798
|
+
declare function rethrowChainError(error: unknown): never;
|
|
1799
|
+
|
|
1800
|
+
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, LocalChangesCollector, 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, SafeMarkup, SignatureInfoDTO, SignatureInfoInputDTO, SignedDigitalDocumentDTO, SignedDigitalDocumentInputDTO, TransactionDTO, UseBucket, bucketProvidersFor, bucketTokenFor, buildPaginationResult, chainErrorCode, chainRefusalOf, chainTextDigest, checkPaymentAmount, checkPaymentSymbol, computeOnboardingExpiresAt, configureExtensionAuth, configurePlatformSettings, createPaginationResult, domainErrorMessage, extensionDatabaseMigrations, extensionEntities, generateHashFromString, generateRandomHash, generateUniqueHash, getAmountPlusFee, getAppliedBlockNum, hasServerSecret, isChainTextDigest, isChainTextOf, isExtensionAvailable, mergeSecretConfig, numericColumnTransformer, paginationInputToOffset, parseChainAssert, platformSettings, providerSuppliedPaths, redactSecretConfig, registerExtensionDatabaseMigrations, registerExtensionEntities, resetExtensionDatabaseMigrations, resetExtensionEntities, resolveChainText, resolveSortColumn, rethrowChainError, sanitizeUserText, validationMessage, verifySignedDocumentAgainstStoredDraft, waitForEvent };
|
|
1801
|
+
export type { AuthRolesOptions, BucketSource, BucketSpec, DeserializedDescriptionOfExtension, DomainErrorParams, EventSource, EventWait, ExcludeCommonProps, ExtensionAppTerminatePayload, ExtensionAuthOptions, ExtensionConfigFieldPolicy, ExtensionConfigPolicy, ExtensionDatabaseMigrationClass, ExtensionDomainInterface, ExtensionDomainRepository, ExtensionEntityClass, ExtensionSchemaMigrationAfterContext, GeneratedDocumentDomainInterface, GeneratedDocumentDraft, IDesktopConfig, IExtensionSchemaMigration, IRegistryExtension, IResolvedRegistryExtension, LoadGeneratedDocumentByDocHash, LocalChangePublisher, LogExtensionDomainInterface, LogExtensionDomainRepository, LogExtensionFilter, LogExtensionPaginationOptions, LogExtensionPaginationResult, PaymentDetails, PlatformBlockchainSettings, PlatformSettings, RegisteredBucket, RequireFields, SignedDocumentMetaCompareMode, SignedDocumentMetaVerification, SignedDocumentToVerify, TransactResultWithResponse };
|
package/dist/index.d.mts
CHANGED
|
@@ -5,7 +5,8 @@ import { Reflector } from '@nestjs/core';
|
|
|
5
5
|
import { Observable } from 'rxjs';
|
|
6
6
|
import { ZodObject } from 'zod';
|
|
7
7
|
import { Cooperative } from 'cooptypes';
|
|
8
|
-
import { MigrationInterface, ObjectLiteral, Repository } from 'typeorm';
|
|
8
|
+
import { MigrationInterface, ObjectLiteral, Repository, EntitySubscriberInterface, InsertEvent, UpdateEvent, RemoveEvent, TransactionCommitEvent, TransactionRollbackEvent, ValueTransformer } from 'typeorm';
|
|
9
|
+
import { ValidationOptions } from 'class-validator';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Пути внутри объекта, по которым пайщик признаётся «своим» для поля.
|
|
@@ -1320,6 +1321,8 @@ declare class DomainToBlockchainUtils {
|
|
|
1320
1321
|
declare function chainTextDigest(text: string | null | undefined): string;
|
|
1321
1322
|
/** Значение из цепи — хеш текста, а не сам текст. */
|
|
1322
1323
|
declare function isChainTextDigest(value: string | null | undefined): value is string;
|
|
1324
|
+
/** Хеш в цепи — хеш именно этого текста (регистр хеша не важен). */
|
|
1325
|
+
declare function isChainTextOf(text: string | null | undefined, chainValue: string | null | undefined): boolean;
|
|
1323
1326
|
/**
|
|
1324
1327
|
* Значение поля из цепи для записи в базу: хеш текст в базе не заменяет.
|
|
1325
1328
|
* Строки, записанные до выноса текстов, несут сам текст и копируются как раньше.
|
|
@@ -1631,6 +1634,58 @@ declare function sanitizeUserText<T extends string | null | undefined>(value: T)
|
|
|
1631
1634
|
*/
|
|
1632
1635
|
declare function resolveSortColumn<T extends ObjectLiteral>(repository: Repository<T>, sortBy: string | undefined, fallback: string): string;
|
|
1633
1636
|
|
|
1637
|
+
/** Публикация сигнала ленты изменений: таблица базы, ключ строки, строка. */
|
|
1638
|
+
type LocalChangePublisher = (table: string, primary_key: string, row: Record<string, unknown> | undefined) => unknown;
|
|
1639
|
+
/**
|
|
1640
|
+
* Сигналы ленты изменений для данных узла вне цепи — общая часть подписчика
|
|
1641
|
+
* TypeORM для ядра и расширений со своей базой.
|
|
1642
|
+
*
|
|
1643
|
+
* Любая запись в наблюдаемую таблицу — через репозиторий, менеджер или
|
|
1644
|
+
* построитель запросов — порождает сигнал «перечитай». Сигнал уходит после
|
|
1645
|
+
* фиксации транзакции: стол по нему читает уже записанное, а откаченная запись
|
|
1646
|
+
* сигнала не даёт. Сырой `query()` подписчик не видит — такую запись
|
|
1647
|
+
* публикует сам её автор.
|
|
1648
|
+
*
|
|
1649
|
+
* Из `typeorm` здесь только типы: метаданные и события приходят от источника
|
|
1650
|
+
* данных вызывающей стороны.
|
|
1651
|
+
*/
|
|
1652
|
+
declare class LocalChangesCollector implements EntitySubscriberInterface {
|
|
1653
|
+
private readonly isWatched;
|
|
1654
|
+
private readonly publish;
|
|
1655
|
+
private readonly pending;
|
|
1656
|
+
constructor(isWatched: (table: string) => boolean, publish: LocalChangePublisher);
|
|
1657
|
+
afterInsert(event: InsertEvent<any>): void;
|
|
1658
|
+
afterUpdate(event: UpdateEvent<any>): void;
|
|
1659
|
+
afterRemove(event: RemoveEvent<any>): void;
|
|
1660
|
+
afterTransactionCommit(event: TransactionCommitEvent): void;
|
|
1661
|
+
afterTransactionRollback(event: TransactionRollbackEvent): void;
|
|
1662
|
+
private collect;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
/**
|
|
1666
|
+
* Трансформер колонок `numeric`, которые в домене — число: Postgres отдаёт
|
|
1667
|
+
* numeric строкой, а сущности и арифметика работают с `number`. Годится для
|
|
1668
|
+
* количеств (0.5 кг) и сумм, которые домен держит числом (сумма платежа);
|
|
1669
|
+
* денежные поля, которые домен держит строкой актива, его не используют.
|
|
1670
|
+
*
|
|
1671
|
+
* Один экземпляр на ядро и расширения — из пакета (из typeorm здесь только тип).
|
|
1672
|
+
*/
|
|
1673
|
+
declare const numericColumnTransformer: ValueTransformer;
|
|
1674
|
+
|
|
1675
|
+
/**
|
|
1676
|
+
* Текст, который уходит в подписываемый документ (повестка собрания, проект
|
|
1677
|
+
* решения совета), без исполняемой разметки.
|
|
1678
|
+
*
|
|
1679
|
+
* Вёрстка допускается: проекты решений на утверждение совета несут абзацы,
|
|
1680
|
+
* таблицы и стили документа. Отклоняются только скрипты, встраиваемые рамки и
|
|
1681
|
+
* объекты, формы, обработчики событий и опасные ссылки — правило одно с
|
|
1682
|
+
* очисткой редактора (`_Common.Text.hasExecutableMarkup` в cooptypes).
|
|
1683
|
+
*
|
|
1684
|
+
* Отказ, а не очистка: документ подписывается по хэшу, и переписанный текст
|
|
1685
|
+
* был бы не тем, что ввёл человек (решение владельца 25.09.2026, C28-80).
|
|
1686
|
+
*/
|
|
1687
|
+
declare function SafeMarkup(validationOptions?: ValidationOptions): (object: object, propertyName: string) => void;
|
|
1688
|
+
|
|
1634
1689
|
/**
|
|
1635
1690
|
* Ошибка API с кодом состояния HTTP.
|
|
1636
1691
|
*
|
|
@@ -1647,35 +1702,6 @@ declare class HttpApiError extends HttpException {
|
|
|
1647
1702
|
constructor(statusCode: HttpStatus, message: string, isOperational?: boolean, stack?: string, subcode?: any);
|
|
1648
1703
|
}
|
|
1649
1704
|
|
|
1650
|
-
/**
|
|
1651
|
-
* Разбор отказа контракта. Контракт пишет причину как `КОД: текст`
|
|
1652
|
-
* (`GATEWAY_OUTCOME_NOT_FOUND: Объект возврата не существует…`); старые
|
|
1653
|
-
* отказы — одним текстом, без кода.
|
|
1654
|
-
*/
|
|
1655
|
-
declare function parseChainAssert(message: string): {
|
|
1656
|
-
code?: string;
|
|
1657
|
-
text: string;
|
|
1658
|
-
};
|
|
1659
|
-
/**
|
|
1660
|
-
* Код отказа контракта — из DomainError, в который отказ уже превращён,
|
|
1661
|
-
* или из сырой ошибки цепи. Отказ без кода — undefined. Сравнивать отказы
|
|
1662
|
-
* контракта — по этому коду, а не по тексту: текст переводится и правится.
|
|
1663
|
-
*/
|
|
1664
|
-
declare function chainErrorCode(error: unknown): string | undefined;
|
|
1665
|
-
/**
|
|
1666
|
-
* Пробросить отказ цепи человеку.
|
|
1667
|
-
*
|
|
1668
|
-
* `eosio::check` возвращает сообщение обёрнутым в `assertion failure with
|
|
1669
|
-
* message: ...`; пайщику нужна причина, а не обёртка виртуальной машины.
|
|
1670
|
-
* Живёт в каркасе, потому что в цепь ходит не одно расширение: вторая копия
|
|
1671
|
-
* этой чистки разошлась бы с первой на первом же новом формате ошибки.
|
|
1672
|
-
*
|
|
1673
|
-
* Отказ с кодом (`КОД: текст`) уходит с этим кодом, текст — из словаря,
|
|
1674
|
-
* а без записи в словаре — текст контракта. Отказ без кода — `CHAIN_ASSERT`,
|
|
1675
|
-
* прочий сбой цепи — `CHAIN_ERROR`, текст — параметром `message`.
|
|
1676
|
-
*/
|
|
1677
|
-
declare function rethrowChainError(error: unknown): never;
|
|
1678
|
-
|
|
1679
1705
|
type DomainErrorParams = Record<string, string | number | boolean | null | undefined>;
|
|
1680
1706
|
interface Branded {
|
|
1681
1707
|
code: string;
|
|
@@ -1733,5 +1759,43 @@ declare function validationMessage(key: string, params?: DomainErrorParams): (ar
|
|
|
1733
1759
|
constraints?: unknown[];
|
|
1734
1760
|
}) => string;
|
|
1735
1761
|
|
|
1736
|
-
|
|
1737
|
-
|
|
1762
|
+
/**
|
|
1763
|
+
* Разбор отказа контракта. Контракт пишет причину как `КОД: текст`
|
|
1764
|
+
* (`GATEWAY_OUTCOME_NOT_FOUND: Объект возврата не существует…`); старые
|
|
1765
|
+
* отказы — одним текстом, без кода.
|
|
1766
|
+
*/
|
|
1767
|
+
declare function parseChainAssert(message: string): {
|
|
1768
|
+
code?: string;
|
|
1769
|
+
text: string;
|
|
1770
|
+
};
|
|
1771
|
+
/**
|
|
1772
|
+
* Код отказа контракта — из DomainError, в который отказ уже превращён,
|
|
1773
|
+
* или из сырой ошибки цепи. Отказ без кода — undefined. Сравнивать отказы
|
|
1774
|
+
* контракта — по этому коду, а не по тексту: текст переводится и правится.
|
|
1775
|
+
*/
|
|
1776
|
+
declare function chainErrorCode(error: unknown): string | undefined;
|
|
1777
|
+
/**
|
|
1778
|
+
* Отказ контракта (`eosio::check`) как отказ с кодом; иной сбой цепи — null.
|
|
1779
|
+
*
|
|
1780
|
+
* Отказ контракта — ответ на действие пайщика, а не сбой сервера. Клиент цепи
|
|
1781
|
+
* (@wharfkit/session) превращает его в простой Error, и без перевода он уходил
|
|
1782
|
+
* ответом 500 без кода — в журнал ошибок как поломка (C28-80). Переводит
|
|
1783
|
+
* единственная отправка в цепь, поэтому код получают все мутации сразу.
|
|
1784
|
+
*/
|
|
1785
|
+
declare function chainRefusalOf(error: unknown): DomainError | null;
|
|
1786
|
+
/**
|
|
1787
|
+
* Пробросить отказ цепи человеку.
|
|
1788
|
+
*
|
|
1789
|
+
* `eosio::check` возвращает сообщение обёрнутым в `assertion failure with
|
|
1790
|
+
* message: ...`; пайщику нужна причина, а не обёртка виртуальной машины.
|
|
1791
|
+
* Живёт в каркасе, потому что в цепь ходит не одно расширение: вторая копия
|
|
1792
|
+
* этой чистки разошлась бы с первой на первом же новом формате ошибки.
|
|
1793
|
+
*
|
|
1794
|
+
* Отказ с кодом (`КОД: текст`) уходит с этим кодом, текст — из словаря,
|
|
1795
|
+
* а без записи в словаре — текст контракта. Отказ без кода — `CHAIN_ASSERT`,
|
|
1796
|
+
* прочий сбой цепи — `CHAIN_ERROR`, текст — параметром `message`.
|
|
1797
|
+
*/
|
|
1798
|
+
declare function rethrowChainError(error: unknown): never;
|
|
1799
|
+
|
|
1800
|
+
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, LocalChangesCollector, 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, SafeMarkup, SignatureInfoDTO, SignatureInfoInputDTO, SignedDigitalDocumentDTO, SignedDigitalDocumentInputDTO, TransactionDTO, UseBucket, bucketProvidersFor, bucketTokenFor, buildPaginationResult, chainErrorCode, chainRefusalOf, chainTextDigest, checkPaymentAmount, checkPaymentSymbol, computeOnboardingExpiresAt, configureExtensionAuth, configurePlatformSettings, createPaginationResult, domainErrorMessage, extensionDatabaseMigrations, extensionEntities, generateHashFromString, generateRandomHash, generateUniqueHash, getAmountPlusFee, getAppliedBlockNum, hasServerSecret, isChainTextDigest, isChainTextOf, isExtensionAvailable, mergeSecretConfig, numericColumnTransformer, paginationInputToOffset, parseChainAssert, platformSettings, providerSuppliedPaths, redactSecretConfig, registerExtensionDatabaseMigrations, registerExtensionEntities, resetExtensionDatabaseMigrations, resetExtensionEntities, resolveChainText, resolveSortColumn, rethrowChainError, sanitizeUserText, validationMessage, verifySignedDocumentAgainstStoredDraft, waitForEvent };
|
|
1801
|
+
export type { AuthRolesOptions, BucketSource, BucketSpec, DeserializedDescriptionOfExtension, DomainErrorParams, EventSource, EventWait, ExcludeCommonProps, ExtensionAppTerminatePayload, ExtensionAuthOptions, ExtensionConfigFieldPolicy, ExtensionConfigPolicy, ExtensionDatabaseMigrationClass, ExtensionDomainInterface, ExtensionDomainRepository, ExtensionEntityClass, ExtensionSchemaMigrationAfterContext, GeneratedDocumentDomainInterface, GeneratedDocumentDraft, IDesktopConfig, IExtensionSchemaMigration, IRegistryExtension, IResolvedRegistryExtension, LoadGeneratedDocumentByDocHash, LocalChangePublisher, LogExtensionDomainInterface, LogExtensionDomainRepository, LogExtensionFilter, LogExtensionPaginationOptions, LogExtensionPaginationResult, PaymentDetails, PlatformBlockchainSettings, PlatformSettings, RegisteredBucket, RequireFields, SignedDocumentMetaCompareMode, SignedDocumentMetaVerification, SignedDocumentToVerify, TransactResultWithResponse };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ import { Reflector } from '@nestjs/core';
|
|
|
5
5
|
import { Observable } from 'rxjs';
|
|
6
6
|
import { ZodObject } from 'zod';
|
|
7
7
|
import { Cooperative } from 'cooptypes';
|
|
8
|
-
import { MigrationInterface, ObjectLiteral, Repository } from 'typeorm';
|
|
8
|
+
import { MigrationInterface, ObjectLiteral, Repository, EntitySubscriberInterface, InsertEvent, UpdateEvent, RemoveEvent, TransactionCommitEvent, TransactionRollbackEvent, ValueTransformer } from 'typeorm';
|
|
9
|
+
import { ValidationOptions } from 'class-validator';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Пути внутри объекта, по которым пайщик признаётся «своим» для поля.
|
|
@@ -1320,6 +1321,8 @@ declare class DomainToBlockchainUtils {
|
|
|
1320
1321
|
declare function chainTextDigest(text: string | null | undefined): string;
|
|
1321
1322
|
/** Значение из цепи — хеш текста, а не сам текст. */
|
|
1322
1323
|
declare function isChainTextDigest(value: string | null | undefined): value is string;
|
|
1324
|
+
/** Хеш в цепи — хеш именно этого текста (регистр хеша не важен). */
|
|
1325
|
+
declare function isChainTextOf(text: string | null | undefined, chainValue: string | null | undefined): boolean;
|
|
1323
1326
|
/**
|
|
1324
1327
|
* Значение поля из цепи для записи в базу: хеш текст в базе не заменяет.
|
|
1325
1328
|
* Строки, записанные до выноса текстов, несут сам текст и копируются как раньше.
|
|
@@ -1631,6 +1634,58 @@ declare function sanitizeUserText<T extends string | null | undefined>(value: T)
|
|
|
1631
1634
|
*/
|
|
1632
1635
|
declare function resolveSortColumn<T extends ObjectLiteral>(repository: Repository<T>, sortBy: string | undefined, fallback: string): string;
|
|
1633
1636
|
|
|
1637
|
+
/** Публикация сигнала ленты изменений: таблица базы, ключ строки, строка. */
|
|
1638
|
+
type LocalChangePublisher = (table: string, primary_key: string, row: Record<string, unknown> | undefined) => unknown;
|
|
1639
|
+
/**
|
|
1640
|
+
* Сигналы ленты изменений для данных узла вне цепи — общая часть подписчика
|
|
1641
|
+
* TypeORM для ядра и расширений со своей базой.
|
|
1642
|
+
*
|
|
1643
|
+
* Любая запись в наблюдаемую таблицу — через репозиторий, менеджер или
|
|
1644
|
+
* построитель запросов — порождает сигнал «перечитай». Сигнал уходит после
|
|
1645
|
+
* фиксации транзакции: стол по нему читает уже записанное, а откаченная запись
|
|
1646
|
+
* сигнала не даёт. Сырой `query()` подписчик не видит — такую запись
|
|
1647
|
+
* публикует сам её автор.
|
|
1648
|
+
*
|
|
1649
|
+
* Из `typeorm` здесь только типы: метаданные и события приходят от источника
|
|
1650
|
+
* данных вызывающей стороны.
|
|
1651
|
+
*/
|
|
1652
|
+
declare class LocalChangesCollector implements EntitySubscriberInterface {
|
|
1653
|
+
private readonly isWatched;
|
|
1654
|
+
private readonly publish;
|
|
1655
|
+
private readonly pending;
|
|
1656
|
+
constructor(isWatched: (table: string) => boolean, publish: LocalChangePublisher);
|
|
1657
|
+
afterInsert(event: InsertEvent<any>): void;
|
|
1658
|
+
afterUpdate(event: UpdateEvent<any>): void;
|
|
1659
|
+
afterRemove(event: RemoveEvent<any>): void;
|
|
1660
|
+
afterTransactionCommit(event: TransactionCommitEvent): void;
|
|
1661
|
+
afterTransactionRollback(event: TransactionRollbackEvent): void;
|
|
1662
|
+
private collect;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
/**
|
|
1666
|
+
* Трансформер колонок `numeric`, которые в домене — число: Postgres отдаёт
|
|
1667
|
+
* numeric строкой, а сущности и арифметика работают с `number`. Годится для
|
|
1668
|
+
* количеств (0.5 кг) и сумм, которые домен держит числом (сумма платежа);
|
|
1669
|
+
* денежные поля, которые домен держит строкой актива, его не используют.
|
|
1670
|
+
*
|
|
1671
|
+
* Один экземпляр на ядро и расширения — из пакета (из typeorm здесь только тип).
|
|
1672
|
+
*/
|
|
1673
|
+
declare const numericColumnTransformer: ValueTransformer;
|
|
1674
|
+
|
|
1675
|
+
/**
|
|
1676
|
+
* Текст, который уходит в подписываемый документ (повестка собрания, проект
|
|
1677
|
+
* решения совета), без исполняемой разметки.
|
|
1678
|
+
*
|
|
1679
|
+
* Вёрстка допускается: проекты решений на утверждение совета несут абзацы,
|
|
1680
|
+
* таблицы и стили документа. Отклоняются только скрипты, встраиваемые рамки и
|
|
1681
|
+
* объекты, формы, обработчики событий и опасные ссылки — правило одно с
|
|
1682
|
+
* очисткой редактора (`_Common.Text.hasExecutableMarkup` в cooptypes).
|
|
1683
|
+
*
|
|
1684
|
+
* Отказ, а не очистка: документ подписывается по хэшу, и переписанный текст
|
|
1685
|
+
* был бы не тем, что ввёл человек (решение владельца 25.09.2026, C28-80).
|
|
1686
|
+
*/
|
|
1687
|
+
declare function SafeMarkup(validationOptions?: ValidationOptions): (object: object, propertyName: string) => void;
|
|
1688
|
+
|
|
1634
1689
|
/**
|
|
1635
1690
|
* Ошибка API с кодом состояния HTTP.
|
|
1636
1691
|
*
|
|
@@ -1647,35 +1702,6 @@ declare class HttpApiError extends HttpException {
|
|
|
1647
1702
|
constructor(statusCode: HttpStatus, message: string, isOperational?: boolean, stack?: string, subcode?: any);
|
|
1648
1703
|
}
|
|
1649
1704
|
|
|
1650
|
-
/**
|
|
1651
|
-
* Разбор отказа контракта. Контракт пишет причину как `КОД: текст`
|
|
1652
|
-
* (`GATEWAY_OUTCOME_NOT_FOUND: Объект возврата не существует…`); старые
|
|
1653
|
-
* отказы — одним текстом, без кода.
|
|
1654
|
-
*/
|
|
1655
|
-
declare function parseChainAssert(message: string): {
|
|
1656
|
-
code?: string;
|
|
1657
|
-
text: string;
|
|
1658
|
-
};
|
|
1659
|
-
/**
|
|
1660
|
-
* Код отказа контракта — из DomainError, в который отказ уже превращён,
|
|
1661
|
-
* или из сырой ошибки цепи. Отказ без кода — undefined. Сравнивать отказы
|
|
1662
|
-
* контракта — по этому коду, а не по тексту: текст переводится и правится.
|
|
1663
|
-
*/
|
|
1664
|
-
declare function chainErrorCode(error: unknown): string | undefined;
|
|
1665
|
-
/**
|
|
1666
|
-
* Пробросить отказ цепи человеку.
|
|
1667
|
-
*
|
|
1668
|
-
* `eosio::check` возвращает сообщение обёрнутым в `assertion failure with
|
|
1669
|
-
* message: ...`; пайщику нужна причина, а не обёртка виртуальной машины.
|
|
1670
|
-
* Живёт в каркасе, потому что в цепь ходит не одно расширение: вторая копия
|
|
1671
|
-
* этой чистки разошлась бы с первой на первом же новом формате ошибки.
|
|
1672
|
-
*
|
|
1673
|
-
* Отказ с кодом (`КОД: текст`) уходит с этим кодом, текст — из словаря,
|
|
1674
|
-
* а без записи в словаре — текст контракта. Отказ без кода — `CHAIN_ASSERT`,
|
|
1675
|
-
* прочий сбой цепи — `CHAIN_ERROR`, текст — параметром `message`.
|
|
1676
|
-
*/
|
|
1677
|
-
declare function rethrowChainError(error: unknown): never;
|
|
1678
|
-
|
|
1679
1705
|
type DomainErrorParams = Record<string, string | number | boolean | null | undefined>;
|
|
1680
1706
|
interface Branded {
|
|
1681
1707
|
code: string;
|
|
@@ -1733,5 +1759,43 @@ declare function validationMessage(key: string, params?: DomainErrorParams): (ar
|
|
|
1733
1759
|
constraints?: unknown[];
|
|
1734
1760
|
}) => string;
|
|
1735
1761
|
|
|
1736
|
-
|
|
1737
|
-
|
|
1762
|
+
/**
|
|
1763
|
+
* Разбор отказа контракта. Контракт пишет причину как `КОД: текст`
|
|
1764
|
+
* (`GATEWAY_OUTCOME_NOT_FOUND: Объект возврата не существует…`); старые
|
|
1765
|
+
* отказы — одним текстом, без кода.
|
|
1766
|
+
*/
|
|
1767
|
+
declare function parseChainAssert(message: string): {
|
|
1768
|
+
code?: string;
|
|
1769
|
+
text: string;
|
|
1770
|
+
};
|
|
1771
|
+
/**
|
|
1772
|
+
* Код отказа контракта — из DomainError, в который отказ уже превращён,
|
|
1773
|
+
* или из сырой ошибки цепи. Отказ без кода — undefined. Сравнивать отказы
|
|
1774
|
+
* контракта — по этому коду, а не по тексту: текст переводится и правится.
|
|
1775
|
+
*/
|
|
1776
|
+
declare function chainErrorCode(error: unknown): string | undefined;
|
|
1777
|
+
/**
|
|
1778
|
+
* Отказ контракта (`eosio::check`) как отказ с кодом; иной сбой цепи — null.
|
|
1779
|
+
*
|
|
1780
|
+
* Отказ контракта — ответ на действие пайщика, а не сбой сервера. Клиент цепи
|
|
1781
|
+
* (@wharfkit/session) превращает его в простой Error, и без перевода он уходил
|
|
1782
|
+
* ответом 500 без кода — в журнал ошибок как поломка (C28-80). Переводит
|
|
1783
|
+
* единственная отправка в цепь, поэтому код получают все мутации сразу.
|
|
1784
|
+
*/
|
|
1785
|
+
declare function chainRefusalOf(error: unknown): DomainError | null;
|
|
1786
|
+
/**
|
|
1787
|
+
* Пробросить отказ цепи человеку.
|
|
1788
|
+
*
|
|
1789
|
+
* `eosio::check` возвращает сообщение обёрнутым в `assertion failure with
|
|
1790
|
+
* message: ...`; пайщику нужна причина, а не обёртка виртуальной машины.
|
|
1791
|
+
* Живёт в каркасе, потому что в цепь ходит не одно расширение: вторая копия
|
|
1792
|
+
* этой чистки разошлась бы с первой на первом же новом формате ошибки.
|
|
1793
|
+
*
|
|
1794
|
+
* Отказ с кодом (`КОД: текст`) уходит с этим кодом, текст — из словаря,
|
|
1795
|
+
* а без записи в словаре — текст контракта. Отказ без кода — `CHAIN_ASSERT`,
|
|
1796
|
+
* прочий сбой цепи — `CHAIN_ERROR`, текст — параметром `message`.
|
|
1797
|
+
*/
|
|
1798
|
+
declare function rethrowChainError(error: unknown): never;
|
|
1799
|
+
|
|
1800
|
+
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, LocalChangesCollector, 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, SafeMarkup, SignatureInfoDTO, SignatureInfoInputDTO, SignedDigitalDocumentDTO, SignedDigitalDocumentInputDTO, TransactionDTO, UseBucket, bucketProvidersFor, bucketTokenFor, buildPaginationResult, chainErrorCode, chainRefusalOf, chainTextDigest, checkPaymentAmount, checkPaymentSymbol, computeOnboardingExpiresAt, configureExtensionAuth, configurePlatformSettings, createPaginationResult, domainErrorMessage, extensionDatabaseMigrations, extensionEntities, generateHashFromString, generateRandomHash, generateUniqueHash, getAmountPlusFee, getAppliedBlockNum, hasServerSecret, isChainTextDigest, isChainTextOf, isExtensionAvailable, mergeSecretConfig, numericColumnTransformer, paginationInputToOffset, parseChainAssert, platformSettings, providerSuppliedPaths, redactSecretConfig, registerExtensionDatabaseMigrations, registerExtensionEntities, resetExtensionDatabaseMigrations, resetExtensionEntities, resolveChainText, resolveSortColumn, rethrowChainError, sanitizeUserText, validationMessage, verifySignedDocumentAgainstStoredDraft, waitForEvent };
|
|
1801
|
+
export type { AuthRolesOptions, BucketSource, BucketSpec, DeserializedDescriptionOfExtension, DomainErrorParams, EventSource, EventWait, ExcludeCommonProps, ExtensionAppTerminatePayload, ExtensionAuthOptions, ExtensionConfigFieldPolicy, ExtensionConfigPolicy, ExtensionDatabaseMigrationClass, ExtensionDomainInterface, ExtensionDomainRepository, ExtensionEntityClass, ExtensionSchemaMigrationAfterContext, GeneratedDocumentDomainInterface, GeneratedDocumentDraft, IDesktopConfig, IExtensionSchemaMigration, IRegistryExtension, IResolvedRegistryExtension, LoadGeneratedDocumentByDocHash, LocalChangePublisher, LogExtensionDomainInterface, LogExtensionDomainRepository, LogExtensionFilter, LogExtensionPaginationOptions, LogExtensionPaginationResult, PaymentDetails, PlatformBlockchainSettings, PlatformSettings, RegisteredBucket, RequireFields, SignedDocumentMetaCompareMode, SignedDocumentMetaVerification, SignedDocumentToVerify, TransactResultWithResponse };
|
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { Reflector } from '@nestjs/core';
|
|
|
5
5
|
import { AuthGuard } from '@nestjs/passport';
|
|
6
6
|
import { timingSafeEqual } from 'node:crypto';
|
|
7
7
|
import { z } from 'zod';
|
|
8
|
-
import { IsNumber, IsString, IsArray, ArrayMinSize, ValidateNested, IsEnum, IsInt, IsOptional, Matches, IsNotEmpty, IsObject } from 'class-validator';
|
|
8
|
+
import { IsNumber, IsString, IsArray, ArrayMinSize, ValidateNested, IsEnum, IsInt, IsOptional, Matches, IsNotEmpty, IsIn, IsObject, registerDecorator } from 'class-validator';
|
|
9
9
|
import { Type } from 'class-transformer';
|
|
10
10
|
import { GraphQLJSON } from 'graphql-type-json';
|
|
11
11
|
import { Classes } from '@coopenomics/sdk';
|
|
@@ -1078,7 +1078,9 @@ __decorateClass$7([
|
|
|
1078
1078
|
Field(() => String, {
|
|
1079
1079
|
description: '\u041D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0438 ("ASC" \u0438\u043B\u0438 "DESC")',
|
|
1080
1080
|
defaultValue: "ASC"
|
|
1081
|
-
})
|
|
1081
|
+
}),
|
|
1082
|
+
IsOptional(),
|
|
1083
|
+
IsIn(["ASC", "DESC"], { message: validationMessage("kit.pagination.invalidSortOrder") })
|
|
1082
1084
|
], PaginationInputDTO.prototype, "sortOrder", 2);
|
|
1083
1085
|
PaginationInputDTO = __decorateClass$7([
|
|
1084
1086
|
InputType("PaginationInput")
|
|
@@ -1146,16 +1148,16 @@ class PaginationUtils {
|
|
|
1146
1148
|
static validatePaginationOptions(options) {
|
|
1147
1149
|
const { page = 1, limit = 10, sortBy, sortOrder = "ASC" } = options;
|
|
1148
1150
|
if (page < 1) {
|
|
1149
|
-
throw DomainError.
|
|
1151
|
+
throw DomainError.badRequest("KIT_PAGE_NUMBER_INVALID");
|
|
1150
1152
|
}
|
|
1151
1153
|
if (limit < 1 || limit > 1e3) {
|
|
1152
|
-
throw DomainError.
|
|
1154
|
+
throw DomainError.badRequest("KIT_PAGE_LIMIT_INVALID");
|
|
1153
1155
|
}
|
|
1154
1156
|
if (sortOrder !== "ASC" && sortOrder !== "DESC") {
|
|
1155
|
-
throw DomainError.
|
|
1157
|
+
throw DomainError.badRequest("KIT_SORT_ORDER_INVALID");
|
|
1156
1158
|
}
|
|
1157
1159
|
if (sortBy !== void 0 && sortBy !== null && !SORT_FIELD_PATTERN.test(sortBy)) {
|
|
1158
|
-
throw DomainError.
|
|
1160
|
+
throw DomainError.badRequest("KIT_SORT_FIELD_INVALID");
|
|
1159
1161
|
}
|
|
1160
1162
|
return {
|
|
1161
1163
|
page,
|
|
@@ -1525,7 +1527,7 @@ class CurrencyValidationUtil {
|
|
|
1525
1527
|
static validateCurrencySymbol(amount, fieldName = t("kit.currency.defaultFieldName")) {
|
|
1526
1528
|
if (!this.hasValidCurrencySymbol(amount)) {
|
|
1527
1529
|
const expectedSymbol = platformSettings().blockchain.rootGovernSymbol;
|
|
1528
|
-
throw DomainError.
|
|
1530
|
+
throw DomainError.badRequest("KIT_CURRENCY_SYMBOL_INVALID", { fieldName, expectedSymbol });
|
|
1529
1531
|
}
|
|
1530
1532
|
}
|
|
1531
1533
|
/**
|
|
@@ -1670,12 +1672,12 @@ let DomainToBlockchainUtils = class {
|
|
|
1670
1672
|
formatQuantityWithPrecision(quantity) {
|
|
1671
1673
|
const parts = quantity.split(" ");
|
|
1672
1674
|
if (parts.length !== 2) {
|
|
1673
|
-
throw DomainError.
|
|
1675
|
+
throw DomainError.badRequest("KIT_QUANTITY_FORMAT_INVALID", { quantity });
|
|
1674
1676
|
}
|
|
1675
1677
|
const [amount, symbol] = parts;
|
|
1676
1678
|
const numericAmount = parseFloat(amount);
|
|
1677
1679
|
if (isNaN(numericAmount)) {
|
|
1678
|
-
throw DomainError.
|
|
1680
|
+
throw DomainError.badRequest("KIT_QUANTITY_AMOUNT_INVALID", { amount });
|
|
1679
1681
|
}
|
|
1680
1682
|
const { rootSymbol, rootPrecision, rootGovernSymbol, rootGovernPrecision } = platformSettings().blockchain;
|
|
1681
1683
|
let precision;
|
|
@@ -1684,7 +1686,7 @@ let DomainToBlockchainUtils = class {
|
|
|
1684
1686
|
} else if (symbol === rootGovernSymbol) {
|
|
1685
1687
|
precision = rootGovernPrecision;
|
|
1686
1688
|
} else {
|
|
1687
|
-
throw DomainError.
|
|
1689
|
+
throw DomainError.badRequest("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
|
|
1688
1690
|
}
|
|
1689
1691
|
return `${numericAmount.toFixed(precision)} ${symbol}`;
|
|
1690
1692
|
}
|
|
@@ -1692,10 +1694,10 @@ let DomainToBlockchainUtils = class {
|
|
|
1692
1694
|
formatNumericStringToAssetString(numericString, precision, symbol) {
|
|
1693
1695
|
const numericValue = parseFloat(numericString);
|
|
1694
1696
|
if (isNaN(numericValue)) {
|
|
1695
|
-
throw DomainError.
|
|
1697
|
+
throw DomainError.badRequest("KIT_NUMBER_INVALID", { value: numericString });
|
|
1696
1698
|
}
|
|
1697
1699
|
if (numericValue < 0) {
|
|
1698
|
-
throw DomainError.
|
|
1700
|
+
throw DomainError.badRequest("KIT_NUMBER_NEGATIVE", { value: numericString });
|
|
1699
1701
|
}
|
|
1700
1702
|
return `${numericValue.toFixed(precision)} ${symbol}`;
|
|
1701
1703
|
}
|
|
@@ -1726,7 +1728,7 @@ DomainToBlockchainUtils = __decorateClass([
|
|
|
1726
1728
|
Injectable()
|
|
1727
1729
|
], DomainToBlockchainUtils);
|
|
1728
1730
|
|
|
1729
|
-
const DIGEST_RE = /^[0-9a-f]{64}
|
|
1731
|
+
const DIGEST_RE = /^[0-9a-f]{64}$/i;
|
|
1730
1732
|
function chainTextDigest(text) {
|
|
1731
1733
|
if (!text)
|
|
1732
1734
|
return "";
|
|
@@ -1735,6 +1737,9 @@ function chainTextDigest(text) {
|
|
|
1735
1737
|
function isChainTextDigest(value) {
|
|
1736
1738
|
return typeof value === "string" && DIGEST_RE.test(value);
|
|
1737
1739
|
}
|
|
1740
|
+
function isChainTextOf(text, chainValue) {
|
|
1741
|
+
return chainTextDigest(text) === (chainValue ?? "").toLowerCase();
|
|
1742
|
+
}
|
|
1738
1743
|
function resolveChainText(chainValue, current) {
|
|
1739
1744
|
return isChainTextDigest(chainValue) ? current ?? "" : chainValue;
|
|
1740
1745
|
}
|
|
@@ -1825,17 +1830,17 @@ class AmountFormatterUtils {
|
|
|
1825
1830
|
*/
|
|
1826
1831
|
static formatAmount(amountStr) {
|
|
1827
1832
|
if (!amountStr || typeof amountStr !== "string") {
|
|
1828
|
-
throw DomainError.
|
|
1833
|
+
throw DomainError.badRequest("KIT_AMOUNT_FORMAT_INVALID", { amount: amountStr });
|
|
1829
1834
|
}
|
|
1830
1835
|
const parts = amountStr.trim().split(/\s+/);
|
|
1831
1836
|
if (parts.length < 1 || !parts[0]) {
|
|
1832
|
-
throw DomainError.
|
|
1837
|
+
throw DomainError.badRequest("KIT_AMOUNT_FORMAT_INVALID", { amount: amountStr });
|
|
1833
1838
|
}
|
|
1834
1839
|
const amountPart = parts[0].replace(",", ".");
|
|
1835
1840
|
const currency = parts.length >= 2 ? parts.slice(1).join(" ") : "";
|
|
1836
1841
|
const amount = parseFloat(amountPart);
|
|
1837
1842
|
if (isNaN(amount)) {
|
|
1838
|
-
throw DomainError.
|
|
1843
|
+
throw DomainError.badRequest("KIT_AMOUNT_NUMBER_INVALID", { value: parts[0] });
|
|
1839
1844
|
}
|
|
1840
1845
|
const formattedAmount = amount.toLocaleString("ru-RU", {
|
|
1841
1846
|
minimumFractionDigits: 2,
|
|
@@ -1911,7 +1916,7 @@ class QuantityUtils {
|
|
|
1911
1916
|
} else if (symbol === rootGovernSymbol) {
|
|
1912
1917
|
return rootGovernPrecision;
|
|
1913
1918
|
} else {
|
|
1914
|
-
throw DomainError.
|
|
1919
|
+
throw DomainError.badRequest("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
|
|
1915
1920
|
}
|
|
1916
1921
|
}
|
|
1917
1922
|
/**
|
|
@@ -1921,7 +1926,7 @@ class QuantityUtils {
|
|
|
1921
1926
|
static validateSymbol(symbol) {
|
|
1922
1927
|
if (!this.isSupportedSymbol(symbol)) {
|
|
1923
1928
|
const { rootSymbol, rootGovernSymbol } = platformSettings().blockchain;
|
|
1924
|
-
throw DomainError.
|
|
1929
|
+
throw DomainError.badRequest("KIT_SYMBOL_UNSUPPORTED", { symbol, rootSymbol, rootGovernSymbol });
|
|
1925
1930
|
}
|
|
1926
1931
|
}
|
|
1927
1932
|
/**
|
|
@@ -1933,7 +1938,7 @@ class QuantityUtils {
|
|
|
1933
1938
|
static formatQuantityForBlockchain(amount, symbol) {
|
|
1934
1939
|
this.validateSymbol(symbol);
|
|
1935
1940
|
if (isNaN(amount) || amount < 0) {
|
|
1936
|
-
throw DomainError.
|
|
1941
|
+
throw DomainError.badRequest("KIT_NUMBER_INVALID", { value: amount });
|
|
1937
1942
|
}
|
|
1938
1943
|
const precision = this.getPrecisionForSymbol(symbol);
|
|
1939
1944
|
const formattedAmount = amount.toFixed(precision);
|
|
@@ -1948,7 +1953,7 @@ class QuantityUtils {
|
|
|
1948
1953
|
static combineQuantityAndSymbol(amount, symbol) {
|
|
1949
1954
|
this.validateSymbol(symbol);
|
|
1950
1955
|
if (isNaN(amount) || amount < 0) {
|
|
1951
|
-
throw DomainError.
|
|
1956
|
+
throw DomainError.badRequest("KIT_NUMBER_INVALID", { value: amount });
|
|
1952
1957
|
}
|
|
1953
1958
|
return `${amount} ${symbol}`;
|
|
1954
1959
|
}
|
|
@@ -1960,12 +1965,12 @@ class QuantityUtils {
|
|
|
1960
1965
|
static parseQuantityString(quantity) {
|
|
1961
1966
|
const parts = quantity.split(" ");
|
|
1962
1967
|
if (parts.length !== 2) {
|
|
1963
|
-
throw DomainError.
|
|
1968
|
+
throw DomainError.badRequest("KIT_QUANTITY_FORMAT_INVALID", { quantity });
|
|
1964
1969
|
}
|
|
1965
1970
|
const [amountStr, symbol] = parts;
|
|
1966
1971
|
const amount = parseFloat(amountStr);
|
|
1967
1972
|
if (isNaN(amount)) {
|
|
1968
|
-
throw DomainError.
|
|
1973
|
+
throw DomainError.badRequest("KIT_QUANTITY_AMOUNT_INVALID", { amount: amountStr });
|
|
1969
1974
|
}
|
|
1970
1975
|
this.validateSymbol(symbol);
|
|
1971
1976
|
return { amount, symbol };
|
|
@@ -2086,6 +2091,79 @@ function resolveSortColumn(repository, sortBy, fallback) {
|
|
|
2086
2091
|
return isKnownColumn ? sortBy : fallback;
|
|
2087
2092
|
}
|
|
2088
2093
|
|
|
2094
|
+
class LocalChangesCollector {
|
|
2095
|
+
constructor(isWatched, publish) {
|
|
2096
|
+
this.isWatched = isWatched;
|
|
2097
|
+
this.publish = publish;
|
|
2098
|
+
this.pending = /* @__PURE__ */ new WeakMap();
|
|
2099
|
+
}
|
|
2100
|
+
afterInsert(event) {
|
|
2101
|
+
this.collect(event.metadata, event.queryRunner, event.entity);
|
|
2102
|
+
}
|
|
2103
|
+
afterUpdate(event) {
|
|
2104
|
+
this.collect(event.metadata, event.queryRunner, { ...event.databaseEntity ?? {}, ...event.entity ?? {} });
|
|
2105
|
+
}
|
|
2106
|
+
afterRemove(event) {
|
|
2107
|
+
const row = event.databaseEntity ?? event.entity;
|
|
2108
|
+
this.collect(event.metadata, event.queryRunner, row, event.entityId);
|
|
2109
|
+
}
|
|
2110
|
+
afterTransactionCommit(event) {
|
|
2111
|
+
const changes = this.pending.get(event.queryRunner);
|
|
2112
|
+
if (!changes)
|
|
2113
|
+
return;
|
|
2114
|
+
this.pending.delete(event.queryRunner);
|
|
2115
|
+
changes.forEach((c) => void this.publish(c.table, c.primary_key, c.row));
|
|
2116
|
+
}
|
|
2117
|
+
afterTransactionRollback(event) {
|
|
2118
|
+
this.pending.delete(event.queryRunner);
|
|
2119
|
+
}
|
|
2120
|
+
collect(metadata, queryRunner, row, entityId) {
|
|
2121
|
+
const table = metadata.tableName;
|
|
2122
|
+
if (!this.isWatched(table))
|
|
2123
|
+
return;
|
|
2124
|
+
const change = { table, primary_key: primaryKeyOf(metadata, row, entityId), row };
|
|
2125
|
+
if (queryRunner?.isTransactionActive) {
|
|
2126
|
+
const list = this.pending.get(queryRunner) ?? [];
|
|
2127
|
+
list.push(change);
|
|
2128
|
+
this.pending.set(queryRunner, list);
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
void this.publish(change.table, change.primary_key, change.row);
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
function primaryKeyOf(metadata, row, entityId) {
|
|
2135
|
+
if (row) {
|
|
2136
|
+
const ids = metadata.primaryColumns.map((c) => c.getEntityValue(row)).filter((v) => v !== void 0 && v !== null);
|
|
2137
|
+
if (ids.length)
|
|
2138
|
+
return ids.map(String).join(":");
|
|
2139
|
+
}
|
|
2140
|
+
if (entityId !== void 0 && entityId !== null) {
|
|
2141
|
+
return typeof entityId === "object" ? Object.values(entityId).map(String).join(":") : String(entityId);
|
|
2142
|
+
}
|
|
2143
|
+
return "";
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
const numericColumnTransformer = {
|
|
2147
|
+
to: (value) => value,
|
|
2148
|
+
from: (value) => value === null || value === void 0 ? value : Number(value)
|
|
2149
|
+
};
|
|
2150
|
+
|
|
2151
|
+
function SafeMarkup(validationOptions) {
|
|
2152
|
+
return function(object, propertyName) {
|
|
2153
|
+
registerDecorator({
|
|
2154
|
+
name: "safeMarkup",
|
|
2155
|
+
target: object.constructor,
|
|
2156
|
+
propertyName,
|
|
2157
|
+
options: { message: validationMessage("kit.markup.executable"), ...validationOptions },
|
|
2158
|
+
validator: {
|
|
2159
|
+
validate(value) {
|
|
2160
|
+
return typeof value !== "string" || !_Common.Text.hasExecutableMarkup(value);
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
});
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2089
2167
|
class HttpApiError extends HttpException {
|
|
2090
2168
|
constructor(statusCode, message, isOperational = true, stack, subcode) {
|
|
2091
2169
|
super(message, statusCode);
|
|
@@ -2101,6 +2179,7 @@ class HttpApiError extends HttpException {
|
|
|
2101
2179
|
|
|
2102
2180
|
const ASSERT_PREFIX = /assertion failure with message: (.+?)(?:\n|$)/;
|
|
2103
2181
|
const CODED = /^([A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+):\s*(.*)$/s;
|
|
2182
|
+
const ENCODING = /^Encoding error at root<[^>]*>((?:\.[^.<:]+<[^>]*>)*):/;
|
|
2104
2183
|
function parseChainAssert(message) {
|
|
2105
2184
|
const assert = message.match(ASSERT_PREFIX);
|
|
2106
2185
|
const body = (assert ? assert[1] : message).trim();
|
|
@@ -2116,7 +2195,23 @@ function chainErrorCode(error) {
|
|
|
2116
2195
|
const raw = error?.message ?? String(error);
|
|
2117
2196
|
return parseChainAssert(raw).code;
|
|
2118
2197
|
}
|
|
2198
|
+
function chainRefusalOf(error) {
|
|
2199
|
+
if (error instanceof DomainError)
|
|
2200
|
+
return error;
|
|
2201
|
+
const raw = error?.message ?? String(error);
|
|
2202
|
+
const encoding = raw.match(ENCODING);
|
|
2203
|
+
if (encoding) {
|
|
2204
|
+
const field = encoding[1].split(".").filter(Boolean).map((part) => part.replace(/<[^>]*>$/, "")).join(".");
|
|
2205
|
+
return DomainError.badRequest("CHAIN_ACTION_ARGUMENT_INVALID", { field });
|
|
2206
|
+
}
|
|
2207
|
+
if (!ASSERT_PREFIX.test(raw))
|
|
2208
|
+
return null;
|
|
2209
|
+
const { code, text } = parseChainAssert(raw);
|
|
2210
|
+
return DomainError.badRequest(code ?? "CHAIN_ASSERT", { message: text });
|
|
2211
|
+
}
|
|
2119
2212
|
function rethrowChainError(error) {
|
|
2213
|
+
if (error instanceof DomainError)
|
|
2214
|
+
throw error;
|
|
2120
2215
|
const raw = error?.message ?? String(error);
|
|
2121
2216
|
const match = raw.match(ASSERT_PREFIX);
|
|
2122
2217
|
if (!match)
|
|
@@ -2125,4 +2220,4 @@ function rethrowChainError(error) {
|
|
|
2125
2220
|
throw DomainError.badRequest(code ?? "CHAIN_ASSERT", { message: text });
|
|
2126
2221
|
}
|
|
2127
2222
|
|
|
2128
|
-
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 };
|
|
2223
|
+
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, LocalChangesCollector, 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, SafeMarkup, SignatureInfoDTO, SignatureInfoInputDTO, SignedDigitalDocumentDTO, SignedDigitalDocumentInputDTO, TransactionDTO, UseBucket, bucketProvidersFor, bucketTokenFor, buildPaginationResult, chainErrorCode, chainRefusalOf, chainTextDigest, checkPaymentAmount, checkPaymentSymbol, computeOnboardingExpiresAt, configureExtensionAuth, configurePlatformSettings, createPaginationResult, domainErrorMessage, extensionDatabaseMigrations, extensionEntities, generateHashFromString, generateRandomHash, generateUniqueHash, getAmountPlusFee, getAppliedBlockNum, hasServerSecret, isChainTextDigest, isChainTextOf, isExtensionAvailable, mergeSecretConfig, numericColumnTransformer, paginationInputToOffset, parseChainAssert, platformSettings, providerSuppliedPaths, redactSecretConfig, registerExtensionDatabaseMigrations, registerExtensionEntities, resetExtensionDatabaseMigrations, resetExtensionEntities, resolveChainText, resolveSortColumn, rethrowChainError, sanitizeUserText, validationMessage, verifySignedDocumentAgainstStoredDraft, waitForEvent };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coopenomics/extension-kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2026.9.
|
|
4
|
+
"version": "2026.9.30",
|
|
5
5
|
"description": "Служебный каркас расширения цифрового кооператива: базовый модуль, сущности и репозитории расширения, авторизация, каркас блокчейн-синхронизации, контракт миграций схемы, контракт реестра",
|
|
6
6
|
"author": "Alex Ant <dacom.dark.sun@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@coopenomics/i18n": "workspace:*",
|
|
42
|
-
"@coopenomics/sdk": "2026.9.
|
|
42
|
+
"@coopenomics/sdk": "2026.9.30",
|
|
43
43
|
"@nestjs/common": "^10.0.0",
|
|
44
44
|
"@nestjs/core": "^10.0.0",
|
|
45
45
|
"@nestjs/graphql": "^12.2.1",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"zod": "^3.0.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@coopenomics/i18n": "2026.9.
|
|
63
|
+
"@coopenomics/i18n": "2026.9.30",
|
|
64
64
|
"@nestjs/common": "^10.0.0",
|
|
65
65
|
"@nestjs/core": "^10.0.0",
|
|
66
66
|
"@nestjs/graphql": "^12.2.1",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@wharfkit/session": "^1.4.0",
|
|
71
71
|
"class-transformer": "^0.5.1",
|
|
72
72
|
"class-validator": "^0.14.1",
|
|
73
|
-
"cooptypes": "2026.9.
|
|
73
|
+
"cooptypes": "2026.9.30",
|
|
74
74
|
"graphql": "^16.9.0",
|
|
75
75
|
"graphql-type-json": "^0.3.2",
|
|
76
76
|
"moment": "^2.30.1",
|
|
@@ -82,5 +82,5 @@
|
|
|
82
82
|
"unbuild": "^2.0.0",
|
|
83
83
|
"zod": "^3.0.0"
|
|
84
84
|
},
|
|
85
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "1346a804b5b9daec565a4bea5104510a6c832b0e"
|
|
86
86
|
}
|