@idp.global/sdk 18.0.0 → 18.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/changelog.md +28 -0
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts_relyingparty/classes.actorscope.d.ts +23 -0
  4. package/dist_ts_relyingparty/classes.actorscope.js +22 -0
  5. package/dist_ts_relyingparty/classes.authorityobserver.d.ts +81 -0
  6. package/dist_ts_relyingparty/classes.authorityobserver.js +185 -0
  7. package/dist_ts_relyingparty/classes.errors.d.ts +33 -0
  8. package/dist_ts_relyingparty/classes.errors.js +53 -0
  9. package/dist_ts_relyingparty/classes.idpoidcprovider.d.ts +142 -0
  10. package/dist_ts_relyingparty/classes.idpoidcprovider.js +255 -0
  11. package/dist_ts_relyingparty/classes.relyingpartyroutes.d.ts +84 -0
  12. package/dist_ts_relyingparty/classes.relyingpartyroutes.js +232 -0
  13. package/dist_ts_relyingparty/classes.relyingpartysessions.d.ts +210 -0
  14. package/dist_ts_relyingparty/classes.relyingpartysessions.js +786 -0
  15. package/dist_ts_relyingparty/classes.rpmodels.d.ts +79 -0
  16. package/dist_ts_relyingparty/classes.rpmodels.js +283 -0
  17. package/dist_ts_relyingparty/classes.secretbox.d.ts +14 -0
  18. package/dist_ts_relyingparty/classes.secretbox.js +45 -0
  19. package/dist_ts_relyingparty/classes.socketbinding.d.ts +75 -0
  20. package/dist_ts_relyingparty/classes.socketbinding.js +94 -0
  21. package/dist_ts_relyingparty/helpers.cookies.d.ts +11 -0
  22. package/dist_ts_relyingparty/helpers.cookies.js +62 -0
  23. package/dist_ts_relyingparty/helpers.crypto.d.ts +12 -0
  24. package/dist_ts_relyingparty/helpers.crypto.js +40 -0
  25. package/dist_ts_relyingparty/helpers.returnpath.d.ts +9 -0
  26. package/dist_ts_relyingparty/helpers.returnpath.js +39 -0
  27. package/dist_ts_relyingparty/index.d.ts +13 -0
  28. package/dist_ts_relyingparty/index.js +14 -0
  29. package/dist_ts_relyingparty/interfaces.d.ts +113 -0
  30. package/dist_ts_relyingparty/interfaces.js +2 -0
  31. package/dist_ts_relyingparty/plugins.d.ts +10 -0
  32. package/dist_ts_relyingparty/plugins.js +11 -0
  33. package/package.json +14 -11
  34. package/readme.md +158 -4
  35. package/ts/00_commitinfo_data.ts +1 -1
  36. package/ts_relyingparty/classes.actorscope.ts +39 -0
  37. package/ts_relyingparty/classes.authorityobserver.ts +253 -0
  38. package/ts_relyingparty/classes.errors.ts +69 -0
  39. package/ts_relyingparty/classes.idpoidcprovider.ts +386 -0
  40. package/ts_relyingparty/classes.relyingpartyroutes.ts +286 -0
  41. package/ts_relyingparty/classes.relyingpartysessions.ts +970 -0
  42. package/ts_relyingparty/classes.rpmodels.ts +146 -0
  43. package/ts_relyingparty/classes.secretbox.ts +49 -0
  44. package/ts_relyingparty/classes.socketbinding.ts +142 -0
  45. package/ts_relyingparty/helpers.cookies.ts +79 -0
  46. package/ts_relyingparty/helpers.crypto.ts +49 -0
  47. package/ts_relyingparty/helpers.returnpath.ts +44 -0
  48. package/ts_relyingparty/index.ts +13 -0
  49. package/ts_relyingparty/interfaces.ts +133 -0
  50. package/ts_relyingparty/plugins.ts +17 -0
@@ -0,0 +1,786 @@
1
+ import { SecretBox } from './classes.secretbox.js';
2
+ import { AuthorityTokenError, LoginCallbackError, RelyingPartyError, RelyingPartyRefusal, } from './classes.errors.js';
3
+ import { OidcCompletionError, OidcRefreshRefusedError, } from './classes.idpoidcprovider.js';
4
+ import { parseCookies, serializeClearedCookie, serializeCookie } from './helpers.cookies.js';
5
+ import { deriveCsrfToken, randomToken, sha256, timingSafeEqualString } from './helpers.crypto.js';
6
+ import { sanitizeReturnPath } from './helpers.returnpath.js';
7
+ export const RELYING_PARTY_DEFAULT_SESSION_MAX_AGE_SECONDS = 12 * 60 * 60;
8
+ export const RELYING_PARTY_DEFAULT_LOGIN_PATH_PREFIX = '/auth/';
9
+ /** The label the CSRF HMAC starts with unless the application names its own. */
10
+ export const RELYING_PARTY_DEFAULT_CSRF_LABEL = 'idp.global/sdk relyingparty csrf' + String.fromCharCode(0);
11
+ const MAX_COOKIE_TOKEN_LENGTH = 256;
12
+ const SESSION_TOUCH_INTERVAL_MS = 60_000;
13
+ /** A sign-in transaction has to survive an interactive login at the provider, not a working day. */
14
+ export const LOGIN_ATTEMPT_MAX_AGE_SECONDS = 10 * 60;
15
+ /** Attempts are swept once they are this much older than their expiry. */
16
+ const LOGIN_ATTEMPT_SWEEP_GRACE_MS = 60 * 60 * 1_000;
17
+ /** How many of them one sign-in removes; the next sign-in takes the following page. */
18
+ const LOGIN_ATTEMPT_SWEEP_BATCH = 100;
19
+ /**
20
+ * How long ending a session waits for the provider to take its tokens back.
21
+ * The local session is already revoked when that call runs.
22
+ */
23
+ export const TOKEN_REVOCATION_BUDGET_MS = 3_000;
24
+ /** How long before its expiry the provider access token is rotated. */
25
+ export const ACCESS_TOKEN_REFRESH_SKEW_MS = 60_000;
26
+ /**
27
+ * What the refresh lock adds on top of the provider request it has to cover:
28
+ * the claim and the store around it, and clock disagreement between replicas.
29
+ */
30
+ export const REFRESH_LOCK_MARGIN_MS = 20_000;
31
+ const REVOKED_TOKEN_FIELDS = { sealedAccessToken: '', sealedRefreshToken: '', refreshLockUntil: 0 };
32
+ const errorMessage = (errorArg) => (errorArg instanceof Error ? errorArg.message : String(errorArg));
33
+ /** Browsers store a cookie with one of these prefixes only when it is set `Secure`; they match it case-insensitively. */
34
+ const SECURE_COOKIE_NAME_PREFIX = /^__(host|secure)-/i;
35
+ const cookieNameFor = (configuredArg, defaultArg, secureArg, labelArg) => {
36
+ if (configuredArg === undefined) {
37
+ return defaultArg;
38
+ }
39
+ if (!secureArg && SECURE_COOKIE_NAME_PREFIX.test(configuredArg)) {
40
+ throw new Error(`cookieNames.${labelArg} "${configuredArg}" needs an https publicOrigin: `
41
+ + 'browsers drop a __Host- or __Secure- cookie that is not set Secure');
42
+ }
43
+ return configuredArg;
44
+ };
45
+ /**
46
+ * The relying-party session layer: provider logins through the authorization
47
+ * code flow with PKCE, HttpOnly cookie sessions with a session-bound CSRF
48
+ * token, provider tokens sealed at rest and rotated under a cross-replica
49
+ * lock, and session ends that hand the tokens back.
50
+ *
51
+ * Nothing here runs on a timer: expired login attempts are swept when the next
52
+ * login starts, and the only timer — the revocation budget — is unreferenced
53
+ * and cleared on every path.
54
+ */
55
+ export class RelyingPartySessions {
56
+ publicOrigin;
57
+ secureCookies;
58
+ sessionCookieName;
59
+ intentCookieName;
60
+ loginPathPrefix;
61
+ sessionMaxAgeSeconds;
62
+ #models;
63
+ #principals;
64
+ #provider;
65
+ #secretBox;
66
+ #csrfKey;
67
+ #csrfLabel;
68
+ #hooks;
69
+ #now;
70
+ /** one rotation per session in this process, however many requests need it at once */
71
+ #refreshesInFlight = new Map();
72
+ constructor(optionsArg) {
73
+ const origin = new URL(optionsArg.publicOrigin);
74
+ if (origin.origin !== optionsArg.publicOrigin) {
75
+ throw new Error('publicOrigin must be a bare origin such as https://app.example');
76
+ }
77
+ if (!Buffer.isBuffer(optionsArg.csrfKey) || optionsArg.csrfKey.length !== 32) {
78
+ throw new Error('csrfKey must be 32 bytes');
79
+ }
80
+ const maxAge = optionsArg.sessionMaxAgeSeconds ?? RELYING_PARTY_DEFAULT_SESSION_MAX_AGE_SECONDS;
81
+ if (!Number.isSafeInteger(maxAge) || maxAge < 1) {
82
+ throw new Error('sessionMaxAgeSeconds must be a positive integer');
83
+ }
84
+ this.publicOrigin = origin.origin;
85
+ this.secureCookies = origin.protocol === 'https:';
86
+ const prefix = this.secureCookies ? '__Host-' : '';
87
+ this.sessionCookieName = cookieNameFor(optionsArg.cookieNames?.session, prefix + 'idp_rp_session', this.secureCookies, 'session');
88
+ this.intentCookieName = cookieNameFor(optionsArg.cookieNames?.intent, prefix + 'idp_rp_intent', this.secureCookies, 'intent');
89
+ this.loginPathPrefix = optionsArg.loginPathPrefix ?? RELYING_PARTY_DEFAULT_LOGIN_PATH_PREFIX;
90
+ if (!this.loginPathPrefix.startsWith('/') || !this.loginPathPrefix.endsWith('/')) {
91
+ throw new Error('loginPathPrefix must start and end with a slash');
92
+ }
93
+ this.sessionMaxAgeSeconds = maxAge;
94
+ this.#models = optionsArg.models;
95
+ this.#principals = optionsArg.principals;
96
+ this.#provider = optionsArg.provider;
97
+ this.#secretBox = new SecretBox(optionsArg.sessionEncryptionKey);
98
+ this.#csrfKey = Buffer.from(optionsArg.csrfKey);
99
+ this.#csrfLabel = optionsArg.csrfLabel ?? RELYING_PARTY_DEFAULT_CSRF_LABEL;
100
+ this.#hooks = optionsArg.hooks ?? {};
101
+ this.#now = optionsArg.now ?? (() => Date.now());
102
+ }
103
+ /** Whether provider logins are offered at all. */
104
+ get providerLoginEnabled() {
105
+ return this.#provider !== undefined;
106
+ }
107
+ /**
108
+ * How long one replica holds the right to rotate a session's tokens. The
109
+ * provider detects reuse of a superseded refresh token and kills the family,
110
+ * so the lock strictly outlives its longest possible holder: one provider
111
+ * request at its full deadline plus the margin.
112
+ */
113
+ get refreshLockMs() {
114
+ return (this.#provider?.requestTimeoutSeconds ?? 0) * 1_000 + REFRESH_LOCK_MARGIN_MS;
115
+ }
116
+ // ==========================================================================
117
+ // sessions
118
+ // ==========================================================================
119
+ /**
120
+ * Opens a session the application authenticated itself (a local password
121
+ * login). It carries no provider tokens.
122
+ */
123
+ async openSession(optionsArg) {
124
+ if (typeof optionsArg?.principalId !== 'string' || optionsArg.principalId.length === 0) {
125
+ throw new Error('openSession needs a principal id');
126
+ }
127
+ const token = randomToken(32);
128
+ const session = this.newSessionDoc(token, optionsArg.principalId);
129
+ await session.save();
130
+ return this.opening(session, token);
131
+ }
132
+ /** The live session behind the session cookie, or `null`. A read touches it at most once a minute. */
133
+ async resolveCookieSession(cookieHeaderArg) {
134
+ const token = parseCookies(cookieHeaderArg).get(this.sessionCookieName);
135
+ if (!token || token.length > MAX_COOKIE_TOKEN_LENGTH) {
136
+ return null;
137
+ }
138
+ const session = await this.#models.Session.getInstance({ tokenHash: sha256(token) });
139
+ const live = await this.liveSession(session);
140
+ if (!live) {
141
+ return null;
142
+ }
143
+ return { session: this.snapshot(live), csrfToken: this.csrfTokenOf(token) };
144
+ }
145
+ /**
146
+ * Re-checks a session by its id with the same strength as a cookie request:
147
+ * live, its principal active, and touched under the liveness it was read
148
+ * with. What a pinned socket connection runs per request.
149
+ */
150
+ async revalidateSession(sessionIdArg) {
151
+ if (typeof sessionIdArg !== 'string' || sessionIdArg.length === 0) {
152
+ return null;
153
+ }
154
+ const live = await this.liveSession(await this.#models.Session.getInstance({ id: sessionIdArg }));
155
+ return live ? this.snapshot(live) : null;
156
+ }
157
+ /** Whether a session is live right now, without touching it; what a sweep asks. */
158
+ async isSessionLive(sessionIdArg) {
159
+ if (typeof sessionIdArg !== 'string' || sessionIdArg.length === 0) {
160
+ return false;
161
+ }
162
+ const session = await this.#models.Session.getInstance({ id: sessionIdArg });
163
+ if (!session || session.revokedAt !== undefined || session.expiresAt <= this.#now()) {
164
+ return false;
165
+ }
166
+ return this.#principals.isActive(session.userId);
167
+ }
168
+ validateCsrf(resolvedArg, candidateArg) {
169
+ return typeof candidateArg === 'string' && timingSafeEqualString(resolvedArg.csrfToken, candidateArg);
170
+ }
171
+ /** Ends one session; see `endSession`. */
172
+ async logout(sessionIdArg) {
173
+ return this.endSession(sessionIdArg);
174
+ }
175
+ /**
176
+ * Revokes a session once, with one conditional update: a touch running
177
+ * beside it cannot write the revocation away, and a second call finds
178
+ * nothing. Its provider tokens go back within `TOKEN_REVOCATION_BUDGET_MS`,
179
+ * after the local revocation, which never depends on the provider. Answers
180
+ * whether this call ended it.
181
+ */
182
+ async endSession(sessionIdArg, reasonArg = 'logout') {
183
+ const ended = await this.revokeSession(sessionIdArg, reasonArg);
184
+ if (ended) {
185
+ await this.revokeSealedTokens(ended);
186
+ }
187
+ return ended !== null;
188
+ }
189
+ /**
190
+ * Ends every live session of a principal but `keepSessionId` (a password
191
+ * change keeps the session that asked for it) and answers how many ended.
192
+ */
193
+ async endSessionsOfPrincipal(principalIdArg, optionsArg = {}) {
194
+ let ended = 0;
195
+ const sessions = await this.#models.Session.getInstances({
196
+ userId: principalIdArg,
197
+ revokedAt: { $exists: false },
198
+ });
199
+ for (const session of sessions) {
200
+ if (session.id === optionsArg.keepSessionId) {
201
+ continue;
202
+ }
203
+ if (await this.endSession(session.id)) {
204
+ ended += 1;
205
+ }
206
+ }
207
+ return ended;
208
+ }
209
+ clearSessionCookie() {
210
+ return serializeClearedCookie(this.sessionCookieName, {
211
+ httpOnly: true,
212
+ secure: this.secureCookies,
213
+ sameSite: 'Lax',
214
+ });
215
+ }
216
+ clearIntentCookie() {
217
+ return serializeClearedCookie(this.intentCookieName, {
218
+ httpOnly: true,
219
+ secure: this.secureCookies,
220
+ sameSite: 'Lax',
221
+ });
222
+ }
223
+ // ==========================================================================
224
+ // provider login
225
+ // ==========================================================================
226
+ /**
227
+ * Starts a sign-in transaction: one attempt document with the PKCE verifier
228
+ * sealed next to it, and an intent cookie that binds the callback to this
229
+ * browser.
230
+ */
231
+ async startLogin(returnPathArg) {
232
+ const provider = this.requireProvider();
233
+ await this.sweepExpiredAttempts();
234
+ const state = randomToken(32);
235
+ const nonce = randomToken(32);
236
+ const pkceVerifier = randomToken(32);
237
+ const bindingToken = randomToken(32);
238
+ const now = this.#now();
239
+ const attempt = new this.#models.LoginAttempt();
240
+ attempt.id = 'lga_' + randomToken(18);
241
+ attempt.stateHash = sha256(state);
242
+ attempt.bindingTokenHash = sha256(bindingToken);
243
+ attempt.sealedPkceVerifier = this.#secretBox.seal(pkceVerifier);
244
+ attempt.nonce = nonce;
245
+ attempt.returnPath = sanitizeReturnPath(returnPathArg, this.publicOrigin, this.loginPathPrefix);
246
+ attempt.createdAt = now;
247
+ attempt.expiresAt = now + LOGIN_ATTEMPT_MAX_AGE_SECONDS * 1_000;
248
+ await attempt.save();
249
+ return {
250
+ authorizationUrl: await provider.buildAuthorizationUrl({ state, nonce, pkceVerifier }),
251
+ intentCookie: serializeCookie(this.intentCookieName, bindingToken, {
252
+ httpOnly: true,
253
+ secure: this.secureCookies,
254
+ sameSite: 'Lax',
255
+ maxAgeSeconds: LOGIN_ATTEMPT_MAX_AGE_SECONDS,
256
+ }),
257
+ };
258
+ }
259
+ /**
260
+ * Consumes the attempt exactly once, exchanges the code, admits the
261
+ * principal and opens a session. A replayed callback, a foreign intent
262
+ * cookie and an expired attempt all find nothing to consume. Nothing of a
263
+ * failed login survives: its tokens are handed back before the failure
264
+ * leaves.
265
+ */
266
+ async completeLogin(callbackUrlArg, cookieHeaderArg) {
267
+ const provider = this.requireProvider();
268
+ const states = callbackUrlArg.searchParams.getAll('state');
269
+ if (states.length !== 1 || !states[0]) {
270
+ throw new RelyingPartyError('invalid_login_callback');
271
+ }
272
+ const bindingToken = parseCookies(cookieHeaderArg).get(this.intentCookieName);
273
+ if (!bindingToken || bindingToken.length > MAX_COOKIE_TOKEN_LENGTH) {
274
+ throw new RelyingPartyError('missing_login_intent');
275
+ }
276
+ const now = this.#now();
277
+ const claimed = await this.#models.LoginAttempt.atomicFindOneAndUpdate({
278
+ stateHash: sha256(states[0]),
279
+ bindingTokenHash: sha256(bindingToken),
280
+ consumedAt: null,
281
+ expiresAt: { $gt: now },
282
+ }, { $set: { consumedAt: now } }, { returnDocument: 'after' });
283
+ if (claimed.status !== 'matched' || !claimed.document) {
284
+ throw new RelyingPartyError('invalid_or_replayed_login');
285
+ }
286
+ const attempt = claimed.document;
287
+ let completion;
288
+ let stage = 'token_exchange';
289
+ try {
290
+ completion = await provider.completeAuthorization({
291
+ callbackUrl: callbackUrlArg,
292
+ state: states[0],
293
+ nonce: attempt.nonce,
294
+ pkceVerifier: this.#secretBox.open(attempt.sealedPkceVerifier),
295
+ });
296
+ stage = 'principal_admission';
297
+ const { principalId } = await this.#principals.admit(completion);
298
+ stage = 'session_persistence';
299
+ return await this.createProviderSession(attempt, principalId, completion);
300
+ }
301
+ catch (error) {
302
+ if (completion) {
303
+ await this.revokeCompletionTokens(completion);
304
+ }
305
+ if (error instanceof RelyingPartyError || error instanceof RelyingPartyRefusal) {
306
+ throw error;
307
+ }
308
+ if (error instanceof OidcCompletionError) {
309
+ throw new LoginCallbackError(error.stage, error);
310
+ }
311
+ throw new LoginCallbackError(stage, error);
312
+ }
313
+ }
314
+ /**
315
+ * The provider sent the browser back without a code. The attempt is
316
+ * consumed so its state can never complete afterwards; a callback that
317
+ * matches no open attempt leaves nothing to do.
318
+ */
319
+ async abandonLogin(callbackUrlArg, cookieHeaderArg) {
320
+ const states = callbackUrlArg.searchParams.getAll('state');
321
+ const bindingToken = parseCookies(cookieHeaderArg).get(this.intentCookieName);
322
+ if (states.length !== 1
323
+ || !states[0]
324
+ || !bindingToken
325
+ || bindingToken.length > MAX_COOKIE_TOKEN_LENGTH) {
326
+ return;
327
+ }
328
+ await this.#models.LoginAttempt.atomicUpdate({ stateHash: sha256(states[0]), bindingTokenHash: sha256(bindingToken), consumedAt: null }, { $set: { consumedAt: this.#now() } });
329
+ }
330
+ /**
331
+ * The last step of a login: the browser arrives with both cookies, and the
332
+ * return path is released only when the session cookie belongs to the
333
+ * session this very attempt created.
334
+ */
335
+ async finalizeLogin(cookieHeaderArg) {
336
+ this.requireProvider();
337
+ const bindingToken = parseCookies(cookieHeaderArg).get(this.intentCookieName);
338
+ if (!bindingToken || bindingToken.length > MAX_COOKIE_TOKEN_LENGTH) {
339
+ throw new RelyingPartyError('invalid_login_finalize');
340
+ }
341
+ const attempt = await this.#models.LoginAttempt.getInstance({ bindingTokenHash: sha256(bindingToken) });
342
+ if (!attempt
343
+ || attempt.consumedAt === undefined
344
+ || attempt.consumedAt === null
345
+ || !attempt.createdSessionId
346
+ || attempt.expiresAt <= this.#now()) {
347
+ throw new RelyingPartyError('invalid_login_finalize');
348
+ }
349
+ const resolved = await this.resolveCookieSession(cookieHeaderArg);
350
+ if (!resolved
351
+ || resolved.session.id !== attempt.createdSessionId
352
+ || resolved.session.loginAttemptId !== attempt.id) {
353
+ throw new RelyingPartyError('invalid_login_finalize');
354
+ }
355
+ return sanitizeReturnPath(attempt.returnPath, this.publicOrigin, this.loginPathPrefix);
356
+ }
357
+ // ==========================================================================
358
+ // provider tokens
359
+ // ==========================================================================
360
+ /**
361
+ * An access token of this login session that is usable right now. A token
362
+ * within `ACCESS_TOKEN_REFRESH_SKEW_MS` of its expiry — or one whose expiry
363
+ * is unknown — is rotated first. The session has to belong to
364
+ * `expectedPrincipalIdArg`, so a credential is never evaluated with another
365
+ * person's tokens.
366
+ */
367
+ async liveProviderToken(sessionIdArg, expectedPrincipalIdArg) {
368
+ const session = await this.#models.Session.getInstance({ id: sessionIdArg });
369
+ const now = this.#now();
370
+ if (!session
371
+ || session.revokedAt !== undefined
372
+ || session.expiresAt <= now
373
+ || !session.idpSubject
374
+ || session.userId !== expectedPrincipalIdArg) {
375
+ throw new AuthorityTokenError('authority_login_required');
376
+ }
377
+ const subject = session.idpSubject;
378
+ const expiresAt = typeof session.accessTokenExpiresAt === 'number' ? session.accessTokenExpiresAt : 0;
379
+ const accessToken = this.openSealed(session.sealedAccessToken);
380
+ // an unknown expiry is rotated too: afterwards the lifetime is known
381
+ const due = accessToken === undefined || expiresAt === 0 || expiresAt <= now + ACCESS_TOKEN_REFRESH_SKEW_MS;
382
+ if (!due && accessToken !== undefined) {
383
+ return { accessToken, subject, expiresAt };
384
+ }
385
+ if (!session.sealedRefreshToken) {
386
+ if (accessToken !== undefined && (expiresAt === 0 || expiresAt > now)) {
387
+ // nothing to rotate with, and the token is not known to be dead
388
+ return { accessToken, subject, expiresAt };
389
+ }
390
+ await this.endProviderSession(sessionIdArg, 'access_token_unusable');
391
+ throw new AuthorityTokenError('authority_login_required');
392
+ }
393
+ return this.refreshProviderToken(sessionIdArg, subject);
394
+ }
395
+ async refreshProviderToken(sessionIdArg, subjectArg) {
396
+ const running = this.#refreshesInFlight.get(sessionIdArg);
397
+ if (running) {
398
+ return running;
399
+ }
400
+ const pending = this.runProviderRefresh(sessionIdArg, subjectArg).finally(() => {
401
+ this.#refreshesInFlight.delete(sessionIdArg);
402
+ });
403
+ this.#refreshesInFlight.set(sessionIdArg, pending);
404
+ return pending;
405
+ }
406
+ async runProviderRefresh(sessionIdArg, subjectArg) {
407
+ const provider = this.#provider;
408
+ if (!provider) {
409
+ throw new AuthorityTokenError('authority_unavailable');
410
+ }
411
+ const now = this.#now();
412
+ const lockUntil = now + this.refreshLockMs;
413
+ const claimed = await this.#models.Session.atomicFindOneAndUpdate({
414
+ id: sessionIdArg,
415
+ revokedAt: { $exists: false },
416
+ expiresAt: { $gt: now },
417
+ // a lock another replica holds is left alone until it expires
418
+ $or: [{ refreshLockUntil: { $lt: now } }, { refreshLockUntil: { $exists: false } }],
419
+ }, { $set: { refreshLockUntil: lockUntil } }, { returnDocument: 'after' });
420
+ if (claimed.status !== 'matched' || !claimed.document) {
421
+ return this.tokenAfterForeignRefresh(sessionIdArg, subjectArg);
422
+ }
423
+ const session = claimed.document;
424
+ // the lock holder decides on the state it just read: a rotation that
425
+ // landed in between is used as it stands, because spending a superseded
426
+ // refresh token would be reuse and would kill the family
427
+ const claimedExpiry = typeof session.accessTokenExpiresAt === 'number' ? session.accessTokenExpiresAt : 0;
428
+ const claimedToken = this.openSealed(session.sealedAccessToken);
429
+ if (claimedToken !== undefined && claimedExpiry > this.#now() + ACCESS_TOKEN_REFRESH_SKEW_MS) {
430
+ await this.#models.Session.atomicUpdate({ id: sessionIdArg, revokedAt: { $exists: false }, refreshLockUntil: lockUntil }, { $set: { refreshLockUntil: 0 } });
431
+ return { accessToken: claimedToken, subject: subjectArg, expiresAt: claimedExpiry };
432
+ }
433
+ const refreshToken = this.openSealed(session.sealedRefreshToken);
434
+ if (refreshToken === undefined) {
435
+ await this.endProviderSession(sessionIdArg, 'sealed_refresh_token_unreadable');
436
+ throw new AuthorityTokenError('authority_login_required');
437
+ }
438
+ let rotated;
439
+ try {
440
+ rotated = await provider.refreshTokens(refreshToken);
441
+ }
442
+ catch (error) {
443
+ if (error instanceof OidcRefreshRefusedError) {
444
+ await this.endProviderSession(sessionIdArg, 'refresh_token_refused');
445
+ throw new AuthorityTokenError('authority_login_required', error);
446
+ }
447
+ this.emit({ event: 'idp_token_refresh_failed', sessionId: sessionIdArg, message: errorMessage(error) });
448
+ throw new AuthorityTokenError('authority_unavailable', error);
449
+ }
450
+ // the rotated pair replaces the old one under this lock, and only while
451
+ // the session is live: a logout in between leaves the update unmatched
452
+ let matched;
453
+ try {
454
+ const written = await this.#models.Session.atomicUpdate({ id: sessionIdArg, revokedAt: { $exists: false }, refreshLockUntil: lockUntil }, {
455
+ $set: {
456
+ sealedAccessToken: this.#secretBox.seal(rotated.accessToken),
457
+ ...(rotated.refreshToken ? { sealedRefreshToken: this.#secretBox.seal(rotated.refreshToken) } : {}),
458
+ accessTokenExpiresAt: rotated.accessTokenExpiresAt,
459
+ refreshLockUntil: 0,
460
+ },
461
+ });
462
+ matched = written.matchedCount;
463
+ }
464
+ catch (error) {
465
+ // the provider has superseded the old pair and nothing here can name the
466
+ // new one: it goes back at once, the lock expires on its own, and the
467
+ // request fails closed
468
+ await this.revokeRotatedTokens(rotated);
469
+ this.emit({ event: 'idp_token_store_failed', sessionId: sessionIdArg, message: errorMessage(error) });
470
+ throw new AuthorityTokenError('authority_unavailable', error);
471
+ }
472
+ if (matched === 0) {
473
+ await this.revokeRotatedTokens(rotated);
474
+ this.notifySessionEnded({ sessionId: sessionIdArg, reason: 'revoked_elsewhere', revokedHere: false });
475
+ throw new AuthorityTokenError('authority_login_required');
476
+ }
477
+ return { accessToken: rotated.accessToken, subject: subjectArg, expiresAt: rotated.accessTokenExpiresAt };
478
+ }
479
+ /**
480
+ * Another replica is rotating this session right now. Whatever it has
481
+ * written by the time this request looks again is used; otherwise the
482
+ * request fails closed and the next one finds the rotated pair.
483
+ */
484
+ async tokenAfterForeignRefresh(sessionIdArg, subjectArg) {
485
+ const session = await this.#models.Session.getInstance({ id: sessionIdArg });
486
+ const now = this.#now();
487
+ const expiresAt = typeof session?.accessTokenExpiresAt === 'number' ? session.accessTokenExpiresAt : 0;
488
+ const accessToken = session ? this.openSealed(session.sealedAccessToken) : undefined;
489
+ if (session
490
+ && session.revokedAt === undefined
491
+ && session.expiresAt > now
492
+ && accessToken !== undefined
493
+ && expiresAt > now + ACCESS_TOKEN_REFRESH_SKEW_MS) {
494
+ return { accessToken, subject: subjectArg, expiresAt };
495
+ }
496
+ throw new AuthorityTokenError('authority_unavailable');
497
+ }
498
+ // ==========================================================================
499
+ // internals
500
+ // ==========================================================================
501
+ requireProvider() {
502
+ if (!this.#provider) {
503
+ throw new RelyingPartyError('login_method_unavailable');
504
+ }
505
+ return this.#provider;
506
+ }
507
+ newSessionDoc(tokenArg, principalIdArg) {
508
+ const now = this.#now();
509
+ const session = new this.#models.Session();
510
+ session.id = 'ses_' + randomToken(18);
511
+ session.tokenHash = sha256(tokenArg);
512
+ session.userId = principalIdArg;
513
+ session.createdAt = now;
514
+ session.lastSeenAt = now;
515
+ session.expiresAt = now + this.sessionMaxAgeSeconds * 1_000;
516
+ return session;
517
+ }
518
+ csrfTokenOf(tokenArg) {
519
+ return deriveCsrfToken(tokenArg, this.#csrfKey, this.#csrfLabel);
520
+ }
521
+ opening(sessionArg, tokenArg) {
522
+ return {
523
+ session: this.snapshot(sessionArg),
524
+ csrfToken: this.csrfTokenOf(tokenArg),
525
+ sessionCookie: serializeCookie(this.sessionCookieName, tokenArg, {
526
+ httpOnly: true,
527
+ secure: this.secureCookies,
528
+ sameSite: 'Lax',
529
+ maxAgeSeconds: this.sessionMaxAgeSeconds,
530
+ }),
531
+ };
532
+ }
533
+ snapshot(sessionArg) {
534
+ return {
535
+ id: sessionArg.id,
536
+ principalId: sessionArg.userId,
537
+ createdAt: sessionArg.createdAt,
538
+ lastSeenAt: sessionArg.lastSeenAt,
539
+ expiresAt: sessionArg.expiresAt,
540
+ ...(sessionArg.idpSubject ? { idpSubject: sessionArg.idpSubject } : {}),
541
+ ...(sessionArg.loginAttemptId ? { loginAttemptId: sessionArg.loginAttemptId } : {}),
542
+ };
543
+ }
544
+ /** A session document that is live right now with an active principal, touched at most once a minute. */
545
+ async liveSession(sessionArg) {
546
+ const now = this.#now();
547
+ if (!sessionArg || sessionArg.revokedAt !== undefined || sessionArg.expiresAt <= now) {
548
+ return null;
549
+ }
550
+ if (!(await this.#principals.isActive(sessionArg.userId))) {
551
+ return null;
552
+ }
553
+ if (now - sessionArg.lastSeenAt > SESSION_TOUCH_INTERVAL_MS) {
554
+ // the touch carries the liveness it was read under into the write: a
555
+ // revocation that landed in between leaves it unmatched
556
+ const touched = await this.#models.Session.atomicUpdate({ id: sessionArg.id, revokedAt: { $exists: false }, expiresAt: { $gt: now } }, { $set: { lastSeenAt: now } });
557
+ if (touched.matchedCount === 0) {
558
+ return null;
559
+ }
560
+ sessionArg.lastSeenAt = now;
561
+ }
562
+ return sessionArg;
563
+ }
564
+ async createProviderSession(attemptArg, principalIdArg, completionArg) {
565
+ const token = randomToken(32);
566
+ const session = this.newSessionDoc(token, principalIdArg);
567
+ session.idpSubject = completionArg.subject;
568
+ session.loginAttemptId = attemptArg.id;
569
+ session.sealedAccessToken = this.#secretBox.seal(completionArg.accessToken);
570
+ session.sealedRefreshToken = completionArg.refreshToken ? this.#secretBox.seal(completionArg.refreshToken) : '';
571
+ session.accessTokenExpiresAt = completionArg.accessTokenExpiresAt;
572
+ session.refreshLockUntil = 0;
573
+ await session.save();
574
+ const linked = await this.#models.LoginAttempt.atomicUpdate({ id: attemptArg.id, createdSessionId: null }, { $set: { createdSessionId: session.id } });
575
+ if (linked.matchedCount !== 1) {
576
+ // the attempt was consumed by another callback in the meantime
577
+ await this.discardUnopenedSession(session.id);
578
+ throw new RelyingPartyError('invalid_or_replayed_login');
579
+ }
580
+ const opening = this.opening(session, token);
581
+ try {
582
+ await this.#hooks.onProviderSessionOpened?.({ session: opening.session });
583
+ }
584
+ catch (error) {
585
+ // the cookie never leaves, so the session is unreachable: it is revoked
586
+ // and its sealed tokens erased before the application's error goes on;
587
+ // the caller hands the provider tokens back
588
+ try {
589
+ await this.discardUnopenedSession(session.id);
590
+ }
591
+ catch (discardError) {
592
+ this.emit({ event: 'session_discard_failed', sessionId: session.id, message: errorMessage(discardError) });
593
+ }
594
+ throw error;
595
+ }
596
+ return opening;
597
+ }
598
+ /** Revokes a session that was never handed out and erases its sealed tokens. */
599
+ async discardUnopenedSession(sessionIdArg) {
600
+ await this.#models.Session.atomicUpdate({ id: sessionIdArg, revokedAt: { $exists: false } }, { $set: { revokedAt: this.#now(), ...REVOKED_TOKEN_FIELDS } });
601
+ }
602
+ /**
603
+ * Tells the application a session ended. Its hook never stands between the
604
+ * local revocation and the provider revocation: a throw or a rejection is
605
+ * reported as `hook_failed` and the end runs on.
606
+ */
607
+ notifySessionEnded(eventArg) {
608
+ const hook = this.#hooks.onSessionEnded;
609
+ if (!hook) {
610
+ return;
611
+ }
612
+ const reportFailure = (errorArg) => {
613
+ this.emit({ event: 'hook_failed', hook: 'onSessionEnded', sessionId: eventArg.sessionId, message: errorMessage(errorArg) });
614
+ };
615
+ try {
616
+ const result = hook.call(this.#hooks, eventArg);
617
+ if (result instanceof Promise) {
618
+ result.catch(reportFailure);
619
+ }
620
+ }
621
+ catch (error) {
622
+ reportFailure(error);
623
+ }
624
+ }
625
+ /**
626
+ * The conditional revocation every end of a session runs. Answers the
627
+ * sealed tokens it took off the session when this call revoked it, `null`
628
+ * when the session was already over.
629
+ */
630
+ async revokeSession(sessionIdArg, reasonArg) {
631
+ if (typeof sessionIdArg !== 'string' || sessionIdArg.length === 0) {
632
+ return null;
633
+ }
634
+ const revoked = await this.#models.Session.atomicFindOneAndUpdate({ id: sessionIdArg, revokedAt: { $exists: false } }, { $set: { revokedAt: this.#now(), ...REVOKED_TOKEN_FIELDS } }, { returnDocument: 'before' });
635
+ const revokedHere = revoked.status === 'matched' && revoked.document !== null;
636
+ this.notifySessionEnded({ sessionId: sessionIdArg, reason: reasonArg, revokedHere });
637
+ if (!revokedHere) {
638
+ return null;
639
+ }
640
+ const before = revoked.document;
641
+ return { sealedAccessToken: before.sealedAccessToken, sealedRefreshToken: before.sealedRefreshToken };
642
+ }
643
+ /** The provider says the login is over: the session ends here without asking it anything more. */
644
+ async endProviderSession(sessionIdArg, reasonArg) {
645
+ if (await this.revokeSession(sessionIdArg, reasonArg)) {
646
+ this.emit({ event: 'idp_session_ended', sessionId: sessionIdArg, reason: reasonArg });
647
+ }
648
+ }
649
+ /**
650
+ * Hands the tokens of an ended session back, best-effort and within the
651
+ * budget. A sealed value this deployment can no longer open is skipped.
652
+ */
653
+ async revokeSealedTokens(sealedArg) {
654
+ if (!this.#provider || (!sealedArg.sealedAccessToken && !sealedArg.sealedRefreshToken)) {
655
+ return;
656
+ }
657
+ await this.withinRevocationBudget(async () => {
658
+ for (const [sealed, hint] of [
659
+ [sealedArg.sealedRefreshToken, 'refresh_token'],
660
+ [sealedArg.sealedAccessToken, 'access_token'],
661
+ ]) {
662
+ if (!sealed) {
663
+ continue;
664
+ }
665
+ const token = this.openSealed(sealed);
666
+ if (token === undefined) {
667
+ this.emit({ event: 'oidc_sealed_token_unreadable', hint, message: 'The sealed provider token could not be opened' });
668
+ continue;
669
+ }
670
+ await this.revokeToken(token, hint);
671
+ }
672
+ });
673
+ }
674
+ async revokeRotatedTokens(rotatedArg) {
675
+ await this.withinRevocationBudget(async () => {
676
+ if (rotatedArg.refreshToken) {
677
+ await this.revokeToken(rotatedArg.refreshToken, 'refresh_token');
678
+ }
679
+ await this.revokeToken(rotatedArg.accessToken, 'access_token');
680
+ });
681
+ }
682
+ async revokeCompletionTokens(completionArg) {
683
+ await this.withinRevocationBudget(async () => {
684
+ if (completionArg.refreshToken) {
685
+ await this.revokeToken(completionArg.refreshToken, 'refresh_token');
686
+ }
687
+ await this.revokeToken(completionArg.accessToken, 'access_token');
688
+ });
689
+ }
690
+ async revokeToken(tokenArg, hintArg) {
691
+ try {
692
+ await this.#provider?.revokeToken(tokenArg, hintArg);
693
+ }
694
+ catch (error) {
695
+ // the local session is gone either way; the provider expires the token
696
+ this.emit({ event: 'oidc_token_revocation_failed', hint: hintArg, message: errorMessage(error) });
697
+ }
698
+ }
699
+ /**
700
+ * Runs a best-effort provider step under `TOKEN_REVOCATION_BUDGET_MS`. What
701
+ * is still in flight at the deadline keeps running and ends on the provider
702
+ * client's own request timeout; only the waiting stops. The timer is cleared
703
+ * on every path and unreferenced.
704
+ */
705
+ async withinRevocationBudget(workArg) {
706
+ let timer;
707
+ const work = workArg().then(() => 'done', (errorArg) => {
708
+ this.emit({ event: 'oidc_token_revocation_failed', message: errorMessage(errorArg) });
709
+ return 'done';
710
+ });
711
+ const deadline = new Promise((resolveArg) => {
712
+ timer = setTimeout(() => resolveArg('deadline'), TOKEN_REVOCATION_BUDGET_MS);
713
+ timer.unref();
714
+ });
715
+ try {
716
+ if ((await Promise.race([work, deadline])) === 'deadline') {
717
+ this.emit({
718
+ event: 'oidc_token_revocation_timed_out',
719
+ message: 'The provider did not answer within the revocation budget',
720
+ });
721
+ }
722
+ }
723
+ finally {
724
+ clearTimeout(timer);
725
+ }
726
+ }
727
+ /** A sealed value this deployment can still open, or `undefined`. */
728
+ openSealed(sealedArg) {
729
+ if (!sealedArg) {
730
+ return undefined;
731
+ }
732
+ try {
733
+ return this.#secretBox.open(sealedArg);
734
+ }
735
+ catch {
736
+ return undefined;
737
+ }
738
+ }
739
+ /**
740
+ * Removes attempts an hour past their expiry, one bounded page per login
741
+ * start. A sweep that cannot run never blocks a sign-in.
742
+ */
743
+ async sweepExpiredAttempts() {
744
+ try {
745
+ const page = await this.#models.LoginAttempt.getPagedInstances({
746
+ filter: { expiresAt: { $lt: this.#now() - LOGIN_ATTEMPT_SWEEP_GRACE_MS } },
747
+ sortField: 'expiresAt',
748
+ sortDirection: 'asc',
749
+ limit: LOGIN_ATTEMPT_SWEEP_BATCH,
750
+ });
751
+ for (const attempt of page.documents) {
752
+ await this.#models.LoginAttempt.atomicDelete({ id: attempt.id });
753
+ }
754
+ }
755
+ catch (error) {
756
+ this.emit({ event: 'login_attempt_sweep_failed', message: errorMessage(error) });
757
+ }
758
+ }
759
+ /**
760
+ * Reports one diagnostic. Every cleanup step reports through here, so a
761
+ * throwing sink must not end it: the event and the sink's failure go to
762
+ * `console.error` instead.
763
+ */
764
+ emit(eventArg) {
765
+ const sink = this.#hooks.onEvent;
766
+ if (!sink) {
767
+ console.error(JSON.stringify(eventArg));
768
+ return;
769
+ }
770
+ const reportFailure = (errorArg) => {
771
+ const failure = { event: 'hook_failed', hook: 'onEvent', message: errorMessage(errorArg) };
772
+ console.error(JSON.stringify(failure));
773
+ console.error(JSON.stringify(eventArg));
774
+ };
775
+ try {
776
+ const result = sink.call(this.#hooks, eventArg);
777
+ if (result instanceof Promise) {
778
+ result.catch(reportFailure);
779
+ }
780
+ }
781
+ catch (error) {
782
+ reportFailure(error);
783
+ }
784
+ }
785
+ }
786
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yZWx5aW5ncGFydHlzZXNzaW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX3JlbHlpbmdwYXJ0eS9jbGFzc2VzLnJlbHlpbmdwYXJ0eXNlc3Npb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNuRCxPQUFPLEVBQ0wsbUJBQW1CLEVBQ25CLGtCQUFrQixFQUNsQixpQkFBaUIsRUFDakIsbUJBQW1CLEdBQ3BCLE1BQU0scUJBQXFCLENBQUM7QUFDN0IsT0FBTyxFQUNMLG1CQUFtQixFQUNuQix1QkFBdUIsR0FLeEIsTUFBTSw4QkFBOEIsQ0FBQztBQUV0QyxPQUFPLEVBQUUsWUFBWSxFQUFFLHNCQUFzQixFQUFFLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzdGLE9BQU8sRUFBRSxlQUFlLEVBQUUsV0FBVyxFQUFFLE1BQU0sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2xHLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBZTdELE1BQU0sQ0FBQyxNQUFNLDZDQUE2QyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDO0FBQzFFLE1BQU0sQ0FBQyxNQUFNLHVDQUF1QyxHQUFHLFFBQVEsQ0FBQztBQUNoRSxnRkFBZ0Y7QUFDaEYsTUFBTSxDQUFDLE1BQU0sZ0NBQWdDLEdBQUcsa0NBQWtDLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUU1RyxNQUFNLHVCQUF1QixHQUFHLEdBQUcsQ0FBQztBQUNwQyxNQUFNLHlCQUF5QixHQUFHLE1BQU0sQ0FBQztBQUN6QyxvR0FBb0c7QUFDcEcsTUFBTSxDQUFDLE1BQU0sNkJBQTZCLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQztBQUNyRCwwRUFBMEU7QUFDMUUsTUFBTSw0QkFBNEIsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEtBQUssQ0FBQztBQUNyRCx1RkFBdUY7QUFDdkYsTUFBTSx5QkFBeUIsR0FBRyxHQUFHLENBQUM7QUFDdEM7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sMEJBQTBCLEdBQUcsS0FBSyxDQUFDO0FBQ2hELHVFQUF1RTtBQUN2RSxNQUFNLENBQUMsTUFBTSw0QkFBNEIsR0FBRyxNQUFNLENBQUM7QUFDbkQ7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQUcsTUFBTSxDQUFDO0FBa0M3QyxNQUFNLG9CQUFvQixHQUFHLEVBQUUsaUJBQWlCLEVBQUUsRUFBRSxFQUFFLGtCQUFrQixFQUFFLEVBQUUsRUFBRSxnQkFBZ0IsRUFBRSxDQUFDLEVBQUUsQ0FBQztBQUVwRyxNQUFNLFlBQVksR0FBRyxDQUFDLFFBQWlCLEVBQVUsRUFBRSxDQUFDLENBQUMsUUFBUSxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7QUFFdEgseUhBQXlIO0FBQ3pILE1BQU0seUJBQXlCLEdBQUcsb0JBQW9CLENBQUM7QUFFdkQsTUFBTSxhQUFhLEdBQUcsQ0FDcEIsYUFBaUMsRUFDakMsVUFBa0IsRUFDbEIsU0FBa0IsRUFDbEIsUUFBOEIsRUFDdEIsRUFBRTtJQUNWLElBQUksYUFBYSxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ2hDLE9BQU8sVUFBVSxDQUFDO0lBQ3BCLENBQUM7SUFDRCxJQUFJLENBQUMsU0FBUyxJQUFJLHlCQUF5QixDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsRUFBRSxDQUFDO1FBQ2hFLE1BQU0sSUFBSSxLQUFLLENBQ2IsZUFBZSxRQUFRLEtBQUssYUFBYSxpQ0FBaUM7Y0FDdEUsb0VBQW9FLENBQ3pFLENBQUM7SUFDSixDQUFDO0lBQ0QsT0FBTyxhQUFhLENBQUM7QUFDdkIsQ0FBQyxDQUFDO0FBRUY7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxPQUFPLG9CQUFvQjtJQUNmLFlBQVksQ0FBUztJQUNyQixhQUFhLENBQVU7SUFDdkIsaUJBQWlCLENBQVM7SUFDMUIsZ0JBQWdCLENBQVM7SUFDekIsZUFBZSxDQUFTO0lBQ3hCLG9CQUFvQixDQUFTO0lBQ3BDLE9BQU8sQ0FBc0I7SUFDN0IsV0FBVyxDQUEwQjtJQUNyQyxTQUFTLENBQWlCO0lBQzFCLFVBQVUsQ0FBWTtJQUN0QixRQUFRLENBQVM7SUFDakIsVUFBVSxDQUFTO0lBQ25CLE1BQU0sQ0FBcUI7SUFDM0IsSUFBSSxDQUFlO0lBQzVCLHNGQUFzRjtJQUM3RSxrQkFBa0IsR0FBRyxJQUFJLEdBQUcsRUFBbUMsQ0FBQztJQUV6RSxZQUFZLFVBQXdDO1FBQ2xELE1BQU0sTUFBTSxHQUFHLElBQUksR0FBRyxDQUFDLFVBQVUsQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUNoRCxJQUFJLE1BQU0sQ0FBQyxNQUFNLEtBQUssVUFBVSxDQUFDLFlBQVksRUFBRSxDQUFDO1lBQzlDLE1BQU0sSUFBSSxLQUFLLENBQUMsZ0VBQWdFLENBQUMsQ0FBQztRQUNwRixDQUFDO1FBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsTUFBTSxLQUFLLEVBQUUsRUFBRSxDQUFDO1lBQzdFLE1BQU0sSUFBSSxLQUFLLENBQUMsMEJBQTBCLENBQUMsQ0FBQztRQUM5QyxDQUFDO1FBQ0QsTUFBTSxNQUFNLEdBQUcsVUFBVSxDQUFDLG9CQUFvQixJQUFJLDZDQUE2QyxDQUFDO1FBQ2hHLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxJQUFJLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNoRCxNQUFNLElBQUksS0FBSyxDQUFDLGlEQUFpRCxDQUFDLENBQUM7UUFDckUsQ0FBQztRQUNELElBQUksQ0FBQyxZQUFZLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztRQUNsQyxJQUFJLENBQUMsYUFBYSxHQUFHLE1BQU0sQ0FBQyxRQUFRLEtBQUssUUFBUSxDQUFDO1FBQ2xELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ25ELElBQUksQ0FBQyxpQkFBaUIsR0FBRyxhQUFhLENBQ3BDLFVBQVUsQ0FBQyxXQUFXLEVBQUUsT0FBTyxFQUMvQixNQUFNLEdBQUcsZ0JBQWdCLEVBQ3pCLElBQUksQ0FBQyxhQUFhLEVBQ2xCLFNBQVMsQ0FDVixDQUFDO1FBQ0YsSUFBSSxDQUFDLGdCQUFnQixHQUFHLGFBQWEsQ0FDbkMsVUFBVSxDQUFDLFdBQVcsRUFBRSxNQUFNLEVBQzlCLE1BQU0sR0FBRyxlQUFlLEVBQ3hCLElBQUksQ0FBQyxhQUFhLEVBQ2xCLFFBQVEsQ0FDVCxDQUFDO1FBQ0YsSUFBSSxDQUFDLGVBQWUsR0FBRyxVQUFVLENBQUMsZUFBZSxJQUFJLHVDQUF1QyxDQUFDO1FBQzdGLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDakYsTUFBTSxJQUFJLEtBQUssQ0FBQyxpREFBaUQsQ0FBQyxDQUFDO1FBQ3JFLENBQUM7UUFDRCxJQUFJLENBQUMsb0JBQW9CLEdBQUcsTUFBTSxDQUFDO1FBQ25DLElBQUksQ0FBQyxPQUFPLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQztRQUNqQyxJQUFJLENBQUMsV0FBVyxHQUFHLFVBQVUsQ0FBQyxVQUFVLENBQUM7UUFDekMsSUFBSSxDQUFDLFNBQVMsR0FBRyxVQUFVLENBQUMsUUFBUSxDQUFDO1FBQ3JDLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxTQUFTLENBQUMsVUFBVSxDQUFDLG9CQUFvQixDQUFDLENBQUM7UUFDakUsSUFBSSxDQUFDLFFBQVEsR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNoRCxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQyxTQUFTLElBQUksZ0NBQWdDLENBQUM7UUFDM0UsSUFBSSxDQUFDLE1BQU0sR0FBRyxVQUFVLENBQUMsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUNyQyxJQUFJLENBQUMsSUFBSSxHQUFHLFVBQVUsQ0FBQyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBRUQsa0RBQWtEO0lBQ2xELElBQVcsb0JBQW9CO1FBQzdCLE9BQU8sSUFBSSxDQUFDLFNBQVMsS0FBSyxTQUFTLENBQUM7SUFDdEMsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsSUFBVyxhQUFhO1FBQ3RCLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLHFCQUFxQixJQUFJLENBQUMsQ0FBQyxHQUFHLEtBQUssR0FBRyxzQkFBc0IsQ0FBQztJQUN2RixDQUFDO0lBRUQsNkVBQTZFO0lBQzdFLFdBQVc7SUFDWCw2RUFBNkU7SUFFN0U7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLFdBQVcsQ0FBQyxVQUFtQztRQUMxRCxJQUFJLE9BQU8sVUFBVSxFQUFFLFdBQVcsS0FBSyxRQUFRLElBQUksVUFBVSxDQUFDLFdBQVcsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDdkYsTUFBTSxJQUFJLEtBQUssQ0FBQyxrQ0FBa0MsQ0FBQyxDQUFDO1FBQ3RELENBQUM7UUFDRCxNQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDOUIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsVUFBVSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ2xFLE1BQU0sT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3JCLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUVELHNHQUFzRztJQUMvRixLQUFLLENBQUMsb0JBQW9CLENBQy9CLGVBQTBDO1FBRTFDLE1BQU0sS0FBSyxHQUFHLFlBQVksQ0FBQyxlQUFlLENBQUMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDeEUsSUFBSSxDQUFDLEtBQUssSUFBSSxLQUFLLENBQUMsTUFBTSxHQUFHLHVCQUF1QixFQUFFLENBQUM7WUFDckQsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBQ0QsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNyRixNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDN0MsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ1YsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBQ0QsT0FBTyxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7SUFDOUUsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsaUJBQWlCLENBQUMsWUFBb0I7UUFDakQsSUFBSSxPQUFPLFlBQVksS0FBSyxRQUFRLElBQUksWUFBWSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNsRSxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFDRCxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsRUFBRSxFQUFFLEVBQUUsWUFBWSxFQUFFLENBQUMsQ0FBQyxDQUFDO1FBQ2xHLE9BQU8sSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUM7SUFDM0MsQ0FBQztJQUVELG1GQUFtRjtJQUM1RSxLQUFLLENBQUMsYUFBYSxDQUFDLFlBQW9CO1FBQzdDLElBQUksT0FBTyxZQUFZLEtBQUssUUFBUSxJQUFJLFlBQVksQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDbEUsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBQ0QsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsRUFBRSxFQUFFLEVBQUUsWUFBWSxFQUFFLENBQUMsQ0FBQztRQUM3RSxJQUFJLENBQUMsT0FBTyxJQUFJLE9BQU8sQ0FBQyxTQUFTLEtBQUssU0FBUyxJQUFJLE9BQU8sQ0FBQyxTQUFTLElBQUksSUFBSSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUM7WUFDcEYsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDbkQsQ0FBQztJQUVNLFlBQVksQ0FBQyxXQUF5QyxFQUFFLFlBQXFCO1FBQ2xGLE9BQU8sT0FBTyxZQUFZLEtBQUssUUFBUSxJQUFJLHFCQUFxQixDQUFDLFdBQVcsQ0FBQyxTQUFTLEVBQUUsWUFBWSxDQUFDLENBQUM7SUFDeEcsQ0FBQztJQUVELDBDQUEwQztJQUNuQyxLQUFLLENBQUMsTUFBTSxDQUFDLFlBQW9CO1FBQ3RDLE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUN2QyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksS0FBSyxDQUFDLFVBQVUsQ0FDckIsWUFBb0IsRUFDcEIsWUFBMkMsUUFBUTtRQUVuRCxNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsWUFBWSxFQUFFLFNBQVMsQ0FBQyxDQUFDO1FBQ2hFLElBQUksS0FBSyxFQUFFLENBQUM7WUFDVixNQUFNLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN2QyxDQUFDO1FBQ0QsT0FBTyxLQUFLLEtBQUssSUFBSSxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7O09BR0c7SUFDSSxLQUFLLENBQUMsc0JBQXNCLENBQ2pDLGNBQXNCLEVBQ3RCLGFBQXlDLEVBQUU7UUFFM0MsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO1FBQ2QsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUM7WUFDdkQsTUFBTSxFQUFFLGNBQWM7WUFDdEIsU0FBUyxFQUFFLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRTtTQUM5QixDQUFDLENBQUM7UUFDSCxLQUFLLE1BQU0sT0FBTyxJQUFJLFFBQVEsRUFBRSxDQUFDO1lBQy9CLElBQUksT0FBTyxDQUFDLEVBQUUsS0FBSyxVQUFVLENBQUMsYUFBYSxFQUFFLENBQUM7Z0JBQzVDLFNBQVM7WUFDWCxDQUFDO1lBQ0QsSUFBSSxNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7Z0JBQ3RDLEtBQUssSUFBSSxDQUFDLENBQUM7WUFDYixDQUFDO1FBQ0gsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVNLGtCQUFrQjtRQUN2QixPQUFPLHNCQUFzQixDQUFDLElBQUksQ0FBQyxpQkFBaUIsRUFBRTtZQUNwRCxRQUFRLEVBQUUsSUFBSTtZQUNkLE1BQU0sRUFBRSxJQUFJLENBQUMsYUFBYTtZQUMxQixRQUFRLEVBQUUsS0FBSztTQUNoQixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU0saUJBQWlCO1FBQ3RCLE9BQU8sc0JBQXNCLENBQUMsSUFBSSxDQUFDLGdCQUFnQixFQUFFO1lBQ25ELFFBQVEsRUFBRSxJQUFJO1lBQ2QsTUFBTSxFQUFFLElBQUksQ0FBQyxhQUFhO1lBQzFCLFFBQVEsRUFBRSxLQUFLO1NBQ2hCLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCw2RUFBNkU7SUFDN0UsaUJBQWlCO0lBQ2pCLDZFQUE2RTtJQUU3RTs7OztPQUlHO0lBQ0ksS0FBSyxDQUFDLFVBQVUsQ0FBQyxhQUFzQjtRQUM1QyxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7UUFDeEMsTUFBTSxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztRQUNsQyxNQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDOUIsTUFBTSxLQUFLLEdBQUcsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzlCLE1BQU0sWUFBWSxHQUFHLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNyQyxNQUFNLFlBQVksR0FBRyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDckMsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBRXhCLE1BQU0sT0FBTyxHQUFHLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUNoRCxPQUFPLENBQUMsRUFBRSxHQUFHLE1BQU0sR0FBRyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDdEMsT0FBTyxDQUFDLFNBQVMsR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDbEMsT0FBTyxDQUFDLGdCQUFnQixHQUFHLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUNoRCxPQUFPLENBQUMsa0JBQWtCLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDaEUsT0FBTyxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7UUFDdEIsT0FBTyxDQUFDLFVBQVUsR0FBRyxrQkFBa0IsQ0FBQyxhQUFhLEVBQUUsSUFBSSxDQUFDLFlBQVksRUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDaEcsT0FBTyxDQUFDLFNBQVMsR0FBRyxHQUFHLENBQUM7UUFDeEIsT0FBTyxDQUFDLFNBQVMsR0FBRyxHQUFHLEdBQUcsNkJBQTZCLEdBQUcsS0FBSyxDQUFDO1FBQ2hFLE1BQU0sT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO1FBRXJCLE9BQU87WUFDTCxnQkFBZ0IsRUFBRSxNQUFNLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLENBQUM7WUFDdEYsWUFBWSxFQUFFLGVBQWUsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsWUFBWSxFQUFFO2dCQUNqRSxRQUFRLEVBQUUsSUFBSTtnQkFDZCxNQUFNLEVBQUUsSUFBSSxDQUFDLGFBQWE7Z0JBQzFCLFFBQVEsRUFBRSxLQUFLO2dCQUNmLGFBQWEsRUFBRSw2QkFBNkI7YUFDN0MsQ0FBQztTQUNILENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksS0FBSyxDQUFDLGFBQWEsQ0FDeEIsY0FBbUIsRUFDbkIsZUFBMEM7UUFFMUMsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ3hDLE1BQU0sTUFBTSxHQUFHLGNBQWMsQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzNELElBQUksTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN0QyxNQUFNLElBQUksaUJBQWlCLENBQUMsd0JBQXdCLENBQUMsQ0FBQztRQUN4RCxDQUFDO1FBQ0QsTUFBTSxZQUFZLEdBQUcsWUFBWSxDQUFDLGVBQWUsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUM5RSxJQUFJLENBQUMsWUFBWSxJQUFJLFlBQVksQ0FBQyxNQUFNLEdBQUcsdUJBQXVCLEVBQUUsQ0FBQztZQUNuRSxNQUFNLElBQUksaUJBQWlCLENBQUMsc0JBQXNCLENBQUMsQ0FBQztRQUN0RCxDQUFDO1FBRUQsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3hCLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsc0JBQXNCLENBQ3BFO1lBQ0UsU0FBUyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7WUFDNUIsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLFlBQVksQ0FBQztZQUN0QyxVQUFVLEVBQUUsSUFBSTtZQUNoQixTQUFTLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFO1NBQ3hCLEVBQ0QsRUFBRSxJQUFJLEVBQUUsRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFDN0IsRUFBRSxjQUFjLEVBQUUsT0FBTyxFQUFFLENBQzVCLENBQUM7UUFDRixJQUFJLE9BQU8sQ0FBQyxNQUFNLEtBQUssU0FBUyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsRUFBRSxDQUFDO1lBQ3RELE1BQU0sSUFBSSxpQkFBaUIsQ0FBQywyQkFBMkIsQ0FBQyxDQUFDO1FBQzNELENBQUM7UUFDRCxNQUFNLE9BQU8sR0FBRyxPQUFPLENBQUMsUUFBdUMsQ0FBQztRQUVoRSxJQUFJLFVBQXVDLENBQUM7UUFDNUMsSUFBSSxLQUFLLEdBQXdCLGdCQUFnQixDQUFDO1FBQ2xELElBQUksQ0FBQztZQUNILFVBQVUsR0FBRyxNQUFNLFFBQVEsQ0FBQyxxQkFBcUIsQ0FBQztnQkFDaEQsV0FBVyxFQUFFLGNBQWM7Z0JBQzNCLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO2dCQUNoQixLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7Z0JBQ3BCLFlBQVksRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsa0JBQWtCLENBQUM7YUFDL0QsQ0FBQyxDQUFDO1lBQ0gsS0FBSyxHQUFHLHFCQUFxQixDQUFDO1lBQzlCLE1BQU0sRUFBRSxXQUFXLEVBQUUsR0FBRyxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQ2pFLEtBQUssR0FBRyxxQkFBcUIsQ0FBQztZQUM5QixPQUFPLE1BQU0sSUFBSSxDQUFDLHFCQUFxQixDQUFDLE9BQU8sRUFBRSxXQUFXLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDNUUsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLFVBQVUsRUFBRSxDQUFDO2dCQUNmLE1BQU0sSUFBSSxDQUFDLHNCQUFzQixDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQ2hELENBQUM7WUFDRCxJQUFJLEtBQUssWUFBWSxpQkFBaUIsSUFBSSxLQUFLLFlBQVksbUJBQW1CLEVBQUUsQ0FBQztnQkFDL0UsTUFBTSxLQUFLLENBQUM7WUFDZCxDQUFDO1lBQ0QsSUFBSSxLQUFLLFlBQVksbUJBQW1CLEVBQUUsQ0FBQztnQkFDekMsTUFBTSxJQUFJLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDbkQsQ0FBQztZQUNELE1BQU0sSUFBSSxrQkFBa0IsQ0FBQyxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDN0MsQ0FBQztJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0ksS0FBSyxDQUFDLFlBQVksQ0FBQyxjQUFtQixFQUFFLGVBQTBDO1FBQ3ZGLE1BQU0sTUFBTSxHQUFHLGNBQWMsQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQzNELE1BQU0sWUFBWSxHQUFHLFlBQVksQ0FBQyxlQUFlLENBQUMsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFDOUUsSUFDRSxNQUFNLENBQUMsTUFBTSxLQUFLLENBQUM7ZUFDaEIsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO2VBQ1YsQ0FBQyxZQUFZO2VBQ2IsWUFBWSxDQUFDLE1BQU0sR0FBRyx1QkFBdUIsRUFDaEQsQ0FBQztZQUNELE9BQU87UUFDVCxDQUFDO1FBQ0QsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQzFDLEVBQUUsU0FBUyxFQUFFLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLENBQUMsWUFBWSxDQUFDLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxFQUMxRixFQUFFLElBQUksRUFBRSxFQUFFLFVBQVUsRUFBRSxJQUFJLENBQUMsSUFBSSxFQUFFLEVBQUUsRUFBRSxDQUN0QyxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsYUFBYSxDQUFDLGVBQTBDO1FBQ25FLElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztRQUN2QixNQUFNLFlBQVksR0FBRyxZQUFZLENBQUMsZUFBZSxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBQzlFLElBQUksQ0FBQyxZQUFZLElBQUksWUFBWSxDQUFDLE1BQU0sR0FBRyx1QkFBdUIsRUFBRSxDQUFDO1lBQ25FLE1BQU0sSUFBSSxpQkFBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO1FBQ3hELENBQUM7UUFDRCxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLFdBQVcsQ0FBQyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDeEcsSUFDRSxDQUFDLE9BQU87ZUFDTCxPQUFPLENBQUMsVUFBVSxLQUFLLFNBQVM7ZUFDaEMsT0FBTyxDQUFDLFVBQVUsS0FBSyxJQUFJO2VBQzNCLENBQUMsT0FBTyxDQUFDLGdCQUFnQjtlQUN6QixPQUFPLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUUsRUFDbkMsQ0FBQztZQUNELE1BQU0sSUFBSSxpQkFBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO1FBQ3hELENBQUM7UUFDRCxNQUFNLFFBQVEsR0FBRyxNQUFNLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUNsRSxJQUNFLENBQUMsUUFBUTtlQUNOLFFBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxLQUFLLE9BQU8sQ0FBQyxnQkFBZ0I7ZUFDaEQsUUFBUSxDQUFDLE9BQU8sQ0FBQyxjQUFjLEtBQUssT0FBTyxDQUFDLEVBQUUsRUFDakQsQ0FBQztZQUNELE1BQU0sSUFBSSxpQkFBaUIsQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO1FBQ3hELENBQUM7UUFDRCxPQUFPLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDLFlBQVksRUFBRSxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDekYsQ0FBQztJQUVELDZFQUE2RTtJQUM3RSxrQkFBa0I7SUFDbEIsNkVBQTZFO0lBRTdFOzs7Ozs7T0FNRztJQUNJLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxZQUFvQixFQUFFLHNCQUE4QjtRQUNqRixNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxZQUFZLEVBQUUsQ0FBQyxDQUFDO1FBQzdFLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN4QixJQUNFLENBQUMsT0FBTztlQUNMLE9BQU8sQ0FBQyxTQUFTLEtBQUssU0FBUztlQUMvQixPQUFPLENBQUMsU0FBUyxJQUFJLEdBQUc7ZUFDeEIsQ0FBQyxPQUFPLENBQUMsVUFBVTtlQUNuQixPQUFPLENBQUMsTUFBTSxLQUFLLHNCQUFzQixFQUM1QyxDQUFDO1lBQ0QsTUFBTSxJQUFJLG1CQUFtQixDQUFDLDBCQUEwQixDQUFDLENBQUM7UUFDNUQsQ0FBQztRQUNELE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUM7UUFDbkMsTUFBTSxTQUFTLEdBQUcsT0FBTyxPQUFPLENBQUMsb0JBQW9CLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsb0JBQW9CLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN0RyxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQy9ELHFFQUFxRTtRQUNyRSxNQUFNLEdBQUcsR0FBRyxXQUFXLEtBQUssU0FBUyxJQUFJLFNBQVMsS0FBSyxDQUFDLElBQUksU0FBUyxJQUFJLEdBQUcsR0FBRyw0QkFBNEIsQ0FBQztRQUM1RyxJQUFJLENBQUMsR0FBRyxJQUFJLFdBQVcsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUN0QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUUsQ0FBQztRQUM3QyxDQUFDO1FBQ0QsSUFBSSxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1lBQ2hDLElBQUksV0FBVyxLQUFLLFNBQVMsSUFBSSxDQUFDLFNBQVMsS0FBSyxDQUFDLElBQUksU0FBUyxHQUFHLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3RFLGdFQUFnRTtnQkFDaEUsT0FBTyxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUUsU0FBUyxFQUFFLENBQUM7WUFDN0MsQ0FBQztZQUNELE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUFDLFlBQVksRUFBRSx1QkFBdUIsQ0FBQyxDQUFDO1lBQ3JFLE1BQU0sSUFBSSxtQkFBbUIsQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO1FBQzVELENBQUM7UUFDRCxPQUFPLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxZQUFZLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDMUQsQ0FBQztJQUVPLEtBQUssQ0FBQyxvQkFBb0IsQ0FBQyxZQUFvQixFQUFFLFVBQWtCO1FBQ3pFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDMUQsSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUNaLE9BQU8sT0FBTyxDQUFDO1FBQ2pCLENBQUM7UUFDRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsWUFBWSxFQUFFLFVBQVUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUU7WUFDN0UsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUMvQyxDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLENBQUMsWUFBWSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ25ELE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFTyxLQUFLLENBQUMsa0JBQWtCLENBQUMsWUFBb0IsRUFBRSxVQUFrQjtRQUN2RSxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNkLE1BQU0sSUFBSSxtQkFBbUIsQ0FBQyx1QkFBdUIsQ0FBQyxDQUFDO1FBQ3pELENBQUM7UUFDRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDeEIsTUFBTSxTQUFTLEdBQUcsR0FBRyxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUM7UUFDM0MsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxzQkFBc0IsQ0FDL0Q7WUFDRSxFQUFFLEVBQUUsWUFBWTtZQUNoQixTQUFTLEVBQUUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFO1lBQzdCLFNBQVMsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUU7WUFDdkIsOERBQThEO1lBQzlELEdBQUcsRUFBRSxDQUFDLEVBQUUsZ0JBQWdCLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFBRSxFQUFFLGdCQUFnQixFQUFFLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUM7U0FDcEYsRUFDRCxFQUFFLElBQUksRUFBRSxFQUFFLGdCQUFnQixFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQ3pDLEVBQUUsY0FBYyxFQUFFLE9BQU8sRUFBRSxDQUM1QixDQUFDO1FBQ0YsSUFBSSxPQUFPLENBQUMsTUFBTSxLQUFLLFNBQVMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUN0RCxPQUFPLElBQUksQ0FBQyx3QkFBd0IsQ0FBQyxZQUFZLEVBQUUsVUFBVSxDQUFDLENBQUM7UUFDakUsQ0FBQztRQUNELE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxRQUFrQyxDQUFDO1FBQzNELHFFQUFxRTtRQUNyRSx3RUFBd0U7UUFDeEUseURBQXlEO1FBQ3pELE1BQU0sYUFBYSxHQUFHLE9BQU8sT0FBTyxDQUFDLG9CQUFvQixLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDMUcsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUNoRSxJQUFJLFlBQVksS0FBSyxTQUFTLElBQUksYUFBYSxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsR0FBRyw0QkFBNEIsRUFBRSxDQUFDO1lBQzdGLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUNyQyxFQUFFLEVBQUUsRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxFQUFFLGdCQUFnQixFQUFFLFNBQVMsRUFBRSxFQUNoRixFQUFFLElBQUksRUFBRSxFQUFFLGdCQUFnQixFQUFFLENBQUMsRUFBRSxFQUFFLENBQ2xDLENBQUM7WUFDRixPQUFPLEVBQUUsV0FBVyxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsQ0FBQztRQUN0RixDQUFDO1FBQ0QsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsa0JBQWtCLENBQUMsQ0FBQztRQUNqRSxJQUFJLFlBQVksS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUMvQixNQUFNLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxZQUFZLEVBQUUsaUNBQWlDLENBQUMsQ0FBQztZQUMvRSxNQUFNLElBQUksbUJBQW1CLENBQUMsMEJBQTBCLENBQUMsQ0FBQztRQUM1RCxDQUFDO1FBQ0QsSUFBSSxPQUE2QixDQUFDO1FBQ2xDLElBQUksQ0FBQztZQUNILE9BQU8sR0FBRyxNQUFNLFFBQVEsQ0FBQyxhQUFhLENBQUMsWUFBWSxDQUFDLENBQUM7UUFDdkQsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLEtBQUssWUFBWSx1QkFBdUIsRUFBRSxDQUFDO2dCQUM3QyxNQUFNLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxZQUFZLEVBQUUsdUJBQXVCLENBQUMsQ0FBQztnQkFDckUsTUFBTSxJQUFJLG1CQUFtQixDQUFDLDBCQUEwQixFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ25FLENBQUM7WUFDRCxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLDBCQUEwQixFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsT0FBTyxFQUFFLFlBQVksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDeEcsTUFBTSxJQUFJLG1CQUFtQixDQUFDLHVCQUF1QixFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ2hFLENBQUM7UUFDRCx3RUFBd0U7UUFDeEUsdUVBQXVFO1FBQ3ZFLElBQUksT0FBZSxDQUFDO1FBQ3BCLElBQUksQ0FBQztZQUNILE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUNyRCxFQUFFLEVBQUUsRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxFQUFFLGdCQUFnQixFQUFFLFNBQVMsRUFBRSxFQUNoRjtnQkFDRSxJQUFJLEVBQUU7b0JBQ0osaUJBQWlCLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQztvQkFDNUQsR0FBRyxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLEVBQUUsa0JBQWtCLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztvQkFDbkcsb0JBQW9CLEVBQUUsT0FBTyxDQUFDLG9CQUFvQjtvQkFDbEQsZ0JBQWdCLEVBQUUsQ0FBQztpQkFDcEI7YUFDRixDQUNGLENBQUM7WUFDRixPQUFPLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQztRQUNqQyxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLHlFQUF5RTtZQUN6RSxzRUFBc0U7WUFDdEUsdUJBQXVCO1lBQ3ZCLE1BQU0sSUFBSSxDQUFDLG1CQUFtQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ3hDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsWUFBWSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUN0RyxNQUFNLElBQUksbUJBQW1CLENBQUMsdUJBQXVCLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDaEUsQ0FBQztRQUNELElBQUksT0FBTyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ2xCLE1BQU0sSUFBSSxDQUFDLG1CQUFtQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ3hDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLG1CQUFtQixFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1lBQ3RHLE1BQU0sSUFBSSxtQkFBbUIsQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO1FBQzVELENBQUM7UUFDRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsT0FBTyxDQUFDLG9CQUFvQixFQUFFLENBQUM7SUFDNUcsQ0FBQztJQUVEOzs7O09BSUc7SUFDSyxLQUFLLENBQUMsd0JBQXdCLENBQUMsWUFBb0IsRUFBRSxVQUFrQjtRQUM3RSxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxZQUFZLEVBQUUsQ0FBQyxDQUFDO1FBQzdFLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN4QixNQUFNLFNBQVMsR0FBRyxPQUFPLE9BQU8sRUFBRSxvQkFBb0IsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3ZHLE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO1FBQ3JGLElBQ0UsT0FBTztlQUNKLE9BQU8sQ0FBQyxTQUFTLEtBQUssU0FBUztlQUMvQixPQUFPLENBQUMsU0FBUyxHQUFHLEdBQUc7ZUFDdkIsV0FBVyxLQUFLLFNBQVM7ZUFDekIsU0FBUyxHQUFHLEdBQUcsR0FBRyw0QkFBNEIsRUFDakQsQ0FBQztZQUNELE9BQU8sRUFBRSxXQUFXLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsQ0FBQztRQUN6RCxDQUFDO1FBQ0QsTUFBTSxJQUFJLG1CQUFtQixDQUFDLHVCQUF1QixDQUFDLENBQUM7SUFDekQsQ0FBQztJQUVELDZFQUE2RTtJQUM3RSxZQUFZO0lBQ1osNkVBQTZFO0lBRXJFLGVBQWU7UUFDckIsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUNwQixNQUFNLElBQUksaUJBQWlCLENBQUMsMEJBQTBCLENBQUMsQ0FBQztRQUMxRCxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUMsU0FBUyxDQUFDO0lBQ3hCLENBQUM7SUFFTyxhQUFhLENBQUMsUUFBZ0IsRUFBRSxjQUFzQjtRQUM1RCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDeEIsTUFBTSxPQUFPLEdBQUcsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQzNDLE9BQU8sQ0FBQyxFQUFFLEdBQUcsTUFBTSxHQUFHLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN0QyxPQUFPLENBQUMsU0FBUyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNyQyxPQUFPLENBQUMsTUFBTSxHQUFHLGNBQWMsQ0FBQztRQUNoQyxPQUFPLENBQUMsU0FBUyxHQUFHLEdBQUcsQ0FBQztRQUN4QixPQUFPLENBQUMsVUFBVSxHQUFHLEdBQUcsQ0FBQztRQUN6QixPQUFPLENBQUMsU0FBUyxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsb0JBQW9CLEdBQUcsS0FBSyxDQUFDO1FBQzVELE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFTyxXQUFXLENBQUMsUUFBZ0I7UUFDbEMsT0FBTyxlQUFlLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ25FLENBQUM7SUFFTyxPQUFPLENBQUMsVUFBa0MsRUFBRSxRQUFnQjtRQUNsRSxPQUFPO1lBQ0wsT0FBTyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDO1lBQ2xDLFNBQVMsRUFBRSxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQztZQUNyQyxhQUFhLEVBQUUsZUFBZSxDQUFDLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxRQUFRLEVBQUU7Z0JBQy9ELFFBQVEsRUFBRSxJQUFJO2dCQUNkLE1BQU0sRUFBRSxJQUFJLENBQUMsYUFBYTtnQkFDMUIsUUFBUSxFQUFFLEtBQUs7Z0JBQ2YsYUFBYSxFQUFFLElBQUksQ0FBQyxvQkFBb0I7YUFDekMsQ0FBQztTQUNILENBQUM7SUFDSixDQUFDO0lBRU8sUUFBUSxDQUFDLFVBQWtDO1FBQ2pELE9BQU87WUFDTCxFQUFFLEVBQUUsVUFBVSxDQUFDLEVBQUU7WUFDakIsV0FBVyxFQUFFLFVBQVUsQ0FBQyxNQUFNO1lBQzlCLFNBQVMsRUFBRSxVQUFVLENBQUMsU0FBUztZQUMvQixVQUFVLEVBQUUsVUFBVSxDQUFDLFVBQVU7WUFDakMsU0FBUyxFQUFFLFVBQVUsQ0FBQyxTQUFTO1lBQy9CLEdBQUcsQ0FBQyxVQUFVLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxFQUFFLFVBQVUsRUFBRSxVQUFVLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN2RSxHQUFHLENBQUMsVUFBVSxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsRUFBRSxjQUFjLEVBQUUsVUFBVSxDQUFDLGNBQWMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7U0FDcEYsQ0FBQztJQUNKLENBQUM7SUFFRCx5R0FBeUc7SUFDakcsS0FBSyxDQUFDLFdBQVcsQ0FBQyxVQUF5QztRQUNqRSxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDeEIsSUFBSSxDQUFDLFVBQVUsSUFBSSxVQUFVLENBQUMsU0FBUyxLQUFLLFNBQVMsSUFBSSxVQUFVLENBQUMsU0FBUyxJQUFJLEdBQUcsRUFBRSxDQUFDO1lBQ3JGLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUNELElBQUksQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUMxRCxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFDRCxJQUFJLEdBQUcsR0FBRyxVQUFVLENBQUMsVUFBVSxHQUFHLHlCQUF5QixFQUFFLENBQUM7WUFDNUQscUVBQXFFO1lBQ3JFLHdEQUF3RDtZQUN4RCxNQUFNLE9BQU8sR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FDckQsRUFBRSxFQUFFLEVBQUUsVUFBVSxDQUFDLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEVBQUUsU0FBUyxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxFQUFFLEVBQzdFLEVBQUUsSUFBSSxFQUFFLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQzlCLENBQUM7WUFDRixJQUFJLE9BQU8sQ0FBQyxZQUFZLEtBQUssQ0FBQyxFQUFFLENBQUM7Z0JBQy9CLE9BQU8sSUFBSSxDQUFDO1lBQ2QsQ0FBQztZQUNELFVBQVUsQ0FBQyxVQUFVLEdBQUcsR0FBRyxDQUFDO1FBQzlCLENBQUM7UUFDRCxPQUFPLFVBQVUsQ0FBQztJQUNwQixDQUFDO0lBRU8sS0FBSyxDQUFDLHFCQUFxQixDQUNqQyxVQUF1QyxFQUN2QyxjQUFzQixFQUN0QixhQUE4QjtRQUU5QixNQUFNLEtBQUssR0FBRyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDOUIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsY0FBYyxDQUFDLENBQUM7UUFDMUQsT0FBTyxDQUFDLFVBQVUsR0FBRyxhQUFhLENBQUMsT0FBTyxDQUFDO1FBQzNDLE9BQU8sQ0FBQyxjQUFjLEdBQUcsVUFBVSxDQUFDLEVBQUUsQ0FBQztRQUN2QyxPQUFPLENBQUMsaUJBQWlCLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQzVFLE9BQU8sQ0FBQyxrQkFBa0IsR0FBRyxhQUFhLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNoSCxPQUFPLENBQUMsb0JBQW9CLEdBQUcsYUFBYSxDQUFDLG9CQUFvQixDQUFDO1FBQ2xFLE9BQU8sQ0FBQyxnQkFBZ0IsR0FBRyxDQUFDLENBQUM7UUFDN0IsTUFBTSxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7UUFFckIsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQ3pELEVBQUUsRUFBRSxFQUFFLFVBQVUsQ0FBQyxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsSUFBSSxFQUFFLEVBQzdDLEVBQUUsSUFBSSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsT0FBTyxDQUFDLEVBQUUsRUFBRSxFQUFFLENBQzNDLENBQUM7UUFDRixJQUFJLE1BQU0sQ0FBQyxZQUFZLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDOUIsK0RBQStEO1lBQy9ELE1BQU0sSUFBSSxDQUFDLHNCQUFzQixDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUM5QyxNQUFNLElBQUksaUJBQWlCLENBQUMsMkJBQTJCLENBQUMsQ0FBQztRQUMzRCxDQUFDO1FBQ0QsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDN0MsSUFBSSxDQUFDO1lBQ0gsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLHVCQUF1QixFQUFFLENBQUMsRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFDNUUsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZix3RUFBd0U7WUFDeEUsdUVBQXVFO1lBQ3ZFLDRDQUE0QztZQUM1QyxJQUFJLENBQUM7Z0JBQ0gsTUFBTSxJQUFJLENBQUMsc0JBQXNCLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQ2hELENBQUM7WUFBQyxPQUFPLFlBQVksRUFBRSxDQUFDO2dCQUN0QixJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLHdCQUF3QixFQUFFLFNBQVMsRUFBRSxPQUFPLENBQUMsRUFBRSxFQUFFLE9BQU8sRUFBRSxZQUFZLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQzdHLENBQUM7WUFDRCxNQUFNLEtBQUssQ0FBQztRQUNkLENBQUM7UUFDRCxPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0lBRUQsZ0ZBQWdGO0lBQ3hFLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxZQUFvQjtRQUN2RCxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FDckMsRUFBRSxFQUFFLEVBQUUsWUFBWSxFQUFFLFNBQVMsRUFBRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUNuRCxFQUFFLElBQUksRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsSUFBSSxFQUFFLEVBQUUsR0FBRyxvQkFBb0IsRUFBRSxFQUFFLENBQzlELENBQUM7SUFDSixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLGtCQUFrQixDQUFDLFFBSTFCO1FBQ0MsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxjQUFjLENBQUM7UUFDeEMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ1YsT0FBTztRQUNULENBQUM7UUFDRCxNQUFNLGFBQWEsR0FBRyxDQUFDLFFBQWlCLEVBQUUsRUFBRTtZQUMxQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLGFBQWEsRUFBRSxJQUFJLEVBQUUsZ0JBQWdCLEVBQUUsU0FBUyxFQUFFLFFBQVEsQ0FBQyxTQUFTLEVBQUUsT0FBTyxFQUFFLFlBQVksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDOUgsQ0FBQyxDQUFDO1FBQ0YsSUFBSSxDQUFDO1lBQ0gsTUFBTSxNQUFNLEdBQVksSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1lBQ3pELElBQUksTUFBTSxZQUFZLE9BQU8sRUFBRSxDQUFDO2dCQUM5QixNQUFNLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFDO1lBQzlCLENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN2QixDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSyxLQUFLLENBQUMsYUFBYSxDQUN6QixZQUFvQixFQUNwQixTQUF3QztRQUV4QyxJQUFJLE9BQU8sWUFBWSxLQUFLLFFBQVEsSUFBSSxZQUFZLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ2xFLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUNELE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsc0JBQXNCLENBQy9ELEVBQUUsRUFBRSxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFDbkQsRUFBRSxJQUFJLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxFQUFFLEdBQUcsb0JBQW9CLEVBQUUsRUFBRSxFQUM3RCxFQUFFLGNBQWMsRUFBRSxRQUFRLEVBQUUsQ0FDN0IsQ0FBQztRQUNGLE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxNQUFNLEtBQUssU0FBUyxJQUFJLE9BQU8sQ0FBQyxRQUFRLEtBQUssSUFBSSxDQUFDO1FBQzlFLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsQ0FBQyxDQUFDO1FBQ3JGLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNqQixPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFDRCxNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsUUFBa0MsQ0FBQztRQUMxRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxDQUFDLGlCQUFpQixFQUFFLGtCQUFrQixFQUFFLE1BQU0sQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO0lBQ3hHLENBQUM7SUFFRCxrR0FBa0c7SUFDMUYsS0FBSyxDQUFDLGtCQUFrQixDQUFDLFlBQW9CLEVBQUUsU0FBd0M7UUFDN0YsSUFBSSxNQUFNLElBQUksQ0FBQyxhQUFhLENBQUMsWUFBWSxFQUFFLFNBQVMsQ0FBQyxFQUFFLENBQUM7WUFDdEQsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDO1FBQ3hGLENBQUM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ssS0FBSyxDQUFDLGtCQUFrQixDQUFDLFNBQXdCO1FBQ3ZELElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxJQUFJLENBQUMsQ0FBQyxTQUFTLENBQUMsaUJBQWlCLElBQUksQ0FBQyxTQUFTLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDO1lBQ3ZGLE9BQU87UUFDVCxDQUFDO1FBQ0QsTUFBTSxJQUFJLENBQUMsc0JBQXNCLENBQUMsS0FBSyxJQUFJLEVBQUU7WUFDM0MsS0FBSyxNQUFNLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxJQUFJO2dCQUMzQixDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsRUFBRSxlQUFlLENBQUM7Z0JBQy9DLENBQUMsU0FBUyxDQUFDLGlCQUFpQixFQUFFLGNBQWMsQ0FBQzthQUNyQyxFQUFFLENBQUM7Z0JBQ1gsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO29CQUNaLFNBQVM7Z0JBQ1gsQ0FBQztnQkFDRCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUN0QyxJQUFJLEtBQUssS0FBSyxTQUFTLEVBQUUsQ0FBQztvQkFDeEIsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSw4QkFBOEIsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLCtDQUErQyxFQUFFLENBQUMsQ0FBQztvQkFDckgsU0FBUztnQkFDWCxDQUFDO2dCQUNELE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFDdEMsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVPLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxVQUFnQztRQUNoRSxNQUFNLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxLQUFLLElBQUksRUFBRTtZQUMzQyxJQUFJLFVBQVUsQ0FBQyxZQUFZLEVBQUUsQ0FBQztnQkFDNUIsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFVBQVUsQ0FBQyxZQUFZLEVBQUUsZUFBZSxDQUFDLENBQUM7WUFDbkUsQ0FBQztZQUNELE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLENBQUMsV0FBVyxFQUFFLGNBQWMsQ0FBQyxDQUFDO1FBQ2pFLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVPLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxhQUE4QjtRQUNqRSxNQUFNLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxLQUFLLElBQUksRUFBRTtZQUMzQyxJQUFJLGFBQWEsQ0FBQyxZQUFZLEVBQUUsQ0FBQztnQkFDL0IsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxZQUFZLEVBQUUsZUFBZSxDQUFDLENBQUM7WUFDdEUsQ0FBQztZQUNELE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsV0FBVyxFQUFFLGNBQWMsQ0FBQyxDQUFDO1FBQ3BFLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVPLEtBQUssQ0FBQyxXQUFXLENBQUMsUUFBZ0IsRUFBRSxPQUF1QjtRQUNqRSxJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksQ0FBQyxTQUFTLEVBQUUsV0FBVyxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUN2RCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLHVFQUF1RTtZQUN2RSxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLDhCQUE4QixFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLFlBQVksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDcEcsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7T0FLRztJQUNLLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxPQUE0QjtRQUMvRCxJQUFJLEtBQWdELENBQUM7UUFDckQsTUFBTSxJQUFJLEdBQUcsT0FBTyxFQUFFLENBQUMsSUFBSSxDQUN6QixHQUFHLEVBQUUsQ0FBQyxNQUFlLEVBQ3JCLENBQUMsUUFBaUIsRUFBRSxFQUFFO1lBQ3BCLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxLQUFLLEVBQUUsOEJBQThCLEVBQUUsT0FBTyxFQUFFLFlBQVksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDdEYsT0FBTyxNQUFlLENBQUM7UUFDekIsQ0FBQyxDQUNGLENBQUM7UUFDRixNQUFNLFFBQVEsR0FBRyxJQUFJLE9BQU8sQ0FBYSxDQUFDLFVBQVUsRUFBRSxFQUFFO1lBQ3RELEtBQUssR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxFQUFFLDBCQUEwQixDQUFDLENBQUM7WUFDN0UsS0FBSyxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ2hCLENBQUMsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDO1lBQ0gsSUFBSSxDQUFDLE1BQU0sT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssVUFBVSxFQUFFLENBQUM7Z0JBQzFELElBQUksQ0FBQyxJQUFJLENBQUM7b0JBQ1IsS0FBSyxFQUFFLGlDQUFpQztvQkFDeEMsT0FBTyxFQUFFLDBEQUEwRDtpQkFDcEUsQ0FBQyxDQUFDO1lBQ0wsQ0FBQztRQUNILENBQUM7Z0JBQVMsQ0FBQztZQUNULFlBQVksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUN0QixDQUFDO0lBQ0gsQ0FBQztJQUVELHFFQUFxRTtJQUM3RCxVQUFVLENBQUMsU0FBNkI7UUFDOUMsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQ2YsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztRQUNELElBQUksQ0FBQztZQUNILE9BQU8sSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDekMsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLE9BQU8sU0FBUyxDQUFDO1FBQ25CLENBQUM7SUFDSCxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ssS0FBSyxDQUFDLG9CQUFvQjtRQUNoQyxJQUFJLENBQUM7WUFDSCxNQUFNLElBQUksR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLGlCQUFpQixDQUFDO2dCQUM3RCxNQUFNLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxHQUFHLDRCQUE0QixFQUFFLEVBQUU7Z0JBQzFFLFNBQVMsRUFBRSxXQUFXO2dCQUN0QixhQUFhLEVBQUUsS0FBSztnQkFDcEIsS0FBSyxFQUFFLHlCQUF5QjthQUNqQyxDQUFDLENBQUM7WUFDSCxLQUFLLE1BQU0sT0FBTyxJQUFJLElBQUksQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDckMsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQUMsRUFBRSxFQUFFLEVBQUUsT0FBTyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDbkUsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSw0QkFBNEIsRUFBRSxPQUFPLEVBQUUsWUFBWSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNuRixDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSyxJQUFJLENBQUMsUUFBNEI7UUFDdkMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUM7UUFDakMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ1YsT0FBTyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7WUFDeEMsT0FBTztRQUNULENBQUM7UUFDRCxNQUFNLGFBQWEsR0FBRyxDQUFDLFFBQWlCLEVBQUUsRUFBRTtZQUMxQyxNQUFNLE9BQU8sR0FBdUIsRUFBRSxLQUFLLEVBQUUsYUFBYSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLFlBQVksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQy9HLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO1lBQ3ZDLE9BQU8sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO1FBQzFDLENBQUMsQ0FBQztRQUNGLElBQUksQ0FBQztZQUNILE1BQU0sTUFBTSxHQUFZLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQztZQUN6RCxJQUFJLE1BQU0sWUFBWSxPQUFPLEVBQUUsQ0FBQztnQkFDOUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMsQ0FBQztZQUM5QixDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDdkIsQ0FBQztJQUNILENBQUM7Q0FDRiJ9