@microsoft/omnichannel-chat-sdk 2.0.1 → 2.0.2-main.355218f
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 +25 -0
- package/lib/OmnichannelChatSDK.d.ts +46 -0
- package/lib/OmnichannelChatSDK.js +319 -66
- package/lib/OmnichannelChatSDK.js.map +1 -1
- package/lib/api/createVoiceVideoCalling.d.ts +1 -0
- package/lib/api/createVoiceVideoCalling.js +29 -17
- package/lib/api/createVoiceVideoCalling.js.map +1 -1
- package/lib/config/settings.d.ts +1 -1
- package/lib/config/settings.js +1 -1
- package/lib/core/ChatSDKConfig.d.ts +6 -0
- package/lib/core/messaging/ACSClient.d.ts +3 -1
- package/lib/core/messaging/ACSClient.js +72 -30
- package/lib/core/messaging/ACSClient.js.map +1 -1
- package/lib/core/messaging/ACSClientConfig.d.ts +3 -0
- package/lib/telemetry/TelemetryEvent.d.ts +4 -0
- package/lib/telemetry/TelemetryEvent.js +4 -0
- package/lib/telemetry/TelemetryEvent.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/chatTokenRenewal.d.ts +44 -0
- package/lib/utils/chatTokenRenewal.js +223 -0
- package/lib/utils/chatTokenRenewal.js.map +1 -0
- package/lib/utils/chatTokenUnauthorizedRecovery.d.ts +64 -0
- package/lib/utils/chatTokenUnauthorizedRecovery.js +293 -0
- package/lib/utils/chatTokenUnauthorizedRecovery.js.map +1 -0
- package/lib/utils/loggerUtils.d.ts +2 -0
- package/lib/utils/loggerUtils.js +48 -8
- package/lib/utils/loggerUtils.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fail-safe ACS chat token refresher backed by the Omnichannel renew chat token API.
|
|
3
|
+
*
|
|
4
|
+
* The refresher NEVER throws and NEVER returns an unusable token. On any failure (API not deployed, feature disabled,
|
|
5
|
+
* chat ended, network error, timeout, invalid response, ...) it returns the last known good token, which is exactly
|
|
6
|
+
* what the SDK did before renewal existed, so behavior falls back to the existing flow.
|
|
7
|
+
*/
|
|
8
|
+
export interface RenewedChatToken {
|
|
9
|
+
ChatId?: string;
|
|
10
|
+
Token?: string;
|
|
11
|
+
ExpiresIn?: string | number;
|
|
12
|
+
VoiceVideoCallToken?: unknown;
|
|
13
|
+
}
|
|
14
|
+
export interface ChatTokenRefresherParams {
|
|
15
|
+
initialToken: string;
|
|
16
|
+
chatId: string;
|
|
17
|
+
/** Returns false once the chat this refresher was created for has ended or was replaced. */
|
|
18
|
+
isChatActive: () => boolean;
|
|
19
|
+
/** Calls the renew chat token API. May throw. */
|
|
20
|
+
renewChatToken: () => Promise<RenewedChatToken>;
|
|
21
|
+
/** Invoked after a usable token is received for the same chat. May throw. */
|
|
22
|
+
onRenewed: (renewed: RenewedChatToken) => void;
|
|
23
|
+
onStart?: () => void;
|
|
24
|
+
onSuccess?: () => void;
|
|
25
|
+
onFailure?: (reason: string, error?: unknown) => void;
|
|
26
|
+
/** Returns true when an HTTP status means renewal cannot succeed for this chat. Checked on each failure. */
|
|
27
|
+
isNonRetriableStatus?: (statusCode: number) => boolean;
|
|
28
|
+
retryCooldownInMs?: number;
|
|
29
|
+
timeoutInMs?: number;
|
|
30
|
+
now?: () => number;
|
|
31
|
+
}
|
|
32
|
+
export declare const defaultChatTokenRenewalRetryCooldownInMs: number;
|
|
33
|
+
export declare const defaultChatTokenRenewalTimeoutInMs: number;
|
|
34
|
+
export declare const defaultNonRetriableStatusCodes: number[];
|
|
35
|
+
export declare const authenticatedNonRetriableStatusCodes: number[];
|
|
36
|
+
/**
|
|
37
|
+
* Returns the expiry of a JWT chat token in milliseconds since epoch, or undefined when it cannot be read.
|
|
38
|
+
*/
|
|
39
|
+
export declare const getChatTokenExpiresOnInMs: (token: unknown) => number | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Returns true when the token is a JWT whose exp claim is far enough in the future to be accepted by ACS.
|
|
42
|
+
*/
|
|
43
|
+
export declare const isChatTokenUsable: (token: unknown, nowInMs?: number) => boolean;
|
|
44
|
+
export declare const createChatTokenRefresher: (params: ChatTokenRefresherParams) => (() => Promise<string>);
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Fail-safe ACS chat token refresher backed by the Omnichannel renew chat token API.
|
|
4
|
+
*
|
|
5
|
+
* The refresher NEVER throws and NEVER returns an unusable token. On any failure (API not deployed, feature disabled,
|
|
6
|
+
* chat ended, network error, timeout, invalid response, ...) it returns the last known good token, which is exactly
|
|
7
|
+
* what the SDK did before renewal existed, so behavior falls back to the existing flow.
|
|
8
|
+
*/
|
|
9
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
10
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
11
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
12
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
13
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
14
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
15
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
19
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
20
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
21
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
22
|
+
function step(op) {
|
|
23
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
24
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
25
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
26
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
27
|
+
switch (op[0]) {
|
|
28
|
+
case 0: case 1: t = op; break;
|
|
29
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
30
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
31
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
32
|
+
default:
|
|
33
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
34
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
35
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
36
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
37
|
+
if (t[2]) _.ops.pop();
|
|
38
|
+
_.trys.pop(); continue;
|
|
39
|
+
}
|
|
40
|
+
op = body.call(thisArg, _);
|
|
41
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
42
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.createChatTokenRefresher = exports.isChatTokenUsable = exports.getChatTokenExpiresOnInMs = exports.authenticatedNonRetriableStatusCodes = exports.defaultNonRetriableStatusCodes = exports.defaultChatTokenRenewalTimeoutInMs = exports.defaultChatTokenRenewalRetryCooldownInMs = void 0;
|
|
47
|
+
exports.defaultChatTokenRenewalRetryCooldownInMs = 60 * 1000;
|
|
48
|
+
exports.defaultChatTokenRenewalTimeoutInMs = 60 * 1000;
|
|
49
|
+
var minimumTokenLifetimeInMs = 60 * 1000;
|
|
50
|
+
// The API is not deployed, renewal is disabled for the org, or the chat cannot be renewed. Retrying will not help.
|
|
51
|
+
exports.defaultNonRetriableStatusCodes = [400, 401, 403, 404, 405];
|
|
52
|
+
// On the authenticated route, 400/401/403 come from customer token validation and can succeed once the host has a fresh token.
|
|
53
|
+
exports.authenticatedNonRetriableStatusCodes = [404, 405];
|
|
54
|
+
var decodeBase64Url = function (value) {
|
|
55
|
+
var base64 = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
56
|
+
var padded = base64 + "=".repeat((4 - (base64.length % 4)) % 4);
|
|
57
|
+
if (typeof atob === "function") {
|
|
58
|
+
return atob(padded);
|
|
59
|
+
}
|
|
60
|
+
if (typeof Buffer !== "undefined" && typeof Buffer.from === "function") {
|
|
61
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Returns the expiry of a JWT chat token in milliseconds since epoch, or undefined when it cannot be read.
|
|
67
|
+
*/
|
|
68
|
+
var getChatTokenExpiresOnInMs = function (token) {
|
|
69
|
+
try {
|
|
70
|
+
if (typeof token !== "string" || token.trim().length === 0) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
var parts = token.split(".");
|
|
74
|
+
if (parts.length !== 3) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
var payload = decodeBase64Url(parts[1]);
|
|
78
|
+
if (!payload) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
var exp = JSON.parse(payload).exp;
|
|
82
|
+
return typeof exp === "number" ? exp * 1000 : undefined;
|
|
83
|
+
}
|
|
84
|
+
catch (_a) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
exports.getChatTokenExpiresOnInMs = getChatTokenExpiresOnInMs;
|
|
89
|
+
/**
|
|
90
|
+
* Returns true when the token is a JWT whose exp claim is far enough in the future to be accepted by ACS.
|
|
91
|
+
*/
|
|
92
|
+
var isChatTokenUsable = function (token, nowInMs) {
|
|
93
|
+
if (nowInMs === void 0) { nowInMs = Date.now(); }
|
|
94
|
+
var expiresOnInMs = (0, exports.getChatTokenExpiresOnInMs)(token);
|
|
95
|
+
return expiresOnInMs !== undefined && expiresOnInMs > nowInMs + minimumTokenLifetimeInMs;
|
|
96
|
+
};
|
|
97
|
+
exports.isChatTokenUsable = isChatTokenUsable;
|
|
98
|
+
var getStatusCode = function (error) {
|
|
99
|
+
var _a;
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101
|
+
var status = (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status;
|
|
102
|
+
return typeof status === "number" ? status : undefined;
|
|
103
|
+
};
|
|
104
|
+
var createChatTokenRefresher = function (params) {
|
|
105
|
+
var _a, _b, _c, _d;
|
|
106
|
+
var retryCooldownInMs = (_a = params.retryCooldownInMs) !== null && _a !== void 0 ? _a : exports.defaultChatTokenRenewalRetryCooldownInMs;
|
|
107
|
+
var timeoutInMs = (_b = params.timeoutInMs) !== null && _b !== void 0 ? _b : exports.defaultChatTokenRenewalTimeoutInMs;
|
|
108
|
+
var now = (_c = params.now) !== null && _c !== void 0 ? _c : (function () { return Date.now(); });
|
|
109
|
+
var isNonRetriableStatus = (_d = params.isNonRetriableStatus) !== null && _d !== void 0 ? _d : (function (statusCode) { return exports.defaultNonRetriableStatusCodes.includes(statusCode); });
|
|
110
|
+
var latestToken = params.initialToken;
|
|
111
|
+
var isDisabled = false;
|
|
112
|
+
var lastFailureTime = null;
|
|
113
|
+
var activeRenewal = null;
|
|
114
|
+
var safeInvoke = function (callback) {
|
|
115
|
+
try {
|
|
116
|
+
callback === null || callback === void 0 ? void 0 : callback();
|
|
117
|
+
}
|
|
118
|
+
catch (_a) {
|
|
119
|
+
// Telemetry callbacks must never break the refresh.
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
var fail = function (reason, error, disable) {
|
|
123
|
+
if (disable === void 0) { disable = false; }
|
|
124
|
+
if (disable) {
|
|
125
|
+
isDisabled = true;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
lastFailureTime = now();
|
|
129
|
+
}
|
|
130
|
+
safeInvoke(function () { var _a; return (_a = params.onFailure) === null || _a === void 0 ? void 0 : _a.call(params, reason, error); });
|
|
131
|
+
return latestToken;
|
|
132
|
+
};
|
|
133
|
+
var renew = function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
134
|
+
var renewed, timeoutId_1, timeout, error_1, statusCode, isNonRetriable;
|
|
135
|
+
return __generator(this, function (_a) {
|
|
136
|
+
switch (_a.label) {
|
|
137
|
+
case 0:
|
|
138
|
+
safeInvoke(params.onStart);
|
|
139
|
+
_a.label = 1;
|
|
140
|
+
case 1:
|
|
141
|
+
_a.trys.push([1, 6, , 7]);
|
|
142
|
+
timeout = new Promise(function (_, reject) {
|
|
143
|
+
timeoutId_1 = setTimeout(function () { return reject(new Error("RenewChatTokenTimeout")); }, timeoutInMs);
|
|
144
|
+
});
|
|
145
|
+
_a.label = 2;
|
|
146
|
+
case 2:
|
|
147
|
+
_a.trys.push([2, , 4, 5]);
|
|
148
|
+
return [4 /*yield*/, Promise.race([params.renewChatToken(), timeout])];
|
|
149
|
+
case 3:
|
|
150
|
+
renewed = _a.sent();
|
|
151
|
+
return [3 /*break*/, 5];
|
|
152
|
+
case 4:
|
|
153
|
+
clearTimeout(timeoutId_1);
|
|
154
|
+
return [7 /*endfinally*/];
|
|
155
|
+
case 5: return [3 /*break*/, 7];
|
|
156
|
+
case 6:
|
|
157
|
+
error_1 = _a.sent();
|
|
158
|
+
statusCode = getStatusCode(error_1);
|
|
159
|
+
isNonRetriable = statusCode !== undefined && isNonRetriableStatus(statusCode);
|
|
160
|
+
return [2 /*return*/, fail(statusCode !== undefined ? "HttpStatus".concat(statusCode) : "RequestFailed", error_1, isNonRetriable)];
|
|
161
|
+
case 7:
|
|
162
|
+
if (!params.isChatActive()) {
|
|
163
|
+
return [2 /*return*/, fail("ChatNotActive")];
|
|
164
|
+
}
|
|
165
|
+
if (!renewed || renewed.ChatId !== params.chatId) {
|
|
166
|
+
return [2 /*return*/, fail("ChatIdMismatch")];
|
|
167
|
+
}
|
|
168
|
+
if (!(0, exports.isChatTokenUsable)(renewed.Token, now())) {
|
|
169
|
+
return [2 /*return*/, fail("InvalidToken")];
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
params.onRenewed(renewed);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
return [2 /*return*/, fail("UpdateFailed", error)];
|
|
176
|
+
}
|
|
177
|
+
latestToken = renewed.Token;
|
|
178
|
+
lastFailureTime = null;
|
|
179
|
+
safeInvoke(params.onSuccess);
|
|
180
|
+
return [2 /*return*/, latestToken];
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}); };
|
|
184
|
+
return function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
185
|
+
var _a;
|
|
186
|
+
return __generator(this, function (_b) {
|
|
187
|
+
switch (_b.label) {
|
|
188
|
+
case 0:
|
|
189
|
+
_b.trys.push([0, 2, , 3]);
|
|
190
|
+
if (isDisabled || !params.isChatActive()) {
|
|
191
|
+
return [2 /*return*/, latestToken];
|
|
192
|
+
}
|
|
193
|
+
if (lastFailureTime !== null && now() - lastFailureTime < retryCooldownInMs) {
|
|
194
|
+
return [2 /*return*/, latestToken];
|
|
195
|
+
}
|
|
196
|
+
if (!activeRenewal) {
|
|
197
|
+
activeRenewal = (function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
198
|
+
return __generator(this, function (_a) {
|
|
199
|
+
switch (_a.label) {
|
|
200
|
+
case 0:
|
|
201
|
+
_a.trys.push([0, , 2, 3]);
|
|
202
|
+
return [4 /*yield*/, renew()];
|
|
203
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
204
|
+
case 2:
|
|
205
|
+
activeRenewal = null;
|
|
206
|
+
return [7 /*endfinally*/];
|
|
207
|
+
case 3: return [2 /*return*/];
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}); })();
|
|
211
|
+
}
|
|
212
|
+
return [4 /*yield*/, activeRenewal];
|
|
213
|
+
case 1: return [2 /*return*/, _b.sent()];
|
|
214
|
+
case 2:
|
|
215
|
+
_a = _b.sent();
|
|
216
|
+
return [2 /*return*/, latestToken];
|
|
217
|
+
case 3: return [2 /*return*/];
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}); };
|
|
221
|
+
};
|
|
222
|
+
exports.createChatTokenRefresher = createChatTokenRefresher;
|
|
223
|
+
//# sourceMappingURL=chatTokenRenewal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatTokenRenewal.js","sourceRoot":"","sources":["../../src/utils/chatTokenRenewal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recovers ACS chat requests that fail with 401 Unauthorized while the chat token has not expired, for example after
|
|
3
|
+
* the token was revoked.
|
|
4
|
+
*
|
|
5
|
+
* Recovery is fail-safe. It never throws, retries a failed request at most once, renews the token at most once per
|
|
6
|
+
* interval for a chat, stops renewing after ACS rejects several renewed tokens in a row, and gives the original 401
|
|
7
|
+
* back to the caller when no different usable token is available, which is exactly the behavior without recovery.
|
|
8
|
+
*/
|
|
9
|
+
import { ChatClientOptions } from "@azure/communication-chat";
|
|
10
|
+
import { CommunicationTokenCredential } from "@azure/communication-common";
|
|
11
|
+
export declare enum UnauthorizedRecoverySource {
|
|
12
|
+
ChatRequest = "ChatRequest"
|
|
13
|
+
}
|
|
14
|
+
export declare enum UnauthorizedRecoveryFailureReason {
|
|
15
|
+
/** Renewal did not return a different usable token. */
|
|
16
|
+
NoNewToken = "NoNewToken",
|
|
17
|
+
/** ACS returned 401 for the renewed token too. */
|
|
18
|
+
RenewedTokenRejected = "RenewedTokenRejected"
|
|
19
|
+
}
|
|
20
|
+
export interface ChatTokenUnauthorizedRecoveryParams {
|
|
21
|
+
initialToken: string;
|
|
22
|
+
/** Renews the chat token. Must never throw and returns the current token when renewal is not possible. */
|
|
23
|
+
refreshToken: () => Promise<string>;
|
|
24
|
+
onStart?: (source: UnauthorizedRecoverySource) => void;
|
|
25
|
+
/** Called when ACS accepts a renewed token, or when an existing newer token is used without renewing. */
|
|
26
|
+
onSuccess?: (source: UnauthorizedRecoverySource, usedExistingToken: boolean) => void;
|
|
27
|
+
onFailure?: (source: UnauthorizedRecoverySource, reason: UnauthorizedRecoveryFailureReason) => void;
|
|
28
|
+
minRenewalIntervalInMs?: number;
|
|
29
|
+
/** Renewals in a row whose token ACS also rejected, after which recovery stops renewing for the chat. */
|
|
30
|
+
maxRejectedRenewals?: number;
|
|
31
|
+
now?: () => number;
|
|
32
|
+
}
|
|
33
|
+
export interface ChatTokenUnauthorizedRecovery {
|
|
34
|
+
/** Returns the token to send: the given token, or the newest usable token known, whichever expires later. */
|
|
35
|
+
selectToken: (token: string) => string;
|
|
36
|
+
/** Records a token obtained outside recovery, such as a proactive renewal. */
|
|
37
|
+
observeToken: (token: string) => void;
|
|
38
|
+
/** Returns a different usable token to retry with after a 401, or undefined when none is available. */
|
|
39
|
+
recover: (failedToken: string | undefined, source: UnauthorizedRecoverySource) => Promise<string | undefined>;
|
|
40
|
+
/** Reports whether ACS accepted a request sent with the given token. */
|
|
41
|
+
reportTokenResult: (token: string, accepted: boolean) => void;
|
|
42
|
+
}
|
|
43
|
+
export declare const defaultUnauthorizedRecoveryMinRenewalIntervalInMs: number;
|
|
44
|
+
export declare const defaultUnauthorizedRecoveryMaxRejectedRenewals = 3;
|
|
45
|
+
export declare const createChatTokenUnauthorizedRecovery: (params: ChatTokenUnauthorizedRecoveryParams) => ChatTokenUnauthorizedRecovery;
|
|
46
|
+
type ChatAdditionalPolicy = NonNullable<ChatClientOptions["additionalPolicies"]>[number];
|
|
47
|
+
export declare const chatTokenUnauthorizedRecoveryPolicyName = "ChatTokenUnauthorizedRecoveryPolicy";
|
|
48
|
+
/**
|
|
49
|
+
* Pipeline policy for ACS chat requests. Runs after the authentication policy on every attempt, sends the newest
|
|
50
|
+
* usable token, and resends a request that failed with 401 once with a different usable token.
|
|
51
|
+
*/
|
|
52
|
+
export declare const createChatTokenUnauthorizedRecoveryPolicy: (recovery: ChatTokenUnauthorizedRecovery) => ChatAdditionalPolicy;
|
|
53
|
+
/**
|
|
54
|
+
* Credential that gives the newest usable token to ACS, including the real-time notification client, which reads
|
|
55
|
+
* the token from the credential instead of the request pipeline.
|
|
56
|
+
*/
|
|
57
|
+
export declare class ChatTokenUnauthorizedRecoveryCredential implements CommunicationTokenCredential {
|
|
58
|
+
private credential;
|
|
59
|
+
private recovery;
|
|
60
|
+
constructor(credential: CommunicationTokenCredential, recovery: ChatTokenUnauthorizedRecovery);
|
|
61
|
+
getToken(options?: Parameters<CommunicationTokenCredential["getToken"]>[0]): Promise<Awaited<ReturnType<CommunicationTokenCredential["getToken"]>>>;
|
|
62
|
+
dispose(): void;
|
|
63
|
+
}
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Recovers ACS chat requests that fail with 401 Unauthorized while the chat token has not expired, for example after
|
|
4
|
+
* the token was revoked.
|
|
5
|
+
*
|
|
6
|
+
* Recovery is fail-safe. It never throws, retries a failed request at most once, renews the token at most once per
|
|
7
|
+
* interval for a chat, stops renewing after ACS rejects several renewed tokens in a row, and gives the original 401
|
|
8
|
+
* back to the caller when no different usable token is available, which is exactly the behavior without recovery.
|
|
9
|
+
*/
|
|
10
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
11
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
12
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
13
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
14
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
15
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
16
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
20
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
21
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
22
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
23
|
+
function step(op) {
|
|
24
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
25
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
26
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
27
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
28
|
+
switch (op[0]) {
|
|
29
|
+
case 0: case 1: t = op; break;
|
|
30
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
31
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
32
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
33
|
+
default:
|
|
34
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
35
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
36
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
37
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
38
|
+
if (t[2]) _.ops.pop();
|
|
39
|
+
_.trys.pop(); continue;
|
|
40
|
+
}
|
|
41
|
+
op = body.call(thisArg, _);
|
|
42
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
43
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.ChatTokenUnauthorizedRecoveryCredential = exports.createChatTokenUnauthorizedRecoveryPolicy = exports.chatTokenUnauthorizedRecoveryPolicyName = exports.createChatTokenUnauthorizedRecovery = exports.defaultUnauthorizedRecoveryMaxRejectedRenewals = exports.defaultUnauthorizedRecoveryMinRenewalIntervalInMs = exports.UnauthorizedRecoveryFailureReason = exports.UnauthorizedRecoverySource = void 0;
|
|
48
|
+
var chatTokenRenewal_1 = require("./chatTokenRenewal");
|
|
49
|
+
var UnauthorizedRecoverySource;
|
|
50
|
+
(function (UnauthorizedRecoverySource) {
|
|
51
|
+
UnauthorizedRecoverySource["ChatRequest"] = "ChatRequest";
|
|
52
|
+
})(UnauthorizedRecoverySource || (exports.UnauthorizedRecoverySource = UnauthorizedRecoverySource = {}));
|
|
53
|
+
var UnauthorizedRecoveryFailureReason;
|
|
54
|
+
(function (UnauthorizedRecoveryFailureReason) {
|
|
55
|
+
/** Renewal did not return a different usable token. */
|
|
56
|
+
UnauthorizedRecoveryFailureReason["NoNewToken"] = "NoNewToken";
|
|
57
|
+
/** ACS returned 401 for the renewed token too. */
|
|
58
|
+
UnauthorizedRecoveryFailureReason["RenewedTokenRejected"] = "RenewedTokenRejected";
|
|
59
|
+
})(UnauthorizedRecoveryFailureReason || (exports.UnauthorizedRecoveryFailureReason = UnauthorizedRecoveryFailureReason = {}));
|
|
60
|
+
exports.defaultUnauthorizedRecoveryMinRenewalIntervalInMs = 60 * 1000;
|
|
61
|
+
exports.defaultUnauthorizedRecoveryMaxRejectedRenewals = 3;
|
|
62
|
+
var createChatTokenUnauthorizedRecovery = function (params) {
|
|
63
|
+
var _a, _b, _c;
|
|
64
|
+
var minRenewalIntervalInMs = (_a = params.minRenewalIntervalInMs) !== null && _a !== void 0 ? _a : exports.defaultUnauthorizedRecoveryMinRenewalIntervalInMs;
|
|
65
|
+
var maxRejectedRenewals = (_b = params.maxRejectedRenewals) !== null && _b !== void 0 ? _b : exports.defaultUnauthorizedRecoveryMaxRejectedRenewals;
|
|
66
|
+
var now = (_c = params.now) !== null && _c !== void 0 ? _c : (function () { return Date.now(); });
|
|
67
|
+
var latestToken = params.initialToken;
|
|
68
|
+
var lastRenewalTime = null;
|
|
69
|
+
var activeRenewal = null;
|
|
70
|
+
var rejectedRenewals = 0;
|
|
71
|
+
// Renewed token whose first ACS response has not been reported yet
|
|
72
|
+
var unverifiedRenewal = null;
|
|
73
|
+
var safeInvoke = function (callback) {
|
|
74
|
+
try {
|
|
75
|
+
callback();
|
|
76
|
+
}
|
|
77
|
+
catch (_a) {
|
|
78
|
+
// Telemetry callbacks must never break recovery
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var isNewer = function (candidate, current) {
|
|
82
|
+
var candidateExpiresOn = (0, chatTokenRenewal_1.getChatTokenExpiresOnInMs)(candidate);
|
|
83
|
+
if (candidateExpiresOn === undefined) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
var currentExpiresOn = (0, chatTokenRenewal_1.getChatTokenExpiresOnInMs)(current);
|
|
87
|
+
return currentExpiresOn === undefined || candidateExpiresOn > currentExpiresOn;
|
|
88
|
+
};
|
|
89
|
+
var observeToken = function (token) {
|
|
90
|
+
try {
|
|
91
|
+
if (token && token !== latestToken && (0, chatTokenRenewal_1.isChatTokenUsable)(token, now()) && isNewer(token, latestToken)) {
|
|
92
|
+
latestToken = token;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (_a) {
|
|
96
|
+
// Keep the current token
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
var selectToken = function (token) {
|
|
100
|
+
try {
|
|
101
|
+
if (latestToken && latestToken !== token && (0, chatTokenRenewal_1.isChatTokenUsable)(latestToken, now()) && isNewer(latestToken, token)) {
|
|
102
|
+
return latestToken;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (_a) {
|
|
106
|
+
// Keep the given token
|
|
107
|
+
}
|
|
108
|
+
return token;
|
|
109
|
+
};
|
|
110
|
+
var renew = function (failedToken, source) { return __awaiter(void 0, void 0, void 0, function () {
|
|
111
|
+
var token, _a;
|
|
112
|
+
return __generator(this, function (_b) {
|
|
113
|
+
switch (_b.label) {
|
|
114
|
+
case 0:
|
|
115
|
+
lastRenewalTime = now();
|
|
116
|
+
safeInvoke(function () { var _a; return (_a = params.onStart) === null || _a === void 0 ? void 0 : _a.call(params, source); });
|
|
117
|
+
_b.label = 1;
|
|
118
|
+
case 1:
|
|
119
|
+
_b.trys.push([1, 3, , 4]);
|
|
120
|
+
return [4 /*yield*/, params.refreshToken()];
|
|
121
|
+
case 2:
|
|
122
|
+
token = _b.sent();
|
|
123
|
+
return [3 /*break*/, 4];
|
|
124
|
+
case 3:
|
|
125
|
+
_a = _b.sent();
|
|
126
|
+
token = undefined;
|
|
127
|
+
return [3 /*break*/, 4];
|
|
128
|
+
case 4:
|
|
129
|
+
if (!token || token === failedToken || !(0, chatTokenRenewal_1.isChatTokenUsable)(token, now())) {
|
|
130
|
+
safeInvoke(function () { var _a; return (_a = params.onFailure) === null || _a === void 0 ? void 0 : _a.call(params, source, UnauthorizedRecoveryFailureReason.NoNewToken); });
|
|
131
|
+
return [2 /*return*/, undefined];
|
|
132
|
+
}
|
|
133
|
+
observeToken(token);
|
|
134
|
+
unverifiedRenewal = { token: token, source: source };
|
|
135
|
+
return [2 /*return*/, token];
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}); };
|
|
139
|
+
var reportTokenResult = function (token, accepted) {
|
|
140
|
+
try {
|
|
141
|
+
if (!unverifiedRenewal || unverifiedRenewal.token !== token) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
var source_1 = unverifiedRenewal.source;
|
|
145
|
+
unverifiedRenewal = null;
|
|
146
|
+
if (accepted) {
|
|
147
|
+
rejectedRenewals = 0;
|
|
148
|
+
safeInvoke(function () { var _a; return (_a = params.onSuccess) === null || _a === void 0 ? void 0 : _a.call(params, source_1, false); });
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
rejectedRenewals++;
|
|
152
|
+
safeInvoke(function () { var _a; return (_a = params.onFailure) === null || _a === void 0 ? void 0 : _a.call(params, source_1, UnauthorizedRecoveryFailureReason.RenewedTokenRejected); });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (_a) {
|
|
156
|
+
// Reporting must never break the request
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
var recover = function (failedToken, source) { return __awaiter(void 0, void 0, void 0, function () {
|
|
160
|
+
var _a;
|
|
161
|
+
return __generator(this, function (_b) {
|
|
162
|
+
switch (_b.label) {
|
|
163
|
+
case 0:
|
|
164
|
+
_b.trys.push([0, 4, , 5]);
|
|
165
|
+
if (latestToken && latestToken !== failedToken && (0, chatTokenRenewal_1.isChatTokenUsable)(latestToken, now())) {
|
|
166
|
+
safeInvoke(function () { var _a; return (_a = params.onSuccess) === null || _a === void 0 ? void 0 : _a.call(params, source, true); });
|
|
167
|
+
return [2 /*return*/, latestToken];
|
|
168
|
+
}
|
|
169
|
+
if (!activeRenewal) return [3 /*break*/, 2];
|
|
170
|
+
return [4 /*yield*/, activeRenewal];
|
|
171
|
+
case 1: return [2 /*return*/, _b.sent()];
|
|
172
|
+
case 2:
|
|
173
|
+
if (rejectedRenewals >= maxRejectedRenewals
|
|
174
|
+
|| (lastRenewalTime !== null && now() - lastRenewalTime < minRenewalIntervalInMs)) {
|
|
175
|
+
return [2 /*return*/, undefined];
|
|
176
|
+
}
|
|
177
|
+
activeRenewal = (function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
178
|
+
return __generator(this, function (_a) {
|
|
179
|
+
switch (_a.label) {
|
|
180
|
+
case 0:
|
|
181
|
+
_a.trys.push([0, , 2, 3]);
|
|
182
|
+
return [4 /*yield*/, renew(failedToken, source)];
|
|
183
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
184
|
+
case 2:
|
|
185
|
+
activeRenewal = null;
|
|
186
|
+
return [7 /*endfinally*/];
|
|
187
|
+
case 3: return [2 /*return*/];
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}); })();
|
|
191
|
+
return [4 /*yield*/, activeRenewal];
|
|
192
|
+
case 3: return [2 /*return*/, _b.sent()];
|
|
193
|
+
case 4:
|
|
194
|
+
_a = _b.sent();
|
|
195
|
+
return [2 /*return*/, undefined];
|
|
196
|
+
case 5: return [2 /*return*/];
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}); };
|
|
200
|
+
return { selectToken: selectToken, observeToken: observeToken, recover: recover, reportTokenResult: reportTokenResult };
|
|
201
|
+
};
|
|
202
|
+
exports.createChatTokenUnauthorizedRecovery = createChatTokenUnauthorizedRecovery;
|
|
203
|
+
exports.chatTokenUnauthorizedRecoveryPolicyName = "ChatTokenUnauthorizedRecoveryPolicy";
|
|
204
|
+
var authorizationHeaderName = "Authorization";
|
|
205
|
+
var bearerPrefix = "Bearer ";
|
|
206
|
+
var getBearerToken = function (request) {
|
|
207
|
+
var header = request.headers.get(authorizationHeaderName);
|
|
208
|
+
return header && header.startsWith(bearerPrefix) ? header.substring(bearerPrefix.length) : undefined;
|
|
209
|
+
};
|
|
210
|
+
// Only bodies that can be sent again unchanged are retried
|
|
211
|
+
var isReplayableBody = function (request) {
|
|
212
|
+
var body = request.body;
|
|
213
|
+
return body === undefined || body === null || typeof body === "string";
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Pipeline policy for ACS chat requests. Runs after the authentication policy on every attempt, sends the newest
|
|
217
|
+
* usable token, and resends a request that failed with 401 once with a different usable token.
|
|
218
|
+
*/
|
|
219
|
+
var createChatTokenUnauthorizedRecoveryPolicy = function (recovery) {
|
|
220
|
+
var policy = {
|
|
221
|
+
name: exports.chatTokenUnauthorizedRecoveryPolicyName,
|
|
222
|
+
sendRequest: function (request, next) { return __awaiter(void 0, void 0, void 0, function () {
|
|
223
|
+
var sentToken, selectedToken, response, token, retryResponse;
|
|
224
|
+
return __generator(this, function (_a) {
|
|
225
|
+
switch (_a.label) {
|
|
226
|
+
case 0:
|
|
227
|
+
sentToken = getBearerToken(request);
|
|
228
|
+
if (sentToken) {
|
|
229
|
+
selectedToken = recovery.selectToken(sentToken);
|
|
230
|
+
if (selectedToken !== sentToken) {
|
|
231
|
+
request.headers.set(authorizationHeaderName, "".concat(bearerPrefix).concat(selectedToken));
|
|
232
|
+
sentToken = selectedToken;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return [4 /*yield*/, next(request)];
|
|
236
|
+
case 1:
|
|
237
|
+
response = _a.sent();
|
|
238
|
+
if (sentToken) {
|
|
239
|
+
recovery.reportTokenResult(sentToken, response.status !== 401);
|
|
240
|
+
}
|
|
241
|
+
if (response.status !== 401 || !sentToken || !isReplayableBody(request)) {
|
|
242
|
+
return [2 /*return*/, response];
|
|
243
|
+
}
|
|
244
|
+
return [4 /*yield*/, recovery.recover(sentToken, UnauthorizedRecoverySource.ChatRequest)];
|
|
245
|
+
case 2:
|
|
246
|
+
token = _a.sent();
|
|
247
|
+
if (!token || token === sentToken) {
|
|
248
|
+
return [2 /*return*/, response];
|
|
249
|
+
}
|
|
250
|
+
request.headers.set(authorizationHeaderName, "".concat(bearerPrefix).concat(token));
|
|
251
|
+
return [4 /*yield*/, next(request)];
|
|
252
|
+
case 3:
|
|
253
|
+
retryResponse = _a.sent();
|
|
254
|
+
recovery.reportTokenResult(token, retryResponse.status !== 401);
|
|
255
|
+
return [2 /*return*/, retryResponse];
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}); }
|
|
259
|
+
};
|
|
260
|
+
return { policy: policy, position: "perRetry" };
|
|
261
|
+
};
|
|
262
|
+
exports.createChatTokenUnauthorizedRecoveryPolicy = createChatTokenUnauthorizedRecoveryPolicy;
|
|
263
|
+
/**
|
|
264
|
+
* Credential that gives the newest usable token to ACS, including the real-time notification client, which reads
|
|
265
|
+
* the token from the credential instead of the request pipeline.
|
|
266
|
+
*/
|
|
267
|
+
var ChatTokenUnauthorizedRecoveryCredential = /** @class */ (function () {
|
|
268
|
+
function ChatTokenUnauthorizedRecoveryCredential(credential, recovery) {
|
|
269
|
+
this.credential = credential;
|
|
270
|
+
this.recovery = recovery;
|
|
271
|
+
}
|
|
272
|
+
ChatTokenUnauthorizedRecoveryCredential.prototype.getToken = function (options) {
|
|
273
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
274
|
+
var accessToken, token, expiresOnTimestamp;
|
|
275
|
+
return __generator(this, function (_a) {
|
|
276
|
+
switch (_a.label) {
|
|
277
|
+
case 0: return [4 /*yield*/, this.credential.getToken(options)];
|
|
278
|
+
case 1:
|
|
279
|
+
accessToken = _a.sent();
|
|
280
|
+
token = this.recovery.selectToken(accessToken.token);
|
|
281
|
+
expiresOnTimestamp = token !== accessToken.token ? (0, chatTokenRenewal_1.getChatTokenExpiresOnInMs)(token) : undefined;
|
|
282
|
+
return [2 /*return*/, expiresOnTimestamp !== undefined ? { token: token, expiresOnTimestamp: expiresOnTimestamp } : accessToken];
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
};
|
|
287
|
+
ChatTokenUnauthorizedRecoveryCredential.prototype.dispose = function () {
|
|
288
|
+
this.credential.dispose();
|
|
289
|
+
};
|
|
290
|
+
return ChatTokenUnauthorizedRecoveryCredential;
|
|
291
|
+
}());
|
|
292
|
+
exports.ChatTokenUnauthorizedRecoveryCredential = ChatTokenUnauthorizedRecoveryCredential;
|
|
293
|
+
//# sourceMappingURL=chatTokenUnauthorizedRecovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chatTokenUnauthorizedRecovery.js","sourceRoot":"","sources":["../../src/utils/chatTokenUnauthorizedRecovery.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIH,uDAAkF;AAElF,IAAY,0BAEX;AAFD,WAAY,0BAA0B;IAClC,yDAA2B,CAAA;AAC/B,CAAC,EAFW,0BAA0B,0CAA1B,0BAA0B,QAErC;AAED,IAAY,iCAKX;AALD,WAAY,iCAAiC;IACzC,uDAAuD;IACvD,8DAAyB,CAAA;IACzB,kDAAkD;IAClD,kFAA6C,CAAA;AACjD,CAAC,EALW,iCAAiC,iDAAjC,iCAAiC,QAK5C;AA2BY,QAAA,iDAAiD,GAAG,EAAE,GAAG,IAAI,CAAC;AAC9D,QAAA,8CAA8C,GAAG,CAAC,CAAC;AAEzD,IAAM,mCAAmC,GAAG,UAAC,MAA2C;;IAC3F,IAAM,sBAAsB,GAAG,MAAA,MAAM,CAAC,sBAAsB,mCAAI,yDAAiD,CAAC;IAClH,IAAM,mBAAmB,GAAG,MAAA,MAAM,CAAC,mBAAmB,mCAAI,sDAA8C,CAAC;IACzG,IAAM,GAAG,GAAG,MAAA,MAAM,CAAC,GAAG,mCAAI,CAAC,cAAM,OAAA,IAAI,CAAC,GAAG,EAAE,EAAV,CAAU,CAAC,CAAC;IAE7C,IAAI,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC;IACtC,IAAI,eAAe,GAAkB,IAAI,CAAC;IAC1C,IAAI,aAAa,GAAuC,IAAI,CAAC;IAC7D,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,mEAAmE;IACnE,IAAI,iBAAiB,GAAiE,IAAI,CAAC;IAE3F,IAAM,UAAU,GAAG,UAAC,QAAoB;QACpC,IAAI,CAAC;YACD,QAAQ,EAAE,CAAC;QACf,CAAC;QAAC,WAAM,CAAC;YACL,gDAAgD;QACpD,CAAC;IACL,CAAC,CAAC;IAEF,IAAM,OAAO,GAAG,UAAC,SAAiB,EAAE,OAAe;QAC/C,IAAM,kBAAkB,GAAG,IAAA,4CAAyB,EAAC,SAAS,CAAC,CAAC;QAChE,IAAI,kBAAkB,KAAK,SAAS,EAAE,CAAC;YACnC,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,IAAM,gBAAgB,GAAG,IAAA,4CAAyB,EAAC,OAAO,CAAC,CAAC;QAC5D,OAAO,gBAAgB,KAAK,SAAS,IAAI,kBAAkB,GAAG,gBAAgB,CAAC;IACnF,CAAC,CAAC;IAEF,IAAM,YAAY,GAAG,UAAC,KAAa;QAC/B,IAAI,CAAC;YACD,IAAI,KAAK,IAAI,KAAK,KAAK,WAAW,IAAI,IAAA,oCAAiB,EAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,CAAC;gBACnG,WAAW,GAAG,KAAK,CAAC;YACxB,CAAC;QACL,CAAC;QAAC,WAAM,CAAC;YACL,yBAAyB;QAC7B,CAAC;IACL,CAAC,CAAC;IAEF,IAAM,WAAW,GAAG,UAAC,KAAa;QAC9B,IAAI,CAAC;YACD,IAAI,WAAW,IAAI,WAAW,KAAK,KAAK,IAAI,IAAA,oCAAiB,EAAC,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC;gBAC/G,OAAO,WAAW,CAAC;YACvB,CAAC;QACL,CAAC;QAAC,WAAM,CAAC;YACL,uBAAuB;QAC3B,CAAC;QAED,OAAO,KAAK,CAAC;IACjB,CAAC,CAAC;IAEF,IAAM,KAAK,GAAG,UAAO,WAA+B,EAAE,MAAkC;;;;;oBACpF,eAAe,GAAG,GAAG,EAAE,CAAC;oBACxB,UAAU,CAAC,sBAAM,OAAA,MAAA,MAAM,CAAC,OAAO,uDAAG,MAAM,CAAC,CAAA,EAAA,CAAC,CAAC;;;;oBAI/B,qBAAM,MAAM,CAAC,YAAY,EAAE,EAAA;;oBAAnC,KAAK,GAAG,SAA2B,CAAC;;;;oBAEpC,KAAK,GAAG,SAAS,CAAC;;;oBAGtB,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,WAAW,IAAI,CAAC,IAAA,oCAAiB,EAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;wBACtE,UAAU,CAAC,sBAAM,OAAA,MAAA,MAAM,CAAC,SAAS,uDAAG,MAAM,EAAE,iCAAiC,CAAC,UAAU,CAAC,CAAA,EAAA,CAAC,CAAC;wBAC3F,sBAAO,SAAS,EAAC;oBACrB,CAAC;oBAED,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,iBAAiB,GAAG,EAAE,KAAK,OAAA,EAAE,MAAM,QAAA,EAAE,CAAC;oBACtC,sBAAO,KAAK,EAAC;;;SAChB,CAAC;IAEF,IAAM,iBAAiB,GAAG,UAAC,KAAa,EAAE,QAAiB;QACvD,IAAI,CAAC;YACD,IAAI,CAAC,iBAAiB,IAAI,iBAAiB,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;gBAC1D,OAAO;YACX,CAAC;YAEO,IAAA,QAAM,GAAK,iBAAiB,OAAtB,CAAuB;YACrC,iBAAiB,GAAG,IAAI,CAAC;YACzB,IAAI,QAAQ,EAAE,CAAC;gBACX,gBAAgB,GAAG,CAAC,CAAC;gBACrB,UAAU,CAAC,sBAAM,OAAA,MAAA,MAAM,CAAC,SAAS,uDAAG,QAAM,EAAE,KAAK,CAAC,CAAA,EAAA,CAAC,CAAC;YACxD,CAAC;iBAAM,CAAC;gBACJ,gBAAgB,EAAE,CAAC;gBACnB,UAAU,CAAC,sBAAM,OAAA,MAAA,MAAM,CAAC,SAAS,uDAAG,QAAM,EAAE,iCAAiC,CAAC,oBAAoB,CAAC,CAAA,EAAA,CAAC,CAAC;YACzG,CAAC;QACL,CAAC;QAAC,WAAM,CAAC;YACL,yCAAyC;QAC7C,CAAC;IACL,CAAC,CAAC;IAEF,IAAM,OAAO,GAAG,UAAO,WAA+B,EAAE,MAAkC;;;;;;oBAElF,IAAI,WAAW,IAAI,WAAW,KAAK,WAAW,IAAI,IAAA,oCAAiB,EAAC,WAAW,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;wBACtF,UAAU,CAAC,sBAAM,OAAA,MAAA,MAAM,CAAC,SAAS,uDAAG,MAAM,EAAE,IAAI,CAAC,CAAA,EAAA,CAAC,CAAC;wBACnD,sBAAO,WAAW,EAAC;oBACvB,CAAC;yBAEG,aAAa,EAAb,wBAAa;oBACN,qBAAM,aAAa,EAAA;wBAA1B,sBAAO,SAAmB,EAAC;;oBAG/B,IAAI,gBAAgB,IAAI,mBAAmB;2BACpC,CAAC,eAAe,KAAK,IAAI,IAAI,GAAG,EAAE,GAAG,eAAe,GAAG,sBAAsB,CAAC,EAAE,CAAC;wBACpF,sBAAO,SAAS,EAAC;oBACrB,CAAC;oBAED,aAAa,GAAG,CAAC;;;;;oCAEF,qBAAM,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC,EAAA;wCAAvC,sBAAO,SAAgC,EAAC;;oCAExC,aAAa,GAAG,IAAI,CAAC;;;;;yBAE5B,CAAC,EAAE,CAAC;oBAEE,qBAAM,aAAa,EAAA;wBAA1B,sBAAO,SAAmB,EAAC;;;oBAE3B,sBAAO,SAAS,EAAC;;;;SAExB,CAAC;IAEF,OAAO,EAAE,WAAW,aAAA,EAAE,YAAY,cAAA,EAAE,OAAO,SAAA,EAAE,iBAAiB,mBAAA,EAAE,CAAC;AACrE,CAAC,CAAC;AA5HW,QAAA,mCAAmC,uCA4H9C;AAMW,QAAA,uCAAuC,GAAG,qCAAqC,CAAC;AAE7F,IAAM,uBAAuB,GAAG,eAAe,CAAC;AAChD,IAAM,YAAY,GAAG,SAAS,CAAC;AAE/B,IAAM,cAAc,GAAG,UAAC,OAA4B;IAChD,IAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAC5D,OAAO,MAAM,IAAI,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACzG,CAAC,CAAC;AAEF,2DAA2D;AAC3D,IAAM,gBAAgB,GAAG,UAAC,OAA4B;IAClD,IAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1B,OAAO,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC;AAC3E,CAAC,CAAC;AAEF;;;GAGG;AACI,IAAM,yCAAyC,GAAG,UAAC,QAAuC;IAC7F,IAAM,MAAM,GAAuB;QAC/B,IAAI,EAAE,+CAAuC;QAC7C,WAAW,EAAE,UAAO,OAAO,EAAE,IAAI;;;;;wBACzB,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;wBACxC,IAAI,SAAS,EAAE,CAAC;4BACN,aAAa,GAAG,QAAQ,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;4BACtD,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;gCAC9B,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,UAAG,YAAY,SAAG,aAAa,CAAE,CAAC,CAAC;gCAChF,SAAS,GAAG,aAAa,CAAC;4BAC9B,CAAC;wBACL,CAAC;wBAEgB,qBAAM,IAAI,CAAC,OAAO,CAAC,EAAA;;wBAA9B,QAAQ,GAAG,SAAmB;wBACpC,IAAI,SAAS,EAAE,CAAC;4BACZ,QAAQ,CAAC,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC;wBACnE,CAAC;wBAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;4BACtE,sBAAO,QAAQ,EAAC;wBACpB,CAAC;wBAEa,qBAAM,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,0BAA0B,CAAC,WAAW,CAAC,EAAA;;wBAAjF,KAAK,GAAG,SAAyE;wBACvF,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;4BAChC,sBAAO,QAAQ,EAAC;wBACpB,CAAC;wBAED,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,UAAG,YAAY,SAAG,KAAK,CAAE,CAAC,CAAC;wBAClD,qBAAM,IAAI,CAAC,OAAO,CAAC,EAAA;;wBAAnC,aAAa,GAAG,SAAmB;wBACzC,QAAQ,CAAC,iBAAiB,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC;wBAChE,sBAAO,aAAa,EAAC;;;aACxB;KACJ,CAAC;IAEF,OAAO,EAAE,MAAM,QAAA,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAC5C,CAAC,CAAC;AAnCW,QAAA,yCAAyC,6CAmCpD;AAEF;;;GAGG;AACH;IAII,iDAAmB,UAAwC,EAAE,QAAuC;QAChG,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;IAEY,0DAAQ,GAArB,UAAsB,OAAiE;;;;;4BAC/D,qBAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAA;;wBAArD,WAAW,GAAG,SAAuC;wBACrD,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;wBACrD,kBAAkB,GAAG,KAAK,KAAK,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,IAAA,4CAAyB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;wBACtG,sBAAO,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,OAAA,EAAE,kBAAkB,oBAAA,EAAE,CAAC,CAAC,CAAC,WAAW,EAAC;;;;KACzF;IAEM,yDAAO,GAAd;QACI,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;IACL,8CAAC;AAAD,CAAC,AAnBD,IAmBC;AAnBY,0FAAuC"}
|