@torrow/sdk-client 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -1
- package/dist/index.d.ts +238 -58
- package/dist/index.js +617 -243
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,103 +11,167 @@ async function buildAuthHeaders(config) {
|
|
|
11
11
|
return headers;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// ../../src/http/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
// ../../src/http/TorrowSdkError.ts
|
|
15
|
+
var TorrowSdkErrorCategory = /* @__PURE__ */ ((TorrowSdkErrorCategory2) => {
|
|
16
|
+
TorrowSdkErrorCategory2["InvalidInput"] = "InvalidInput";
|
|
17
|
+
TorrowSdkErrorCategory2["InvalidState"] = "InvalidState";
|
|
18
|
+
TorrowSdkErrorCategory2["AccessDenied"] = "AccessDenied";
|
|
19
|
+
TorrowSdkErrorCategory2["InsufficientData"] = "InsufficientData";
|
|
20
|
+
TorrowSdkErrorCategory2["InvalidResponse"] = "InvalidResponse";
|
|
21
|
+
TorrowSdkErrorCategory2["VersionConflict"] = "VersionConflict";
|
|
22
|
+
TorrowSdkErrorCategory2["AuthenticationRequired"] = "AuthenticationRequired";
|
|
23
|
+
TorrowSdkErrorCategory2["UnsupportedOperation"] = "UnsupportedOperation";
|
|
24
|
+
TorrowSdkErrorCategory2["RateLimited"] = "RateLimited";
|
|
25
|
+
TorrowSdkErrorCategory2["HttpFailure"] = "HttpFailure";
|
|
26
|
+
TorrowSdkErrorCategory2["TransportFailure"] = "TransportFailure";
|
|
27
|
+
TorrowSdkErrorCategory2["StorageFailure"] = "StorageFailure";
|
|
28
|
+
TorrowSdkErrorCategory2["Cancelled"] = "Cancelled";
|
|
29
|
+
TorrowSdkErrorCategory2["Unknown"] = "Unknown";
|
|
30
|
+
return TorrowSdkErrorCategory2;
|
|
31
|
+
})(TorrowSdkErrorCategory || {});
|
|
32
|
+
var TorrowSdkRequestState = /* @__PURE__ */ ((TorrowSdkRequestState2) => {
|
|
33
|
+
TorrowSdkRequestState2["NotStarted"] = "NotStarted";
|
|
34
|
+
TorrowSdkRequestState2["Started"] = "Started";
|
|
35
|
+
return TorrowSdkRequestState2;
|
|
36
|
+
})(TorrowSdkRequestState || {});
|
|
37
|
+
var TorrowSdkEffectArea = /* @__PURE__ */ ((TorrowSdkEffectArea2) => {
|
|
38
|
+
TorrowSdkEffectArea2["Remote"] = "Remote";
|
|
39
|
+
TorrowSdkEffectArea2["SessionMemory"] = "SessionMemory";
|
|
40
|
+
TorrowSdkEffectArea2["Storage"] = "Storage";
|
|
41
|
+
return TorrowSdkEffectArea2;
|
|
42
|
+
})(TorrowSdkEffectArea || {});
|
|
43
|
+
var TorrowSdkEffectState = /* @__PURE__ */ ((TorrowSdkEffectState2) => {
|
|
44
|
+
TorrowSdkEffectState2["NotApplied"] = "NotApplied";
|
|
45
|
+
TorrowSdkEffectState2["Applied"] = "Applied";
|
|
46
|
+
TorrowSdkEffectState2["Unknown"] = "Unknown";
|
|
47
|
+
return TorrowSdkEffectState2;
|
|
48
|
+
})(TorrowSdkEffectState || {});
|
|
49
|
+
var TorrowSdkErrorDomain = /* @__PURE__ */ ((TorrowSdkErrorDomain2) => {
|
|
50
|
+
TorrowSdkErrorDomain2["BehaviorSettings"] = "BehaviorSettings";
|
|
51
|
+
TorrowSdkErrorDomain2["Otp"] = "Otp";
|
|
52
|
+
TorrowSdkErrorDomain2["AuthSession"] = "AuthSession";
|
|
53
|
+
return TorrowSdkErrorDomain2;
|
|
54
|
+
})(TorrowSdkErrorDomain || {});
|
|
55
|
+
var OtpErrorReason = /* @__PURE__ */ ((OtpErrorReason2) => {
|
|
56
|
+
OtpErrorReason2["InvalidChallenge"] = "InvalidChallenge";
|
|
57
|
+
OtpErrorReason2["UnsupportedMethod"] = "UnsupportedMethod";
|
|
58
|
+
OtpErrorReason2["AuthHeaderMissing"] = "AuthHeaderMissing";
|
|
59
|
+
OtpErrorReason2["CodeRequestLimitExceeded"] = "CodeRequestLimitExceeded";
|
|
60
|
+
return OtpErrorReason2;
|
|
61
|
+
})(OtpErrorReason || {});
|
|
62
|
+
var AuthSessionErrorReason = /* @__PURE__ */ ((AuthSessionErrorReason2) => {
|
|
63
|
+
AuthSessionErrorReason2["SessionNotReady"] = "SessionNotReady";
|
|
64
|
+
AuthSessionErrorReason2["CallbackFailed"] = "CallbackFailed";
|
|
65
|
+
return AuthSessionErrorReason2;
|
|
66
|
+
})(AuthSessionErrorReason || {});
|
|
67
|
+
var BehaviorSettingsEffectStep = /* @__PURE__ */ ((BehaviorSettingsEffectStep2) => {
|
|
68
|
+
BehaviorSettingsEffectStep2["Write"] = "Write";
|
|
69
|
+
return BehaviorSettingsEffectStep2;
|
|
70
|
+
})(BehaviorSettingsEffectStep || {});
|
|
71
|
+
var AuthSessionEffectStep = /* @__PURE__ */ ((AuthSessionEffectStep2) => {
|
|
72
|
+
AuthSessionEffectStep2["CreateAuthSession"] = "CreateAuthSession";
|
|
73
|
+
AuthSessionEffectStep2["CloseSession"] = "CloseSession";
|
|
74
|
+
AuthSessionEffectStep2["IssueTokens"] = "IssueTokens";
|
|
75
|
+
AuthSessionEffectStep2["RefreshTokens"] = "RefreshTokens";
|
|
76
|
+
AuthSessionEffectStep2["RevokeTokens"] = "RevokeTokens";
|
|
77
|
+
AuthSessionEffectStep2["SendCode"] = "SendCode";
|
|
78
|
+
AuthSessionEffectStep2["SetTokens"] = "SetTokens";
|
|
79
|
+
AuthSessionEffectStep2["ClearTokens"] = "ClearTokens";
|
|
80
|
+
AuthSessionEffectStep2["SaveTokens"] = "SaveTokens";
|
|
81
|
+
AuthSessionEffectStep2["ClearStorage"] = "ClearStorage";
|
|
82
|
+
return AuthSessionEffectStep2;
|
|
83
|
+
})(AuthSessionEffectStep || {});
|
|
84
|
+
var TorrowSdkError = class _TorrowSdkError extends Error {
|
|
85
|
+
/** Общая причина отказа. */
|
|
86
|
+
category;
|
|
87
|
+
/** Безопасные метаданные ответа. */
|
|
88
|
+
http;
|
|
89
|
+
/** Неизменяемое предметное уточнение. */
|
|
90
|
+
detail;
|
|
91
|
+
/** Начало fetch; не доставка. */
|
|
92
|
+
request;
|
|
93
|
+
/** Имя составной операции без пользовательских значений. */
|
|
94
|
+
operation;
|
|
95
|
+
/** Стадия отказа без аргументов. */
|
|
96
|
+
failureAt;
|
|
97
|
+
/** Неизменяемые сведения об описанных изменениях. */
|
|
98
|
+
effects;
|
|
99
|
+
#sensitive;
|
|
100
|
+
constructor(init) {
|
|
101
|
+
super(`Torrow SDK: ${init.category}`);
|
|
102
|
+
this.name = "TorrowSdkError";
|
|
103
|
+
this.category = init.category;
|
|
104
|
+
this.http = init.http && Object.freeze({ ...init.http });
|
|
105
|
+
this.detail = init.detail && Object.freeze({ ...init.detail });
|
|
106
|
+
this.request = init.request;
|
|
107
|
+
this.operation = init.operation;
|
|
108
|
+
this.failureAt = init.failureAt;
|
|
109
|
+
this.effects = init.effects && Object.freeze(init.effects.map((effect) => Object.freeze({ ...effect })));
|
|
110
|
+
this.#sensitive = Object.freeze({ ...init.sensitive, cleanup: init.sensitive?.cleanup && Object.freeze({ ...init.sensitive.cleanup }) });
|
|
111
|
+
Object.freeze(this);
|
|
112
|
+
}
|
|
113
|
+
/** Безопасный снимок для обычной сериализации и диагностики. */
|
|
114
|
+
toJSON() {
|
|
115
|
+
return {
|
|
116
|
+
category: this.category,
|
|
117
|
+
http: this.http,
|
|
118
|
+
detail: this.detail,
|
|
119
|
+
request: this.request,
|
|
120
|
+
operation: this.operation,
|
|
121
|
+
failureAt: this.failureAt,
|
|
122
|
+
effects: this.effects
|
|
123
|
+
};
|
|
25
124
|
}
|
|
26
|
-
|
|
27
|
-
|
|
125
|
+
/** Явный доступ к секретной диагностике; вызывающий код отвечает за её защиту. */
|
|
126
|
+
getSensitiveDetails() {
|
|
127
|
+
return this.#sensitive;
|
|
28
128
|
}
|
|
29
|
-
|
|
30
|
-
|
|
129
|
+
/** Новый снимок для составной операции; исходная ошибка не изменяется. */
|
|
130
|
+
withContext(context) {
|
|
131
|
+
return new _TorrowSdkError({ ...this.toJSON(), ...context, sensitive: this.#sensitive });
|
|
31
132
|
}
|
|
32
|
-
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// ../../src/http/transport-errors.ts
|
|
136
|
+
var KnownApiErrorName = /* @__PURE__ */ ((KnownApiErrorName2) => {
|
|
137
|
+
KnownApiErrorName2["ResourceUnavailable"] = "REQUESTED_RESOURCE_IS_NOT_AVAILABLE";
|
|
138
|
+
KnownApiErrorName2["QuantityUnavailable"] = "REQUESTED_RESOURCE_QUANTITY_IS_NOT_AVAILABLE";
|
|
139
|
+
KnownApiErrorName2["ServiceUnavailableAtTime"] = "SERVICE_IS_NOT_AVAILABLE_AT_TIME";
|
|
140
|
+
KnownApiErrorName2["WrongVersion"] = "OBJECT_HAS_WRONG_VERSION";
|
|
141
|
+
KnownApiErrorName2["CodeRequestLimit"] = "CODE_REQUEST_LIMIT_EXCEEDED";
|
|
142
|
+
KnownApiErrorName2["AccessDenied"] = "ACCESS_DENIED";
|
|
143
|
+
KnownApiErrorName2["UserNoRights"] = "USER_HAVE_NO_RIGHTS_TO_ELEMENTS";
|
|
144
|
+
KnownApiErrorName2["AuthHeaderMissing"] = "AUTH_HEADER_MISSING";
|
|
145
|
+
KnownApiErrorName2["WrongSessionCode"] = "WRONG_SESSION_ACCESS_CODE";
|
|
146
|
+
KnownApiErrorName2["AttemptsExceeded"] = "EXCEEDED_ATTEMPTS_COUNT";
|
|
147
|
+
return KnownApiErrorName2;
|
|
148
|
+
})(KnownApiErrorName || {});
|
|
149
|
+
function createHttpError(init) {
|
|
150
|
+
const body = record(init.body);
|
|
151
|
+
const code = body?.code;
|
|
152
|
+
const apiCode = typeof code === "string" ? code : typeof code === "number" && Number.isFinite(code) ? String(code) : void 0;
|
|
153
|
+
const apiName = [body?.error, body?.name].find((value) => typeof value === "string" && Object.values(KnownApiErrorName).includes(value));
|
|
154
|
+
const nested = record(body?.torrowError) ?? record(body?.torrowErrorClient) ?? record(body?.innerError);
|
|
155
|
+
const trace = body?.traceIdentifier ?? nested?.traceIdentifier;
|
|
156
|
+
const http = {
|
|
157
|
+
status: init.status,
|
|
158
|
+
apiCode,
|
|
159
|
+
apiErrorName: typeof apiName === "string" ? apiName : void 0,
|
|
160
|
+
requestId: init.requestId,
|
|
161
|
+
traceIdentifier: init.traceIdentifier || (typeof trace === "string" ? trace : void 0)
|
|
162
|
+
};
|
|
163
|
+
const category = init.status === 401 ? "AuthenticationRequired" /* AuthenticationRequired */ : init.status === 403 ? "AccessDenied" /* AccessDenied */ : init.status === 429 ? "RateLimited" /* RateLimited */ : "HttpFailure" /* HttpFailure */;
|
|
164
|
+
return new TorrowSdkError({ category, http, request: "Started" /* Started */, sensitive: init });
|
|
33
165
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
return void 0;
|
|
37
|
-
}
|
|
38
|
-
const c = body.code;
|
|
39
|
-
if (typeof c === "string") {
|
|
40
|
-
return c;
|
|
41
|
-
}
|
|
42
|
-
return typeof c === "number" && Number.isFinite(c) ? String(c) : void 0;
|
|
166
|
+
function record(value) {
|
|
167
|
+
return value !== null && typeof value === "object" ? value : void 0;
|
|
43
168
|
}
|
|
44
|
-
function
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (typeof o.traceIdentifier === "string" && o.traceIdentifier.length > 0) {
|
|
50
|
-
return o.traceIdentifier;
|
|
51
|
-
}
|
|
52
|
-
for (const key of ["torrowError", "torrowErrorClient", "innerError"]) {
|
|
53
|
-
const nested = o[key];
|
|
54
|
-
if (nested !== null && typeof nested === "object") {
|
|
55
|
-
const t = nested.traceIdentifier;
|
|
56
|
-
if (typeof t === "string" && t.length > 0) {
|
|
57
|
-
return t;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return void 0;
|
|
169
|
+
function normalizeTransportError(cause, request, signal, url, parsing = false) {
|
|
170
|
+
if (cause instanceof TorrowSdkError) return cause;
|
|
171
|
+
const cancelled = signal?.aborted || record(cause)?.name === "AbortError";
|
|
172
|
+
const category = cancelled ? "Cancelled" /* Cancelled */ : parsing ? "InvalidResponse" /* InvalidResponse */ : request === "Started" /* Started */ && cause instanceof TypeError ? "TransportFailure" /* TransportFailure */ : "Unknown" /* Unknown */;
|
|
173
|
+
return new TorrowSdkError({ category, request, sensitive: { cause, url } });
|
|
62
174
|
}
|
|
63
|
-
var TorrowHttpError = class _TorrowHttpError extends Error {
|
|
64
|
-
status;
|
|
65
|
-
body;
|
|
66
|
-
apiErrorName;
|
|
67
|
-
/** Код ошибки API строкой: числовой `TorrowErrorClient.code` приводится к строке (`"311293"`). */
|
|
68
|
-
code;
|
|
69
|
-
requestId;
|
|
70
|
-
/**
|
|
71
|
-
* Идентификатор трассировки шлюза (`TorrowErrorClient.traceIdentifier`
|
|
72
|
-
* или заголовок `trace-identifier`) — для саппорта/логов.
|
|
73
|
-
*/
|
|
74
|
-
traceIdentifier;
|
|
75
|
-
url;
|
|
76
|
-
constructor(init) {
|
|
77
|
-
const apiName = pickApiErrorName(init.body);
|
|
78
|
-
const code = pickApiCode(init.body);
|
|
79
|
-
const traceIdentifier = (typeof init.traceIdentifier === "string" && init.traceIdentifier.length > 0 ? init.traceIdentifier : void 0) ?? pickTraceIdentifier(init.body);
|
|
80
|
-
const message = _TorrowHttpError.buildMessage(
|
|
81
|
-
init.status,
|
|
82
|
-
init.url,
|
|
83
|
-
apiName,
|
|
84
|
-
code,
|
|
85
|
-
traceIdentifier
|
|
86
|
-
);
|
|
87
|
-
super(message, { cause: init.cause });
|
|
88
|
-
this.name = "TorrowHttpError";
|
|
89
|
-
this.status = init.status;
|
|
90
|
-
this.body = init.body;
|
|
91
|
-
this.url = init.url;
|
|
92
|
-
this.requestId = init.requestId;
|
|
93
|
-
this.traceIdentifier = traceIdentifier;
|
|
94
|
-
this.apiErrorName = apiName;
|
|
95
|
-
this.code = code;
|
|
96
|
-
}
|
|
97
|
-
static buildMessage(status, url, apiErrorName, code, traceIdentifier) {
|
|
98
|
-
const parts = [`HTTP ${status}`, url];
|
|
99
|
-
if (code) {
|
|
100
|
-
parts.push(`code=${code}`);
|
|
101
|
-
}
|
|
102
|
-
if (apiErrorName) {
|
|
103
|
-
parts.push(apiErrorName);
|
|
104
|
-
}
|
|
105
|
-
if (traceIdentifier) {
|
|
106
|
-
parts.push(`trace-identifier=${traceIdentifier}`);
|
|
107
|
-
}
|
|
108
|
-
return parts.join(" \u2014 ");
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
175
|
|
|
112
176
|
// ../../src/util/json.ts
|
|
113
177
|
var ISO_DATE_REGEX = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)(Z|([+-])(\d{2}):(\d{2}))$/;
|
|
@@ -679,46 +743,59 @@ var HttpClient = class {
|
|
|
679
743
|
this.config = config;
|
|
680
744
|
}
|
|
681
745
|
config;
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
method
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
746
|
+
/** Выполняет запрос; сетевые и подготовительные отказы возвращает как TorrowSdkError. */
|
|
747
|
+
async requestJson(init, onDispatch) {
|
|
748
|
+
let request = "NotStarted" /* NotStarted */;
|
|
749
|
+
let url;
|
|
750
|
+
let parsing = false;
|
|
751
|
+
try {
|
|
752
|
+
const { fetch: fetchFn = globalThis.fetch } = this.config;
|
|
753
|
+
url = this.buildUrl(init.path, init.query);
|
|
754
|
+
const authHeaders = await buildAuthHeaders(this.config);
|
|
755
|
+
const headers = {
|
|
756
|
+
Accept: JSON_MIME,
|
|
757
|
+
...authHeaders,
|
|
758
|
+
...this.config.defaultHeaders,
|
|
759
|
+
...init.headers
|
|
760
|
+
};
|
|
761
|
+
if (this.config.userAgent) {
|
|
762
|
+
headers["User-Agent"] = this.config.userAgent;
|
|
763
|
+
}
|
|
764
|
+
const needsBody = init.body !== void 0 && init.method !== "GET" && init.method !== "DELETE";
|
|
765
|
+
if (needsBody) {
|
|
766
|
+
headers["Content-Type"] = JSON_MIME;
|
|
767
|
+
}
|
|
768
|
+
const requestInit = {
|
|
769
|
+
method: init.method,
|
|
770
|
+
headers,
|
|
771
|
+
body: needsBody ? stringifyJsonBody(init.body) : void 0,
|
|
772
|
+
signal: init.signal
|
|
773
|
+
};
|
|
774
|
+
init.signal?.throwIfAborted();
|
|
775
|
+
onDispatch?.();
|
|
776
|
+
request = "Started" /* Started */;
|
|
777
|
+
const res = await fetchFn(url, requestInit);
|
|
778
|
+
const requestId = res.headers.get("x-request-id") ?? void 0;
|
|
779
|
+
const traceIdentifier = res.headers.get("trace-identifier") ?? void 0;
|
|
780
|
+
const text = await res.text();
|
|
781
|
+
if (!res.ok) {
|
|
782
|
+
const errBody = tryParseErrorBody(text, res.headers.get("content-type"));
|
|
783
|
+
throw createHttpError({
|
|
784
|
+
status: res.status,
|
|
785
|
+
body: errBody,
|
|
786
|
+
url,
|
|
787
|
+
requestId,
|
|
788
|
+
traceIdentifier
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
if (text === "") {
|
|
792
|
+
return void 0;
|
|
793
|
+
}
|
|
794
|
+
parsing = true;
|
|
795
|
+
return parseOperationResponse(text, init.method, init.path, res.status);
|
|
796
|
+
} catch (cause) {
|
|
797
|
+
throw normalizeTransportError(cause, request, init.signal, url, parsing);
|
|
720
798
|
}
|
|
721
|
-
return parseOperationResponse(text, init.method, init.path, res.status);
|
|
722
799
|
}
|
|
723
800
|
buildUrl(path, query) {
|
|
724
801
|
const origin = this.config.baseUrl ?? DEFAULT_TORROW_API_BASE_URL;
|
|
@@ -1210,7 +1287,8 @@ var TorrowItemCoreFacade = class {
|
|
|
1210
1287
|
}
|
|
1211
1288
|
/**
|
|
1212
1289
|
* `PUT /api/v1/{domain}/{torrowId}/participation/requestaccess` — запрос доступа (не-владелец).
|
|
1213
|
-
* Body: **`profileId`**.
|
|
1290
|
+
* Body: **`profileId`**. Новый участник → **`Requested`**; некоторые существующие состояния → **`Accepted`**.
|
|
1291
|
+
* Проверять возвращённый participantState; после Accepted заново прочитать элемент и проверить полноту содержимого.
|
|
1214
1292
|
* Владелец своего item вызвать не может. Дальше: {@link grantAccess} / {@link declineAccess} / {@link revokeAccessRequest}.
|
|
1215
1293
|
*/
|
|
1216
1294
|
requestAccess(torrowId, profileId, options) {
|
|
@@ -3075,6 +3153,28 @@ var RelationsFacade = class {
|
|
|
3075
3153
|
}
|
|
3076
3154
|
};
|
|
3077
3155
|
|
|
3156
|
+
// ../../src/resources/common/behavior-settings-errors.ts
|
|
3157
|
+
function behaviorSettingsError(category) {
|
|
3158
|
+
return new TorrowSdkError({ category, detail: { domain: "BehaviorSettings" /* BehaviorSettings */ } });
|
|
3159
|
+
}
|
|
3160
|
+
function contextualizeBehaviorError(cause, operation, failureAt, state) {
|
|
3161
|
+
const error = cause instanceof TorrowSdkError ? cause : new TorrowSdkError({
|
|
3162
|
+
category: "Unknown" /* Unknown */,
|
|
3163
|
+
sensitive: { cause }
|
|
3164
|
+
});
|
|
3165
|
+
return error.withContext({
|
|
3166
|
+
operation,
|
|
3167
|
+
failureAt,
|
|
3168
|
+
detail: error.detail ?? { domain: "BehaviorSettings" /* BehaviorSettings */ },
|
|
3169
|
+
effects: state === void 0 ? void 0 : [{ area: "Remote" /* Remote */, step: "Write" /* Write */, state }]
|
|
3170
|
+
});
|
|
3171
|
+
}
|
|
3172
|
+
function isBehaviorCasRefusal(cause) {
|
|
3173
|
+
if (!(cause instanceof TorrowSdkError) || cause.request !== "Started" /* Started */ || cause.http?.status !== 500) return false;
|
|
3174
|
+
const body = cause.getSensitiveDetails().body;
|
|
3175
|
+
return body !== null && typeof body === "object" && "name" in body && body.name === "OBJECT_HAS_WRONG_VERSION" /* VersionConflict */ && "code" in body && (body.code === 208213 /* VersionConflict */ || body.code === String(208213 /* VersionConflict */));
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3078
3178
|
// ../../src/resources/common/implicit-tag-params.ts
|
|
3079
3179
|
var SERVICE_IMPLICIT_TAG_SUFFIX = {
|
|
3080
3180
|
orderNameLegacy: ":ordername",
|
|
@@ -3092,7 +3192,6 @@ var SERVICE_IMPLICIT_TAG_SUFFIX = {
|
|
|
3092
3192
|
autoStartTelegramNotificationBot: "AutoStartTelegramNotificationBot",
|
|
3093
3193
|
autoStartTelegramNotificationBotTypo: "AutoStartTelegramNotifcationBot",
|
|
3094
3194
|
autoStartConnectNotifications: "AutoStartConnectNotifications",
|
|
3095
|
-
hideMessengerConnectUiLegacy: "HideTelegramNotificationBot",
|
|
3096
3195
|
hideMessengerConnectUi: "HideMessengerNotifications",
|
|
3097
3196
|
/**
|
|
3098
3197
|
* Резерв под серверную договоренность. Формат в `tags`: `NNN:payment_redirect_timeout_sec`.
|
|
@@ -3100,6 +3199,7 @@ var SERVICE_IMPLICIT_TAG_SUFFIX = {
|
|
|
3100
3199
|
*/
|
|
3101
3200
|
paymentRedirectTimeoutSec: ":payment_redirect_timeout_sec"
|
|
3102
3201
|
};
|
|
3202
|
+
var OBSOLETE_MESSENGER_CONNECT_UI_TAG = "HideTelegramNotificationBot";
|
|
3103
3203
|
var SERVICE_IMPLICIT_TAG_REGISTRY = {
|
|
3104
3204
|
orderName: {
|
|
3105
3205
|
field: "orderName",
|
|
@@ -3145,13 +3245,8 @@ var SERVICE_IMPLICIT_TAG_REGISTRY = {
|
|
|
3145
3245
|
},
|
|
3146
3246
|
hideMessengerConnectUi: {
|
|
3147
3247
|
field: "hideMessengerConnectUi",
|
|
3148
|
-
kind: "
|
|
3149
|
-
tokens: [SERVICE_IMPLICIT_TAG_SUFFIX.hideMessengerConnectUi
|
|
3150
|
-
},
|
|
3151
|
-
hideTelegramNotificationBot: {
|
|
3152
|
-
field: "hideTelegramNotificationBot",
|
|
3153
|
-
kind: "containsFlag",
|
|
3154
|
-
tokens: [SERVICE_IMPLICIT_TAG_SUFFIX.hideMessengerConnectUiLegacy]
|
|
3248
|
+
kind: "exactFlag",
|
|
3249
|
+
tokens: [SERVICE_IMPLICIT_TAG_SUFFIX.hideMessengerConnectUi]
|
|
3155
3250
|
},
|
|
3156
3251
|
autoStartNotificationDelaySeconds: {
|
|
3157
3252
|
field: "autoStartNotificationDelaySeconds",
|
|
@@ -3184,8 +3279,8 @@ function splitBySuffix(tag, suffix, caseInsensitive = false) {
|
|
|
3184
3279
|
}
|
|
3185
3280
|
function firstPrefixBySuffix(tags, suffix, options) {
|
|
3186
3281
|
for (const tag of tags) {
|
|
3187
|
-
const
|
|
3188
|
-
if (
|
|
3282
|
+
const prefix2 = splitBySuffix(tag, suffix, options?.caseInsensitive ?? false);
|
|
3283
|
+
if (prefix2 !== void 0) return prefix2;
|
|
3189
3284
|
}
|
|
3190
3285
|
return void 0;
|
|
3191
3286
|
}
|
|
@@ -3239,8 +3334,8 @@ function parseServiceImplicitTagParams(tags) {
|
|
|
3239
3334
|
if (value !== void 0) mutable[config.field] = value;
|
|
3240
3335
|
}
|
|
3241
3336
|
}
|
|
3242
|
-
if (config.kind === "
|
|
3243
|
-
const hasAny = safeTags.some((tag) => config.tokens.some((token) => tag
|
|
3337
|
+
if (config.kind === "exactFlag") {
|
|
3338
|
+
const hasAny = safeTags.some((tag) => config.tokens.some((token) => tag === token));
|
|
3244
3339
|
if (hasAny) mutable[config.field] = true;
|
|
3245
3340
|
}
|
|
3246
3341
|
}
|
|
@@ -3315,9 +3410,6 @@ function buildServiceImplicitTags(params, options) {
|
|
|
3315
3410
|
if (params.hideMessengerConnectUi) {
|
|
3316
3411
|
tags.push(SERVICE_IMPLICIT_TAG_REGISTRY.hideMessengerConnectUi.tokens[0]);
|
|
3317
3412
|
}
|
|
3318
|
-
if (params.hideTelegramNotificationBot) {
|
|
3319
|
-
tags.push(SERVICE_IMPLICIT_TAG_REGISTRY.hideTelegramNotificationBot.tokens[0]);
|
|
3320
|
-
}
|
|
3321
3413
|
return tags;
|
|
3322
3414
|
}
|
|
3323
3415
|
function isImplicitServiceTag(tag) {
|
|
@@ -3328,8 +3420,8 @@ function isImplicitServiceTag(tag) {
|
|
|
3328
3420
|
);
|
|
3329
3421
|
if (matched) return true;
|
|
3330
3422
|
}
|
|
3331
|
-
if (config.kind === "
|
|
3332
|
-
if (config.tokens.some((token) => tag
|
|
3423
|
+
if (config.kind === "exactFlag") {
|
|
3424
|
+
if (config.tokens.some((token) => tag === token)) return true;
|
|
3333
3425
|
}
|
|
3334
3426
|
if (config.kind === "autoStart") {
|
|
3335
3427
|
const isAutoStart = config.suffixes.some((suffix) => tag.match(new RegExp(`^\\d+:${suffix}$`)) !== null);
|
|
@@ -3345,15 +3437,17 @@ function isImplicitServiceTag(tag) {
|
|
|
3345
3437
|
}
|
|
3346
3438
|
function mergeServiceImplicitTags(baseTags, params, options) {
|
|
3347
3439
|
const safeBase = baseTags ?? [];
|
|
3348
|
-
const preservedNonImplicit = safeBase.filter(
|
|
3440
|
+
const preservedNonImplicit = safeBase.filter(
|
|
3441
|
+
(tag) => tag !== OBSOLETE_MESSENGER_CONNECT_UI_TAG && !isImplicitServiceTag(tag)
|
|
3442
|
+
);
|
|
3349
3443
|
const nextImplicit = buildServiceImplicitTags(params, options);
|
|
3350
3444
|
return [...preservedNonImplicit, ...nextImplicit];
|
|
3351
3445
|
}
|
|
3352
3446
|
function parseResourceBehaviorSettings(tags) {
|
|
3353
|
-
const
|
|
3447
|
+
const marginTimeSec = toPositiveInt(firstPrefixBySuffix(tags ?? [], SERVICE_IMPLICIT_TAG_SUFFIX.marginTime));
|
|
3354
3448
|
const lockId = toPositiveInt(firstPrefixBySuffix(tags ?? [], SERVICE_IMPLICIT_TAG_SUFFIX.lockId));
|
|
3355
3449
|
const result = {};
|
|
3356
|
-
if (
|
|
3450
|
+
if (marginTimeSec !== void 0) result.marginTimeSec = marginTimeSec;
|
|
3357
3451
|
if (lockId !== void 0) result.lockId = lockId;
|
|
3358
3452
|
return result;
|
|
3359
3453
|
}
|
|
@@ -3488,39 +3582,77 @@ var ResourcesFacade = class extends TorrowItemFacade {
|
|
|
3488
3582
|
return this.getBehaviorSettingsFromResource(resource);
|
|
3489
3583
|
}
|
|
3490
3584
|
async updateBehaviorSettings(resourceId, settingsPatch, options) {
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3585
|
+
return this.writeBehaviorSettings(resourceId, settingsPatch, options, "ResourcesFacade.updateBehaviorSettings" /* ResourceUpdate */);
|
|
3586
|
+
}
|
|
3587
|
+
/** Контекст локален вызову; алгоритм и число запросов двух публичных операций совпадают. */
|
|
3588
|
+
async writeBehaviorSettings(resourceId, settingsPatch, options, operation) {
|
|
3589
|
+
let failureAt = "ReadCurrent" /* ReadCurrent */;
|
|
3590
|
+
let effect = "NotApplied" /* NotApplied */;
|
|
3591
|
+
try {
|
|
3592
|
+
const resource = await this.getById(resourceId, {
|
|
3593
|
+
includeDeleted: options?.includeDeleted,
|
|
3594
|
+
isNeedItem: options?.isNeedItem,
|
|
3595
|
+
isNeedParentResource: options?.isNeedParentResource,
|
|
3596
|
+
includeChildTypes: options?.includeChildTypes,
|
|
3597
|
+
securityToken: options?.securityToken,
|
|
3598
|
+
signal: options?.signal
|
|
3599
|
+
});
|
|
3600
|
+
failureAt = "PrepareWrite" /* PrepareWrite */;
|
|
3601
|
+
const mergeParameters = options?.mergeParameters ?? true;
|
|
3602
|
+
const nextSettings = mergeParameters ? {
|
|
3603
|
+
...this.getBehaviorSettingsFromResource(resource),
|
|
3604
|
+
...settingsPatch
|
|
3605
|
+
} : { ...settingsPatch };
|
|
3606
|
+
const nextTags = mergeResourceBehaviorTags(resource.tags, nextSettings);
|
|
3607
|
+
failureAt = "WriteSettings" /* WriteSettings */;
|
|
3608
|
+
let dispatched = false;
|
|
3609
|
+
let acknowledgement;
|
|
3610
|
+
try {
|
|
3611
|
+
acknowledgement = await this.http.requestJson({
|
|
3612
|
+
method: "PUT",
|
|
3613
|
+
path: `${BASE7}/${encodePathSegment(resourceId)}/tags`,
|
|
3614
|
+
body: nextTags,
|
|
3615
|
+
query: {
|
|
3616
|
+
versionToCheck: options?.checkVersion ? resource.meta?.version ?? void 0 : void 0,
|
|
3617
|
+
isNeedAnnouncement: options?.isNeedAnnouncement
|
|
3618
|
+
},
|
|
3619
|
+
signal: options?.signal
|
|
3620
|
+
}, () => {
|
|
3621
|
+
dispatched = true;
|
|
3622
|
+
});
|
|
3623
|
+
} catch (cause) {
|
|
3624
|
+
effect = dispatched ? "Unknown" /* Unknown */ : "NotApplied" /* NotApplied */;
|
|
3625
|
+
const error = contextualizeBehaviorError(cause, operation, failureAt, effect).withContext({
|
|
3626
|
+
request: dispatched ? "Started" /* Started */ : "NotStarted" /* NotStarted */
|
|
3627
|
+
});
|
|
3628
|
+
if (dispatched && isBehaviorCasRefusal(error)) {
|
|
3629
|
+
effect = "NotApplied" /* NotApplied */;
|
|
3630
|
+
throw error.withContext({
|
|
3631
|
+
category: "VersionConflict" /* VersionConflict */,
|
|
3632
|
+
detail: { domain: "BehaviorSettings" /* BehaviorSettings */ }
|
|
3633
|
+
});
|
|
3634
|
+
}
|
|
3635
|
+
throw error;
|
|
3636
|
+
}
|
|
3637
|
+
effect = acknowledgement?.id === resourceId ? "Applied" /* Applied */ : "Unknown" /* Unknown */;
|
|
3638
|
+
failureAt = "ReadBack" /* ReadBack */;
|
|
3639
|
+
return await this.getById(resourceId, {
|
|
3640
|
+
includeDeleted: options?.includeDeleted,
|
|
3641
|
+
isNeedItem: options?.isNeedItem,
|
|
3642
|
+
isNeedParentResource: options?.isNeedParentResource,
|
|
3643
|
+
includeChildTypes: options?.includeChildTypes,
|
|
3644
|
+
securityToken: options?.securityToken,
|
|
3645
|
+
signal: options?.signal
|
|
3646
|
+
});
|
|
3647
|
+
} catch (cause) {
|
|
3648
|
+
throw contextualizeBehaviorError(cause, operation, failureAt, effect);
|
|
3649
|
+
}
|
|
3518
3650
|
}
|
|
3519
3651
|
setBehaviorSettings(resourceId, settings, options) {
|
|
3520
|
-
return this.
|
|
3652
|
+
return this.writeBehaviorSettings(resourceId, settings, {
|
|
3521
3653
|
...options,
|
|
3522
3654
|
mergeParameters: false
|
|
3523
|
-
});
|
|
3655
|
+
}, "ResourcesFacade.setBehaviorSettings" /* ResourceSet */);
|
|
3524
3656
|
}
|
|
3525
3657
|
/** `GET /api/v1/resources/personallist` */
|
|
3526
3658
|
listPersonal(options) {
|
|
@@ -3591,6 +3723,173 @@ var ResourcesFacade = class extends TorrowItemFacade {
|
|
|
3591
3723
|
}
|
|
3592
3724
|
};
|
|
3593
3725
|
|
|
3726
|
+
// ../../src/resources/services/behavior-settings.ts
|
|
3727
|
+
var ServiceBehaviorSettingsIssueReason = /* @__PURE__ */ ((ServiceBehaviorSettingsIssueReason2) => {
|
|
3728
|
+
ServiceBehaviorSettingsIssueReason2["InvalidValue"] = "InvalidValue";
|
|
3729
|
+
ServiceBehaviorSettingsIssueReason2["AmbiguousValue"] = "AmbiguousValue";
|
|
3730
|
+
return ServiceBehaviorSettingsIssueReason2;
|
|
3731
|
+
})(ServiceBehaviorSettingsIssueReason || {});
|
|
3732
|
+
var configs = Object.values(SERVICE_IMPLICIT_TAG_REGISTRY);
|
|
3733
|
+
function isBehaviorVersion(value) {
|
|
3734
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 2147483647;
|
|
3735
|
+
}
|
|
3736
|
+
function record2(value) {
|
|
3737
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3738
|
+
}
|
|
3739
|
+
function validateBehaviorResponse(value, serviceId) {
|
|
3740
|
+
if (!record2(value) || value.id !== serviceId) throw behaviorSettingsError("InvalidResponse" /* InvalidResponse */);
|
|
3741
|
+
const personal = value.personalInfo;
|
|
3742
|
+
if (record2(personal) && (personal.itemContentType === "NoRights" /* NoRights */ || personal.itemContentType === "SubscriptionIsNotValid" /* SubscriptionIsNotValid */)) {
|
|
3743
|
+
throw behaviorSettingsError("AccessDenied" /* AccessDenied */);
|
|
3744
|
+
}
|
|
3745
|
+
if (!record2(personal) || personal.itemContentType !== "Full" /* Full */ || value.tags == null || !record2(value.meta) || !isBehaviorVersion(value.meta.version)) {
|
|
3746
|
+
throw behaviorSettingsError("InsufficientData" /* InsufficientData */);
|
|
3747
|
+
}
|
|
3748
|
+
if (!Array.isArray(value.tags) || !value.tags.every((tag) => typeof tag === "string")) {
|
|
3749
|
+
throw behaviorSettingsError("InvalidResponse" /* InvalidResponse */);
|
|
3750
|
+
}
|
|
3751
|
+
const operations = personal.operationList;
|
|
3752
|
+
if (operations != null && (!Array.isArray(operations) || !operations.every((operation) => typeof operation === "string" || typeof operation === "number" && Number.isSafeInteger(operation)))) {
|
|
3753
|
+
throw behaviorSettingsError("InvalidResponse" /* InvalidResponse */);
|
|
3754
|
+
}
|
|
3755
|
+
return { tags: [...new Set(value.tags)], version: value.meta.version, operations };
|
|
3756
|
+
}
|
|
3757
|
+
function assertBehaviorPermission(operations) {
|
|
3758
|
+
if (!Array.isArray(operations)) throw behaviorSettingsError("InsufficientData" /* InsufficientData */);
|
|
3759
|
+
if (!operations.includes("SetItemTags" /* SetItemTags */)) throw behaviorSettingsError("AccessDenied" /* AccessDenied */);
|
|
3760
|
+
}
|
|
3761
|
+
function suffixPrefix(tag, suffix, ignoreCase = false) {
|
|
3762
|
+
const matches = ignoreCase ? tag.toLowerCase().endsWith(suffix.toLowerCase()) : tag.endsWith(suffix);
|
|
3763
|
+
return matches ? tag.slice(0, -suffix.length) : void 0;
|
|
3764
|
+
}
|
|
3765
|
+
function positive(raw) {
|
|
3766
|
+
const value = Number(raw);
|
|
3767
|
+
return /^\d+$/.test(raw) && Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
3768
|
+
}
|
|
3769
|
+
function prefix(tag, config) {
|
|
3770
|
+
if (config.kind === "string" /* String */ || config.kind === "positiveInt" /* PositiveInt */) {
|
|
3771
|
+
for (const suffix of config.suffixes) {
|
|
3772
|
+
const value = suffixPrefix(tag, suffix, config.caseInsensitive);
|
|
3773
|
+
if (value !== void 0) return value;
|
|
3774
|
+
}
|
|
3775
|
+
} else if (config.kind === "exactFlag" /* ExactFlag */) {
|
|
3776
|
+
if (config.tokens.includes(tag) || tag === "HideTelegramNotificationBot" /* LegacyHide */) return "true";
|
|
3777
|
+
} else if (config.kind === "autoStart" /* AutoStart */) {
|
|
3778
|
+
for (const suffix of config.suffixes) {
|
|
3779
|
+
const value = suffixPrefix(tag, `:${suffix}`);
|
|
3780
|
+
if (value !== void 0) return value;
|
|
3781
|
+
}
|
|
3782
|
+
} else if (config.kind === "agreement" /* Agreement */) {
|
|
3783
|
+
for (const suffix of [config.textSuffix, config.linkSuffix]) {
|
|
3784
|
+
const position = tag.lastIndexOf(suffix);
|
|
3785
|
+
if (position >= 0 && /^\d+$/.test(tag.slice(position + suffix.length))) return tag;
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
return void 0;
|
|
3789
|
+
}
|
|
3790
|
+
function readBehaviorSettings(tags) {
|
|
3791
|
+
const settings = { hideMessengerConnectUi: false };
|
|
3792
|
+
const issues = [];
|
|
3793
|
+
const target = settings;
|
|
3794
|
+
for (const config of configs) {
|
|
3795
|
+
const matching = [...new Set(tags)].filter((tag) => prefix(tag, config) !== void 0);
|
|
3796
|
+
if (!matching.length) continue;
|
|
3797
|
+
let invalid = false;
|
|
3798
|
+
let ambiguous = false;
|
|
3799
|
+
let result;
|
|
3800
|
+
if (config.kind === "agreement" /* Agreement */) {
|
|
3801
|
+
const entries = /* @__PURE__ */ new Map();
|
|
3802
|
+
for (const tag of matching) {
|
|
3803
|
+
for (const [attribute, suffix] of [["text", config.textSuffix], ["link", config.linkSuffix]]) {
|
|
3804
|
+
const position = tag.lastIndexOf(suffix);
|
|
3805
|
+
if (position < 0 || !/^\d+$/.test(tag.slice(position + suffix.length))) continue;
|
|
3806
|
+
const index = positive(tag.slice(position + suffix.length));
|
|
3807
|
+
let value = tag.slice(0, position).trim();
|
|
3808
|
+
if (attribute === "link") value = value.replace(/^(https?)\/\//, "$1://");
|
|
3809
|
+
if (!index || !value) {
|
|
3810
|
+
invalid = true;
|
|
3811
|
+
continue;
|
|
3812
|
+
}
|
|
3813
|
+
const entry = entries.get(index) ?? { index };
|
|
3814
|
+
if (entry[attribute] !== void 0 && entry[attribute] !== value) ambiguous = true;
|
|
3815
|
+
entry[attribute] = value;
|
|
3816
|
+
entries.set(index, entry);
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
result = [...entries.values()].sort((a, b) => a.index - b.index);
|
|
3820
|
+
} else {
|
|
3821
|
+
const values = /* @__PURE__ */ new Set();
|
|
3822
|
+
for (const tag of matching) {
|
|
3823
|
+
const raw = prefix(tag, config);
|
|
3824
|
+
const value = config.kind === "exactFlag" /* ExactFlag */ ? true : config.kind === "string" /* String */ ? raw.trim() || void 0 : positive(raw);
|
|
3825
|
+
if (value === void 0) invalid = true;
|
|
3826
|
+
else values.add(value);
|
|
3827
|
+
}
|
|
3828
|
+
ambiguous = values.size > 1;
|
|
3829
|
+
result = [...values][0];
|
|
3830
|
+
}
|
|
3831
|
+
if (invalid || ambiguous) {
|
|
3832
|
+
issues.push({ field: config.field, reason: invalid ? "InvalidValue" /* InvalidValue */ : "AmbiguousValue" /* AmbiguousValue */ });
|
|
3833
|
+
} else target[config.field] = result;
|
|
3834
|
+
}
|
|
3835
|
+
return { settings, issues };
|
|
3836
|
+
}
|
|
3837
|
+
function normalizeBehaviorInput(input) {
|
|
3838
|
+
function fail() {
|
|
3839
|
+
throw behaviorSettingsError("InvalidInput" /* InvalidInput */);
|
|
3840
|
+
}
|
|
3841
|
+
if (!record2(input) || Object.keys(input).some((key) => !Object.hasOwn(SERVICE_IMPLICIT_TAG_REGISTRY, key))) fail();
|
|
3842
|
+
const output = {};
|
|
3843
|
+
for (const config of configs) {
|
|
3844
|
+
if (!Object.hasOwn(input, config.field)) continue;
|
|
3845
|
+
const value = input[config.field];
|
|
3846
|
+
if (config.kind === "string" /* String */) {
|
|
3847
|
+
if (typeof value !== "string" || !value.trim()) fail();
|
|
3848
|
+
output[config.field] = value.trim();
|
|
3849
|
+
} else if (config.kind === "positiveInt" /* PositiveInt */ || config.kind === "autoStart" /* AutoStart */) {
|
|
3850
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value <= 0) fail();
|
|
3851
|
+
output[config.field] = value;
|
|
3852
|
+
} else if (config.kind === "exactFlag" /* ExactFlag */) {
|
|
3853
|
+
if (typeof value !== "boolean") fail();
|
|
3854
|
+
output[config.field] = value;
|
|
3855
|
+
} else {
|
|
3856
|
+
if (!Array.isArray(value)) fail();
|
|
3857
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3858
|
+
output[config.field] = value.map((entry) => {
|
|
3859
|
+
if (!record2(entry) || Object.keys(entry).some((key) => !["index", "text", "link"].includes(key)) || typeof entry.index !== "number" || !Number.isSafeInteger(entry.index) || entry.index <= 0 || seen.has(entry.index)) fail();
|
|
3860
|
+
seen.add(entry.index);
|
|
3861
|
+
const normalized = { index: entry.index };
|
|
3862
|
+
for (const attribute of ["text", "link"]) {
|
|
3863
|
+
if (Object.hasOwn(entry, attribute)) {
|
|
3864
|
+
if (typeof entry[attribute] !== "string" || !entry[attribute].trim()) fail();
|
|
3865
|
+
normalized[attribute] = entry[attribute].trim();
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
if (!normalized.text && !normalized.link) fail();
|
|
3869
|
+
return normalized;
|
|
3870
|
+
}).sort((a, b) => a.index - b.index);
|
|
3871
|
+
}
|
|
3872
|
+
}
|
|
3873
|
+
return output;
|
|
3874
|
+
}
|
|
3875
|
+
function mergeBehaviorSettings(tags, settings, mergeParameters) {
|
|
3876
|
+
const selected = configs.filter((config) => !mergeParameters || Object.hasOwn(settings, config.field));
|
|
3877
|
+
const output = tags.filter((tag) => !selected.some((config) => prefix(tag, config) !== void 0));
|
|
3878
|
+
for (const config of selected) {
|
|
3879
|
+
const value = settings[config.field];
|
|
3880
|
+
if (value === void 0) continue;
|
|
3881
|
+
if (config.kind === "string" /* String */ || config.kind === "positiveInt" /* PositiveInt */) output.push(`${value}${config.suffixes[0]}`);
|
|
3882
|
+
else if (config.kind === "exactFlag" /* ExactFlag */) {
|
|
3883
|
+
if (value) output.push(config.tokens[0]);
|
|
3884
|
+
} else if (config.kind === "autoStart" /* AutoStart */) output.push(`${value}:${config.suffixes[0]}`);
|
|
3885
|
+
else if (config.kind === "agreement" /* Agreement */) for (const entry of settings.agreementItems ?? []) {
|
|
3886
|
+
if (entry.text) output.push(`${entry.text}${config.textSuffix}${entry.index}`);
|
|
3887
|
+
if (entry.link) output.push(`${entry.link.replace(/^(https?):\/\//, "$1//")}${config.linkSuffix}${entry.index}`);
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
return [...new Set(output)];
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3594
3893
|
// ../../src/resources/services/service-requests.ts
|
|
3595
3894
|
var BASE8 = "/api/v1/services";
|
|
3596
3895
|
function toSdkServiceItem(service) {
|
|
@@ -3707,62 +4006,118 @@ var ServicesFacade = class extends TorrowItemFacade {
|
|
|
3707
4006
|
getById(torrowId, options) {
|
|
3708
4007
|
return getService(this.http, torrowId, options);
|
|
3709
4008
|
}
|
|
3710
|
-
/**
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
4009
|
+
/** Читает сырой OpenAPI DTO до проекции, чтобы malformed tags не маскировались парсером. */
|
|
4010
|
+
async readBehaviorState(serviceId, options) {
|
|
4011
|
+
if (typeof serviceId !== "string" || !serviceId.trim()) {
|
|
4012
|
+
throw behaviorSettingsError("InvalidInput" /* InvalidInput */);
|
|
4013
|
+
}
|
|
4014
|
+
const value = await this.http.requestJson({
|
|
4015
|
+
method: "GET",
|
|
4016
|
+
path: `${BASE9}/${encodePathSegment(serviceId)}`,
|
|
4017
|
+
query: {
|
|
4018
|
+
profileId: options?.profileId,
|
|
4019
|
+
versionToCheck: options?.versionToCheck,
|
|
4020
|
+
includeDeleted: options?.includeDeleted,
|
|
4021
|
+
includeChildTypes: options?.includeChildTypes,
|
|
4022
|
+
securityToken: options?.securityToken
|
|
4023
|
+
},
|
|
4024
|
+
signal: options?.signal
|
|
4025
|
+
});
|
|
4026
|
+
return validateBehaviorResponse(value, serviceId);
|
|
3714
4027
|
}
|
|
3715
|
-
/**
|
|
4028
|
+
/** Один GET: версия всей услуги, предметные настройки и диагностика неполного снимка. */
|
|
3716
4029
|
async getBehaviorSettings(serviceId, options) {
|
|
3717
|
-
|
|
3718
|
-
|
|
4030
|
+
try {
|
|
4031
|
+
const state = await this.readBehaviorState(serviceId, options);
|
|
4032
|
+
return { serviceId, version: state.version, ...readBehaviorSettings(state.tags) };
|
|
4033
|
+
} catch (cause) {
|
|
4034
|
+
throw contextualizeBehaviorError(cause, "ServicesFacade.getBehaviorSettings" /* ServiceGet */, "ReadCurrent" /* ReadCurrent */);
|
|
4035
|
+
}
|
|
3719
4036
|
}
|
|
3720
4037
|
/**
|
|
3721
|
-
*
|
|
3722
|
-
*
|
|
3723
|
-
*
|
|
3724
|
-
* Персист только через **`PUT .../services/{id}/tags`**: поле `tags` в теле `PUT /services/{id}`
|
|
3725
|
-
* сервер игнорирует (`tags` в `TorrowItemKeysExcludedFromItemWriteBody`).
|
|
4038
|
+
* GET → локальный patch → PUT /tags с необязательной проверкой версии; автоматического readback нет.
|
|
4039
|
+
* mergeParameters=true сохраняет пропущенные поля, false заменяет все настройки.
|
|
4040
|
+
* Порядок и одинаковые дубли сервер не сохраняет; no-op определяется множеством строк.
|
|
3726
4041
|
*/
|
|
3727
|
-
async updateBehaviorSettings(serviceId, settingsPatch, options) {
|
|
3728
|
-
|
|
3729
|
-
profileId: options?.profileId,
|
|
3730
|
-
includeDeleted: options?.includeDeleted,
|
|
3731
|
-
includeChildTypes: options?.includeChildTypes,
|
|
3732
|
-
securityToken: options?.securityToken,
|
|
3733
|
-
signal: options?.signal
|
|
3734
|
-
});
|
|
3735
|
-
const mergeParameters = options?.mergeParameters ?? true;
|
|
3736
|
-
const nextSettings = mergeParameters ? {
|
|
3737
|
-
...this.getBehaviorSettingsFromService(service),
|
|
3738
|
-
...settingsPatch
|
|
3739
|
-
} : { ...settingsPatch };
|
|
3740
|
-
const nextTags = mergeServiceImplicitTags(service.tags, nextSettings);
|
|
3741
|
-
await this.updateTags(serviceId, nextTags, {
|
|
3742
|
-
versionToCheck: options?.checkVersion ? service.meta?.version ?? void 0 : void 0,
|
|
3743
|
-
isNeedAnnouncement: options?.isNeedAnnouncement,
|
|
3744
|
-
signal: options?.signal
|
|
3745
|
-
});
|
|
3746
|
-
return this.getById(serviceId, {
|
|
3747
|
-
profileId: options?.profileId,
|
|
3748
|
-
includeDeleted: options?.includeDeleted,
|
|
3749
|
-
includeChildTypes: options?.includeChildTypes,
|
|
3750
|
-
securityToken: options?.securityToken,
|
|
3751
|
-
signal: options?.signal
|
|
3752
|
-
});
|
|
4042
|
+
async updateBehaviorSettings(serviceId, settingsPatch, options = {}) {
|
|
4043
|
+
return this.writeBehaviorSettings(serviceId, settingsPatch, options, "ServicesFacade.updateBehaviorSettings" /* ServiceUpdate */);
|
|
3753
4044
|
}
|
|
3754
|
-
/**
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
4045
|
+
/** Общий алгоритм двух публичных операций; контекст и эффект принадлежат только текущему вызову. */
|
|
4046
|
+
async writeBehaviorSettings(serviceId, settingsPatch, options, operation) {
|
|
4047
|
+
let failureAt = "ValidateInput" /* ValidateInput */;
|
|
4048
|
+
let effect = "NotApplied" /* NotApplied */;
|
|
4049
|
+
try {
|
|
4050
|
+
if (!options || options.versionToCheck !== void 0 && !isBehaviorVersion(options.versionToCheck) || Object.hasOwn(options, "checkVersion") && options.checkVersion !== true || Object.hasOwn(options, "mergeParameters") && typeof options.mergeParameters !== "boolean" || options.isNeedAnnouncement != null && typeof options.isNeedAnnouncement !== "boolean" || Object.keys(options).some((key) => !["versionToCheck", "checkVersion", "mergeParameters", "profileId", "securityToken", "isNeedAnnouncement", "signal"].includes(key))) {
|
|
4051
|
+
throw behaviorSettingsError("InvalidInput" /* InvalidInput */);
|
|
4052
|
+
}
|
|
4053
|
+
const normalized = normalizeBehaviorInput(settingsPatch);
|
|
4054
|
+
options = { ...options };
|
|
4055
|
+
failureAt = "ReadCurrent" /* ReadCurrent */;
|
|
4056
|
+
const state = await this.readBehaviorState(serviceId, {
|
|
4057
|
+
profileId: options.profileId,
|
|
4058
|
+
securityToken: options.securityToken,
|
|
4059
|
+
signal: options.signal
|
|
4060
|
+
});
|
|
4061
|
+
failureAt = "PrepareWrite" /* PrepareWrite */;
|
|
4062
|
+
assertBehaviorPermission(state.operations);
|
|
4063
|
+
if (options.versionToCheck !== void 0 && state.version !== options.versionToCheck) {
|
|
4064
|
+
throw behaviorSettingsError("VersionConflict" /* VersionConflict */);
|
|
4065
|
+
}
|
|
4066
|
+
const tags = mergeBehaviorSettings(state.tags, normalized, options.mergeParameters ?? true);
|
|
4067
|
+
const projected = readBehaviorSettings(tags);
|
|
4068
|
+
const unchanged = tags.length === state.tags.length && tags.every((tag) => state.tags.includes(tag));
|
|
4069
|
+
if (unchanged) return { serviceId, version: state.version, ...projected, changed: false };
|
|
4070
|
+
failureAt = "WriteSettings" /* WriteSettings */;
|
|
4071
|
+
let result;
|
|
4072
|
+
let dispatched = false;
|
|
4073
|
+
try {
|
|
4074
|
+
result = await this.http.requestJson({
|
|
4075
|
+
method: "PUT",
|
|
4076
|
+
path: `${BASE9}/${encodePathSegment(serviceId)}/tags`,
|
|
4077
|
+
query: { versionToCheck: options.versionToCheck ?? (options.checkVersion ? state.version : void 0), isNeedAnnouncement: options.isNeedAnnouncement ?? false },
|
|
4078
|
+
body: tags,
|
|
4079
|
+
signal: options.signal
|
|
4080
|
+
}, () => {
|
|
4081
|
+
dispatched = true;
|
|
4082
|
+
});
|
|
4083
|
+
} catch (cause) {
|
|
4084
|
+
effect = dispatched ? "Unknown" /* Unknown */ : "NotApplied" /* NotApplied */;
|
|
4085
|
+
const error = contextualizeBehaviorError(cause, operation, failureAt, effect).withContext({
|
|
4086
|
+
request: dispatched ? "Started" /* Started */ : "NotStarted" /* NotStarted */
|
|
4087
|
+
});
|
|
4088
|
+
if (dispatched && isBehaviorCasRefusal(error)) {
|
|
4089
|
+
effect = "NotApplied" /* NotApplied */;
|
|
4090
|
+
throw error.withContext({
|
|
4091
|
+
category: "VersionConflict" /* VersionConflict */,
|
|
4092
|
+
detail: { domain: "BehaviorSettings" /* BehaviorSettings */ }
|
|
4093
|
+
});
|
|
4094
|
+
}
|
|
4095
|
+
throw error;
|
|
4096
|
+
}
|
|
4097
|
+
effect = "Unknown" /* Unknown */;
|
|
4098
|
+
failureAt = "ValidateWriteResponse" /* ValidateWriteResponse */;
|
|
4099
|
+
if (!result || result.id !== serviceId || !isBehaviorVersion(result.meta?.version)) {
|
|
4100
|
+
throw new TorrowSdkError({
|
|
4101
|
+
category: "InvalidResponse" /* InvalidResponse */,
|
|
4102
|
+
request: "Started" /* Started */,
|
|
4103
|
+
detail: { domain: "BehaviorSettings" /* BehaviorSettings */ },
|
|
4104
|
+
sensitive: { body: result }
|
|
4105
|
+
});
|
|
4106
|
+
}
|
|
4107
|
+
return { serviceId, version: result.meta.version, ...projected, changed: true };
|
|
4108
|
+
} catch (cause) {
|
|
4109
|
+
throw contextualizeBehaviorError(cause, operation, failureAt, effect);
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
/** Полная замена: пропущенные поля очищаются, посторонние данные сохраняются. */
|
|
4113
|
+
setBehaviorSettings(serviceId, settings, options = {}) {
|
|
4114
|
+
return this.writeBehaviorSettings(serviceId, settings, { ...options, mergeParameters: false }, "ServicesFacade.setBehaviorSettings" /* ServiceSet */);
|
|
3760
4115
|
}
|
|
3761
4116
|
/**
|
|
3762
4117
|
* Запись на услугу — `POST /api/v1/services/{serviceId}/order`.
|
|
3763
4118
|
* Минимум в теле: непустой смысл в `checkInProperties` (слот, участник, контакты — по правилам услуги и OpenAPI).
|
|
3764
4119
|
* Успех: `OrderResp` (часто интересен `order` как кейс и `consumerSecurityToken` для гостевого доступа).
|
|
3765
|
-
* Типичные отказы: нет слота, услуга недоступна по времени, подписка/баланс — разбирай тело `
|
|
4120
|
+
* Типичные отказы: нет слота, услуга недоступна по времени, подписка/баланс — разбирай тело `TorrowSdkError` (`name` / `code`); эталон UX — `torrow-apps` `CreateOrderTask`.
|
|
3766
4121
|
*/
|
|
3767
4122
|
createOrder(serviceId, body, options) {
|
|
3768
4123
|
return createOrder(this.http, serviceId, body, options);
|
|
@@ -5589,21 +5944,30 @@ var ShortUrlFacade = class {
|
|
|
5589
5944
|
* Возвращает заголовок `Location` без следования редиректу.
|
|
5590
5945
|
*/
|
|
5591
5946
|
async getRedirectLocation(shortUrl, options) {
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5947
|
+
let request = "NotStarted" /* NotStarted */;
|
|
5948
|
+
let url;
|
|
5949
|
+
try {
|
|
5950
|
+
const fetchFn = this.config.fetch ?? globalThis.fetch;
|
|
5951
|
+
const origin = this.config.baseUrl ?? DEFAULT_TORROW_API_BASE_URL;
|
|
5952
|
+
url = joinBaseUrlAndPath(origin, `${BASE27}/${encodePathSegment(shortUrl)}`);
|
|
5953
|
+
const authHeaders = await buildAuthHeaders(this.config);
|
|
5954
|
+
const init = {
|
|
5955
|
+
method: "GET",
|
|
5956
|
+
redirect: "manual",
|
|
5957
|
+
headers: {
|
|
5958
|
+
Accept: "*/*",
|
|
5959
|
+
...authHeaders,
|
|
5960
|
+
...this.config.defaultHeaders
|
|
5961
|
+
},
|
|
5962
|
+
signal: options?.signal
|
|
5963
|
+
};
|
|
5964
|
+
options?.signal?.throwIfAborted();
|
|
5965
|
+
request = "Started" /* Started */;
|
|
5966
|
+
const res = await fetchFn(url, init);
|
|
5967
|
+
return res.headers.get("Location");
|
|
5968
|
+
} catch (cause) {
|
|
5969
|
+
throw normalizeTransportError(cause, request, options?.signal, url);
|
|
5970
|
+
}
|
|
5607
5971
|
}
|
|
5608
5972
|
};
|
|
5609
5973
|
|
|
@@ -6755,8 +7119,8 @@ var ServiceOrderConflictErrorNameValues = {
|
|
|
6755
7119
|
ServiceUnavailableAtTime: "SERVICE_IS_NOT_AVAILABLE_AT_TIME"
|
|
6756
7120
|
};
|
|
6757
7121
|
function getServiceOrderConflictReason(error) {
|
|
6758
|
-
if (!(error instanceof
|
|
6759
|
-
switch (error.apiErrorName) {
|
|
7122
|
+
if (!(error instanceof TorrowSdkError)) return void 0;
|
|
7123
|
+
switch (error.http?.apiErrorName) {
|
|
6760
7124
|
case ServiceOrderConflictErrorNameValues.ResourceUnavailable:
|
|
6761
7125
|
return "resource-unavailable";
|
|
6762
7126
|
case ServiceOrderConflictErrorNameValues.QuantityUnavailable:
|
|
@@ -6960,6 +7324,9 @@ export {
|
|
|
6960
7324
|
AuthFacade,
|
|
6961
7325
|
AuthScopeValueList,
|
|
6962
7326
|
AuthScopeValues,
|
|
7327
|
+
AuthSessionEffectStep,
|
|
7328
|
+
AuthSessionErrorReason,
|
|
7329
|
+
BehaviorSettingsEffectStep,
|
|
6963
7330
|
BlockReasonsFacade,
|
|
6964
7331
|
CalendarsFacade,
|
|
6965
7332
|
CaseStateValues,
|
|
@@ -6995,6 +7362,7 @@ export {
|
|
|
6995
7362
|
LoyaltySystemsFacade,
|
|
6996
7363
|
NoteTypeValues,
|
|
6997
7364
|
NotesFacade,
|
|
7365
|
+
OtpErrorReason,
|
|
6998
7366
|
ParticipationFieldRequirementTypeValues,
|
|
6999
7367
|
ParticipationFieldTypeValues,
|
|
7000
7368
|
PinnedViewsFacade,
|
|
@@ -7008,6 +7376,7 @@ export {
|
|
|
7008
7376
|
SearchFacade,
|
|
7009
7377
|
SendCodeAccountTypeValueList,
|
|
7010
7378
|
SendCodeAccountTypeValues,
|
|
7379
|
+
ServiceBehaviorSettingsIssueReason,
|
|
7011
7380
|
ServiceBuilder,
|
|
7012
7381
|
ServiceCheckInWizardStepParameterName,
|
|
7013
7382
|
ServiceCheckInWizardStepType,
|
|
@@ -7027,11 +7396,16 @@ export {
|
|
|
7027
7396
|
TokenStatusValueList,
|
|
7028
7397
|
TokenStatusValues,
|
|
7029
7398
|
TorrowClient,
|
|
7030
|
-
TorrowHttpError,
|
|
7031
7399
|
TorrowIdError,
|
|
7032
7400
|
TorrowItemTypeValueList,
|
|
7033
7401
|
TorrowItemTypeValues,
|
|
7034
7402
|
TorrowObjectTypeCode,
|
|
7403
|
+
TorrowSdkEffectArea,
|
|
7404
|
+
TorrowSdkEffectState,
|
|
7405
|
+
TorrowSdkError,
|
|
7406
|
+
TorrowSdkErrorCategory,
|
|
7407
|
+
TorrowSdkErrorDomain,
|
|
7408
|
+
TorrowSdkRequestState,
|
|
7035
7409
|
UsageConditionTypeValues,
|
|
7036
7410
|
UsageTypeValues,
|
|
7037
7411
|
UserAccessLevelValues,
|