urllib
Version:
Help in opening URLs (mostly HTTP) in a complex world — basic and digest authentication, redirections, timeout and more. Base undici API.
655 lines • 58.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpClient = exports.channels = exports.HEADER_USER_AGENT = exports.VERSION = exports.PROTO_RE = void 0;
const node_diagnostics_channel_1 = __importDefault(require("node:diagnostics_channel"));
const node_events_1 = require("node:events");
const node_http_1 = require("node:http");
const node_util_1 = require("node:util");
const node_zlib_1 = require("node:zlib");
const node_stream_1 = require("node:stream");
const promises_1 = require("node:stream/promises");
const node_path_1 = require("node:path");
const node_fs_1 = require("node:fs");
const node_url_1 = require("node:url");
const node_perf_hooks_1 = require("node:perf_hooks");
const node_querystring_1 = __importDefault(require("node:querystring"));
const promises_2 = require("node:timers/promises");
const undici_1 = require("undici");
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const symbols_js_1 = __importDefault(require("undici/lib/core/symbols.js"));
const mime_types_1 = __importDefault(require("mime-types"));
const qs_1 = __importDefault(require("qs"));
// Compatible with old style formstream
const formstream_1 = __importDefault(require("formstream"));
const FormData_js_1 = require("./FormData.js");
const HttpAgent_js_1 = require("./HttpAgent.js");
const utils_js_1 = require("./utils.js");
const symbols_js_2 = __importDefault(require("./symbols.js"));
const diagnosticsChannel_js_1 = require("./diagnosticsChannel.js");
const HttpClientError_js_1 = require("./HttpClientError.js");
exports.PROTO_RE = /^https?:\/\//i;
function noop() {
// noop
}
const debug = (0, node_util_1.debuglog)('urllib:HttpClient');
exports.VERSION = '4.8.2';
// 'node-urllib/4.0.0 Node.js/18.19.0 (darwin; x64)'
exports.HEADER_USER_AGENT = `node-urllib/${exports.VERSION} Node.js/${process.version.substring(1)} (${process.platform}; ${process.arch})`;
function getFileName(stream) {
const filePath = stream.path;
if (filePath) {
return (0, node_path_1.basename)(filePath);
}
return '';
}
function defaultIsRetry(response) {
return response.status >= 500;
}
exports.channels = {
request: node_diagnostics_channel_1.default.channel('urllib:request'),
response: node_diagnostics_channel_1.default.channel('urllib:response'),
fetchRequest: node_diagnostics_channel_1.default.channel('urllib:fetch:request'),
fetchResponse: node_diagnostics_channel_1.default.channel('urllib:fetch:response'),
};
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
const RedirectStatusCodes = [
301, // Moved Permanently
302, // Found
303, // See Other
307, // Temporary Redirect
308, // Permanent Redirect
];
class HttpClient extends node_events_1.EventEmitter {
#defaultArgs;
#dispatcher;
constructor(clientOptions) {
super();
this.#defaultArgs = clientOptions?.defaultArgs;
if (clientOptions?.lookup || clientOptions?.checkAddress) {
this.#dispatcher = new HttpAgent_js_1.HttpAgent({
lookup: clientOptions.lookup,
checkAddress: clientOptions.checkAddress,
connect: clientOptions.connect,
allowH2: clientOptions.allowH2,
});
}
else if (clientOptions?.connect) {
this.#dispatcher = new undici_1.Agent({
connect: clientOptions.connect,
allowH2: clientOptions.allowH2,
});
}
else if (clientOptions?.allowH2) {
// Support HTTP2
this.#dispatcher = new undici_1.Agent({
allowH2: clientOptions.allowH2,
});
}
(0, diagnosticsChannel_js_1.initDiagnosticsChannel)();
}
getDispatcher() {
return this.#dispatcher ?? (0, undici_1.getGlobalDispatcher)();
}
setDispatcher(dispatcher) {
this.#dispatcher = dispatcher;
}
getDispatcherPoolStats() {
const agent = this.getDispatcher();
// origin => Pool Instance
const clients = Reflect.get(agent, symbols_js_1.default.kClients);
const poolStatsMap = {};
if (!clients) {
return poolStatsMap;
}
for (const [key, ref] of clients) {
const pool = (typeof ref.deref === 'function' ? ref.deref() : ref);
// NOTE: pool become to { dispatcher: Pool } in undici@v7
const stats = pool?.stats ?? pool?.dispatcher?.stats;
if (!stats)
continue;
poolStatsMap[key] = {
connected: stats.connected,
free: stats.free,
pending: stats.pending,
queued: stats.queued,
running: stats.running,
size: stats.size,
};
}
return poolStatsMap;
}
async request(url, options) {
return await this.#requestInternal(url, options);
}
// alias to request, keep compatible with urllib@2 HttpClient.curl
async curl(url, options) {
return await this.request(url, options);
}
async #requestInternal(url, options, requestContext) {
const requestId = (0, utils_js_1.globalId)('HttpClientRequest');
let requestUrl;
if (typeof url === 'string') {
if (!exports.PROTO_RE.test(url)) {
// Support `request('www.server.com')`
url = 'http://' + url;
}
requestUrl = new URL(url);
}
else {
if (!url.searchParams) {
// url maybe url.parse(url) object in urllib2
requestUrl = new URL((0, node_url_1.format)(url));
}
else {
// or even if not, we clone to avoid mutating it
requestUrl = new URL(url.toString());
}
}
const method = (options?.type || options?.method || 'GET').toUpperCase();
const originalHeaders = options?.headers;
const headers = {};
const args = {
retry: 0,
socketErrorRetry: 1,
timing: true,
...this.#defaultArgs,
...options,
// keep method and headers exists on args for request event handler to easy use
method,
headers,
};
requestContext = {
retries: 0,
socketErrorRetries: 0,
redirects: 0,
history: [],
...requestContext,
};
if (!requestContext.requestStartTime) {
requestContext.requestStartTime = node_perf_hooks_1.performance.now();
}
requestContext.history.push(requestUrl.href);
const requestStartTime = requestContext.requestStartTime;
// https://developer.chrome.com/docs/devtools/network/reference/?utm_source=devtools#timing-explanation
const timing = {
// socket assigned
queuing: 0,
// dns lookup time
dnslookup: 0,
// socket connected
connected: 0,
// request headers sent
requestHeadersSent: 0,
// request sent, including headers and body
requestSent: 0,
// Time to first byte (TTFB), the response headers have been received
waiting: 0,
// the response body and trailers have been received
contentDownload: 0,
};
const originalOpaque = args.opaque;
// using opaque to diagnostics channel, binding request and socket
const internalOpaque = {
[symbols_js_2.default.kRequestId]: requestId,
[symbols_js_2.default.kRequestStartTime]: requestStartTime,
[symbols_js_2.default.kEnableRequestTiming]: !!args.timing,
[symbols_js_2.default.kRequestTiming]: timing,
[symbols_js_2.default.kRequestOriginalOpaque]: originalOpaque,
};
const reqMeta = {
requestId,
url: requestUrl.href,
args,
ctx: args.ctx,
retries: requestContext.retries,
};
const socketInfo = {
id: 0,
localAddress: '',
localPort: 0,
remoteAddress: '',
remotePort: 0,
remoteFamily: '',
bytesWritten: 0,
bytesRead: 0,
handledRequests: 0,
handledResponses: 0,
};
// keep urllib createCallbackResponse style
const resHeaders = {};
let res = {
status: -1,
statusCode: -1,
statusText: '',
statusMessage: '',
headers: resHeaders,
size: 0,
aborted: false,
rt: 0,
keepAliveSocket: true,
requestUrls: requestContext.history,
timing,
socket: socketInfo,
retries: requestContext.retries,
socketErrorRetries: requestContext.socketErrorRetries,
};
let headersTimeout = 5000;
let bodyTimeout = 5000;
if (args.timeout) {
if (Array.isArray(args.timeout)) {
headersTimeout = args.timeout[0] ?? headersTimeout;
bodyTimeout = args.timeout[1] ?? bodyTimeout;
}
else {
// compatible with urllib@2 timeout string format
headersTimeout = bodyTimeout = typeof args.timeout === 'string' ? parseInt(args.timeout) : args.timeout;
}
}
if (originalHeaders) {
// convert headers to lower-case
for (const name in originalHeaders) {
headers[name.toLowerCase()] = originalHeaders[name];
}
}
// hidden user-agent
const hiddenUserAgent = 'user-agent' in headers && !headers['user-agent'];
if (hiddenUserAgent) {
delete headers['user-agent'];
}
else if (!headers['user-agent']) {
// need to set user-agent
headers['user-agent'] = exports.HEADER_USER_AGENT;
}
// Alias to dataType = 'stream'
if (args.streaming || args.customResponse) {
args.dataType = 'stream';
}
if (args.dataType === 'json' && !headers.accept) {
headers.accept = 'application/json';
}
// gzip alias to compressed
if (args.gzip && args.compressed !== false) {
args.compressed = true;
}
if (args.compressed && !headers['accept-encoding']) {
headers['accept-encoding'] = 'gzip, br';
}
if (requestContext.retries > 0) {
headers['x-urllib-retry'] = `${requestContext.retries}/${args.retry}`;
}
if (requestContext.socketErrorRetries > 0) {
headers['x-urllib-retry-on-socket-error'] = `${requestContext.socketErrorRetries}/${args.socketErrorRetry}`;
}
if (args.auth && !headers.authorization) {
headers.authorization = `Basic ${Buffer.from(args.auth).toString('base64')}`;
}
// streaming request should disable socketErrorRetry and retry
let isStreamingRequest = false;
let isStreamingResponse = false;
if (args.dataType === 'stream' || args.writeStream) {
isStreamingResponse = true;
}
let maxRedirects = args.maxRedirects ?? 10;
try {
const requestOptions = {
method,
// disable undici auto redirect handler
// maxRedirections: 0,
headersTimeout,
headers,
bodyTimeout,
opaque: internalOpaque,
dispatcher: args.dispatcher ?? this.#dispatcher,
signal: args.signal,
reset: false,
};
if (typeof args.highWaterMark === 'number') {
requestOptions.highWaterMark = args.highWaterMark;
}
if (typeof args.reset === 'boolean') {
requestOptions.reset = args.reset;
}
if (args.followRedirect === false) {
maxRedirects = 0;
}
const isGETOrHEAD = requestOptions.method === 'GET' || requestOptions.method === 'HEAD';
// alias to args.content
if (args.stream && !args.content) {
// convert old style stream to new stream
// https://nodejs.org/dist/latest-v18.x/docs/api/stream.html#readablewrapstream
if ((0, utils_js_1.isReadable)(args.stream) && !(args.stream instanceof node_stream_1.Readable)) {
debug('Request#%d convert old style stream to Readable', requestId);
args.stream = new node_stream_1.Readable().wrap(args.stream);
isStreamingRequest = true;
}
else if (args.stream instanceof formstream_1.default) {
debug('Request#%d convert formstream to Readable', requestId);
args.stream = new node_stream_1.Readable().wrap(args.stream);
isStreamingRequest = true;
}
args.content = args.stream;
}
if (args.files) {
if (isGETOrHEAD) {
requestOptions.method = 'POST';
}
const formData = new FormData_js_1.FormData();
const uploadFiles = [];
if (Array.isArray(args.files)) {
for (const [index, file] of args.files.entries()) {
const field = index === 0 ? 'file' : `file${index}`;
uploadFiles.push([field, file]);
}
}
else if (args.files instanceof node_stream_1.Readable || (0, utils_js_1.isReadable)(args.files)) {
uploadFiles.push(['file', args.files]);
}
else if (typeof args.files === 'string' || Buffer.isBuffer(args.files)) {
uploadFiles.push(['file', args.files]);
}
else if (typeof args.files === 'object') {
const files = args.files;
for (const field in files) {
// set custom fileName
const file = files[field];
uploadFiles.push([field, file, field]);
}
}
// set normal fields first
if (args.data) {
for (const field in args.data) {
formData.append(field, args.data[field]);
}
}
for (const [index, [field, file, customFileName]] of uploadFiles.entries()) {
let fileName = '';
let value;
if (typeof file === 'string') {
fileName = (0, node_path_1.basename)(file);
value = (0, node_fs_1.createReadStream)(file);
}
else if (Buffer.isBuffer(file)) {
fileName = customFileName || `bufferfile${index}`;
value = file;
}
else if (file instanceof node_stream_1.Readable || (0, utils_js_1.isReadable)(file)) {
fileName = getFileName(file) || customFileName || `streamfile${index}`;
isStreamingRequest = true;
value = file;
}
const mimeType = mime_types_1.default.lookup(fileName) || '';
formData.append(field, value, {
filename: fileName,
contentType: mimeType,
});
debug('formData append field: %s, mimeType: %s, fileName: %s', field, mimeType, fileName);
}
Object.assign(headers, formData.getHeaders());
requestOptions.body = formData;
}
else if (args.content) {
if (!isGETOrHEAD) {
// handle content
requestOptions.body = args.content;
if (args.contentType) {
headers['content-type'] = args.contentType;
}
else if (typeof args.content === 'string' && !headers['content-type']) {
headers['content-type'] = 'text/plain;charset=UTF-8';
}
isStreamingRequest = (0, utils_js_1.isReadable)(args.content);
}
}
else if (args.data) {
const isStringOrBufferOrReadable = typeof args.data === 'string'
|| Buffer.isBuffer(args.data)
|| (0, utils_js_1.isReadable)(args.data);
if (isGETOrHEAD) {
if (!isStringOrBufferOrReadable) {
let query;
if (args.nestedQuerystring) {
query = qs_1.default.stringify(args.data);
}
else {
query = node_querystring_1.default.stringify(args.data);
}
// reset the requestUrl
const href = requestUrl.href;
requestUrl = new URL(href + (href.includes('?') ? '&' : '?') + query);
}
}
else {
if (isStringOrBufferOrReadable) {
requestOptions.body = args.data;
isStreamingRequest = (0, utils_js_1.isReadable)(args.data);
}
else {
if (args.contentType === 'json'
|| args.contentType === 'application/json'
|| headers['content-type']?.startsWith('application/json')) {
requestOptions.body = JSON.stringify(args.data);
if (!headers['content-type']) {
headers['content-type'] = 'application/json';
}
}
else {
headers['content-type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
if (args.nestedQuerystring) {
requestOptions.body = qs_1.default.stringify(args.data);
}
else {
requestOptions.body = new URLSearchParams(args.data).toString();
}
}
}
}
}
if (isStreamingRequest) {
args.retry = 0;
args.socketErrorRetry = 0;
maxRedirects = 0;
}
if (isStreamingResponse) {
args.retry = 0;
args.socketErrorRetry = 0;
}
debug('Request#%d %s %s, headers: %j, headersTimeout: %s, bodyTimeout: %s, isStreamingRequest: %s, isStreamingResponse: %s, maxRedirections: %s, redirects: %s', requestId, requestOptions.method, requestUrl.href, headers, headersTimeout, bodyTimeout, isStreamingRequest, isStreamingResponse, maxRedirects, requestContext.redirects);
requestOptions.headers = headers;
exports.channels.request.publish({
request: reqMeta,
});
if (this.listenerCount('request') > 0) {
this.emit('request', reqMeta);
}
let response = await (0, undici_1.request)(requestUrl, requestOptions);
if (response.statusCode === 401 && (response.headers['www-authenticate'] || response.headers['x-www-authenticate']) &&
!requestOptions.headers.authorization && args.digestAuth) {
// handle digest auth
const authenticateHeaders = response.headers['www-authenticate'] ?? response.headers['x-www-authenticate'];
const authenticate = Array.isArray(authenticateHeaders)
? authenticateHeaders.find(authHeader => authHeader.startsWith('Digest '))
: authenticateHeaders;
if (authenticate && authenticate.startsWith('Digest ')) {
debug('Request#%d %s: got digest auth header WWW-Authenticate: %s', requestId, requestUrl.href, authenticate);
requestOptions.headers.authorization = (0, utils_js_1.digestAuthHeader)(requestOptions.method, `${requestUrl.pathname}${requestUrl.search}`, authenticate, args.digestAuth);
debug('Request#%d %s: auth with digest header: %s', requestId, url, requestOptions.headers.authorization);
if (Array.isArray(response.headers['set-cookie'])) {
// FIXME: merge exists cookie header
requestOptions.headers.cookie = response.headers['set-cookie'].join(';');
}
// Ensure the previous response is consumed as we re-use the same variable
await response.body.arrayBuffer();
response = await (0, undici_1.request)(requestUrl, requestOptions);
}
}
const contentEncoding = response.headers['content-encoding'];
const isCompressedContent = contentEncoding === 'gzip' || contentEncoding === 'br';
res.headers = response.headers;
res.status = res.statusCode = response.statusCode;
res.statusMessage = res.statusText = node_http_1.STATUS_CODES[res.status] || '';
if (res.headers['content-length']) {
res.size = parseInt(res.headers['content-length']);
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections
if (RedirectStatusCodes.includes(res.statusCode) && maxRedirects > 0 && requestContext.redirects < maxRedirects) {
if (res.headers.location) {
requestContext.redirects++;
const nextUrl = new URL(res.headers.location, requestUrl.href);
// Ensure the response is consumed
await response.body.arrayBuffer();
debug('Request#%d got response, status: %s, headers: %j, timing: %j, redirect to %s', requestId, res.status, res.headers, res.timing, nextUrl.href);
return await this.#requestInternal(nextUrl.href, options, requestContext);
}
}
let data = null;
if (args.dataType === 'stream') {
// only auto decompress on request args.compressed = true
if (args.compressed === true && isCompressedContent) {
// gzip or br
const decoder = contentEncoding === 'gzip' ? (0, node_zlib_1.createGunzip)() : (0, node_zlib_1.createBrotliDecompress)();
res = Object.assign((0, node_stream_1.pipeline)(response.body, decoder, noop), res);
}
else {
res = Object.assign(response.body, res);
}
}
else if (args.writeStream) {
if (args.compressed === true && isCompressedContent) {
const decoder = contentEncoding === 'gzip' ? (0, node_zlib_1.createGunzip)() : (0, node_zlib_1.createBrotliDecompress)();
await (0, promises_1.pipeline)(response.body, decoder, args.writeStream);
}
else {
await (0, promises_1.pipeline)(response.body, args.writeStream);
}
}
else {
// buffer
data = Buffer.from(await response.body.arrayBuffer());
if (isCompressedContent && data.length > 0) {
try {
data = contentEncoding === 'gzip' ? (0, node_zlib_1.gunzipSync)(data) : (0, node_zlib_1.brotliDecompressSync)(data);
}
catch (err) {
if (err.name === 'Error') {
err.name = 'UnzipError';
}
throw err;
}
}
if (args.dataType === 'text' || args.dataType === 'html') {
data = data.toString();
}
else if (args.dataType === 'json') {
if (data.length === 0) {
data = null;
}
else {
data = (0, utils_js_1.parseJSON)(data.toString(), args.fixJSONCtlChars);
}
}
}
res.rt = (0, utils_js_1.performanceTime)(requestStartTime);
// get real socket info from internalOpaque
(0, utils_js_1.updateSocketInfo)(socketInfo, internalOpaque);
const clientResponse = {
opaque: originalOpaque,
data,
status: res.status,
statusCode: res.status,
statusText: res.statusText,
headers: res.headers,
url: requestUrl.href,
redirected: requestContext.history.length > 1,
requestUrls: res.requestUrls,
res,
};
debug('Request#%d got response, status: %s, headers: %j, timing: %j, socket: %j', requestId, res.status, res.headers, res.timing, res.socket);
if (args.retry > 0 && requestContext.retries < args.retry) {
const isRetry = args.isRetry ?? defaultIsRetry;
if (isRetry(clientResponse)) {
if (args.retryDelay) {
await (0, promises_2.setTimeout)(args.retryDelay);
}
requestContext.retries++;
return await this.#requestInternal(url, options, requestContext);
}
}
exports.channels.response.publish({
request: reqMeta,
response: res,
});
if (this.listenerCount('response') > 0) {
this.emit('response', {
requestId,
error: null,
ctx: args.ctx,
req: {
...reqMeta,
options: args,
},
res,
});
}
return clientResponse;
}
catch (rawError) {
debug('Request#%d throw error: %s, socketErrorRetry: %s, socketErrorRetries: %s', requestId, rawError, args.socketErrorRetry, requestContext.socketErrorRetries);
let err = rawError;
if (err.name === 'HeadersTimeoutError') {
err = new HttpClientError_js_1.HttpClientRequestTimeoutError(headersTimeout, { cause: err });
}
else if (err.name === 'BodyTimeoutError') {
err = new HttpClientError_js_1.HttpClientRequestTimeoutError(bodyTimeout, { cause: err });
}
else if (err.name === 'InformationalError' && err.message.includes('stream timeout')) {
err = new HttpClientError_js_1.HttpClientRequestTimeoutError(bodyTimeout, { cause: err });
}
else if (err.code === 'UND_ERR_CONNECT_TIMEOUT') {
err = new HttpClientError_js_1.HttpClientConnectTimeoutError(err.message, err.code, { cause: err });
}
else if (err.code === 'UND_ERR_SOCKET' || err.code === 'ECONNRESET') {
// auto retry on socket error, https://github.com/node-modules/urllib/issues/454
if (args.socketErrorRetry > 0 && requestContext.socketErrorRetries < args.socketErrorRetry) {
requestContext.socketErrorRetries++;
debug('Request#%d retry on socket error, socketErrorRetries: %d', requestId, requestContext.socketErrorRetries);
return await this.#requestInternal(url, options, requestContext);
}
}
err.opaque = originalOpaque;
err.status = res.status;
err.headers = res.headers;
err.res = res;
if (err.socket) {
// store rawSocket
err._rawSocket = err.socket;
}
err.socket = socketInfo;
res.rt = (0, utils_js_1.performanceTime)(requestStartTime);
(0, utils_js_1.updateSocketInfo)(socketInfo, internalOpaque, rawError);
exports.channels.response.publish({
request: reqMeta,
response: res,
error: err,
});
if (this.listenerCount('response') > 0) {
this.emit('response', {
requestId,
error: err,
ctx: args.ctx,
req: {
...reqMeta,
options: args,
},
res,
});
}
throw err;
}
}
}
exports.HttpClient = HttpClient;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSHR0cENsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9IdHRwQ2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBLHdGQUEwRDtBQUMxRCw2Q0FBMkM7QUFFM0MseUNBQXlDO0FBQ3pDLHlDQUFxQztBQUNyQyx5Q0FLbUI7QUFDbkIsNkNBQWlEO0FBQ2pELG1EQUFtRTtBQUNuRSx5Q0FBcUM7QUFDckMscUNBQTJDO0FBQzNDLHVDQUErQztBQUMvQyxxREFBOEM7QUFDOUMsd0VBQTJDO0FBQzNDLG1EQUEyRDtBQUMzRCxtQ0FNZ0I7QUFDaEIsNkRBQTZEO0FBQzdELGFBQWE7QUFDYiw0RUFBdUQ7QUFDdkQsNERBQThCO0FBQzlCLDRDQUFvQjtBQUNwQix1Q0FBdUM7QUFDdkMsNERBQW9DO0FBQ3BDLCtDQUF5QztBQUN6QyxpREFBaUU7QUFJakUseUNBQWtIO0FBQ2xILDhEQUFtQztBQUNuQyxtRUFBaUU7QUFDakUsNkRBQW9HO0FBUXZGLFFBQUEsUUFBUSxHQUFHLGVBQWUsQ0FBQztBQTBCeEMsU0FBUyxJQUFJO0lBQ1gsT0FBTztBQUNULENBQUM7QUFFRCxNQUFNLEtBQUssR0FBRyxJQUFBLG9CQUFRLEVBQUMsbUJBQW1CLENBQUMsQ0FBQztBQXdDL0IsUUFBQSxPQUFPLEdBQUcsU0FBUyxDQUFDO0FBQ2pDLG9EQUFvRDtBQUN2QyxRQUFBLGlCQUFpQixHQUM1QixlQUFlLGVBQU8sWUFBWSxPQUFPLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsS0FBSyxPQUFPLENBQUMsUUFBUSxLQUFLLE9BQU8sQ0FBQyxJQUFJLEdBQUcsQ0FBQztBQUUxRyxTQUFTLFdBQVcsQ0FBQyxNQUFnQjtJQUNuQyxNQUFNLFFBQVEsR0FBWSxNQUFjLENBQUMsSUFBSSxDQUFDO0lBQzlDLElBQUksUUFBUSxFQUFFLENBQUM7UUFDYixPQUFPLElBQUEsb0JBQVEsRUFBQyxRQUFRLENBQUMsQ0FBQztJQUM1QixDQUFDO0lBQ0QsT0FBTyxFQUFFLENBQUM7QUFDWixDQUFDO0FBRUQsU0FBUyxjQUFjLENBQUMsUUFBNEI7SUFDbEQsT0FBTyxRQUFRLENBQUMsTUFBTSxJQUFJLEdBQUcsQ0FBQztBQUNoQyxDQUFDO0FBVVksUUFBQSxRQUFRLEdBQUc7SUFDdEIsT0FBTyxFQUFFLGtDQUFrQixDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQztJQUNyRCxRQUFRLEVBQUUsa0NBQWtCLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDO0lBQ3ZELFlBQVksRUFBRSxrQ0FBa0IsQ0FBQyxPQUFPLENBQUMsc0JBQXNCLENBQUM7SUFDaEUsYUFBYSxFQUFFLGtDQUFrQixDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsQ0FBQztDQUNuRSxDQUFDO0FBK0JGLGlFQUFpRTtBQUNqRSxNQUFNLG1CQUFtQixHQUFHO0lBQzFCLEdBQUcsRUFBRSxvQkFBb0I7SUFDekIsR0FBRyxFQUFFLFFBQVE7SUFDYixHQUFHLEVBQUUsWUFBWTtJQUNqQixHQUFHLEVBQUUscUJBQXFCO0lBQzFCLEdBQUcsRUFBRSxxQkFBcUI7Q0FDM0IsQ0FBQztBQUVGLE1BQWEsVUFBVyxTQUFRLDBCQUFZO0lBQzFDLFlBQVksQ0FBa0I7SUFDOUIsV0FBVyxDQUFjO0lBRXpCLFlBQVksYUFBNkI7UUFDdkMsS0FBSyxFQUFFLENBQUM7UUFDUixJQUFJLENBQUMsWUFBWSxHQUFHLGFBQWEsRUFBRSxXQUFXLENBQUM7UUFDL0MsSUFBSSxhQUFhLEVBQUUsTUFBTSxJQUFJLGFBQWEsRUFBRSxZQUFZLEVBQUUsQ0FBQztZQUN6RCxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksd0JBQVMsQ0FBQztnQkFDL0IsTUFBTSxFQUFFLGFBQWEsQ0FBQyxNQUFNO2dCQUM1QixZQUFZLEVBQUUsYUFBYSxDQUFDLFlBQVk7Z0JBQ3hDLE9BQU8sRUFBRSxhQUFhLENBQUMsT0FBTztnQkFDOUIsT0FBTyxFQUFFLGFBQWEsQ0FBQyxPQUFPO2FBQy9CLENBQUMsQ0FBQztRQUNMLENBQUM7YUFBTSxJQUFJLGFBQWEsRUFBRSxPQUFPLEVBQUUsQ0FBQztZQUNsQyxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksY0FBSyxDQUFDO2dCQUMzQixPQUFPLEVBQUUsYUFBYSxDQUFDLE9BQU87Z0JBQzlCLE9BQU8sRUFBRSxhQUFhLENBQUMsT0FBTzthQUMvQixDQUFDLENBQUM7UUFDTCxDQUFDO2FBQU0sSUFBSSxhQUFhLEVBQUUsT0FBTyxFQUFFLENBQUM7WUFDbEMsZ0JBQWdCO1lBQ2hCLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxjQUFLLENBQUM7Z0JBQzNCLE9BQU8sRUFBRSxhQUFhLENBQUMsT0FBTzthQUMvQixDQUFDLENBQUM7UUFDTCxDQUFDO1FBQ0QsSUFBQSw4Q0FBc0IsR0FBRSxDQUFDO0lBQzNCLENBQUM7SUFFRCxhQUFhO1FBQ1gsT0FBTyxJQUFJLENBQUMsV0FBVyxJQUFJLElBQUEsNEJBQW1CLEdBQUUsQ0FBQztJQUNuRCxDQUFDO0lBRUQsYUFBYSxDQUFDLFVBQXNCO1FBQ2xDLElBQUksQ0FBQyxXQUFXLEdBQUcsVUFBVSxDQUFDO0lBQ2hDLENBQUM7SUFFRCxzQkFBc0I7UUFDcEIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ25DLDBCQUEwQjtRQUMxQixNQUFNLE9BQU8sR0FBMkMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsb0JBQWEsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNuRyxNQUFNLFlBQVksR0FBNkIsRUFBRSxDQUFDO1FBQ2xELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNiLE9BQU8sWUFBWSxDQUFDO1FBQ3RCLENBQUM7UUFDRCxLQUFLLE1BQU0sQ0FBRSxHQUFHLEVBQUUsR0FBRyxDQUFFLElBQUksT0FBTyxFQUFFLENBQUM7WUFDbkMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxPQUFPLEdBQUcsQ0FBQyxLQUFLLEtBQUssVUFBVSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBNkMsQ0FBQztZQUMvRyx5REFBeUQ7WUFDekQsTUFBTSxLQUFLLEdBQUcsSUFBSSxFQUFFLEtBQUssSUFBSSxJQUFJLEVBQUUsVUFBVSxFQUFFLEtBQUssQ0FBQztZQUNyRCxJQUFJLENBQUMsS0FBSztnQkFBRSxTQUFTO1lBRXJCLFlBQVksQ0FBQyxHQUFHLENBQUMsR0FBRztnQkFDbEIsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO2dCQUMxQixJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUk7Z0JBQ2hCLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTztnQkFDdEIsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNO2dCQUNwQixPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87Z0JBQ3RCLElBQUksRUFBRSxLQUFLLENBQUMsSUFBSTthQUNFLENBQUM7UUFDdkIsQ0FBQztRQUNELE9BQU8sWUFBWSxDQUFDO0lBQ3RCLENBQUM7SUFFRCxLQUFLLENBQUMsT0FBTyxDQUFVLEdBQWUsRUFBRSxPQUF3QjtRQUM5RCxPQUFPLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFJLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUN0RCxDQUFDO0lBRUQsa0VBQWtFO0lBQ2xFLEtBQUssQ0FBQyxJQUFJLENBQVUsR0FBZSxFQUFFLE9BQXdCO1FBQzNELE9BQU8sTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFJLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUM3QyxDQUFDO0lBRUQsS0FBSyxDQUFDLGdCQUFnQixDQUFJLEdBQWUsRUFBRSxPQUF3QixFQUFFLGNBQStCO1FBQ2xHLE1BQU0sU0FBUyxHQUFHLElBQUEsbUJBQVEsRUFBQyxtQkFBbUIsQ0FBQyxDQUFDO1FBQ2hELElBQUksVUFBZSxDQUFDO1FBQ3BCLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDNUIsSUFBSSxDQUFDLGdCQUFRLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3hCLHNDQUFzQztnQkFDdEMsR0FBRyxHQUFHLFNBQVMsR0FBRyxHQUFHLENBQUM7WUFDeEIsQ0FBQztZQUNELFVBQVUsR0FBRyxJQUFJLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUM1QixDQUFDO2FBQU0sQ0FBQztZQUNOLElBQUksQ0FBQyxHQUFHLENBQUMsWUFBWSxFQUFFLENBQUM7Z0JBQ3RCLDZDQUE2QztnQkFDN0MsVUFBVSxHQUFHLElBQUksR0FBRyxDQUFDLElBQUEsaUJBQVMsRUFBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQ3ZDLENBQUM7aUJBQU0sQ0FBQztnQkFDTixnREFBZ0Q7Z0JBQ2hELFVBQVUsR0FBRyxJQUFJLEdBQUcsQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztZQUN2QyxDQUFDO1FBQ0gsQ0FBQztRQUVELE1BQU0sTUFBTSxHQUFHLENBQUMsT0FBTyxFQUFFLElBQUksSUFBSSxPQUFPLEVBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyxDQUFDLFdBQVcsRUFBZ0IsQ0FBQztRQUN2RixNQUFNLGVBQWUsR0FBRyxPQUFPLEVBQUUsT0FBTyxDQUFDO1FBQ3pDLE1BQU0sT0FBTyxHQUF3QixFQUFFLENBQUM7UUFDeEMsTUFBTSxJQUFJLEdBQUc7WUFDWCxLQUFLLEVBQUUsQ0FBQztZQUNSLGdCQUFnQixFQUFFLENBQUM7WUFDbkIsTUFBTSxFQUFFLElBQUk7WUFDWixHQUFHLElBQUksQ0FBQyxZQUFZO1lBQ3BCLEdBQUcsT0FBTztZQUNWLCtFQUErRTtZQUMvRSxNQUFNO1lBQ04sT0FBTztTQUNSLENBQUM7UUFDRixjQUFjLEdBQUc7WUFDZixPQUFPLEVBQUUsQ0FBQztZQUNWLGtCQUFrQixFQUFFLENBQUM7WUFDckIsU0FBUyxFQUFFLENBQUM7WUFDWixPQUFPLEVBQUUsRUFBRTtZQUNYLEdBQUcsY0FBYztTQUNsQixDQUFDO1FBQ0YsSUFBSSxDQUFDLGNBQWMsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQ3JDLGNBQWMsQ0FBQyxnQkFBZ0IsR0FBRyw2QkFBVyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ3RELENBQUM7UUFDRCxjQUFjLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDN0MsTUFBTSxnQkFBZ0IsR0FBRyxjQUFjLENBQUMsZ0JBQWdCLENBQUM7UUFFekQsdUdBQXVHO1FBQ3ZHLE1BQU0sTUFBTSxHQUFHO1lBQ2Isa0JBQWtCO1lBQ2xCLE9BQU8sRUFBRSxDQUFDO1lBQ1Ysa0JBQWtCO1lBQ2xCLFNBQVMsRUFBRSxDQUFDO1lBQ1osbUJBQW1CO1lBQ25CLFNBQVMsRUFBRSxDQUFDO1lBQ1osdUJBQXVCO1lBQ3ZCLGtCQUFrQixFQUFFLENBQUM7WUFDckIsMkNBQTJDO1lBQzNDLFdBQVcsRUFBRSxDQUFDO1lBQ2QscUVBQXFFO1lBQ3JFLE9BQU8sRUFBRSxDQUFDO1lBQ1Ysb0RBQW9EO1lBQ3BELGVBQWUsRUFBRSxDQUFDO1NBQ25CLENBQUM7UUFDRixNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDO1FBQ25DLGtFQUFrRTtRQUNsRSxNQUFNLGNBQWMsR0FBRztZQUNyQixDQUFDLG9CQUFPLENBQUMsVUFBVSxDQUFDLEVBQUUsU0FBUztZQUMvQixDQUFDLG9CQUFPLENBQUMsaUJBQWlCLENBQUMsRUFBRSxnQkFBZ0I7WUFDN0MsQ0FBQyxvQkFBTyxDQUFDLG9CQUFvQixDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNO1lBQzdDLENBQUMsb0JBQU8sQ0FBQyxjQUFjLENBQUMsRUFBRSxNQUFNO1lBQ2hDLENBQUMsb0JBQU8sQ0FBQyxzQkFBc0IsQ0FBQyxFQUFFLGNBQWM7U0FDakQsQ0FBQztRQUNGLE1BQU0sT0FBTyxHQUFHO1lBQ2QsU0FBUztZQUNULEdBQUcsRUFBRSxVQUFVLENBQUMsSUFBSTtZQUNwQixJQUFJO1lBQ0osR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHO1lBQ2IsT0FBTyxFQUFFLGNBQWMsQ0FBQyxPQUFPO1NBQ2pCLENBQUM7UUFDakIsTUFBTSxVQUFVLEdBQWU7WUFDN0IsRUFBRSxFQUFFLENBQUM7WUFDTCxZQUFZLEVBQUUsRUFBRTtZQUNoQixTQUFTLEVBQUUsQ0FBQztZQUNaLGFBQWEsRUFBRSxFQUFFO1lBQ2pCLFVBQVUsRUFBRSxDQUFDO1lBQ2IsWUFBWSxFQUFFLEVBQUU7WUFDaEIsWUFBWSxFQUFFLENBQUM7WUFDZixTQUFTLEVBQUUsQ0FBQztZQUNaLGVBQWUsRUFBRSxDQUFDO1lBQ2xCLGdCQUFnQixFQUFFLENBQUM7U0FDcEIsQ0FBQztRQUNGLDJDQUEyQztRQUMzQyxNQUFNLFVBQVUsR0FBd0IsRUFBRSxDQUFDO1FBQzNDLElBQUksR0FBRyxHQUFHO1lBQ1IsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUNWLFVBQVUsRUFBRSxDQUFDLENBQUM7WUFDZCxVQUFVLEVBQUUsRUFBRTtZQUNkLGFBQWEsRUFBRSxFQUFFO1lBQ2pCLE9BQU8sRUFBRSxVQUFVO1lBQ25CLElBQUksRUFBRSxDQUFDO1lBQ1AsT0FBTyxFQUFFLEtBQUs7WUFDZCxFQUFFLEVBQUUsQ0FBQztZQUNMLGVBQWUsRUFBRSxJQUFJO1lBQ3JCLFdBQVcsRUFBRSxjQUFjLENBQUMsT0FBTztZQUNuQyxNQUFNO1lBQ04sTUFBTSxFQUFFLFVBQVU7WUFDbEIsT0FBTyxFQUFFLGNBQWMsQ0FBQyxPQUFPO1lBQy9CLGtCQUFrQixFQUFFLGNBQWMsQ0FBQyxrQkFBa0I7U0FDeEIsQ0FBQztRQUVoQyxJQUFJLGNBQWMsR0FBRyxJQUFJLENBQUM7UUFDMUIsSUFBSSxXQUFXLEdBQUcsSUFBSSxDQUFDO1FBQ3ZCLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2pCLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDaEMsY0FBYyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksY0FBYyxDQUFDO2dCQUNuRCxXQUFXLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxXQUFXLENBQUM7WUFDL0MsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLGlEQUFpRDtnQkFDakQsY0FBYyxHQUFHLFdBQVcsR0FBRyxPQUFPLElBQUksQ0FBQyxPQUFPLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDO1lBQzFHLENBQUM7UUFDSCxDQUFDO1FBQ0QsSUFBSSxlQUFlLEVBQUUsQ0FBQztZQUNwQixnQ0FBZ0M7WUFDaEMsS0FBSyxNQUFNLElBQUksSUFBSSxlQUFlLEVBQUUsQ0FBQztnQkFDbkMsT0FBTyxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQyxHQUFHLGVBQWUsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUN0RCxDQUFDO1FBQ0gsQ0FBQztRQUNELG9CQUFvQjtRQUNwQixNQUFNLGVBQWUsR0FBRyxZQUFZLElBQUksT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQzFFLElBQUksZUFBZSxFQUFFLENBQUM7WUFDcEIsT0FBTyxPQUFPLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDL0IsQ0FBQzthQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQztZQUNsQyx5QkFBeUI7WUFDekIsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUFHLHlCQUFpQixDQUFDO1FBQzVDLENBQUM7UUFDRCwrQkFBK0I7UUFDL0IsSUFBSSxJQUFJLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUMxQyxJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztRQUMzQixDQUFDO1FBQ0QsSUFBSSxJQUFJLENBQUMsUUFBUSxLQUFLLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNoRCxPQUFPLENBQUMsTUFBTSxHQUFHLGtCQUFrQixDQUFDO1FBQ3RDLENBQUM7UUFDRCwyQkFBMkI7UUFDM0IsSUFBSSxJQUFJLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxVQUFVLEtBQUssS0FBSyxFQUFFLENBQUM7WUFDM0MsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFDekIsQ0FBQztRQUNELElBQUksSUFBSSxDQUFDLFVBQVUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7WUFDbkQsT0FBTyxDQUFDLGlCQUFpQixDQUFDLEdBQUcsVUFBVSxDQUFDO1FBQzFDLENBQUM7UUFDRCxJQUFJLGNBQWMsQ0FBQyxPQUFPLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDL0IsT0FBTyxDQUFDLGdCQUFnQixDQUFDLEdBQUcsR0FBRyxjQUFjLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUN4RSxDQUFDO1FBQ0QsSUFBSSxjQUFjLENBQUMsa0JBQWtCLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDMUMsT0FBTyxDQUFDLGdDQUFnQyxDQUFDLEdBQUcsR0FBRyxjQUFjLENBQUMsa0JBQWtCLElBQUksSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFDOUcsQ0FBQztRQUNELElBQUksSUFBSSxDQUFDLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxhQUFhLEVBQUUsQ0FBQztZQUN4QyxPQUFPLENBQUMsYUFBYSxHQUFHLFNBQVMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7UUFDL0UsQ0FBQztRQUVELDhEQUE4RDtRQUM5RCxJQUFJLGtCQUFrQixHQUFHLEtBQUssQ0FBQztRQUMvQixJQUFJLG1CQUFtQixHQUFHLEtBQUssQ0FBQztRQUNoQyxJQUFJLElBQUksQ0FBQyxRQUFRLEtBQUssUUFBUSxJQUFJLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNuRCxtQkFBbUIsR0FBRyxJQUFJLENBQUM7UUFDN0IsQ0FBQztRQUVELElBQUksWUFBWSxHQUFHLElBQUksQ0FBQyxZQUFZLElBQUksRUFBRSxDQUFDO1FBRTNDLElBQUksQ0FBQztZQUNILE1BQU0sY0FBYyxHQUF5QjtnQkFDM0MsTUFBTTtnQkFDTix1Q0FBdUM7Z0JBQ3ZDLHNCQUFzQjtnQkFDdEIsY0FBYztnQkFDZCxPQUFPO2dCQUNQLFdBQVc7Z0JBQ1gsTUFBTSxFQUFFLGNBQWM7Z0JBQ3RCLFVBQVUsRUFBRSxJQUFJLENBQUMsVUFBVSxJQUFJLElBQUksQ0FBQyxXQUFXO2dCQUMvQyxNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07Z0JBQ25CLEtBQUssRUFBRSxLQUFLO2FBQ2IsQ0FBQztZQUNGLElBQUksT0FBTyxJQUFJLENBQUMsYUFBYSxLQUFLLFFBQVEsRUFBRSxDQUFDO2dCQUMzQyxjQUFjLENBQUMsYUFBYSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUM7WUFDcEQsQ0FBQztZQUNELElBQUksT0FBTyxJQUFJLENBQUMsS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO2dCQUNwQyxjQUFjLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUM7WUFDcEMsQ0FBQztZQUNELElBQUksSUFBSSxDQUFDLGNBQWMsS0FBSyxLQUFLLEVBQUUsQ0FBQztnQkFDbEMsWUFBWSxHQUFHLENBQUMsQ0FBQztZQUNuQixDQUFDO1lBRUQsTUFBTSxXQUFXLEdBQUcsY0FBYyxDQUFDLE1BQU0sS0FBSyxLQUFLLElBQUksY0FBYyxDQUFDLE1BQU0sS0FBSyxNQUFNLENBQUM7WUFDeEYsd0JBQXdCO1lBQ3hCLElBQUksSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDakMseUNBQXlDO2dCQUN6QywrRUFBK0U7Z0JBQy9FLElBQUksSUFBQSxxQkFBVSxFQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sWUFBWSxzQkFBUSxDQUFDLEVBQUUsQ0FBQztvQkFDbEUsS0FBSyxDQUFDLGlEQUFpRCxFQUFFLFNBQVMsQ0FBQyxDQUFDO29CQUNwRSxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksc0JBQVEsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7b0JBQy9DLGtCQUFrQixHQUFHLElBQUksQ0FBQztnQkFDNUIsQ0FBQztxQkFBTSxJQUFJLElBQUksQ0FBQyxNQUFNLFlBQVksb0JBQVUsRUFBRSxDQUFDO29CQUM3QyxLQUFLLENBQUMsMkNBQTJDLEVBQUUsU0FBUyxDQUFDLENBQUM7b0JBQzlELElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxzQkFBUSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztvQkFDL0Msa0JBQWtCLEdBQUcsSUFBSSxDQUFDO2dCQUM1QixDQUFDO2dCQUNELElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztZQUM3QixDQUFDO1lBRUQsSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQ2YsSUFBSSxXQUFXLEVBQUUsQ0FBQztvQkFDaEIsY0FBYyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7Z0JBQ2pDLENBQUM7Z0JBQ0QsTUFBTSxRQUFRLEdBQUcsSUFBSSxzQkFBUSxFQUFFLENBQUM7Z0JBQ2hDLE1BQU0sV0FBVyxHQUFvRCxFQUFFLENBQUM7Z0JBQ3hFLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztvQkFDOUIsS0FBSyxNQUFNLENBQUUsS0FBSyxFQUFFLElBQUksQ0FBRSxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQzt3QkFDbkQsTUFBTSxLQUFLLEdBQUcsS0FBSyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxPQUFPLEtBQUssRUFBRSxDQUFDO3dCQUNwRCxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUUsS0FBSyxFQUFFLElBQUksQ0FBRSxDQUFDLENBQUM7b0JBQ3BDLENBQUM7Z0JBQ0gsQ0FBQztxQkFBTSxJQUFJLElBQUksQ0FBQyxLQUFLLFlBQVksc0JBQVEsSUFBSSxJQUFBLHFCQUFVLEVBQUMsSUFBSSxDQUFDLEtBQVksQ0FBQyxFQUFFLENBQUM7b0JBQzNFLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBRSxNQUFNLEVBQUUsSUFBSSxDQUFDLEtBQWlCLENBQUUsQ0FBQyxDQUFDO2dCQUN2RCxDQUFDO3FCQUFNLElBQUksT0FBTyxJQUFJLENBQUMsS0FBSyxLQUFLLFFBQVEsSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO29CQUN6RSxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUUsQ0FBQyxDQUFDO2dCQUMzQyxDQUFDO3FCQUFNLElBQUksT0FBTyxJQUFJLENBQUMsS0FBSyxLQUFLLFFBQVEsRUFBRSxDQUFDO29CQUMxQyxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBbUQsQ0FBQztvQkFDdkUsS0FBSyxNQUFNLEtBQUssSUFBSSxLQUFLLEVBQUUsQ0FBQzt3QkFDMUIsc0JBQXNCO3dCQUN0QixNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7d0JBQzFCLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssQ0FBRSxDQUFDLENBQUM7b0JBQzNDLENBQUM7Z0JBQ0gsQ0FBQztnQkFDRCwwQkFBMEI7Z0JBQzFCLElBQUksSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO29CQUNkLEtBQUssTUFBTSxLQUFLLElBQUksSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO3dCQUM5QixRQUFRLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7b0JBQzNDLENBQUM7Z0JBQ0gsQ0FBQztnQkFDRCxLQUFLLE1BQU0sQ0FBRSxLQUFLLEVBQUUsQ0FBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLGNBQWMsQ0FBRSxDQUFDLElBQUksV0FBVyxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUM7b0JBQzlFLElBQUksUUFBUSxHQUFHLEVBQUUsQ0FBQztvQkFDbEIsSUFBSSxLQUFVLENBQUM7b0JBQ2YsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLEVBQUUsQ0FBQzt3QkFDN0IsUUFBUSxHQUFHLElBQUEsb0JBQVEsRUFBQyxJQUFJLENBQUMsQ0FBQzt3QkFDMUIsS0FBSyxHQUFHLElBQUEsMEJBQWdCLEVBQUMsSUFBSSxDQUFDLENBQUM7b0JBQ2pDLENBQUM7eUJBQU0sSUFBSSxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7d0JBQ2pDLFFBQVEsR0FBRyxjQUFjLElBQUksYUFBYSxLQUFLLEVBQUUsQ0FBQzt3QkFDbEQsS0FBSyxHQUFHLElBQUksQ0FBQztvQkFDZixDQUFDO3lCQUFNLElBQUksSUFBSSxZQUFZLHNCQUFRLElBQUksSUFBQSxxQkFBVSxFQUFDLElBQVcsQ0FBQyxFQUFFLENBQUM7d0JBQy9ELFFBQVEsR0FBRyxXQUFXLENBQUMsSUFBSSxDQUFDLElBQUksY0FBYyxJQUFJLGFBQWEsS0FBSyxFQUFFLENBQUM7d0JBQ3ZFLGtCQUFrQixHQUFHLElBQUksQ0FBQzt3QkFDMUIsS0FBSyxHQUFHLElBQUksQ0FBQztvQkFDZixDQUFDO29CQUNELE1BQU0sUUFBUSxHQUFHLG9CQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztvQkFDN0MsUUFBUSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFO3dCQUM1QixRQUFRLEVBQUUsUUFBUTt3QkFDbEIsV0FBVyxFQUFFLFFBQVE7cUJBQ3RCLENBQUMsQ0FBQztvQkFDSCxLQUFLLENBQUMsdURBQXVELEVBQzNELEtBQUssRUFBRSxRQUFRLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQy9CLENBQUM7Z0JBQ0QsTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUM7Z0JBQzlDLGNBQWMsQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDO1lBQ2pDLENBQUM7aUJBQU0sSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ3hCLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztvQkFDakIsaUJBQWlCO29CQUNqQixjQUFjLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUM7b0JBQ25DLElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO3dCQUNyQixPQUFPLENBQUMsY0FBYyxDQUFDLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQztvQkFDN0MsQ0FBQzt5QkFBTSxJQUFJLE9BQU8sSUFBSSxDQUFDLE9BQU8sS0FBSyxRQUFRLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQzt3QkFDeEUsT0FBTyxDQUFDLGNBQWMsQ0FBQyxHQUFHLDBCQUEwQixDQUFDO29CQUN2RCxDQUFDO29CQUNELGtCQUFrQixHQUFHLElBQUEscUJBQVUsRUFBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7Z0JBQ2hELENBQUM7WUFDSCxDQUFDO2lCQUFNLElBQUksSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUNyQixNQUFNLDBCQUEwQixHQUFHLE9BQU8sSUFBSSxDQUFDLElBQUksS0FBSyxRQUFRO3VCQUMzRCxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7dUJBQzFCLElBQUEscUJBQVUsRUFBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQzNCLElBQUksV0FBVyxFQUFFLENBQUM7b0JBQ2hCLElBQUksQ0FBQywwQkFBMEIsRUFBRSxDQUFDO3dCQUNoQyxJQUFJLEtBQWEsQ0FBQzt3QkFDbEIsSUFBSSxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQzs0QkFDM0IsS0FBSyxHQUFHLFlBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO3dCQUNsQyxDQUFDOzZCQUFNLENBQUM7NEJBQ04sS0FBSyxHQUFHLDBCQUFXLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQzt3QkFDM0MsQ0FBQzt3QkFDRCx1QkFBdUI7d0JBQ3ZCLE1BQU0sSUFBSSxHQUFHLFVBQVUsQ0FBQyxJQUFJLENBQUM7d0JBQzdCLFVBQVUsR0FBRyxJQUFJLEdBQUcsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEtBQUssQ0FBQyxDQUFDO29CQUN4RSxDQUFDO2dCQUNILENBQUM7cUJBQU0sQ0FBQztvQkFDTixJQUFJLDBCQUEwQixFQUFFLENBQUM7d0JBQy9CLGNBQWMsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQzt3QkFDaEMsa0JBQWtCLEdBQUcsSUFBQSxxQkFBVSxFQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztvQkFDN0MsQ0FBQzt5QkFBTSxDQUFDO3dCQUNOLElBQUksSUFBSSxDQUFDLFdBQVcsS0FBSyxNQUFNOytCQUMxQixJQUFJLENBQUMsV0FBVyxLQUFLLGtCQUFrQjsrQkFDdkMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxFQUFFLFVBQVUsQ0FBQyxrQkFBa0IsQ0FBQyxFQUFFLENBQUM7NEJBQzdELGNBQWMsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7NEJBQ2hELElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztnQ0FDN0IsT0FBTyxDQUFDLGNBQWMsQ0FBQyxHQUFHLGtCQUFrQixDQUFDOzRCQUMvQyxDQUFDO3dCQUNILENBQUM7NkJBQU0sQ0FBQzs0QkFDTixPQUFPLENBQUMsY0FBYyxDQUFDLEdBQUcsaURBQWlELENBQUM7NEJBQzVFLElBQUksSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7Z0NBQzNCLGNBQWMsQ0FBQyxJQUFJLEdBQUcsWUFBRSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7NEJBQ2hELENBQUM7aUNBQU0sQ0FBQztnQ0FDTixjQUFjLENBQUMsSUFBSSxHQUFHLElBQUksZUFBZSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQzs0QkFDbEUsQ0FBQzt3QkFDSCxDQUFDO29CQUNILENBQUM7Z0JBQ0gsQ0FBQztZQUNILENBQUM7WUFDRCxJQUFJLGtCQUFrQixFQUFFLENBQUM7Z0JBQ3ZCLElBQUksQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDO2dCQUNmLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxDQUFDLENBQUM7Z0JBQzFCLFlBQVksR0FBRyxDQUFDLENBQUM7WUFDbkIsQ0FBQztZQUNELElBQUksbUJBQW1CLEVBQUUsQ0FBQztnQkFDeEIsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUM7Z0JBQ2YsSUFBSSxDQUFDLGdCQUFnQixHQUFHLENBQUMsQ0FBQztZQUM1QixDQUFDO1lBRUQsS0FBSyxDQUFDLHlKQUF5SixFQUM3SixTQUFTLEVBQUUsY0FBYyxDQUFDLE1BQU0sRUFBRSxVQUFVLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxjQUFjLEVBQUUsV0FBVyxFQUFFLGtCQUFrQixFQUFFLG1CQUFtQixFQUFFLFlBQVksRUFBRSxjQUFjLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDNUssY0FBYyxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7WUFDakMsZ0JBQVEsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDO2dCQUN2QixPQUFPLEVBQUUsT0FBTzthQUNZLENBQUMsQ0FBQztZQUNoQyxJQUFJLElBQUksQ0FBQyxhQUFhLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3RDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQ2hDLENBQUM7WUFFRCxJQUFJLFFBQVEsR0FBRyxNQUFNLElBQUEsZ0JBQWEsRUFBQyxVQUFVLEVBQUUsY0FBcUMsQ0FBQyxDQUFDO1lBQ3RGLElBQUksUUFBUSxDQUFDLFVBQVUsS0FBSyxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDLElBQUksUUFBUSxDQUFDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO2dCQUNqSCxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsYUFBYSxJQUFJLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDM0QscUJBQXFCO2dCQUNyQixNQUFNLG1CQUFtQixHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsa0JBQWtCLENBQUMsSUFBSSxRQUFRLENBQUMsT0FBTyxDQUFDLG9CQUFvQixDQUFDLENBQUM7Z0JBQzNHLE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQUM7b0JBQ3JELENBQUMsQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxDQUFDO29CQUMxRSxDQUFDLENBQUMsbUJBQW1CLENBQUM7Z0JBQ3hCLElBQUksWUFBWSxJQUFJLFlBQVksQ0FBQyxVQUFVLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztvQkFDdkQsS0FBSyxDQUFDLDREQUE0RCxFQUFFLFNBQVMsRUFBRSxVQUFVLENBQUMsSUFBSSxFQUFFLFlBQVksQ0FBQyxDQUFDO29CQUM5RyxjQUFjLENBQUMsT0FBTyxDQUFDLGFBQWEsR0FBRyxJQUFBLDJCQUFnQixFQUFDLGNBQWMsQ0FBQyxNQUFPLEVBQzVFLEdBQUcsVUFBVSxDQUFDLFFBQVEsR0FBRyxVQUFVLENBQUMsTUFBTSxFQUFFLEVBQUUsWUFBWSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztvQkFDL0UsS0FBSyxD