UNPKG

@microsoft/teams.apps

Version:

<p> <a href="https://www.npmjs.com/package/@microsoft/teams.apps" target="_blank"> <img src="https://img.shields.io/npm/v/@microsoft/teams.apps/latest" /> </a> <a href="https://www.npmjs.com/package/@microsoft/teams.apps?activeTab=code

507 lines • 43.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DEFAULT_OAUTH_SETTINGS = exports.OAuthFlow = void 0; exports.startOAuthSignIn = startOAuthSignIn; const axios_1 = require("axios"); const teams_api_1 = require("@microsoft/teams.api"); const constants_1 = require("../diagnostics/constants"); const telemetry_1 = require("./telemetry"); /** @internal Gets a cached token or emits an OAuth sign-in card. */ async function startOAuthSignIn(context, connectionName, options = {}, onSignInInitiated) { const oauthCardText = options.oauthCardText ?? 'Please Sign In...'; const signInButtonText = options.signInButtonText ?? 'Sign In'; try { const response = await context.api.users.getToken({ channelId: context.activity.channelId, userId: context.activity.from.id, connectionName, }); if (response.token) { return response.token; } } catch (error) { if (!isTokenNotFoundError(error)) { throw error; } } const conversation = { ...context.ref }; const tokenExchangeState = { connectionName, conversation, relatesTo: context.activity.relatesTo, msAppId: context.appId, }; const state = Buffer.from(JSON.stringify(tokenExchangeState)).toString('base64'); const resource = await context.api.bots.signIn.getResource({ state }); const conversationType = context.activity.conversation.conversationType; const isChannel = conversationType === 'channel'; const isChannelOrGroup = isChannel || conversationType === 'groupChat'; const recipient = isChannelOrGroup ? { ...context.activity.from, isTargeted: true } : context.activity.from; const tokenExchangeResource = isChannel ? undefined : resource.tokenExchangeResource; await context.send(options.overrideSignInActivity?.(tokenExchangeResource, resource.tokenPostResource, resource.signInLink) ?? { type: 'message', recipient, attachments: [ (0, teams_api_1.cardAttachment)('oauth', { text: oauthCardText, connectionName, tokenExchangeResource, tokenPostResource: resource.tokenPostResource, buttons: [ { type: 'signin', title: signInButtonText, value: options.signInLink || resource.signInLink, }, ], }), ], }, conversation); await onSignInInitiated?.(context, connectionName, tokenExchangeResource !== undefined); return undefined; } /** * High-level OAuth lifecycle for one Bot Framework OAuth connection. * * Flows are registered through `AppOptions.oauthFlows` or * `app.addOAuthFlow(...)` so inbound sign-in invokes can be dispatched to the * correct connection. Apps without registered flows retain their legacy * default connection as the same flow type for backward compatibility. */ class OAuthFlow { connectionName; options; static PENDING_TTL_MS = 5 * 60 * 1000; static MAX_PENDING_ENTRIES = 1_000; static EXCHANGE_TTL_MS = 5 * 60 * 1000; static MAX_EXCHANGE_ENTRIES = 1_000; static EXCHANGE_STATE_KEY = '__oauth:exchanges'; signInCompleteHandler; signInFailureHandler; pendingSignIns = new Map(); pendingSsoSignIns = new Map(); // Process-local only: this map collapses concurrent duplicates that reach the // same instance; persisted conversation state covers cross-instance retries. tokenExchangeLocks = new Map(); // Retains completed exchanges when state is disabled or unavailable. processedExchanges = new Map(); /** * Creates an OAuth flow for a connection. * * Applications should use `AppOptions.oauthFlows` or * `app.addOAuthFlow(...)` so the flow is registered for inbound invokes. * @param connectionName OAuth connection name configured on the bot. * @param options Optional default card options. */ constructor(connectionName, options = {}) { this.connectionName = connectionName; this.options = options; if (!connectionName.trim()) { throw new Error('OAuth connection name is required.'); } } /** * Registers the callback invoked after interactive sign-in completes. * * Calling this method again replaces the previous callback. */ onSignInComplete(handler) { this.signInCompleteHandler = handler; return this; } /** * Registers the callback invoked when interactive sign-in fails. * * Calling this method again replaces the previous callback. */ onSignInFailure(handler) { this.signInFailureHandler = handler; return this; } /** * Silently gets the current user's token for this connection. * * Returns undefined when the token service returns no token or reports `404`. * Other transport and service failures, including `400` and `412`, propagate. */ async getToken(context) { return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, this.connectionName, constants_1.APP_OAUTH_OPERATION.getToken, async (_span, telemetry) => { try { const response = await context.api.users.getToken({ channelId: context.activity.channelId, userId: context.activity.from.id, connectionName: this.connectionName, }); const token = response.token || undefined; telemetry.result = token ? constants_1.APP_OAUTH_RESULT.hit : constants_1.APP_OAUTH_RESULT.miss; return token; } catch (error) { if (isTokenNotFoundError(error)) { telemetry.result = constants_1.APP_OAUTH_RESULT.miss; return undefined; } throw error; } }); } /** * Gets a cached token or sends an OAuth card to start interactive sign-in. * * Returns the cached token string when already signed in. Returns undefined * after sending a card; completion is then delivered to the registered * callback and the existing app `signin` event. Cached-token lookup errors * other than `404` propagate without sending a card. */ signIn(context, options = {}) { return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, this.connectionName, constants_1.APP_OAUTH_OPERATION.signIn, async (_span, telemetry) => { const token = await startOAuthSignIn(context, this.connectionName, { ...this.options, ...options, connectionName: this.connectionName, }, (signInContext, _connectionName, supportsSso) => { this.recordPending(signInContext, supportsSso); }); telemetry.result = token ? constants_1.APP_OAUTH_RESULT.cached : constants_1.APP_OAUTH_RESULT.cardSent; return token; }); } /** * Revokes the current user's token for this connection. */ signOut(context) { return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, this.connectionName, constants_1.APP_OAUTH_OPERATION.signOut, async (_span, telemetry) => { await context.api.users.signOut({ channelId: context.activity.channelId, userId: context.activity.from.id, connectionName: this.connectionName, }); telemetry.result = constants_1.APP_OAUTH_RESULT.success; }); } /** * Returns whether a token is currently available for this connection. */ async isSignedIn(context) { return (await this.getToken(context)) !== undefined; } /** @internal Invokes the registered completion callback. */ async complete(context, token) { await this.signInCompleteHandler?.(context, token); } /** @internal Invokes the registered failure callback. */ async fail(context, failure) { await this.signInFailureHandler?.(context, failure); } /** @internal Exchanges an SSO token for this flow. */ async exchangeToken(context, value, onSuccess, onError, connectionName = this.connectionName) { return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, connectionName, constants_1.APP_OAUTH_OPERATION.tokenExchange, async (span, telemetry) => { const exchangeId = value.id; if (exchangeId) { if (this.isExchangeProcessed(context, exchangeId)) { telemetry.result = constants_1.APP_OAUTH_RESULT.duplicate; telemetry.responseStatus = 200; return { status: 200 }; } const existingLock = this.tokenExchangeLocks.get(exchangeId); if (existingLock) { const result = await existingLock; telemetry.result = result.status === 200 ? constants_1.APP_OAUTH_RESULT.duplicate : constants_1.APP_OAUTH_RESULT.failure; telemetry.responseStatus = result.status; return result; } } const performExchange = async () => { let token; try { token = await context.api.users.exchangeToken({ channelId: context.activity.channelId, userId: context.activity.from.id, connectionName, exchangeRequest: { token: value.token, }, }); } catch (error) { if (!(error instanceof axios_1.AxiosError)) { // Token-service failures use AxiosError. Let anything outside // that contract follow the app's normal error handling. throw error; } if (isExpectedOAuthInvokeError(error)) { // 412 tells Teams that silent SSO could not complete and it // should fall back to the interactive OAuth card. await this.fail(context); telemetry.callbackInvoked = this.signInFailureHandler !== undefined; telemetry.result = constants_1.APP_OAUTH_RESULT.failure; telemetry.responseStatus = 412; return this.tokenExchangeFailure(value.id, connectionName); } // Unexpected service statuses are operational errors, not a // request for interactive fallback, so preserve their status. this.clearPending(context); await this.fail(context); telemetry.callbackInvoked = this.signInFailureHandler !== undefined; telemetry.result = constants_1.APP_OAUTH_RESULT.failure; const status = error.status || 500; telemetry.responseStatus = status; (0, telemetry_1.recordUnexpectedOAuthError)(span, telemetry, error, constants_1.APP_OAUTH_ERROR_TYPE.httpError); onError(error); return { status }; } if (!token?.token) { this.clearPending(context); await this.fail(context); telemetry.callbackInvoked = this.signInFailureHandler !== undefined; telemetry.result = constants_1.APP_OAUTH_RESULT.failure; telemetry.responseStatus = 412; return this.tokenExchangeFailure(value.id, connectionName); } if (exchangeId) { this.markExchangeProcessed(context, exchangeId); } this.clearPending(context); await onSuccess(token); telemetry.callbackInvoked = this.signInCompleteHandler !== undefined; telemetry.result = constants_1.APP_OAUTH_RESULT.success; telemetry.responseStatus = 200; return { status: 200 }; }; if (!exchangeId) { return performExchange(); } const lock = performExchange(); this.tokenExchangeLocks.set(exchangeId, lock); try { return await lock; } finally { this.tokenExchangeLocks.delete(exchangeId); } }); } /** @internal Attempts to redeem a verify-state code for this flow. */ async verifyState(context, state, onSuccess, onError) { return (0, telemetry_1.traceOAuthOperation)(constants_1.APP_SPAN_NAMES.oauth, this.connectionName, constants_1.APP_OAUTH_OPERATION.verifyState, async (span, telemetry) => { if (!state) { context.log.warn(`auth state not found for conversation "${context.activity.conversation.id}" and user "${context.activity.from.id}"`); telemetry.result = constants_1.APP_OAUTH_RESULT.noToken; telemetry.responseStatus = 404; return undefined; } let token; try { token = await context.api.users.getToken({ channelId: context.activity.channelId, userId: context.activity.from.id, connectionName: this.connectionName, code: state, }); } catch (error) { if (!(error instanceof axios_1.AxiosError)) { throw error; } if (isExpectedOAuthInvokeError(error)) { telemetry.result = constants_1.APP_OAUTH_RESULT.noToken; telemetry.responseStatus = error.status; return undefined; } this.clearPending(context); await this.fail(context); telemetry.callbackInvoked = this.signInFailureHandler !== undefined; telemetry.result = constants_1.APP_OAUTH_RESULT.failure; const status = error.status || 500; telemetry.responseStatus = status; (0, telemetry_1.recordUnexpectedOAuthError)(span, telemetry, error, constants_1.APP_OAUTH_ERROR_TYPE.httpError); onError(error); return { status }; } if (!token?.token) { telemetry.result = constants_1.APP_OAUTH_RESULT.noToken; telemetry.responseStatus = 404; return undefined; } this.clearPending(context); await onSuccess(token); telemetry.callbackInvoked = this.signInCompleteHandler !== undefined; telemetry.result = constants_1.APP_OAUTH_RESULT.success; telemetry.responseStatus = 200; return { status: 200 }; }); } /** @internal Records this flow as the source of an interactive sign-in. */ recordPending(context, supportsSso) { const startedAt = Date.now(); this.setPendingValue(context, false, startedAt); if (supportsSso) { this.setPendingValue(context, true, startedAt); } else { this.deletePendingValue(context, true); } } /** @internal Gets when this flow most recently initiated sign-in. */ getPending(context, ssoOnly) { const now = Date.now(); const stateKey = this.pendingStateKey(ssoOnly); const userState = context.state?.user; if (userState) { const stateValue = userState.get(stateKey); if (stateValue !== undefined) { if (now - stateValue < OAuthFlow.PENDING_TTL_MS) { return stateValue; } userState.delete(stateKey); } return undefined; } const pending = this.pendingMap(ssoOnly); this.prunePending(pending, now); return pending.get(this.pendingContextKey(context)); } /** @internal Clears pending sign-in attribution for this flow. */ clearPending(context) { this.deletePendingValue(context, false); this.deletePendingValue(context, true); } setPendingValue(context, ssoOnly, value) { const userState = context.state?.user; if (userState) { userState.set(this.pendingStateKey(ssoOnly), value); return; } const pending = this.pendingMap(ssoOnly); pending.set(this.pendingContextKey(context), value); this.prunePending(pending, value); } deletePendingValue(context, ssoOnly) { context.state?.user?.delete(this.pendingStateKey(ssoOnly)); this.pendingMap(ssoOnly).delete(this.pendingContextKey(context)); } pendingStateKey(ssoOnly) { return `__oauth:pending:${ssoOnly ? 'sso:' : ''}${this.connectionName.toLowerCase()}`; } pendingMap(ssoOnly) { return ssoOnly ? this.pendingSsoSignIns : this.pendingSignIns; } pendingContextKey(context) { return JSON.stringify([ context.activity.conversation.id, context.activity.from.id, ]); } prunePending(pending, now) { for (const [contextKey, timestamp] of pending) { if (now - timestamp >= OAuthFlow.PENDING_TTL_MS) { pending.delete(contextKey); } } if (pending.size <= OAuthFlow.MAX_PENDING_ENTRIES) { return; } for (const [contextKey] of [...pending.entries()] .sort(([, left], [, right]) => right - left) .slice(OAuthFlow.MAX_PENDING_ENTRIES)) { pending.delete(contextKey); } } tokenExchangeFailure(id, connectionName = this.connectionName) { return { status: 412, body: { id, connectionName, failureDetail: 'unable to exchange token...', }, }; } isExchangeProcessed(context, exchangeId) { const now = Date.now(); const stateTimestamp = this.getExchangeState(context, now)[exchangeId]; if (stateTimestamp !== undefined && now - stateTimestamp < OAuthFlow.EXCHANGE_TTL_MS) { return true; } this.pruneProcessedExchanges(now); return this.processedExchanges.has(exchangeId); } markExchangeProcessed(context, exchangeId) { const timestamp = Date.now(); this.processedExchanges.set(exchangeId, timestamp); this.pruneProcessedExchanges(timestamp); const conversationState = context.state?.conversation; if (!conversationState) { return; } const exchanges = this.getExchangeState(context, timestamp); exchanges[exchangeId] = timestamp; const bounded = Object.fromEntries(Object.entries(exchanges) .sort(([, left], [, right]) => right - left) .slice(0, OAuthFlow.MAX_EXCHANGE_ENTRIES)); conversationState.set(OAuthFlow.EXCHANGE_STATE_KEY, bounded); } pruneProcessedExchanges(now) { for (const [exchangeId, timestamp] of this.processedExchanges) { if (now - timestamp >= OAuthFlow.EXCHANGE_TTL_MS) { this.processedExchanges.delete(exchangeId); } } if (this.processedExchanges.size <= OAuthFlow.MAX_EXCHANGE_ENTRIES) { return; } for (const [exchangeId] of [...this.processedExchanges.entries()] .sort(([, left], [, right]) => right - left) .slice(OAuthFlow.MAX_EXCHANGE_ENTRIES)) { this.processedExchanges.delete(exchangeId); } } getExchangeState(context, now) { const conversationState = context.state?.conversation; if (!conversationState) { return {}; } const stored = conversationState.get(OAuthFlow.EXCHANGE_STATE_KEY) ?? {}; const current = Object.fromEntries(Object.entries(stored) .filter(([, timestamp]) => Number.isFinite(timestamp) && now - timestamp < OAuthFlow.EXCHANGE_TTL_MS) .sort(([, left], [, right]) => right - left) .slice(0, OAuthFlow.MAX_EXCHANGE_ENTRIES)); if (Object.keys(current).length !== Object.keys(stored).length) { if (Object.keys(current).length === 0) { conversationState.delete(OAuthFlow.EXCHANGE_STATE_KEY); } else { conversationState.set(OAuthFlow.EXCHANGE_STATE_KEY, current); } } return current; } } exports.OAuthFlow = OAuthFlow; /** @internal Defaults retained for deprecated context OAuth behavior. */ exports.DEFAULT_OAUTH_SETTINGS = { defaultConnectionName: 'graph' }; function isTokenNotFoundError(error) { return error instanceof axios_1.AxiosError && error.status === 404; } function isExpectedOAuthInvokeError(error) { return error instanceof axios_1.AxiosError && (error.status === 400 || error.status === 404 || error.status === 412); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb2F1dGgvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBNkZBLDRDQTZFQztBQTFLRCxpQ0FBbUM7QUFFbkMsb0RBWThCO0FBRzlCLHdEQUtrQztBQUlsQywyQ0FHcUI7QUErRHJCLG9FQUFvRTtBQUM3RCxLQUFLLFVBQVUsZ0JBQWdCLENBQ3BDLE9BQXlCLEVBQ3pCLGNBQXNCLEVBQ3RCLFVBQThCLEVBQUUsRUFDaEMsaUJBQStDO0lBRS9DLE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FBQyxhQUFhLElBQUksbUJBQW1CLENBQUM7SUFDbkUsTUFBTSxnQkFBZ0IsR0FBRyxPQUFPLENBQUMsZ0JBQWdCLElBQUksU0FBUyxDQUFDO0lBRS9ELElBQUksQ0FBQztRQUNILE1BQU0sUUFBUSxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDO1lBQ2hELFNBQVMsRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVM7WUFDckMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDaEMsY0FBYztTQUNmLENBQUMsQ0FBQztRQUNILElBQUksUUFBUSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ25CLE9BQU8sUUFBUSxDQUFDLEtBQUssQ0FBQztRQUN4QixDQUFDO0lBQ0gsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixJQUFJLENBQUMsb0JBQW9CLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNqQyxNQUFNLEtBQUssQ0FBQztRQUNkLENBQUM7SUFDSCxDQUFDO0lBRUQsTUFBTSxZQUFZLEdBQUcsRUFBRSxHQUFHLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN4QyxNQUFNLGtCQUFrQixHQUF1QjtRQUM3QyxjQUFjO1FBQ2QsWUFBWTtRQUNaLFNBQVMsRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVM7UUFDckMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxLQUFLO0tBQ3ZCLENBQUM7SUFDRixNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUNqRixNQUFNLFFBQVEsR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQ3RFLE1BQU0sZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsZ0JBQWdCLENBQUM7SUFDeEUsTUFBTSxTQUFTLEdBQUcsZ0JBQWdCLEtBQUssU0FBUyxDQUFDO0lBQ2pELE1BQU0sZ0JBQWdCLEdBQ3BCLFNBQVMsSUFBSSxnQkFBZ0IsS0FBSyxXQUFXLENBQUM7SUFDaEQsTUFBTSxTQUFTLEdBQUcsZ0JBQWdCO1FBQ2hDLENBQUMsQ0FBQyxFQUFFLEdBQUcsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRTtRQUNoRCxDQUFDLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUM7SUFDMUIsTUFBTSxxQkFBcUIsR0FBRyxTQUFTO1FBQ3JDLENBQUMsQ0FBQyxTQUFTO1FBQ1gsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQztJQUVuQyxNQUFNLE9BQU8sQ0FBQyxJQUFJLENBQ2hCLE9BQU8sQ0FBQyxzQkFBc0IsRUFBRSxDQUM5QixxQkFBcUIsRUFDckIsUUFBUSxDQUFDLGlCQUFpQixFQUMxQixRQUFRLENBQUMsVUFBVSxDQUNwQixJQUFJO1FBQ0gsSUFBSSxFQUFFLFNBQVM7UUFDZixTQUFTO1FBQ1QsV0FBVyxFQUFFO1lBQ1gsSUFBQSwwQkFBYyxFQUFDLE9BQU8sRUFBRTtnQkFDdEIsSUFBSSxFQUFFLGFBQWE7Z0JBQ25CLGNBQWM7Z0JBQ2QscUJBQXFCO2dCQUNyQixpQkFBaUIsRUFBRSxRQUFRLENBQUMsaUJBQWlCO2dCQUM3QyxPQUFPLEVBQUU7b0JBQ1A7d0JBQ0UsSUFBSSxFQUFFLFFBQVE7d0JBQ2QsS0FBSyxFQUFFLGdCQUFnQjt3QkFDdkIsS0FBSyxFQUFFLE9BQU8sQ0FBQyxVQUFVLElBQUksUUFBUSxDQUFDLFVBQVU7cUJBQ2pEO2lCQUNGO2FBQ0YsQ0FBQztTQUNIO0tBQ0YsRUFDRCxZQUFZLENBQ2IsQ0FBQztJQUVGLE1BQU0saUJBQWlCLEVBQUUsQ0FDdkIsT0FBTyxFQUNQLGNBQWMsRUFDZCxxQkFBcUIsS0FBSyxTQUFTLENBQ3BDLENBQUM7SUFDRixPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDO0FBb0NEOzs7Ozs7O0dBT0c7QUFDSCxNQUFhLFNBQVM7SUE2QlQ7SUFDUTtJQTdCWCxNQUFNLENBQVUsY0FBYyxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0lBQy9DLE1BQU0sQ0FBVSxtQkFBbUIsR0FBRyxLQUFLLENBQUM7SUFDNUMsTUFBTSxDQUFVLGVBQWUsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQztJQUNoRCxNQUFNLENBQVUsb0JBQW9CLEdBQUcsS0FBSyxDQUFDO0lBQzdDLE1BQU0sQ0FBVSxrQkFBa0IsR0FBRyxtQkFBbUIsQ0FBQztJQUV6RCxxQkFBcUIsQ0FBdUM7SUFDNUQsb0JBQW9CLENBQXNDO0lBQ2pELGNBQWMsR0FBRyxJQUFJLEdBQUcsRUFBa0IsQ0FBQztJQUMzQyxpQkFBaUIsR0FBRyxJQUFJLEdBQUcsRUFBa0IsQ0FBQztJQUMvRCw4RUFBOEU7SUFDOUUsNkVBQTZFO0lBQzVELGtCQUFrQixHQUFHLElBQUksR0FBRyxFQUcxQyxDQUFDO0lBQ0oscUVBQXFFO0lBQ3BELGtCQUFrQixHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO0lBRWhFOzs7Ozs7O09BT0c7SUFDSCxZQUNXLGNBQXNCLEVBQ2QsVUFBOEIsRUFBRTtRQUR4QyxtQkFBYyxHQUFkLGNBQWMsQ0FBUTtRQUNkLFlBQU8sR0FBUCxPQUFPLENBQXlCO1FBRWpELElBQUksQ0FBQyxjQUFjLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FBQztZQUMzQixNQUFNLElBQUksS0FBSyxDQUFDLG9DQUFvQyxDQUFDLENBQUM7UUFDeEQsQ0FBQztJQUVILENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsZ0JBQWdCLENBQUMsT0FBNEM7UUFDM0QsSUFBSSxDQUFDLHFCQUFxQixHQUFHLE9BQU8sQ0FBQztRQUNyQyxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsZUFBZSxDQUFDLE9BQTJDO1FBQ3pELElBQUksQ0FBQyxvQkFBb0IsR0FBRyxPQUFPLENBQUM7UUFDcEMsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCxLQUFLLENBQUMsUUFBUSxDQUFDLE9BQXlCO1FBQ3RDLE9BQU8sSUFBQSwrQkFBbUIsRUFDeEIsMEJBQWMsQ0FBQyxLQUFLLEVBQ3BCLElBQUksQ0FBQyxjQUFjLEVBQ25CLCtCQUFtQixDQUFDLFFBQVEsRUFDNUIsS0FBSyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsRUFBRTtZQUN6QixJQUFJLENBQUM7Z0JBQ0gsTUFBTSxRQUFRLEdBQUcsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUM7b0JBQ2hELFNBQVMsRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVM7b0JBQ3JDLE1BQU0sRUFBRSxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFO29CQUNoQyxjQUFjLEVBQUUsSUFBSSxDQUFDLGNBQWM7aUJBQ3BDLENBQUMsQ0FBQztnQkFDSCxNQUFNLEtBQUssR0FBRyxRQUFRLENBQUMsS0FBSyxJQUFJLFNBQVMsQ0FBQztnQkFDMUMsU0FBUyxDQUFDLE1BQU0sR0FBRyxLQUFLO29CQUN0QixDQUFDLENBQUMsNEJBQWdCLENBQUMsR0FBRztvQkFDdEIsQ0FBQyxDQUFDLDRCQUFnQixDQUFDLElBQUksQ0FBQztnQkFDMUIsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixJQUFJLG9CQUFvQixDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7b0JBQ2hDLFNBQVMsQ0FBQyxNQUFNLEdBQUcsNEJBQWdCLENBQUMsSUFBSSxDQUFDO29CQUN6QyxPQUFPLFNBQVMsQ0FBQztnQkFDbkIsQ0FBQztnQkFFRCxNQUFNLEtBQUssQ0FBQztZQUNkLENBQUM7UUFDSCxDQUFDLENBQ0YsQ0FBQztJQUNKLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsTUFBTSxDQUNKLE9BQXlCLEVBQ3pCLFVBQThCLEVBQUU7UUFFaEMsT0FBTyxJQUFBLCtCQUFtQixFQUN4QiwwQkFBYyxDQUFDLEtBQUssRUFDcEIsSUFBSSxDQUFDLGNBQWMsRUFDbkIsK0JBQW1CLENBQUMsTUFBTSxFQUMxQixLQUFLLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxFQUFFO1lBQ3pCLE1BQU0sS0FBSyxHQUFHLE1BQU0sZ0JBQWdCLENBQ2xDLE9BQU8sRUFDUCxJQUFJLENBQUMsY0FBYyxFQUNuQjtnQkFDRSxHQUFHLElBQUksQ0FBQyxPQUFPO2dCQUNmLEdBQUcsT0FBTztnQkFDVixjQUFjLEVBQUUsSUFBSSxDQUFDLGNBQWM7YUFDcEMsRUFDRCxDQUFDLGFBQWEsRUFBRSxlQUFlLEVBQUUsV0FBVyxFQUFFLEVBQUU7Z0JBQzlDLElBQUksQ0FBQyxhQUFhLENBQUMsYUFBYSxFQUFFLFdBQVcsQ0FBQyxDQUFDO1lBQ2pELENBQUMsQ0FDRixDQUFDO1lBQ0YsU0FBUyxDQUFDLE1BQU0sR0FBRyxLQUFLO2dCQUN0QixDQUFDLENBQUMsNEJBQWdCLENBQUMsTUFBTTtnQkFDekIsQ0FBQyxDQUFDLDRCQUFnQixDQUFDLFFBQVEsQ0FBQztZQUM5QixPQUFPLEtBQUssQ0FBQztRQUNmLENBQUMsQ0FDRixDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0gsT0FBTyxDQUFDLE9BQXlCO1FBQy9CLE9BQU8sSUFBQSwrQkFBbUIsRUFDeEIsMEJBQWMsQ0FBQyxLQUFLLEVBQ3BCLElBQUksQ0FBQyxjQUFjLEVBQ25CLCtCQUFtQixDQUFDLE9BQU8sRUFDM0IsS0FBSyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsRUFBRTtZQUN6QixNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQztnQkFDOUIsU0FBUyxFQUFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsU0FBUztnQkFDckMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUU7Z0JBQ2hDLGNBQWMsRUFBRSxJQUFJLENBQUMsY0FBYzthQUNwQyxDQUFDLENBQUM7WUFDSCxTQUFTLENBQUMsTUFBTSxHQUFHLDRCQUFnQixDQUFDLE9BQU8sQ0FBQztRQUM5QyxDQUFDLENBQ0YsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxVQUFVLENBQUMsT0FBeUI7UUFDeEMsT0FBTyxDQUFDLE1BQU0sSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLFNBQVMsQ0FBQztJQUN0RCxDQUFDO0lBRUQsNERBQTREO0lBQzVELEtBQUssQ0FBQyxRQUFRLENBQ1osT0FHQyxFQUNELEtBQW9CO1FBRXBCLE1BQU0sSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3JELENBQUM7SUFFRCx5REFBeUQ7SUFDekQsS0FBSyxDQUFDLElBQUksQ0FDUixPQUtDLEVBQ0QsT0FBdUI7UUFFdkIsTUFBTSxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDdEQsQ0FBQztJQUVELHNEQUFzRDtJQUN0RCxLQUFLLENBQUMsYUFBYSxDQUNqQixPQUdDLEVBQ0QsS0FBa0QsRUFDbEQsU0FBeUQsRUFDekQsT0FBb0MsRUFDcEMsY0FBYyxHQUFHLElBQUksQ0FBQyxjQUFjO1FBRXBDLE9BQU8sSUFBQSwrQkFBbUIsRUFDeEIsMEJBQWMsQ0FBQyxLQUFLLEVBQ3BCLGNBQWMsRUFDZCwrQkFBbUIsQ0FBQyxhQUFhLEVBQ2pDLEtBQUssRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEVBQUU7WUFDeEIsTUFBTSxVQUFVLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUM1QixJQUFJLFVBQVUsRUFBRSxDQUFDO2dCQUNmLElBQUksSUFBSSxDQUFDLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsRUFBRSxDQUFDO29CQUNsRCxTQUFTLENBQUMsTUFBTSxHQUFHLDRCQUFnQixDQUFDLFNBQVMsQ0FBQztvQkFDOUMsU0FBUyxDQUFDLGNBQWMsR0FBRyxHQUFHLENBQUM7b0JBQy9CLE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUM7Z0JBQ3pCLENBQUM7Z0JBRUQsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQztnQkFDN0QsSUFBSSxZQUFZLEVBQUUsQ0FBQztvQkFDakIsTUFBTSxNQUFNLEdBQUcsTUFBTSxZQUFZLENBQUM7b0JBQ2xDLFNBQVMsQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLE1BQU0sS0FBSyxHQUFHO3dCQUN0QyxDQUFDLENBQUMsNEJBQWdCLENBQUMsU0FBUzt3QkFDNUIsQ0FBQyxDQUFDLDRCQUFnQixDQUFDLE9BQU8sQ0FBQztvQkFDN0IsU0FBUyxDQUFDLGNBQWMsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO29CQUN6QyxPQUFPLE1BQU0sQ0FBQztnQkFDaEIsQ0FBQztZQUNILENBQUM7WUFFRCxNQUFNLGVBQWUsR0FBRyxLQUFLLElBRTNCLEVBQUU7Z0JBQ0YsSUFBSSxLQUFvQixDQUFDO2dCQUN6QixJQUFJLENBQUM7b0JBQ0gsS0FBSyxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUFDO3dCQUM1QyxTQUFTLEVBQUUsT0FBTyxDQUFDLFFBQVEsQ0FBQyxTQUFTO3dCQUNyQyxNQUFNLEVBQUUsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRTt3QkFDaEMsY0FBYzt3QkFDZCxlQUFlLEVBQUU7NEJBQ2YsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLO3lCQUNuQjtxQkFDRixDQUFDLENBQUM7Z0JBQ0wsQ0FBQztnQkFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO29CQUNmLElBQUksQ0FBQyxDQUFDLEtBQUssWUFBWSxrQkFBVSxDQUFDLEVBQUUsQ0FBQzt3QkFDbkMsOERBQThEO3dCQUM5RCx3REFBd0Q7d0JBQ3hELE1BQU0sS0FBSyxDQUFDO29CQUNkLENBQUM7b0JBRUQsSUFBSSwwQkFBMEIsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO3dCQUN0Qyw0REFBNEQ7d0JBQzVELGtEQUFrRDt3QkFDbEQsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO3dCQUN6QixTQUFTLENBQUMsZUFBZTs0QkFDdkIsSUFBSSxDQUFDLG9CQUFvQixLQUFLLFNBQVMsQ0FBQzt3QkFDMUMsU0FBUyxDQUFDLE1BQU0sR0FBRyw0QkFBZ0IsQ0FBQyxPQUFPLENBQUM7d0JBQzVDLFNBQVMsQ0FBQyxjQUFjLEdBQUcsR0FBRyxDQUFDO3dCQUMvQixPQUFPLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsRUFBRSxFQUFFLGNBQWMsQ0FBQyxDQUFDO29CQUM3RCxDQUFDO29CQUVELDREQUE0RDtvQkFDNUQsOERBQThEO29CQUM5RCxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO29CQUMzQixNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7b0JBQ3pCLFNBQVMsQ0FBQyxlQUFlO3dCQUN2QixJQUFJLENBQUMsb0JBQW9CLEtBQUssU0FBUyxDQUFDO29CQUMxQyxTQUFTLENBQUMsTUFBTSxHQUFHLDRCQUFnQixDQUFDLE9BQU8sQ0FBQztvQkFDNUMsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLE1BQU0sSUFBSSxHQUFHLENBQUM7b0JBQ25DLFNBQVMsQ0FBQyxjQUFjLEdBQUcsTUFBTSxDQUFDO29CQUNsQyxJQUFBLHNDQUEwQixFQUN4QixJQUFJLEVBQ0osU0FBUyxFQUNULEtBQUssRUFDTCxnQ0FBb0IsQ0FBQyxTQUFTLENBQy9CLENBQUM7b0JBQ0YsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO29CQUNmLE9BQU8sRUFBRSxNQUFNLEVBQUUsQ0FBQztnQkFDcEIsQ0FBQztnQkFFRCxJQUFJLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxDQUFDO29CQUNsQixJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO29CQUMzQixNQUFNLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7b0JBQ3pCLFNBQVMsQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixLQUFLLFNBQVMsQ0FBQztvQkFDcEUsU0FBUyxDQUFDLE1BQU0sR0FBRyw0QkFBZ0IsQ0FBQyxPQUFPLENBQUM7b0JBQzVDLFNBQVMsQ0FBQyxjQUFjLEdBQUcsR0FBRyxDQUFDO29CQUMvQixPQUFPLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLENBQUMsRUFBRSxFQUFFLGNBQWMsQ0FBQyxDQUFDO2dCQUM3RCxDQUFDO2dCQUVELElBQUksVUFBVSxFQUFFLENBQUM7b0JBQ2YsSUFBSSxDQUFDLHFCQUFxQixDQUFDLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztnQkFDbEQsQ0FBQztnQkFDRCxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO2dCQUMzQixNQUFNLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDdkIsU0FBUyxDQUFDLGVBQWUsR0FBRyxJQUFJLENBQUMscUJBQXFCLEtBQUssU0FBUyxDQUFDO2dCQUNyRSxTQUFTLENBQUMsTUFBTSxHQUFHLDRCQUFnQixDQUFDLE9BQU8sQ0FBQztnQkFDNUMsU0FBUyxDQUFDLGNBQWMsR0FBRyxHQUFHLENBQUM7Z0JBQy9CLE9BQU8sRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUM7WUFDekIsQ0FBQyxDQUFDO1lBRUYsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUNoQixPQUFPLGVBQWUsRUFBRSxDQUFDO1lBQzNCLENBQUM7WUFFRCxNQUFNLElBQUksR0FBRyxlQUFlLEVBQUUsQ0FBQztZQUMvQixJQUFJLENBQUMsa0JBQWtCLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUM5QyxJQUFJLENBQUM7Z0JBQ0gsT0FBTyxNQUFNLElBQUksQ0FBQztZQUNwQixDQUFDO29CQUFTLENBQUM7Z0JBQ1QsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUM3QyxDQUFDO1FBQ0gsQ0FBQyxDQUNGLENBQUM7SUFDSixDQUFDO0lBRUQsc0VBQXNFO0lBQ3RFLEtBQUssQ0FBQyxXQUFXLENBQ2YsT0FHQyxFQUNELEtBQXlCLEVBQ3pCLFNBQXlELEVBQ3pELE9BQW9DO1FBRXBDLE9BQU8sSUFBQSwrQkFBbUIsRUFDeEIsMEJBQWMsQ0FBQyxLQUFLLEVBQ3BCLElBQUksQ0FBQyxjQUFjLEVBQ25CLCtCQUFtQixDQUFDLFdBQVcsRUFDL0IsS0FBSyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsRUFBRTtZQUN4QixJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQ1gsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQ2QsMENBQTBDLE9BQU8sQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLEVBQUUsZUFBZSxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLEdBQUcsQ0FDckgsQ0FBQztnQkFDRixTQUFTLENBQUMsTUFBTSxHQUFHLDRCQUFnQixDQUFDLE9BQU8sQ0FBQztnQkFDNUMsU0FBUyxDQUFDLGNBQWMsR0FBRyxHQUFHLENBQUM7Z0JBQy9CLE9BQU8sU0FBUyxDQUFDO1lBQ25CLENBQUM7WUFFRCxJQUFJLEtBQW9CLENBQUM7WUFDekIsSUFBSSxDQUFDO2dCQUNILEtBQUssR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FBQztvQkFDdkMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsU0FBUztvQkFDckMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUU7b0JBQ2hDLGNBQWMsRUFBRSxJQUFJLENBQUMsY0FBYztvQkFDbkMsSUFBSSxFQUFFLEtBQUs7aUJBQ1osQ0FBQyxDQUFDO1lBQ0wsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsSUFBSSxDQUFDLENBQUMsS0FBSyxZQUFZLGtCQUFVLENBQUMsRUFBRSxDQUFDO29CQUNuQyxNQUFNLEtBQUssQ0FBQztnQkFDZCxDQUFDO2dCQUVELElBQUksMEJBQTBCLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztvQkFDdEMsU0FBUyxDQUFDLE1BQU0sR0FBRyw0QkFBZ0IsQ0FBQyxPQUFPLENBQUM7b0JBQzVDLFNBQVMsQ0FBQyxjQUFjLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQztvQkFDeEMsT0FBTyxTQUFTLENBQUM7Z0JBQ25CLENBQUM7Z0JBRUQsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQztnQkFDM0IsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO2dCQUN6QixTQUFTLENBQUMsZUFBZSxHQUFHLElBQUksQ0FBQyxvQkFBb0IsS0FBSyxTQUFTLENBQUM7Z0JBQ3BFLFNBQVMsQ0FBQyxNQUFNLEdBQUcsNEJBQWdCLENBQUMsT0FBTyxDQUFDO2dCQUU1QyxNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsTUFBTSxJQUFJLEdBQUcsQ0FBQztnQkFDbkMsU0FBUyxDQUFDLGNBQWMsR0FBRyxNQUFNLENBQUM7Z0JBQ2xDLElBQUEsc0NBQTBCLEVBQ3hCLElBQUksRUFDSixTQUFTLEVBQ1QsS0FBSyxFQUNMLGdDQUFvQixDQUFDLFNBQVMsQ0FDL0IsQ0FBQztnQkFDRixPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBQ2YsT0FBTyxFQUFFLE1BQU0sRUFBRSxDQUFDO1lBQ3BCLENBQUM7WUFFRCxJQUFJLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxDQUFDO2dCQUNsQixTQUFTLENBQUMsTUFBTSxHQUFHLDRCQUFnQixDQUFDLE9BQU8sQ0FBQztnQkFDNUMsU0FBUyxDQUFDLGNBQWMsR0FBRyxHQUFHLENBQUM7Z0JBQy9CLE9BQU8sU0FBUyxDQUFDO1lBQ25CLENBQUM7WUFFRCxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQzNCLE1BQU0sU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ3ZCLFNBQVMsQ0FBQyxlQUFlLEdBQUcsSUFBSSxDQUFDLHFCQUFxQixLQUFLLFNBQVMsQ0FBQztZQUNyRSxTQUFTLENBQUMsTUFBTSxHQUFHLDRCQUFnQixDQUFDLE9BQU8sQ0FBQztZQUM1QyxTQUFTLENBQUMsY0FBYyxHQUFHLEdBQUcsQ0FBQztZQUMvQixPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxDQUFDO1FBQ3pCLENBQUMsQ0FDRixDQUFDO0lBQ0osQ0FBQztJQUVELDJFQUEyRTtJQUMzRSxhQUFhLENBQUMsT0FBeUIsRUFBRSxXQUFvQjtRQUMzRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDN0IsSUFBSSxDQUFDLGVBQWUsQ0FBQyxPQUFPLEVBQUUsS0FBSyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQ2hELElBQUksV0FBVyxFQUFFLENBQUM7WUFDaEIsSUFBSSxDQUFDLGVBQWUsQ0FBQyxPQUFPLEVBQUUsSUFBSSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQ2pELENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztRQUN6QyxDQUFDO0lBQ0gsQ0FBQztJQUVELHFFQUFxRTtJQUNyRSxVQUFVLENBQUMsT0FBeUIsRUFBRSxPQUFnQjtRQUNwRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDdkIsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUMvQyxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQztRQUN0QyxJQUFJLFNBQVMsRUFBRSxDQUFDO1lBQ2QsTUFBTSxVQUFVLEdBQUcsU0FBUyxDQUFDLEdBQUcsQ0FBUyxRQUFRLENBQUMsQ0FBQztZQUNuRCxJQUFJLFVBQVUsS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDN0IsSUFBSSxHQUFHLEdBQUcsVUFBVSxHQUFHLFNBQVMsQ0FBQyxjQUFjLEVBQUUsQ0FBQztvQkFDaEQsT0FBTyxVQUFVLENBQUM7Z0JBQ3BCLENBQUM7Z0JBQ0QsU0FBUyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUM3QixDQUFDO1lBQ0QsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztRQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDaEMsT0FBTyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ3RELENBQUM7SUFFRCxrRUFBa0U7SUFDbEUsWUFBWSxDQUFDLE9BQXlCO1FBQ3BDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDeEMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRU8sZUFBZSxDQUNyQixPQUF5QixFQUN6QixPQUFnQixFQUNoQixLQUFhO1FBRWIsTUFBTSxTQUFTLEdBQUcsT0FBTyxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUM7UUFDdEMsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNkLFNBQVMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxPQUFPLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUNwRCxPQUFPO1FBQ1QsQ0FBQztRQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDekMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsT0FBTyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDcEQsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUVPLGtCQUFrQixDQUN4QixPQUF5QixFQUN6QixPQUFnQjtRQUVoQixPQUFPLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1FBQzNELElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQ25FLENBQUM7SUFFTyxlQUFlLENBQUMsT0FBZ0I7UUFDdEMsT0FBTyxtQkFBbUIsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsRUFBRSxFQUFFLENBQUM7SUFDeEYsQ0FBQztJQUVPLFVBQVUsQ0FBQyxPQUFnQjtRQUNqQyxPQUFPLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDO0lBQ2hFLENBQUM7SUFFTyxpQkFBaUIsQ0FBQyxPQUF5QjtRQUNqRCxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUM7WUFDcEIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsRUFBRTtZQUNoQyxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFO1NBQ3pCLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFTyxZQUFZLENBQUMsT0FBNEIsRUFBRSxHQUFXO1FBQzVELEtBQUssTUFBTSxDQUFDLFVBQVUsRUFBRSxTQUFTLENBQUMsSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUM5QyxJQUFJLEdBQUcsR0FBRyxTQUFTLElBQUksU0FBUyxDQUFDLGNBQWMsRUFBRSxDQUFDO2dCQUNoRCxPQUFPLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQzdCLENBQUM7UUFDSCxDQUFDO1FBRUQsSUFBSSxPQUFPLENBQUMsSUFBSSxJQUFJLFNBQVMsQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1lBQ2xELE9BQU87UUFDVCxDQUFDO1FBRUQsS0FBSyxNQUFNLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxHQUFHLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQzthQUM5QyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEtBQUssQ0FBQyxFQUFFLEVBQUUsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDO2FBQzNDLEtBQUssQ0FBQyxTQUFTLENBQUMsbUJBQW1CLENBQUMsRUFBRSxDQUFDO1lBQ3hDLE9BQU8sQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDN0IsQ0FBQztJQUNILENBQUM7SUFFTyxvQkFBb0IsQ0FDMUIsRUFBVSxFQUNWLGNBQWMsR0FBRyxJQUFJLENBQUMsY0FBYztRQUVwQyxPQUFPO1lBQ0wsTUFBTSxFQUFFLEdBQUc7WUFDWCxJQUFJLEVBQUU7Z0JBQ0osRUFBRTtnQkFDRixjQUFjO2dCQUNkLGFBQWEsRUFBRSw2QkFBNkI7YUFDN0M7U0FDRixDQUFDO0lBQ0osQ0FBQztJQUVPLG1CQUFtQixDQUN6QixPQUF5QixFQUN6QixVQUFrQjtRQUVsQixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDdkIsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLE9BQU8sRUFBRSxHQUFHLENBQUMsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUN2RSxJQUNFLGNBQWMsS0FBSyxTQUFTO1lBQzVCLEdBQUcsR0FBRyxjQUFjLEdBQUcsU0FBUyxDQUFDLGVBQWUsRUFDaEQsQ0FBQztZQUNELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUVELElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNsQyxPQUFPLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUVPLHFCQUFxQixDQUMzQixPQUF5QixFQUN6QixVQUFrQjtRQUVsQixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDN0IsSUFBSSxDQUFDLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyxVQUFVLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDbkQsSUFBSSxDQUFDLHVCQUF1QixDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRXhDLE1BQU0saUJBQWlCLEdBQUcsT0FBTyxDQUFDLEtBQUssRUFBRSxZQUFZLENBQUM7UUFDdEQsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7WUFDdkIsT0FBTztRQUNULENBQUM7UUFFRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsT0FBTyxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQzVELFNBQVMsQ0FBQyxVQUFVLENBQUMsR0FBRyxTQUFTLENBQUM7UUFDbEMsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FDaEMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUM7YUFDdEIsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQzthQUMzQyxLQUFLLENBQUMsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxvQkFBb0IsQ0FBQyxDQUM1QyxDQUFDO1FBQ0YsaUJBQWlCLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUMvRCxDQUFDO0lBRU8sdUJBQXVCLENBQUMsR0FBVztRQUN6QyxLQUFLLE1BQU0sQ0FBQyxVQUFVLEVBQUUsU0FBUyxDQUFDLElBQUksSUFBSSxDQUFDLGtCQUFrQixFQUFFLENBQUM7WUFDOUQsSUFBSSxHQUFHLEdBQUcsU0FBUyxJQUFJLFNBQVMsQ0FBQyxlQUFlLEVBQUUsQ0FBQztnQkFDakQsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUM3QyxDQUFDO1FBQ0gsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksSUFBSSxTQUFTLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztZQUNuRSxPQUFPO1FBQ1QsQ0FBQztRQUVELEtBQUssTUFBTSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsT0FBTyxFQUFFLENBQUM7YUFDOUQsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxLQUFLLENBQUMsRUFBRSxFQUFFLENBQUMsS0FBSyxHQUFHLElBQUksQ0FBQzthQUMzQyxLQUFLLENBQUMsU0FBUyxDQUFDLG9CQUFvQixDQUFDLEVBQUUsQ0FBQztZQUN6QyxJQUFJLENBQUMsa0JBQWtCLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzdDLENBQUM7SUFDSCxDQUFDO0lBRU8sZ0JBQWdCLENBQ3RCLE9BQXlCLEVBQ3pCLEdBQVc7UUFFWCxNQUFNLGlCQUFpQixHQUFHLE9BQU8sQ0FBQyxLQUFLLEVBQUUsWUFBWSxDQUFDO1FBQ3RELElBQUksQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1lBQ3ZCLE9BQU8sRUFBRSxDQUFDO1FBQ1osQ0FBQztRQUVELE1BQU0sTUFBTSxHQUFHLGlCQUFpQixDQUFDLEdBQUcsQ0FDbEMsU0FBUyxDQUFDLGtCQUFrQixDQUM3QixJQUFJLEVBQUUsQ0FBQztRQUNSLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQ2hDLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO2FBQ25CLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxTQUFTLENBQUMsRUFBRSxFQUFFLENBQ3hCLE1BQU0sQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDO1lBQzFCLEdBQUcsR0FBRyxTQUFTLEdBQUcsU0FBUyxDQUFDLGVBQWUsQ0FDNUM7YUFDQSxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEtBQUssQ0FBQyxFQUFFLEVBQUUsQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDO2FBQzNDLEtBQUssQ0FBQyxDQUFDLEVBQUUsU0FBUyxDQUFDLG9CQUFvQixDQUFDLENBQzVDLENBQUM7UUFFRixJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxLQUFLLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDL0QsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztnQkFDdEMsaUJBQWlCLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1lBQ3pELENBQUM7aUJBQU0sQ0FBQztnQkFDTixpQkFBaUIsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLGtCQUFrQixFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQy9ELENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQzs7QUE3akJILDhCQThqQkM7QUFxQ0QseUVBQXlFO0FBQzVELFFBQUEsc0JBQXNCLEdBQTJEO0lBQzVGLHFCQUFxQixFQUFFLE9BQU87Q0FDL0IsQ0FBQztBQUVGLFNBQVMsb0JBQW9CLENBQUMsS0FBYztJQUMxQyxPQUFPLEtBQUssWUFBWSxrQkFBVSxJQUFJLEtBQUssQ0FBQyxNQUFNLEtBQUssR0FBRyxDQUFDO0FBQzdELENBQUM7QUFFRCxTQUFTLDBCQUEwQixDQUFDLEtBQWM7SUFDaEQsT0FBTyxLQUFLLFlBQVksa0JBQVU7UUFDaEMsQ0FBQyxLQUFLLENBQUMsTUFBTSxLQUFLLEdBQUcsSUFBSSxLQUFLLENBQUMsTUFBTSxLQUFLLEdBQUcsSUFBSSxLQUFLLENBQUMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0FBQzNFLENBQUMifQ==