urllib
Version:
Help in opening URLs (mostly HTTP) in a complex world — basic and digest authentication, redirections, timeout and more. Base undici API.
648 lines • 58.4 kB
JavaScript
import diagnosticsChannel from 'node:diagnostics_channel';
import { EventEmitter } from 'node:events';
import { STATUS_CODES } from 'node:http';
import { debuglog } from 'node:util';
import { createGunzip, createBrotliDecompress, gunzipSync, brotliDecompressSync, } from 'node:zlib';
import { Readable, pipeline } from 'node:stream';
import { pipeline as pipelinePromise } from 'node:stream/promises';
import { basename } from 'node:path';
import { createReadStream } from 'node:fs';
import { format as urlFormat } from 'node:url';
import { performance } from 'node:perf_hooks';
import querystring from 'node:querystring';
import { setTimeout as sleep } from 'node:timers/promises';
import { request as undiciRequest, Agent, getGlobalDispatcher, } from 'undici';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import undiciSymbols from 'undici/lib/core/symbols.js';
import mime from 'mime-types';
import qs from 'qs';
// Compatible with old style formstream
import FormStream from 'formstream';
import { FormData } from './FormData.js';
import { HttpAgent } from './HttpAgent.js';
import { parseJSON, digestAuthHeader, globalId, performanceTime, isReadable, updateSocketInfo } from './utils.js';
import symbols from './symbols.js';
import { initDiagnosticsChannel } from './diagnosticsChannel.js';
import { HttpClientConnectTimeoutError, HttpClientRequestTimeoutError } from './HttpClientError.js';
export const PROTO_RE = /^https?:\/\//i;
function noop() {
// noop
}
const debug = debuglog('urllib:HttpClient');
export const VERSION = '4.8.0';
// 'node-urllib/4.0.0 Node.js/18.19.0 (darwin; x64)'
export const HEADER_USER_AGENT = `node-urllib/${VERSION} Node.js/${process.version.substring(1)} (${process.platform}; ${process.arch})`;
function getFileName(stream) {
const filePath = stream.path;
if (filePath) {
return basename(filePath);
}
return '';
}
function defaultIsRetry(response) {
return response.status >= 500;
}
export const channels = {
request: diagnosticsChannel.channel('urllib:request'),
response: diagnosticsChannel.channel('urllib:response'),
fetchRequest: diagnosticsChannel.channel('urllib:fetch:request'),
fetchResponse: diagnosticsChannel.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
];
export class HttpClient extends EventEmitter {
#defaultArgs;
#dispatcher;
constructor(clientOptions) {
super();
this.#defaultArgs = clientOptions?.defaultArgs;
if (clientOptions?.lookup || clientOptions?.checkAddress) {
this.#dispatcher = new HttpAgent({
lookup: clientOptions.lookup,
checkAddress: clientOptions.checkAddress,
connect: clientOptions.connect,
allowH2: clientOptions.allowH2,
});
}
else if (clientOptions?.connect) {
this.#dispatcher = new Agent({
connect: clientOptions.connect,
allowH2: clientOptions.allowH2,
});
}
else if (clientOptions?.allowH2) {
// Support HTTP2
this.#dispatcher = new Agent({
allowH2: clientOptions.allowH2,
});
}
initDiagnosticsChannel();
}
getDispatcher() {
return this.#dispatcher ?? getGlobalDispatcher();
}
setDispatcher(dispatcher) {
this.#dispatcher = dispatcher;
}
getDispatcherPoolStats() {
const agent = this.getDispatcher();
// origin => Pool Instance
const clients = Reflect.get(agent, undiciSymbols.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 = globalId('HttpClientRequest');
let requestUrl;
if (typeof url === 'string') {
if (!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(urlFormat(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 = 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.kRequestId]: requestId,
[symbols.kRequestStartTime]: requestStartTime,
[symbols.kEnableRequestTiming]: !!args.timing,
[symbols.kRequestTiming]: timing,
[symbols.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'] = 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 (isReadable(args.stream) && !(args.stream instanceof Readable)) {
debug('Request#%d convert old style stream to Readable', requestId);
args.stream = new Readable().wrap(args.stream);
isStreamingRequest = true;
}
else if (args.stream instanceof FormStream) {
debug('Request#%d convert formstream to Readable', requestId);
args.stream = new Readable().wrap(args.stream);
isStreamingRequest = true;
}
args.content = args.stream;
}
if (args.files) {
if (isGETOrHEAD) {
requestOptions.method = 'POST';
}
const formData = new 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 Readable || 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 = basename(file);
value = createReadStream(file);
}
else if (Buffer.isBuffer(file)) {
fileName = customFileName || `bufferfile${index}`;
value = file;
}
else if (file instanceof Readable || isReadable(file)) {
fileName = getFileName(file) || customFileName || `streamfile${index}`;
isStreamingRequest = true;
value = file;
}
const mimeType = mime.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 = isReadable(args.content);
}
}
else if (args.data) {
const isStringOrBufferOrReadable = typeof args.data === 'string'
|| Buffer.isBuffer(args.data)
|| isReadable(args.data);
if (isGETOrHEAD) {
if (!isStringOrBufferOrReadable) {
let query;
if (args.nestedQuerystring) {
query = qs.stringify(args.data);
}
else {
query = querystring.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 = 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.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;
channels.request.publish({
request: reqMeta,
});
if (this.listenerCount('request') > 0) {
this.emit('request', reqMeta);
}
let response = await undiciRequest(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 = 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 undiciRequest(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 = 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' ? createGunzip() : createBrotliDecompress();
res = Object.assign(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' ? createGunzip() : createBrotliDecompress();
await pipelinePromise(response.body, decoder, args.writeStream);
}
else {
await pipelinePromise(response.body, args.writeStream);
}
}
else {
// buffer
data = Buffer.from(await response.body.arrayBuffer());
if (isCompressedContent && data.length > 0) {
try {
data = contentEncoding === 'gzip' ? gunzipSync(data) : 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 = parseJSON(data.toString(), args.fixJSONCtlChars);
}
}
}
res.rt = performanceTime(requestStartTime);
// get real socket info from internalOpaque
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 sleep(args.retryDelay);
}
requestContext.retries++;
return await this.#requestInternal(url, options, requestContext);
}
}
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 HttpClientRequestTimeoutError(headersTimeout, { cause: err });
}
else if (err.name === 'BodyTimeoutError') {
err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: err });
}
else if (err.name === 'InformationalError' && err.message.includes('stream timeout')) {
err = new HttpClientRequestTimeoutError(bodyTimeout, { cause: err });
}
else if (err.code === 'UND_ERR_CONNECT_TIMEOUT') {
err = new 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 = performanceTime(requestStartTime);
updateSocketInfo(socketInfo, internalOpaque, rawError);
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;
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSHR0cENsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9IdHRwQ2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sa0JBQWtCLE1BQU0sMEJBQTBCLENBQUM7QUFDMUQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUUzQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDckMsT0FBTyxFQUNMLFlBQVksRUFDWixzQkFBc0IsRUFDdEIsVUFBVSxFQUNWLG9CQUFvQixHQUNyQixNQUFNLFdBQVcsQ0FBQztBQUNuQixPQUFPLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNqRCxPQUFPLEVBQUUsUUFBUSxJQUFJLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ25FLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDckMsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQzNDLE9BQU8sRUFBRSxNQUFNLElBQUksU0FBUyxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQy9DLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM5QyxPQUFPLFdBQVcsTUFBTSxrQkFBa0IsQ0FBQztBQUMzQyxPQUFPLEVBQUUsVUFBVSxJQUFJLEtBQUssRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzNELE9BQU8sRUFDTCxPQUFPLElBQUksYUFBYSxFQUV4QixLQUFLLEVBQ0wsbUJBQW1CLEdBRXBCLE1BQU0sUUFBUSxDQUFDO0FBQ2hCLDZEQUE2RDtBQUM3RCxhQUFhO0FBQ2IsT0FBTyxhQUFhLE1BQU0sNEJBQTRCLENBQUM7QUFDdkQsT0FBTyxJQUFJLE1BQU0sWUFBWSxDQUFDO0FBQzlCLE9BQU8sRUFBRSxNQUFNLElBQUksQ0FBQztBQUNwQix1Q0FBdUM7QUFDdkMsT0FBTyxVQUFVLE1BQU0sWUFBWSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLFNBQVMsRUFBd0IsTUFBTSxnQkFBZ0IsQ0FBQztBQUlqRSxPQUFPLEVBQUUsU0FBUyxFQUFFLGdCQUFnQixFQUFFLFFBQVEsRUFBRSxlQUFlLEVBQUUsVUFBVSxFQUFFLGdCQUFnQixFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQ2xILE9BQU8sT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUNuQyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNqRSxPQUFPLEVBQUUsNkJBQTZCLEVBQUUsNkJBQTZCLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQVFwRyxNQUFNLENBQUMsTUFBTSxRQUFRLEdBQUcsZUFBZSxDQUFDO0FBMEJ4QyxTQUFTLElBQUk7SUFDWCxPQUFPO0FBQ1QsQ0FBQztBQUVELE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO0FBd0M1QyxNQUFNLENBQUMsTUFBTSxPQUFPLEdBQUcsU0FBUyxDQUFDO0FBQ2pDLG9EQUFvRDtBQUNwRCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FDNUIsZUFBZSxPQUFPLFlBQVksT0FBTyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEtBQUssT0FBTyxDQUFDLFFBQVEsS0FBSyxPQUFPLENBQUMsSUFBSSxHQUFHLENBQUM7QUFFMUcsU0FBUyxXQUFXLENBQUMsTUFBZ0I7SUFDbkMsTUFBTSxRQUFRLEdBQVksTUFBYyxDQUFDLElBQUksQ0FBQztJQUM5QyxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQ2IsT0FBTyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDNUIsQ0FBQztJQUNELE9BQU8sRUFBRSxDQUFDO0FBQ1osQ0FBQztBQUVELFNBQVMsY0FBYyxDQUFDLFFBQTRCO0lBQ2xELE9BQU8sUUFBUSxDQUFDLE1BQU0sSUFBSSxHQUFHLENBQUM7QUFDaEMsQ0FBQztBQVVELE1BQU0sQ0FBQyxNQUFNLFFBQVEsR0FBRztJQUN0QixPQUFPLEVBQUUsa0JBQWtCLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDO0lBQ3JELFFBQVEsRUFBRSxrQkFBa0IsQ0FBQyxPQUFPLENBQUMsaUJBQWlCLENBQUM7SUFDdkQsWUFBWSxFQUFFLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxzQkFBc0IsQ0FBQztJQUNoRSxhQUFhLEVBQUUsa0JBQWtCLENBQUMsT0FBTyxDQUFDLHVCQUF1QixDQUFDO0NBQ25FLENBQUM7QUErQkYsaUVBQWlFO0FBQ2pFLE1BQU0sbUJBQW1CLEdBQUc7SUFDMUIsR0FBRyxFQUFFLG9CQUFvQjtJQUN6QixHQUFHLEVBQUUsUUFBUTtJQUNiLEdBQUcsRUFBRSxZQUFZO0lBQ2pCLEdBQUcsRUFBRSxxQkFBcUI7SUFDMUIsR0FBRyxFQUFFLHFCQUFxQjtDQUMzQixDQUFDO0FBRUYsTUFBTSxPQUFPLFVBQVcsU0FBUSxZQUFZO0lBQzFDLFlBQVksQ0FBa0I7SUFDOUIsV0FBVyxDQUFjO0lBRXpCLFlBQVksYUFBNkI7UUFDdkMsS0FBSyxFQUFFLENBQUM7UUFDUixJQUFJLENBQUMsWUFBWSxHQUFHLGFBQWEsRUFBRSxXQUFXLENBQUM7UUFDL0MsSUFBSSxhQUFhLEVBQUUsTUFBTSxJQUFJLGFBQWEsRUFBRSxZQUFZLEVBQUUsQ0FBQztZQUN6RCxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksU0FBUyxDQUFDO2dCQUMvQixNQUFNLEVBQUUsYUFBYSxDQUFDLE1BQU07Z0JBQzVCLFlBQVksRUFBRSxhQUFhLENBQUMsWUFBWTtnQkFDeEMsT0FBTyxFQUFFLGFBQWEsQ0FBQyxPQUFPO2dCQUM5QixPQUFPLEVBQUUsYUFBYSxDQUFDLE9BQU87YUFDL0IsQ0FBQyxDQUFDO1FBQ0wsQ0FBQzthQUFNLElBQUksYUFBYSxFQUFFLE9BQU8sRUFBRSxDQUFDO1lBQ2xDLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxLQUFLLENBQUM7Z0JBQzNCLE9BQU8sRUFBRSxhQUFhLENBQUMsT0FBTztnQkFDOUIsT0FBTyxFQUFFLGFBQWEsQ0FBQyxPQUFPO2FBQy9CLENBQUMsQ0FBQztRQUNMLENBQUM7YUFBTSxJQUFJLGFBQWEsRUFBRSxPQUFPLEVBQUUsQ0FBQztZQUNsQyxnQkFBZ0I7WUFDaEIsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLEtBQUssQ0FBQztnQkFDM0IsT0FBTyxFQUFFLGFBQWEsQ0FBQyxPQUFPO2FBQy9CLENBQUMsQ0FBQztRQUNMLENBQUM7UUFDRCxzQkFBc0IsRUFBRSxDQUFDO0lBQzNCLENBQUM7SUFFRCxhQUFhO1FBQ1gsT0FBTyxJQUFJLENBQUMsV0FBVyxJQUFJLG1CQUFtQixFQUFFLENBQUM7SUFDbkQsQ0FBQztJQUVELGFBQWEsQ0FBQyxVQUFzQjtRQUNsQyxJQUFJLENBQUMsV0FBVyxHQUFHLFVBQVUsQ0FBQztJQUNoQyxDQUFDO0lBRUQsc0JBQXNCO1FBQ3BCLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUNuQywwQkFBMEI7UUFDMUIsTUFBTSxPQUFPLEdBQTJDLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLGFBQWEsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNuRyxNQUFNLFlBQVksR0FBNkIsRUFBRSxDQUFDO1FBQ2xELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNiLE9BQU8sWUFBWSxDQUFDO1FBQ3RCLENBQUM7UUFDRCxLQUFLLE1BQU0sQ0FBRSxHQUFHLEVBQUUsR0FBRyxDQUFFLElBQUksT0FBTyxFQUFFLENBQUM7WUFDbkMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxPQUFPLEdBQUcsQ0FBQyxLQUFLLEtBQUssVUFBVSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBNkMsQ0FBQztZQUMvRyx5REFBeUQ7WUFDekQsTUFBTSxLQUFLLEdBQUcsSUFBSSxFQUFFLEtBQUssSUFBSSxJQUFJLEVBQUUsVUFBVSxFQUFFLEtBQUssQ0FBQztZQUNyRCxJQUFJLENBQUMsS0FBSztnQkFBRSxTQUFTO1lBRXJCLFlBQVksQ0FBQyxHQUFHLENBQUMsR0FBRztnQkFDbEIsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO2dCQUMxQixJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUk7Z0JBQ2hCLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTztnQkFDdEIsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNO2dCQUNwQixPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU87Z0JBQ3RCLElBQUksRUFBRSxLQUFLLENBQUMsSUFBSTthQUNFLENBQUM7UUFDdkIsQ0FBQztRQUNELE9BQU8sWUFBWSxDQUFDO0lBQ3RCLENBQUM7SUFFRCxLQUFLLENBQUMsT0FBTyxDQUFVLEdBQWUsRUFBRSxPQUF3QjtRQUM5RCxPQUFPLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFJLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUN0RCxDQUFDO0lBRUQsa0VBQWtFO0lBQ2xFLEtBQUssQ0FBQyxJQUFJLENBQVUsR0FBZSxFQUFFLE9BQXdCO1FBQzNELE9BQU8sTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFJLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUM3QyxDQUFDO0lBRUQsS0FBSyxDQUFDLGdCQUFnQixDQUFJLEdBQWUsRUFBRSxPQUF3QixFQUFFLGNBQStCO1FBQ2xHLE1BQU0sU0FBUyxHQUFHLFFBQVEsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDO1FBQ2hELElBQUksVUFBZSxDQUFDO1FBQ3BCLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDNUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztnQkFDeEIsc0NBQXNDO2dCQUN0QyxHQUFHLEdBQUcsU0FBUyxHQUFHLEdBQUcsQ0FBQztZQUN4QixDQUFDO1lBQ0QsVUFBVSxHQUFHLElBQUksR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzVCLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLEdBQUcsQ0FBQyxZQUFZLEVBQUUsQ0FBQztnQkFDdEIsNkNBQTZDO2dCQUM3QyxVQUFVLEdBQUcsSUFBSSxHQUFHLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDdkMsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLGdEQUFnRDtnQkFDaEQsVUFBVSxHQUFHLElBQUksR0FBRyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1lBQ3ZDLENBQUM7UUFDSCxDQUFDO1FBRUQsTUFBTSxNQUFNLEdBQUcsQ0FBQyxPQUFPLEVBQUUsSUFBSSxJQUFJLE9BQU8sRUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLENBQUMsV0FBVyxFQUFnQixDQUFDO1FBQ3ZGLE1BQU0sZUFBZSxHQUFHLE9BQU8sRUFBRSxPQUFPLENBQUM7UUFDekMsTUFBTSxPQUFPLEdBQXdCLEVBQUUsQ0FBQztRQUN4QyxNQUFNLElBQUksR0FBRztZQUNYLEtBQUssRUFBRSxDQUFDO1lBQ1IsZ0JBQWdCLEVBQUUsQ0FBQztZQUNuQixNQUFNLEVBQUUsSUFBSTtZQUNaLEdBQUcsSUFBSSxDQUFDLFlBQVk7WUFDcEIsR0FBRyxPQUFPO1lBQ1YsK0VBQStFO1lBQy9FLE1BQU07WUFDTixPQUFPO1NBQ1IsQ0FBQztRQUNGLGNBQWMsR0FBRztZQUNmLE9BQU8sRUFBRSxDQUFDO1lBQ1Ysa0JBQWtCLEVBQUUsQ0FBQztZQUNyQixTQUFTLEVBQUUsQ0FBQztZQUNaLE9BQU8sRUFBRSxFQUFFO1lBQ1gsR0FBRyxjQUFjO1NBQ2xCLENBQUM7UUFDRixJQUFJLENBQUMsY0FBYyxDQUFDLGdCQUFnQixFQUFFLENBQUM7WUFDckMsY0FBYyxDQUFDLGdCQUFnQixHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUN0RCxDQUFDO1FBQ0QsY0FBYyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQzdDLE1BQU0sZ0JBQWdCLEdBQUcsY0FBYyxDQUFDLGdCQUFnQixDQUFDO1FBRXpELHVHQUF1RztRQUN2RyxNQUFNLE1BQU0sR0FBRztZQUNiLGtCQUFrQjtZQUNsQixPQUFPLEVBQUUsQ0FBQztZQUNWLGtCQUFrQjtZQUNsQixTQUFTLEVBQUUsQ0FBQztZQUNaLG1CQUFtQjtZQUNuQixTQUFTLEVBQUUsQ0FBQztZQUNaLHVCQUF1QjtZQUN2QixrQkFBa0IsRUFBRSxDQUFDO1lBQ3JCLDJDQUEyQztZQUMzQyxXQUFXLEVBQUUsQ0FBQztZQUNkLHFFQUFxRTtZQUNyRSxPQUFPLEVBQUUsQ0FBQztZQUNWLG9EQUFvRDtZQUNwRCxlQUFlLEVBQUUsQ0FBQztTQUNuQixDQUFDO1FBQ0YsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUNuQyxrRUFBa0U7UUFDbEUsTUFBTSxjQUFjLEdBQUc7WUFDckIsQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLEVBQUUsU0FBUztZQUMvQixDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxFQUFFLGdCQUFnQjtZQUM3QyxDQUFDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTTtZQUM3QyxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsRUFBRSxNQUFNO1lBQ2hDLENBQUMsT0FBTyxDQUFDLHNCQUFzQixDQUFDLEVBQUUsY0FBYztTQUNqRCxDQUFDO1FBQ0YsTUFBTSxPQUFPLEdBQUc7WUFDZCxTQUFTO1lBQ1QsR0FBRyxFQUFFLFVBQVUsQ0FBQyxJQUFJO1lBQ3BCLElBQUk7WUFDSixHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUc7WUFDYixPQUFPLEVBQUUsY0FBYyxDQUFDLE9BQU87U0FDakIsQ0FBQztRQUNqQixNQUFNLFVBQVUsR0FBZTtZQUM3QixFQUFFLEVBQUUsQ0FBQztZQUNMLFlBQVksRUFBRSxFQUFFO1lBQ2hCLFNBQVMsRUFBRSxDQUFDO1lBQ1osYUFBYSxFQUFFLEVBQUU7WUFDakIsVUFBVSxFQUFFLENBQUM7WUFDYixZQUFZLEVBQUUsRUFBRTtZQUNoQixZQUFZLEVBQUUsQ0FBQztZQUNmLFNBQVMsRUFBRSxDQUFDO1lBQ1osZUFBZSxFQUFFLENBQUM7WUFDbEIsZ0JBQWdCLEVBQUUsQ0FBQztTQUNwQixDQUFDO1FBQ0YsMkNBQTJDO1FBQzNDLE1BQU0sVUFBVSxHQUF3QixFQUFFLENBQUM7UUFDM0MsSUFBSSxHQUFHLEdBQUc7WUFDUixNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQ1YsVUFBVSxFQUFFLENBQUMsQ0FBQztZQUNkLFVBQVUsRUFBRSxFQUFFO1lBQ2QsYUFBYSxFQUFFLEVBQUU7WUFDakIsT0FBTyxFQUFFLFVBQVU7WUFDbkIsSUFBSSxFQUFFLENBQUM7WUFDUCxPQUFPLEVBQUUsS0FBSztZQUNkLEVBQUUsRUFBRSxDQUFDO1lBQ0wsZUFBZSxFQUFFLElBQUk7WUFDckIsV0FBVyxFQUFFLGNBQWMsQ0FBQyxPQUFPO1lBQ25DLE1BQU07WUFDTixNQUFNLEVBQUUsVUFBVTtZQUNsQixPQUFPLEVBQUUsY0FBYyxDQUFDLE9BQU87WUFDL0Isa0JBQWtCLEVBQUUsY0FBYyxDQUFDLGtCQUFrQjtTQUN4QixDQUFDO1FBRWhDLElBQUksY0FBYyxHQUFHLElBQUksQ0FBQztRQUMxQixJQUFJLFdBQVcsR0FBRyxJQUFJLENBQUM7UUFDdkIsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDakIsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUNoQyxjQUFjLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxjQUFjLENBQUM7Z0JBQ25ELFdBQVcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxJQUFJLFdBQVcsQ0FBQztZQUMvQyxDQUFDO2lCQUFNLENBQUM7Z0JBQ04saURBQWlEO2dCQUNqRCxjQUFjLEdBQUcsV0FBVyxHQUFHLE9BQU8sSUFBSSxDQUFDLE9BQU8sS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUM7WUFDMUcsQ0FBQztRQUNILENBQUM7UUFDRCxJQUFJLGVBQWUsRUFBRSxDQUFDO1lBQ3BCLGdDQUFnQztZQUNoQyxLQUFLLE1BQU0sSUFBSSxJQUFJLGVBQWUsRUFBRSxDQUFDO2dCQUNuQyxPQUFPLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLEdBQUcsZUFBZSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ3RELENBQUM7UUFDSCxDQUFDO1FBQ0Qsb0JBQW9CO1FBQ3BCLE1BQU0sZUFBZSxHQUFHLFlBQVksSUFBSSxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDMUUsSUFBSSxlQUFlLEVBQUUsQ0FBQztZQUNwQixPQUFPLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUMvQixDQUFDO2FBQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDO1lBQ2xDLHlCQUF5QjtZQUN6QixPQUFPLENBQUMsWUFBWSxDQUFDLEdBQUcsaUJBQWlCLENBQUM7UUFDNUMsQ0FBQztRQUNELCtCQUErQjtRQUMvQixJQUFJLElBQUksQ0FBQyxTQUFTLElBQUksSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQzFDLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1FBQzNCLENBQUM7UUFDRCxJQUFJLElBQUksQ0FBQyxRQUFRLEtBQUssTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2hELE9BQU8sQ0FBQyxNQUFNLEdBQUcsa0JBQWtCLENBQUM7UUFDdEMsQ0FBQztRQUNELDJCQUEyQjtRQUMzQixJQUFJLElBQUksQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDLFVBQVUsS0FBSyxLQUFLLEVBQUUsQ0FBQztZQUMzQyxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQztRQUN6QixDQUFDO1FBQ0QsSUFBSSxJQUFJLENBQUMsVUFBVSxJQUFJLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLEVBQUUsQ0FBQztZQUNuRCxPQUFPLENBQUMsaUJBQWlCLENBQUMsR0FBRyxVQUFVLENBQUM7UUFDMUMsQ0FBQztRQUNELElBQUksY0FBYyxDQUFDLE9BQU8sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMvQixPQUFPLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxHQUFHLGNBQWMsQ0FBQyxPQUFPLElBQUksSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3hFLENBQUM7UUFDRCxJQUFJLGNBQWMsQ0FBQyxrQkFBa0IsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUMxQyxPQUFPLENBQUMsZ0NBQWdDLENBQUMsR0FBRyxHQUFHLGNBQWMsQ0FBQyxrQkFBa0IsSUFBSSxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUM5RyxDQUFDO1FBQ0QsSUFBSSxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsRUFBRSxDQUFDO1lBQ3hDLE9BQU8sQ0FBQyxhQUFhLEdBQUcsU0FBUyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUMvRSxDQUFDO1FBRUQsOERBQThEO1FBQzlELElBQUksa0JBQWtCLEdBQUcsS0FBSyxDQUFDO1FBQy9CLElBQUksbUJBQW1CLEdBQUcsS0FBSyxDQUFDO1FBQ2hDLElBQUksSUFBSSxDQUFDLFFBQVEsS0FBSyxRQUFRLElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ25ELG1CQUFtQixHQUFHLElBQUksQ0FBQztRQUM3QixDQUFDO1FBRUQsSUFBSSxZQUFZLEdBQUcsSUFBSSxDQUFDLFlBQVksSUFBSSxFQUFFLENBQUM7UUFFM0MsSUFBSSxDQUFDO1lBQ0gsTUFBTSxjQUFjLEdBQXlCO2dCQUMzQyxNQUFNO2dCQUNOLHVDQUF1QztnQkFDdkMsZUFBZSxFQUFFLENBQUM7Z0JBQ2xCLGNBQWM7Z0JBQ2QsT0FBTztnQkFDUCxXQUFXO2dCQUNYLE1BQU0sRUFBRSxjQUFjO2dCQUN0QixVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVUsSUFBSSxJQUFJLENBQUMsV0FBVztnQkFDL0MsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNO2dCQUNuQixLQUFLLEVBQUUsS0FBSzthQUNiLENBQUM7WUFDRixJQUFJLE9BQU8sSUFBSSxDQUFDLGFBQWEsS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDM0MsY0FBYyxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDO1lBQ3BELENBQUM7WUFDRCxJQUFJLE9BQU8sSUFBSSxDQUFDLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDcEMsY0FBYyxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDO1lBQ3BDLENBQUM7WUFDRCxJQUFJLElBQUksQ0FBQyxjQUFjLEtBQUssS0FBSyxFQUFFLENBQUM7Z0JBQ2xDLFlBQVksR0FBRyxDQUFDLENBQUM7WUFDbkIsQ0FBQztZQUVELE1BQU0sV0FBVyxHQUFHLGNBQWMsQ0FBQyxNQUFNLEtBQUssS0FBSyxJQUFJLGNBQWMsQ0FBQyxNQUFNLEtBQUssTUFBTSxDQUFDO1lBQ3hGLHdCQUF3QjtZQUN4QixJQUFJLElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ2pDLHlDQUF5QztnQkFDekMsK0VBQStFO2dCQUMvRSxJQUFJLFVBQVUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLFlBQVksUUFBUSxDQUFDLEVBQUUsQ0FBQztvQkFDbEUsS0FBSyxDQUFDLGlEQUFpRCxFQUFFLFNBQVMsQ0FBQyxDQUFDO29CQUNwRSxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksUUFBUSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztvQkFDL0Msa0JBQWtCLEdBQUcsSUFBSSxDQUFDO2dCQUM1QixDQUFDO3FCQUFNLElBQUksSUFBSSxDQUFDLE1BQU0sWUFBWSxVQUFVLEVBQUUsQ0FBQztvQkFDN0MsS0FBSyxDQUFDLDJDQUEyQyxFQUFFLFNBQVMsQ0FBQyxDQUFDO29CQUM5RCxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksUUFBUSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztvQkFDL0Msa0JBQWtCLEdBQUcsSUFBSSxDQUFDO2dCQUM1QixDQUFDO2dCQUNELElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztZQUM3QixDQUFDO1lBRUQsSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQ2YsSUFBSSxXQUFXLEVBQUUsQ0FBQztvQkFDaEIsY0FBYyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7Z0JBQ2pDLENBQUM7Z0JBQ0QsTUFBTSxRQUFRLEdBQUcsSUFBSSxRQUFRLEVBQUUsQ0FBQztnQkFDaEMsTUFBTSxXQUFXLEdBQW9ELEVBQUUsQ0FBQztnQkFDeEUsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO29CQUM5QixLQUFLLE1BQU0sQ0FBRSxLQUFLLEVBQUUsSUFBSSxDQUFFLElBQUksSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDO3dCQUNuRCxNQUFNLEtBQUssR0FBRyxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7d0JBQ3BELFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBRSxLQUFLLEVBQUUsSUFBSSxDQUFFLENBQUMsQ0FBQztvQkFDcEMsQ0FBQztnQkFDSCxDQUFDO3FCQUFNLElBQUksSUFBSSxDQUFDLEtBQUssWUFBWSxRQUFRLElBQUksVUFBVSxDQUFDLElBQUksQ0FBQyxLQUFZLENBQUMsRUFBRSxDQUFDO29CQUMzRSxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxLQUFpQixDQUFFLENBQUMsQ0FBQztnQkFDdkQsQ0FBQztxQkFBTSxJQUFJLE9BQU8sSUFBSSxDQUFDLEtBQUssS0FBSyxRQUFRLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztvQkFDekUsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFFLENBQUMsQ0FBQztnQkFDM0MsQ0FBQztxQkFBTSxJQUFJLE9BQU8sSUFBSSxDQUFDLEtBQUssS0FBSyxRQUFRLEVBQUUsQ0FBQztvQkFDMUMsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQW1ELENBQUM7b0JBQ3ZFLEtBQUssTUFBTSxLQUFLLElBQUksS0FBSyxFQUFFLENBQUM7d0JBQzFCLHNCQUFzQjt3QkFDdEIsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDO3dCQUMxQixXQUFXLENBQUMsSUFBSSxDQUFDLENBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxLQUFLLENBQUUsQ0FBQyxDQUFDO29CQUMzQyxDQUFDO2dCQUNILENBQUM7Z0JBQ0QsMEJBQTBCO2dCQUMxQixJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztvQkFDZCxLQUFLLE1BQU0sS0FBSyxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQzt3QkFDOUIsUUFBUSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO29CQUMzQyxDQUFDO2dCQUNILENBQUM7Z0JBQ0QsS0FBSyxNQUFNLENBQUUsS0FBSyxFQUFFLENBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxjQUFjLENBQUUsQ0FBQyxJQUFJLFdBQVcsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDO29CQUM5RSxJQUFJLFFBQVEsR0FBRyxFQUFFLENBQUM7b0JBQ2xCLElBQUksS0FBVSxDQUFDO29CQUNmLElBQUksT0FBTyxJQUFJLEtBQUssUUFBUSxFQUFFLENBQUM7d0JBQzdCLFFBQVEsR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7d0JBQzFCLEtBQUssR0FBRyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztvQkFDakMsQ0FBQzt5QkFBTSxJQUFJLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQzt3QkFDakMsUUFBUSxHQUFHLGNBQWMsSUFBSSxhQUFhLEtBQUssRUFBRSxDQUFDO3dCQUNsRCxLQUFLLEdBQUcsSUFBSSxDQUFDO29CQUNmLENBQUM7eUJBQU0sSUFBSSxJQUFJLFlBQVksUUFBUSxJQUFJLFVBQVUsQ0FBQyxJQUFXLENBQUMsRUFBRSxDQUFDO3dCQUMvRCxRQUFRLEdBQUcsV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLGNBQWMsSUFBSSxhQUFhLEtBQUssRUFBRSxDQUFDO3dCQUN2RSxrQkFBa0IsR0FBRyxJQUFJLENBQUM7d0JBQzFCLEtBQUssR0FBRyxJQUFJLENBQUM7b0JBQ2YsQ0FBQztvQkFDRCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztvQkFDN0MsUUFBUSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFO3dCQUM1QixRQUFRLEVBQUUsUUFBUTt3QkFDbEIsV0FBVyxFQUFFLFFBQVE7cUJBQ3RCLENBQUMsQ0FBQztvQkFDSCxLQUFLLENBQUMsdURBQXVELEVBQzNELEtBQUssRUFBRSxRQUFRLEVBQUUsUUFBUSxDQUFDLENBQUM7Z0JBQy9CLENBQUM7Z0JBQ0QsTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUUsUUFBUSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUM7Z0JBQzlDLGNBQWMsQ0FBQyxJQUFJLEdBQUcsUUFBUSxDQUFDO1lBQ2pDLENBQUM7aUJBQU0sSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ3hCLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztvQkFDakIsaUJBQWlCO29CQUNqQixjQUFjLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUM7b0JBQ25DLElBQUksSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO3dCQUNyQixPQUFPLENBQUMsY0FBYyxDQUFDLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQztvQkFDN0MsQ0FBQzt5QkFBTSxJQUFJLE9BQU8sSUFBSSxDQUFDLE9BQU8sS0FBSyxRQUFRLElBQUksQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQzt3QkFDeEUsT0FBTyxDQUFDLGNBQWMsQ0FBQyxHQUFHLDBCQUEwQixDQUFDO29CQUN2RCxDQUFDO29CQUNELGtCQUFrQixHQUFHLFVBQVUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7Z0JBQ2hELENBQUM7WUFDSCxDQUFDO2lCQUFNLElBQUksSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUNyQixNQUFNLDBCQUEwQixHQUFHLE9BQU8sSUFBSSxDQUFDLElBQUksS0FBSyxRQUFRO3VCQUMzRCxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7dUJBQzFCLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQzNCLElBQUksV0FBVyxFQUFFLENBQUM7b0JBQ2hCLElBQUksQ0FBQywwQkFBMEIsRUFBRSxDQUFDO3dCQUNoQyxJQUFJLEtBQWEsQ0FBQzt3QkFDbEIsSUFBSSxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQzs0QkFDM0IsS0FBSyxHQUFHLEVBQUUsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO3dCQUNsQyxDQUFDOzZCQUFNLENBQUM7NEJBQ04sS0FBSyxHQUFHLFdBQVcsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO3dCQUMzQyxDQUFDO3dCQUNELHVCQUF1Qjt3QkFDdkIsTUFBTSxJQUFJLEdBQUcsVUFBVSxDQUFDLElBQUksQ0FBQzt3QkFDN0IsVUFBVSxHQUFHLElBQUksR0FBRyxDQUFDLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsS0FBSyxDQUFDLENBQUM7b0JBQ3hFLENBQUM7Z0JBQ0gsQ0FBQztxQkFBTSxDQUFDO29CQUNOLElBQUksMEJBQTBCLEVBQUUsQ0FBQzt3QkFDL0IsY0FBYyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDO3dCQUNoQyxrQkFBa0IsR0FBRyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO29CQUM3QyxDQUFDO3lCQUFNLENBQUM7d0JBQ04sSUFBSSxJQUFJLENBQUMsV0FBVyxLQUFLLE1BQU07K0JBQzFCLElBQUksQ0FBQyxXQUFXLEtBQUssa0JBQWtCOytCQUN2QyxPQUFPLENBQUMsY0FBYyxDQUFDLEVBQUUsVUFBVSxDQUFDLGtCQUFrQixDQUFDLEVBQUUsQ0FBQzs0QkFDN0QsY0FBYyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQzs0QkFDaEQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDO2dDQUM3QixPQUFPLENBQUMsY0FBYyxDQUFDLEdBQUcsa0JBQWtCLENBQUM7NEJBQy9DLENBQUM7d0JBQ0gsQ0FBQzs2QkFBTSxDQUFDOzRCQUNOLE9BQU8sQ0FBQyxjQUFjLENBQUMsR0FBRyxpREFBaUQsQ0FBQzs0QkFDNUUsSUFBSSxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztnQ0FDM0IsY0FBYyxDQUFDLElBQUksR0FBRyxFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQzs0QkFDaEQsQ0FBQztpQ0FBTSxDQUFDO2dDQUNOLGNBQWMsQ0FBQyxJQUFJLEdBQUcsSUFBSSxlQUFlLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDOzRCQUNsRSxDQUFDO3dCQUNILENBQUM7b0JBQ0gsQ0FBQztnQkFDSCxDQUFDO1lBQ0gsQ0FBQztZQUNELElBQUksa0JBQWtCLEVBQUUsQ0FBQztnQkFDdkIsSUFBSSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUM7Z0JBQ2YsSUFBSSxDQUFDLGdCQUFnQixHQUFHLENBQUMsQ0FBQztnQkFDMUIsWUFBWSxHQUFHLENBQUMsQ0FBQztZQUNuQixDQUFDO1lBQ0QsSUFBSSxtQkFBbUIsRUFBRSxDQUFDO2dCQUN4QixJQUFJLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQztnQkFDZixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDO1lBQzVCLENBQUM7WUFFRCxLQUFLLENBQUMseUpBQXlKLEVBQzdKLFNBQVMsRUFBRSxjQUFjLENBQUMsTUFBTSxFQUFFLFVBQVUsQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxXQUFXLEVBQUUsa0JBQWtCLEVBQUUsbUJBQW1CLEVBQUUsWUFBWSxFQUFFLGNBQWMsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUM1SyxjQUFjLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztZQUNqQyxRQUFRLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQztnQkFDdkIsT0FBTyxFQUFFLE9BQU87YUFDWSxDQUFDLENBQUM7WUFDaEMsSUFBSSxJQUFJLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUN0QyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUNoQyxDQUFDO1lBRUQsSUFBSSxRQUFRLEdBQUcsTUFBTSxhQUFhLENBQUMsVUFBVSxFQUFFLGNBQXFDLENBQUMsQ0FBQztZQUN0RixJQUFJLFFBQVEsQ0FBQyxVQUFVLEtBQUssR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxPQUFPLENBQUMsb0JBQW9CLENBQUMsQ0FBQztnQkFDakgsQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLGFBQWEsSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7Z0JBQzNELHFCQUFxQjtnQkFDckIsTUFBTSxtQkFBbUIsR0FBRyxRQUFRLENBQUMsT0FBTyxDQUFDLGtCQUFrQixDQUFDLElBQUksUUFBUSxDQUFDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO2dCQUMzRyxNQUFNLFlBQVksR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDO29CQUNyRCxDQUFDLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsQ0FBQztvQkFDMUUsQ0FBQyxDQUFDLG1CQUFtQixDQUFDO2dCQUN4QixJQUFJLFlBQVksSUFBSSxZQUFZLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7b0JBQ3ZELEtBQUssQ0FBQyw0REFBNEQsRUFBRSxTQUFTLEVBQUUsVUFBVSxDQUFDLElBQUksRUFBRSxZQUFZLENBQUMsQ0FBQztvQkFDOUcsY0FBYyxDQUFDLE9BQU8sQ0FBQyxhQUFhLEdBQUcsZ0JBQWdCLENBQUMsY0FBYyxDQUFDLE1BQU8sRUFDNUUsR0FBRyxVQUFVLENBQUMsUUFBUSxHQUFHLFVBQVUsQ0FBQyxNQUFNLEVBQUUsRUFBRSxZQUFZLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO29CQUMvRSxLQUFLLENBQUMsNENBQTRDLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxjQUFjLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxDQUFDO29CQUMxRyxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQyxFQUFFLENBQUM7d0JBQ2xELG9DQUFvQzt3QkFDcEMsY0FBYyxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ