@cartanova/qgrid-cli 1.0.4 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/dist/application/qgrid/pool.js +15 -6
- package/bundle/dist/application/qgrid/qgrid.frame.js +17 -22
- package/bundle/dist/application/qgrid/qgrid.types.js +5 -2
- package/bundle/dist/application/request-log/request-log.model.js +2 -2
- package/bundle/dist/sonamu.config.js +5 -2
- package/bundle/src/application/qgrid/pool.ts +17 -7
- package/bundle/src/application/qgrid/qgrid.frame.ts +18 -25
- package/bundle/src/application/qgrid/qgrid.types.ts +1 -1
- package/bundle/src/application/request-log/request-log.model.ts +2 -2
- package/bundle/src/sonamu.config.ts +1 -1
- package/bundle/web-dist/{web-dist/client/assets/index-DlPchHBY.js → client/assets/index-C0m8rRjO.js} +2 -2
- package/bundle/web-dist/client/assets/{routes-CagDz23B.js → routes-B1eOXnpo.js} +1 -1
- package/bundle/web-dist/client/assets/{show-aXUGa0gm.js → show-BztN6MXO.js} +1 -1
- package/bundle/web-dist/client/assets/tokens-rR2_-Pte.js +1 -0
- package/bundle/web-dist/client/index.html +1 -1
- package/bundle/web-dist/server/assets/{tokens-JH6FsnWZ.js → tokens-ARL5AOAh.js} +4 -1
- package/bundle/web-dist/server/entry-server.generated.js +1 -1
- package/dist/cli.js +38 -24
- package/package.json +10 -10
- package/bundle/dist/dist/_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js +0 -9
- package/bundle/dist/dist/_virtual/_@oxc-project_runtime@0.122.0/helpers/decorateMetadata.js +0 -6
- package/bundle/dist/dist/_virtual/_rolldown/runtime.js +0 -15
- package/bundle/dist/dist/application/qgrid/oauth.js +0 -140
- package/bundle/dist/dist/application/qgrid/pool.js +0 -100
- package/bundle/dist/dist/application/qgrid/qgrid.frame.js +0 -280
- package/bundle/dist/dist/application/qgrid/qgrid.types.js +0 -79
- package/bundle/dist/dist/application/qgrid/worker.js +0 -183
- package/bundle/dist/dist/application/queries.generated.js +0 -31
- package/bundle/dist/dist/application/request-log/request-log.model.js +0 -113
- package/bundle/dist/dist/application/request-log/request-log.types.js +0 -13
- package/bundle/dist/dist/application/sonamu.generated.js +0 -99
- package/bundle/dist/dist/application/sonamu.generated.sso.js +0 -34
- package/bundle/dist/dist/application/token/token.model.js +0 -157
- package/bundle/dist/dist/application/token/token.types.js +0 -22
- package/bundle/dist/dist/i18n/en.js +0 -63
- package/bundle/dist/dist/i18n/ko.js +0 -62
- package/bundle/dist/dist/i18n/sd.generated.js +0 -272
- package/bundle/dist/dist/index.js +0 -11
- package/bundle/dist/dist/migrations/20260410171405_create__request_logs.js +0 -26
- package/bundle/dist/dist/migrations/20260410171406_create__tokens.js +0 -24
- package/bundle/dist/dist/migrations/20260411120624_alter_tokens_alter2.js +0 -16
- package/bundle/dist/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +0 -155
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_expect@4.1.2/node_modules/@vitest/expect/dist/index.js +0 -1484
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_pretty-format@4.1.2/node_modules/@vitest/pretty-format/dist/index.js +0 -887
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_runner@4.1.2/node_modules/@vitest/runner/dist/chunk-artifact.js +0 -2746
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_runner@4.1.2/node_modules/@vitest/runner/dist/index.js +0 -8
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_runner@4.1.2/node_modules/@vitest/runner/dist/utils.js +0 -8
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_snapshot@4.1.2/node_modules/@vitest/snapshot/dist/index.js +0 -662
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_spy@4.1.2/node_modules/@vitest/spy/dist/index.js +0 -386
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js +0 -82
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/constants.js +0 -48
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/diff.js +0 -1307
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/display.js +0 -558
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/error.js +0 -33
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/helpers.js +0 -226
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/offset.js +0 -27
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/serialize.js +0 -77
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/source-map.js +0 -374
- package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/timers.js +0 -37
- package/bundle/dist/dist/node_modules/.pnpm/chai@6.2.2/node_modules/chai/index.js +0 -2886
- package/bundle/dist/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.js +0 -10
- package/bundle/dist/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.js +0 -109
- package/bundle/dist/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.js +0 -10
- package/bundle/dist/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.js +0 -10
- package/bundle/dist/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.js +0 -10
- package/bundle/dist/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +0 -1127
- package/bundle/dist/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/index.js +0 -21
- package/bundle/dist/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.js +0 -432
- package/bundle/dist/dist/node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.js +0 -506
- package/bundle/dist/dist/node_modules/.pnpm/tinyrainbow@3.1.0/node_modules/tinyrainbow/dist/index.js +0 -86
- package/bundle/dist/dist/node_modules/.pnpm/vite@8.0.3_@emnapi_core@1.9.2_@emnapi_runtime@1.9.2_@types_node@25.0.7_esbuild@0.27.4_jiti@2.6.1_sass@1.98.0_tsx@4.21.0/node_modules/vite/dist/node/module-runner.js +0 -426
- package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.js +0 -6
- package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/benchmark.CX_oY03V.js +0 -41
- package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/evaluatedModules.Dg1zASAC.js +0 -19
- package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/index.Chj8NDwU.js +0 -202
- package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/index.DOa3dzoN.js +0 -16
- package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/rpc.MzXet3jl.js +0 -59
- package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/test.p_J6dB8a.js +0 -2910
- package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.js +0 -44
- package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/index.js +0 -28
- package/bundle/dist/dist/sonamu.config.js +0 -196
- package/bundle/dist/dist/testing/fixture.js +0 -8
- package/bundle/dist/dist/testing/global.js +0 -9
- package/bundle/dist/dist/testing/setup-mocks.js +0 -17
- package/bundle/dist/dist/utils/subset-loaders.js +0 -12
- package/bundle/src/src/application/qgrid/oauth.ts +0 -181
- package/bundle/src/src/application/qgrid/pool.ts +0 -132
- package/bundle/src/src/application/qgrid/qgrid.frame.ts +0 -275
- package/bundle/src/src/application/qgrid/qgrid.types.ts +0 -117
- package/bundle/src/src/application/qgrid/worker.ts +0 -234
- package/bundle/src/src/application/queries.generated.ts +0 -59
- package/bundle/src/src/application/request-log/request-log.entity.json +0 -75
- package/bundle/src/src/application/request-log/request-log.model.ts +0 -155
- package/bundle/src/src/application/request-log/request-log.types.ts +0 -15
- package/bundle/src/src/application/sonamu.generated.http +0 -187
- package/bundle/src/src/application/sonamu.generated.sso.ts +0 -66
- package/bundle/src/src/application/sonamu.generated.ts +0 -135
- package/bundle/src/src/application/token/token.entity.json +0 -37
- package/bundle/src/src/application/token/token.model.ts +0 -160
- package/bundle/src/src/application/token/token.types.ts +0 -26
- package/bundle/src/src/i18n/en.ts +0 -59
- package/bundle/src/src/i18n/ko.ts +0 -59
- package/bundle/src/src/i18n/sd.generated.ts +0 -384
- package/bundle/src/src/index.ts +0 -6
- package/bundle/src/src/migrations/20260410171405_create__request_logs.ts +0 -23
- package/bundle/src/src/migrations/20260410171406_create__tokens.ts +0 -21
- package/bundle/src/src/migrations/20260411120624_alter_tokens_alter2.ts +0 -15
- package/bundle/src/src/sonamu.config.ts +0 -248
- package/bundle/src/src/testing/fixture.ts +0 -6
- package/bundle/src/src/testing/global.ts +0 -6
- package/bundle/src/src/testing/setup-mocks.ts +0 -44
- package/bundle/src/src/typings/fastify.d.ts +0 -8
- package/bundle/src/src/typings/sonamu.d.ts +0 -17
- package/bundle/src/src/utils/subset-loaders.ts +0 -11
- package/bundle/web-dist/client/assets/index-DlPchHBY.js +0 -116
- package/bundle/web-dist/client/assets/tokens-DRkXtZ3Q.js +0 -1
- package/bundle/web-dist/web-dist/client/assets/index-vZsvyuFn.css +0 -1
- package/bundle/web-dist/web-dist/client/assets/rolldown-runtime-Dw2cE7zH.js +0 -1
- package/bundle/web-dist/web-dist/client/assets/routes-CagDz23B.js +0 -1
- package/bundle/web-dist/web-dist/client/assets/sd.generated-BrQoQXvv.js +0 -47
- package/bundle/web-dist/web-dist/client/assets/services.generated-BspO2L0j.js +0 -1
- package/bundle/web-dist/web-dist/client/assets/show-aXUGa0gm.js +0 -29
- package/bundle/web-dist/web-dist/client/assets/tokens-DRkXtZ3Q.js +0 -1
- package/bundle/web-dist/web-dist/client/assets/vendor-react-DH4kUfK2.js +0 -9
- package/bundle/web-dist/web-dist/client/assets/vendor-tanstack-CUxlo6YN.js +0 -1
- package/bundle/web-dist/web-dist/client/index.html +0 -18
- package/bundle/web-dist/web-dist/server/assets/react-dom-CGIzOSuj.js +0 -335
- package/bundle/web-dist/web-dist/server/assets/routes-ClzReRep.js +0 -490
- package/bundle/web-dist/web-dist/server/assets/sd.generated-BnonqqKr.js +0 -21694
- package/bundle/web-dist/web-dist/server/assets/services.generated-D5ABD5fr.js +0 -836
- package/bundle/web-dist/web-dist/server/assets/show-C9pcdSxc.js +0 -23364
- package/bundle/web-dist/web-dist/server/assets/tokens-JH6FsnWZ.js +0 -860
- package/bundle/web-dist/web-dist/server/entry-server.generated.js +0 -44646
|
@@ -1,2746 +0,0 @@
|
|
|
1
|
-
import { format, formatRegExp, objDisplay } from "../../../../../@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/display.js";
|
|
2
|
-
import { assertTypes, createDefer, filterOutComments, isNegativeNaN, isObject, objectAttr, ordinal, shuffle, toArray, unique } from "../../../../../@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/helpers.js";
|
|
3
|
-
import { processError } from "../../../../../@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/error.js";
|
|
4
|
-
import { getSafeTimers } from "../../../../../@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/timers.js";
|
|
5
|
-
import { parseSingleStack } from "../../../../../@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/source-map.js";
|
|
6
|
-
import { relative } from "../../../../../pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.js";
|
|
7
|
-
import "../../../../../pathe@2.0.3/node_modules/pathe/dist/index.js";
|
|
8
|
-
//#region ../../node_modules/.pnpm/@vitest+runner@4.1.2/node_modules/@vitest/runner/dist/chunk-artifact.js
|
|
9
|
-
var PendingError = class extends Error {
|
|
10
|
-
code = "VITEST_PENDING";
|
|
11
|
-
taskId;
|
|
12
|
-
constructor(message, task, note) {
|
|
13
|
-
super(message);
|
|
14
|
-
this.message = message;
|
|
15
|
-
this.note = note;
|
|
16
|
-
this.taskId = task.id;
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
var TestRunAbortError = class extends Error {
|
|
20
|
-
name = "TestRunAbortError";
|
|
21
|
-
reason;
|
|
22
|
-
constructor(message, reason) {
|
|
23
|
-
super(message);
|
|
24
|
-
this.reason = reason;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
var FixtureDependencyError = class extends Error {
|
|
28
|
-
name = "FixtureDependencyError";
|
|
29
|
-
};
|
|
30
|
-
var FixtureAccessError = class extends Error {
|
|
31
|
-
name = "FixtureAccessError";
|
|
32
|
-
};
|
|
33
|
-
var FixtureParseError = class extends Error {
|
|
34
|
-
name = "FixtureParseError";
|
|
35
|
-
};
|
|
36
|
-
var AroundHookSetupError = class extends Error {
|
|
37
|
-
name = "AroundHookSetupError";
|
|
38
|
-
};
|
|
39
|
-
var AroundHookTeardownError = class extends Error {
|
|
40
|
-
name = "AroundHookTeardownError";
|
|
41
|
-
};
|
|
42
|
-
var AroundHookMultipleCallsError = class extends Error {
|
|
43
|
-
name = "AroundHookMultipleCallsError";
|
|
44
|
-
};
|
|
45
|
-
const fnMap = /* @__PURE__ */ new WeakMap();
|
|
46
|
-
const testFixtureMap = /* @__PURE__ */ new WeakMap();
|
|
47
|
-
const hooksMap = /* @__PURE__ */ new WeakMap();
|
|
48
|
-
function setFn(key, fn) {
|
|
49
|
-
fnMap.set(key, fn);
|
|
50
|
-
}
|
|
51
|
-
function getFn(key) {
|
|
52
|
-
return fnMap.get(key);
|
|
53
|
-
}
|
|
54
|
-
function setTestFixture(key, fixture) {
|
|
55
|
-
testFixtureMap.set(key, fixture);
|
|
56
|
-
}
|
|
57
|
-
function getTestFixtures(key) {
|
|
58
|
-
return testFixtureMap.get(key);
|
|
59
|
-
}
|
|
60
|
-
function setHooks(key, hooks) {
|
|
61
|
-
hooksMap.set(key, hooks);
|
|
62
|
-
}
|
|
63
|
-
function getHooks(key) {
|
|
64
|
-
return hooksMap.get(key);
|
|
65
|
-
}
|
|
66
|
-
const FIXTURE_STACK_TRACE_KEY = Symbol.for("VITEST_FIXTURE_STACK_TRACE");
|
|
67
|
-
var TestFixtures = class TestFixtures {
|
|
68
|
-
_suiteContexts;
|
|
69
|
-
_overrides = /* @__PURE__ */ new WeakMap();
|
|
70
|
-
_registrations;
|
|
71
|
-
static _definitions = [];
|
|
72
|
-
static _builtinFixtures = [
|
|
73
|
-
"task",
|
|
74
|
-
"signal",
|
|
75
|
-
"onTestFailed",
|
|
76
|
-
"onTestFinished",
|
|
77
|
-
"skip",
|
|
78
|
-
"annotate"
|
|
79
|
-
];
|
|
80
|
-
static _fixtureOptionKeys = [
|
|
81
|
-
"auto",
|
|
82
|
-
"injected",
|
|
83
|
-
"scope"
|
|
84
|
-
];
|
|
85
|
-
static _fixtureScopes = [
|
|
86
|
-
"test",
|
|
87
|
-
"file",
|
|
88
|
-
"worker"
|
|
89
|
-
];
|
|
90
|
-
static _workerContextSuite = { type: "worker" };
|
|
91
|
-
static clearDefinitions() {
|
|
92
|
-
TestFixtures._definitions.length = 0;
|
|
93
|
-
}
|
|
94
|
-
static getWorkerContexts() {
|
|
95
|
-
return TestFixtures._definitions.map((f) => f.getWorkerContext());
|
|
96
|
-
}
|
|
97
|
-
static getFileContexts(file) {
|
|
98
|
-
return TestFixtures._definitions.map((f) => f.getFileContext(file));
|
|
99
|
-
}
|
|
100
|
-
static isFixtureOptions(obj) {
|
|
101
|
-
return isObject(obj) && Object.keys(obj).some((key) => TestFixtures._fixtureOptionKeys.includes(key));
|
|
102
|
-
}
|
|
103
|
-
constructor(registrations) {
|
|
104
|
-
this._registrations = registrations ?? /* @__PURE__ */ new Map();
|
|
105
|
-
this._suiteContexts = /* @__PURE__ */ new WeakMap();
|
|
106
|
-
TestFixtures._definitions.push(this);
|
|
107
|
-
}
|
|
108
|
-
extend(runner, userFixtures) {
|
|
109
|
-
const { suite } = getCurrentSuite();
|
|
110
|
-
const isTopLevel = !suite || suite.file === suite;
|
|
111
|
-
return new TestFixtures(this.parseUserFixtures(runner, userFixtures, isTopLevel));
|
|
112
|
-
}
|
|
113
|
-
get(suite) {
|
|
114
|
-
let currentSuite = suite;
|
|
115
|
-
while (currentSuite) {
|
|
116
|
-
const overrides = this._overrides.get(currentSuite);
|
|
117
|
-
if (overrides) return overrides;
|
|
118
|
-
if (currentSuite === currentSuite.file) break;
|
|
119
|
-
currentSuite = currentSuite.suite || currentSuite.file;
|
|
120
|
-
}
|
|
121
|
-
return this._registrations;
|
|
122
|
-
}
|
|
123
|
-
override(runner, userFixtures) {
|
|
124
|
-
const { suite: currentSuite, file } = getCurrentSuite();
|
|
125
|
-
const suite = currentSuite || file;
|
|
126
|
-
const isTopLevel = !currentSuite || currentSuite.file === currentSuite;
|
|
127
|
-
const suiteRegistrations = new Map(this.get(suite));
|
|
128
|
-
const registrations = this.parseUserFixtures(runner, userFixtures, isTopLevel, suiteRegistrations);
|
|
129
|
-
if (isTopLevel) this._registrations = registrations;
|
|
130
|
-
else this._overrides.set(suite, registrations);
|
|
131
|
-
}
|
|
132
|
-
getFileContext(file) {
|
|
133
|
-
if (!this._suiteContexts.has(file)) this._suiteContexts.set(file, Object.create(null));
|
|
134
|
-
return this._suiteContexts.get(file);
|
|
135
|
-
}
|
|
136
|
-
getWorkerContext() {
|
|
137
|
-
if (!this._suiteContexts.has(TestFixtures._workerContextSuite)) this._suiteContexts.set(TestFixtures._workerContextSuite, Object.create(null));
|
|
138
|
-
return this._suiteContexts.get(TestFixtures._workerContextSuite);
|
|
139
|
-
}
|
|
140
|
-
parseUserFixtures(runner, userFixtures, supportNonTest, registrations = new Map(this._registrations)) {
|
|
141
|
-
const errors = [];
|
|
142
|
-
Object.entries(userFixtures).forEach(([name, fn]) => {
|
|
143
|
-
let options;
|
|
144
|
-
let value;
|
|
145
|
-
let _options;
|
|
146
|
-
if (Array.isArray(fn) && fn.length >= 2 && TestFixtures.isFixtureOptions(fn[1])) {
|
|
147
|
-
_options = fn[1];
|
|
148
|
-
options = {
|
|
149
|
-
auto: _options.auto ?? false,
|
|
150
|
-
scope: _options.scope ?? "test",
|
|
151
|
-
injected: _options.injected ?? false
|
|
152
|
-
};
|
|
153
|
-
value = options.injected ? runner.injectValue?.(name) ?? fn[0] : fn[0];
|
|
154
|
-
} else value = fn;
|
|
155
|
-
const parent = registrations.get(name);
|
|
156
|
-
if (parent && options) {
|
|
157
|
-
if (parent.scope !== options.scope) errors.push(new FixtureDependencyError(`The "${name}" fixture was already registered with a "${options.scope}" scope.`));
|
|
158
|
-
if (parent.auto !== options.auto) errors.push(new FixtureDependencyError(`The "${name}" fixture was already registered as { auto: ${options.auto} }.`));
|
|
159
|
-
} else if (parent) options = {
|
|
160
|
-
auto: parent.auto,
|
|
161
|
-
scope: parent.scope,
|
|
162
|
-
injected: parent.injected
|
|
163
|
-
};
|
|
164
|
-
else if (!options) options = {
|
|
165
|
-
auto: false,
|
|
166
|
-
injected: false,
|
|
167
|
-
scope: "test"
|
|
168
|
-
};
|
|
169
|
-
if (options.scope && !TestFixtures._fixtureScopes.includes(options.scope)) errors.push(new FixtureDependencyError(`The "${name}" fixture has unknown scope "${options.scope}".`));
|
|
170
|
-
if (!supportNonTest && options.scope !== "test") errors.push(new FixtureDependencyError(`The "${name}" fixture cannot be defined with a ${options.scope} scope${!_options?.scope && parent?.scope ? " (inherited from the base fixture)" : ""} inside the describe block. Define it at the top level of the file instead.`));
|
|
171
|
-
const deps = isFixtureFunction(value) ? getUsedProps(value) : /* @__PURE__ */ new Set();
|
|
172
|
-
const item = {
|
|
173
|
-
name,
|
|
174
|
-
value,
|
|
175
|
-
auto: options.auto ?? false,
|
|
176
|
-
injected: options.injected ?? false,
|
|
177
|
-
scope: options.scope ?? "test",
|
|
178
|
-
deps,
|
|
179
|
-
parent
|
|
180
|
-
};
|
|
181
|
-
if (isFixtureFunction(value)) Object.assign(value, { [FIXTURE_STACK_TRACE_KEY]: /* @__PURE__ */ new Error("STACK_TRACE_ERROR") });
|
|
182
|
-
registrations.set(name, item);
|
|
183
|
-
if (item.scope === "worker" && (runner.pool === "vmThreads" || runner.pool === "vmForks")) item.scope = "file";
|
|
184
|
-
});
|
|
185
|
-
for (const fixture of registrations.values()) for (const depName of fixture.deps) {
|
|
186
|
-
if (TestFixtures._builtinFixtures.includes(depName)) continue;
|
|
187
|
-
const dep = registrations.get(depName);
|
|
188
|
-
if (!dep) {
|
|
189
|
-
errors.push(new FixtureDependencyError(`The "${fixture.name}" fixture depends on unknown fixture "${depName}".`));
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (depName === fixture.name && !fixture.parent) {
|
|
193
|
-
errors.push(new FixtureDependencyError(`The "${fixture.name}" fixture depends on itself, but does not have a base implementation.`));
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
if (TestFixtures._fixtureScopes.indexOf(fixture.scope) > TestFixtures._fixtureScopes.indexOf(dep.scope)) {
|
|
197
|
-
errors.push(new FixtureDependencyError(`The ${fixture.scope} "${fixture.name}" fixture cannot depend on a ${dep.scope} fixture "${dep.name}".`));
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
if (errors.length === 1) throw errors[0];
|
|
202
|
-
else if (errors.length > 1) throw new AggregateError(errors, "Cannot resolve user fixtures. See errors for more information.");
|
|
203
|
-
return registrations;
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
const cleanupFnArrayMap = /* @__PURE__ */ new WeakMap();
|
|
207
|
-
async function callFixtureCleanup(context) {
|
|
208
|
-
const cleanupFnArray = cleanupFnArrayMap.get(context) ?? [];
|
|
209
|
-
for (const cleanup of cleanupFnArray.reverse()) await cleanup();
|
|
210
|
-
cleanupFnArrayMap.delete(context);
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Returns the current number of cleanup functions registered for the context.
|
|
214
|
-
* This can be used as a checkpoint to later clean up only fixtures added after this point.
|
|
215
|
-
*/
|
|
216
|
-
function getFixtureCleanupCount(context) {
|
|
217
|
-
return cleanupFnArrayMap.get(context)?.length ?? 0;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Cleans up only fixtures that were added after the given checkpoint index.
|
|
221
|
-
* This is used by aroundEach to clean up fixtures created inside runTest()
|
|
222
|
-
* while preserving fixtures that were created for aroundEach itself.
|
|
223
|
-
*/
|
|
224
|
-
async function callFixtureCleanupFrom(context, fromIndex) {
|
|
225
|
-
const cleanupFnArray = cleanupFnArrayMap.get(context);
|
|
226
|
-
if (!cleanupFnArray || cleanupFnArray.length <= fromIndex) return;
|
|
227
|
-
const toCleanup = cleanupFnArray.slice(fromIndex);
|
|
228
|
-
for (const cleanup of toCleanup.reverse()) await cleanup();
|
|
229
|
-
cleanupFnArray.length = fromIndex;
|
|
230
|
-
}
|
|
231
|
-
const contextHasFixturesCache = /* @__PURE__ */ new WeakMap();
|
|
232
|
-
function withFixtures(fn, options) {
|
|
233
|
-
const collector = getCurrentSuite();
|
|
234
|
-
const suite = options?.suite || collector.suite || collector.file;
|
|
235
|
-
return async (hookContext) => {
|
|
236
|
-
const context = hookContext || options?.context;
|
|
237
|
-
if (!context) {
|
|
238
|
-
if (options?.suiteHook) validateSuiteHook(fn, options.suiteHook, options.stackTraceError);
|
|
239
|
-
return fn({});
|
|
240
|
-
}
|
|
241
|
-
const fixtures = options?.fixtures || getTestFixtures(context);
|
|
242
|
-
if (!fixtures) return fn(context);
|
|
243
|
-
const registrations = fixtures.get(suite);
|
|
244
|
-
if (!registrations.size) return fn(context);
|
|
245
|
-
const usedFixtures = [];
|
|
246
|
-
const usedProps = getUsedProps(fn);
|
|
247
|
-
for (const fixture of registrations.values()) if (fixture.auto || usedProps.has(fixture.name)) usedFixtures.push(fixture);
|
|
248
|
-
if (!usedFixtures.length) return fn(context);
|
|
249
|
-
if (!cleanupFnArrayMap.has(context)) cleanupFnArrayMap.set(context, []);
|
|
250
|
-
const cleanupFnArray = cleanupFnArrayMap.get(context);
|
|
251
|
-
const pendingFixtures = resolveDeps(usedFixtures, registrations);
|
|
252
|
-
if (!pendingFixtures.length) return fn(context);
|
|
253
|
-
if (options?.suiteHook) {
|
|
254
|
-
const testScopedFixtures = pendingFixtures.filter((f) => f.scope === "test");
|
|
255
|
-
if (testScopedFixtures.length > 0) {
|
|
256
|
-
const fixtureNames = testScopedFixtures.map((f) => `"${f.name}"`).join(", ");
|
|
257
|
-
const error = new FixtureDependencyError(`Test-scoped fixtures cannot be used inside ${options.suiteHook} hook. The following fixtures are test-scoped: ${fixtureNames}. Use { scope: 'file' } or { scope: 'worker' } fixtures instead, or move the logic to ${{
|
|
258
|
-
aroundAll: "aroundEach",
|
|
259
|
-
beforeAll: "beforeEach",
|
|
260
|
-
afterAll: "afterEach"
|
|
261
|
-
}[options.suiteHook]} hook.`);
|
|
262
|
-
if (options.stackTraceError?.stack) error.stack = error.message + options.stackTraceError.stack.replace(options.stackTraceError.message, "");
|
|
263
|
-
throw error;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
if (!contextHasFixturesCache.has(context)) contextHasFixturesCache.set(context, /* @__PURE__ */ new WeakSet());
|
|
267
|
-
const cachedFixtures = contextHasFixturesCache.get(context);
|
|
268
|
-
for (const fixture of pendingFixtures) if (fixture.scope === "test") {
|
|
269
|
-
if (cachedFixtures.has(fixture)) continue;
|
|
270
|
-
cachedFixtures.add(fixture);
|
|
271
|
-
const resolvedValue = await resolveTestFixtureValue(fixture, context, cleanupFnArray);
|
|
272
|
-
context[fixture.name] = resolvedValue;
|
|
273
|
-
cleanupFnArray.push(() => {
|
|
274
|
-
cachedFixtures.delete(fixture);
|
|
275
|
-
});
|
|
276
|
-
} else {
|
|
277
|
-
const resolvedValue = await resolveScopeFixtureValue(fixtures, suite, fixture);
|
|
278
|
-
context[fixture.name] = resolvedValue;
|
|
279
|
-
}
|
|
280
|
-
return fn(context);
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
function isFixtureFunction(value) {
|
|
284
|
-
return typeof value === "function";
|
|
285
|
-
}
|
|
286
|
-
function resolveTestFixtureValue(fixture, context, cleanupFnArray) {
|
|
287
|
-
if (!isFixtureFunction(fixture.value)) return fixture.value;
|
|
288
|
-
return resolveFixtureFunction(fixture.value, fixture.name, context, cleanupFnArray);
|
|
289
|
-
}
|
|
290
|
-
const scopedFixturePromiseCache = /* @__PURE__ */ new WeakMap();
|
|
291
|
-
async function resolveScopeFixtureValue(fixtures, suite, fixture) {
|
|
292
|
-
const workerContext = fixtures.getWorkerContext();
|
|
293
|
-
const fileContext = fixtures.getFileContext(suite.file);
|
|
294
|
-
const fixtureContext = fixture.scope === "worker" ? workerContext : fileContext;
|
|
295
|
-
if (!isFixtureFunction(fixture.value)) {
|
|
296
|
-
fixtureContext[fixture.name] = fixture.value;
|
|
297
|
-
return fixture.value;
|
|
298
|
-
}
|
|
299
|
-
if (fixture.name in fixtureContext) return fixtureContext[fixture.name];
|
|
300
|
-
if (scopedFixturePromiseCache.has(fixture)) return scopedFixturePromiseCache.get(fixture);
|
|
301
|
-
if (!cleanupFnArrayMap.has(fixtureContext)) cleanupFnArrayMap.set(fixtureContext, []);
|
|
302
|
-
const cleanupFnFileArray = cleanupFnArrayMap.get(fixtureContext);
|
|
303
|
-
const promise = resolveFixtureFunction(fixture.value, fixture.name, fixture.scope === "file" ? {
|
|
304
|
-
...workerContext,
|
|
305
|
-
...fileContext
|
|
306
|
-
} : fixtureContext, cleanupFnFileArray).then((value) => {
|
|
307
|
-
fixtureContext[fixture.name] = value;
|
|
308
|
-
scopedFixturePromiseCache.delete(fixture);
|
|
309
|
-
return value;
|
|
310
|
-
});
|
|
311
|
-
scopedFixturePromiseCache.set(fixture, promise);
|
|
312
|
-
return promise;
|
|
313
|
-
}
|
|
314
|
-
async function resolveFixtureFunction(fixtureFn, fixtureName, context, cleanupFnArray) {
|
|
315
|
-
const useFnArgPromise = createDefer();
|
|
316
|
-
const stackTraceError = FIXTURE_STACK_TRACE_KEY in fixtureFn && fixtureFn[FIXTURE_STACK_TRACE_KEY] instanceof Error ? fixtureFn[FIXTURE_STACK_TRACE_KEY] : void 0;
|
|
317
|
-
let isUseFnArgResolved = false;
|
|
318
|
-
const fixtureReturn = fixtureFn(context, async (useFnArg) => {
|
|
319
|
-
isUseFnArgResolved = true;
|
|
320
|
-
useFnArgPromise.resolve(useFnArg);
|
|
321
|
-
const useReturnPromise = createDefer();
|
|
322
|
-
cleanupFnArray.push(async () => {
|
|
323
|
-
useReturnPromise.resolve();
|
|
324
|
-
await fixtureReturn;
|
|
325
|
-
});
|
|
326
|
-
await useReturnPromise;
|
|
327
|
-
}).then(() => {
|
|
328
|
-
if (!isUseFnArgResolved) {
|
|
329
|
-
const error = /* @__PURE__ */ new Error(`Fixture "${fixtureName}" returned without calling "use". Make sure to call "use" in every code path of the fixture function.`);
|
|
330
|
-
if (stackTraceError?.stack) error.stack = error.message + stackTraceError.stack.replace(stackTraceError.message, "");
|
|
331
|
-
useFnArgPromise.reject(error);
|
|
332
|
-
}
|
|
333
|
-
}).catch((e) => {
|
|
334
|
-
if (!isUseFnArgResolved) {
|
|
335
|
-
useFnArgPromise.reject(e);
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
throw e;
|
|
339
|
-
});
|
|
340
|
-
return useFnArgPromise;
|
|
341
|
-
}
|
|
342
|
-
function resolveDeps(usedFixtures, registrations, depSet = /* @__PURE__ */ new Set(), pendingFixtures = []) {
|
|
343
|
-
usedFixtures.forEach((fixture) => {
|
|
344
|
-
if (pendingFixtures.includes(fixture)) return;
|
|
345
|
-
if (!isFixtureFunction(fixture.value) || !fixture.deps) {
|
|
346
|
-
pendingFixtures.push(fixture);
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
if (depSet.has(fixture)) if (fixture.parent) fixture = fixture.parent;
|
|
350
|
-
else throw new Error(`Circular fixture dependency detected: ${fixture.name} <- ${[...depSet].reverse().map((d) => d.name).join(" <- ")}`);
|
|
351
|
-
depSet.add(fixture);
|
|
352
|
-
resolveDeps([...fixture.deps].map((n) => n === fixture.name ? fixture.parent : registrations.get(n)).filter((n) => !!n), registrations, depSet, pendingFixtures);
|
|
353
|
-
pendingFixtures.push(fixture);
|
|
354
|
-
depSet.clear();
|
|
355
|
-
});
|
|
356
|
-
return pendingFixtures;
|
|
357
|
-
}
|
|
358
|
-
function validateSuiteHook(fn, hook, suiteError) {
|
|
359
|
-
const usedProps = getUsedProps(fn, {
|
|
360
|
-
sourceError: suiteError,
|
|
361
|
-
suiteHook: hook
|
|
362
|
-
});
|
|
363
|
-
if (usedProps.size) {
|
|
364
|
-
const error = new FixtureAccessError(`The ${hook} hook uses fixtures "${[...usedProps].join("\", \"")}", but has no access to context. Did you forget to call it as "test.${hook}()" instead of "${hook}()"?\nIf you used internal "suite" task as the first argument previously, access it in the second argument instead. See https://vitest.dev/guide/test-context#suite-level-hooks`);
|
|
365
|
-
if (suiteError) error.stack = suiteError.stack?.replace(suiteError.message, error.message);
|
|
366
|
-
throw error;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
const kPropsSymbol = Symbol("$vitest:fixture-props");
|
|
370
|
-
const kPropNamesSymbol = Symbol("$vitest:fixture-prop-names");
|
|
371
|
-
function configureProps(fn, options) {
|
|
372
|
-
Object.defineProperty(fn, kPropsSymbol, {
|
|
373
|
-
value: options,
|
|
374
|
-
enumerable: false
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
function memoProps(fn, props) {
|
|
378
|
-
fn[kPropNamesSymbol] = props;
|
|
379
|
-
return props;
|
|
380
|
-
}
|
|
381
|
-
function getUsedProps(fn, { sourceError, suiteHook } = {}) {
|
|
382
|
-
if (kPropNamesSymbol in fn) return fn[kPropNamesSymbol];
|
|
383
|
-
const { index: fixturesIndex = 0, original: implementation = fn } = kPropsSymbol in fn ? fn[kPropsSymbol] : {};
|
|
384
|
-
let fnString = filterOutComments(implementation.toString());
|
|
385
|
-
if (/__async\((?:this|null), (?:null|arguments|\[[_0-9, ]*\]), function\*/.test(fnString)) fnString = fnString.split(/__async\((?:this|null),/)[1];
|
|
386
|
-
const match = fnString.match(/[^(]*\(([^)]*)/);
|
|
387
|
-
if (!match) return memoProps(fn, /* @__PURE__ */ new Set());
|
|
388
|
-
const args = splitByComma(match[1]);
|
|
389
|
-
if (!args.length) return memoProps(fn, /* @__PURE__ */ new Set());
|
|
390
|
-
const fixturesArgument = args[fixturesIndex];
|
|
391
|
-
if (!fixturesArgument) return memoProps(fn, /* @__PURE__ */ new Set());
|
|
392
|
-
if (!(fixturesArgument[0] === "{" && fixturesArgument.endsWith("}"))) {
|
|
393
|
-
const ordinalArgument = ordinal(fixturesIndex + 1);
|
|
394
|
-
const error = new FixtureParseError(`The ${ordinalArgument} argument inside a fixture must use object destructuring pattern, e.g. ({ task } => {}). Instead, received "${fixturesArgument}".${suiteHook ? ` If you used internal "suite" task as the ${ordinalArgument} argument previously, access it in the ${ordinal(fixturesIndex + 2)} argument instead.` : ""}`);
|
|
395
|
-
if (sourceError) error.stack = sourceError.stack?.replace(sourceError.message, error.message);
|
|
396
|
-
throw error;
|
|
397
|
-
}
|
|
398
|
-
const props = splitByComma(fixturesArgument.slice(1, -1).replace(/\s/g, "")).map((prop) => {
|
|
399
|
-
return prop.replace(/:.*|=.*/g, "");
|
|
400
|
-
});
|
|
401
|
-
const last = props.at(-1);
|
|
402
|
-
if (last && last.startsWith("...")) {
|
|
403
|
-
const error = new FixtureParseError(`Rest parameters are not supported in fixtures, received "${last}".`);
|
|
404
|
-
if (sourceError) error.stack = sourceError.stack?.replace(sourceError.message, error.message);
|
|
405
|
-
throw error;
|
|
406
|
-
}
|
|
407
|
-
return memoProps(fn, new Set(props));
|
|
408
|
-
}
|
|
409
|
-
function splitByComma(s) {
|
|
410
|
-
const result = [];
|
|
411
|
-
const stack = [];
|
|
412
|
-
let start = 0;
|
|
413
|
-
for (let i = 0; i < s.length; i++) if (s[i] === "{" || s[i] === "[") stack.push(s[i] === "{" ? "}" : "]");
|
|
414
|
-
else if (s[i] === stack.at(-1)) stack.pop();
|
|
415
|
-
else if (!stack.length && s[i] === ",") {
|
|
416
|
-
const token = s.substring(start, i).trim();
|
|
417
|
-
if (token) result.push(token);
|
|
418
|
-
start = i + 1;
|
|
419
|
-
}
|
|
420
|
-
const lastToken = s.substring(start).trim();
|
|
421
|
-
if (lastToken) result.push(lastToken);
|
|
422
|
-
return result;
|
|
423
|
-
}
|
|
424
|
-
let _test;
|
|
425
|
-
function setCurrentTest(test) {
|
|
426
|
-
_test = test;
|
|
427
|
-
}
|
|
428
|
-
function getCurrentTest() {
|
|
429
|
-
return _test;
|
|
430
|
-
}
|
|
431
|
-
const tests = [];
|
|
432
|
-
function addRunningTest(test) {
|
|
433
|
-
tests.push(test);
|
|
434
|
-
return () => {
|
|
435
|
-
tests.splice(tests.indexOf(test));
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
function getRunningTests() {
|
|
439
|
-
return tests;
|
|
440
|
-
}
|
|
441
|
-
const kChainableContext = Symbol("kChainableContext");
|
|
442
|
-
function getChainableContext(chainable) {
|
|
443
|
-
return chainable?.[kChainableContext];
|
|
444
|
-
}
|
|
445
|
-
function createChainable(keys, fn, context) {
|
|
446
|
-
function create(context) {
|
|
447
|
-
const chain = function(...args) {
|
|
448
|
-
return fn.apply(context, args);
|
|
449
|
-
};
|
|
450
|
-
Object.assign(chain, fn);
|
|
451
|
-
Object.defineProperty(chain, kChainableContext, {
|
|
452
|
-
value: {
|
|
453
|
-
withContext: () => chain.bind(context),
|
|
454
|
-
getFixtures: () => context.fixtures,
|
|
455
|
-
setContext: (key, value) => {
|
|
456
|
-
context[key] = value;
|
|
457
|
-
},
|
|
458
|
-
mergeContext: (ctx) => {
|
|
459
|
-
Object.assign(context, ctx);
|
|
460
|
-
}
|
|
461
|
-
},
|
|
462
|
-
enumerable: false
|
|
463
|
-
});
|
|
464
|
-
for (const key of keys) Object.defineProperty(chain, key, { get() {
|
|
465
|
-
return create({
|
|
466
|
-
...context,
|
|
467
|
-
[key]: true
|
|
468
|
-
});
|
|
469
|
-
} });
|
|
470
|
-
return chain;
|
|
471
|
-
}
|
|
472
|
-
const chain = create(context ?? {});
|
|
473
|
-
Object.defineProperty(chain, "fn", {
|
|
474
|
-
value: fn,
|
|
475
|
-
enumerable: false
|
|
476
|
-
});
|
|
477
|
-
return chain;
|
|
478
|
-
}
|
|
479
|
-
function getDefaultHookTimeout() {
|
|
480
|
-
return getRunner().config.hookTimeout;
|
|
481
|
-
}
|
|
482
|
-
const CLEANUP_TIMEOUT_KEY = Symbol.for("VITEST_CLEANUP_TIMEOUT");
|
|
483
|
-
const CLEANUP_STACK_TRACE_KEY = Symbol.for("VITEST_CLEANUP_STACK_TRACE");
|
|
484
|
-
const AROUND_TIMEOUT_KEY = Symbol.for("VITEST_AROUND_TIMEOUT");
|
|
485
|
-
const AROUND_STACK_TRACE_KEY = Symbol.for("VITEST_AROUND_STACK_TRACE");
|
|
486
|
-
function getBeforeHookCleanupCallback(hook, result, context) {
|
|
487
|
-
if (typeof result === "function") return withTimeout(result, CLEANUP_TIMEOUT_KEY in hook && typeof hook[CLEANUP_TIMEOUT_KEY] === "number" ? hook[CLEANUP_TIMEOUT_KEY] : getDefaultHookTimeout(), true, CLEANUP_STACK_TRACE_KEY in hook && hook[CLEANUP_STACK_TRACE_KEY] instanceof Error ? hook[CLEANUP_STACK_TRACE_KEY] : void 0, (_, error) => {
|
|
488
|
-
if (context) abortContextSignal(context, error);
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
/**
|
|
492
|
-
* Registers a callback function to be executed once before all tests within the current suite.
|
|
493
|
-
* This hook is useful for scenarios where you need to perform setup operations that are common to all tests in a suite, such as initializing a database connection or setting up a test environment.
|
|
494
|
-
*
|
|
495
|
-
* **Note:** The `beforeAll` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
496
|
-
*
|
|
497
|
-
* @param {Function} fn - The callback function to be executed before all tests.
|
|
498
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
499
|
-
* @returns {void}
|
|
500
|
-
* @example
|
|
501
|
-
* ```ts
|
|
502
|
-
* // Example of using beforeAll to set up a database connection
|
|
503
|
-
* beforeAll(async () => {
|
|
504
|
-
* await database.connect();
|
|
505
|
-
* });
|
|
506
|
-
* ```
|
|
507
|
-
*/
|
|
508
|
-
function beforeAll(fn, timeout = getDefaultHookTimeout()) {
|
|
509
|
-
assertTypes(fn, "\"beforeAll\" callback", ["function"]);
|
|
510
|
-
const stackTraceError = /* @__PURE__ */ new Error("STACK_TRACE_ERROR");
|
|
511
|
-
const context = getChainableContext(this);
|
|
512
|
-
return getCurrentSuite().on("beforeAll", Object.assign(withTimeout(withSuiteFixtures("beforeAll", fn, context, stackTraceError), timeout, true, stackTraceError), {
|
|
513
|
-
[CLEANUP_TIMEOUT_KEY]: timeout,
|
|
514
|
-
[CLEANUP_STACK_TRACE_KEY]: stackTraceError
|
|
515
|
-
}));
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Registers a callback function to be executed once after all tests within the current suite have completed.
|
|
519
|
-
* This hook is useful for scenarios where you need to perform cleanup operations after all tests in a suite have run, such as closing database connections or cleaning up temporary files.
|
|
520
|
-
*
|
|
521
|
-
* **Note:** The `afterAll` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
522
|
-
*
|
|
523
|
-
* @param {Function} fn - The callback function to be executed after all tests.
|
|
524
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
525
|
-
* @returns {void}
|
|
526
|
-
* @example
|
|
527
|
-
* ```ts
|
|
528
|
-
* // Example of using afterAll to close a database connection
|
|
529
|
-
* afterAll(async () => {
|
|
530
|
-
* await database.disconnect();
|
|
531
|
-
* });
|
|
532
|
-
* ```
|
|
533
|
-
*/
|
|
534
|
-
function afterAll(fn, timeout) {
|
|
535
|
-
assertTypes(fn, "\"afterAll\" callback", ["function"]);
|
|
536
|
-
const context = getChainableContext(this);
|
|
537
|
-
const stackTraceError = /* @__PURE__ */ new Error("STACK_TRACE_ERROR");
|
|
538
|
-
return getCurrentSuite().on("afterAll", withTimeout(withSuiteFixtures("afterAll", fn, context, stackTraceError), timeout ?? getDefaultHookTimeout(), true, stackTraceError));
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* Registers a callback function to be executed before each test within the current suite.
|
|
542
|
-
* This hook is useful for scenarios where you need to reset or reinitialize the test environment before each test runs, such as resetting database states, clearing caches, or reinitializing variables.
|
|
543
|
-
*
|
|
544
|
-
* **Note:** The `beforeEach` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
545
|
-
*
|
|
546
|
-
* @param {Function} fn - The callback function to be executed before each test. This function receives an `TestContext` parameter if additional test context is needed.
|
|
547
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
548
|
-
* @returns {void}
|
|
549
|
-
* @example
|
|
550
|
-
* ```ts
|
|
551
|
-
* // Example of using beforeEach to reset a database state
|
|
552
|
-
* beforeEach(async () => {
|
|
553
|
-
* await database.reset();
|
|
554
|
-
* });
|
|
555
|
-
* ```
|
|
556
|
-
*/
|
|
557
|
-
function beforeEach(fn, timeout = getDefaultHookTimeout()) {
|
|
558
|
-
assertTypes(fn, "\"beforeEach\" callback", ["function"]);
|
|
559
|
-
const stackTraceError = /* @__PURE__ */ new Error("STACK_TRACE_ERROR");
|
|
560
|
-
const wrapper = (context, suite) => {
|
|
561
|
-
return withFixtures(fn, { suite })(context);
|
|
562
|
-
};
|
|
563
|
-
return getCurrentSuite().on("beforeEach", Object.assign(withTimeout(wrapper, timeout ?? getDefaultHookTimeout(), true, stackTraceError, abortIfTimeout), {
|
|
564
|
-
[CLEANUP_TIMEOUT_KEY]: timeout,
|
|
565
|
-
[CLEANUP_STACK_TRACE_KEY]: stackTraceError
|
|
566
|
-
}));
|
|
567
|
-
}
|
|
568
|
-
/**
|
|
569
|
-
* Registers a callback function to be executed after each test within the current suite has completed.
|
|
570
|
-
* This hook is useful for scenarios where you need to clean up or reset the test environment after each test runs, such as deleting temporary files, clearing test-specific database entries, or resetting mocked functions.
|
|
571
|
-
*
|
|
572
|
-
* **Note:** The `afterEach` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
573
|
-
*
|
|
574
|
-
* @param {Function} fn - The callback function to be executed after each test. This function receives an `TestContext` parameter if additional test context is needed.
|
|
575
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
576
|
-
* @returns {void}
|
|
577
|
-
* @example
|
|
578
|
-
* ```ts
|
|
579
|
-
* // Example of using afterEach to delete temporary files created during a test
|
|
580
|
-
* afterEach(async () => {
|
|
581
|
-
* await fileSystem.deleteTempFiles();
|
|
582
|
-
* });
|
|
583
|
-
* ```
|
|
584
|
-
*/
|
|
585
|
-
function afterEach(fn, timeout) {
|
|
586
|
-
assertTypes(fn, "\"afterEach\" callback", ["function"]);
|
|
587
|
-
const wrapper = (context, suite) => {
|
|
588
|
-
return withFixtures(fn, { suite })(context);
|
|
589
|
-
};
|
|
590
|
-
return getCurrentSuite().on("afterEach", withTimeout(wrapper, timeout ?? getDefaultHookTimeout(), true, /* @__PURE__ */ new Error("STACK_TRACE_ERROR"), abortIfTimeout));
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Registers a callback function to be executed when a test fails within the current suite.
|
|
594
|
-
* This function allows for custom actions to be performed in response to test failures, such as logging, cleanup, or additional diagnostics.
|
|
595
|
-
*
|
|
596
|
-
* **Note:** The `onTestFailed` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
597
|
-
*
|
|
598
|
-
* @param {Function} fn - The callback function to be executed upon a test failure. The function receives the test result (including errors).
|
|
599
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
600
|
-
* @throws {Error} Throws an error if the function is not called within a test.
|
|
601
|
-
* @returns {void}
|
|
602
|
-
* @example
|
|
603
|
-
* ```ts
|
|
604
|
-
* // Example of using onTestFailed to log failure details
|
|
605
|
-
* onTestFailed(({ errors }) => {
|
|
606
|
-
* console.log(`Test failed: ${test.name}`, errors);
|
|
607
|
-
* });
|
|
608
|
-
* ```
|
|
609
|
-
*/
|
|
610
|
-
const onTestFailed = createTestHook("onTestFailed", (test, handler, timeout) => {
|
|
611
|
-
test.onFailed ||= [];
|
|
612
|
-
test.onFailed.push(withTimeout(handler, timeout ?? getDefaultHookTimeout(), true, /* @__PURE__ */ new Error("STACK_TRACE_ERROR"), abortIfTimeout));
|
|
613
|
-
});
|
|
614
|
-
/**
|
|
615
|
-
* Registers a callback function to be executed when the current test finishes, regardless of the outcome (pass or fail).
|
|
616
|
-
* This function is ideal for performing actions that should occur after every test execution, such as cleanup, logging, or resetting shared resources.
|
|
617
|
-
*
|
|
618
|
-
* This hook is useful if you have access to a resource in the test itself and you want to clean it up after the test finishes. It is a more compact way to clean up resources than using the combination of `beforeEach` and `afterEach`.
|
|
619
|
-
*
|
|
620
|
-
* **Note:** The `onTestFinished` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
621
|
-
*
|
|
622
|
-
* **Note:** The `onTestFinished` hook is not called if the test is canceled with a dynamic `ctx.skip()` call.
|
|
623
|
-
*
|
|
624
|
-
* @param {Function} fn - The callback function to be executed after a test finishes. The function can receive parameters providing details about the completed test, including its success or failure status.
|
|
625
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
626
|
-
* @throws {Error} Throws an error if the function is not called within a test.
|
|
627
|
-
* @returns {void}
|
|
628
|
-
* @example
|
|
629
|
-
* ```ts
|
|
630
|
-
* // Example of using onTestFinished for cleanup
|
|
631
|
-
* const db = await connectToDatabase();
|
|
632
|
-
* onTestFinished(async () => {
|
|
633
|
-
* await db.disconnect();
|
|
634
|
-
* });
|
|
635
|
-
* ```
|
|
636
|
-
*/
|
|
637
|
-
const onTestFinished = createTestHook("onTestFinished", (test, handler, timeout) => {
|
|
638
|
-
test.onFinished ||= [];
|
|
639
|
-
test.onFinished.push(withTimeout(handler, timeout ?? getDefaultHookTimeout(), true, /* @__PURE__ */ new Error("STACK_TRACE_ERROR"), abortIfTimeout));
|
|
640
|
-
});
|
|
641
|
-
/**
|
|
642
|
-
* Registers a callback function that wraps around all tests within the current suite.
|
|
643
|
-
* The callback receives a `runSuite` function that must be called to run the suite's tests.
|
|
644
|
-
* This hook is useful for scenarios where you need to wrap an entire suite in a context
|
|
645
|
-
* (e.g., starting a server, opening a database connection that all tests share).
|
|
646
|
-
*
|
|
647
|
-
* **Note:** When multiple `aroundAll` hooks are registered, they are nested inside each other.
|
|
648
|
-
* The first registered hook is the outermost wrapper.
|
|
649
|
-
*
|
|
650
|
-
* @param {Function} fn - The callback function that wraps the suite. Must call `runSuite()` to run the tests.
|
|
651
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
652
|
-
* @returns {void}
|
|
653
|
-
* @example
|
|
654
|
-
* ```ts
|
|
655
|
-
* // Example of using aroundAll to wrap suite in a tracing span
|
|
656
|
-
* aroundAll(async (runSuite) => {
|
|
657
|
-
* await tracer.trace('test-suite', runSuite);
|
|
658
|
-
* });
|
|
659
|
-
* ```
|
|
660
|
-
* @example
|
|
661
|
-
* ```ts
|
|
662
|
-
* // Example of using aroundAll with fixtures
|
|
663
|
-
* aroundAll(async (runSuite, { db }) => {
|
|
664
|
-
* await db.transaction(() => runSuite());
|
|
665
|
-
* });
|
|
666
|
-
* ```
|
|
667
|
-
*/
|
|
668
|
-
function aroundAll(fn, timeout) {
|
|
669
|
-
assertTypes(fn, "\"aroundAll\" callback", ["function"]);
|
|
670
|
-
const stackTraceError = /* @__PURE__ */ new Error("STACK_TRACE_ERROR");
|
|
671
|
-
const resolvedTimeout = timeout ?? getDefaultHookTimeout();
|
|
672
|
-
const context = getChainableContext(this);
|
|
673
|
-
return getCurrentSuite().on("aroundAll", Object.assign(withSuiteFixtures("aroundAll", fn, context, stackTraceError, 1), {
|
|
674
|
-
[AROUND_TIMEOUT_KEY]: resolvedTimeout,
|
|
675
|
-
[AROUND_STACK_TRACE_KEY]: stackTraceError
|
|
676
|
-
}));
|
|
677
|
-
}
|
|
678
|
-
/**
|
|
679
|
-
* Registers a callback function that wraps around each test within the current suite.
|
|
680
|
-
* The callback receives a `runTest` function that must be called to run the test.
|
|
681
|
-
* This hook is useful for scenarios where you need to wrap tests in a context (e.g., database transactions).
|
|
682
|
-
*
|
|
683
|
-
* **Note:** When multiple `aroundEach` hooks are registered, they are nested inside each other.
|
|
684
|
-
* The first registered hook is the outermost wrapper.
|
|
685
|
-
*
|
|
686
|
-
* @param {Function} fn - The callback function that wraps the test. Must call `runTest()` to run the test.
|
|
687
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
688
|
-
* @returns {void}
|
|
689
|
-
* @example
|
|
690
|
-
* ```ts
|
|
691
|
-
* // Example of using aroundEach to wrap tests in a database transaction
|
|
692
|
-
* aroundEach(async (runTest) => {
|
|
693
|
-
* await database.transaction(() => runTest());
|
|
694
|
-
* });
|
|
695
|
-
* ```
|
|
696
|
-
* @example
|
|
697
|
-
* ```ts
|
|
698
|
-
* // Example of using aroundEach with fixtures
|
|
699
|
-
* aroundEach(async (runTest, { db }) => {
|
|
700
|
-
* await db.transaction(() => runTest());
|
|
701
|
-
* });
|
|
702
|
-
* ```
|
|
703
|
-
*/
|
|
704
|
-
function aroundEach(fn, timeout) {
|
|
705
|
-
assertTypes(fn, "\"aroundEach\" callback", ["function"]);
|
|
706
|
-
const stackTraceError = /* @__PURE__ */ new Error("STACK_TRACE_ERROR");
|
|
707
|
-
const resolvedTimeout = timeout ?? getDefaultHookTimeout();
|
|
708
|
-
const wrapper = (runTest, context, suite) => {
|
|
709
|
-
const innerFn = (ctx) => fn(runTest, ctx, suite);
|
|
710
|
-
configureProps(innerFn, {
|
|
711
|
-
index: 1,
|
|
712
|
-
original: fn
|
|
713
|
-
});
|
|
714
|
-
return withFixtures(innerFn, { suite })(context);
|
|
715
|
-
};
|
|
716
|
-
return getCurrentSuite().on("aroundEach", Object.assign(wrapper, {
|
|
717
|
-
[AROUND_TIMEOUT_KEY]: resolvedTimeout,
|
|
718
|
-
[AROUND_STACK_TRACE_KEY]: stackTraceError
|
|
719
|
-
}));
|
|
720
|
-
}
|
|
721
|
-
function withSuiteFixtures(suiteHook, fn, context, stackTraceError, contextIndex = 0) {
|
|
722
|
-
return (...args) => {
|
|
723
|
-
const suite = args.at(-1);
|
|
724
|
-
const prefix = args.slice(0, -1);
|
|
725
|
-
const wrapper = (ctx) => fn(...prefix, ctx, suite);
|
|
726
|
-
configureProps(wrapper, {
|
|
727
|
-
index: contextIndex,
|
|
728
|
-
original: fn
|
|
729
|
-
});
|
|
730
|
-
const fixtures = context?.getFixtures();
|
|
731
|
-
const fileContext = fixtures?.getFileContext(suite.file);
|
|
732
|
-
return withFixtures(wrapper, {
|
|
733
|
-
suiteHook,
|
|
734
|
-
fixtures,
|
|
735
|
-
context: fileContext,
|
|
736
|
-
stackTraceError
|
|
737
|
-
})();
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
function getAroundHookTimeout(hook) {
|
|
741
|
-
return AROUND_TIMEOUT_KEY in hook && typeof hook[AROUND_TIMEOUT_KEY] === "number" ? hook[AROUND_TIMEOUT_KEY] : getDefaultHookTimeout();
|
|
742
|
-
}
|
|
743
|
-
function getAroundHookStackTrace(hook) {
|
|
744
|
-
return AROUND_STACK_TRACE_KEY in hook && hook[AROUND_STACK_TRACE_KEY] instanceof Error ? hook[AROUND_STACK_TRACE_KEY] : void 0;
|
|
745
|
-
}
|
|
746
|
-
function createTestHook(name, handler) {
|
|
747
|
-
return (fn, timeout) => {
|
|
748
|
-
assertTypes(fn, `"${name}" callback`, ["function"]);
|
|
749
|
-
const current = getCurrentTest();
|
|
750
|
-
if (!current) throw new Error(`Hook ${name}() can only be called inside a test`);
|
|
751
|
-
return handler(current, fn, timeout);
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
/**
|
|
755
|
-
* If any tasks been marked as `only`, mark all other tasks as `skip`.
|
|
756
|
-
*/
|
|
757
|
-
function interpretTaskModes(file, namePattern, testLocations, testIds, testTagsFilter, onlyMode, parentIsOnly, allowOnly) {
|
|
758
|
-
const matchedLocations = [];
|
|
759
|
-
const traverseSuite = (suite, parentIsOnly, parentMatchedWithLocation) => {
|
|
760
|
-
const suiteIsOnly = parentIsOnly || suite.mode === "only";
|
|
761
|
-
const hasSomeTasksOnly = onlyMode && suite.tasks.some((t) => t.mode === "only" || t.type === "suite" && someTasksAreOnly(t));
|
|
762
|
-
suite.tasks.forEach((t) => {
|
|
763
|
-
const includeTask = hasSomeTasksOnly ? t.mode === "only" || t.type === "suite" && someTasksAreOnly(t) : suiteIsOnly || t.mode === "only";
|
|
764
|
-
if (onlyMode) {
|
|
765
|
-
if (t.type === "suite" && (includeTask || someTasksAreOnly(t))) {
|
|
766
|
-
if (t.mode === "only") {
|
|
767
|
-
checkAllowOnly(t, allowOnly);
|
|
768
|
-
t.mode = "run";
|
|
769
|
-
}
|
|
770
|
-
} else if (t.mode === "run" && !includeTask) t.mode = "skip";
|
|
771
|
-
else if (t.mode === "only") {
|
|
772
|
-
checkAllowOnly(t, allowOnly);
|
|
773
|
-
t.mode = "run";
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
let hasLocationMatch = parentMatchedWithLocation;
|
|
777
|
-
if (testLocations !== void 0 && testLocations.length !== 0) {
|
|
778
|
-
if (t.location && testLocations?.includes(t.location.line)) {
|
|
779
|
-
t.mode = "run";
|
|
780
|
-
matchedLocations.push(t.location.line);
|
|
781
|
-
hasLocationMatch = true;
|
|
782
|
-
} else if (parentMatchedWithLocation) t.mode = "run";
|
|
783
|
-
else if (t.type === "test") t.mode = "skip";
|
|
784
|
-
}
|
|
785
|
-
if (t.type === "test") {
|
|
786
|
-
if (namePattern && !getTaskFullName(t).match(namePattern)) t.mode = "skip";
|
|
787
|
-
if (testIds && !testIds.includes(t.id)) t.mode = "skip";
|
|
788
|
-
if (testTagsFilter && !testTagsFilter(t.tags || [])) t.mode = "skip";
|
|
789
|
-
} else if (t.type === "suite") if (t.mode === "skip") skipAllTasks(t);
|
|
790
|
-
else if (t.mode === "todo") todoAllTasks(t);
|
|
791
|
-
else traverseSuite(t, includeTask, hasLocationMatch);
|
|
792
|
-
});
|
|
793
|
-
if (suite.mode === "run" || suite.mode === "queued") {
|
|
794
|
-
if (suite.tasks.length && suite.tasks.every((i) => i.mode !== "run" && i.mode !== "queued")) suite.mode = "skip";
|
|
795
|
-
}
|
|
796
|
-
};
|
|
797
|
-
traverseSuite(file, parentIsOnly, false);
|
|
798
|
-
const nonMatching = testLocations?.filter((loc) => !matchedLocations.includes(loc));
|
|
799
|
-
if (nonMatching && nonMatching.length !== 0) {
|
|
800
|
-
const message = nonMatching.length === 1 ? `line ${nonMatching[0]}` : `lines ${nonMatching.join(", ")}`;
|
|
801
|
-
if (file.result === void 0) file.result = {
|
|
802
|
-
state: "fail",
|
|
803
|
-
errors: []
|
|
804
|
-
};
|
|
805
|
-
if (file.result.errors === void 0) file.result.errors = [];
|
|
806
|
-
file.result.errors.push(processError(/* @__PURE__ */ new Error(`No test found in ${file.name} in ${message}`)));
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
function getTaskFullName(task) {
|
|
810
|
-
return `${task.suite ? `${getTaskFullName(task.suite)} ` : ""}${task.name}`;
|
|
811
|
-
}
|
|
812
|
-
function someTasksAreOnly(suite) {
|
|
813
|
-
return suite.tasks.some((t) => t.mode === "only" || t.type === "suite" && someTasksAreOnly(t));
|
|
814
|
-
}
|
|
815
|
-
function skipAllTasks(suite) {
|
|
816
|
-
suite.tasks.forEach((t) => {
|
|
817
|
-
if (t.mode === "run" || t.mode === "queued") {
|
|
818
|
-
t.mode = "skip";
|
|
819
|
-
if (t.type === "suite") skipAllTasks(t);
|
|
820
|
-
}
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
function todoAllTasks(suite) {
|
|
824
|
-
suite.tasks.forEach((t) => {
|
|
825
|
-
if (t.mode === "run" || t.mode === "queued") {
|
|
826
|
-
t.mode = "todo";
|
|
827
|
-
if (t.type === "suite") todoAllTasks(t);
|
|
828
|
-
}
|
|
829
|
-
});
|
|
830
|
-
}
|
|
831
|
-
function checkAllowOnly(task, allowOnly) {
|
|
832
|
-
if (allowOnly) return;
|
|
833
|
-
task.result = {
|
|
834
|
-
state: "fail",
|
|
835
|
-
errors: [processError(/* @__PURE__ */ new Error("[Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error"))]
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
/* @__NO_SIDE_EFFECTS__ */
|
|
839
|
-
function generateHash(str) {
|
|
840
|
-
let hash = 0;
|
|
841
|
-
if (str.length === 0) return `${hash}`;
|
|
842
|
-
for (let i = 0; i < str.length; i++) {
|
|
843
|
-
const char = str.charCodeAt(i);
|
|
844
|
-
hash = (hash << 5) - hash + char;
|
|
845
|
-
hash = hash & hash;
|
|
846
|
-
}
|
|
847
|
-
return `${hash}`;
|
|
848
|
-
}
|
|
849
|
-
function calculateSuiteHash(parent) {
|
|
850
|
-
parent.tasks.forEach((t, idx) => {
|
|
851
|
-
t.id = `${parent.id}_${idx}`;
|
|
852
|
-
if (t.type === "suite") calculateSuiteHash(t);
|
|
853
|
-
});
|
|
854
|
-
}
|
|
855
|
-
function createFileTask(filepath, root, projectName, pool, viteEnvironment) {
|
|
856
|
-
const path = relative(root, filepath);
|
|
857
|
-
const file = {
|
|
858
|
-
id: /* @__PURE__ */ generateFileHash(path, projectName),
|
|
859
|
-
name: path,
|
|
860
|
-
fullName: path,
|
|
861
|
-
type: "suite",
|
|
862
|
-
mode: "queued",
|
|
863
|
-
filepath,
|
|
864
|
-
tasks: [],
|
|
865
|
-
meta: Object.create(null),
|
|
866
|
-
projectName,
|
|
867
|
-
file: void 0,
|
|
868
|
-
pool,
|
|
869
|
-
viteEnvironment
|
|
870
|
-
};
|
|
871
|
-
file.file = file;
|
|
872
|
-
return file;
|
|
873
|
-
}
|
|
874
|
-
/**
|
|
875
|
-
* Generate a unique ID for a file based on its path and project name
|
|
876
|
-
* @param file File relative to the root of the project to keep ID the same between different machines
|
|
877
|
-
* @param projectName The name of the test project
|
|
878
|
-
*/
|
|
879
|
-
/* @__NO_SIDE_EFFECTS__ */
|
|
880
|
-
function generateFileHash(file, projectName) {
|
|
881
|
-
return /* @__PURE__ */ generateHash(`${file}${projectName || ""}`);
|
|
882
|
-
}
|
|
883
|
-
function findTestFileStackTrace(testFilePath, error) {
|
|
884
|
-
const lines = error.split("\n").slice(1);
|
|
885
|
-
for (const line of lines) {
|
|
886
|
-
const stack = parseSingleStack(line);
|
|
887
|
-
if (stack && stack.file === testFilePath) return stack;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
/**
|
|
891
|
-
* Return a function for running multiple async operations with limited concurrency.
|
|
892
|
-
*/
|
|
893
|
-
function limitConcurrency(concurrency = Infinity) {
|
|
894
|
-
let count = 0;
|
|
895
|
-
let head;
|
|
896
|
-
let tail;
|
|
897
|
-
const finish = () => {
|
|
898
|
-
count--;
|
|
899
|
-
if (head) {
|
|
900
|
-
head[0]();
|
|
901
|
-
head = head[1];
|
|
902
|
-
tail = head && tail;
|
|
903
|
-
}
|
|
904
|
-
};
|
|
905
|
-
const acquire = () => {
|
|
906
|
-
let released = false;
|
|
907
|
-
const release = () => {
|
|
908
|
-
if (!released) {
|
|
909
|
-
released = true;
|
|
910
|
-
finish();
|
|
911
|
-
}
|
|
912
|
-
};
|
|
913
|
-
if (count++ < concurrency) return release;
|
|
914
|
-
return new Promise((resolve) => {
|
|
915
|
-
if (tail) tail = tail[1] = [() => resolve(release)];
|
|
916
|
-
else head = tail = [() => resolve(release)];
|
|
917
|
-
});
|
|
918
|
-
};
|
|
919
|
-
const limiterFn = (func, ...args) => {
|
|
920
|
-
function run(release) {
|
|
921
|
-
try {
|
|
922
|
-
const result = func(...args);
|
|
923
|
-
if (result instanceof Promise) return result.finally(release);
|
|
924
|
-
release();
|
|
925
|
-
return Promise.resolve(result);
|
|
926
|
-
} catch (error) {
|
|
927
|
-
release();
|
|
928
|
-
return Promise.reject(error);
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
const release = acquire();
|
|
932
|
-
return release instanceof Promise ? release.then(run) : run(release);
|
|
933
|
-
};
|
|
934
|
-
return Object.assign(limiterFn, { acquire });
|
|
935
|
-
}
|
|
936
|
-
/**
|
|
937
|
-
* Partition in tasks groups by consecutive concurrent
|
|
938
|
-
*/
|
|
939
|
-
function partitionSuiteChildren(suite) {
|
|
940
|
-
let tasksGroup = [];
|
|
941
|
-
const tasksGroups = [];
|
|
942
|
-
for (const c of suite.tasks) if (tasksGroup.length === 0 || c.concurrent === tasksGroup[0].concurrent) tasksGroup.push(c);
|
|
943
|
-
else {
|
|
944
|
-
tasksGroups.push(tasksGroup);
|
|
945
|
-
tasksGroup = [c];
|
|
946
|
-
}
|
|
947
|
-
if (tasksGroup.length > 0) tasksGroups.push(tasksGroup);
|
|
948
|
-
return tasksGroups;
|
|
949
|
-
}
|
|
950
|
-
const filterMap = /* @__PURE__ */ new WeakMap();
|
|
951
|
-
/**
|
|
952
|
-
* @experimental
|
|
953
|
-
*/
|
|
954
|
-
function matchesTags(testTags) {
|
|
955
|
-
const runner = getRunner();
|
|
956
|
-
const tagsFilter = runner._currentSpecification?.testTagsFilter ?? runner.config.tagsFilter;
|
|
957
|
-
if (!tagsFilter) return true;
|
|
958
|
-
let tagsFilterPredicate = filterMap.get(tagsFilter);
|
|
959
|
-
if (!tagsFilterPredicate) {
|
|
960
|
-
tagsFilterPredicate = createTagsFilter(tagsFilter, runner.config.tags);
|
|
961
|
-
filterMap.set(tagsFilter, tagsFilterPredicate);
|
|
962
|
-
}
|
|
963
|
-
return tagsFilterPredicate(testTags);
|
|
964
|
-
}
|
|
965
|
-
function validateTags(config, tags) {
|
|
966
|
-
if (!config.strictTags) return;
|
|
967
|
-
const availableTags = new Set(config.tags.map((tag) => tag.name));
|
|
968
|
-
for (const tag of tags) if (!availableTags.has(tag)) throw createNoTagsError(config.tags, tag);
|
|
969
|
-
}
|
|
970
|
-
function createNoTagsError(availableTags, tag, prefix = "tag") {
|
|
971
|
-
if (!availableTags.length) throw new Error(`The Vitest config does't define any "tags", cannot apply "${tag}" ${prefix} for this test. See: https://vitest.dev/guide/test-tags`);
|
|
972
|
-
throw new Error(`The ${prefix} "${tag}" is not defined in the configuration. Available tags are:\n${availableTags.map((t) => `- ${t.name}${t.description ? `: ${t.description}` : ""}`).join("\n")}`);
|
|
973
|
-
}
|
|
974
|
-
function createTagsFilter(tagsExpr, availableTags) {
|
|
975
|
-
const matchers = tagsExpr.map((expr) => parseTagsExpression(expr, availableTags));
|
|
976
|
-
return (testTags) => {
|
|
977
|
-
return matchers.every((matcher) => matcher(testTags));
|
|
978
|
-
};
|
|
979
|
-
}
|
|
980
|
-
function parseTagsExpression(expr, availableTags) {
|
|
981
|
-
const stream = new TokenStream(tokenize(expr), expr);
|
|
982
|
-
const ast = parseOrExpression(stream, availableTags);
|
|
983
|
-
if (stream.peek().type !== "EOF") throw new Error(`Invalid tags expression: unexpected "${formatToken(stream.peek())}" in "${expr}"`);
|
|
984
|
-
return (tags) => evaluateNode(ast, tags);
|
|
985
|
-
}
|
|
986
|
-
function formatToken(token) {
|
|
987
|
-
switch (token.type) {
|
|
988
|
-
case "TAG": return token.value;
|
|
989
|
-
default: return formatTokenType(token.type);
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
function tokenize(expr) {
|
|
993
|
-
const tokens = [];
|
|
994
|
-
let i = 0;
|
|
995
|
-
while (i < expr.length) {
|
|
996
|
-
if (expr[i] === " " || expr[i] === " ") {
|
|
997
|
-
i++;
|
|
998
|
-
continue;
|
|
999
|
-
}
|
|
1000
|
-
if (expr[i] === "(") {
|
|
1001
|
-
tokens.push({ type: "LPAREN" });
|
|
1002
|
-
i++;
|
|
1003
|
-
continue;
|
|
1004
|
-
}
|
|
1005
|
-
if (expr[i] === ")") {
|
|
1006
|
-
tokens.push({ type: "RPAREN" });
|
|
1007
|
-
i++;
|
|
1008
|
-
continue;
|
|
1009
|
-
}
|
|
1010
|
-
if (expr[i] === "!") {
|
|
1011
|
-
tokens.push({ type: "NOT" });
|
|
1012
|
-
i++;
|
|
1013
|
-
continue;
|
|
1014
|
-
}
|
|
1015
|
-
if (expr.slice(i, i + 2) === "&&") {
|
|
1016
|
-
tokens.push({ type: "AND" });
|
|
1017
|
-
i += 2;
|
|
1018
|
-
continue;
|
|
1019
|
-
}
|
|
1020
|
-
if (expr.slice(i, i + 2) === "||") {
|
|
1021
|
-
tokens.push({ type: "OR" });
|
|
1022
|
-
i += 2;
|
|
1023
|
-
continue;
|
|
1024
|
-
}
|
|
1025
|
-
if (/^and(?:\s|\)|$)/i.test(expr.slice(i))) {
|
|
1026
|
-
tokens.push({ type: "AND" });
|
|
1027
|
-
i += 3;
|
|
1028
|
-
continue;
|
|
1029
|
-
}
|
|
1030
|
-
if (/^or(?:\s|\)|$)/i.test(expr.slice(i))) {
|
|
1031
|
-
tokens.push({ type: "OR" });
|
|
1032
|
-
i += 2;
|
|
1033
|
-
continue;
|
|
1034
|
-
}
|
|
1035
|
-
if (/^not\s/i.test(expr.slice(i))) {
|
|
1036
|
-
tokens.push({ type: "NOT" });
|
|
1037
|
-
i += 3;
|
|
1038
|
-
continue;
|
|
1039
|
-
}
|
|
1040
|
-
let tag = "";
|
|
1041
|
-
while (i < expr.length && expr[i] !== " " && expr[i] !== " " && expr[i] !== "(" && expr[i] !== ")" && expr[i] !== "!" && expr[i] !== "&" && expr[i] !== "|") {
|
|
1042
|
-
const remaining = expr.slice(i);
|
|
1043
|
-
if (tag === "" && (/^and(?:\s|\)|$)/i.test(remaining) || /^or(?:\s|\)|$)/i.test(remaining) || /^not\s/i.test(remaining))) break;
|
|
1044
|
-
tag += expr[i];
|
|
1045
|
-
i++;
|
|
1046
|
-
}
|
|
1047
|
-
if (tag) tokens.push({
|
|
1048
|
-
type: "TAG",
|
|
1049
|
-
value: tag
|
|
1050
|
-
});
|
|
1051
|
-
}
|
|
1052
|
-
tokens.push({ type: "EOF" });
|
|
1053
|
-
return tokens;
|
|
1054
|
-
}
|
|
1055
|
-
var TokenStream = class {
|
|
1056
|
-
pos = 0;
|
|
1057
|
-
constructor(tokens, expr) {
|
|
1058
|
-
this.tokens = tokens;
|
|
1059
|
-
this.expr = expr;
|
|
1060
|
-
}
|
|
1061
|
-
peek() {
|
|
1062
|
-
return this.tokens[this.pos];
|
|
1063
|
-
}
|
|
1064
|
-
next() {
|
|
1065
|
-
return this.tokens[this.pos++];
|
|
1066
|
-
}
|
|
1067
|
-
expect(type) {
|
|
1068
|
-
const token = this.next();
|
|
1069
|
-
if (token.type !== type) {
|
|
1070
|
-
if (type === "RPAREN" && token.type === "EOF") throw new Error(`Invalid tags expression: missing closing ")" in "${this.expr}"`);
|
|
1071
|
-
throw new Error(`Invalid tags expression: expected "${formatTokenType(type)}" but got "${formatToken(token)}" in "${this.expr}"`);
|
|
1072
|
-
}
|
|
1073
|
-
return token;
|
|
1074
|
-
}
|
|
1075
|
-
unexpectedToken() {
|
|
1076
|
-
const token = this.peek();
|
|
1077
|
-
if (token.type === "EOF") throw new Error(`Invalid tags expression: unexpected end of expression in "${this.expr}"`);
|
|
1078
|
-
throw new Error(`Invalid tags expression: unexpected "${formatToken(token)}" in "${this.expr}"`);
|
|
1079
|
-
}
|
|
1080
|
-
};
|
|
1081
|
-
function formatTokenType(type) {
|
|
1082
|
-
switch (type) {
|
|
1083
|
-
case "TAG": return "tag";
|
|
1084
|
-
case "AND": return "and";
|
|
1085
|
-
case "OR": return "or";
|
|
1086
|
-
case "NOT": return "not";
|
|
1087
|
-
case "LPAREN": return "(";
|
|
1088
|
-
case "RPAREN": return ")";
|
|
1089
|
-
case "EOF": return "end of expression";
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
function parseOrExpression(stream, availableTags) {
|
|
1093
|
-
let left = parseAndExpression(stream, availableTags);
|
|
1094
|
-
while (stream.peek().type === "OR") {
|
|
1095
|
-
stream.next();
|
|
1096
|
-
const right = parseAndExpression(stream, availableTags);
|
|
1097
|
-
left = {
|
|
1098
|
-
type: "or",
|
|
1099
|
-
left,
|
|
1100
|
-
right
|
|
1101
|
-
};
|
|
1102
|
-
}
|
|
1103
|
-
return left;
|
|
1104
|
-
}
|
|
1105
|
-
function parseAndExpression(stream, availableTags) {
|
|
1106
|
-
let left = parseUnaryExpression(stream, availableTags);
|
|
1107
|
-
while (stream.peek().type === "AND") {
|
|
1108
|
-
stream.next();
|
|
1109
|
-
const right = parseUnaryExpression(stream, availableTags);
|
|
1110
|
-
left = {
|
|
1111
|
-
type: "and",
|
|
1112
|
-
left,
|
|
1113
|
-
right
|
|
1114
|
-
};
|
|
1115
|
-
}
|
|
1116
|
-
return left;
|
|
1117
|
-
}
|
|
1118
|
-
function parseUnaryExpression(stream, availableTags) {
|
|
1119
|
-
if (stream.peek().type === "NOT") {
|
|
1120
|
-
stream.next();
|
|
1121
|
-
return {
|
|
1122
|
-
type: "not",
|
|
1123
|
-
operand: parseUnaryExpression(stream, availableTags)
|
|
1124
|
-
};
|
|
1125
|
-
}
|
|
1126
|
-
return parsePrimaryExpression(stream, availableTags);
|
|
1127
|
-
}
|
|
1128
|
-
function parsePrimaryExpression(stream, availableTags) {
|
|
1129
|
-
const token = stream.peek();
|
|
1130
|
-
if (token.type === "LPAREN") {
|
|
1131
|
-
stream.next();
|
|
1132
|
-
const expr = parseOrExpression(stream, availableTags);
|
|
1133
|
-
stream.expect("RPAREN");
|
|
1134
|
-
return expr;
|
|
1135
|
-
}
|
|
1136
|
-
if (token.type === "TAG") {
|
|
1137
|
-
stream.next();
|
|
1138
|
-
const tagValue = token.value;
|
|
1139
|
-
return {
|
|
1140
|
-
type: "tag",
|
|
1141
|
-
value: tagValue,
|
|
1142
|
-
pattern: resolveTagPattern(tagValue, availableTags)
|
|
1143
|
-
};
|
|
1144
|
-
}
|
|
1145
|
-
stream.unexpectedToken();
|
|
1146
|
-
}
|
|
1147
|
-
function createWildcardRegex(pattern) {
|
|
1148
|
-
return new RegExp(`^${pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`);
|
|
1149
|
-
}
|
|
1150
|
-
function resolveTagPattern(tagPattern, availableTags) {
|
|
1151
|
-
if (tagPattern.includes("*")) {
|
|
1152
|
-
const regex = createWildcardRegex(tagPattern);
|
|
1153
|
-
if (!availableTags.some((tag) => regex.test(tag.name))) throw createNoTagsError(availableTags, tagPattern, "tag pattern");
|
|
1154
|
-
return regex;
|
|
1155
|
-
}
|
|
1156
|
-
if (!availableTags.length || !availableTags.some((tag) => tag.name === tagPattern)) throw createNoTagsError(availableTags, tagPattern, "tag pattern");
|
|
1157
|
-
return null;
|
|
1158
|
-
}
|
|
1159
|
-
function evaluateNode(node, tags) {
|
|
1160
|
-
switch (node.type) {
|
|
1161
|
-
case "tag":
|
|
1162
|
-
if (node.pattern) return tags.some((tag) => node.pattern.test(tag));
|
|
1163
|
-
return tags.includes(node.value);
|
|
1164
|
-
case "not": return !evaluateNode(node.operand, tags);
|
|
1165
|
-
case "and": return evaluateNode(node.left, tags) && evaluateNode(node.right, tags);
|
|
1166
|
-
case "or": return evaluateNode(node.left, tags) || evaluateNode(node.right, tags);
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
function isTestCase(s) {
|
|
1170
|
-
return s.type === "test";
|
|
1171
|
-
}
|
|
1172
|
-
function getTests(suite) {
|
|
1173
|
-
const tests = [];
|
|
1174
|
-
const arraySuites = toArray(suite);
|
|
1175
|
-
for (const s of arraySuites) if (isTestCase(s)) tests.push(s);
|
|
1176
|
-
else for (const task of s.tasks) if (isTestCase(task)) tests.push(task);
|
|
1177
|
-
else {
|
|
1178
|
-
const taskTests = getTests(task);
|
|
1179
|
-
for (const test of taskTests) tests.push(test);
|
|
1180
|
-
}
|
|
1181
|
-
return tests;
|
|
1182
|
-
}
|
|
1183
|
-
function getTasks(tasks = []) {
|
|
1184
|
-
return toArray(tasks).flatMap((s) => isTestCase(s) ? [s] : [s, ...getTasks(s.tasks)]);
|
|
1185
|
-
}
|
|
1186
|
-
function getSuites(suite) {
|
|
1187
|
-
return toArray(suite).flatMap((s) => s.type === "suite" ? [s, ...getSuites(s.tasks)] : []);
|
|
1188
|
-
}
|
|
1189
|
-
function hasTests(suite) {
|
|
1190
|
-
return toArray(suite).some((s) => s.tasks.some((c) => isTestCase(c) || hasTests(c)));
|
|
1191
|
-
}
|
|
1192
|
-
function hasFailed(suite) {
|
|
1193
|
-
return toArray(suite).some((s) => s.result?.state === "fail" || s.type === "suite" && hasFailed(s.tasks));
|
|
1194
|
-
}
|
|
1195
|
-
function getNames(task) {
|
|
1196
|
-
const names = [task.name];
|
|
1197
|
-
let current = task;
|
|
1198
|
-
while (current?.suite) {
|
|
1199
|
-
current = current.suite;
|
|
1200
|
-
if (current?.name) names.unshift(current.name);
|
|
1201
|
-
}
|
|
1202
|
-
if (current !== task.file) names.unshift(task.file.name);
|
|
1203
|
-
return names;
|
|
1204
|
-
}
|
|
1205
|
-
function getFullName(task, separator = " > ") {
|
|
1206
|
-
return getNames(task).join(separator);
|
|
1207
|
-
}
|
|
1208
|
-
function getTestName(task, separator = " > ") {
|
|
1209
|
-
return getNames(task).slice(1).join(separator);
|
|
1210
|
-
}
|
|
1211
|
-
function createTaskName(names, separator = " > ") {
|
|
1212
|
-
return names.filter((name) => name !== void 0).join(separator);
|
|
1213
|
-
}
|
|
1214
|
-
/**
|
|
1215
|
-
* Creates a suite of tests, allowing for grouping and hierarchical organization of tests.
|
|
1216
|
-
* Suites can contain both tests and other suites, enabling complex test structures.
|
|
1217
|
-
*
|
|
1218
|
-
* @param {string} name - The name of the suite, used for identification and reporting.
|
|
1219
|
-
* @param {Function} fn - A function that defines the tests and suites within this suite.
|
|
1220
|
-
* @example
|
|
1221
|
-
* ```ts
|
|
1222
|
-
* // Define a suite with two tests
|
|
1223
|
-
* suite('Math operations', () => {
|
|
1224
|
-
* test('should add two numbers', () => {
|
|
1225
|
-
* expect(add(1, 2)).toBe(3);
|
|
1226
|
-
* });
|
|
1227
|
-
*
|
|
1228
|
-
* test('should subtract two numbers', () => {
|
|
1229
|
-
* expect(subtract(5, 2)).toBe(3);
|
|
1230
|
-
* });
|
|
1231
|
-
* });
|
|
1232
|
-
* ```
|
|
1233
|
-
* @example
|
|
1234
|
-
* ```ts
|
|
1235
|
-
* // Define nested suites
|
|
1236
|
-
* suite('String operations', () => {
|
|
1237
|
-
* suite('Trimming', () => {
|
|
1238
|
-
* test('should trim whitespace from start and end', () => {
|
|
1239
|
-
* expect(' hello '.trim()).toBe('hello');
|
|
1240
|
-
* });
|
|
1241
|
-
* });
|
|
1242
|
-
*
|
|
1243
|
-
* suite('Concatenation', () => {
|
|
1244
|
-
* test('should concatenate two strings', () => {
|
|
1245
|
-
* expect('hello' + ' ' + 'world').toBe('hello world');
|
|
1246
|
-
* });
|
|
1247
|
-
* });
|
|
1248
|
-
* });
|
|
1249
|
-
* ```
|
|
1250
|
-
*/
|
|
1251
|
-
const suite = createSuite();
|
|
1252
|
-
/**
|
|
1253
|
-
* Defines a test case with a given name and test function. The test function can optionally be configured with test options.
|
|
1254
|
-
*
|
|
1255
|
-
* @param {string | Function} name - The name of the test or a function that will be used as a test name.
|
|
1256
|
-
* @param {TestOptions | TestFunction} [optionsOrFn] - Optional. The test options or the test function if no explicit name is provided.
|
|
1257
|
-
* @param {number | TestOptions | TestFunction} [optionsOrTest] - Optional. The test function or options, depending on the previous parameters.
|
|
1258
|
-
* @throws {Error} If called inside another test function.
|
|
1259
|
-
* @example
|
|
1260
|
-
* ```ts
|
|
1261
|
-
* // Define a simple test
|
|
1262
|
-
* test('should add two numbers', () => {
|
|
1263
|
-
* expect(add(1, 2)).toBe(3);
|
|
1264
|
-
* });
|
|
1265
|
-
* ```
|
|
1266
|
-
* @example
|
|
1267
|
-
* ```ts
|
|
1268
|
-
* // Define a test with options
|
|
1269
|
-
* test('should subtract two numbers', { retry: 3 }, () => {
|
|
1270
|
-
* expect(subtract(5, 2)).toBe(3);
|
|
1271
|
-
* });
|
|
1272
|
-
* ```
|
|
1273
|
-
*/
|
|
1274
|
-
const test = createTest(function(name, optionsOrFn, optionsOrTest) {
|
|
1275
|
-
if (getCurrentTest()) throw new Error("Calling the test function inside another test function is not allowed. Please put it inside \"describe\" or \"suite\" so it can be properly collected.");
|
|
1276
|
-
getCurrentSuite().test.fn.call(this, formatName(name), optionsOrFn, optionsOrTest);
|
|
1277
|
-
});
|
|
1278
|
-
/**
|
|
1279
|
-
* Creates a suite of tests, allowing for grouping and hierarchical organization of tests.
|
|
1280
|
-
* Suites can contain both tests and other suites, enabling complex test structures.
|
|
1281
|
-
*
|
|
1282
|
-
* @param {string} name - The name of the suite, used for identification and reporting.
|
|
1283
|
-
* @param {Function} fn - A function that defines the tests and suites within this suite.
|
|
1284
|
-
* @example
|
|
1285
|
-
* ```ts
|
|
1286
|
-
* // Define a suite with two tests
|
|
1287
|
-
* describe('Math operations', () => {
|
|
1288
|
-
* test('should add two numbers', () => {
|
|
1289
|
-
* expect(add(1, 2)).toBe(3);
|
|
1290
|
-
* });
|
|
1291
|
-
*
|
|
1292
|
-
* test('should subtract two numbers', () => {
|
|
1293
|
-
* expect(subtract(5, 2)).toBe(3);
|
|
1294
|
-
* });
|
|
1295
|
-
* });
|
|
1296
|
-
* ```
|
|
1297
|
-
* @example
|
|
1298
|
-
* ```ts
|
|
1299
|
-
* // Define nested suites
|
|
1300
|
-
* describe('String operations', () => {
|
|
1301
|
-
* describe('Trimming', () => {
|
|
1302
|
-
* test('should trim whitespace from start and end', () => {
|
|
1303
|
-
* expect(' hello '.trim()).toBe('hello');
|
|
1304
|
-
* });
|
|
1305
|
-
* });
|
|
1306
|
-
*
|
|
1307
|
-
* describe('Concatenation', () => {
|
|
1308
|
-
* test('should concatenate two strings', () => {
|
|
1309
|
-
* expect('hello' + ' ' + 'world').toBe('hello world');
|
|
1310
|
-
* });
|
|
1311
|
-
* });
|
|
1312
|
-
* });
|
|
1313
|
-
* ```
|
|
1314
|
-
*/
|
|
1315
|
-
const describe = suite;
|
|
1316
|
-
/**
|
|
1317
|
-
* Defines a test case with a given name and test function. The test function can optionally be configured with test options.
|
|
1318
|
-
*
|
|
1319
|
-
* @param {string | Function} name - The name of the test or a function that will be used as a test name.
|
|
1320
|
-
* @param {TestOptions | TestFunction} [optionsOrFn] - Optional. The test options or the test function if no explicit name is provided.
|
|
1321
|
-
* @param {number | TestOptions | TestFunction} [optionsOrTest] - Optional. The test function or options, depending on the previous parameters.
|
|
1322
|
-
* @throws {Error} If called inside another test function.
|
|
1323
|
-
* @example
|
|
1324
|
-
* ```ts
|
|
1325
|
-
* // Define a simple test
|
|
1326
|
-
* it('adds two numbers', () => {
|
|
1327
|
-
* expect(add(1, 2)).toBe(3);
|
|
1328
|
-
* });
|
|
1329
|
-
* ```
|
|
1330
|
-
* @example
|
|
1331
|
-
* ```ts
|
|
1332
|
-
* // Define a test with options
|
|
1333
|
-
* it('subtracts two numbers', { retry: 3 }, () => {
|
|
1334
|
-
* expect(subtract(5, 2)).toBe(3);
|
|
1335
|
-
* });
|
|
1336
|
-
* ```
|
|
1337
|
-
*/
|
|
1338
|
-
const it = test;
|
|
1339
|
-
let runner;
|
|
1340
|
-
let defaultSuite;
|
|
1341
|
-
let currentTestFilepath;
|
|
1342
|
-
function assert(condition, message) {
|
|
1343
|
-
if (!condition) throw new Error(`Vitest failed to find ${message}. One of the following is possible:
|
|
1344
|
-
- "vitest" is imported directly without running "vitest" command
|
|
1345
|
-
- "vitest" is imported inside "globalSetup" (to fix this, use "setupFiles" instead, because "globalSetup" runs in a different context)
|
|
1346
|
-
- "vitest" is imported inside Vite / Vitest config file
|
|
1347
|
-
- Otherwise, it might be a Vitest bug. Please report it to https://github.com/vitest-dev/vitest/issues
|
|
1348
|
-
`);
|
|
1349
|
-
}
|
|
1350
|
-
function getDefaultSuite() {
|
|
1351
|
-
assert(defaultSuite, "the default suite");
|
|
1352
|
-
return defaultSuite;
|
|
1353
|
-
}
|
|
1354
|
-
function getRunner() {
|
|
1355
|
-
assert(runner, "the runner");
|
|
1356
|
-
return runner;
|
|
1357
|
-
}
|
|
1358
|
-
function createDefaultSuite(runner) {
|
|
1359
|
-
const config = runner.config.sequence;
|
|
1360
|
-
const options = {};
|
|
1361
|
-
if (config.concurrent != null) options.concurrent = config.concurrent;
|
|
1362
|
-
const collector = suite("", options, () => {});
|
|
1363
|
-
delete collector.suite;
|
|
1364
|
-
return collector;
|
|
1365
|
-
}
|
|
1366
|
-
function clearCollectorContext(file, currentRunner) {
|
|
1367
|
-
currentTestFilepath = file.filepath;
|
|
1368
|
-
runner = currentRunner;
|
|
1369
|
-
if (!defaultSuite) defaultSuite = createDefaultSuite(currentRunner);
|
|
1370
|
-
defaultSuite.file = file;
|
|
1371
|
-
collectorContext.tasks.length = 0;
|
|
1372
|
-
defaultSuite.clear();
|
|
1373
|
-
collectorContext.currentSuite = defaultSuite;
|
|
1374
|
-
}
|
|
1375
|
-
function getCurrentSuite() {
|
|
1376
|
-
const currentSuite = collectorContext.currentSuite || defaultSuite;
|
|
1377
|
-
assert(currentSuite, "the current suite");
|
|
1378
|
-
return currentSuite;
|
|
1379
|
-
}
|
|
1380
|
-
function createSuiteHooks() {
|
|
1381
|
-
return {
|
|
1382
|
-
beforeAll: [],
|
|
1383
|
-
afterAll: [],
|
|
1384
|
-
beforeEach: [],
|
|
1385
|
-
afterEach: [],
|
|
1386
|
-
aroundEach: [],
|
|
1387
|
-
aroundAll: []
|
|
1388
|
-
};
|
|
1389
|
-
}
|
|
1390
|
-
const POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
|
|
1391
|
-
function parseArguments(optionsOrFn, timeoutOrTest) {
|
|
1392
|
-
if (timeoutOrTest != null && typeof timeoutOrTest === "object") throw new TypeError(`Signature "test(name, fn, { ... })" was deprecated in Vitest 3 and removed in Vitest 4. Please, provide options as a second argument instead.`);
|
|
1393
|
-
let options = {};
|
|
1394
|
-
let fn;
|
|
1395
|
-
if (typeof timeoutOrTest === "number") options = { timeout: timeoutOrTest };
|
|
1396
|
-
else if (typeof optionsOrFn === "object") options = optionsOrFn;
|
|
1397
|
-
if (typeof optionsOrFn === "function") {
|
|
1398
|
-
if (typeof timeoutOrTest === "function") throw new TypeError("Cannot use two functions as arguments. Please use the second argument for options.");
|
|
1399
|
-
fn = optionsOrFn;
|
|
1400
|
-
} else if (typeof timeoutOrTest === "function") fn = timeoutOrTest;
|
|
1401
|
-
return {
|
|
1402
|
-
options,
|
|
1403
|
-
handler: fn
|
|
1404
|
-
};
|
|
1405
|
-
}
|
|
1406
|
-
function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions) {
|
|
1407
|
-
const tasks = [];
|
|
1408
|
-
let suite;
|
|
1409
|
-
initSuite(true);
|
|
1410
|
-
const task = function(name = "", options = {}) {
|
|
1411
|
-
const currentSuite = collectorContext.currentSuite?.suite;
|
|
1412
|
-
const testTags = unique([...(currentSuite ?? collectorContext.currentSuite?.file)?.tags || [], ...toArray(options.tags)]);
|
|
1413
|
-
const tagsOptions = testTags.map((tag) => {
|
|
1414
|
-
const tagDefinition = runner.config.tags?.find((t) => t.name === tag);
|
|
1415
|
-
if (!tagDefinition && runner.config.strictTags) throw createNoTagsError(runner.config.tags, tag);
|
|
1416
|
-
return tagDefinition;
|
|
1417
|
-
}).filter((r) => r != null).sort((tag1, tag2) => (tag2.priority ?? POSITIVE_INFINITY) - (tag1.priority ?? POSITIVE_INFINITY)).reduce((acc, tag) => {
|
|
1418
|
-
const { name, description, priority, meta, ...options } = tag;
|
|
1419
|
-
Object.assign(acc, options);
|
|
1420
|
-
if (meta) acc.meta = Object.assign(acc.meta ?? Object.create(null), meta);
|
|
1421
|
-
return acc;
|
|
1422
|
-
}, {});
|
|
1423
|
-
const testOwnMeta = options.meta;
|
|
1424
|
-
options = {
|
|
1425
|
-
...tagsOptions,
|
|
1426
|
-
...options
|
|
1427
|
-
};
|
|
1428
|
-
const timeout = options.timeout ?? runner.config.testTimeout;
|
|
1429
|
-
const parentMeta = currentSuite?.meta;
|
|
1430
|
-
const tagMeta = tagsOptions.meta;
|
|
1431
|
-
const testMeta = Object.create(null);
|
|
1432
|
-
if (tagMeta) Object.assign(testMeta, tagMeta);
|
|
1433
|
-
if (parentMeta) Object.assign(testMeta, parentMeta);
|
|
1434
|
-
if (testOwnMeta) Object.assign(testMeta, testOwnMeta);
|
|
1435
|
-
const task = {
|
|
1436
|
-
id: "",
|
|
1437
|
-
name,
|
|
1438
|
-
fullName: createTaskName([currentSuite?.fullName ?? collectorContext.currentSuite?.file?.fullName, name]),
|
|
1439
|
-
fullTestName: createTaskName([currentSuite?.fullTestName, name]),
|
|
1440
|
-
suite: currentSuite,
|
|
1441
|
-
each: options.each,
|
|
1442
|
-
fails: options.fails,
|
|
1443
|
-
context: void 0,
|
|
1444
|
-
type: "test",
|
|
1445
|
-
file: currentSuite?.file ?? collectorContext.currentSuite?.file,
|
|
1446
|
-
timeout,
|
|
1447
|
-
retry: options.retry ?? runner.config.retry,
|
|
1448
|
-
repeats: options.repeats,
|
|
1449
|
-
mode: options.only ? "only" : options.skip ? "skip" : options.todo ? "todo" : "run",
|
|
1450
|
-
meta: testMeta,
|
|
1451
|
-
annotations: [],
|
|
1452
|
-
artifacts: [],
|
|
1453
|
-
tags: testTags
|
|
1454
|
-
};
|
|
1455
|
-
const handler = options.handler;
|
|
1456
|
-
if (task.mode === "run" && !handler) task.mode = "todo";
|
|
1457
|
-
if (options.concurrent || !options.sequential && runner.config.sequence.concurrent) task.concurrent = true;
|
|
1458
|
-
task.shuffle = suiteOptions?.shuffle;
|
|
1459
|
-
const context = createTestContext(task, runner);
|
|
1460
|
-
Object.defineProperty(task, "context", {
|
|
1461
|
-
value: context,
|
|
1462
|
-
enumerable: false
|
|
1463
|
-
});
|
|
1464
|
-
setTestFixture(context, options.fixtures ?? new TestFixtures());
|
|
1465
|
-
const limit = Error.stackTraceLimit;
|
|
1466
|
-
Error.stackTraceLimit = 10;
|
|
1467
|
-
const stackTraceError = /* @__PURE__ */ new Error("STACK_TRACE_ERROR");
|
|
1468
|
-
Error.stackTraceLimit = limit;
|
|
1469
|
-
if (handler) setFn(task, withTimeout(withCancel(withAwaitAsyncAssertions(withFixtures(handler, { context }), task), task.context.signal), timeout, false, stackTraceError, (_, error) => abortIfTimeout([context], error)));
|
|
1470
|
-
if (runner.config.includeTaskLocation) {
|
|
1471
|
-
const error = stackTraceError.stack;
|
|
1472
|
-
const stack = findTestFileStackTrace(currentTestFilepath, error);
|
|
1473
|
-
if (stack) task.location = {
|
|
1474
|
-
line: stack.line,
|
|
1475
|
-
column: stack.column
|
|
1476
|
-
};
|
|
1477
|
-
}
|
|
1478
|
-
tasks.push(task);
|
|
1479
|
-
return task;
|
|
1480
|
-
};
|
|
1481
|
-
const test = createTest(function(name, optionsOrFn, timeoutOrTest) {
|
|
1482
|
-
let { options, handler } = parseArguments(optionsOrFn, timeoutOrTest);
|
|
1483
|
-
if (typeof suiteOptions === "object") options = Object.assign({}, suiteOptions, options);
|
|
1484
|
-
const concurrent = this.concurrent ?? (!this.sequential && options?.concurrent);
|
|
1485
|
-
if (options.concurrent != null && concurrent != null) options.concurrent = concurrent;
|
|
1486
|
-
const sequential = this.sequential ?? (!this.concurrent && options?.sequential);
|
|
1487
|
-
if (options.sequential != null && sequential != null) options.sequential = sequential;
|
|
1488
|
-
const test = task(formatName(name), {
|
|
1489
|
-
...this,
|
|
1490
|
-
...options,
|
|
1491
|
-
handler
|
|
1492
|
-
});
|
|
1493
|
-
test.type = "test";
|
|
1494
|
-
});
|
|
1495
|
-
const collector = {
|
|
1496
|
-
type: "collector",
|
|
1497
|
-
name,
|
|
1498
|
-
mode,
|
|
1499
|
-
suite,
|
|
1500
|
-
options: suiteOptions,
|
|
1501
|
-
test,
|
|
1502
|
-
file: suite.file,
|
|
1503
|
-
tasks,
|
|
1504
|
-
collect,
|
|
1505
|
-
task,
|
|
1506
|
-
clear,
|
|
1507
|
-
on: addHook
|
|
1508
|
-
};
|
|
1509
|
-
function addHook(name, ...fn) {
|
|
1510
|
-
getHooks(suite)[name].push(...fn);
|
|
1511
|
-
}
|
|
1512
|
-
function initSuite(includeLocation) {
|
|
1513
|
-
if (typeof suiteOptions === "number") suiteOptions = { timeout: suiteOptions };
|
|
1514
|
-
const currentSuite = collectorContext.currentSuite?.suite;
|
|
1515
|
-
const parentTask = currentSuite ?? collectorContext.currentSuite?.file;
|
|
1516
|
-
const suiteTags = toArray(suiteOptions?.tags);
|
|
1517
|
-
validateTags(runner.config, suiteTags);
|
|
1518
|
-
suite = {
|
|
1519
|
-
id: "",
|
|
1520
|
-
type: "suite",
|
|
1521
|
-
name,
|
|
1522
|
-
fullName: createTaskName([currentSuite?.fullName ?? collectorContext.currentSuite?.file?.fullName, name]),
|
|
1523
|
-
fullTestName: createTaskName([currentSuite?.fullTestName, name]),
|
|
1524
|
-
suite: currentSuite,
|
|
1525
|
-
mode,
|
|
1526
|
-
each,
|
|
1527
|
-
file: currentSuite?.file ?? collectorContext.currentSuite?.file,
|
|
1528
|
-
shuffle: suiteOptions?.shuffle,
|
|
1529
|
-
tasks: [],
|
|
1530
|
-
meta: suiteOptions?.meta ?? Object.create(null),
|
|
1531
|
-
concurrent: suiteOptions?.concurrent,
|
|
1532
|
-
tags: unique([...parentTask?.tags || [], ...suiteTags])
|
|
1533
|
-
};
|
|
1534
|
-
if (runner && includeLocation && runner.config.includeTaskLocation) {
|
|
1535
|
-
const limit = Error.stackTraceLimit;
|
|
1536
|
-
Error.stackTraceLimit = 15;
|
|
1537
|
-
const error = (/* @__PURE__ */ new Error("stacktrace")).stack;
|
|
1538
|
-
Error.stackTraceLimit = limit;
|
|
1539
|
-
const stack = findTestFileStackTrace(currentTestFilepath, error);
|
|
1540
|
-
if (stack) suite.location = {
|
|
1541
|
-
line: stack.line,
|
|
1542
|
-
column: stack.column
|
|
1543
|
-
};
|
|
1544
|
-
}
|
|
1545
|
-
setHooks(suite, createSuiteHooks());
|
|
1546
|
-
}
|
|
1547
|
-
function clear() {
|
|
1548
|
-
tasks.length = 0;
|
|
1549
|
-
initSuite(false);
|
|
1550
|
-
}
|
|
1551
|
-
async function collect(file) {
|
|
1552
|
-
if (!file) throw new TypeError("File is required to collect tasks.");
|
|
1553
|
-
if (factory) await runWithSuite(collector, () => factory(test));
|
|
1554
|
-
const allChildren = [];
|
|
1555
|
-
for (const i of tasks) allChildren.push(i.type === "collector" ? await i.collect(file) : i);
|
|
1556
|
-
suite.tasks = allChildren;
|
|
1557
|
-
return suite;
|
|
1558
|
-
}
|
|
1559
|
-
collectTask(collector);
|
|
1560
|
-
return collector;
|
|
1561
|
-
}
|
|
1562
|
-
function withAwaitAsyncAssertions(fn, task) {
|
|
1563
|
-
return (async (...args) => {
|
|
1564
|
-
const fnResult = await fn(...args);
|
|
1565
|
-
if (task.promises) {
|
|
1566
|
-
const errors = (await Promise.allSettled(task.promises)).map((r) => r.status === "rejected" ? r.reason : void 0).filter(Boolean);
|
|
1567
|
-
if (errors.length) throw errors;
|
|
1568
|
-
}
|
|
1569
|
-
return fnResult;
|
|
1570
|
-
});
|
|
1571
|
-
}
|
|
1572
|
-
function createSuite() {
|
|
1573
|
-
function suiteFn(name, factoryOrOptions, optionsOrFactory) {
|
|
1574
|
-
if (getCurrentTest()) throw new Error("Calling the suite function inside test function is not allowed. It can be only called at the top level or inside another suite function.");
|
|
1575
|
-
const currentSuite = collectorContext.currentSuite || defaultSuite;
|
|
1576
|
-
let { options, handler: factory } = parseArguments(factoryOrOptions, optionsOrFactory);
|
|
1577
|
-
const isConcurrentSpecified = options.concurrent || this.concurrent || options.sequential === false;
|
|
1578
|
-
const isSequentialSpecified = options.sequential || this.sequential || options.concurrent === false;
|
|
1579
|
-
const { meta: parentMeta, ...parentOptions } = currentSuite?.options || {};
|
|
1580
|
-
options = {
|
|
1581
|
-
...parentOptions,
|
|
1582
|
-
...options
|
|
1583
|
-
};
|
|
1584
|
-
const shuffle = this.shuffle ?? options.shuffle ?? currentSuite?.options?.shuffle ?? runner?.config.sequence.shuffle;
|
|
1585
|
-
if (shuffle != null) options.shuffle = shuffle;
|
|
1586
|
-
let mode = this.only ?? options.only ? "only" : this.skip ?? options.skip ? "skip" : this.todo ?? options.todo ? "todo" : "run";
|
|
1587
|
-
if (mode === "run" && !factory) mode = "todo";
|
|
1588
|
-
const isConcurrent = isConcurrentSpecified || options.concurrent && !isSequentialSpecified;
|
|
1589
|
-
const isSequential = isSequentialSpecified || options.sequential && !isConcurrentSpecified;
|
|
1590
|
-
if (isConcurrent != null) options.concurrent = isConcurrent && !isSequential;
|
|
1591
|
-
if (isSequential != null) options.sequential = isSequential && !isConcurrent;
|
|
1592
|
-
if (parentMeta) options.meta = Object.assign(Object.create(null), parentMeta, options.meta);
|
|
1593
|
-
return createSuiteCollector(formatName(name), factory, mode, this.each, options);
|
|
1594
|
-
}
|
|
1595
|
-
suiteFn.each = function(cases, ...args) {
|
|
1596
|
-
const context = getChainableContext(this);
|
|
1597
|
-
const suite = context.withContext();
|
|
1598
|
-
context.setContext("each", true);
|
|
1599
|
-
if (Array.isArray(cases) && args.length) cases = formatTemplateString(cases, args);
|
|
1600
|
-
return (name, optionsOrFn, fnOrOptions) => {
|
|
1601
|
-
const _name = formatName(name);
|
|
1602
|
-
const arrayOnlyCases = cases.every(Array.isArray);
|
|
1603
|
-
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
|
1604
|
-
const fnFirst = typeof optionsOrFn === "function";
|
|
1605
|
-
cases.forEach((i, idx) => {
|
|
1606
|
-
const items = Array.isArray(i) ? i : [i];
|
|
1607
|
-
if (fnFirst) if (arrayOnlyCases) suite(formatTitle(_name, items, idx), handler ? () => handler(...items) : void 0, options.timeout);
|
|
1608
|
-
else suite(formatTitle(_name, items, idx), handler ? () => handler(i) : void 0, options.timeout);
|
|
1609
|
-
else if (arrayOnlyCases) suite(formatTitle(_name, items, idx), options, handler ? () => handler(...items) : void 0);
|
|
1610
|
-
else suite(formatTitle(_name, items, idx), options, handler ? () => handler(i) : void 0);
|
|
1611
|
-
});
|
|
1612
|
-
context.setContext("each", void 0);
|
|
1613
|
-
};
|
|
1614
|
-
};
|
|
1615
|
-
suiteFn.for = function(cases, ...args) {
|
|
1616
|
-
if (Array.isArray(cases) && args.length) cases = formatTemplateString(cases, args);
|
|
1617
|
-
return (name, optionsOrFn, fnOrOptions) => {
|
|
1618
|
-
const name_ = formatName(name);
|
|
1619
|
-
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
|
1620
|
-
cases.forEach((item, idx) => {
|
|
1621
|
-
suite(formatTitle(name_, toArray(item), idx), options, handler ? () => handler(item) : void 0);
|
|
1622
|
-
});
|
|
1623
|
-
};
|
|
1624
|
-
};
|
|
1625
|
-
suiteFn.skipIf = (condition) => condition ? suite.skip : suite;
|
|
1626
|
-
suiteFn.runIf = (condition) => condition ? suite : suite.skip;
|
|
1627
|
-
return createChainable([
|
|
1628
|
-
"concurrent",
|
|
1629
|
-
"sequential",
|
|
1630
|
-
"shuffle",
|
|
1631
|
-
"skip",
|
|
1632
|
-
"only",
|
|
1633
|
-
"todo"
|
|
1634
|
-
], suiteFn);
|
|
1635
|
-
}
|
|
1636
|
-
function createTaskCollector(fn) {
|
|
1637
|
-
const taskFn = fn;
|
|
1638
|
-
taskFn.each = function(cases, ...args) {
|
|
1639
|
-
const context = getChainableContext(this);
|
|
1640
|
-
const test = context.withContext();
|
|
1641
|
-
context.setContext("each", true);
|
|
1642
|
-
if (Array.isArray(cases) && args.length) cases = formatTemplateString(cases, args);
|
|
1643
|
-
return (name, optionsOrFn, fnOrOptions) => {
|
|
1644
|
-
const _name = formatName(name);
|
|
1645
|
-
const arrayOnlyCases = cases.every(Array.isArray);
|
|
1646
|
-
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
|
1647
|
-
const fnFirst = typeof optionsOrFn === "function";
|
|
1648
|
-
cases.forEach((i, idx) => {
|
|
1649
|
-
const items = Array.isArray(i) ? i : [i];
|
|
1650
|
-
if (fnFirst) if (arrayOnlyCases) test(formatTitle(_name, items, idx), handler ? () => handler(...items) : void 0, options.timeout);
|
|
1651
|
-
else test(formatTitle(_name, items, idx), handler ? () => handler(i) : void 0, options.timeout);
|
|
1652
|
-
else if (arrayOnlyCases) test(formatTitle(_name, items, idx), options, handler ? () => handler(...items) : void 0);
|
|
1653
|
-
else test(formatTitle(_name, items, idx), options, handler ? () => handler(i) : void 0);
|
|
1654
|
-
});
|
|
1655
|
-
context.setContext("each", void 0);
|
|
1656
|
-
};
|
|
1657
|
-
};
|
|
1658
|
-
taskFn.for = function(cases, ...args) {
|
|
1659
|
-
const test = getChainableContext(this).withContext();
|
|
1660
|
-
if (Array.isArray(cases) && args.length) cases = formatTemplateString(cases, args);
|
|
1661
|
-
return (name, optionsOrFn, fnOrOptions) => {
|
|
1662
|
-
const _name = formatName(name);
|
|
1663
|
-
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
|
1664
|
-
cases.forEach((item, idx) => {
|
|
1665
|
-
const handlerWrapper = handler ? (ctx) => handler(item, ctx) : void 0;
|
|
1666
|
-
if (handlerWrapper) configureProps(handlerWrapper, {
|
|
1667
|
-
index: 1,
|
|
1668
|
-
original: handler
|
|
1669
|
-
});
|
|
1670
|
-
test(formatTitle(_name, toArray(item), idx), options, handlerWrapper);
|
|
1671
|
-
});
|
|
1672
|
-
};
|
|
1673
|
-
};
|
|
1674
|
-
taskFn.skipIf = function(condition) {
|
|
1675
|
-
return condition ? this.skip : this;
|
|
1676
|
-
};
|
|
1677
|
-
taskFn.runIf = function(condition) {
|
|
1678
|
-
return condition ? this : this.skip;
|
|
1679
|
-
};
|
|
1680
|
-
/**
|
|
1681
|
-
* Parse builder pattern arguments into a fixtures object.
|
|
1682
|
-
* Handles both builder pattern (name, options?, value) and object syntax.
|
|
1683
|
-
*/
|
|
1684
|
-
function parseBuilderFixtures(fixturesOrName, optionsOrFn, maybeFn) {
|
|
1685
|
-
if (typeof fixturesOrName !== "string") return fixturesOrName;
|
|
1686
|
-
const fixtureName = fixturesOrName;
|
|
1687
|
-
let fixtureOptions;
|
|
1688
|
-
let fixtureValue;
|
|
1689
|
-
if (maybeFn !== void 0) {
|
|
1690
|
-
fixtureOptions = optionsOrFn;
|
|
1691
|
-
fixtureValue = maybeFn;
|
|
1692
|
-
} else if (optionsOrFn !== null && typeof optionsOrFn === "object" && !Array.isArray(optionsOrFn) && TestFixtures.isFixtureOptions(optionsOrFn)) {
|
|
1693
|
-
fixtureOptions = optionsOrFn;
|
|
1694
|
-
fixtureValue = {};
|
|
1695
|
-
} else {
|
|
1696
|
-
fixtureOptions = void 0;
|
|
1697
|
-
fixtureValue = optionsOrFn;
|
|
1698
|
-
}
|
|
1699
|
-
if (typeof fixtureValue === "function") {
|
|
1700
|
-
const builderFn = fixtureValue;
|
|
1701
|
-
const fixture = async (ctx, use) => {
|
|
1702
|
-
let cleanup;
|
|
1703
|
-
const onCleanup = (fn) => {
|
|
1704
|
-
if (cleanup !== void 0) throw new Error("onCleanup can only be called once per fixture. Define separate fixtures if you need multiple cleanup functions.");
|
|
1705
|
-
cleanup = fn;
|
|
1706
|
-
};
|
|
1707
|
-
await use(await builderFn(ctx, { onCleanup }));
|
|
1708
|
-
if (cleanup) await cleanup();
|
|
1709
|
-
};
|
|
1710
|
-
configureProps(fixture, { original: builderFn });
|
|
1711
|
-
if (fixtureOptions) return { [fixtureName]: [fixture, fixtureOptions] };
|
|
1712
|
-
return { [fixtureName]: fixture };
|
|
1713
|
-
}
|
|
1714
|
-
if (fixtureOptions) return { [fixtureName]: [fixtureValue, fixtureOptions] };
|
|
1715
|
-
return { [fixtureName]: fixtureValue };
|
|
1716
|
-
}
|
|
1717
|
-
taskFn.override = function(fixturesOrName, optionsOrFn, maybeFn) {
|
|
1718
|
-
const userFixtures = parseBuilderFixtures(fixturesOrName, optionsOrFn, maybeFn);
|
|
1719
|
-
getChainableContext(this).getFixtures().override(runner, userFixtures);
|
|
1720
|
-
return this;
|
|
1721
|
-
};
|
|
1722
|
-
taskFn.scoped = function(fixtures) {
|
|
1723
|
-
console.warn(`test.scoped() is deprecated and will be removed in future versions. Please use test.override() instead.`);
|
|
1724
|
-
return this.override(fixtures);
|
|
1725
|
-
};
|
|
1726
|
-
taskFn.extend = function(fixturesOrName, optionsOrFn, maybeFn) {
|
|
1727
|
-
const userFixtures = parseBuilderFixtures(fixturesOrName, optionsOrFn, maybeFn);
|
|
1728
|
-
const fixtures = getChainableContext(this).getFixtures().extend(runner, userFixtures);
|
|
1729
|
-
const _test = createTest(function(name, optionsOrFn, optionsOrTest) {
|
|
1730
|
-
fn.call(this, formatName(name), optionsOrFn, optionsOrTest);
|
|
1731
|
-
});
|
|
1732
|
-
getChainableContext(_test).mergeContext({ fixtures });
|
|
1733
|
-
return _test;
|
|
1734
|
-
};
|
|
1735
|
-
taskFn.describe = suite;
|
|
1736
|
-
taskFn.suite = suite;
|
|
1737
|
-
taskFn.beforeEach = beforeEach;
|
|
1738
|
-
taskFn.afterEach = afterEach;
|
|
1739
|
-
taskFn.beforeAll = beforeAll;
|
|
1740
|
-
taskFn.afterAll = afterAll;
|
|
1741
|
-
taskFn.aroundEach = aroundEach;
|
|
1742
|
-
taskFn.aroundAll = aroundAll;
|
|
1743
|
-
return createChainable([
|
|
1744
|
-
"concurrent",
|
|
1745
|
-
"sequential",
|
|
1746
|
-
"skip",
|
|
1747
|
-
"only",
|
|
1748
|
-
"todo",
|
|
1749
|
-
"fails"
|
|
1750
|
-
], taskFn, { fixtures: new TestFixtures() });
|
|
1751
|
-
}
|
|
1752
|
-
function createTest(fn) {
|
|
1753
|
-
return createTaskCollector(fn);
|
|
1754
|
-
}
|
|
1755
|
-
function formatName(name) {
|
|
1756
|
-
return typeof name === "string" ? name : typeof name === "function" ? name.name || "<anonymous>" : String(name);
|
|
1757
|
-
}
|
|
1758
|
-
function formatTitle(template, items, idx) {
|
|
1759
|
-
if (template.includes("%#") || template.includes("%$")) template = template.replace(/%%/g, "__vitest_escaped_%__").replace(/%#/g, `${idx}`).replace(/%\$/g, `${idx + 1}`).replace(/__vitest_escaped_%__/g, "%%");
|
|
1760
|
-
const count = template.split("%").length - 1;
|
|
1761
|
-
if (template.includes("%f")) (template.match(/%f/g) || []).forEach((_, i) => {
|
|
1762
|
-
if (isNegativeNaN(items[i]) || Object.is(items[i], -0)) {
|
|
1763
|
-
let occurrence = 0;
|
|
1764
|
-
template = template.replace(/%f/g, (match) => {
|
|
1765
|
-
occurrence++;
|
|
1766
|
-
return occurrence === i + 1 ? "-%f" : match;
|
|
1767
|
-
});
|
|
1768
|
-
}
|
|
1769
|
-
});
|
|
1770
|
-
const isObjectItem = isObject(items[0]);
|
|
1771
|
-
function formatAttribute(s) {
|
|
1772
|
-
return s.replace(/\$([$\w.]+)/g, (_, key) => {
|
|
1773
|
-
const isArrayKey = /^\d+$/.test(key);
|
|
1774
|
-
if (!isObjectItem && !isArrayKey) return `$${key}`;
|
|
1775
|
-
const arrayElement = isArrayKey ? objectAttr(items, key) : void 0;
|
|
1776
|
-
return objDisplay(isObjectItem ? objectAttr(items[0], key, arrayElement) : arrayElement, { truncate: runner?.config?.chaiConfig?.truncateThreshold });
|
|
1777
|
-
});
|
|
1778
|
-
}
|
|
1779
|
-
let output = "";
|
|
1780
|
-
let i = 0;
|
|
1781
|
-
handleRegexMatch(template, formatRegExp, (match) => {
|
|
1782
|
-
if (i < count) output += format(match[0], items[i++]);
|
|
1783
|
-
else output += match[0];
|
|
1784
|
-
}, (nonMatch) => {
|
|
1785
|
-
output += formatAttribute(nonMatch);
|
|
1786
|
-
});
|
|
1787
|
-
return output;
|
|
1788
|
-
}
|
|
1789
|
-
function handleRegexMatch(input, regex, onMatch, onNonMatch) {
|
|
1790
|
-
let lastIndex = 0;
|
|
1791
|
-
for (const m of input.matchAll(regex)) {
|
|
1792
|
-
if (lastIndex < m.index) onNonMatch(input.slice(lastIndex, m.index));
|
|
1793
|
-
onMatch(m);
|
|
1794
|
-
lastIndex = m.index + m[0].length;
|
|
1795
|
-
}
|
|
1796
|
-
if (lastIndex < input.length) onNonMatch(input.slice(lastIndex));
|
|
1797
|
-
}
|
|
1798
|
-
function formatTemplateString(cases, args) {
|
|
1799
|
-
const header = cases.join("").trim().replace(/ /g, "").split("\n").map((i) => i.split("|"))[0];
|
|
1800
|
-
const res = [];
|
|
1801
|
-
for (let i = 0; i < Math.floor(args.length / header.length); i++) {
|
|
1802
|
-
const oneCase = {};
|
|
1803
|
-
for (let j = 0; j < header.length; j++) oneCase[header[j]] = args[i * header.length + j];
|
|
1804
|
-
res.push(oneCase);
|
|
1805
|
-
}
|
|
1806
|
-
return res;
|
|
1807
|
-
}
|
|
1808
|
-
const now$2 = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
|
|
1809
|
-
const collectorContext = {
|
|
1810
|
-
tasks: [],
|
|
1811
|
-
currentSuite: null
|
|
1812
|
-
};
|
|
1813
|
-
function collectTask(task) {
|
|
1814
|
-
collectorContext.currentSuite?.tasks.push(task);
|
|
1815
|
-
}
|
|
1816
|
-
async function runWithSuite(suite, fn) {
|
|
1817
|
-
const prev = collectorContext.currentSuite;
|
|
1818
|
-
collectorContext.currentSuite = suite;
|
|
1819
|
-
await fn();
|
|
1820
|
-
collectorContext.currentSuite = prev;
|
|
1821
|
-
}
|
|
1822
|
-
function withTimeout(fn, timeout, isHook = false, stackTraceError, onTimeout) {
|
|
1823
|
-
if (timeout <= 0 || timeout === Number.POSITIVE_INFINITY) return fn;
|
|
1824
|
-
const { setTimeout, clearTimeout } = getSafeTimers();
|
|
1825
|
-
return (function runWithTimeout(...args) {
|
|
1826
|
-
const startTime = now$2();
|
|
1827
|
-
const runner = getRunner();
|
|
1828
|
-
runner._currentTaskStartTime = startTime;
|
|
1829
|
-
runner._currentTaskTimeout = timeout;
|
|
1830
|
-
return new Promise((resolve_, reject_) => {
|
|
1831
|
-
const timer = setTimeout(() => {
|
|
1832
|
-
clearTimeout(timer);
|
|
1833
|
-
rejectTimeoutError();
|
|
1834
|
-
}, timeout);
|
|
1835
|
-
timer.unref?.();
|
|
1836
|
-
function rejectTimeoutError() {
|
|
1837
|
-
const error = makeTimeoutError(isHook, timeout, stackTraceError);
|
|
1838
|
-
onTimeout?.(args, error);
|
|
1839
|
-
reject_(error);
|
|
1840
|
-
}
|
|
1841
|
-
function resolve(result) {
|
|
1842
|
-
runner._currentTaskStartTime = void 0;
|
|
1843
|
-
runner._currentTaskTimeout = void 0;
|
|
1844
|
-
clearTimeout(timer);
|
|
1845
|
-
if (now$2() - startTime >= timeout) {
|
|
1846
|
-
rejectTimeoutError();
|
|
1847
|
-
return;
|
|
1848
|
-
}
|
|
1849
|
-
resolve_(result);
|
|
1850
|
-
}
|
|
1851
|
-
function reject(error) {
|
|
1852
|
-
runner._currentTaskStartTime = void 0;
|
|
1853
|
-
runner._currentTaskTimeout = void 0;
|
|
1854
|
-
clearTimeout(timer);
|
|
1855
|
-
reject_(error);
|
|
1856
|
-
}
|
|
1857
|
-
try {
|
|
1858
|
-
const result = fn(...args);
|
|
1859
|
-
if (typeof result === "object" && result != null && typeof result.then === "function") result.then(resolve, reject);
|
|
1860
|
-
else resolve(result);
|
|
1861
|
-
} catch (error) {
|
|
1862
|
-
reject(error);
|
|
1863
|
-
}
|
|
1864
|
-
});
|
|
1865
|
-
});
|
|
1866
|
-
}
|
|
1867
|
-
function withCancel(fn, signal) {
|
|
1868
|
-
return (function runWithCancel(...args) {
|
|
1869
|
-
return new Promise((resolve, reject) => {
|
|
1870
|
-
signal.addEventListener("abort", () => reject(signal.reason));
|
|
1871
|
-
try {
|
|
1872
|
-
const result = fn(...args);
|
|
1873
|
-
if (typeof result === "object" && result != null && typeof result.then === "function") result.then(resolve, reject);
|
|
1874
|
-
else resolve(result);
|
|
1875
|
-
} catch (error) {
|
|
1876
|
-
reject(error);
|
|
1877
|
-
}
|
|
1878
|
-
});
|
|
1879
|
-
});
|
|
1880
|
-
}
|
|
1881
|
-
const abortControllers = /* @__PURE__ */ new WeakMap();
|
|
1882
|
-
function abortIfTimeout([context], error) {
|
|
1883
|
-
if (context) abortContextSignal(context, error);
|
|
1884
|
-
}
|
|
1885
|
-
function abortContextSignal(context, error) {
|
|
1886
|
-
abortControllers.get(context)?.abort(error);
|
|
1887
|
-
}
|
|
1888
|
-
function createTestContext(test, runner) {
|
|
1889
|
-
const context = function() {
|
|
1890
|
-
throw new Error("done() callback is deprecated, use promise instead");
|
|
1891
|
-
};
|
|
1892
|
-
let abortController = abortControllers.get(context);
|
|
1893
|
-
if (!abortController) {
|
|
1894
|
-
abortController = new AbortController();
|
|
1895
|
-
abortControllers.set(context, abortController);
|
|
1896
|
-
}
|
|
1897
|
-
context.signal = abortController.signal;
|
|
1898
|
-
context.task = test;
|
|
1899
|
-
context.skip = (condition, note) => {
|
|
1900
|
-
if (condition === false) return;
|
|
1901
|
-
test.result ??= { state: "skip" };
|
|
1902
|
-
test.result.pending = true;
|
|
1903
|
-
throw new PendingError("test is skipped; abort execution", test, typeof condition === "string" ? condition : note);
|
|
1904
|
-
};
|
|
1905
|
-
context.annotate = ((message, type, attachment) => {
|
|
1906
|
-
if (test.result && test.result.state !== "run") throw new Error(`Cannot annotate tests outside of the test run. The test "${test.name}" finished running with the "${test.result.state}" state already.`);
|
|
1907
|
-
const annotation = {
|
|
1908
|
-
message,
|
|
1909
|
-
type: typeof type === "object" || type === void 0 ? "notice" : type
|
|
1910
|
-
};
|
|
1911
|
-
const annotationAttachment = typeof type === "object" ? type : attachment;
|
|
1912
|
-
if (annotationAttachment) {
|
|
1913
|
-
annotation.attachment = annotationAttachment;
|
|
1914
|
-
manageArtifactAttachment(annotation.attachment);
|
|
1915
|
-
}
|
|
1916
|
-
return recordAsyncOperation(test, recordArtifact(test, {
|
|
1917
|
-
type: "internal:annotation",
|
|
1918
|
-
annotation
|
|
1919
|
-
}).then(async ({ annotation }) => {
|
|
1920
|
-
if (!runner.onTestAnnotate) throw new Error(`Test runner doesn't support test annotations.`);
|
|
1921
|
-
await finishSendTasksUpdate(runner);
|
|
1922
|
-
const resolvedAnnotation = await runner.onTestAnnotate(test, annotation);
|
|
1923
|
-
test.annotations.push(resolvedAnnotation);
|
|
1924
|
-
return resolvedAnnotation;
|
|
1925
|
-
}));
|
|
1926
|
-
});
|
|
1927
|
-
context.onTestFailed = (handler, timeout) => {
|
|
1928
|
-
test.onFailed ||= [];
|
|
1929
|
-
test.onFailed.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, /* @__PURE__ */ new Error("STACK_TRACE_ERROR"), (_, error) => abortController.abort(error)));
|
|
1930
|
-
};
|
|
1931
|
-
context.onTestFinished = (handler, timeout) => {
|
|
1932
|
-
test.onFinished ||= [];
|
|
1933
|
-
test.onFinished.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, /* @__PURE__ */ new Error("STACK_TRACE_ERROR"), (_, error) => abortController.abort(error)));
|
|
1934
|
-
};
|
|
1935
|
-
return runner.extendTaskContext?.(context) || context;
|
|
1936
|
-
}
|
|
1937
|
-
function makeTimeoutError(isHook, timeout, stackTraceError) {
|
|
1938
|
-
const message = `${isHook ? "Hook" : "Test"} timed out in ${timeout}ms.\nIf this is a long-running ${isHook ? "hook" : "test"}, pass a timeout value as the last argument or configure it globally with "${isHook ? "hookTimeout" : "testTimeout"}".`;
|
|
1939
|
-
const error = new Error(message);
|
|
1940
|
-
if (stackTraceError?.stack) error.stack = stackTraceError.stack.replace(error.message, stackTraceError.message);
|
|
1941
|
-
return error;
|
|
1942
|
-
}
|
|
1943
|
-
async function runSetupFiles(config, files, runner) {
|
|
1944
|
-
if (config.sequence.setupFiles === "parallel") await Promise.all(files.map(async (fsPath) => {
|
|
1945
|
-
await runner.importFile(fsPath, "setup");
|
|
1946
|
-
}));
|
|
1947
|
-
else for (const fsPath of files) await runner.importFile(fsPath, "setup");
|
|
1948
|
-
}
|
|
1949
|
-
const now$1 = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
|
|
1950
|
-
async function collectTests(specs, runner) {
|
|
1951
|
-
const files = [];
|
|
1952
|
-
const config = runner.config;
|
|
1953
|
-
const $ = runner.trace;
|
|
1954
|
-
let defaultTagsFilter;
|
|
1955
|
-
for (const spec of specs) {
|
|
1956
|
-
const filepath = typeof spec === "string" ? spec : spec.filepath;
|
|
1957
|
-
await $("collect_spec", { "code.file.path": filepath }, async () => {
|
|
1958
|
-
runner._currentSpecification = typeof spec === "string" ? { filepath: spec } : spec;
|
|
1959
|
-
const testLocations = typeof spec === "string" ? void 0 : spec.testLocations;
|
|
1960
|
-
const testNamePattern = typeof spec === "string" ? void 0 : spec.testNamePattern;
|
|
1961
|
-
const testIds = typeof spec === "string" ? void 0 : spec.testIds;
|
|
1962
|
-
const testTagsFilter = typeof spec === "object" && spec.testTagsFilter ? createTagsFilter(spec.testTagsFilter, config.tags) : void 0;
|
|
1963
|
-
const fileTags = typeof spec === "string" ? [] : spec.fileTags || [];
|
|
1964
|
-
const file = createFileTask(filepath, config.root, config.name, runner.pool, runner.viteEnvironment);
|
|
1965
|
-
file.tags = fileTags;
|
|
1966
|
-
file.shuffle = config.sequence.shuffle;
|
|
1967
|
-
try {
|
|
1968
|
-
validateTags(runner.config, fileTags);
|
|
1969
|
-
runner.onCollectStart?.(file);
|
|
1970
|
-
clearCollectorContext(file, runner);
|
|
1971
|
-
const setupFiles = toArray(config.setupFiles);
|
|
1972
|
-
if (setupFiles.length) {
|
|
1973
|
-
const setupStart = now$1();
|
|
1974
|
-
await runSetupFiles(config, setupFiles, runner);
|
|
1975
|
-
file.setupDuration = now$1() - setupStart;
|
|
1976
|
-
} else file.setupDuration = 0;
|
|
1977
|
-
const collectStart = now$1();
|
|
1978
|
-
await runner.importFile(filepath, "collect");
|
|
1979
|
-
const durations = runner.getImportDurations?.();
|
|
1980
|
-
if (durations) file.importDurations = durations;
|
|
1981
|
-
const defaultTasks = await getDefaultSuite().collect(file);
|
|
1982
|
-
const fileHooks = createSuiteHooks();
|
|
1983
|
-
mergeHooks(fileHooks, getHooks(defaultTasks));
|
|
1984
|
-
for (const c of [...defaultTasks.tasks, ...collectorContext.tasks]) if (c.type === "test" || c.type === "suite") file.tasks.push(c);
|
|
1985
|
-
else if (c.type === "collector") {
|
|
1986
|
-
const suite = await c.collect(file);
|
|
1987
|
-
if (suite.name || suite.tasks.length) {
|
|
1988
|
-
mergeHooks(fileHooks, getHooks(suite));
|
|
1989
|
-
file.tasks.push(suite);
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
setHooks(file, fileHooks);
|
|
1993
|
-
file.collectDuration = now$1() - collectStart;
|
|
1994
|
-
} catch (e) {
|
|
1995
|
-
file.result = {
|
|
1996
|
-
state: "fail",
|
|
1997
|
-
errors: e instanceof AggregateError ? e.errors.map((e) => processError(e, runner.config.diffOptions)) : [processError(e, runner.config.diffOptions)]
|
|
1998
|
-
};
|
|
1999
|
-
const durations = runner.getImportDurations?.();
|
|
2000
|
-
if (durations) file.importDurations = durations;
|
|
2001
|
-
}
|
|
2002
|
-
calculateSuiteHash(file);
|
|
2003
|
-
const hasOnlyTasks = someTasksAreOnly(file);
|
|
2004
|
-
if (!testTagsFilter && !defaultTagsFilter && config.tagsFilter) defaultTagsFilter = createTagsFilter(config.tagsFilter, config.tags);
|
|
2005
|
-
interpretTaskModes(file, testNamePattern ?? config.testNamePattern, testLocations, testIds, testTagsFilter ?? defaultTagsFilter, hasOnlyTasks, false, config.allowOnly);
|
|
2006
|
-
if (file.mode === "queued") file.mode = "run";
|
|
2007
|
-
files.push(file);
|
|
2008
|
-
});
|
|
2009
|
-
}
|
|
2010
|
-
return files;
|
|
2011
|
-
}
|
|
2012
|
-
function mergeHooks(baseHooks, hooks) {
|
|
2013
|
-
for (const _key in hooks) {
|
|
2014
|
-
const key = _key;
|
|
2015
|
-
baseHooks[key].push(...hooks[key]);
|
|
2016
|
-
}
|
|
2017
|
-
return baseHooks;
|
|
2018
|
-
}
|
|
2019
|
-
const now = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
|
|
2020
|
-
const unixNow = Date.now;
|
|
2021
|
-
const { clearTimeout, setTimeout } = getSafeTimers();
|
|
2022
|
-
let limitMaxConcurrency;
|
|
2023
|
-
/**
|
|
2024
|
-
* Normalizes retry configuration to extract individual values.
|
|
2025
|
-
* Handles both number and object forms.
|
|
2026
|
-
*/
|
|
2027
|
-
function getRetryCount(retry) {
|
|
2028
|
-
if (retry === void 0) return 0;
|
|
2029
|
-
if (typeof retry === "number") return retry;
|
|
2030
|
-
return retry.count ?? 0;
|
|
2031
|
-
}
|
|
2032
|
-
function getRetryDelay(retry) {
|
|
2033
|
-
if (retry === void 0) return 0;
|
|
2034
|
-
if (typeof retry === "number") return 0;
|
|
2035
|
-
return retry.delay ?? 0;
|
|
2036
|
-
}
|
|
2037
|
-
function getRetryCondition(retry) {
|
|
2038
|
-
if (retry === void 0) return;
|
|
2039
|
-
if (typeof retry === "number") return;
|
|
2040
|
-
return retry.condition;
|
|
2041
|
-
}
|
|
2042
|
-
function updateSuiteHookState(task, name, state, runner) {
|
|
2043
|
-
if (!task.result) task.result = { state: "run" };
|
|
2044
|
-
if (!task.result.hooks) task.result.hooks = {};
|
|
2045
|
-
const suiteHooks = task.result.hooks;
|
|
2046
|
-
if (suiteHooks) {
|
|
2047
|
-
suiteHooks[name] = state;
|
|
2048
|
-
let event = state === "run" ? "before-hook-start" : "before-hook-end";
|
|
2049
|
-
if (name === "afterAll" || name === "afterEach") event = state === "run" ? "after-hook-start" : "after-hook-end";
|
|
2050
|
-
updateTask(event, task, runner);
|
|
2051
|
-
}
|
|
2052
|
-
}
|
|
2053
|
-
function getSuiteHooks(suite, name, sequence) {
|
|
2054
|
-
const hooks = getHooks(suite)[name];
|
|
2055
|
-
if (sequence === "stack" && (name === "afterAll" || name === "afterEach")) return hooks.slice().reverse();
|
|
2056
|
-
return hooks;
|
|
2057
|
-
}
|
|
2058
|
-
async function callTestHooks(runner, test, hooks, sequence) {
|
|
2059
|
-
if (sequence === "stack") hooks = hooks.slice().reverse();
|
|
2060
|
-
if (!hooks.length) return;
|
|
2061
|
-
const context = test.context;
|
|
2062
|
-
const onTestFailed = test.context.onTestFailed;
|
|
2063
|
-
const onTestFinished = test.context.onTestFinished;
|
|
2064
|
-
context.onTestFailed = () => {
|
|
2065
|
-
throw new Error(`Cannot call "onTestFailed" inside a test hook.`);
|
|
2066
|
-
};
|
|
2067
|
-
context.onTestFinished = () => {
|
|
2068
|
-
throw new Error(`Cannot call "onTestFinished" inside a test hook.`);
|
|
2069
|
-
};
|
|
2070
|
-
if (sequence === "parallel") try {
|
|
2071
|
-
await Promise.all(hooks.map((fn) => limitMaxConcurrency(() => fn(test.context))));
|
|
2072
|
-
} catch (e) {
|
|
2073
|
-
failTask(test.result, e, runner.config.diffOptions);
|
|
2074
|
-
}
|
|
2075
|
-
else for (const fn of hooks) try {
|
|
2076
|
-
await limitMaxConcurrency(() => fn(test.context));
|
|
2077
|
-
} catch (e) {
|
|
2078
|
-
failTask(test.result, e, runner.config.diffOptions);
|
|
2079
|
-
}
|
|
2080
|
-
context.onTestFailed = onTestFailed;
|
|
2081
|
-
context.onTestFinished = onTestFinished;
|
|
2082
|
-
}
|
|
2083
|
-
async function callSuiteHook(suite, currentTask, name, runner, args) {
|
|
2084
|
-
const sequence = runner.config.sequence.hooks;
|
|
2085
|
-
const callbacks = [];
|
|
2086
|
-
const parentSuite = "filepath" in suite ? null : suite.suite || suite.file;
|
|
2087
|
-
if (name === "beforeEach" && parentSuite) callbacks.push(...await callSuiteHook(parentSuite, currentTask, name, runner, args));
|
|
2088
|
-
const hooks = getSuiteHooks(suite, name, sequence);
|
|
2089
|
-
if (hooks.length > 0) updateSuiteHookState(currentTask, name, "run", runner);
|
|
2090
|
-
async function runHook(hook) {
|
|
2091
|
-
return limitMaxConcurrency(async () => {
|
|
2092
|
-
return getBeforeHookCleanupCallback(hook, await hook(...args), name === "beforeEach" ? args[0] : void 0);
|
|
2093
|
-
});
|
|
2094
|
-
}
|
|
2095
|
-
if (sequence === "parallel") callbacks.push(...await Promise.all(hooks.map((hook) => runHook(hook))));
|
|
2096
|
-
else for (const hook of hooks) callbacks.push(await runHook(hook));
|
|
2097
|
-
if (hooks.length > 0) updateSuiteHookState(currentTask, name, "pass", runner);
|
|
2098
|
-
if (name === "afterEach" && parentSuite) callbacks.push(...await callSuiteHook(parentSuite, currentTask, name, runner, args));
|
|
2099
|
-
return callbacks;
|
|
2100
|
-
}
|
|
2101
|
-
function getAroundEachHooks(suite) {
|
|
2102
|
-
const hooks = [];
|
|
2103
|
-
const parentSuite = "filepath" in suite ? null : suite.suite || suite.file;
|
|
2104
|
-
if (parentSuite) hooks.push(...getAroundEachHooks(parentSuite));
|
|
2105
|
-
hooks.push(...getHooks(suite).aroundEach);
|
|
2106
|
-
return hooks;
|
|
2107
|
-
}
|
|
2108
|
-
function getAroundAllHooks(suite) {
|
|
2109
|
-
return getHooks(suite).aroundAll;
|
|
2110
|
-
}
|
|
2111
|
-
function makeAroundHookTimeoutError(hookName, phase, timeout, stackTraceError) {
|
|
2112
|
-
const message = `The ${phase} phase of "${hookName}" hook timed out after ${timeout}ms.`;
|
|
2113
|
-
const error = new (phase === "setup" ? AroundHookSetupError : AroundHookTeardownError)(message);
|
|
2114
|
-
if (stackTraceError?.stack) error.stack = stackTraceError.stack.replace(stackTraceError.message, error.message);
|
|
2115
|
-
return error;
|
|
2116
|
-
}
|
|
2117
|
-
async function callAroundHooks(runInner, options) {
|
|
2118
|
-
const { hooks, hookName, callbackName, onTimeout, invokeHook } = options;
|
|
2119
|
-
if (!hooks.length) {
|
|
2120
|
-
await runInner();
|
|
2121
|
-
return;
|
|
2122
|
-
}
|
|
2123
|
-
const hookErrors = [];
|
|
2124
|
-
const createTimeoutPromise = (timeout, phase, stackTraceError) => {
|
|
2125
|
-
let timer;
|
|
2126
|
-
let timedout = false;
|
|
2127
|
-
const promise = new Promise((_, reject) => {
|
|
2128
|
-
if (timeout > 0 && timeout !== Number.POSITIVE_INFINITY) {
|
|
2129
|
-
timer = setTimeout(() => {
|
|
2130
|
-
timedout = true;
|
|
2131
|
-
const error = makeAroundHookTimeoutError(hookName, phase, timeout, stackTraceError);
|
|
2132
|
-
onTimeout?.(error);
|
|
2133
|
-
reject(error);
|
|
2134
|
-
}, timeout);
|
|
2135
|
-
timer.unref?.();
|
|
2136
|
-
}
|
|
2137
|
-
});
|
|
2138
|
-
const clear = () => {
|
|
2139
|
-
if (timer) {
|
|
2140
|
-
clearTimeout(timer);
|
|
2141
|
-
timer = void 0;
|
|
2142
|
-
}
|
|
2143
|
-
};
|
|
2144
|
-
return {
|
|
2145
|
-
promise,
|
|
2146
|
-
clear,
|
|
2147
|
-
isTimedOut: () => timedout
|
|
2148
|
-
};
|
|
2149
|
-
};
|
|
2150
|
-
const runNextHook = async (index) => {
|
|
2151
|
-
if (index >= hooks.length) return runInner();
|
|
2152
|
-
const hook = hooks[index];
|
|
2153
|
-
const timeout = getAroundHookTimeout(hook);
|
|
2154
|
-
const stackTraceError = getAroundHookStackTrace(hook);
|
|
2155
|
-
let useCalled = false;
|
|
2156
|
-
let setupTimeout;
|
|
2157
|
-
let teardownTimeout;
|
|
2158
|
-
let setupLimitConcurrencyRelease;
|
|
2159
|
-
let teardownLimitConcurrencyRelease;
|
|
2160
|
-
let resolveUseCalled;
|
|
2161
|
-
const useCalledPromise = new Promise((resolve) => {
|
|
2162
|
-
resolveUseCalled = resolve;
|
|
2163
|
-
});
|
|
2164
|
-
let resolveUseReturned;
|
|
2165
|
-
const useReturnedPromise = new Promise((resolve) => {
|
|
2166
|
-
resolveUseReturned = resolve;
|
|
2167
|
-
});
|
|
2168
|
-
let resolveHookComplete;
|
|
2169
|
-
let rejectHookComplete;
|
|
2170
|
-
const hookCompletePromise = new Promise((resolve, reject) => {
|
|
2171
|
-
resolveHookComplete = resolve;
|
|
2172
|
-
rejectHookComplete = reject;
|
|
2173
|
-
});
|
|
2174
|
-
const use = async () => {
|
|
2175
|
-
if (setupTimeout.isTimedOut()) throw new Error("__VITEST_INTERNAL_AROUND_HOOK_ABORT__");
|
|
2176
|
-
if (useCalled) throw new AroundHookMultipleCallsError(`The \`${callbackName}\` callback was called multiple times in the \`${hookName}\` hook. The callback can only be called once per hook.`);
|
|
2177
|
-
useCalled = true;
|
|
2178
|
-
resolveUseCalled();
|
|
2179
|
-
setupTimeout.clear();
|
|
2180
|
-
setupLimitConcurrencyRelease?.();
|
|
2181
|
-
await runNextHook(index + 1).catch((e) => hookErrors.push(e));
|
|
2182
|
-
teardownLimitConcurrencyRelease = await limitMaxConcurrency.acquire();
|
|
2183
|
-
teardownTimeout = createTimeoutPromise(timeout, "teardown", stackTraceError);
|
|
2184
|
-
resolveUseReturned();
|
|
2185
|
-
};
|
|
2186
|
-
setupLimitConcurrencyRelease = await limitMaxConcurrency.acquire();
|
|
2187
|
-
setupTimeout = createTimeoutPromise(timeout, "setup", stackTraceError);
|
|
2188
|
-
(async () => {
|
|
2189
|
-
try {
|
|
2190
|
-
await invokeHook(hook, use);
|
|
2191
|
-
if (!useCalled) throw new AroundHookSetupError(`The \`${callbackName}\` callback was not called in the \`${hookName}\` hook. Make sure to call \`${callbackName}\` to run the ${hookName === "aroundEach" ? "test" : "suite"}.`);
|
|
2192
|
-
resolveHookComplete();
|
|
2193
|
-
} catch (error) {
|
|
2194
|
-
rejectHookComplete(error);
|
|
2195
|
-
} finally {
|
|
2196
|
-
setupLimitConcurrencyRelease?.();
|
|
2197
|
-
teardownLimitConcurrencyRelease?.();
|
|
2198
|
-
}
|
|
2199
|
-
})();
|
|
2200
|
-
try {
|
|
2201
|
-
await Promise.race([
|
|
2202
|
-
useCalledPromise,
|
|
2203
|
-
hookCompletePromise,
|
|
2204
|
-
setupTimeout.promise
|
|
2205
|
-
]);
|
|
2206
|
-
} finally {
|
|
2207
|
-
setupLimitConcurrencyRelease?.();
|
|
2208
|
-
setupTimeout.clear();
|
|
2209
|
-
}
|
|
2210
|
-
await Promise.race([useReturnedPromise, hookCompletePromise]);
|
|
2211
|
-
try {
|
|
2212
|
-
await Promise.race([hookCompletePromise, teardownTimeout?.promise]);
|
|
2213
|
-
} finally {
|
|
2214
|
-
teardownLimitConcurrencyRelease?.();
|
|
2215
|
-
teardownTimeout?.clear();
|
|
2216
|
-
}
|
|
2217
|
-
};
|
|
2218
|
-
await runNextHook(0).catch((e) => hookErrors.push(e));
|
|
2219
|
-
if (hookErrors.length > 0) throw hookErrors;
|
|
2220
|
-
}
|
|
2221
|
-
async function callAroundAllHooks(suite, runSuiteInner) {
|
|
2222
|
-
await callAroundHooks(runSuiteInner, {
|
|
2223
|
-
hooks: getAroundAllHooks(suite),
|
|
2224
|
-
hookName: "aroundAll",
|
|
2225
|
-
callbackName: "runSuite()",
|
|
2226
|
-
invokeHook: (hook, use) => hook(use, suite)
|
|
2227
|
-
});
|
|
2228
|
-
}
|
|
2229
|
-
async function callAroundEachHooks(suite, test, runTest) {
|
|
2230
|
-
await callAroundHooks(() => runTest(getFixtureCleanupCount(test.context)), {
|
|
2231
|
-
hooks: getAroundEachHooks(suite),
|
|
2232
|
-
hookName: "aroundEach",
|
|
2233
|
-
callbackName: "runTest()",
|
|
2234
|
-
onTimeout: (error) => abortContextSignal(test.context, error),
|
|
2235
|
-
invokeHook: (hook, use) => hook(use, test.context, suite)
|
|
2236
|
-
});
|
|
2237
|
-
}
|
|
2238
|
-
const packs = /* @__PURE__ */ new Map();
|
|
2239
|
-
const eventsPacks = [];
|
|
2240
|
-
const pendingTasksUpdates = [];
|
|
2241
|
-
function sendTasksUpdate(runner) {
|
|
2242
|
-
if (packs.size) {
|
|
2243
|
-
const taskPacks = Array.from(packs).map(([id, task]) => {
|
|
2244
|
-
return [
|
|
2245
|
-
id,
|
|
2246
|
-
task[0],
|
|
2247
|
-
task[1]
|
|
2248
|
-
];
|
|
2249
|
-
});
|
|
2250
|
-
const p = runner.onTaskUpdate?.(taskPacks, eventsPacks);
|
|
2251
|
-
if (p) {
|
|
2252
|
-
pendingTasksUpdates.push(p);
|
|
2253
|
-
p.then(() => pendingTasksUpdates.splice(pendingTasksUpdates.indexOf(p), 1), () => {});
|
|
2254
|
-
}
|
|
2255
|
-
eventsPacks.length = 0;
|
|
2256
|
-
packs.clear();
|
|
2257
|
-
}
|
|
2258
|
-
}
|
|
2259
|
-
async function finishSendTasksUpdate(runner) {
|
|
2260
|
-
sendTasksUpdate(runner);
|
|
2261
|
-
await Promise.all(pendingTasksUpdates);
|
|
2262
|
-
}
|
|
2263
|
-
function throttle(fn, ms) {
|
|
2264
|
-
let last = 0;
|
|
2265
|
-
let pendingCall;
|
|
2266
|
-
return function call(...args) {
|
|
2267
|
-
const now = unixNow();
|
|
2268
|
-
if (now - last > ms) {
|
|
2269
|
-
last = now;
|
|
2270
|
-
clearTimeout(pendingCall);
|
|
2271
|
-
pendingCall = void 0;
|
|
2272
|
-
return fn.apply(this, args);
|
|
2273
|
-
}
|
|
2274
|
-
pendingCall ??= setTimeout(() => call.bind(this)(...args), ms);
|
|
2275
|
-
};
|
|
2276
|
-
}
|
|
2277
|
-
const sendTasksUpdateThrottled = throttle(sendTasksUpdate, 100);
|
|
2278
|
-
function updateTask(event, task, runner) {
|
|
2279
|
-
eventsPacks.push([
|
|
2280
|
-
task.id,
|
|
2281
|
-
event,
|
|
2282
|
-
void 0
|
|
2283
|
-
]);
|
|
2284
|
-
packs.set(task.id, [task.result, task.meta]);
|
|
2285
|
-
sendTasksUpdateThrottled(runner);
|
|
2286
|
-
}
|
|
2287
|
-
async function callCleanupHooks(runner, cleanups) {
|
|
2288
|
-
const sequence = runner.config.sequence.hooks;
|
|
2289
|
-
if (sequence === "stack") cleanups = cleanups.slice().reverse();
|
|
2290
|
-
if (sequence === "parallel") await Promise.all(cleanups.map(async (fn) => {
|
|
2291
|
-
if (typeof fn !== "function") return;
|
|
2292
|
-
await limitMaxConcurrency(() => fn());
|
|
2293
|
-
}));
|
|
2294
|
-
else for (const fn of cleanups) {
|
|
2295
|
-
if (typeof fn !== "function") continue;
|
|
2296
|
-
await limitMaxConcurrency(() => fn());
|
|
2297
|
-
}
|
|
2298
|
-
}
|
|
2299
|
-
/**
|
|
2300
|
-
* Determines if a test should be retried based on its retryCondition configuration
|
|
2301
|
-
*/
|
|
2302
|
-
function passesRetryCondition(test, errors) {
|
|
2303
|
-
const condition = getRetryCondition(test.retry);
|
|
2304
|
-
if (!errors || errors.length === 0) return false;
|
|
2305
|
-
if (!condition) return true;
|
|
2306
|
-
const error = errors[errors.length - 1];
|
|
2307
|
-
if (condition instanceof RegExp) return condition.test(error.message || "");
|
|
2308
|
-
else if (typeof condition === "function") return condition(error);
|
|
2309
|
-
return false;
|
|
2310
|
-
}
|
|
2311
|
-
async function runTest(test, runner) {
|
|
2312
|
-
await runner.onBeforeRunTask?.(test);
|
|
2313
|
-
if (test.mode !== "run" && test.mode !== "queued") {
|
|
2314
|
-
updateTask("test-prepare", test, runner);
|
|
2315
|
-
updateTask("test-finished", test, runner);
|
|
2316
|
-
return;
|
|
2317
|
-
}
|
|
2318
|
-
if (test.result?.state === "fail") {
|
|
2319
|
-
updateTask("test-failed-early", test, runner);
|
|
2320
|
-
return;
|
|
2321
|
-
}
|
|
2322
|
-
const start = now();
|
|
2323
|
-
test.result = {
|
|
2324
|
-
state: "run",
|
|
2325
|
-
startTime: unixNow(),
|
|
2326
|
-
retryCount: 0
|
|
2327
|
-
};
|
|
2328
|
-
updateTask("test-prepare", test, runner);
|
|
2329
|
-
const cleanupRunningTest = addRunningTest(test);
|
|
2330
|
-
setCurrentTest(test);
|
|
2331
|
-
const suite = test.suite || test.file;
|
|
2332
|
-
const $ = runner.trace;
|
|
2333
|
-
const repeats = test.repeats ?? 0;
|
|
2334
|
-
for (let repeatCount = 0; repeatCount <= repeats; repeatCount++) {
|
|
2335
|
-
const retry = getRetryCount(test.retry);
|
|
2336
|
-
for (let retryCount = 0; retryCount <= retry; retryCount++) {
|
|
2337
|
-
let beforeEachCleanups = [];
|
|
2338
|
-
await callAroundEachHooks(suite, test, async (fixtureCheckpoint) => {
|
|
2339
|
-
try {
|
|
2340
|
-
await runner.onBeforeTryTask?.(test, {
|
|
2341
|
-
retry: retryCount,
|
|
2342
|
-
repeats: repeatCount
|
|
2343
|
-
});
|
|
2344
|
-
test.result.repeatCount = repeatCount;
|
|
2345
|
-
beforeEachCleanups = await $("test.beforeEach", () => callSuiteHook(suite, test, "beforeEach", runner, [test.context, suite]));
|
|
2346
|
-
if (runner.runTask) await $("test.callback", () => limitMaxConcurrency(() => runner.runTask(test)));
|
|
2347
|
-
else {
|
|
2348
|
-
const fn = getFn(test);
|
|
2349
|
-
if (!fn) throw new Error("Test function is not found. Did you add it using `setFn`?");
|
|
2350
|
-
await $("test.callback", () => limitMaxConcurrency(() => fn()));
|
|
2351
|
-
}
|
|
2352
|
-
await runner.onAfterTryTask?.(test, {
|
|
2353
|
-
retry: retryCount,
|
|
2354
|
-
repeats: repeatCount
|
|
2355
|
-
});
|
|
2356
|
-
if (test.result.state !== "fail") test.result.state = "pass";
|
|
2357
|
-
} catch (e) {
|
|
2358
|
-
failTask(test.result, e, runner.config.diffOptions);
|
|
2359
|
-
}
|
|
2360
|
-
try {
|
|
2361
|
-
await runner.onTaskFinished?.(test);
|
|
2362
|
-
} catch (e) {
|
|
2363
|
-
failTask(test.result, e, runner.config.diffOptions);
|
|
2364
|
-
}
|
|
2365
|
-
try {
|
|
2366
|
-
await $("test.afterEach", () => callSuiteHook(suite, test, "afterEach", runner, [test.context, suite]));
|
|
2367
|
-
if (beforeEachCleanups.length) await $("test.cleanup", () => callCleanupHooks(runner, beforeEachCleanups));
|
|
2368
|
-
await callFixtureCleanupFrom(test.context, fixtureCheckpoint);
|
|
2369
|
-
} catch (e) {
|
|
2370
|
-
failTask(test.result, e, runner.config.diffOptions);
|
|
2371
|
-
}
|
|
2372
|
-
if (test.onFinished?.length) await $("test.onFinished", () => callTestHooks(runner, test, test.onFinished, "stack"));
|
|
2373
|
-
if (test.result.state === "fail" && test.onFailed?.length) await $("test.onFailed", () => callTestHooks(runner, test, test.onFailed, runner.config.sequence.hooks));
|
|
2374
|
-
test.onFailed = void 0;
|
|
2375
|
-
test.onFinished = void 0;
|
|
2376
|
-
await runner.onAfterRetryTask?.(test, {
|
|
2377
|
-
retry: retryCount,
|
|
2378
|
-
repeats: repeatCount
|
|
2379
|
-
});
|
|
2380
|
-
}).catch((error) => {
|
|
2381
|
-
failTask(test.result, error, runner.config.diffOptions);
|
|
2382
|
-
});
|
|
2383
|
-
try {
|
|
2384
|
-
await callFixtureCleanup(test.context);
|
|
2385
|
-
} catch (e) {
|
|
2386
|
-
failTask(test.result, e, runner.config.diffOptions);
|
|
2387
|
-
}
|
|
2388
|
-
if (test.result?.pending || test.result?.state === "skip") {
|
|
2389
|
-
test.mode = "skip";
|
|
2390
|
-
test.result = {
|
|
2391
|
-
state: "skip",
|
|
2392
|
-
note: test.result?.note,
|
|
2393
|
-
pending: true,
|
|
2394
|
-
duration: now() - start
|
|
2395
|
-
};
|
|
2396
|
-
updateTask("test-finished", test, runner);
|
|
2397
|
-
setCurrentTest(void 0);
|
|
2398
|
-
cleanupRunningTest();
|
|
2399
|
-
return;
|
|
2400
|
-
}
|
|
2401
|
-
if (test.result.state === "pass") break;
|
|
2402
|
-
if (retryCount < retry) {
|
|
2403
|
-
if (!passesRetryCondition(test, test.result.errors)) break;
|
|
2404
|
-
test.result.state = "run";
|
|
2405
|
-
test.result.retryCount = (test.result.retryCount ?? 0) + 1;
|
|
2406
|
-
const delay = getRetryDelay(test.retry);
|
|
2407
|
-
if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2408
|
-
}
|
|
2409
|
-
updateTask("test-retried", test, runner);
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
if (test.fails) if (test.result.state === "pass") {
|
|
2413
|
-
const error = processError(/* @__PURE__ */ new Error("Expect test to fail"));
|
|
2414
|
-
test.result.state = "fail";
|
|
2415
|
-
test.result.errors = [error];
|
|
2416
|
-
} else {
|
|
2417
|
-
test.result.state = "pass";
|
|
2418
|
-
test.result.errors = void 0;
|
|
2419
|
-
}
|
|
2420
|
-
cleanupRunningTest();
|
|
2421
|
-
setCurrentTest(void 0);
|
|
2422
|
-
test.result.duration = now() - start;
|
|
2423
|
-
await runner.onAfterRunTask?.(test);
|
|
2424
|
-
updateTask("test-finished", test, runner);
|
|
2425
|
-
}
|
|
2426
|
-
function failTask(result, err, diffOptions) {
|
|
2427
|
-
if (err instanceof PendingError) {
|
|
2428
|
-
result.state = "skip";
|
|
2429
|
-
result.note = err.note;
|
|
2430
|
-
result.pending = true;
|
|
2431
|
-
return;
|
|
2432
|
-
}
|
|
2433
|
-
if (err instanceof TestRunAbortError) {
|
|
2434
|
-
result.state = "skip";
|
|
2435
|
-
result.note = err.message;
|
|
2436
|
-
return;
|
|
2437
|
-
}
|
|
2438
|
-
result.state = "fail";
|
|
2439
|
-
const errors = Array.isArray(err) ? err : [err];
|
|
2440
|
-
for (const e of errors) {
|
|
2441
|
-
const errors = e instanceof AggregateError ? e.errors.map((e) => processError(e, diffOptions)) : [processError(e, diffOptions)];
|
|
2442
|
-
result.errors ??= [];
|
|
2443
|
-
result.errors.push(...errors);
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2446
|
-
function markTasksAsSkipped(suite, runner) {
|
|
2447
|
-
suite.tasks.forEach((t) => {
|
|
2448
|
-
t.mode = "skip";
|
|
2449
|
-
t.result = {
|
|
2450
|
-
...t.result,
|
|
2451
|
-
state: "skip"
|
|
2452
|
-
};
|
|
2453
|
-
updateTask("test-finished", t, runner);
|
|
2454
|
-
if (t.type === "suite") markTasksAsSkipped(t, runner);
|
|
2455
|
-
});
|
|
2456
|
-
}
|
|
2457
|
-
function markPendingTasksAsSkipped(suite, runner, note) {
|
|
2458
|
-
suite.tasks.forEach((t) => {
|
|
2459
|
-
if (!t.result || t.result.state === "run") {
|
|
2460
|
-
t.mode = "skip";
|
|
2461
|
-
t.result = {
|
|
2462
|
-
...t.result,
|
|
2463
|
-
state: "skip",
|
|
2464
|
-
note
|
|
2465
|
-
};
|
|
2466
|
-
updateTask("test-cancel", t, runner);
|
|
2467
|
-
}
|
|
2468
|
-
if (t.type === "suite") markPendingTasksAsSkipped(t, runner, note);
|
|
2469
|
-
});
|
|
2470
|
-
}
|
|
2471
|
-
async function runSuite(suite, runner) {
|
|
2472
|
-
await runner.onBeforeRunSuite?.(suite);
|
|
2473
|
-
if (suite.result?.state === "fail") {
|
|
2474
|
-
markTasksAsSkipped(suite, runner);
|
|
2475
|
-
updateTask("suite-failed-early", suite, runner);
|
|
2476
|
-
return;
|
|
2477
|
-
}
|
|
2478
|
-
const start = now();
|
|
2479
|
-
const mode = suite.mode;
|
|
2480
|
-
suite.result = {
|
|
2481
|
-
state: mode === "skip" || mode === "todo" ? mode : "run",
|
|
2482
|
-
startTime: unixNow()
|
|
2483
|
-
};
|
|
2484
|
-
const $ = runner.trace;
|
|
2485
|
-
updateTask("suite-prepare", suite, runner);
|
|
2486
|
-
let beforeAllCleanups = [];
|
|
2487
|
-
if (suite.mode === "skip") {
|
|
2488
|
-
suite.result.state = "skip";
|
|
2489
|
-
updateTask("suite-finished", suite, runner);
|
|
2490
|
-
} else if (suite.mode === "todo") {
|
|
2491
|
-
suite.result.state = "todo";
|
|
2492
|
-
updateTask("suite-finished", suite, runner);
|
|
2493
|
-
} else {
|
|
2494
|
-
let suiteRan = false;
|
|
2495
|
-
try {
|
|
2496
|
-
await callAroundAllHooks(suite, async () => {
|
|
2497
|
-
suiteRan = true;
|
|
2498
|
-
try {
|
|
2499
|
-
try {
|
|
2500
|
-
beforeAllCleanups = await $("suite.beforeAll", () => callSuiteHook(suite, suite, "beforeAll", runner, [suite]));
|
|
2501
|
-
} catch (e) {
|
|
2502
|
-
failTask(suite.result, e, runner.config.diffOptions);
|
|
2503
|
-
markTasksAsSkipped(suite, runner);
|
|
2504
|
-
return;
|
|
2505
|
-
}
|
|
2506
|
-
if (runner.runSuite) await runner.runSuite(suite);
|
|
2507
|
-
else for (let tasksGroup of partitionSuiteChildren(suite)) if (tasksGroup[0].concurrent === true) await Promise.all(tasksGroup.map((c) => runSuiteChild(c, runner)));
|
|
2508
|
-
else {
|
|
2509
|
-
const { sequence } = runner.config;
|
|
2510
|
-
if (suite.shuffle) tasksGroup = shuffle([tasksGroup.filter((group) => group.type === "suite"), tasksGroup.filter((group) => group.type === "test")], sequence.seed).flatMap((group) => shuffle(group, sequence.seed));
|
|
2511
|
-
for (const c of tasksGroup) await runSuiteChild(c, runner);
|
|
2512
|
-
}
|
|
2513
|
-
} finally {
|
|
2514
|
-
try {
|
|
2515
|
-
await $("suite.afterAll", () => callSuiteHook(suite, suite, "afterAll", runner, [suite]));
|
|
2516
|
-
if (beforeAllCleanups.length) await $("suite.cleanup", () => callCleanupHooks(runner, beforeAllCleanups));
|
|
2517
|
-
if (suite.file === suite) {
|
|
2518
|
-
const contexts = TestFixtures.getFileContexts(suite.file);
|
|
2519
|
-
await Promise.all(contexts.map((context) => callFixtureCleanup(context)));
|
|
2520
|
-
}
|
|
2521
|
-
} catch (e) {
|
|
2522
|
-
failTask(suite.result, e, runner.config.diffOptions);
|
|
2523
|
-
}
|
|
2524
|
-
}
|
|
2525
|
-
});
|
|
2526
|
-
} catch (e) {
|
|
2527
|
-
if (!suiteRan) markTasksAsSkipped(suite, runner);
|
|
2528
|
-
failTask(suite.result, e, runner.config.diffOptions);
|
|
2529
|
-
}
|
|
2530
|
-
if (suite.mode === "run" || suite.mode === "queued") if (!runner.config.passWithNoTests && !hasTests(suite)) {
|
|
2531
|
-
suite.result.state = "fail";
|
|
2532
|
-
if (!suite.result.errors?.length) {
|
|
2533
|
-
const error = processError(/* @__PURE__ */ new Error(`No test found in suite ${suite.name}`));
|
|
2534
|
-
suite.result.errors = [error];
|
|
2535
|
-
}
|
|
2536
|
-
} else if (hasFailed(suite)) suite.result.state = "fail";
|
|
2537
|
-
else suite.result.state = "pass";
|
|
2538
|
-
suite.result.duration = now() - start;
|
|
2539
|
-
await runner.onAfterRunSuite?.(suite);
|
|
2540
|
-
updateTask("suite-finished", suite, runner);
|
|
2541
|
-
}
|
|
2542
|
-
}
|
|
2543
|
-
async function runSuiteChild(c, runner) {
|
|
2544
|
-
const $ = runner.trace;
|
|
2545
|
-
if (c.type === "test") return $("run.test", {
|
|
2546
|
-
"vitest.test.id": c.id,
|
|
2547
|
-
"vitest.test.name": c.name,
|
|
2548
|
-
"vitest.test.mode": c.mode,
|
|
2549
|
-
"vitest.test.timeout": c.timeout,
|
|
2550
|
-
"code.file.path": c.file.filepath,
|
|
2551
|
-
"code.line.number": c.location?.line,
|
|
2552
|
-
"code.column.number": c.location?.column
|
|
2553
|
-
}, () => runTest(c, runner));
|
|
2554
|
-
else if (c.type === "suite") return $("run.suite", {
|
|
2555
|
-
"vitest.suite.id": c.id,
|
|
2556
|
-
"vitest.suite.name": c.name,
|
|
2557
|
-
"vitest.suite.mode": c.mode,
|
|
2558
|
-
"code.file.path": c.file.filepath,
|
|
2559
|
-
"code.line.number": c.location?.line,
|
|
2560
|
-
"code.column.number": c.location?.column
|
|
2561
|
-
}, () => runSuite(c, runner));
|
|
2562
|
-
}
|
|
2563
|
-
async function runFiles(files, runner) {
|
|
2564
|
-
limitMaxConcurrency ??= limitConcurrency(runner.config.maxConcurrency);
|
|
2565
|
-
for (const file of files) {
|
|
2566
|
-
if (!file.tasks.length && !runner.config.passWithNoTests) {
|
|
2567
|
-
if (!file.result?.errors?.length) file.result = {
|
|
2568
|
-
state: "fail",
|
|
2569
|
-
errors: [processError(/* @__PURE__ */ new Error(`No test suite found in file ${file.filepath}`))]
|
|
2570
|
-
};
|
|
2571
|
-
}
|
|
2572
|
-
await runner.trace("run.spec", {
|
|
2573
|
-
"code.file.path": file.filepath,
|
|
2574
|
-
"vitest.suite.tasks.length": file.tasks.length
|
|
2575
|
-
}, () => runSuite(file, runner));
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
const workerRunners = /* @__PURE__ */ new WeakSet();
|
|
2579
|
-
function defaultTrace(_, attributes, cb) {
|
|
2580
|
-
if (typeof attributes === "function") return attributes();
|
|
2581
|
-
return cb();
|
|
2582
|
-
}
|
|
2583
|
-
async function startTests(specs, runner) {
|
|
2584
|
-
runner.trace ??= defaultTrace;
|
|
2585
|
-
const cancel = runner.cancel?.bind(runner);
|
|
2586
|
-
runner.cancel = (reason) => {
|
|
2587
|
-
const error = new TestRunAbortError("The test run was aborted by the user.", reason);
|
|
2588
|
-
getRunningTests().forEach((test) => {
|
|
2589
|
-
abortContextSignal(test.context, error);
|
|
2590
|
-
markPendingTasksAsSkipped(test.file, runner, error.message);
|
|
2591
|
-
});
|
|
2592
|
-
return cancel?.(reason);
|
|
2593
|
-
};
|
|
2594
|
-
if (!workerRunners.has(runner)) {
|
|
2595
|
-
runner.onCleanupWorkerContext?.(async () => {
|
|
2596
|
-
await Promise.all([...TestFixtures.getWorkerContexts()].map((context) => callFixtureCleanup(context))).finally(() => {
|
|
2597
|
-
TestFixtures.clearDefinitions();
|
|
2598
|
-
});
|
|
2599
|
-
});
|
|
2600
|
-
workerRunners.add(runner);
|
|
2601
|
-
}
|
|
2602
|
-
try {
|
|
2603
|
-
const paths = specs.map((f) => typeof f === "string" ? f : f.filepath);
|
|
2604
|
-
await runner.onBeforeCollect?.(paths);
|
|
2605
|
-
const files = await collectTests(specs, runner);
|
|
2606
|
-
await runner.onCollected?.(files);
|
|
2607
|
-
await runner.onBeforeRunFiles?.(files);
|
|
2608
|
-
await runFiles(files, runner);
|
|
2609
|
-
await runner.onAfterRunFiles?.(files);
|
|
2610
|
-
await finishSendTasksUpdate(runner);
|
|
2611
|
-
return files;
|
|
2612
|
-
} finally {
|
|
2613
|
-
runner.cancel = cancel;
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2616
|
-
async function publicCollect(specs, runner) {
|
|
2617
|
-
runner.trace ??= defaultTrace;
|
|
2618
|
-
const paths = specs.map((f) => typeof f === "string" ? f : f.filepath);
|
|
2619
|
-
await runner.onBeforeCollect?.(paths);
|
|
2620
|
-
const files = await collectTests(specs, runner);
|
|
2621
|
-
await runner.onCollected?.(files);
|
|
2622
|
-
return files;
|
|
2623
|
-
}
|
|
2624
|
-
/**
|
|
2625
|
-
* @experimental
|
|
2626
|
-
* @advanced
|
|
2627
|
-
*
|
|
2628
|
-
* Records a custom test artifact during test execution.
|
|
2629
|
-
*
|
|
2630
|
-
* This function allows you to attach structured data, files, or metadata to a test.
|
|
2631
|
-
*
|
|
2632
|
-
* Vitest automatically injects the source location where the artifact was created and manages any attachments you include.
|
|
2633
|
-
*
|
|
2634
|
-
* **Note:** artifacts must be recorded before the task is reported. Any artifacts recorded after that will not be included in the task.
|
|
2635
|
-
*
|
|
2636
|
-
* @param task - The test task context, typically accessed via `this.task` in custom matchers or `context.task` in tests
|
|
2637
|
-
* @param artifact - The artifact to record. Must extend {@linkcode TestArtifactBase}
|
|
2638
|
-
*
|
|
2639
|
-
* @returns A promise that resolves to the recorded artifact with location injected
|
|
2640
|
-
*
|
|
2641
|
-
* @throws {Error} If the test runner doesn't support artifacts
|
|
2642
|
-
*
|
|
2643
|
-
* @example
|
|
2644
|
-
* ```ts
|
|
2645
|
-
* // In a custom assertion
|
|
2646
|
-
* async function toHaveValidSchema(this: MatcherState, actual: unknown) {
|
|
2647
|
-
* const validation = validateSchema(actual)
|
|
2648
|
-
*
|
|
2649
|
-
* await recordArtifact(this.task, {
|
|
2650
|
-
* type: 'my-plugin:schema-validation',
|
|
2651
|
-
* passed: validation.valid,
|
|
2652
|
-
* errors: validation.errors,
|
|
2653
|
-
* })
|
|
2654
|
-
*
|
|
2655
|
-
* return { pass: validation.valid, message: () => '...' }
|
|
2656
|
-
* }
|
|
2657
|
-
* ```
|
|
2658
|
-
*/
|
|
2659
|
-
async function recordArtifact(task, artifact) {
|
|
2660
|
-
const runner = getRunner();
|
|
2661
|
-
const stack = findTestFileStackTrace(task.file.filepath, (/* @__PURE__ */ new Error("STACK_TRACE")).stack);
|
|
2662
|
-
if (stack) {
|
|
2663
|
-
artifact.location = {
|
|
2664
|
-
file: stack.file,
|
|
2665
|
-
line: stack.line,
|
|
2666
|
-
column: stack.column
|
|
2667
|
-
};
|
|
2668
|
-
if (artifact.type === "internal:annotation") artifact.annotation.location = artifact.location;
|
|
2669
|
-
}
|
|
2670
|
-
if (Array.isArray(artifact.attachments)) for (const attachment of artifact.attachments) manageArtifactAttachment(attachment);
|
|
2671
|
-
if (artifact.type === "internal:annotation") return artifact;
|
|
2672
|
-
if (!runner.onTestArtifactRecord) throw new Error(`Test runner doesn't support test artifacts.`);
|
|
2673
|
-
await finishSendTasksUpdate(runner);
|
|
2674
|
-
const resolvedArtifact = await runner.onTestArtifactRecord(task, artifact);
|
|
2675
|
-
task.artifacts.push(resolvedArtifact);
|
|
2676
|
-
return resolvedArtifact;
|
|
2677
|
-
}
|
|
2678
|
-
const table = [];
|
|
2679
|
-
for (let i = 65; i < 91; i++) table.push(String.fromCharCode(i));
|
|
2680
|
-
for (let i = 97; i < 123; i++) table.push(String.fromCharCode(i));
|
|
2681
|
-
for (let i = 0; i < 10; i++) table.push(i.toString(10));
|
|
2682
|
-
table.push("+", "/");
|
|
2683
|
-
function encodeUint8Array(bytes) {
|
|
2684
|
-
let base64 = "";
|
|
2685
|
-
const len = bytes.byteLength;
|
|
2686
|
-
for (let i = 0; i < len; i += 3) if (len === i + 1) {
|
|
2687
|
-
const a = (bytes[i] & 252) >> 2;
|
|
2688
|
-
const b = (bytes[i] & 3) << 4;
|
|
2689
|
-
base64 += table[a];
|
|
2690
|
-
base64 += table[b];
|
|
2691
|
-
base64 += "==";
|
|
2692
|
-
} else if (len === i + 2) {
|
|
2693
|
-
const a = (bytes[i] & 252) >> 2;
|
|
2694
|
-
const b = (bytes[i] & 3) << 4 | (bytes[i + 1] & 240) >> 4;
|
|
2695
|
-
const c = (bytes[i + 1] & 15) << 2;
|
|
2696
|
-
base64 += table[a];
|
|
2697
|
-
base64 += table[b];
|
|
2698
|
-
base64 += table[c];
|
|
2699
|
-
base64 += "=";
|
|
2700
|
-
} else {
|
|
2701
|
-
const a = (bytes[i] & 252) >> 2;
|
|
2702
|
-
const b = (bytes[i] & 3) << 4 | (bytes[i + 1] & 240) >> 4;
|
|
2703
|
-
const c = (bytes[i + 1] & 15) << 2 | (bytes[i + 2] & 192) >> 6;
|
|
2704
|
-
const d = bytes[i + 2] & 63;
|
|
2705
|
-
base64 += table[a];
|
|
2706
|
-
base64 += table[b];
|
|
2707
|
-
base64 += table[c];
|
|
2708
|
-
base64 += table[d];
|
|
2709
|
-
}
|
|
2710
|
-
return base64;
|
|
2711
|
-
}
|
|
2712
|
-
/**
|
|
2713
|
-
* Records an async operation associated with a test task.
|
|
2714
|
-
*
|
|
2715
|
-
* This function tracks promises that should be awaited before a test completes.
|
|
2716
|
-
* The promise is automatically removed from the test's promise list once it settles.
|
|
2717
|
-
*/
|
|
2718
|
-
function recordAsyncOperation(test, promise) {
|
|
2719
|
-
promise = promise.finally(() => {
|
|
2720
|
-
if (!test.promises) return;
|
|
2721
|
-
const index = test.promises.indexOf(promise);
|
|
2722
|
-
if (index !== -1) test.promises.splice(index, 1);
|
|
2723
|
-
});
|
|
2724
|
-
if (!test.promises) test.promises = [];
|
|
2725
|
-
test.promises.push(promise);
|
|
2726
|
-
return promise;
|
|
2727
|
-
}
|
|
2728
|
-
/**
|
|
2729
|
-
* Validates and prepares a test attachment for serialization.
|
|
2730
|
-
*
|
|
2731
|
-
* This function ensures attachments have either `body` or `path` set (but not both), and converts `Uint8Array` bodies to base64-encoded strings for easier serialization.
|
|
2732
|
-
*
|
|
2733
|
-
* @param attachment - The attachment to validate and prepare
|
|
2734
|
-
*
|
|
2735
|
-
* @throws {TypeError} If neither `body` nor `path` is provided
|
|
2736
|
-
* @throws {TypeError} If both `body` and `path` are provided
|
|
2737
|
-
*/
|
|
2738
|
-
function manageArtifactAttachment(attachment) {
|
|
2739
|
-
if (attachment.body == null && !attachment.path) throw new TypeError(`Test attachment requires "body" or "path" to be set. Both are missing.`);
|
|
2740
|
-
if (attachment.body && attachment.path) throw new TypeError(`Test attachment requires only one of "body" or "path" to be set. Both are specified.`);
|
|
2741
|
-
if (attachment.body instanceof Uint8Array) attachment.body = encodeUint8Array(attachment.body);
|
|
2742
|
-
}
|
|
2743
|
-
//#endregion
|
|
2744
|
-
export { afterAll, afterEach, aroundAll, aroundEach, beforeAll, beforeEach, calculateSuiteHash, createChainable, createFileTask, createTagsFilter, createTaskCollector, createTaskName, describe, findTestFileStackTrace, generateFileHash, generateHash, getCurrentSuite, getCurrentTest, getFn, getFullName, getHooks, getNames, getSuites, getTasks, getTestName, getTests, hasFailed, hasTests, interpretTaskModes, isTestCase, it, limitConcurrency, matchesTags, onTestFailed, onTestFinished, partitionSuiteChildren, publicCollect, recordArtifact, setFn, setHooks, someTasksAreOnly, startTests, suite, test, updateTask, validateTags };
|
|
2745
|
-
|
|
2746
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2h1bmstYXJ0aWZhY3QuanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vbm9kZV9tb2R1bGVzLy5wbnBtL0B2aXRlc3QrcnVubmVyQDQuMS4yL25vZGVfbW9kdWxlcy9Adml0ZXN0L3J1bm5lci9kaXN0L2NodW5rLWFydGlmYWN0LmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHByb2Nlc3NFcnJvciB9IGZyb20gJ0B2aXRlc3QvdXRpbHMvZXJyb3InO1xuaW1wb3J0IHsgaXNPYmplY3QsIGZpbHRlck91dENvbW1lbnRzLCBvcmRpbmFsLCBjcmVhdGVEZWZlciwgYXNzZXJ0VHlwZXMsIHRvQXJyYXksIGlzTmVnYXRpdmVOYU4sIHVuaXF1ZSwgb2JqZWN0QXR0ciwgc2h1ZmZsZSB9IGZyb20gJ0B2aXRlc3QvdXRpbHMvaGVscGVycyc7XG5pbXBvcnQgeyBnZXRTYWZlVGltZXJzIH0gZnJvbSAnQHZpdGVzdC91dGlscy90aW1lcnMnO1xuaW1wb3J0IHsgZm9ybWF0LCBmb3JtYXRSZWdFeHAsIG9iakRpc3BsYXkgfSBmcm9tICdAdml0ZXN0L3V0aWxzL2Rpc3BsYXknO1xuaW1wb3J0IHsgcGFyc2VTaW5nbGVTdGFjayB9IGZyb20gJ0B2aXRlc3QvdXRpbHMvc291cmNlLW1hcCc7XG5pbXBvcnQgeyByZWxhdGl2ZSB9IGZyb20gJ3BhdGhlJztcblxuY2xhc3MgUGVuZGluZ0Vycm9yIGV4dGVuZHMgRXJyb3Ige1xuXHRjb2RlID0gXCJWSVRFU1RfUEVORElOR1wiO1xuXHR0YXNrSWQ7XG5cdGNvbnN0cnVjdG9yKG1lc3NhZ2UsIHRhc2ssIG5vdGUpIHtcblx0XHRzdXBlcihtZXNzYWdlKTtcblx0XHR0aGlzLm1lc3NhZ2UgPSBtZXNzYWdlO1xuXHRcdHRoaXMubm90ZSA9IG5vdGU7XG5cdFx0dGhpcy50YXNrSWQgPSB0YXNrLmlkO1xuXHR9XG59XG5jbGFzcyBUZXN0UnVuQWJvcnRFcnJvciBleHRlbmRzIEVycm9yIHtcblx0bmFtZSA9IFwiVGVzdFJ1bkFib3J0RXJyb3JcIjtcblx0cmVhc29uO1xuXHRjb25zdHJ1Y3RvcihtZXNzYWdlLCByZWFzb24pIHtcblx0XHRzdXBlcihtZXNzYWdlKTtcblx0XHR0aGlzLnJlYXNvbiA9IHJlYXNvbjtcblx0fVxufVxuY2xhc3MgRml4dHVyZURlcGVuZGVuY3lFcnJvciBleHRlbmRzIEVycm9yIHtcblx0bmFtZSA9IFwiRml4dHVyZURlcGVuZGVuY3lFcnJvclwiO1xufVxuY2xhc3MgRml4dHVyZUFjY2Vzc0Vycm9yIGV4dGVuZHMgRXJyb3Ige1xuXHRuYW1lID0gXCJGaXh0dXJlQWNjZXNzRXJyb3JcIjtcbn1cbmNsYXNzIEZpeHR1cmVQYXJzZUVycm9yIGV4dGVuZHMgRXJyb3Ige1xuXHRuYW1lID0gXCJGaXh0dXJlUGFyc2VFcnJvclwiO1xufVxuY2xhc3MgQXJvdW5kSG9va1NldHVwRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG5cdG5hbWUgPSBcIkFyb3VuZEhvb2tTZXR1cEVycm9yXCI7XG59XG5jbGFzcyBBcm91bmRIb29rVGVhcmRvd25FcnJvciBleHRlbmRzIEVycm9yIHtcblx0bmFtZSA9IFwiQXJvdW5kSG9va1RlYXJkb3duRXJyb3JcIjtcbn1cbmNsYXNzIEFyb3VuZEhvb2tNdWx0aXBsZUNhbGxzRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG5cdG5hbWUgPSBcIkFyb3VuZEhvb2tNdWx0aXBsZUNhbGxzRXJyb3JcIjtcbn1cblxuLy8gdXNlIFdlYWtNYXAgaGVyZSB0byBtYWtlIHRoZSBUZXN0IGFuZCBTdWl0ZSBvYmplY3Qgc2VyaWFsaXphYmxlXG5jb25zdCBmbk1hcCA9IG5ldyBXZWFrTWFwKCk7XG5jb25zdCB0ZXN0Rml4dHVyZU1hcCA9IG5ldyBXZWFrTWFwKCk7XG5jb25zdCBob29rc01hcCA9IG5ldyBXZWFrTWFwKCk7XG5mdW5jdGlvbiBzZXRGbihrZXksIGZuKSB7XG5cdGZuTWFwLnNldChrZXksIGZuKTtcbn1cbmZ1bmN0aW9uIGdldEZuKGtleSkge1xuXHRyZXR1cm4gZm5NYXAuZ2V0KGtleSk7XG59XG5mdW5jdGlvbiBzZXRUZXN0Rml4dHVyZShrZXksIGZpeHR1cmUpIHtcblx0dGVzdEZpeHR1cmVNYXAuc2V0KGtleSwgZml4dHVyZSk7XG59XG5mdW5jdGlvbiBnZXRUZXN0Rml4dHVyZXMoa2V5KSB7XG5cdHJldHVybiB0ZXN0Rml4dHVyZU1hcC5nZXQoa2V5KTtcbn1cbmZ1bmN0aW9uIHNldEhvb2tzKGtleSwgaG9va3MpIHtcblx0aG9va3NNYXAuc2V0KGtleSwgaG9va3MpO1xufVxuZnVuY3Rpb24gZ2V0SG9va3Moa2V5KSB7XG5cdHJldHVybiBob29rc01hcC5nZXQoa2V5KTtcbn1cblxuY29uc3QgRklYVFVSRV9TVEFDS19UUkFDRV9LRVkgPSBTeW1ib2wuZm9yKFwiVklURVNUX0ZJWFRVUkVfU1RBQ0tfVFJBQ0VcIik7XG5jbGFzcyBUZXN0Rml4dHVyZXMge1xuXHRfc3VpdGVDb250ZXh0cztcblx0X292ZXJyaWRlcyA9IG5ldyBXZWFrTWFwKCk7XG5cdF9yZWdpc3RyYXRpb25zO1xuXHRzdGF0aWMgX2RlZmluaXRpb25zID0gW107XG5cdHN0YXRpYyBfYnVpbHRpbkZpeHR1cmVzID0gW1xuXHRcdFwidGFza1wiLFxuXHRcdFwic2lnbmFsXCIsXG5cdFx0XCJvblRlc3RGYWlsZWRcIixcblx0XHRcIm9uVGVzdEZpbmlzaGVkXCIsXG5cdFx0XCJza2lwXCIsXG5cdFx0XCJhbm5vdGF0ZVwiXG5cdF07XG5cdHN0YXRpYyBfZml4dHVyZU9wdGlvbktleXMgPSBbXG5cdFx0XCJhdXRvXCIsXG5cdFx0XCJpbmplY3RlZFwiLFxuXHRcdFwic2NvcGVcIlxuXHRdO1xuXHRzdGF0aWMgX2ZpeHR1cmVTY29wZXMgPSBbXG5cdFx0XCJ0ZXN0XCIsXG5cdFx0XCJmaWxlXCIsXG5cdFx0XCJ3b3JrZXJcIlxuXHRdO1xuXHRzdGF0aWMgX3dvcmtlckNvbnRleHRTdWl0ZSA9IHsgdHlwZTogXCJ3b3JrZXJcIiB9O1xuXHRzdGF0aWMgY2xlYXJEZWZpbml0aW9ucygpIHtcblx0XHRUZXN0Rml4dHVyZXMuX2RlZmluaXRpb25zLmxlbmd0aCA9IDA7XG5cdH1cblx0c3RhdGljIGdldFdvcmtlckNvbnRleHRzKCkge1xuXHRcdHJldHVybiBUZXN0Rml4dHVyZXMuX2RlZmluaXRpb25zLm1hcCgoZikgPT4gZi5nZXRXb3JrZXJDb250ZXh0KCkpO1xuXHR9XG5cdHN0YXRpYyBnZXRGaWxlQ29udGV4dHMoZmlsZSkge1xuXHRcdHJldHVybiBUZXN0Rml4dHVyZXMuX2RlZmluaXRpb25zLm1hcCgoZikgPT4gZi5nZXRGaWxlQ29udGV4dChmaWxlKSk7XG5cdH1cblx0c3RhdGljIGlzRml4dHVyZU9wdGlvbnMob2JqKSB7XG5cdFx0cmV0dXJuIGlzT2JqZWN0KG9iaikgJiYgT2JqZWN0LmtleXMob2JqKS5zb21lKChrZXkpID0+IFRlc3RGaXh0dXJlcy5fZml4dHVyZU9wdGlvbktleXMuaW5jbHVkZXMoa2V5KSk7XG5cdH1cblx0Y29uc3RydWN0b3IocmVnaXN0cmF0aW9ucykge1xuXHRcdHRoaXMuX3JlZ2lzdHJhdGlvbnMgPSByZWdpc3RyYXRpb25zID8/IG5ldyBNYXAoKTtcblx0XHR0aGlzLl9zdWl0ZUNvbnRleHRzID0gbmV3IFdlYWtNYXAoKTtcblx0XHRUZXN0Rml4dHVyZXMuX2RlZmluaXRpb25zLnB1c2godGhpcyk7XG5cdH1cblx0ZXh0ZW5kKHJ1bm5lciwgdXNlckZpeHR1cmVzKSB7XG5cdFx0Y29uc3QgeyBzdWl0ZSB9ID0gZ2V0Q3VycmVudFN1aXRlKCk7XG5cdFx0Y29uc3QgaXNUb3BMZXZlbCA9ICFzdWl0ZSB8fCBzdWl0ZS5maWxlID09PSBzdWl0ZTtcblx0XHRjb25zdCByZWdpc3RyYXRpb25zID0gdGhpcy5wYXJzZVVzZXJGaXh0dXJlcyhydW5uZXIsIHVzZXJGaXh0dXJlcywgaXNUb3BMZXZlbCk7XG5cdFx0cmV0dXJuIG5ldyBUZXN0Rml4dHVyZXMocmVnaXN0cmF0aW9ucyk7XG5cdH1cblx0Z2V0KHN1aXRlKSB7XG5cdFx0bGV0IGN1cnJlbnRTdWl0ZSA9IHN1aXRlO1xuXHRcdHdoaWxlIChjdXJyZW50U3VpdGUpIHtcblx0XHRcdGNvbnN0IG92ZXJyaWRlcyA9IHRoaXMuX292ZXJyaWRlcy5nZXQoY3VycmVudFN1aXRlKTtcblx0XHRcdC8vIHJldHVybiB0aGUgY2xvc2VzdCBvdmVycmlkZVxuXHRcdFx0aWYgKG92ZXJyaWRlcykge1xuXHRcdFx0XHRyZXR1cm4gb3ZlcnJpZGVzO1xuXHRcdFx0fVxuXHRcdFx0aWYgKGN1cnJlbnRTdWl0ZSA9PT0gY3VycmVudFN1aXRlLmZpbGUpIHtcblx0XHRcdFx0YnJlYWs7XG5cdFx0XHR9XG5cdFx0XHRjdXJyZW50U3VpdGUgPSBjdXJyZW50U3VpdGUuc3VpdGUgfHwgY3VycmVudFN1aXRlLmZpbGU7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzLl9yZWdpc3RyYXRpb25zO1xuXHR9XG5cdG92ZXJyaWRlKHJ1bm5lciwgdXNlckZpeHR1cmVzKSB7XG5cdFx0Y29uc3QgeyBzdWl0ZTogY3VycmVudFN1aXRlLCBmaWxlIH0gPSBnZXRDdXJyZW50U3VpdGUoKTtcblx0XHRjb25zdCBzdWl0ZSA9IGN1cnJlbnRTdWl0ZSB8fCBmaWxlO1xuXHRcdGNvbnN0IGlzVG9wTGV2ZWwgPSAhY3VycmVudFN1aXRlIHx8IGN1cnJlbnRTdWl0ZS5maWxlID09PSBjdXJyZW50U3VpdGU7XG5cdFx0Ly8gQ3JlYXRlIGEgY29weSBvZiB0aGUgY2xvc2VzdCBwYXJlbnQncyByZWdpc3RyYXRpb25zIHRvIGF2b2lkIG1vZGlmeWluZyB0aGVtXG5cdFx0Ly8gRm9yIGNoYWluZWQgY2FsbHMsIHRoaXMuZ2V0KHN1aXRlKSByZXR1cm5zIHRoaXMgc3VpdGUncyBvdmVycmlkZXM7IGZvciBmaXJzdCBjYWxsLCByZXR1cm5zIHBhcmVudCdzXG5cdFx0Y29uc3Qgc3VpdGVSZWdpc3RyYXRpb25zID0gbmV3IE1hcCh0aGlzLmdldChzdWl0ZSkpO1xuXHRcdGNvbnN0IHJlZ2lzdHJhdGlvbnMgPSB0aGlzLnBhcnNlVXNlckZpeHR1cmVzKHJ1bm5lciwgdXNlckZpeHR1cmVzLCBpc1RvcExldmVsLCBzdWl0ZVJlZ2lzdHJhdGlvbnMpO1xuXHRcdC8vIElmIGRlZmluZWQgaW4gdG9wLWxldmVsLCBqdXN0IG92ZXJyaWRlIGFsbCByZWdpc3RyYXRpb25zXG5cdFx0Ly8gV2UgZG9uJ3Qgc3VwcG9ydCBvdmVycmlkaW5nIHN1aXRlLWxldmVsIGZpeHR1cmVzIGFueXdheSAoaXQgd2lsbCB0aHJvdyBhbiBlcnJvcilcblx0XHRpZiAoaXNUb3BMZXZlbCkge1xuXHRcdFx0dGhpcy5fcmVnaXN0cmF0aW9ucyA9IHJlZ2lzdHJhdGlvbnM7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuX292ZXJyaWRlcy5zZXQoc3VpdGUsIHJlZ2lzdHJhdGlvbnMpO1xuXHRcdH1cblx0fVxuXHRnZXRGaWxlQ29udGV4dChmaWxlKSB7XG5cdFx0aWYgKCF0aGlzLl9zdWl0ZUNvbnRleHRzLmhhcyhmaWxlKSkge1xuXHRcdFx0dGhpcy5fc3VpdGVDb250ZXh0cy5zZXQoZmlsZSwgT2JqZWN0LmNyZWF0ZShudWxsKSk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzLl9zdWl0ZUNvbnRleHRzLmdldChmaWxlKTtcblx0fVxuXHRnZXRXb3JrZXJDb250ZXh0KCkge1xuXHRcdGlmICghdGhpcy5fc3VpdGVDb250ZXh0cy5oYXMoVGVzdEZpeHR1cmVzLl93b3JrZXJDb250ZXh0U3VpdGUpKSB7XG5cdFx0XHR0aGlzLl9zdWl0ZUNvbnRleHRzLnNldChUZXN0Rml4dHVyZXMuX3dvcmtlckNvbnRleHRTdWl0ZSwgT2JqZWN0LmNyZWF0ZShudWxsKSk7XG5cdFx0fVxuXHRcdHJldHVybiB0aGlzLl9zdWl0ZUNvbnRleHRzLmdldChUZXN0Rml4dHVyZXMuX3dvcmtlckNvbnRleHRTdWl0ZSk7XG5cdH1cblx0cGFyc2VVc2VyRml4dHVyZXMocnVubmVyLCB1c2VyRml4dHVyZXMsIHN1cHBvcnROb25UZXN0LCByZWdpc3RyYXRpb25zID0gbmV3IE1hcCh0aGlzLl9yZWdpc3RyYXRpb25zKSkge1xuXHRcdGNvbnN0IGVycm9ycyA9IFtdO1xuXHRcdE9iamVjdC5lbnRyaWVzKHVzZXJGaXh0dXJlcykuZm9yRWFjaCgoW25hbWUsIGZuXSkgPT4ge1xuXHRcdFx0bGV0IG9wdGlvbnM7XG5cdFx0XHRsZXQgdmFsdWU7XG5cdFx0XHRsZXQgX29wdGlvbnM7XG5cdFx0XHRpZiAoQXJyYXkuaXNBcnJheShmbikgJiYgZm4ubGVuZ3RoID49IDIgJiYgVGVzdEZpeHR1cmVzLmlzRml4dHVyZU9wdGlvbnMoZm5bMV0pKSB7XG5cdFx0XHRcdF9vcHRpb25zID0gZm5bMV07XG5cdFx0XHRcdG9wdGlvbnMgPSB7XG5cdFx0XHRcdFx0YXV0bzogX29wdGlvbnMuYXV0byA/PyBmYWxzZSxcblx0XHRcdFx0XHRzY29wZTogX29wdGlvbnMuc2NvcGUgPz8gXCJ0ZXN0XCIsXG5cdFx0XHRcdFx0aW5qZWN0ZWQ6IF9vcHRpb25zLmluamVjdGVkID8/IGZhbHNlXG5cdFx0XHRcdH07XG5cdFx0XHRcdHZhbHVlID0gb3B0aW9ucy5pbmplY3RlZCA/IHJ1bm5lci5pbmplY3RWYWx1ZT8uKG5hbWUpID8/IGZuWzBdIDogZm5bMF07XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHR2YWx1ZSA9IGZuO1xuXHRcdFx0fVxuXHRcdFx0Y29uc3QgcGFyZW50ID0gcmVnaXN0cmF0aW9ucy5nZXQobmFtZSk7XG5cdFx0XHRpZiAocGFyZW50ICYmIG9wdGlvbnMpIHtcblx0XHRcdFx0aWYgKHBhcmVudC5zY29wZSAhPT0gb3B0aW9ucy5zY29wZSkge1xuXHRcdFx0XHRcdGVycm9ycy5wdXNoKG5ldyBGaXh0dXJlRGVwZW5kZW5jeUVycm9yKGBUaGUgXCIke25hbWV9XCIgZml4dHVyZSB3YXMgYWxyZWFkeSByZWdpc3RlcmVkIHdpdGggYSBcIiR7b3B0aW9ucy5zY29wZX1cIiBzY29wZS5gKSk7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKHBhcmVudC5hdXRvICE9PSBvcHRpb25zLmF1dG8pIHtcblx0XHRcdFx0XHRlcnJvcnMucHVzaChuZXcgRml4dHVyZURlcGVuZGVuY3lFcnJvcihgVGhlIFwiJHtuYW1lfVwiIGZpeHR1cmUgd2FzIGFscmVhZHkgcmVnaXN0ZXJlZCBhcyB7IGF1dG86ICR7b3B0aW9ucy5hdXRvfSB9LmApKTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIGlmIChwYXJlbnQpIHtcblx0XHRcdFx0b3B0aW9ucyA9IHtcblx0XHRcdFx0XHRhdXRvOiBwYXJlbnQuYXV0byxcblx0XHRcdFx0XHRzY29wZTogcGFyZW50LnNjb3BlLFxuXHRcdFx0XHRcdGluamVjdGVkOiBwYXJlbnQuaW5qZWN0ZWRcblx0XHRcdFx0fTtcblx0XHRcdH0gZWxzZSBpZiAoIW9wdGlvbnMpIHtcblx0XHRcdFx0b3B0aW9ucyA9IHtcblx0XHRcdFx0XHRhdXRvOiBmYWxzZSxcblx0XHRcdFx0XHRpbmplY3RlZDogZmFsc2UsXG5cdFx0XHRcdFx0c2NvcGU6IFwidGVzdFwiXG5cdFx0XHRcdH07XG5cdFx0XHR9XG5cdFx0XHRpZiAob3B0aW9ucy5zY29wZSAmJiAhVGVzdEZpeHR1cmVzLl9maXh0dXJlU2NvcGVzLmluY2x1ZGVzKG9wdGlvbnMuc2NvcGUpKSB7XG5cdFx0XHRcdGVycm9ycy5wdXNoKG5ldyBGaXh0dXJlRGVwZW5kZW5jeUVycm9yKGBUaGUgXCIke25hbWV9XCIgZml4dHVyZSBoYXMgdW5rbm93biBzY29wZSBcIiR7b3B0aW9ucy5zY29wZX1cIi5gKSk7XG5cdFx0XHR9XG5cdFx0XHRpZiAoIXN1cHBvcnROb25UZXN0ICYmIG9wdGlvbnMuc2NvcGUgIT09IFwidGVzdFwiKSB7XG5cdFx0XHRcdGVycm9ycy5wdXNoKG5ldyBGaXh0dXJlRGVwZW5kZW5jeUVycm9yKGBUaGUgXCIke25hbWV9XCIgZml4dHVyZSBjYW5ub3QgYmUgZGVmaW5lZCB3aXRoIGEgJHtvcHRpb25zLnNjb3BlfSBzY29wZSR7IV9vcHRpb25zPy5zY29wZSAmJiBwYXJlbnQ/LnNjb3BlID8gXCIgKGluaGVyaXRlZCBmcm9tIHRoZSBiYXNlIGZpeHR1cmUpXCIgOiBcIlwifSBpbnNpZGUgdGhlIGRlc2NyaWJlIGJsb2NrLiBEZWZpbmUgaXQgYXQgdGhlIHRvcCBsZXZlbCBvZiB0aGUgZmlsZSBpbnN0ZWFkLmApKTtcblx0XHRcdH1cblx0XHRcdGNvbnN0IGRlcHMgPSBpc0ZpeHR1cmVGdW5jdGlvbih2YWx1ZSkgPyBnZXRVc2VkUHJvcHModmFsdWUpIDogbmV3IFNldCgpO1xuXHRcdFx0Y29uc3QgaXRlbSA9IHtcblx0XHRcdFx0bmFtZSxcblx0XHRcdFx0dmFsdWUsXG5cdFx0XHRcdGF1dG86IG9wdGlvbnMuYXV0byA/PyBmYWxzZSxcblx0XHRcdFx0aW5qZWN0ZWQ6IG9wdGlvbnMuaW5qZWN0ZWQgPz8gZmFsc2UsXG5cdFx0XHRcdHNjb3BlOiBvcHRpb25zLnNjb3BlID8/IFwidGVzdFwiLFxuXHRcdFx0XHRkZXBzLFxuXHRcdFx0XHRwYXJlbnRcblx0XHRcdH07XG5cdFx0XHRpZiAoaXNGaXh0dXJlRnVuY3Rpb24odmFsdWUpKSB7XG5cdFx0XHRcdE9iamVjdC5hc3NpZ24odmFsdWUsIHsgW0ZJWFRVUkVfU1RBQ0tfVFJBQ0VfS0VZXTogbmV3IEVycm9yKFwiU1RBQ0tfVFJBQ0VfRVJST1JcIikgfSk7XG5cdFx0XHR9XG5cdFx0XHRyZWdpc3RyYXRpb25zLnNldChuYW1lLCBpdGVtKTtcblx0XHRcdGlmIChpdGVtLnNjb3BlID09PSBcIndvcmtlclwiICYmIChydW5uZXIucG9vbCA9PT0gXCJ2bVRocmVhZHNcIiB8fCBydW5uZXIucG9vbCA9PT0gXCJ2bUZvcmtzXCIpKSB7XG5cdFx0XHRcdGl0ZW0uc2NvcGUgPSBcImZpbGVcIjtcblx0XHRcdH1cblx0XHR9KTtcblx0XHQvLyB2YWxpZGF0ZSBmaXh0dXJlIGRlcGVuZGVuY3kgc2NvcGVzXG5cdFx0Zm9yIChjb25zdCBmaXh0dXJlIG9mIHJlZ2lzdHJhdGlvbnMudmFsdWVzKCkpIHtcblx0XHRcdGZvciAoY29uc3QgZGVwTmFtZSBvZiBmaXh0dXJlLmRlcHMpIHtcblx0XHRcdFx0aWYgKFRlc3RGaXh0dXJlcy5fYnVpbHRpbkZpeHR1cmVzLmluY2x1ZGVzKGRlcE5hbWUpKSB7XG5cdFx0XHRcdFx0Y29udGludWU7XG5cdFx0XHRcdH1cblx0XHRcdFx0Y29uc3QgZGVwID0gcmVnaXN0cmF0aW9ucy5nZXQoZGVwTmFtZSk7XG5cdFx0XHRcdGlmICghZGVwKSB7XG5cdFx0XHRcdFx0ZXJyb3JzLnB1c2gobmV3IEZpeHR1cmVEZXBlbmRlbmN5RXJyb3IoYFRoZSBcIiR7Zml4dHVyZS5uYW1lfVwiIGZpeHR1cmUgZGVwZW5kcyBvbiB1bmtub3duIGZpeHR1cmUgXCIke2RlcE5hbWV9XCIuYCkpO1xuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmIChkZXBOYW1lID09PSBmaXh0dXJlLm5hbWUgJiYgIWZpeHR1cmUucGFyZW50KSB7XG5cdFx0XHRcdFx0ZXJyb3JzLnB1c2gobmV3IEZpeHR1cmVEZXBlbmRlbmN5RXJyb3IoYFRoZSBcIiR7Zml4dHVyZS5uYW1lfVwiIGZpeHR1cmUgZGVwZW5kcyBvbiBpdHNlbGYsIGJ1dCBkb2VzIG5vdCBoYXZlIGEgYmFzZSBpbXBsZW1lbnRhdGlvbi5gKSk7XG5cdFx0XHRcdFx0Y29udGludWU7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKFRlc3RGaXh0dXJlcy5fZml4dHVyZVNjb3Blcy5pbmRleE9mKGZpeHR1cmUuc2NvcGUpID4gVGVzdEZpeHR1cmVzLl9maXh0dXJlU2NvcGVzLmluZGV4T2YoZGVwLnNjb3BlKSkge1xuXHRcdFx0XHRcdGVycm9ycy5wdXNoKG5ldyBGaXh0dXJlRGVwZW5kZW5jeUVycm9yKGBUaGUgJHtmaXh0dXJlLnNjb3BlfSBcIiR7Zml4dHVyZS5uYW1lfVwiIGZpeHR1cmUgY2Fubm90IGRlcGVuZCBvbiBhICR7ZGVwLnNjb3BlfSBmaXh0dXJlIFwiJHtkZXAubmFtZX1cIi5gKSk7XG5cdFx0XHRcdFx0Y29udGludWU7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cdFx0aWYgKGVycm9ycy5sZW5ndGggPT09IDEpIHtcblx0XHRcdHRocm93IGVycm9yc1swXTtcblx0XHR9IGVsc2UgaWYgKGVycm9ycy5sZW5ndGggPiAxKSB7XG5cdFx0XHR0aHJvdyBuZXcgQWdncmVnYXRlRXJyb3IoZXJyb3JzLCBcIkNhbm5vdCByZXNvbHZlIHVzZXIgZml4dHVyZXMuIFNlZSBlcnJvcnMgZm9yIG1vcmUgaW5mb3JtYXRpb24uXCIpO1xuXHRcdH1cblx0XHRyZXR1cm4gcmVnaXN0cmF0aW9ucztcblx0fVxufVxuY29uc3QgY2xlYW51cEZuQXJyYXlNYXAgPSBuZXcgV2Vha01hcCgpO1xuYXN5bmMgZnVuY3Rpb24gY2FsbEZpeHR1cmVDbGVhbnVwKGNvbnRleHQpIHtcblx0Y29uc3QgY2xlYW51cEZuQXJyYXkgPSBjbGVhbnVwRm5BcnJheU1hcC5nZXQoY29udGV4dCkgPz8gW107XG5cdGZvciAoY29uc3QgY2xlYW51cCBvZiBjbGVhbnVwRm5BcnJheS5yZXZlcnNlKCkpIHtcblx0XHRhd2FpdCBjbGVhbnVwKCk7XG5cdH1cblx0Y2xlYW51cEZuQXJyYXlNYXAuZGVsZXRlKGNvbnRleHQpO1xufVxuLyoqXG4qIFJldHVybnMgdGhlIGN1cnJlbnQgbnVtYmVyIG9mIGNsZWFudXAgZnVuY3Rpb25zIHJlZ2lzdGVyZWQgZm9yIHRoZSBjb250ZXh0LlxuKiBUaGlzIGNhbiBiZSB1c2VkIGFzIGEgY2hlY2twb2ludCB0byBsYXRlciBjbGVhbiB1cCBvbmx5IGZpeHR1cmVzIGFkZGVkIGFmdGVyIHRoaXMgcG9pbnQuXG4qL1xuZnVuY3Rpb24gZ2V0Rml4dHVyZUNsZWFudXBDb3VudChjb250ZXh0KSB7XG5cdHJldHVybiBjbGVhbnVwRm5BcnJheU1hcC5nZXQoY29udGV4dCk/Lmxlbmd0aCA/PyAwO1xufVxuLyoqXG4qIENsZWFucyB1cCBvbmx5IGZpeHR1cmVzIHRoYXQgd2VyZSBhZGRlZCBhZnRlciB0aGUgZ2l2ZW4gY2hlY2twb2ludCBpbmRleC5cbiogVGhpcyBpcyB1c2VkIGJ5IGFyb3VuZEVhY2ggdG8gY2xlYW4gdXAgZml4dHVyZXMgY3JlYXRlZCBpbnNpZGUgcnVuVGVzdCgpXG4qIHdoaWxlIHByZXNlcnZpbmcgZml4dHVyZXMgdGhhdCB3ZXJlIGNyZWF0ZWQgZm9yIGFyb3VuZEVhY2ggaXRzZWxmLlxuKi9cbmFzeW5jIGZ1bmN0aW9uIGNhbGxGaXh0dXJlQ2xlYW51cEZyb20oY29udGV4dCwgZnJvbUluZGV4KSB7XG5cdGNvbnN0IGNsZWFudXBGbkFycmF5ID0gY2xlYW51cEZuQXJyYXlNYXAuZ2V0KGNvbnRleHQpO1xuXHRpZiAoIWNsZWFudXBGbkFycmF5IHx8IGNsZWFudXBGbkFycmF5Lmxlbmd0aCA8PSBmcm9tSW5kZXgpIHtcblx0XHRyZXR1cm47XG5cdH1cblx0Ly8gR2V0IGl0ZW1zIGFkZGVkIGFmdGVyIHRoZSBjaGVja3BvaW50XG5cdGNvbnN0IHRvQ2xlYW51cCA9IGNsZWFudXBGbkFycmF5LnNsaWNlKGZyb21JbmRleCk7XG5cdC8vIENsZWFuIHVwIGluIHJldmVyc2Ugb3JkZXJcblx0Zm9yIChjb25zdCBjbGVhbnVwIG9mIHRvQ2xlYW51cC5yZXZlcnNlKCkpIHtcblx0XHRhd2FpdCBjbGVhbnVwKCk7XG5cdH1cblx0Ly8gUmVtb3ZlIGNsZWFuZWQgdXAgaXRlbXMgZnJvbSB0aGUgYXJyYXksIGtlZXBpbmcgaXRlbXMgYmVmb3JlIGNoZWNrcG9pbnRcblx0Y2xlYW51cEZuQXJyYXkubGVuZ3RoID0gZnJvbUluZGV4O1xufVxuY29uc3QgY29udGV4dEhhc0ZpeHR1cmVzQ2FjaGUgPSBuZXcgV2Vha01hcCgpO1xuZnVuY3Rpb24gd2l0aEZpeHR1cmVzKGZuLCBvcHRpb25zKSB7XG5cdGNvbnN0IGNvbGxlY3RvciA9IGdldEN1cnJlbnRTdWl0ZSgpO1xuXHRjb25zdCBzdWl0ZSA9IG9wdGlvbnM/LnN1aXRlIHx8IGNvbGxlY3Rvci5zdWl0ZSB8fCBjb2xsZWN0b3IuZmlsZTtcblx0cmV0dXJuIGFzeW5jIChob29rQ29udGV4dCkgPT4ge1xuXHRcdGNvbnN0IGNvbnRleHQgPSBob29rQ29udGV4dCB8fCBvcHRpb25zPy5jb250ZXh0O1xuXHRcdGlmICghY29udGV4dCkge1xuXHRcdFx0aWYgKG9wdGlvbnM/LnN1aXRlSG9vaykge1xuXHRcdFx0XHR2YWxpZGF0ZVN1aXRlSG9vayhmbiwgb3B0aW9ucy5zdWl0ZUhvb2ssIG9wdGlvbnMuc3RhY2tUcmFjZUVycm9yKTtcblx0XHRcdH1cblx0XHRcdHJldHVybiBmbih7fSk7XG5cdFx0fVxuXHRcdGNvbnN0IGZpeHR1cmVzID0gb3B0aW9ucz8uZml4dHVyZXMgfHwgZ2V0VGVzdEZpeHR1cmVzKGNvbnRleHQpO1xuXHRcdGlmICghZml4dHVyZXMpIHtcblx0XHRcdHJldHVybiBmbihjb250ZXh0KTtcblx0XHR9XG5cdFx0Y29uc3QgcmVnaXN0cmF0aW9ucyA9IGZpeHR1cmVzLmdldChzdWl0ZSk7XG5cdFx0aWYgKCFyZWdpc3RyYXRpb25zLnNpemUpIHtcblx0XHRcdHJldHVybiBmbihjb250ZXh0KTtcblx0XHR9XG5cdFx0Y29uc3QgdXNlZEZpeHR1cmVzID0gW107XG5cdFx0Y29uc3QgdXNlZFByb3BzID0gZ2V0VXNlZFByb3BzKGZuKTtcblx0XHRmb3IgKGNvbnN0IGZpeHR1cmUgb2YgcmVnaXN0cmF0aW9ucy52YWx1ZXMoKSkge1xuXHRcdFx0aWYgKGZpeHR1cmUuYXV0byB8fCB1c2VkUHJvcHMuaGFzKGZpeHR1cmUubmFtZSkpIHtcblx0XHRcdFx0dXNlZEZpeHR1cmVzLnB1c2goZml4dHVyZSk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGlmICghdXNlZEZpeHR1cmVzLmxlbmd0aCkge1xuXHRcdFx0cmV0dXJuIGZuKGNvbnRleHQpO1xuXHRcdH1cblx0XHRpZiAoIWNsZWFudXBGbkFycmF5TWFwLmhhcyhjb250ZXh0KSkge1xuXHRcdFx0Y2xlYW51cEZuQXJyYXlNYXAuc2V0KGNvbnRleHQsIFtdKTtcblx0XHR9XG5cdFx0Y29uc3QgY2xlYW51cEZuQXJyYXkgPSBjbGVhbnVwRm5BcnJheU1hcC5nZXQoY29udGV4dCk7XG5cdFx0Y29uc3QgcGVuZGluZ0ZpeHR1cmVzID0gcmVzb2x2ZURlcHModXNlZEZpeHR1cmVzLCByZWdpc3RyYXRpb25zKTtcblx0XHRpZiAoIXBlbmRpbmdGaXh0dXJlcy5sZW5ndGgpIHtcblx0XHRcdHJldHVybiBmbihjb250ZXh0KTtcblx0XHR9XG5cdFx0Ly8gQ2hlY2sgaWYgc3VpdGUtbGV2ZWwgaG9vayBpcyB0cnlpbmcgdG8gYWNjZXNzIHRlc3Qtc2NvcGVkIGZpeHR1cmVzXG5cdFx0Ly8gU3VpdGUgaG9va3MgKGJlZm9yZUFsbC9hZnRlckFsbC9hcm91bmRBbGwpIGNhbiBvbmx5IGFjY2VzcyBmaWxlL3dvcmtlciBzY29wZWQgZml4dHVyZXNcblx0XHRpZiAob3B0aW9ucz8uc3VpdGVIb29rKSB7XG5cdFx0XHRjb25zdCB0ZXN0U2NvcGVkRml4dHVyZXMgPSBwZW5kaW5nRml4dHVyZXMuZmlsdGVyKChmKSA9PiBmLnNjb3BlID09PSBcInRlc3RcIik7XG5cdFx0XHRpZiAodGVzdFNjb3BlZEZpeHR1cmVzLmxlbmd0aCA+IDApIHtcblx0XHRcdFx0Y29uc3QgZml4dHVyZU5hbWVzID0gdGVzdFNjb3BlZEZpeHR1cmVzLm1hcCgoZikgPT4gYFwiJHtmLm5hbWV9XCJgKS5qb2luKFwiLCBcIik7XG5cdFx0XHRcdGNvbnN0IGFsdGVybmF0aXZlSG9vayA9IHtcblx0XHRcdFx0XHRhcm91bmRBbGw6IFwiYXJvdW5kRWFjaFwiLFxuXHRcdFx0XHRcdGJlZm9yZUFsbDogXCJiZWZvcmVFYWNoXCIsXG5cdFx0XHRcdFx0YWZ0ZXJBbGw6IFwiYWZ0ZXJFYWNoXCJcblx0XHRcdFx0fTtcblx0XHRcdFx0Y29uc3QgZXJyb3IgPSBuZXcgRml4dHVyZURlcGVuZGVuY3lFcnJvcihgVGVzdC1zY29wZWQgZml4dHVyZXMgY2Fubm90IGJlIHVzZWQgaW5zaWRlICR7b3B0aW9ucy5zdWl0ZUhvb2t9IGhvb2suIGAgKyBgVGhlIGZvbGxvd2luZyBmaXh0dXJlcyBhcmUgdGVzdC1zY29wZWQ6ICR7Zml4dHVyZU5hbWVzfS4gYCArIGBVc2UgeyBzY29wZTogJ2ZpbGUnIH0gb3IgeyBzY29wZTogJ3dvcmtlcicgfSBmaXh0dXJlcyBpbnN0ZWFkLCBvciBtb3ZlIHRoZSBsb2dpYyB0byAke2FsdGVybmF0aXZlSG9va1tvcHRpb25zLnN1aXRlSG9va119IGhvb2suYCk7XG5cdFx0XHRcdC8vIFVzZSBzdGFjayB0cmFjZSBmcm9tIGhvb2sgcmVnaXN0cmF0aW9uIGZvciBiZXR0ZXIgZXJyb3IgbG9jYXRpb25cblx0XHRcdFx0aWYgKG9wdGlvbnMuc3RhY2tUcmFjZUVycm9yPy5zdGFjaykge1xuXHRcdFx0XHRcdGVycm9yLnN0YWNrID0gZXJyb3IubWVzc2FnZSArIG9wdGlvbnMuc3RhY2tUcmFjZUVycm9yLnN0YWNrLnJlcGxhY2Uob3B0aW9ucy5zdGFja1RyYWNlRXJyb3IubWVzc2FnZSwgXCJcIik7XG5cdFx0XHRcdH1cblx0XHRcdFx0dGhyb3cgZXJyb3I7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGlmICghY29udGV4dEhhc0ZpeHR1cmVzQ2FjaGUuaGFzKGNvbnRleHQpKSB7XG5cdFx0XHRjb250ZXh0SGFzRml4dHVyZXNDYWNoZS5zZXQoY29udGV4dCwgbmV3IFdlYWtTZXQoKSk7XG5cdFx0fVxuXHRcdGNvbnN0IGNhY2hlZEZpeHR1cmVzID0gY29udGV4dEhhc0ZpeHR1cmVzQ2FjaGUuZ2V0KGNvbnRleHQpO1xuXHRcdGZvciAoY29uc3QgZml4dHVyZSBvZiBwZW5kaW5nRml4dHVyZXMpIHtcblx0XHRcdGlmIChmaXh0dXJlLnNjb3BlID09PSBcInRlc3RcIikge1xuXHRcdFx0XHQvLyBmaXh0dXJlIGNvdWxkIGJlIGFscmVhZHkgaW5pdGlhbGl6ZWQgZHVyaW5nIFwiYmVmb3JlXCIgaG9va1xuXHRcdFx0XHQvLyB3ZSBjYW4ndCBjaGVjayBcImZpeHR1cmUubmFtZVwiIGluIGNvbnRleHQgYmVjYXVzZSBjb250ZXh0IG1heVxuXHRcdFx0XHQvLyBhY2Nlc3MgdGhlIHBhcmVudCBmaXh0dXJlICh7IGE6ICh7IGEgfSkgPT4ge30gfSlcblx0XHRcdFx0aWYgKGNhY2hlZEZpeHR1cmVzLmhhcyhmaXh0dXJlKSkge1xuXHRcdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGNhY2hlZEZpeHR1cmVzLmFkZChmaXh0dXJlKTtcblx0XHRcdFx0Y29uc3QgcmVzb2x2ZWRWYWx1ZSA9IGF3YWl0IHJlc29sdmVUZXN0Rml4dHVyZVZhbHVlKGZpeHR1cmUsIGNvbnRleHQsIGNsZWFudXBGbkFycmF5KTtcblx0XHRcdFx0Y29udGV4dFtmaXh0dXJlLm5hbWVdID0gcmVzb2x2ZWRWYWx1ZTtcblx0XHRcdFx0Y2xlYW51cEZuQXJyYXkucHVzaCgoKSA9PiB7XG5cdFx0XHRcdFx0Y2FjaGVkRml4dHVyZXMuZGVsZXRlKGZpeHR1cmUpO1xuXHRcdFx0XHR9KTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGNvbnN0IHJlc29sdmVkVmFsdWUgPSBhd2FpdCByZXNvbHZlU2NvcGVGaXh0dXJlVmFsdWUoZml4dHVyZXMsIHN1aXRlLCBmaXh0dXJlKTtcblx0XHRcdFx0Y29udGV4dFtmaXh0dXJlLm5hbWVdID0gcmVzb2x2ZWRWYWx1ZTtcblx0XHRcdH1cblx0XHR9XG5cdFx0cmV0dXJuIGZuKGNvbnRleHQpO1xuXHR9O1xufVxuZnVuY3Rpb24gaXNGaXh0dXJlRnVuY3Rpb24odmFsdWUpIHtcblx0cmV0dXJuIHR5cGVvZiB2YWx1ZSA9PT0gXCJmdW5jdGlvblwiO1xufVxuZnVuY3Rpb24gcmVzb2x2ZVRlc3RGaXh0dXJlVmFsdWUoZml4dHVyZSwgY29udGV4dCwgY2xlYW51cEZuQXJyYXkpIHtcblx0aWYgKCFpc0ZpeHR1cmVGdW5jdGlvbihmaXh0dXJlLnZhbHVlKSkge1xuXHRcdHJldHVybiBmaXh0dXJlLnZhbHVlO1xuXHR9XG5cdHJldHVybiByZXNvbHZlRml4dHVyZUZ1bmN0aW9uKGZpeHR1cmUudmFsdWUsIGZpeHR1cmUubmFtZSwgY29udGV4dCwgY2xlYW51cEZuQXJyYXkpO1xufVxuY29uc3Qgc2NvcGVkRml4dHVyZVByb21pc2VDYWNoZSA9IG5ldyBXZWFrTWFwKCk7XG5hc3luYyBmdW5jdGlvbiByZXNvbHZlU2NvcGVGaXh0dXJlVmFsdWUoZml4dHVyZXMsIHN1aXRlLCBmaXh0dXJlKSB7XG5cdGNvbnN0IHdvcmtlckNvbnRleHQgPSBmaXh0dXJlcy5nZXRXb3JrZXJDb250ZXh0KCk7XG5cdGNvbnN0IGZpbGVDb250ZXh0ID0gZml4dHVyZXMuZ2V0RmlsZUNvbnRleHQoc3VpdGUuZmlsZSk7XG5cdGNvbnN0IGZpeHR1cmVDb250ZXh0ID0gZml4dHVyZS5zY29wZSA9PT0gXCJ3b3JrZXJcIiA/IHdvcmtlckNvbnRleHQgOiBmaWxlQ29udGV4dDtcblx0aWYgKCFpc0ZpeHR1cmVGdW5jdGlvbihmaXh0dXJlLnZhbHVlKSkge1xuXHRcdGZpeHR1cmVDb250ZXh0W2ZpeHR1cmUubmFtZV0gPSBmaXh0dXJlLnZhbHVlO1xuXHRcdHJldHVybiBmaXh0dXJlLnZhbHVlO1xuXHR9XG5cdGlmIChmaXh0dXJlLm5hbWUgaW4gZml4dHVyZUNvbnRleHQpIHtcblx0XHRyZXR1cm4gZml4dHVyZUNvbnRleHRbZml4dHVyZS5uYW1lXTtcblx0fVxuXHRpZiAoc2NvcGVkRml4dHVyZVByb21pc2VDYWNoZS5oYXMoZml4dHVyZSkpIHtcblx0XHRyZXR1cm4gc2NvcGVkRml4dHVyZVByb21pc2VDYWNoZS5nZXQoZml4dHVyZSk7XG5cdH1cblx0aWYgKCFjbGVhbnVwRm5BcnJheU1hcC5oYXMoZml4dHVyZUNvbnRleHQpKSB7XG5cdFx0Y2xlYW51cEZuQXJyYXlNYXAuc2V0KGZpeHR1cmVDb250ZXh0LCBbXSk7XG5cdH1cblx0Y29uc3QgY2xlYW51cEZuRmlsZUFycmF5ID0gY2xlYW51cEZuQXJyYXlNYXAuZ2V0KGZpeHR1cmVDb250ZXh0KTtcblx0Y29uc3QgcHJvbWlzZSA9IHJlc29sdmVGaXh0dXJlRnVuY3Rpb24oZml4dHVyZS52YWx1ZSwgZml4dHVyZS5uYW1lLCBmaXh0dXJlLnNjb3BlID09PSBcImZpbGVcIiA/IHtcblx0XHQuLi53b3JrZXJDb250ZXh0LFxuXHRcdC4uLmZpbGVDb250ZXh0XG5cdH0gOiBmaXh0dXJlQ29udGV4dCwgY2xlYW51cEZuRmlsZUFycmF5KS50aGVuKCh2YWx1ZSkgPT4ge1xuXHRcdGZpeHR1cmVDb250ZXh0W2ZpeHR1cmUubmFtZV0gPSB2YWx1ZTtcblx0XHRzY29wZWRGaXh0dXJlUHJvbWlzZUNhY2hlLmRlbGV0ZShmaXh0dXJlKTtcblx0XHRyZXR1cm4gdmFsdWU7XG5cdH0pO1xuXHRzY29wZWRGaXh0dXJlUHJvbWlzZUNhY2hlLnNldChmaXh0dXJlLCBwcm9taXNlKTtcblx0cmV0dXJuIHByb21pc2U7XG59XG5hc3luYyBmdW5jdGlvbiByZXNvbHZlRml4dHVyZUZ1bmN0aW9uKGZpeHR1cmVGbiwgZml4dHVyZU5hbWUsIGNvbnRleHQsIGNsZWFudXBGbkFycmF5KSB7XG5cdC8vIHdhaXQgZm9yIGB1c2VgIGNhbGwgdG8gZXh0cmFjdCBmaXh0dXJlIHZhbHVlXG5cdGNvbnN0IHVzZUZuQXJnUHJvbWlzZSA9IGNyZWF0ZURlZmVyKCk7XG5cdGNvbnN0IHN0YWNrVHJhY2VFcnJvciA9IEZJWFRVUkVfU1RBQ0tfVFJBQ0VfS0VZIGluIGZpeHR1cmVGbiAmJiBmaXh0dXJlRm5bRklYVFVSRV9TVEFDS19UUkFDRV9LRVldIGluc3RhbmNlb2YgRXJyb3IgPyBmaXh0dXJlRm5bRklYVFVSRV9TVEFDS19UUkFDRV9LRVldIDogdW5kZWZpbmVkO1xuXHRsZXQgaXNVc2VGbkFyZ1Jlc29sdmVkID0gZmFsc2U7XG5cdGNvbnN0IGZpeHR1cmVSZXR1cm4gPSBmaXh0dXJlRm4oY29udGV4dCwgYXN5bmMgKHVzZUZuQXJnKSA9PiB7XG5cdFx0Ly8gZXh0cmFjdCBgdXNlYCBhcmd1bWVudFxuXHRcdGlzVXNlRm5BcmdSZXNvbHZlZCA9IHRydWU7XG5cdFx0dXNlRm5BcmdQcm9taXNlLnJlc29sdmUodXNlRm5BcmcpO1xuXHRcdC8vIHN1c3BlbmQgZml4dHVyZSB0ZWFyZG93biBieSBob2xkaW5nIG9mZiBgdXNlUmV0dXJuUHJvbWlzZWAgcmVzb2x1dGlvbiB1bnRpbCBjbGVhbnVwXG5cdFx0Y29uc3QgdXNlUmV0dXJuUHJvbWlzZSA9IGNyZWF0ZURlZmVyKCk7XG5cdFx0Y2xlYW51cEZuQXJyYXkucHVzaChhc3luYyAoKSA9PiB7XG5cdFx0XHQvLyBzdGFydCB0ZWFyZG93biBieSByZXNvbHZpbmcgYHVzZWAgUHJvbWlzZVxuXHRcdFx0dXNlUmV0dXJuUHJvbWlzZS5yZXNvbHZlKCk7XG5cdFx0XHQvLyB3YWl0IGZvciBmaW5pc2hpbmcgdGVhcmRvd25cblx0XHRcdGF3YWl0IGZpeHR1cmVSZXR1cm47XG5cdFx0fSk7XG5cdFx0YXdhaXQgdXNlUmV0dXJuUHJvbWlzZTtcblx0fSkudGhlbigoKSA9PiB7XG5cdFx0Ly8gZml4dHVyZSByZXR1cm5lZCB3aXRob3V0IGNhbGxpbmcgdXNlKClcblx0XHRpZiAoIWlzVXNlRm5BcmdSZXNvbHZlZCkge1xuXHRcdFx0Y29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoYEZpeHR1cmUgXCIke2ZpeHR1cmVOYW1lfVwiIHJldHVybmVkIHdpdGhvdXQgY2FsbGluZyBcInVzZVwiLiBNYWtlIHN1cmUgdG8gY2FsbCBcInVzZVwiIGluIGV2ZXJ5IGNvZGUgcGF0aCBvZiB0aGUgZml4dHVyZSBmdW5jdGlvbi5gKTtcblx0XHRcdGlmIChzdGFja1RyYWNlRXJyb3I/LnN0YWNrKSB7XG5cdFx0XHRcdGVycm9yLnN0YWNrID0gZXJyb3IubWVzc2FnZSArIHN0YWNrVHJhY2VFcnJvci5zdGFjay5yZXBsYWNlKHN0YWNrVHJhY2VFcnJvci5tZXNzYWdlLCBcIlwiKTtcblx0XHRcdH1cblx0XHRcdHVzZUZuQXJnUHJvbWlzZS5yZWplY3QoZXJyb3IpO1xuXHRcdH1cblx0fSkuY2F0Y2goKGUpID0+IHtcblx0XHQvLyB0cmVhdCBmaXh0dXJlIHNldHVwIGVycm9yIGFzIHRlc3QgZmFpbHVyZVxuXHRcdGlmICghaXNVc2VGbkFyZ1Jlc29sdmVkKSB7XG5cdFx0XHR1c2VGbkFyZ1Byb21pc2UucmVqZWN0KGUpO1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHQvLyBvdGhlcndpc2UgcmUtdGhyb3cgdG8gYXZvaWQgc2lsZW5jaW5nIGVycm9yIGR1cmluZyBjbGVhbnVwXG5cdFx0dGhyb3cgZTtcblx0fSk7XG5cdHJldHVybiB1c2VGbkFyZ1Byb21pc2U7XG59XG5mdW5jdGlvbiByZXNvbHZlRGVwcyh1c2VkRml4dHVyZXMsIHJlZ2lzdHJhdGlvbnMsIGRlcFNldCA9IG5ldyBTZXQoKSwgcGVuZGluZ0ZpeHR1cmVzID0gW10pIHtcblx0dXNlZEZpeHR1cmVzLmZvckVhY2goKGZpeHR1cmUpID0+IHtcblx0XHRpZiAocGVuZGluZ0ZpeHR1cmVzLmluY2x1ZGVzKGZpeHR1cmUpKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGlmICghaXNGaXh0dXJlRnVuY3Rpb24oZml4dHVyZS52YWx1ZSkgfHwgIWZpeHR1cmUuZGVwcykge1xuXHRcdFx0cGVuZGluZ0ZpeHR1cmVzLnB1c2goZml4dHVyZSk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdGlmIChkZXBTZXQuaGFzKGZpeHR1cmUpKSB7XG5cdFx0XHRpZiAoZml4dHVyZS5wYXJlbnQpIHtcblx0XHRcdFx0Zml4dHVyZSA9IGZpeHR1cmUucGFyZW50O1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKGBDaXJjdWxhciBmaXh0dXJlIGRlcGVuZGVuY3kgZGV0ZWN0ZWQ6ICR7Zml4dHVyZS5uYW1lfSA8LSAke1suLi5kZXBTZXRdLnJldmVyc2UoKS5tYXAoKGQpID0+IGQubmFtZSkuam9pbihcIiA8LSBcIil9YCk7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdGRlcFNldC5hZGQoZml4dHVyZSk7XG5cdFx0cmVzb2x2ZURlcHMoWy4uLmZpeHR1cmUuZGVwc10ubWFwKChuKSA9PiBuID09PSBmaXh0dXJlLm5hbWUgPyBmaXh0dXJlLnBhcmVudCA6IHJlZ2lzdHJhdGlvbnMuZ2V0KG4pKS5maWx0ZXIoKG4pID0+ICEhbiksIHJlZ2lzdHJhdGlvbnMsIGRlcFNldCwgcGVuZGluZ0ZpeHR1cmVzKTtcblx0XHRwZW5kaW5nRml4dHVyZXMucHVzaChmaXh0dXJlKTtcblx0XHRkZXBTZXQuY2xlYXIoKTtcblx0fSk7XG5cdHJldHVybiBwZW5kaW5nRml4dHVyZXM7XG59XG5mdW5jdGlvbiB2YWxpZGF0ZVN1aXRlSG9vayhmbiwgaG9vaywgc3VpdGVFcnJvcikge1xuXHRjb25zdCB1c2VkUHJvcHMgPSBnZXRVc2VkUHJvcHMoZm4sIHtcblx0XHRzb3VyY2VFcnJvcjogc3VpdGVFcnJvcixcblx0XHRzdWl0ZUhvb2s6IGhvb2tcblx0fSk7XG5cdGlmICh1c2VkUHJvcHMuc2l6ZSkge1xuXHRcdGNvbnN0IGVycm9yID0gbmV3IEZpeHR1cmVBY2Nlc3NFcnJvcihgVGhlICR7aG9va30gaG9vayB1c2VzIGZpeHR1cmVzIFwiJHtbLi4udXNlZFByb3BzXS5qb2luKFwiXFxcIiwgXFxcIlwiKX1cIiwgYnV0IGhhcyBubyBhY2Nlc3MgdG8gY29udGV4dC4gYCArIGBEaWQgeW91IGZvcmdldCB0byBjYWxsIGl0IGFzIFwidGVzdC4ke2hvb2t9KClcIiBpbnN0ZWFkIG9mIFwiJHtob29rfSgpXCI/XFxuYCArIGBJZiB5b3UgdXNlZCBpbnRlcm5hbCBcInN1aXRlXCIgdGFzayBhcyB0aGUgZmlyc3QgYXJndW1lbnQgcHJldmlvdXNseSwgYWNjZXNzIGl0IGluIHRoZSBzZWNvbmQgYXJndW1lbnQgaW5zdGVhZC4gYCArIGBTZWUgaHR0cHM6Ly92aXRlc3QuZGV2L2d1aWRlL3Rlc3QtY29udGV4dCNzdWl0ZS1sZXZlbC1ob29rc2ApO1xuXHRcdGlmIChzdWl0ZUVycm9yKSB7XG5cdFx0XHRlcnJvci5zdGFjayA9IHN1aXRlRXJyb3Iuc3RhY2s/LnJlcGxhY2Uoc3VpdGVFcnJvci5tZXNzYWdlLCBlcnJvci5tZXNzYWdlKTtcblx0XHR9XG5cdFx0dGhyb3cgZXJyb3I7XG5cdH1cbn1cbmNvbnN0IGtQcm9wc1N5bWJvbCA9IFN5bWJvbChcIiR2aXRlc3Q6Zml4dHVyZS1wcm9wc1wiKTtcbmNvbnN0IGtQcm9wTmFtZXNTeW1ib2wgPSBTeW1ib2woXCIkdml0ZXN0OmZpeHR1cmUtcHJvcC1uYW1lc1wiKTtcbmZ1bmN0aW9uIGNvbmZpZ3VyZVByb3BzKGZuLCBvcHRpb25zKSB7XG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShmbiwga1Byb3BzU3ltYm9sLCB7XG5cdFx0dmFsdWU6IG9wdGlvbnMsXG5cdFx0ZW51bWVyYWJsZTogZmFsc2Vcblx0fSk7XG59XG5mdW5jdGlvbiBtZW1vUHJvcHMoZm4sIHByb3BzKSB7XG5cdGZuW2tQcm9wTmFtZXNTeW1ib2xdID0gcHJvcHM7XG5cdHJldHVybiBwcm9wcztcbn1cbmZ1bmN0aW9uIGdldFVzZWRQcm9wcyhmbiwgeyBzb3VyY2VFcnJvciwgc3VpdGVIb29rIH0gPSB7fSkge1xuXHRpZiAoa1Byb3BOYW1lc1N5bWJvbCBpbiBmbikge1xuXHRcdHJldHVybiBmbltrUHJvcE5hbWVzU3ltYm9sXTtcblx0fVxuXHRjb25zdCB7IGluZGV4OiBmaXh0dXJlc0luZGV4ID0gMCwgb3JpZ2luYWw6IGltcGxlbWVudGF0aW9uID0gZm4gfSA9IGtQcm9wc1N5bWJvbCBpbiBmbiA/IGZuW2tQcm9wc1N5bWJvbF0gOiB7fTtcblx0bGV0IGZuU3RyaW5nID0gZmlsdGVyT3V0Q29tbWVudHMoaW1wbGVtZW50YXRpb24udG9TdHJpbmcoKSk7XG5cdC8vIG1hdGNoIGxvd2VyZWQgYXN5bmMgZnVuY3Rpb24gYW5kIHN0cmlwIGl0IG9mZlxuXHQvLyBleGFtcGxlIGNvZGUgb24gZXNidWlsZC10cnkgaHR0cHM6Ly9lc2J1aWxkLmdpdGh1Yi5pby90cnkvI1lnQXdMakkwTGpBQUxTMXpkWEJ3YjNKMFpXUTZZWE41Ym1NdFlYZGhhWFE5Wm1Gc2MyVUFaUUJsYm5SeWVTNTBjd0JqYjI1emRDQnZJRDBnZXdvZ0lHWXhPaUJoYzNsdVl5QW9LU0E5UGlCN2ZTd0tJQ0JtTWpvZ1lYTjVibU1nS0dFcElEMCtJSHQ5TEFvZ0lHWXpPaUJoYzNsdVl5QW9ZU3dnWWlrZ1BUNGdlMzBzQ2lBZ1pqUTZJR0Z6ZVc1aklHWjFibU4wYVc5dUtHRXBJSHQ5TEFvZ0lHWTFPaUJoYzNsdVl5Qm1kVzVqZEdsdmJpQm1aaWhoS1NCN2ZTd0tJQ0JoYzNsdVl5Qm1OaWhoS1NCN2ZTd0tDaUFnWnpFNklHRnplVzVqSUNncElEMCtJSHQ5TEFvZ0lHY3lPaUJoYzNsdVl5QW9leUJoSUgwcElEMCtJSHQ5TEFvZ0lHY3pPaUJoYzNsdVl5QW9leUJoSUgwc0lHSXBJRDArSUh0OUxBb2dJR2MwT2lCaGMzbHVZeUJtZFc1amRHbHZiaUFvZXlCaElIMHBJSHQ5TEFvZ0lHYzFPaUJoYzNsdVl5Qm1kVzVqZEdsdmJpQm5aeWg3SUdFZ2ZTa2dlMzBzQ2lBZ1lYTjVibU1nWnpZb2V5QmhJSDBwSUh0OUxBb0tJQ0JvTVRvZ1lYTjVibU1nS0NrZ1BUNGdlMzBzQ2lBZ0x5OGdZMjl0YldWdWRDQmlaWFIzWldWdUNpQWdhREk2SUdGemVXNWpJQ2hoS1NBOVBpQjdmU3dLZlFcblx0Ly8gICBfX2FzeW5jKHRoaXMsIG51bGwsIGZ1bmN0aW9uKlxuXHQvLyAgIF9fYXN5bmModGhpcywgYXJndW1lbnRzLCBmdW5jdGlvbipcblx0Ly8gICBfX2FzeW5jKHRoaXMsIFtfMCwgXzFdLCBmdW5jdGlvbipcblx0aWYgKC9fX2FzeW5jXFwoKD86dGhpc3xudWxsKSwgKD86bnVsbHxhcmd1bWVudHN8XFxbW18wLTksIF0qXFxdKSwgZnVuY3Rpb25cXCovLnRlc3QoZm5TdHJpbmcpKSB7XG5cdFx0Zm5TdHJpbmcgPSBmblN0cmluZy5zcGxpdCgvX19hc3luY1xcKCg/OnRoaXN8bnVsbCksLylbMV07XG5cdH1cblx0Y29uc3QgbWF0Y2ggPSBmblN0cmluZy5tYXRjaCgvW14oXSpcXCgoW14pXSopLyk7XG5cdGlmICghbWF0Y2gpIHtcblx0XHRyZXR1cm4gbWVtb1Byb3BzKGZuLCBuZXcgU2V0KCkpO1xuXHR9XG5cdGNvbnN0IGFyZ3MgPSBzcGxpdEJ5Q29tbWEobWF0Y2hbMV0pO1xuXHRpZiAoIWFyZ3MubGVuZ3RoKSB7XG5cdFx0cmV0dXJuIG1lbW9Qcm9wcyhmbiwgbmV3IFNldCgpKTtcblx0fVxuXHRjb25zdCBmaXh0dXJlc0FyZ3VtZW50ID0gYXJnc1tmaXh0dXJlc0luZGV4XTtcblx0aWYgKCFmaXh0dXJlc0FyZ3VtZW50KSB7XG5cdFx0cmV0dXJuIG1lbW9Qcm9wcyhmbiwgbmV3IFNldCgpKTtcblx0fVxuXHRpZiAoIShmaXh0dXJlc0FyZ3VtZW50WzBdID09PSBcIntcIiAmJiBmaXh0dXJlc0FyZ3VtZW50LmVuZHNXaXRoKFwifVwiKSkpIHtcblx0XHRjb25zdCBvcmRpbmFsQXJndW1lbnQgPSBvcmRpbmFsKGZpeHR1cmVzSW5kZXggKyAxKTtcblx0XHRjb25zdCBlcnJvciA9IG5ldyBGaXh0dXJlUGFyc2VFcnJvcihgVGhlICR7b3JkaW5hbEFyZ3VtZW50fSBhcmd1bWVudCBpbnNpZGUgYSBmaXh0dXJlIG11c3QgdXNlIG9iamVjdCBkZXN0cnVjdHVyaW5nIHBhdHRlcm4sIGUuZy4gKHsgdGFzayB9ID0+IHt9KS4gYCArIGBJbnN0ZWFkLCByZWNlaXZlZCBcIiR7Zml4dHVyZXNBcmd1bWVudH1cIi5gICsgYCR7c3VpdGVIb29rID8gYCBJZiB5b3UgdXNlZCBpbnRlcm5hbCBcInN1aXRlXCIgdGFzayBhcyB0aGUgJHtvcmRpbmFsQXJndW1lbnR9IGFyZ3VtZW50IHByZXZpb3VzbHksIGFjY2VzcyBpdCBpbiB0aGUgJHtvcmRpbmFsKGZpeHR1cmVzSW5kZXggKyAyKX0gYXJndW1lbnQgaW5zdGVhZC5gIDogXCJcIn1gKTtcblx0XHRpZiAoc291cmNlRXJyb3IpIHtcblx0XHRcdGVycm9yLnN0YWNrID0gc291cmNlRXJyb3Iuc3RhY2s/LnJlcGxhY2Uoc291cmNlRXJyb3IubWVzc2FnZSwgZXJyb3IubWVzc2FnZSk7XG5cdFx0fVxuXHRcdHRocm93IGVycm9yO1xuXHR9XG5cdGNvbnN0IF9maXJzdCA9IGZpeHR1cmVzQXJndW1lbnQuc2xpY2UoMSwgLTEpLnJlcGxhY2UoL1xccy9nLCBcIlwiKTtcblx0Y29uc3QgcHJvcHMgPSBzcGxpdEJ5Q29tbWEoX2ZpcnN0KS5tYXAoKHByb3ApID0+IHtcblx0XHRyZXR1cm4gcHJvcC5yZXBsYWNlKC86Lip8PS4qL2csIFwiXCIpO1xuXHR9KTtcblx0Y29uc3QgbGFzdCA9IHByb3BzLmF0KC0xKTtcblx0aWYgKGxhc3QgJiYgbGFzdC5zdGFydHNXaXRoKFwiLi4uXCIpKSB7XG5cdFx0Y29uc3QgZXJyb3IgPSBuZXcgRml4dHVyZVBhcnNlRXJyb3IoYFJlc3QgcGFyYW1ldGVycyBhcmUgbm90IHN1cHBvcnRlZCBpbiBmaXh0dXJlcywgcmVjZWl2ZWQgXCIke2xhc3R9XCIuYCk7XG5cdFx0aWYgKHNvdXJjZUVycm9yKSB7XG5cdFx0XHRlcnJvci5zdGFjayA9IHNvdXJjZUVycm9yLnN0YWNrPy5yZXBsYWNlKHNvdXJjZUVycm9yLm1lc3NhZ2UsIGVycm9yLm1lc3NhZ2UpO1xuXHRcdH1cblx0XHR0aHJvdyBlcnJvcjtcblx0fVxuXHRyZXR1cm4gbWVtb1Byb3BzKGZuLCBuZXcgU2V0KHByb3BzKSk7XG59XG5mdW5jdGlvbiBzcGxpdEJ5Q29tbWEocykge1xuXHRjb25zdCByZXN1bHQgPSBbXTtcblx0Y29uc3Qgc3RhY2sgPSBbXTtcblx0bGV0IHN0YXJ0ID0gMDtcblx0Zm9yIChsZXQgaSA9IDA7IGkgPCBzLmxlbmd0aDsgaSsrKSB7XG5cdFx0aWYgKHNbaV0gPT09IFwie1wiIHx8IHNbaV0gPT09IFwiW1wiKSB7XG5cdFx0XHRzdGFjay5wdXNoKHNbaV0gPT09IFwie1wiID8gXCJ9XCIgOiBcIl1cIik7XG5cdFx0fSBlbHNlIGlmIChzW2ldID09PSBzdGFjay5hdCgtMSkpIHtcblx0XHRcdHN0YWNrLnBvcCgpO1xuXHRcdH0gZWxzZSBpZiAoIXN0YWNrLmxlbmd0aCAmJiBzW2ldID09PSBcIixcIikge1xuXHRcdFx0Y29uc3QgdG9rZW4gPSBzLnN1YnN0cmluZyhzdGFydCwgaSkudHJpbSgpO1xuXHRcdFx0aWYgKHRva2VuKSB7XG5cdFx0XHRcdHJlc3VsdC5wdXNoKHRva2VuKTtcblx0XHRcdH1cblx0XHRcdHN0YXJ0ID0gaSArIDE7XG5cdFx0fVxuXHR9XG5cdGNvbnN0IGxhc3RUb2tlbiA9IHMuc3Vic3RyaW5nKHN0YXJ0KS50cmltKCk7XG5cdGlmIChsYXN0VG9rZW4pIHtcblx0XHRyZXN1bHQucHVzaChsYXN0VG9rZW4pO1xuXHR9XG5cdHJldHVybiByZXN1bHQ7XG59XG5cbmxldCBfdGVzdDtcbmZ1bmN0aW9uIHNldEN1cnJlbnRUZXN0KHRlc3QpIHtcblx0X3Rlc3QgPSB0ZXN0O1xufVxuZnVuY3Rpb24gZ2V0Q3VycmVudFRlc3QoKSB7XG5cdHJldHVybiBfdGVzdDtcbn1cbmNvbnN0IHRlc3RzID0gW107XG5mdW5jdGlvbiBhZGRSdW5uaW5nVGVzdCh0ZXN0KSB7XG5cdHRlc3RzLnB1c2godGVzdCk7XG5cdHJldHVybiAoKSA9PiB7XG5cdFx0dGVzdHMuc3BsaWNlKHRlc3RzLmluZGV4T2YodGVzdCkpO1xuXHR9O1xufVxuZnVuY3Rpb24gZ2V0UnVubmluZ1Rlc3RzKCkge1xuXHRyZXR1cm4gdGVzdHM7XG59XG5cbmNvbnN0IGtDaGFpbmFibGVDb250ZXh0ID0gU3ltYm9sKFwia0NoYWluYWJsZUNvbnRleHRcIik7XG5mdW5jdGlvbiBnZXRDaGFpbmFibGVDb250ZXh0KGNoYWluYWJsZSkge1xuXHRyZXR1cm4gY2hhaW5hYmxlPy5ba0NoYWluYWJsZUNvbnRleHRdO1xufVxuZnVuY3Rpb24gY3JlYXRlQ2hhaW5hYmxlKGtleXMsIGZuLCBjb250ZXh0KSB7XG5cdGZ1bmN0aW9uIGNyZWF0ZShjb250ZXh0KSB7XG5cdFx0Y29uc3QgY2hhaW4gPSBmdW5jdGlvbiguLi5hcmdzKSB7XG5cdFx0XHRyZXR1cm4gZm4uYXBwbHkoY29udGV4dCwgYXJncyk7XG5cdFx0fTtcblx0XHRPYmplY3QuYXNzaWduKGNoYWluLCBmbik7XG5cdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGNoYWluLCBrQ2hhaW5hYmxlQ29udGV4dCwge1xuXHRcdFx0dmFsdWU6IHtcblx0XHRcdFx0d2l0aENvbnRleHQ6ICgpID0+IGNoYWluLmJpbmQoY29udGV4dCksXG5cdFx0XHRcdGdldEZpeHR1cmVzOiAoKSA9PiBjb250ZXh0LmZpeHR1cmVzLFxuXHRcdFx0XHRzZXRDb250ZXh0OiAoa2V5LCB2YWx1ZSkgPT4ge1xuXHRcdFx0XHRcdGNvbnRleHRba2V5XSA9IHZhbHVlO1xuXHRcdFx0XHR9LFxuXHRcdFx0XHRtZXJnZUNvbnRleHQ6IChjdHgpID0+IHtcblx0XHRcdFx0XHRPYmplY3QuYXNzaWduKGNvbnRleHQsIGN0eCk7XG5cdFx0XHRcdH1cblx0XHRcdH0sXG5cdFx0XHRlbnVtZXJhYmxlOiBmYWxzZVxuXHRcdH0pO1xuXHRcdGZvciAoY29uc3Qga2V5IG9mIGtleXMpIHtcblx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShjaGFpbiwga2V5LCB7IGdldCgpIHtcblx0XHRcdFx0cmV0dXJuIGNyZWF0ZSh7XG5cdFx0XHRcdFx0Li4uY29udGV4dCxcblx0XHRcdFx0XHRba2V5XTogdHJ1ZVxuXHRcdFx0XHR9KTtcblx0XHRcdH0gfSk7XG5cdFx0fVxuXHRcdHJldHVybiBjaGFpbjtcblx0fVxuXHRjb25zdCBjaGFpbiA9IGNyZWF0ZShjb250ZXh0ID8/IHt9KTtcblx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGNoYWluLCBcImZuXCIsIHtcblx0XHR2YWx1ZTogZm4sXG5cdFx0ZW51bWVyYWJsZTogZmFsc2Vcblx0fSk7XG5cdHJldHVybiBjaGFpbjtcbn1cblxuZnVuY3Rpb24gZ2V0RGVmYXVsdEhvb2tUaW1lb3V0KCkge1xuXHRyZXR1cm4gZ2V0UnVubmVyKCkuY29uZmlnLmhvb2tUaW1lb3V0O1xufVxuY29uc3QgQ0xFQU5VUF9USU1FT1VUX0tFWSA9IFN5bWJvbC5mb3IoXCJWSVRFU1RfQ0xFQU5VUF9USU1FT1VUXCIpO1xuY29uc3QgQ0xFQU5VUF9TVEFDS19UUkFDRV9LRVkgPSBTeW1ib2wuZm9yKFwiVklURVNUX0NMRUFOVVBfU1RBQ0tfVFJBQ0VcIik7XG5jb25zdCBBUk9VTkRfVElNRU9VVF9LRVkgPSBTeW1ib2wuZm9yKFwiVklURVNUX0FST1VORF9USU1FT1VUXCIpO1xuY29uc3QgQVJPVU5EX1NUQUNLX1RSQUNFX0tFWSA9IFN5bWJvbC5mb3IoXCJWSVRFU1RfQVJPVU5EX1NUQUNLX1RSQUNFXCIpO1xuZnVuY3Rpb24gZ2V0QmVmb3JlSG9va0NsZWFudXBDYWxsYmFjayhob29rLCByZXN1bHQsIGNvbnRleHQpIHtcblx0aWYgKHR5cGVvZiByZXN1bHQgPT09IFwiZnVuY3Rpb25cIikge1xuXHRcdGNvbnN0IHRpbWVvdXQgPSBDTEVBTlVQX1RJTUVPVVRfS0VZIGluIGhvb2sgJiYgdHlwZW9mIGhvb2tbQ0xFQU5VUF9USU1FT1VUX0tFWV0gPT09IFwibnVtYmVyXCIgPyBob29rW0NMRUFOVVBfVElNRU9VVF9LRVldIDogZ2V0RGVmYXVsdEhvb2tUaW1lb3V0KCk7XG5cdFx0Y29uc3Qgc3RhY2tUcmFjZUVycm9yID0gQ0xFQU5VUF9TVEFDS19UUkFDRV9LRVkgaW4gaG9vayAmJiBob29rW0NMRUFOVVBfU1RBQ0tfVFJBQ0VfS0VZXSBpbnN0YW5jZW9mIEVycm9yID8gaG9va1tDTEVBTlVQX1NUQUNLX1RSQUNFX0tFWV0gOiB1bmRlZmluZWQ7XG5cdFx0cmV0dXJuIHdpdGhUaW1lb3V0KHJlc3VsdCwgdGltZW91dCwgdHJ1ZSwgc3RhY2tUcmFjZUVycm9yLCAoXywgZXJyb3IpID0+IHtcblx0XHRcdGlmIChjb250ZXh0KSB7XG5cdFx0XHRcdGFib3J0Q29udGV4dFNpZ25hbChjb250ZXh0LCBlcnJvcik7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cbn1cbi8qKlxuKiBSZWdpc3RlcnMgYSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBleGVjdXRlZCBvbmNlIGJlZm9yZSBhbGwgdGVzdHMgd2l0aGluIHRoZSBjdXJyZW50IHN1aXRlLlxuKiBUaGlzIGhvb2sgaXMgdXNlZnVsIGZvciBzY2VuYXJpb3Mgd2hlcmUgeW91IG5lZWQgdG8gcGVyZm9ybSBzZXR1cCBvcGVyYXRpb25zIHRoYXQgYXJlIGNvbW1vbiB0byBhbGwgdGVzdHMgaW4gYSBzdWl0ZSwgc3VjaCBhcyBpbml0aWFsaXppbmcgYSBkYXRhYmFzZSBjb25uZWN0aW9uIG9yIHNldHRpbmcgdXAgYSB0ZXN0IGVudmlyb25tZW50LlxuKlxuKiAqKk5vdGU6KiogVGhlIGBiZWZvcmVBbGxgIGhvb2tzIGFyZSBleGVjdXRlZCBpbiB0aGUgb3JkZXIgdGhleSBhcmUgZGVmaW5lZCBvbmUgYWZ0ZXIgYW5vdGhlci4gWW91IGNhbiBjb25maWd1cmUgdGhpcyBieSBjaGFuZ2luZyB0aGUgYHNlcXVlbmNlLmhvb2tzYCBvcHRpb24gaW4gdGhlIGNvbmZpZyBmaWxlLlxuKlxuKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBleGVjdXRlZCBiZWZvcmUgYWxsIHRlc3RzLlxuKiBAcGFyYW0ge251bWJlcn0gW3RpbWVvdXRdIC0gT3B0aW9uYWwgdGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIHRoZSBob29rLiBJZiBub3QgcHJvdmlkZWQsIHRoZSBkZWZhdWx0IGhvb2sgdGltZW91dCBmcm9tIHRoZSBydW5uZXIncyBjb25maWd1cmF0aW9uIGlzIHVzZWQuXG4qIEByZXR1cm5zIHt2b2lkfVxuKiBAZXhhbXBsZVxuKiBgYGB0c1xuKiAvLyBFeGFtcGxlIG9mIHVzaW5nIGJlZm9yZUFsbCB0byBzZXQgdXAgYSBkYXRhYmFzZSBjb25uZWN0aW9uXG4qIGJlZm9yZUFsbChhc3luYyAoKSA9PiB7XG4qICAgYXdhaXQgZGF0YWJhc2UuY29ubmVjdCgpO1xuKiB9KTtcbiogYGBgXG4qL1xuZnVuY3Rpb24gYmVmb3JlQWxsKGZuLCB0aW1lb3V0ID0gZ2V0RGVmYXVsdEhvb2tUaW1lb3V0KCkpIHtcblx0YXNzZXJ0VHlwZXMoZm4sIFwiXFxcImJlZm9yZUFsbFxcXCIgY2FsbGJhY2tcIiwgW1wiZnVuY3Rpb25cIl0pO1xuXHRjb25zdCBzdGFja1RyYWNlRXJyb3IgPSBuZXcgRXJyb3IoXCJTVEFDS19UUkFDRV9FUlJPUlwiKTtcblx0Y29uc3QgY29udGV4dCA9IGdldENoYWluYWJsZUNvbnRleHQodGhpcyk7XG5cdHJldHVybiBnZXRDdXJyZW50U3VpdGUoKS5vbihcImJlZm9yZUFsbFwiLCBPYmplY3QuYXNzaWduKHdpdGhUaW1lb3V0KHdpdGhTdWl0ZUZpeHR1cmVzKFwiYmVmb3JlQWxsXCIsIGZuLCBjb250ZXh0LCBzdGFja1RyYWNlRXJyb3IpLCB0aW1lb3V0LCB0cnVlLCBzdGFja1RyYWNlRXJyb3IpLCB7XG5cdFx0W0NMRUFOVVBfVElNRU9VVF9LRVldOiB0aW1lb3V0LFxuXHRcdFtDTEVBTlVQX1NUQUNLX1RSQUNFX0tFWV06IHN0YWNrVHJhY2VFcnJvclxuXHR9KSk7XG59XG4vKipcbiogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgZXhlY3V0ZWQgb25jZSBhZnRlciBhbGwgdGVzdHMgd2l0aGluIHRoZSBjdXJyZW50IHN1aXRlIGhhdmUgY29tcGxldGVkLlxuKiBUaGlzIGhvb2sgaXMgdXNlZnVsIGZvciBzY2VuYXJpb3Mgd2hlcmUgeW91IG5lZWQgdG8gcGVyZm9ybSBjbGVhbnVwIG9wZXJhdGlvbnMgYWZ0ZXIgYWxsIHRlc3RzIGluIGEgc3VpdGUgaGF2ZSBydW4sIHN1Y2ggYXMgY2xvc2luZyBkYXRhYmFzZSBjb25uZWN0aW9ucyBvciBjbGVhbmluZyB1cCB0ZW1wb3JhcnkgZmlsZXMuXG4qXG4qICoqTm90ZToqKiBUaGUgYGFmdGVyQWxsYCBob29rcyBhcmUgcnVubmluZyBpbiByZXZlcnNlIG9yZGVyIG9mIHRoZWlyIHJlZ2lzdHJhdGlvbi4gWW91IGNhbiBjb25maWd1cmUgdGhpcyBieSBjaGFuZ2luZyB0aGUgYHNlcXVlbmNlLmhvb2tzYCBvcHRpb24gaW4gdGhlIGNvbmZpZyBmaWxlLlxuKlxuKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBleGVjdXRlZCBhZnRlciBhbGwgdGVzdHMuXG4qIEBwYXJhbSB7bnVtYmVyfSBbdGltZW91dF0gLSBPcHRpb25hbCB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIGhvb2suIElmIG5vdCBwcm92aWRlZCwgdGhlIGRlZmF1bHQgaG9vayB0aW1lb3V0IGZyb20gdGhlIHJ1bm5lcidzIGNvbmZpZ3VyYXRpb24gaXMgdXNlZC5cbiogQHJldHVybnMge3ZvaWR9XG4qIEBleGFtcGxlXG4qIGBgYHRzXG4qIC8vIEV4YW1wbGUgb2YgdXNpbmcgYWZ0ZXJBbGwgdG8gY2xvc2UgYSBkYXRhYmFzZSBjb25uZWN0aW9uXG4qIGFmdGVyQWxsKGFzeW5jICgpID0+IHtcbiogICBhd2FpdCBkYXRhYmFzZS5kaXNjb25uZWN0KCk7XG4qIH0pO1xuKiBgYGBcbiovXG5mdW5jdGlvbiBhZnRlckFsbChmbiwgdGltZW91dCkge1xuXHRhc3NlcnRUeXBlcyhmbiwgXCJcXFwiYWZ0ZXJBbGxcXFwiIGNhbGxiYWNrXCIsIFtcImZ1bmN0aW9uXCJdKTtcblx0Y29uc3QgY29udGV4dCA9IGdldENoYWluYWJsZUNvbnRleHQodGhpcyk7XG5cdGNvbnN0IHN0YWNrVHJhY2VFcnJvciA9IG5ldyBFcnJvcihcIlNUQUNLX1RSQUNFX0VSUk9SXCIpO1xuXHRyZXR1cm4gZ2V0Q3VycmVudFN1aXRlKCkub24oXCJhZnRlckFsbFwiLCB3aXRoVGltZW91dCh3aXRoU3VpdGVGaXh0dXJlcyhcImFmdGVyQWxsXCIsIGZuLCBjb250ZXh0LCBzdGFja1RyYWNlRXJyb3IpLCB0aW1lb3V0ID8/IGdldERlZmF1bHRIb29rVGltZW91dCgpLCB0cnVlLCBzdGFja1RyYWNlRXJyb3IpKTtcbn1cbi8qKlxuKiBSZWdpc3RlcnMgYSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBleGVjdXRlZCBiZWZvcmUgZWFjaCB0ZXN0IHdpdGhpbiB0aGUgY3VycmVudCBzdWl0ZS5cbiogVGhpcyBob29rIGlzIHVzZWZ1bCBmb3Igc2NlbmFyaW9zIHdoZXJlIHlvdSBuZWVkIHRvIHJlc2V0IG9yIHJlaW5pdGlhbGl6ZSB0aGUgdGVzdCBlbnZpcm9ubWVudCBiZWZvcmUgZWFjaCB0ZXN0IHJ1bnMsIHN1Y2ggYXMgcmVzZXR0aW5nIGRhdGFiYXNlIHN0YXRlcywgY2xlYXJpbmcgY2FjaGVzLCBvciByZWluaXRpYWxpemluZyB2YXJpYWJsZXMuXG4qXG4qICoqTm90ZToqKiBUaGUgYGJlZm9yZUVhY2hgIGhvb2tzIGFyZSBleGVjdXRlZCBpbiB0aGUgb3JkZXIgdGhleSBhcmUgZGVmaW5lZCBvbmUgYWZ0ZXIgYW5vdGhlci4gWW91IGNhbiBjb25maWd1cmUgdGhpcyBieSBjaGFuZ2luZyB0aGUgYHNlcXVlbmNlLmhvb2tzYCBvcHRpb24gaW4gdGhlIGNvbmZpZyBmaWxlLlxuKlxuKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBleGVjdXRlZCBiZWZvcmUgZWFjaCB0ZXN0LiBUaGlzIGZ1bmN0aW9uIHJlY2VpdmVzIGFuIGBUZXN0Q29udGV4dGAgcGFyYW1ldGVyIGlmIGFkZGl0aW9uYWwgdGVzdCBjb250ZXh0IGlzIG5lZWRlZC5cbiogQHBhcmFtIHtudW1iZXJ9IFt0aW1lb3V0XSAtIE9wdGlvbmFsIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciB0aGUgaG9vay4gSWYgbm90IHByb3ZpZGVkLCB0aGUgZGVmYXVsdCBob29rIHRpbWVvdXQgZnJvbSB0aGUgcnVubmVyJ3MgY29uZmlndXJhdGlvbiBpcyB1c2VkLlxuKiBAcmV0dXJucyB7dm9pZH1cbiogQGV4YW1wbGVcbiogYGBgdHNcbiogLy8gRXhhbXBsZSBvZiB1c2luZyBiZWZvcmVFYWNoIHRvIHJlc2V0IGEgZGF0YWJhc2Ugc3RhdGVcbiogYmVmb3JlRWFjaChhc3luYyAoKSA9PiB7XG4qICAgYXdhaXQgZGF0YWJhc2UucmVzZXQoKTtcbiogfSk7XG4qIGBgYFxuKi9cbmZ1bmN0aW9uIGJlZm9yZUVhY2goZm4sIHRpbWVvdXQgPSBnZXREZWZhdWx0SG9va1RpbWVvdXQoKSkge1xuXHRhc3NlcnRUeXBlcyhmbiwgXCJcXFwiYmVmb3JlRWFjaFxcXCIgY2FsbGJhY2tcIiwgW1wiZnVuY3Rpb25cIl0pO1xuXHRjb25zdCBzdGFja1RyYWNlRXJyb3IgPSBuZXcgRXJyb3IoXCJTVEFDS19UUkFDRV9FUlJPUlwiKTtcblx0Y29uc3Qgd3JhcHBlciA9IChjb250ZXh0LCBzdWl0ZSkgPT4ge1xuXHRcdGNvbnN0IGZpeHR1cmVSZXNvbHZlciA9IHdpdGhGaXh0dXJlcyhmbiwgeyBzdWl0ZSB9KTtcblx0XHRyZXR1cm4gZml4dHVyZVJlc29sdmVyKGNvbnRleHQpO1xuXHR9O1xuXHRyZXR1cm4gZ2V0Q3VycmVudFN1aXRlKCkub24oXCJiZWZvcmVFYWNoXCIsIE9iamVjdC5hc3NpZ24od2l0aFRpbWVvdXQod3JhcHBlciwgdGltZW91dCA/PyBnZXREZWZhdWx0SG9va1RpbWVvdXQoKSwgdHJ1ZSwgc3RhY2tUcmFjZUVycm9yLCBhYm9ydElmVGltZW91dCksIHtcblx0XHRbQ0xFQU5VUF9USU1FT1VUX0tFWV06IHRpbWVvdXQsXG5cdFx0W0NMRUFOVVBfU1RBQ0tfVFJBQ0VfS0VZXTogc3RhY2tUcmFjZUVycm9yXG5cdH0pKTtcbn1cbi8qKlxuKiBSZWdpc3RlcnMgYSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBleGVjdXRlZCBhZnRlciBlYWNoIHRlc3Qgd2l0aGluIHRoZSBjdXJyZW50IHN1aXRlIGhhcyBjb21wbGV0ZWQuXG4qIFRoaXMgaG9vayBpcyB1c2VmdWwgZm9yIHNjZW5hcmlvcyB3aGVyZSB5b3UgbmVlZCB0byBjbGVhbiB1cCBvciByZXNldCB0aGUgdGVzdCBlbnZpcm9ubWVudCBhZnRlciBlYWNoIHRlc3QgcnVucywgc3VjaCBhcyBkZWxldGluZyB0ZW1wb3JhcnkgZmlsZXMsIGNsZWFyaW5nIHRlc3Qtc3BlY2lmaWMgZGF0YWJhc2UgZW50cmllcywgb3IgcmVzZXR0aW5nIG1vY2tlZCBmdW5jdGlvbnMuXG4qXG4qICoqTm90ZToqKiBUaGUgYGFmdGVyRWFjaGAgaG9va3MgYXJlIHJ1bm5pbmcgaW4gcmV2ZXJzZSBvcmRlciBvZiB0aGVpciByZWdpc3RyYXRpb24uIFlvdSBjYW4gY29uZmlndXJlIHRoaXMgYnkgY2hhbmdpbmcgdGhlIGBzZXF1ZW5jZS5ob29rc2Agb3B0aW9uIGluIHRoZSBjb25maWcgZmlsZS5cbipcbiogQHBhcmFtIHtGdW5jdGlvbn0gZm4gLSBUaGUgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgZXhlY3V0ZWQgYWZ0ZXIgZWFjaCB0ZXN0LiBUaGlzIGZ1bmN0aW9uIHJlY2VpdmVzIGFuIGBUZXN0Q29udGV4dGAgcGFyYW1ldGVyIGlmIGFkZGl0aW9uYWwgdGVzdCBjb250ZXh0IGlzIG5lZWRlZC5cbiogQHBhcmFtIHtudW1iZXJ9IFt0aW1lb3V0XSAtIE9wdGlvbmFsIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciB0aGUgaG9vay4gSWYgbm90IHByb3ZpZGVkLCB0aGUgZGVmYXVsdCBob29rIHRpbWVvdXQgZnJvbSB0aGUgcnVubmVyJ3MgY29uZmlndXJhdGlvbiBpcyB1c2VkLlxuKiBAcmV0dXJucyB7dm9pZH1cbiogQGV4YW1wbGVcbiogYGBgdHNcbiogLy8gRXhhbXBsZSBvZiB1c2luZyBhZnRlckVhY2ggdG8gZGVsZXRlIHRlbXBvcmFyeSBmaWxlcyBjcmVhdGVkIGR1cmluZyBhIHRlc3RcbiogYWZ0ZXJFYWNoKGFzeW5jICgpID0+IHtcbiogICBhd2FpdCBmaWxlU3lzdGVtLmRlbGV0ZVRlbXBGaWxlcygpO1xuKiB9KTtcbiogYGBgXG4qL1xuZnVuY3Rpb24gYWZ0ZXJFYWNoKGZuLCB0aW1lb3V0KSB7XG5cdGFzc2VydFR5cGVzKGZuLCBcIlxcXCJhZnRlckVhY2hcXFwiIGNhbGxiYWNrXCIsIFtcImZ1bmN0aW9uXCJdKTtcblx0Y29uc3Qgd3JhcHBlciA9IChjb250ZXh0LCBzdWl0ZSkgPT4ge1xuXHRcdGNvbnN0IGZpeHR1cmVSZXNvbHZlciA9IHdpdGhGaXh0dXJlcyhmbiwgeyBzdWl0ZSB9KTtcblx0XHRyZXR1cm4gZml4dHVyZVJlc29sdmVyKGNvbnRleHQpO1xuXHR9O1xuXHRyZXR1cm4gZ2V0Q3VycmVudFN1aXRlKCkub24oXCJhZnRlckVhY2hcIiwgd2l0aFRpbWVvdXQod3JhcHBlciwgdGltZW91dCA/PyBnZXREZWZhdWx0SG9va1RpbWVvdXQoKSwgdHJ1ZSwgbmV3IEVycm9yKFwiU1RBQ0tfVFJBQ0VfRVJST1JcIiksIGFib3J0SWZUaW1lb3V0KSk7XG59XG4vKipcbiogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgZnVuY3Rpb24gdG8gYmUgZXhlY3V0ZWQgd2hlbiBhIHRlc3QgZmFpbHMgd2l0aGluIHRoZSBjdXJyZW50IHN1aXRlLlxuKiBUaGlzIGZ1bmN0aW9uIGFsbG93cyBmb3IgY3VzdG9tIGFjdGlvbnMgdG8gYmUgcGVyZm9ybWVkIGluIHJlc3BvbnNlIHRvIHRlc3QgZmFpbHVyZXMsIHN1Y2ggYXMgbG9nZ2luZywgY2xlYW51cCwgb3IgYWRkaXRpb25hbCBkaWFnbm9zdGljcy5cbipcbiogKipOb3RlOioqIFRoZSBgb25UZXN0RmFpbGVkYCBob29rcyBhcmUgcnVubmluZyBpbiByZXZlcnNlIG9yZGVyIG9mIHRoZWlyIHJlZ2lzdHJhdGlvbi4gWW91IGNhbiBjb25maWd1cmUgdGhpcyBieSBjaGFuZ2luZyB0aGUgYHNlcXVlbmNlLmhvb2tzYCBvcHRpb24gaW4gdGhlIGNvbmZpZyBmaWxlLlxuKlxuKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBleGVjdXRlZCB1cG9uIGEgdGVzdCBmYWlsdXJlLiBUaGUgZnVuY3Rpb24gcmVjZWl2ZXMgdGhlIHRlc3QgcmVzdWx0IChpbmNsdWRpbmcgZXJyb3JzKS5cbiogQHBhcmFtIHtudW1iZXJ9IFt0aW1lb3V0XSAtIE9wdGlvbmFsIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIGZvciB0aGUgaG9vay4gSWYgbm90IHByb3ZpZGVkLCB0aGUgZGVmYXVsdCBob29rIHRpbWVvdXQgZnJvbSB0aGUgcnVubmVyJ3MgY29uZmlndXJhdGlvbiBpcyB1c2VkLlxuKiBAdGhyb3dzIHtFcnJvcn0gVGhyb3dzIGFuIGVycm9yIGlmIHRoZSBmdW5jdGlvbiBpcyBub3QgY2FsbGVkIHdpdGhpbiBhIHRlc3QuXG4qIEByZXR1cm5zIHt2b2lkfVxuKiBAZXhhbXBsZVxuKiBgYGB0c1xuKiAvLyBFeGFtcGxlIG9mIHVzaW5nIG9uVGVzdEZhaWxlZCB0byBsb2cgZmFpbHVyZSBkZXRhaWxzXG4qIG9uVGVzdEZhaWxlZCgoeyBlcnJvcnMgfSkgPT4ge1xuKiAgIGNvbnNvbGUubG9nKGBUZXN0IGZhaWxlZDogJHt0ZXN0Lm5hbWV9YCwgZXJyb3JzKTtcbiogfSk7XG4qIGBgYFxuKi9cbmNvbnN0IG9uVGVzdEZhaWxlZCA9IGNyZWF0ZVRlc3RIb29rKFwib25UZXN0RmFpbGVkXCIsICh0ZXN0LCBoYW5kbGVyLCB0aW1lb3V0KSA9PiB7XG5cdHRlc3Qub25GYWlsZWQgfHw9IFtdO1xuXHR0ZXN0Lm9uRmFpbGVkLnB1c2god2l0aFRpbWVvdXQoaGFuZGxlciwgdGltZW91dCA/PyBnZXREZWZhdWx0SG9va1RpbWVvdXQoKSwgdHJ1ZSwgbmV3IEVycm9yKFwiU1RBQ0tfVFJBQ0VfRVJST1JcIiksIGFib3J0SWZUaW1lb3V0KSk7XG59KTtcbi8qKlxuKiBSZWdpc3RlcnMgYSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBleGVjdXRlZCB3aGVuIHRoZSBjdXJyZW50IHRlc3QgZmluaXNoZXMsIHJlZ2FyZGxlc3Mgb2YgdGhlIG91dGNvbWUgKHBhc3Mgb3IgZmFpbCkuXG4qIFRoaXMgZnVuY3Rpb24gaXMgaWRlYWwgZm9yIHBlcmZvcm1pbmcgYWN0aW9ucyB0aGF0IHNob3VsZCBvY2N1ciBhZnRlciBldmVyeSB0ZXN0IGV4ZWN1dGlvbiwgc3VjaCBhcyBjbGVhbnVwLCBsb2dnaW5nLCBvciByZXNldHRpbmcgc2hhcmVkIHJlc291cmNlcy5cbipcbiogVGhpcyBob29rIGlzIHVzZWZ1bCBpZiB5b3UgaGF2ZSBhY2Nlc3MgdG8gYSByZXNvdXJjZSBpbiB0aGUgdGVzdCBpdHNlbGYgYW5kIHlvdSB3YW50IHRvIGNsZWFuIGl0IHVwIGFmdGVyIHRoZSB0ZXN0IGZpbmlzaGVzLiBJdCBpcyBhIG1vcmUgY29tcGFjdCB3YXkgdG8gY2xlYW4gdXAgcmVzb3VyY2VzIHRoYW4gdXNpbmcgdGhlIGNvbWJpbmF0aW9uIG9mIGBiZWZvcmVFYWNoYCBhbmQgYGFmdGVyRWFjaGAuXG4qXG4qICoqTm90ZToqKiBUaGUgYG9uVGVzdEZpbmlzaGVkYCBob29rcyBhcmUgcnVubmluZyBpbiByZXZlcnNlIG9yZGVyIG9mIHRoZWlyIHJlZ2lzdHJhdGlvbi4gWW91IGNhbiBjb25maWd1cmUgdGhpcyBieSBjaGFuZ2luZyB0aGUgYHNlcXVlbmNlLmhvb2tzYCBvcHRpb24gaW4gdGhlIGNvbmZpZyBmaWxlLlxuKlxuKiAqKk5vdGU6KiogVGhlIGBvblRlc3RGaW5pc2hlZGAgaG9vayBpcyBub3QgY2FsbGVkIGlmIHRoZSB0ZXN0IGlzIGNhbmNlbGVkIHdpdGggYSBkeW5hbWljIGBjdHguc2tpcCgpYCBjYWxsLlxuKlxuKiBAcGFyYW0ge0Z1bmN0aW9ufSBmbiAtIFRoZSBjYWxsYmFjayBmdW5jdGlvbiB0byBiZSBleGVjdXRlZCBhZnRlciBhIHRlc3QgZmluaXNoZXMuIFRoZSBmdW5jdGlvbiBjYW4gcmVjZWl2ZSBwYXJhbWV0ZXJzIHByb3ZpZGluZyBkZXRhaWxzIGFib3V0IHRoZSBjb21wbGV0ZWQgdGVzdCwgaW5jbHVkaW5nIGl0cyBzdWNjZXNzIG9yIGZhaWx1cmUgc3RhdHVzLlxuKiBAcGFyYW0ge251bWJlcn0gW3RpbWVvdXRdIC0gT3B0aW9uYWwgdGltZW91dCBpbiBtaWxsaXNlY29uZHMgZm9yIHRoZSBob29rLiBJZiBub3QgcHJvdmlkZWQsIHRoZSBkZWZhdWx0IGhvb2sgdGltZW91dCBmcm9tIHRoZSBydW5uZXIncyBjb25maWd1cmF0aW9uIGlzIHVzZWQuXG4qIEB0aHJvd3Mge0Vycm9yfSBUaHJvd3MgYW4gZXJyb3IgaWYgdGhlIGZ1bmN0aW9uIGlzIG5vdCBjYWxsZWQgd2l0aGluIGEgdGVzdC5cbiogQHJldHVybnMge3ZvaWR9XG4qIEBleGFtcGxlXG4qIGBgYHRzXG4qIC8vIEV4YW1wbGUgb2YgdXNpbmcgb25UZXN0RmluaXNoZWQgZm9yIGNsZWFudXBcbiogY29uc3QgZGIgPSBhd2FpdCBjb25uZWN0VG9EYXRhYmFzZSgpO1xuKiBvblRlc3RGaW5pc2hlZChhc3luYyAoKSA9PiB7XG4qICAgYXdhaXQgZGIuZGlzY29ubmVjdCgpO1xuKiB9KTtcbiogYGBgXG4qL1xuY29uc3Qgb25UZXN0RmluaXNoZWQgPSBjcmVhdGVUZXN0SG9vayhcIm9uVGVzdEZpbmlzaGVkXCIsICh0ZXN0LCBoYW5kbGVyLCB0aW1lb3V0KSA9PiB7XG5cdHRlc3Qub25GaW5pc2hlZCB8fD0gW107XG5cdHRlc3Qub25GaW5pc2hlZC5wdXNoKHdpdGhUaW1lb3V0KGhhbmRsZXIsIHRpbWVvdXQgPz8gZ2V0RGVmYXVsdEhvb2tUaW1lb3V0KCksIHRydWUsIG5ldyBFcnJvcihcIlNUQUNLX1RSQUNFX0VSUk9SXCIpLCBhYm9ydElmVGltZW91dCkpO1xufSk7XG4vKipcbiogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgZnVuY3Rpb24gdGhhdCB3cmFwcyBhcm91bmQgYWxsIHRlc3RzIHdpdGhpbiB0aGUgY3VycmVudCBzdWl0ZS5cbiogVGhlIGNhbGxiYWNrIHJlY2VpdmVzIGEgYHJ1blN1aXRlYCBmdW5jdGlvbiB0aGF0IG11c3QgYmUgY2FsbGVkIHRvIHJ1biB0aGUgc3VpdGUncyB0ZXN0cy5cbiogVGhpcyBob29rIGlzIHVzZWZ1bCBmb3Igc2NlbmFyaW9zIHdoZXJlIHlvdSBuZWVkIHRvIHdyYXAgYW4gZW50aXJlIHN1aXRlIGluIGEgY29udGV4dFxuKiAoZS5nLiwgc3RhcnRpbmcgYSBzZXJ2ZXIsIG9wZW5pbmcgYSBkYXRhYmFzZSBjb25uZWN0aW9uIHRoYXQgYWxsIHRlc3RzIHNoYXJlKS5cbipcbiogKipOb3RlOioqIFdoZW4gbXVsdGlwbGUgYGFyb3VuZEFsbGAgaG9va3MgYXJlIHJlZ2lzdGVyZWQsIHRoZXkgYXJlIG5lc3RlZCBpbnNpZGUgZWFjaCBvdGhlci5cbiogVGhlIGZpcnN0IHJlZ2lzdGVyZWQgaG9vayBpcyB0aGUgb3V0ZXJtb3N0IHdyYXBwZXIuXG4qXG4qIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRoYXQgd3JhcHMgdGhlIHN1aXRlLiBNdXN0IGNhbGwgYHJ1blN1aXRlKClgIHRvIHJ1biB0aGUgdGVzdHMuXG4qIEBwYXJhbSB7bnVtYmVyfSBbdGltZW91dF0gLSBPcHRpb25hbCB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIGhvb2suIElmIG5vdCBwcm92aWRlZCwgdGhlIGRlZmF1bHQgaG9vayB0aW1lb3V0IGZyb20gdGhlIHJ1bm5lcidzIGNvbmZpZ3VyYXRpb24gaXMgdXNlZC5cbiogQHJldHVybnMge3ZvaWR9XG4qIEBleGFtcGxlXG4qIGBgYHRzXG4qIC8vIEV4YW1wbGUgb2YgdXNpbmcgYXJvdW5kQWxsIHRvIHdyYXAgc3VpdGUgaW4gYSB0cmFjaW5nIHNwYW5cbiogYXJvdW5kQWxsKGFzeW5jIChydW5TdWl0ZSkgPT4ge1xuKiAgIGF3YWl0IHRyYWNlci50cmFjZSgndGVzdC1zdWl0ZScsIHJ1blN1aXRlKTtcbiogfSk7XG4qIGBgYFxuKiBAZXhhbXBsZVxuKiBgYGB0c1xuKiAvLyBFeGFtcGxlIG9mIHVzaW5nIGFyb3VuZEFsbCB3aXRoIGZpeHR1cmVzXG4qIGFyb3VuZEFsbChhc3luYyAocnVuU3VpdGUsIHsgZGIgfSkgPT4ge1xuKiAgIGF3YWl0IGRiLnRyYW5zYWN0aW9uKCgpID0+IHJ1blN1aXRlKCkpO1xuKiB9KTtcbiogYGBgXG4qL1xuZnVuY3Rpb24gYXJvdW5kQWxsKGZuLCB0aW1lb3V0KSB7XG5cdGFzc2VydFR5cGVzKGZuLCBcIlxcXCJhcm91bmRBbGxcXFwiIGNhbGxiYWNrXCIsIFtcImZ1bmN0aW9uXCJdKTtcblx0Y29uc3Qgc3RhY2tUcmFjZUVycm9yID0gbmV3IEVycm9yKFwiU1RBQ0tfVFJBQ0VfRVJST1JcIik7XG5cdGNvbnN0IHJlc29sdmVkVGltZW91dCA9IHRpbWVvdXQgPz8gZ2V0RGVmYXVsdEhvb2tUaW1lb3V0KCk7XG5cdGNvbnN0IGNvbnRleHQgPSBnZXRDaGFpbmFibGVDb250ZXh0KHRoaXMpO1xuXHRyZXR1cm4gZ2V0Q3VycmVudFN1aXRlKCkub24oXCJhcm91bmRBbGxcIiwgT2JqZWN0LmFzc2lnbih3aXRoU3VpdGVGaXh0dXJlcyhcImFyb3VuZEFsbFwiLCBmbiwgY29udGV4dCwgc3RhY2tUcmFjZUVycm9yLCAxKSwge1xuXHRcdFtBUk9VTkRfVElNRU9VVF9LRVldOiByZXNvbHZlZFRpbWVvdXQsXG5cdFx0W0FST1VORF9TVEFDS19UUkFDRV9LRVldOiBzdGFja1RyYWNlRXJyb3Jcblx0fSkpO1xufVxuLyoqXG4qIFJlZ2lzdGVycyBhIGNhbGxiYWNrIGZ1bmN0aW9uIHRoYXQgd3JhcHMgYXJvdW5kIGVhY2ggdGVzdCB3aXRoaW4gdGhlIGN1cnJlbnQgc3VpdGUuXG4qIFRoZSBjYWxsYmFjayByZWNlaXZlcyBhIGBydW5UZXN0YCBmdW5jdGlvbiB0aGF0IG11c3QgYmUgY2FsbGVkIHRvIHJ1biB0aGUgdGVzdC5cbiogVGhpcyBob29rIGlzIHVzZWZ1bCBmb3Igc2NlbmFyaW9zIHdoZXJlIHlvdSBuZWVkIHRvIHdyYXAgdGVzdHMgaW4gYSBjb250ZXh0IChlLmcuLCBkYXRhYmFzZSB0cmFuc2FjdGlvbnMpLlxuKlxuKiAqKk5vdGU6KiogV2hlbiBtdWx0aXBsZSBgYXJvdW5kRWFjaGAgaG9va3MgYXJlIHJlZ2lzdGVyZWQsIHRoZXkgYXJlIG5lc3RlZCBpbnNpZGUgZWFjaCBvdGhlci5cbiogVGhlIGZpcnN0IHJlZ2lzdGVyZWQgaG9vayBpcyB0aGUgb3V0ZXJtb3N0IHdyYXBwZXIuXG4qXG4qIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gVGhlIGNhbGxiYWNrIGZ1bmN0aW9uIHRoYXQgd3JhcHMgdGhlIHRlc3QuIE11c3QgY2FsbCBgcnVuVGVzdCgpYCB0byBydW4gdGhlIHRlc3QuXG4qIEBwYXJhbSB7bnVtYmVyfSBbdGltZW91dF0gLSBPcHRpb25hbCB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcyBmb3IgdGhlIGhvb2suIElmIG5vdCBwcm92aWRlZCwgdGhlIGRlZmF1bHQgaG9vayB0aW1lb3V0IGZyb20gdGhlIHJ1bm5lcidzIGNvbmZpZ3VyYXRpb24gaXMgdXNlZC5cbiogQHJldHVybnMge3ZvaWR9XG4qIEBleGFtcGxlXG4qIGBgYHRzXG4qIC8vIEV4YW1wbGUgb2YgdXNpbmcgYXJvdW5kRWFjaCB0byB3cmFwIHRlc3RzIGluIGEgZGF0YWJhc2UgdHJhbnNhY3Rpb25cbiogYXJvdW5kRWFjaChhc3luYyAocnVuVGVzdCkgPT4ge1xuKiAgIGF3YWl0IGRhdGFiYXNlLnRyYW5zYWN0aW9uKCgpID0+IHJ1blRlc3QoKSk7XG4qIH0pO1xuKiBgYGBcbiogQGV4YW1wbGVcbiogYGBgdHNcbiogLy8gRXhhbXBsZSBvZiB1c2luZyBhcm91bmRFYWNoIHdpdGggZml4dHVyZXNcbiogYXJvdW5kRWFjaChhc3luYyAocnVuVGVzdCwgeyBkYiB9KSA9PiB7XG4qICAgYXdhaXQgZGIudHJhbnNhY3Rpb24oKCkgPT4gcnVuVGVzdCgpKTtcbiogfSk7XG4qIGBgYFxuKi9cbmZ1bmN0aW9uIGFyb3VuZEVhY2goZm4sIHRpbWVvdXQpIHtcblx0YXNzZXJ0VHlwZXMoZm4sIFwiXFxcImFyb3VuZEVhY2hcXFwiIGNhbGxiYWNrXCIsIFtcImZ1bmN0aW9uXCJdKTtcblx0Y29uc3Qgc3RhY2tUcmFjZUVycm9yID0gbmV3IEVycm9yKFwiU1RBQ0tfVFJBQ0VfRVJST1JcIik7XG5cdGNvbnN0IHJlc29sdmVkVGltZW91dCA9IHRpbWVvdXQgPz8gZ2V0RGVmYXVsdEhvb2tUaW1lb3V0KCk7XG5cdGNvbnN0IHdyYXBwZXIgPSAocnVuVGVzdCwgY29udGV4dCwgc3VpdGUpID0+IHtcblx0XHRjb25zdCBpbm5lckZuID0gKGN0eCkgPT4gZm4ocnVuVGVzdCwgY3R4LCBzdWl0ZSk7XG5cdFx0Y29uZmlndXJlUHJvcHMoaW5uZXJGbiwge1xuXHRcdFx0aW5kZXg6IDEsXG5cdFx0XHRvcmlnaW5hbDogZm5cblx0XHR9KTtcblx0XHRjb25zdCBmaXh0dXJlUmVzb2x2ZXIgPSB3aXRoRml4dHVyZXMoaW5uZXJGbiwgeyBzdWl0ZSB9KTtcblx0XHRyZXR1cm4gZml4dHVyZVJlc29sdmVyKGNvbnRleHQpO1xuXHR9O1xuXHRyZXR1cm4gZ2V0Q3VycmVudFN1aXRlKCkub24oXCJhcm91bmRFYWNoXCIsIE9iamVjdC5hc3NpZ24od3JhcHBlciwge1xuXHRcdFtBUk9VTkRfVElNRU9VVF9LRVldOiByZXNvbHZlZFRpbWVvdXQsXG5cdFx0W0FST1VORF9TVEFDS19UUkFDRV9LRVldOiBzdGFja1RyYWNlRXJyb3Jcblx0fSkpO1xufVxuZnVuY3Rpb24gd2l0aFN1aXRlRml4dHVyZXMoc3VpdGVIb29rLCBmbiwgY29udGV4dCwgc3RhY2tUcmFjZUVycm9yLCBjb250ZXh0SW5kZXggPSAwKSB7XG5cdHJldHVybiAoLi4uYXJncykgPT4ge1xuXHRcdGNvbnN0IHN1aXRlID0gYXJncy5hdCgtMSk7XG5cdFx0Y29uc3QgcHJlZml4ID0gYXJncy5zbGljZSgwLCAtMSk7XG5cdFx0Y29uc3Qgd3JhcHBlciA9IChjdHgpID0+IGZuKC4uLnByZWZpeCwgY3R4LCBzdWl0ZSk7XG5cdFx0Y29uZmlndXJlUHJvcHMod3JhcHBlciwge1xuXHRcdFx0aW5kZXg6IGNvbnRleHRJbmRleCxcblx0XHRcdG9yaWdpbmFsOiBmblxuXHRcdH0pO1xuXHRcdGNvbnN0IGZpeHR1cmVzID0gY29udGV4dD8uZ2V0Rml4dHVyZXMoKTtcblx0XHRjb25zdCBmaWxlQ29udGV4dCA9IGZpeHR1cmVzPy5nZXRGaWxlQ29udGV4dChzdWl0ZS5maWxlKTtcblx0XHRjb25zdCBmaXh0dXJlZCA9IHdpdGhGaXh0dXJlcyh3cmFwcGVyLCB7XG5cdFx0XHRzdWl0ZUhvb2ssXG5cdFx0XHRmaXh0dXJlcyxcblx0XHRcdGNvbnRleHQ6IGZpbGVDb250ZXh0LFxuXHRcdFx0c3RhY2tUcmFjZUVycm9yXG5cdFx0fSk7XG5cdFx0cmV0dXJuIGZpeHR1cmVkKCk7XG5cdH07XG59XG5mdW5jdGlvbiBnZXRBcm91bmRIb29rVGltZW91dChob29rKSB7XG5cdHJldHVybiBBUk9VTkRfVElNRU9VVF9LRVkgaW4gaG9vayAmJiB0eXBlb2YgaG9va1tBUk9VTkRfVElNRU9VVF9LRVldID09PSBcIm51bWJlclwiID8gaG9va1tBUk9VTkRfVElNRU9VVF9LRVldIDogZ2V0RGVmYXVsdEhvb2tUaW1lb3V0KCk7XG59XG5mdW5jdGlvbiBnZXRBcm91bmRIb29rU3RhY2tUcmFjZShob29rKSB7XG5cdHJldHVybiBBUk9VTkRfU1RBQ0tfVFJBQ0VfS0VZIGluIGhvb2sgJiYgaG9va1tBUk9VTkRfU1RBQ0tfVFJBQ0VfS0VZXSBpbnN0YW5jZW9mIEVycm9yID8gaG9va1tBUk9VTkRfU1RBQ0tfVFJBQ0VfS0VZXSA6IHVuZGVmaW5lZDtcbn1cbmZ1bmN0aW9uIGNyZWF0ZVRlc3RIb29rKG5hbWUsIGhhbmRsZXIpIHtcblx0cmV0dXJuIChmbiwgdGltZW91dCkgPT4ge1xuXHRcdGFzc2VydFR5cGVzKGZuLCBgXCIke25hbWV9XCIgY2FsbGJhY2tgLCBbXCJmdW5jdGlvblwiXSk7XG5cdFx0Y29uc3QgY3VycmVudCA9IGdldEN1cnJlbnRUZXN0KCk7XG5cdFx0aWYgKCFjdXJyZW50KSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoYEhvb2sgJHtuYW1lfSgpIGNhbiBvbmx5IGJlIGNhbGxlZCBpbnNpZGUgYSB0ZXN0YCk7XG5cdFx0fVxuXHRcdHJldHVybiBoYW5kbGVyKGN1cnJlbnQsIGZuLCB0aW1lb3V0KTtcblx0fTtcbn1cblxuLyoqXG4qIElmIGFueSB0YXNrcyBiZWVuIG1hcmtlZCBhcyBgb25seWAsIG1hcmsgYWxsIG90aGVyIHRhc2tzIGFzIGBza2lwYC5cbiovXG5mdW5jdGlvbiBpbnRlcnByZXRUYXNrTW9kZXMoZmlsZSwgbmFtZVBhdHRlcm4sIHRlc3RMb2NhdGlvbnMsIHRlc3RJZHMsIHRlc3RUYWdzRmlsdGVyLCBvbmx5TW9kZSwgcGFyZW50SXNPbmx5LCBhbGxvd09ubHkpIHtcblx0Y29uc3QgbWF0Y2hlZExvY2F0aW9ucyA9IFtdO1xuXHRjb25zdCB0cmF2ZXJzZVN1aXRlID0gKHN1aXRlLCBwYXJlbnRJc09ubHksIHBhcmVudE1hdGNoZWRXaXRoTG9jYXRpb24pID0+IHtcblx0XHRjb25zdCBzdWl0ZUlzT25seSA9IHBhcmVudElzT25seSB8fCBzdWl0ZS5tb2RlID09PSBcIm9ubHlcIjtcblx0XHQvLyBDaGVjayBpZiBhbnkgdGFza3MgaW4gdGhpcyBzdWl0ZSBoYXZlIGAub25seWAgLSBpZiBzbywgb25seSB0aG9zZSBzaG91bGQgcnVuXG5cdFx0Y29uc3QgaGFzU29tZVRhc2tzT25seSA9IG9ubHlNb2RlICYmIHN1aXRlLnRhc2tzLnNvbWUoKHQpID0+IHQubW9kZSA9PT0gXCJvbmx5XCIgfHwgdC50eXBlID09PSBcInN1aXRlXCIgJiYgc29tZVRhc2tzQXJlT25seSh0KSk7XG5cdFx0c3VpdGUudGFza3MuZm9yRWFjaCgodCkgPT4ge1xuXHRcdFx0Ly8gQ2hlY2sgaWYgZWl0aGVyIHRoZSBwYXJlbnQgc3VpdGUgb3IgdGhlIHRhc2sgaXRzZWxmIGFyZSBtYXJrZWQgYXMgaW5jbHVkZWRcblx0XHRcdC8vIElmIHRoZXJlIGFyZSB0YXNrcyB3aXRoIGAub25seWAgaW4gdGhpcyBzdWl0ZSwgb25seSBpbmNsdWRlIHRob3NlIChub3QgYWxsIHRhc2tzIGZyb20gZGVzY3JpYmUub25seSlcblx0XHRcdGNvbnN0IGluY2x1ZGVUYXNrID0gaGFzU29tZVRhc2tzT25seSA/IHQubW9kZSA9PT0gXCJvbmx5XCIgfHwgdC50eXBlID09PSBcInN1aXRlXCIgJiYgc29tZVRhc2tzQXJlT25seSh0KSA6IHN1aXRlSXNPbmx5IHx8IHQubW9kZSA9PT0gXCJvbmx5XCI7XG5cdFx0XHRpZiAob25seU1vZGUpIHtcblx0XHRcdFx0aWYgKHQudHlwZSA9PT0gXCJzdWl0ZVwiICYmIChpbmNsdWRlVGFzayB8fCBzb21lVGFza3NBcmVPbmx5KHQpKSkge1xuXHRcdFx0XHRcdC8vIERvbid0IHNraXAgdGhpcyBzdWl0ZVxuXHRcdFx0XHRcdGlmICh0Lm1vZGUgPT09IFwib25seVwiKSB7XG5cdFx0XHRcdFx0XHRjaGVja0FsbG93T25seSh0LCBhbGxvd09ubHkpO1xuXHRcdFx0XHRcdFx0dC5tb2RlID0gXCJydW5cIjtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gZWxzZSBpZiAodC5tb2RlID09PSBcInJ1blwiICYmICFpbmNsdWRlVGFzaykge1xuXHRcdFx0XHRcdHQubW9kZSA9IFwic2tpcFwiO1xuXHRcdFx0XHR9IGVsc2UgaWYgKHQubW9kZSA9PT0gXCJvbmx5XCIpIHtcblx0XHRcdFx0XHRjaGVja0FsbG93T25seSh0LCBhbGxvd09ubHkpO1xuXHRcdFx0XHRcdHQubW9kZSA9IFwicnVuXCI7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGxldCBoYXNMb2NhdGlvbk1hdGNoID0gcGFyZW50TWF0Y2hlZFdpdGhMb2NhdGlvbjtcblx0XHRcdC8vIE1hdGNoIHRlc3QgbG9jYXRpb24gYWdhaW5zdCBwcm92aWRlZCBsb2NhdGlvbnMsIG9ubHkgcnVuIGlmIHByZXNlbnRcblx0XHRcdC8vIGluIGB0ZXN0TG9jYXRpb25zYC4gTm90ZTogaWYgYGluY2x1ZGVUYXNrTG9jYXRpb25gIGlzIG5vdCBlbmFibGVkLFxuXHRcdFx0Ly8gYWxsIHRlc3Qgd2lsbCBiZSBza2lwcGVkLlxuXHRcdFx0aWYgKHRlc3RMb2NhdGlvbnMgIT09IHVuZGVmaW5lZCAmJiB0ZXN0TG9jYXRpb25zLmxlbmd0aCAhPT0gMCkge1xuXHRcdFx0XHRpZiAodC5sb2NhdGlvbiAmJiB0ZXN0TG9jYXRpb25zPy5pbmNsdWRlcyh0LmxvY2F0aW9uLmxpbmUpKSB7XG5cdFx0XHRcdFx0dC5tb2RlID0gXCJydW5cIjtcblx0XHRcdFx0XHRtYXRjaGVkTG9jYXRpb25zLnB1c2godC5sb2NhdGlvbi5saW5lKTtcblx0XHRcdFx0XHRoYXNMb2NhdGlvbk1hdGNoID0gdHJ1ZTtcblx0XHRcdFx0fSBlbHNlIGlmIChwYXJlbnRNYXRjaGVkV2l0aExvY2F0aW9uKSB7XG5cdFx0XHRcdFx0dC5tb2RlID0gXCJydW5cIjtcblx0XHRcdFx0fSBlbHNlIGlmICh0LnR5cGUgPT09IFwidGVzdFwiKSB7XG5cdFx0XHRcdFx0dC5tb2RlID0gXCJza2lwXCI7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGlmICh0LnR5cGUgPT09IFwidGVzdFwiKSB7XG5cdFx0XHRcdGlmIChuYW1lUGF0dGVybiAmJiAhZ2V0VGFza0Z1bGxOYW1lKHQpLm1hdGNoKG5hbWVQYXR0ZXJuKSkge1xuXHRcdFx0XHRcdHQubW9kZSA9IFwic2tpcFwiO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmICh0ZXN0SWRzICYmICF0ZXN0SWRzLmluY2x1ZGVzKHQuaWQpKSB7XG5cdFx0XHRcdFx0dC5tb2RlID0gXCJza2lwXCI7XG5cdFx0XHRcdH1cblx0XHRcdFx0aWYgKHRlc3RUYWdzRmlsdGVyICYmICF0ZXN0VGFnc0ZpbHRlcih0LnRhZ3MgfHwgW10pKSB7XG5cdFx0XHRcdFx0dC5tb2RlID0gXCJza2lwXCI7XG5cdFx0XHRcdH1cblx0XHRcdH0gZWxzZSBpZiAodC50eXBlID09PSBcInN1aXRlXCIpIHtcblx0XHRcdFx0aWYgKHQubW9kZSA9PT0gXCJza2lwXCIpIHtcblx0XHRcdFx0XHRza2lwQWxsVGFza3ModCk7XG5cdFx0XHRcdH0gZWxzZSBpZiAodC5tb2RlID09PSBcInRvZG9cIikge1xuXHRcdFx0XHRcdHRvZG9BbGxUYXNrcyh0KTtcblx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHR0cmF2ZXJzZVN1aXRlKHQsIGluY2x1ZGVUYXNrLCBoYXNMb2NhdGlvbk1hdGNoKTtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0pO1xuXHRcdC8vIGlmIGFsbCBzdWJ0YXNrcyBhcmUgc2tpcHBlZCwgbWFyayBhcyBza2lwXG5cdFx0aWYgKHN1aXRlLm1vZGUgPT09IFwicnVuXCIgfHwgc3VpdGUubW9kZSA9PT0gXCJxdWV1ZWRcIikge1xuXHRcdFx0aWYgKHN1aXRlLnRhc2tzLmxlbmd0aCAmJiBzdWl0ZS50YXNrcy5ldmVyeSgoaSkgPT4gaS5tb2RlICE9PSBcInJ1blwiICYmIGkubW9kZSAhPT0gXCJxdWV1ZWRcIikpIHtcblx0XHRcdFx0c3VpdGUubW9kZSA9IFwic2tpcFwiO1xuXHRcdFx0fVxuXHRcdH1cblx0fTtcblx0dHJhdmVyc2VTdWl0ZShmaWxlLCBwYXJlbnRJc09ubHksIGZhbHNlKTtcblx0Y29uc3Qgbm9uTWF0Y2hpbmcgPSB0ZXN0TG9jYXRpb25zPy5maWx0ZXIoKGxvYykgPT4gIW1hdGNoZWRMb2NhdGlvbnMuaW5jbHVkZXMobG9jKSk7XG5cdGlmIChub25NYXRjaGluZyAmJiBub25NYXRjaGluZy5sZW5ndGggIT09IDApIHtcblx0XHRjb25zdCBtZXNzYWdlID0gbm9uTWF0Y2hpbmcubGVuZ3RoID09PSAxID8gYGxpbmUgJHtub25NYXRjaGluZ1swXX1gIDogYGxpbmVzICR7bm9uTWF0Y2hpbmcuam9pbihcIiwgXCIpfWA7XG5cdFx0aWYgKGZpbGUucmVzdWx0ID09PSB1bmRlZmluZWQpIHtcblx0XHRcdGZpbGUucmVzdWx0ID0ge1xuXHRcdFx0XHRzdGF0ZTogXCJmYWlsXCIsXG5cdFx0XHRcdGVycm9yczogW11cblx0XHRcdH07XG5cdFx0fVxuXHRcdGlmIChmaWxlLnJlc3VsdC5lcnJvcnMgPT09IHVuZGVmaW5lZCkge1xuXHRcdFx0ZmlsZS5yZXN1bHQuZXJyb3JzID0gW107XG5cdFx0fVxuXHRcdGZpbGUucmVzdWx0LmVycm9ycy5wdXNoKHByb2Nlc3NFcnJvcihuZXcgRXJyb3IoYE5vIHRlc3QgZm91bmQgaW4gJHtmaWxlLm5hbWV9IGluICR7bWVzc2FnZX1gKSkpO1xuXHR9XG59XG5mdW5jdGlvbiBnZXRUYXNrRnVsbE5hbWUodGFzaykge1xuXHRyZXR1cm4gYCR7dGFzay5zdWl0ZSA/IGAke2dldFRhc2tGdWxsTmFtZSh0YXNrLnN1aXRlKX0gYCA6IFwiXCJ9JHt0YXNrLm5hbWV9YDtcbn1cbmZ1bmN0aW9uIHNvbWVUYXNrc0FyZU9ubHkoc3VpdGUpIHtcblx0cmV0dXJuIHN1aXRlLnRhc2tzLnNvbWUoKHQpID0+IHQubW9kZSA9PT0gXCJvbmx5XCIgfHwgdC50eXBlID09PSBcInN1aXRlXCIgJiYgc29tZVRhc2tzQXJlT25seSh0KSk7XG59XG5mdW5jdGlvbiBza2lwQWxsVGFza3Moc3VpdGUpIHtcblx0c3VpdGUudGFza3MuZm9yRWFjaCgodCkgPT4ge1xuXHRcdGlmICh0Lm1vZGUgPT09IFwicnVuXCIgfHwgdC5tb2RlID09PSBcInF1ZXVlZFwiKSB7XG5cdFx0XHR0Lm1vZGUgPSBcInNraXBcIjtcblx0XHRcdGlmICh0LnR5cGUgPT09IFwic3VpdGVcIikge1xuXHRcdFx0XHRza2lwQWxsVGFza3ModCk7XG5cdFx0XHR9XG5cdFx0fVxuXHR9KTtcbn1cbmZ1bmN0aW9uIHRvZG9BbGxUYXNrcyhzdWl0ZSkge1xuXHRzdWl0ZS50YXNrcy5mb3JFYWNoKCh0KSA9PiB7XG5cdFx0aWYgKHQubW9kZSA9PT0gXCJydW5cIiB8fCB0Lm1vZGUgPT09IFwicXVldWVkXCIpIHtcblx0XHRcdHQubW9kZSA9IFwidG9kb1wiO1xuXHRcdFx0aWYgKHQudHlwZSA9PT0gXCJzdWl0ZVwiKSB7XG5cdFx0XHRcdHRvZG9BbGxUYXNrcyh0KTtcblx0XHRcdH1cblx0XHR9XG5cdH0pO1xufVxuZnVuY3Rpb24gY2hlY2tBbGxvd09ubHkodGFzaywgYWxsb3dPbmx5KSB7XG5cdGlmIChhbGxvd09ubHkpIHtcblx0XHRyZXR1cm47XG5cdH1cblx0Y29uc3QgZXJyb3IgPSBwcm9jZXNzRXJyb3IobmV3IEVycm9yKFwiW1ZpdGVzdF0gVW5leHBlY3RlZCAub25seSBtb2RpZmllci4gUmVtb3ZlIGl0IG9yIHBhc3MgLS1hbGxvd09ubHkgYXJndW1lbnQgdG8gYnlwYXNzIHRoaXMgZXJyb3JcIikpO1xuXHR0YXNrLnJlc3VsdCA9IHtcblx0XHRzdGF0ZTogXCJmYWlsXCIsXG5cdFx0ZXJyb3JzOiBbZXJyb3JdXG5cdH07XG59XG4vKiBAX19OT19TSURFX0VGRkVDVFNfXyAqL1xuZnVuY3Rpb24gZ2VuZXJhdGVIYXNoKHN0cikge1xuXHRsZXQgaGFzaCA9IDA7XG5cdGlmIChzdHIubGVuZ3RoID09PSAwKSB7XG5cdFx0cmV0dXJuIGAke2hhc2h9YDtcblx0fVxuXHRmb3IgKGxldCBpID0gMDsgaSA8IHN0ci5sZW5ndGg7IGkrKykge1xuXHRcdGNvbnN0IGNoYXIgPSBzdHIuY2hhckNvZGVBdChpKTtcblx0XHRoYXNoID0gKGhhc2ggPDwgNSkgLSBoYXNoICsgY2hhcjtcblx0XHRoYXNoID0gaGFzaCAmIGhhc2g7XG5cdH1cblx0cmV0dXJuIGAke2hhc2h9YDtcbn1cbmZ1bmN0aW9uIGNhbGN1bGF0ZVN1aXRlSGFzaChwYXJlbnQpIHtcblx0cGFyZW50LnRhc2tzLmZvckVhY2goKHQsIGlkeCkgPT4ge1xuXHRcdHQuaWQgPSBgJHtwYXJlbnQuaWR9XyR7aWR4fWA7XG5cdFx0aWYgKHQudHlwZSA9PT0gXCJzdWl0ZVwiKSB7XG5cdFx0XHRjYWxjdWxhdGVTdWl0ZUhhc2godCk7XG5cdFx0fVxuXHR9KTtcbn1cbmZ1bmN0aW9uIGNyZWF0ZUZpbGVUYXNrKGZpbGVwYXRoLCByb290LCBwcm9qZWN0TmFtZSwgcG9vbCwgdml0ZUVudmlyb25tZW50KSB7XG5cdGNvbnN0IHBhdGggPSByZWxhdGl2ZShyb290LCBmaWxlcGF0aCk7XG5cdGNvbnN0IGZpbGUgPSB7XG5cdFx0aWQ6IGdlbmVyYXRlRmlsZUhhc2gocGF0aCwgcHJvamVjdE5hbWUpLFxuXHRcdG5hbWU6IHBhdGgsXG5cdFx0ZnVsbE5hbWU6IHBhdGgsXG5cdFx0dHlwZTogXCJzdWl0ZVwiLFxuXHRcdG1vZGU6IFwicXVldWVkXCIsXG5cdFx0ZmlsZXBhdGgsXG5cdFx0dGFza3M6IFtdLFxuXHRcdG1ldGE6IE9iamVjdC5jcmVhdGUobnVsbCksXG5cdFx0cHJvamVjdE5hbWUsXG5cdFx0ZmlsZTogdW5kZWZpbmVkLFxuXHRcdHBvb2wsXG5cdFx0dml0ZUVudmlyb25tZW50XG5cdH07XG5cdGZpbGUuZmlsZSA9IGZpbGU7XG5cdHJldHVybiBmaWxlO1xufVxuLyoqXG4qIEdlbmVyYXRlIGEgdW5pcXVlIElEIGZvciBhIGZpbGUgYmFzZWQgb24gaXRzIHBhdGggYW5kIHByb2plY3QgbmFtZVxuKiBAcGFyYW0gZmlsZSBGaWxlIHJlbGF0aXZlIHRvIHRoZSByb290IG9mIHRoZSBwcm9qZWN0IHRvIGtlZXAgSUQgdGhlIHNhbWUgYmV0d2VlbiBkaWZmZXJlbnQgbWFjaGluZXNcbiogQHBhcmFtIHByb2plY3ROYW1lIFRoZSBuYW1lIG9mIHRoZSB0ZXN0IHByb2plY3RcbiovXG4vKiBAX19OT19TSURFX0VGRkVDVFNfXyAqL1xuZnVuY3Rpb24gZ2VuZXJhdGVGaWxlSGFzaChmaWxlLCBwcm9qZWN0TmFtZSkge1xuXHRyZXR1cm4gLyogQF9fUFVSRV9fICovIGdlbmVyYXRlSGFzaChgJHtmaWxlfSR7cHJvamVjdE5hbWUgfHwgXCJcIn1gKTtcbn1cbmZ1bmN0aW9uIGZpbmRUZXN0RmlsZVN0YWNrVHJhY2UodGVzdEZpbGVQYXRoLCBlcnJvcikge1xuXHQvLyBmaXJzdCBsaW5lIGlzIHRoZSBlcnJvciBtZXNzYWdlXG5cdGNvbnN0IGxpbmVzID0gZXJyb3Iuc3BsaXQoXCJcXG5cIikuc2xpY2UoMSk7XG5cdGZvciAoY29uc3QgbGluZSBvZiBsaW5lcykge1xuXHRcdGNvbnN0IHN0YWNrID0gcGFyc2VTaW5nbGVTdGFjayhsaW5lKTtcblx0XHRpZiAoc3RhY2sgJiYgc3RhY2suZmlsZSA9PT0gdGVzdEZpbGVQYXRoKSB7XG5cdFx0XHRyZXR1cm4gc3RhY2s7XG5cdFx0fVxuXHR9XG59XG5cbi8qKlxuKiBSZXR1cm4gYSBmdW5jdGlvbiBmb3IgcnVubmluZyBtdWx0aXBsZSBhc3luYyBvcGVyYXRpb25zIHdpdGggbGltaXRlZCBjb25jdXJyZW5jeS5cbiovXG5mdW5jdGlvbiBsaW1pdENvbmN1cnJlbmN5KGNvbmN1cnJlbmN5ID0gSW5maW5pdHkpIHtcblx0Ly8gVGhlIG51bWJlciBvZiBjdXJyZW50bHkgYWN0aXZlICsgcGVuZGluZyB0YXNrcy5cblx0bGV0IGNvdW50ID0gMDtcblx0Ly8gVGhlIGhlYWQgYW5kIHRhaWwgb2YgdGhlIHBlbmRpbmcgdGFzayBxdWV1ZSwgYnVpbHQgdXNpbmcgYSBzaW5nbHkgbGlua2VkIGxpc3QuXG5cdC8vIEJvdGggaGVhZCBhbmQgdGFpbCBhcmUgaW5pdGlhbGx5IHVuZGVmaW5lZCwgc2lnbmlmeWluZyBhbiBlbXB0eSBxdWV1ZS5cblx0Ly8gVGhleSBib3RoIGJlY29tZSB1bmRlZmluZWQgYWdhaW4gd2hlbmV2ZXIgdGhlcmUgYXJlIG5vIHBlbmRpbmcgdGFza3MuXG5cdGxldCBoZWFkO1xuXHRsZXQgdGFpbDtcblx0Ly8gQSBib29ra2VlcGluZyBmdW5jdGlvbiBleGVjdXRlZCB3aGVuZXZlciBhIHRhc2sgaGFzIGJlZW4gcnVuIHRvIGNvbXBsZXRpb24uXG5cdGNvbnN0IGZpbmlzaCA9ICgpID0+IHtcblx0XHRjb3VudC0tO1xuXHRcdC8vIENoZWNrIGlmIHRoZXJlIGFyZSBmdXJ0aGVyIHBlbmRpbmcgdGFza3MgaW4gdGhlIHF1ZXVlLlxuXHRcdGlmIChoZWFkKSB7XG5cdFx0XHQvLyBBbGxvdyB0aGUgbmV4dCBwZW5kaW5nIHRhc2sgdG8gcnVuIGFuZCBwb3AgaXQgZnJvbSB0aGUgcXVldWUuXG5cdFx0XHRoZWFkWzBdKCk7XG5cdFx0XHRoZWFkID0gaGVhZFsxXTtcblx0XHRcdC8vIFRoZSBoZWFkIG1heSBub3cgYmUgdW5kZWZpbmVkIGlmIHRoZXJlIGFyZSBubyBmdXJ0aGVyIHBlbmRpbmcgdGFza3MuXG5cdFx0XHQvLyBJbiB0aGF0IGNhc2UsIHNldCB0YWlsIHRvIHVuZGVmaW5lZCBhcyB3ZWxsLlxuXHRcdFx0dGFpbCA9IGhlYWQgJiYgdGFpbDtcblx0XHR9XG5cdH07XG5cdGNvbnN0IGFjcXVpcmUgPSAoKSA9PiB7XG5cdFx0bGV0IHJlbGVhc2VkID0gZmFsc2U7XG5cdFx0Y29uc3QgcmVsZWFzZSA9ICgpID0+IHtcblx0XHRcdGlmICghcmVsZWFzZWQpIHtcblx0XHRcdFx0cmVsZWFzZWQgPSB0cnVlO1xuXHRcdFx0XHRmaW5pc2goKTtcblx0XHRcdH1cblx0XHR9O1xuXHRcdGlmIChjb3VudCsrIDwgY29uY3VycmVuY3kpIHtcblx0XHRcdHJldHVybiByZWxlYXNlO1xuXHRcdH1cblx0XHRyZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcblx0XHRcdGlmICh0YWlsKSB7XG5cdFx0XHRcdC8vIFRoZXJlIGFyZSBwZW5kaW5nIHRhc2tzLCBzbyBhcHBlbmQgdG8gdGhlIHF1ZXVlLlxuXHRcdFx0XHR0YWlsID0gdGFpbFsxXSA9IFsoKSA9PiByZXNvbHZlKHJlbGVhc2UpXTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdC8vIE5vIG90aGVyIHBlbmRpbmcgdGFza3MsIGluaXRpYWxpemUgdGhlIHF1ZXVlIHdpdGggYSBuZXcgdGFpbCBhbmQgaGVhZC5cblx0XHRcdFx0aGVhZCA9IHRhaWwgPSBbKCkgPT4gcmVzb2x2ZShyZWxlYXNlKV07XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH07XG5cdGNvbnN0IGxpbWl0ZXJGbiA9IChmdW5jLCAuLi5hcmdzKSA9PiB7XG5cdFx0ZnVuY3Rpb24gcnVuKHJlbGVhc2UpIHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGNvbnN0IHJlc3VsdCA9IGZ1bmMoLi4uYXJncyk7XG5cdFx0XHRcdGlmIChyZXN1bHQgaW5zdGFuY2VvZiBQcm9taXNlKSB7XG5cdFx0XHRcdFx0cmV0dXJuIHJlc3VsdC5maW5hbGx5KHJlbGVhc2UpO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHJlbGVhc2UoKTtcblx0XHRcdFx0cmV0dXJuIFByb21pc2UucmVzb2x2ZShyZXN1bHQpO1xuXHRcdFx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHRcdFx0cmVsZWFzZSgpO1xuXHRcdFx0XHRyZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRjb25zdCByZWxlYXNlID0gYWNxdWlyZSgpO1xuXHRcdHJldHVybiByZWxlYXNlIGluc3RhbmNlb2YgUHJvbWlzZSA/IHJlbGVhc2UudGhlbihydW4pIDogcnVuKHJlbGVhc2UpO1xuXHR9O1xuXHRyZXR1cm4gT2JqZWN0LmFzc2lnbihsaW1pdGVyRm4sIHsgYWNxdWlyZSB9KTtcbn1cblxuLyoqXG4qIFBhcnRpdGlvbiBpbiB0YXNrcyBncm91cHMgYnkgY29uc2VjdXRpdmUgY29uY3VycmVudFxuKi9cbmZ1bmN0aW9uIHBhcnRpdGlvblN1aXRlQ2hpbGRyZW4oc3VpdGUpIHtcblx0bGV0IHRhc2tzR3JvdXAgPSBbXTtcblx0Y29uc3QgdGFza3NHcm91cHMgPSBbXTtcblx0Zm9yIChjb25zdCBjIG9mIHN1aXRlLnRhc2tzKSB7XG5cdFx0aWYgKHRhc2tzR3JvdXAubGVuZ3RoID09PSAwIHx8IGMuY29uY3VycmVudCA9PT0gdGFza3NHcm91cFswXS5jb25jdXJyZW50KSB7XG5cdFx0XHR0YXNrc0dyb3VwLnB1c2goYyk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRhc2tzR3JvdXBzLnB1c2godGFza3NHcm91cCk7XG5cdFx0XHR0YXNrc0dyb3VwID0gW2NdO1xuXHRcdH1cblx0fVxuXHRpZiAodGFza3NHcm91cC5sZW5ndGggPiAwKSB7XG5cdFx0dGFza3NHcm91cHMucHVzaCh0YXNrc0dyb3VwKTtcblx0fVxuXHRyZXR1cm4gdGFza3NHcm91cHM7XG59XG5cbmNvbnN0IGZpbHRlck1hcCA9IG5ldyBXZWFrTWFwKCk7XG4vKipcbiogQGV4cGVyaW1lbnRhbFxuKi9cbmZ1bmN0aW9uIG1hdGNoZXNUYWdzKHRlc3RUYWdzKSB7XG5cdGNvbnN0IHJ1bm5lciA9IGdldFJ1bm5lcigpO1xuXHRjb25zdCB0YWdzRmlsdGVyID0gcnVubmVyLl9jdXJyZW50U3BlY2lmaWNhdGlvbj8udGVzdFRhZ3NGaWx0ZXIgPz8gcnVubmVyLmNvbmZpZy50YWdzRmlsdGVyO1xuXHRpZiAoIXRhZ3NGaWx0ZXIpIHtcblx0XHRyZXR1cm4gdHJ1ZTtcblx0fVxuXHRsZXQgdGFnc0ZpbHRlclByZWRpY2F0ZSA9IGZpbHRlck1hcC5nZXQodGFnc0ZpbHRlcik7XG5cdGlmICghdGFnc0ZpbHRlclByZWRpY2F0ZSkge1xuXHRcdHRhZ3NGaWx0ZXJQcmVkaWNhdGUgPSBjcmVhdGVUYWdzRmlsdGVyKHRhZ3NGaWx0ZXIsIHJ1bm5lci5jb25maWcudGFncyk7XG5cdFx0ZmlsdGVyTWFwLnNldCh0YWdzRmlsdGVyLCB0YWdzRmlsdGVyUHJlZGljYXRlKTtcblx0fVxuXHRyZXR1cm4gdGFnc0ZpbHRlclByZWRpY2F0ZSh0ZXN0VGFncyk7XG59XG5mdW5jdGlvbiB2YWxpZGF0ZVRhZ3MoY29uZmlnLCB0YWdzKSB7XG5cdGlmICghY29uZmlnLnN0cmljdFRhZ3MpIHtcblx0XHRyZXR1cm47XG5cdH1cblx0Y29uc3QgYXZhaWxhYmxlVGFncyA9IG5ldyBTZXQoY29uZmlnLnRhZ3MubWFwKCh0YWcpID0+IHRhZy5uYW1lKSk7XG5cdGZvciAoY29uc3QgdGFnIG9mIHRhZ3MpIHtcblx0XHRpZiAoIWF2YWlsYWJsZVRhZ3MuaGFzKHRhZykpIHtcblx0XHRcdHRocm93IGNyZWF0ZU5vVGFnc0Vycm9yKGNvbmZpZy50YWdzLCB0YWcpO1xuXHRcdH1cblx0fVxufVxuZnVuY3Rpb24gY3JlYXRlTm9UYWdzRXJyb3IoYXZhaWxhYmxlVGFncywgdGFnLCBwcmVmaXggPSBcInRhZ1wiKSB7XG5cdGlmICghYXZhaWxhYmxlVGFncy5sZW5ndGgpIHtcblx0XHR0aHJvdyBuZXcgRXJyb3IoYFRoZSBWaXRlc3QgY29uZmlnIGRvZXMndCBkZWZpbmUgYW55IFwidGFnc1wiLCBjYW5ub3QgYXBwbHkgXCIke3RhZ31cIiAke3ByZWZpeH0gZm9yIHRoaXMgdGVzdC4gU2VlOiBodHRwczovL3ZpdGVzdC5kZXYvZ3VpZGUvdGVzdC10YWdzYCk7XG5cdH1cblx0dGhyb3cgbmV3IEVycm9yKGBUaGUgJHtwcmVmaXh9IFwiJHt0YWd9XCIgaXMgbm90IGRlZmluZWQgaW4gdGhlIGNvbmZpZ3VyYXRpb24uIEF2YWlsYWJsZSB0YWdzIGFyZTpcXG4ke2F2YWlsYWJsZVRhZ3MubWFwKCh0KSA9PiBgLSAke3QubmFtZX0ke3QuZGVzY3JpcHRpb24gPyBgOiAke3QuZGVzY3JpcHRpb259YCA6IFwiXCJ9YCkuam9pbihcIlxcblwiKX1gKTtcbn1cbmZ1bmN0aW9uIGNyZWF0ZVRhZ3NGaWx0ZXIodGFnc0V4cHIsIGF2YWlsYWJsZVRhZ3MpIHtcblx0Y29uc3QgbWF0Y2hlcnMgPSB0YWdzRXhwci5tYXAoKGV4cHIpID0+IHBhcnNlVGFnc0V4cHJlc3Npb24oZXhwciwgYXZhaWxhYmxlVGFncykpO1xuXHRyZXR1cm4gKHRlc3RUYWdzKSA9PiB7XG5cdFx0cmV0dXJuIG1hdGNoZXJzLmV2ZXJ5KChtYXRjaGVyKSA9PiBtYXRjaGVyKHRlc3RUYWdzKSk7XG5cdH07XG59XG5mdW5jdGlvbiBwYXJzZVRhZ3NFeHByZXNzaW9uKGV4cHIsIGF2YWlsYWJsZVRhZ3MpIHtcblx0Y29uc3QgdG9rZW5zID0gdG9rZW5pemUoZXhwcik7XG5cdGNvbnN0IHN0cmVhbSA9IG5ldyBUb2tlblN0cmVhbSh0b2tlbnMsIGV4cHIpO1xuXHRjb25zdCBhc3QgPSBwYXJzZU9yRXhwcmVzc2lvbihzdHJlYW0sIGF2YWlsYWJsZVRhZ3MpO1xuXHRpZiAoc3RyZWFtLnBlZWsoKS50eXBlICE9PSBcIkVPRlwiKSB7XG5cdFx0dGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHRhZ3MgZXhwcmVzc2lvbjogdW5leHBlY3RlZCBcIiR7Zm9ybWF0VG9rZW4oc3RyZWFtLnBlZWsoKSl9XCIgaW4gXCIke2V4cHJ9XCJgKTtcblx0fVxuXHRyZXR1cm4gKHRhZ3MpID0+IGV2YWx1YXRlTm9kZShhc3QsIHRhZ3MpO1xufVxuZnVuY3Rpb24gZm9ybWF0VG9rZW4odG9rZW4pIHtcblx0c3dpdGNoICh0b2tlbi50eXBlKSB7XG5cdFx0Y2FzZSBcIlRBR1wiOiByZXR1cm4gdG9rZW4udmFsdWU7XG5cdFx0ZGVmYXVsdDogcmV0dXJuIGZvcm1hdFRva2VuVHlwZSh0b2tlbi50eXBlKTtcblx0fVxufVxuZnVuY3Rpb24gdG9rZW5pemUoZXhwcikge1xuXHRjb25zdCB0b2tlbnMgPSBbXTtcblx0bGV0IGkgPSAwO1xuXHR3aGlsZSAoaSA8IGV4cHIubGVuZ3RoKSB7XG5cdFx0aWYgKGV4cHJbaV0gPT09IFwiIFwiIHx8IGV4cHJbaV0gPT09IFwiXHRcIikge1xuXHRcdFx0aSsrO1xuXHRcdFx0Y29udGludWU7XG5cdFx0fVxuXHRcdGlmIChleHByW2ldID09PSBcIihcIikge1xuXHRcdFx0dG9rZW5zLnB1c2goeyB0eXBlOiBcIkxQQVJFTlwiIH0pO1xuXHRcdFx0aSsrO1xuXHRcdFx0Y29udGludWU7XG5cdFx0fVxuXHRcdGlmIChleHByW2ldID09PSBcIilcIikge1xuXHRcdFx0dG9rZW5zLnB1c2goeyB0eXBlOiBcIlJQQVJFTlwiIH0pO1xuXHRcdFx0aSsrO1xuXHRcdFx0Y29udGludWU7XG5cdFx0fVxuXHRcdGlmIChleHByW2ldID09PSBcIiFcIikge1xuXHRcdFx0dG9rZW5zLnB1c2goeyB0eXBlOiBcIk5PVFwiIH0pO1xuXHRcdFx0aSsrO1xuXHRcdFx0Y29udGludWU7XG5cdFx0fVxuXHRcdGlmIChleHByLnNsaWNlKGksIGkgKyAyKSA9PT0gXCImJlwiKSB7XG5cdFx0XHR0b2tlbnMucHVzaCh7IHR5cGU6IFwiQU5EXCIgfSk7XG5cdFx0XHRpICs9IDI7XG5cdFx0XHRjb250aW51ZTtcblx0XHR9XG5cdFx0aWYgKGV4cHIuc2xpY2UoaSwgaSArIDIpID09PSBcInx8XCIpIHtcblx0XHRcdHRva2Vucy5wdXNoKHsgdHlwZTogXCJPUlwiIH0pO1xuXHRcdFx0aSArPSAyO1xuXHRcdFx0Y29udGludWU7XG5cdFx0fVxuXHRcdGlmICgvXmFuZCg/Olxcc3xcXCl8JCkvaS50ZXN0KGV4cHIuc2xpY2UoaSkpKSB7XG5cdFx0XHR0b2tlbnMucHVzaCh7IHR5cGU6IFwiQU5EXCIgfSk7XG5cdFx0XHRpICs9IDM7XG5cdFx0XHRjb250aW51ZTtcblx0XHR9XG5cdFx0aWYgKC9eb3IoPzpcXHN8XFwpfCQpL2kudGVzdChleHByLnNsaWNlKGkpKSkge1xuXHRcdFx0dG9rZW5zLnB1c2goeyB0eXBlOiBcIk9SXCIgfSk7XG5cdFx0XHRpICs9IDI7XG5cdFx0XHRjb250aW51ZTtcblx0XHR9XG5cdFx0aWYgKC9ebm90XFxzL2kudGVzdChleHByLnNsaWNlKGkpKSkge1xuXHRcdFx0dG9rZW5zLnB1c2goeyB0eXBlOiBcIk5PVFwiIH0pO1xuXHRcdFx0aSArPSAzO1xuXHRcdFx0Y29udGludWU7XG5cdFx0fVxuXHRcdGxldCB0YWcgPSBcIlwiO1xuXHRcdHdoaWxlIChpIDwgZXhwci5sZW5ndGggJiYgZXhwcltpXSAhPT0gXCIgXCIgJiYgZXhwcltpXSAhPT0gXCJcdFwiICYmIGV4cHJbaV0gIT09IFwiKFwiICYmIGV4cHJbaV0gIT09IFwiKVwiICYmIGV4cHJbaV0gIT09IFwiIVwiICYmIGV4cHJbaV0gIT09IFwiJlwiICYmIGV4cHJbaV0gIT09IFwifFwiKSB7XG5cdFx0XHRjb25zdCByZW1haW5pbmcgPSBleHByLnNsaWNlKGkpO1xuXHRcdFx0Ly8gT25seSB0cmVhdCBhbmQvb3Ivbm90IGFzIG9wZXJhdG9ycyBpZiB3ZSdyZSBhdCB0aGUgc3RhcnQgb2YgYSB0YWcgKGFmdGVyIHdoaXRlc3BhY2UpXG5cdFx0XHQvLyBUaGlzIGFsbG93cyB0YWdzIGxpa2UgXCJkZW1hbmRcIiwgXCJlZGl0b3JcIiwgXCJjYW5ub3RcIiB0byB3b3JrIGNvcnJlY3RseVxuXHRcdFx0aWYgKHRhZyA9PT0gXCJcIiAmJiAoL15hbmQoPzpcXHN8XFwpfCQpL2kudGVzdChyZW1haW5pbmcpIHx8IC9eb3IoPzpcXHN8XFwpfCQpL2kudGVzdChyZW1haW5pbmcpIHx8IC9ebm90XFxzL2kudGVzdChyZW1haW5pbmcpKSkge1xuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblx0XHRcdHRhZyArPSBleHByW2ldO1xuXHRcdFx0aSsrO1xuXHRcdH1cblx0XHRpZiAodGFnKSB7XG5cdFx0XHR0b2tlbnMucHVzaCh7XG5cdFx0XHRcdHR5cGU6IFwiVEFHXCIsXG5cdFx0XHRcdHZhbHVlOiB0YWdcblx0XHRcdH0pO1xuXHRcdH1cblx0fVxuXHR0b2tlbnMucHVzaCh7IHR5cGU6IFwiRU9GXCIgfSk7XG5cdHJldHVybiB0b2tlbnM7XG59XG5jbGFzcyBUb2tlblN0cmVhbSB7XG5cdHBvcyA9IDA7XG5cdGNvbnN0cnVjdG9yKHRva2VucywgZXhwcikge1xuXHRcdHRoaXMudG9rZW5zID0gdG9rZW5zO1xuXHRcdHRoaXMuZXhwciA9IGV4cHI7XG5cdH1cblx0cGVlaygpIHtcblx0XHRyZXR1cm4gdGhpcy50b2tlbnNbdGhpcy5wb3NdO1xuXHR9XG5cdG5leHQoKSB7XG5cdFx0cmV0dXJuIHRoaXMudG9rZW5zW3RoaXMucG9zKytdO1xuXHR9XG5cdGV4cGVjdCh0eXBlKSB7XG5cdFx0Y29uc3QgdG9rZW4gPSB0aGlzLm5leHQoKTtcblx0XHRpZiAodG9rZW4udHlwZSAhPT0gdHlwZSkge1xuXHRcdFx0aWYgKHR5cGUgPT09IFwiUlBBUkVOXCIgJiYgdG9rZW4udHlwZSA9PT0gXCJFT0ZcIikge1xuXHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgdGFncyBleHByZXNzaW9uOiBtaXNzaW5nIGNsb3NpbmcgXCIpXCIgaW4gXCIke3RoaXMuZXhwcn1cImApO1xuXHRcdFx0fVxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHRhZ3MgZXhwcmVzc2lvbjogZXhwZWN0ZWQgXCIke2Zvcm1hdFRva2VuVHlwZSh0eXBlKX1cIiBidXQgZ290IFwiJHtmb3JtYXRUb2tlbih0b2tlbil9XCIgaW4gXCIke3RoaXMuZXhwcn1cImApO1xuXHRcdH1cblx0XHRyZXR1cm4gdG9rZW47XG5cdH1cblx0dW5leHBlY3RlZFRva2VuKCkge1xuXHRcdGNvbnN0IHRva2VuID0gdGhpcy5wZWVrKCk7XG5cdFx0aWYgKHRva2VuLnR5cGUgPT09IFwiRU9GXCIpIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcihgSW52YWxpZCB0YWdzIGV4cHJlc3Npb246IHVuZXhwZWN0ZWQgZW5kIG9mIGV4cHJlc3Npb24gaW4gXCIke3RoaXMuZXhwcn1cImApO1xuXHRcdH1cblx0XHR0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgdGFncyBleHByZXNzaW9uOiB1bmV4cGVjdGVkIFwiJHtmb3JtYXRUb2tlbih0b2tlbil9XCIgaW4gXCIke3RoaXMuZXhwcn1cImApO1xuXHR9XG59XG5mdW5jdGlvbiBmb3JtYXRUb2tlblR5cGUodHlwZSkge1xuXHRzd2l0Y2ggKHR5cGUpIHtcblx0XHRjYXNlIFwiVEFHXCI6IHJldHVybiBcInRhZ1wiO1xuXHRcdGNhc2UgXCJBTkRcIjogcmV0dXJuIFwiYW5kXCI7XG5cdFx0Y2FzZSBcIk9SXCI6IHJldHVybiBcIm9yXCI7XG5cdFx0Y2FzZSBcIk5PVFwiOiByZXR1cm4gXCJub3RcIjtcblx0XHRjYXNlIFwiTFBBUkVOXCI6IHJldHVybiBcIihcIjtcblx0XHRjYXNlIFwiUlBBUkVOXCI6IHJldHVybiBcIilcIjtcblx0XHRjYXNlIFwiRU9GXCI6IHJldHVybiBcImVuZCBvZiBleHByZXNzaW9uXCI7XG5cdH1cbn1cbmZ1bmN0aW9uIHBhcnNlT3JFeHByZXNzaW9uKHN0cmVhbSwgYXZhaWxhYmxlVGFncykge1xuXHRsZXQgbGVmdCA9IHBhcnNlQW5kRXhwcmVzc2lvbihzdHJlYW0sIGF2YWlsYWJsZVRhZ3MpO1xuXHR3aGlsZSAoc3RyZWFtLnBlZWsoKS50eXBlID09PSBcIk9SXCIpIHtcblx0XHRzdHJlYW0ubmV4dCgpO1xuXHRcdGNvbnN0IHJpZ2h0ID0gcGFyc2VBbmRFeHByZXNzaW9uKHN0cmVhbSwgYXZhaWxhYmxlVGFncyk7XG5cdFx0bGVmdCA9IHtcblx0XHRcdHR5cGU6IFwib3JcIixcblx0XHRcdGxlZnQsXG5cdFx0XHRyaWdodFxuXHRcdH07XG5cdH1cblx0cmV0dXJuIGxlZnQ7XG59XG5mdW5jdGlvbiBwYXJzZUFuZEV4cHJlc3Npb24oc3RyZWFtLCBhdmFpbGFibGVUYWdzKSB7XG5cdGxldCBsZWZ0ID0gcGFyc2VVbmFyeUV4cHJlc3Npb24oc3RyZWFtLCBhdmFpbGFibGVUYWdzKTtcblx0d2hpbGUgKHN0cmVhbS5wZWVrKCkudHlwZSA9PT0gXCJBTkRcIikge1xuXHRcdHN0cmVhbS5uZXh0KCk7XG5cdFx0Y29uc3QgcmlnaHQgPSBwYXJzZVVuYXJ5RXhwcmVzc2lvbihzdHJlYW0sIGF2YWlsYWJsZVRhZ3MpO1xuXHRcdGxlZnQgPSB7XG5cdFx0XHR0eXBlOiBcImFuZFwiLFxuXHRcdFx0bGVmdCxcblx0XHRcdHJpZ2h0XG5cdFx0fTtcblx0fVxuXHRyZXR1cm4gbGVmdDtcbn1cbmZ1bmN0aW9uIHBhcnNlVW5hcnlFeHByZXNzaW9uKHN0cmVhbSwgYXZhaWxhYmxlVGFncykge1xuXHRpZiAoc3RyZWFtLnBlZWsoKS50eXBlID09PSBcIk5PVFwiKSB7XG5cdFx0c3RyZWFtLm5leHQoKTtcblx0XHRjb25zdCBvcGVyYW5kID0gcGFyc2VVbmFyeUV4cHJlc3Npb24oc3RyZWFtLCBhdmFpbGFibGVUYWdzKTtcblx0XHRyZXR1cm4ge1xuXHRcdFx0dHlwZTogXCJub3RcIixcblx0XHRcdG9wZXJhbmRcblx0XHR9O1xuXHR9XG5cdHJldHVybiBwYXJzZVByaW1hcnlFeHByZXNzaW9uKHN0cmVhbSwgYXZhaWxhYmxlVGFncyk7XG59XG5mdW5jdGlvbiBwYXJzZVByaW1hcnlFeHByZXNzaW9uKHN0cmVhbSwgYXZhaWxhYmxlVGFncykge1xuXHRjb25zdCB0b2tlbiA9IHN0cmVhbS5wZWVrKCk7XG5cdGlmICh0b2tlbi50eXBlID09PSBcIkxQQVJFTlwiKSB7XG5cdFx0c3RyZWFtLm5leHQoKTtcblx0XHRjb25zdCBleHByID0gcGFyc2VPckV4cHJlc3Npb24oc3RyZWFtLCBhdmFpbGFibGVUYWdzKTtcblx0XHRzdHJlYW0uZXhwZWN0KFwiUlBBUkVOXCIpO1xuXHRcdHJldHVybiBleHByO1xuXHR9XG5cdGlmICh0b2tlbi50eXBlID09PSBcIlRBR1wiKSB7XG5cdFx0c3RyZWFtLm5leHQoKTtcblx0XHRjb25zdCB0YWdWYWx1ZSA9IHRva2VuLnZhbHVlO1xuXHRcdGNvbnN0IHBhdHRlcm4gPSByZXNvbHZlVGFnUGF0dGVybih0YWdWYWx1ZSwgYXZhaWxhYmxlVGFncyk7XG5cdFx0cmV0dXJuIHtcblx0XHRcdHR5cGU6IFwidGFnXCIsXG5cdFx0XHR2YWx1ZTogdGFnVmFsdWUsXG5cdFx0XHRwYXR0ZXJuXG5cdFx0fTtcblx0fVxuXHRzdHJlYW0udW5leHBlY3RlZFRva2VuKCk7XG59XG5mdW5jdGlvbiBjcmVhdGVXaWxkY2FyZFJlZ2V4KHBhdHRlcm4pIHtcblx0cmV0dXJuIG5ldyBSZWdFeHAoYF4ke3BhdHRlcm4ucmVwbGFjZSgvWy4rP14ke30oKXxbXFxdXFxcXF0vZywgXCJcXFxcJCZcIikucmVwbGFjZSgvXFwqL2csIFwiLipcIil9JGApO1xufVxuZnVuY3Rpb24gcmVzb2x2ZVRhZ1BhdHRlcm4odGFnUGF0dGVybiwgYXZhaWxhYmxlVGFncykge1xuXHRpZiAodGFnUGF0dGVybi5pbmNsdWRlcyhcIipcIikpIHtcblx0XHRjb25zdCByZWdleCA9IGNyZWF0ZVdpbGRjYXJkUmVnZXgodGFnUGF0dGVybik7XG5cdFx0Y29uc3QgaGFzTWF0Y2ggPSBhdmFpbGFibGVUYWdzLnNvbWUoKHRhZykgPT4gcmVnZXgudGVzdCh0YWcubmFtZSkpO1xuXHRcdGlmICghaGFzTWF0Y2gpIHtcblx0XHRcdHRocm93IGNyZWF0ZU5vVGFnc0Vycm9yKGF2YWlsYWJsZVRhZ3MsIHRhZ1BhdHRlcm4sIFwidGFnIHBhdHRlcm5cIik7XG5cdFx0fVxuXHRcdHJldHVybiByZWdleDtcblx0fVxuXHRpZiAoIWF2YWlsYWJsZVRhZ3MubGVuZ3RoIHx8ICFhdmFpbGFibGVUYWdzLnNvbWUoKHRhZykgPT4gdGFnLm5hbWUgPT09IHRhZ1BhdHRlcm4pKSB7XG5cdFx0dGhyb3cgY3JlYXRlTm9UYWdzRXJyb3IoYXZhaWxhYmxlVGFncywgdGFnUGF0dGVybiwgXCJ0YWcgcGF0dGVyblwiKTtcblx0fVxuXHRyZXR1cm4gbnVsbDtcbn1cbmZ1bmN0aW9uIGV2YWx1YXRlTm9kZShub2RlLCB0YWdzKSB7XG5cdHN3aXRjaCAobm9kZS50eXBlKSB7XG5cdFx0Y2FzZSBcInRhZ1wiOlxuXHRcdFx0aWYgKG5vZGUucGF0dGVybikge1xuXHRcdFx0XHRyZXR1cm4gdGFncy5zb21lKCh0YWcpID0+IG5vZGUucGF0dGVybi50ZXN0KHRhZykpO1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIHRhZ3MuaW5jbHVkZXMobm9kZS52YWx1ZSk7XG5cdFx0Y2FzZSBcIm5vdFwiOiByZXR1cm4gIWV2YWx1YXRlTm9kZShub2RlLm9wZXJhbmQsIHRhZ3MpO1xuXHRcdGNhc2UgXCJhbmRcIjogcmV0dXJuIGV2YWx1YXRlTm9kZShub2RlLmxlZnQsIHRhZ3MpICYmIGV2YWx1YXRlTm9kZShub2RlLnJpZ2h0LCB0YWdzKTtcblx0XHRjYXNlIFwib3JcIjogcmV0dXJuIGV2YWx1YXRlTm9kZShub2RlLmxlZnQsIHRhZ3MpIHx8IGV2YWx1YXRlTm9kZShub2RlLnJpZ2h0LCB0YWdzKTtcblx0fVxufVxuXG5mdW5jdGlvbiBpc1Rlc3RDYXNlKHMpIHtcblx0cmV0dXJuIHMudHlwZSA9PT0gXCJ0ZXN0XCI7XG59XG5mdW5jdGlvbiBnZXRUZXN0cyhzdWl0ZSkge1xuXHRjb25zdCB0ZXN0cyA9IFtdO1xuXHRjb25zdCBhcnJheVN1aXRlcyA9IHRvQXJyYXkoc3VpdGUpO1xuXHRmb3IgKGNvbnN0IHMgb2YgYXJyYXlTdWl0ZXMpIHtcblx0XHRpZiAoaXNUZXN0Q2FzZShzKSkge1xuXHRcdFx0dGVzdHMucHVzaChzKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0Zm9yIChjb25zdCB0YXNrIG9mIHMudGFza3MpIHtcblx0XHRcdFx0aWYgKGlzVGVzdENhc2UodGFzaykpIHtcblx0XHRcdFx0XHR0ZXN0cy5wdXNoKHRhc2spO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGNvbnN0IHRhc2tUZXN0cyA9IGdldFRlc3RzKHRhc2spO1xuXHRcdFx0XHRcdGZvciAoY29uc3QgdGVzdCBvZiB0YXNrVGVzdHMpIHtcblx0XHRcdFx0XHRcdHRlc3RzLnB1c2godGVzdCk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fVxuXHR9XG5cdHJldHVybiB0ZXN0cztcbn1cbmZ1bmN0aW9uIGdldFRhc2tzKHRhc2tzID0gW10pIHtcblx0cmV0dXJuIHRvQXJyYXkodGFza3MpLmZsYXRNYXAoKHMpID0+IGlzVGVzdENhc2UocykgPyBbc10gOiBbcywgLi4uZ2V0VGFza3Mocy50YXNrcyldKTtcbn1cbmZ1bmN0aW9uIGdldFN1aXRlcyhzdWl0ZSkge1xuXHRyZXR1cm4gdG9BcnJheShzdWl0ZSkuZmxhdE1hcCgocykgPT4gcy50eXBlID09PSBcInN1aXRlXCIgPyBbcywgLi4uZ2V0U3VpdGVzKHMudGFza3MpXSA6IFtdKTtcbn1cbmZ1bmN0aW9uIGhhc1Rlc3RzKHN1aXRlKSB7XG5cdHJldHVybiB0b0FycmF5KHN1aXRlKS5zb21lKChzKSA9PiBzLnRhc2tzLnNvbWUoKGMpID0+IGlzVGVzdENhc2UoYykgfHwgaGFzVGVzdHMoYykpKTtcbn1cbmZ1bmN0aW9uIGhhc0ZhaWxlZChzdWl0ZSkge1xuXHRyZXR1cm4gdG9BcnJheShzdWl0ZSkuc29tZSgocykgPT4gcy5yZXN1bHQ/LnN0YXRlID09PSBcImZhaWxcIiB8fCBzLnR5cGUgPT09IFwic3VpdGVcIiAmJiBoYXNGYWlsZWQocy50YXNrcykpO1xufVxuZnVuY3Rpb24gZ2V0TmFtZXModGFzaykge1xuXHRjb25zdCBuYW1lcyA9IFt0YXNrLm5hbWVdO1xuXHRsZXQgY3VycmVudCA9IHRhc2s7XG5cdHdoaWxlIChjdXJyZW50Py5zdWl0ZSkge1xuXHRcdGN1cnJlbnQgPSBjdXJyZW50LnN1aXRlO1xuXHRcdGlmIChjdXJyZW50Py5uYW1lKSB7XG5cdFx0XHRuYW1lcy51bnNoaWZ0KGN1cnJlbnQubmFtZSk7XG5cdFx0fVxuXHR9XG5cdGlmIChjdXJyZW50ICE9PSB0YXNrLmZpbGUpIHtcblx0XHRuYW1lcy51bnNoaWZ0KHRhc2suZmlsZS5uYW1lKTtcblx0fVxuXHRyZXR1cm4gbmFtZXM7XG59XG5mdW5jdGlvbiBnZXRGdWxsTmFtZSh0YXNrLCBzZXBhcmF0b3IgPSBcIiA+IFwiKSB7XG5cdHJldHVybiBnZXROYW1lcyh0YXNrKS5qb2luKHNlcGFyYXRvcik7XG59XG5mdW5jdGlvbiBnZXRUZXN0TmFtZSh0YXNrLCBzZXBhcmF0b3IgPSBcIiA+IFwiKSB7XG5cdHJldHVybiBnZXROYW1lcyh0YXNrKS5zbGljZSgxKS5qb2luKHNlcGFyYXRvcik7XG59XG5mdW5jdGlvbiBjcmVhdGVUYXNrTmFtZShuYW1lcywgc2VwYXJhdG9yID0gXCIgPiBcIikge1xuXHRyZXR1cm4gbmFtZXMuZmlsdGVyKChuYW1lKSA9PiBuYW1lICE9PSB1bmRlZmluZWQpLmpvaW4oc2VwYXJhdG9yKTtcbn1cblxuLyoqXG4qIENyZWF0ZXMgYSBzdWl0ZSBvZiB0ZXN0cywgYWxsb3dpbmcgZm9yIGdyb3VwaW5nIGFuZCBoaWVyYXJjaGljYWwgb3JnYW5pemF0aW9uIG9mIHRlc3RzLlxuKiBTdWl0ZXMgY2FuIGNvbnRhaW4gYm90aCB0ZXN0cyBhbmQgb3RoZXIgc3VpdGVzLCBlbmFibGluZyBjb21wbGV4IHRlc3Qgc3RydWN0dXJlcy5cbipcbiogQHBhcmFtIHtzdHJpbmd9IG5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgc3VpdGUsIHVzZWQgZm9yIGlkZW50aWZpY2F0aW9uIGFuZCByZXBvcnRpbmcuXG4qIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gQSBmdW5jdGlvbiB0aGF0IGRlZmluZXMgdGhlIHRlc3RzIGFuZCBzdWl0ZXMgd2l0aGluIHRoaXMgc3VpdGUuXG4qIEBleGFtcGxlXG4qIGBgYHRzXG4qIC8vIERlZmluZSBhIHN1aXRlIHdpdGggdHdvIHRlc3RzXG4qIHN1aXRlKCdNYXRoIG9wZXJhdGlvbnMnLCAoKSA9PiB7XG4qICAgdGVzdCgnc2hvdWxkIGFkZCB0d28gbnVtYmVycycsICgpID0+IHtcbiogICAgIGV4cGVjdChhZGQoMSwgMikpLnRvQmUoMyk7XG4qICAgfSk7XG4qXG4qICAgdGVzdCgnc2hvdWxkIHN1YnRyYWN0IHR3byBudW1iZXJzJywgKCkgPT4ge1xuKiAgICAgZXhwZWN0KHN1YnRyYWN0KDUsIDIpKS50b0JlKDMpO1xuKiAgIH0pO1xuKiB9KTtcbiogYGBgXG4qIEBleGFtcGxlXG4qIGBgYHRzXG4qIC8vIERlZmluZSBuZXN0ZWQgc3VpdGVzXG4qIHN1aXRlKCdTdHJpbmcgb3BlcmF0aW9ucycsICgpID0+IHtcbiogICBzdWl0ZSgnVHJpbW1pbmcnLCAoKSA9PiB7XG4qICAgICB0ZXN0KCdzaG91bGQgdHJpbSB3aGl0ZXNwYWNlIGZyb20gc3RhcnQgYW5kIGVuZCcsICgpID0+IHtcbiogICAgICAgZXhwZWN0KCcgIGhlbGxvICAnLnRyaW0oKSkudG9CZSgnaGVsbG8nKTtcbiogICAgIH0pO1xuKiAgIH0pO1xuKlxuKiAgIHN1aXRlKCdDb25jYXRlbmF0aW9uJywgKCkgPT4ge1xuKiAgICAgdGVzdCgnc2hvdWxkIGNvbmNhdGVuYXRlIHR3byBzdHJpbmdzJywgKCkgPT4ge1xuKiAgICAgICBleHBlY3QoJ2hlbGxvJyArICcgJyArICd3b3JsZCcpLnRvQmUoJ2hlbGxvIHdvcmxkJyk7XG4qICAgICB9KTtcbiogICB9KTtcbiogfSk7XG4qIGBgYFxuKi9cbmNvbnN0IHN1aXRlID0gY3JlYXRlU3VpdGUoKTtcbi8qKlxuKiBEZWZpbmVzIGEgdGVzdCBjYXNlIHdpdGggYSBnaXZlbiBuYW1lIGFuZCB0ZXN0IGZ1bmN0aW9uLiBUaGUgdGVzdCBmdW5jdGlvbiBjYW4gb3B0aW9uYWxseSBiZSBjb25maWd1cmVkIHdpdGggdGVzdCBvcHRpb25zLlxuKlxuKiBAcGFyYW0ge3N0cmluZyB8IEZ1bmN0aW9ufSBuYW1lIC0gVGhlIG5hbWUgb2YgdGhlIHRlc3Qgb3IgYSBmdW5jdGlvbiB0aGF0IHdpbGwgYmUgdXNlZCBhcyBhIHRlc3QgbmFtZS5cbiogQHBhcmFtIHtUZXN0T3B0aW9ucyB8IFRlc3RGdW5jdGlvbn0gW29wdGlvbnNPckZuXSAtIE9wdGlvbmFsLiBUaGUgdGVzdCBvcHRpb25zIG9yIHRoZSB0ZXN0IGZ1bmN0aW9uIGlmIG5vIGV4cGxpY2l0IG5hbWUgaXMgcHJvdmlkZWQuXG4qIEBwYXJhbSB7bnVtYmVyIHwgVGVzdE9wdGlvbnMgfCBUZXN0RnVuY3Rpb259IFtvcHRpb25zT3JUZXN0XSAtIE9wdGlvbmFsLiBUaGUgdGVzdCBmdW5jdGlvbiBvciBvcHRpb25zLCBkZXBlbmRpbmcgb24gdGhlIHByZXZpb3VzIHBhcmFtZXRlcnMuXG4qIEB0aHJvd3Mge0Vycm9yfSBJZiBjYWxsZWQgaW5zaWRlIGFub3RoZXIgdGVzdCBmdW5jdGlvbi5cbiogQGV4YW1wbGVcbiogYGBgdHNcbiogLy8gRGVmaW5lIGEgc2ltcGxlIHRlc3RcbiogdGVzdCgnc2hvdWxkIGFkZCB0d28gbnVtYmVycycsICgpID0+IHtcbiogICBleHBlY3QoYWRkKDEsIDIpKS50b0JlKDMpO1xuKiB9KTtcbiogYGBgXG4qIEBleGFtcGxlXG4qIGBgYHRzXG4qIC8vIERlZmluZSBhIHRlc3Qgd2l0aCBvcHRpb25zXG4qIHRlc3QoJ3Nob3VsZCBzdWJ0cmFjdCB0d28gbnVtYmVycycsIHsgcmV0cnk6IDMgfSwgKCkgPT4ge1xuKiAgIGV4cGVjdChzdWJ0cmFjdCg1LCAyKSkudG9CZSgzKTtcbiogfSk7XG4qIGBgYFxuKi9cbmNvbnN0IHRlc3QgPSBjcmVhdGVUZXN0KGZ1bmN0aW9uKG5hbWUsIG9wdGlvbnNPckZuLCBvcHRpb25zT3JUZXN0KSB7XG5cdGlmIChnZXRDdXJyZW50VGVzdCgpKSB7XG5cdFx0dGhyb3cgbmV3IEVycm9yKFwiQ2FsbGluZyB0aGUgdGVzdCBmdW5jdGlvbiBpbnNpZGUgYW5vdGhlciB0ZXN0IGZ1bmN0aW9uIGlzIG5vdCBhbGxvd2VkLiBQbGVhc2UgcHV0IGl0IGluc2lkZSBcXFwiZGVzY3JpYmVcXFwiIG9yIFxcXCJzdWl0ZVxcXCIgc28gaXQgY2FuIGJlIHByb3Blcmx5IGNvbGxlY3RlZC5cIik7XG5cdH1cblx0Z2V0Q3VycmVudFN1aXRlKCkudGVzdC5mbi5jYWxsKHRoaXMsIGZvcm1hdE5hbWUobmFtZSksIG9wdGlvbnNPckZuLCBvcHRpb25zT3JUZXN0KTtcbn0pO1xuLyoqXG4qIENyZWF0ZXMgYSBzdWl0ZSBvZiB0ZXN0cywgYWxsb3dpbmcgZm9yIGdyb3VwaW5nIGFuZCBoaWVyYXJjaGljYWwgb3JnYW5pemF0aW9uIG9mIHRlc3RzLlxuKiBTdWl0ZXMgY2FuIGNvbnRhaW4gYm90aCB0ZXN0cyBhbmQgb3RoZXIgc3VpdGVzLCBlbmFibGluZyBjb21wbGV4IHRlc3Qgc3RydWN0dXJlcy5cbipcbiogQHBhcmFtIHtzdHJpbmd9IG5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgc3VpdGUsIHVzZWQgZm9yIGlkZW50aWZpY2F0aW9uIGFuZCByZXBvcnRpbmcuXG4qIEBwYXJhbSB7RnVuY3Rpb259IGZuIC0gQSBmdW5jdGlvbiB0aGF0IGRlZmluZXMgdGhlIHRlc3RzIGFuZCBzdWl0ZXMgd2l0aGluIHRoaXMgc3VpdGUuXG4qIEBleGFtcGxlXG4qIGBgYHRzXG4qIC8vIERlZmluZSBhIHN1aXRlIHdpdGggdHdvIHRlc3RzXG4qIGRlc2NyaWJlKCdNYXRoIG9wZXJhdGlvbnMnLCAoKSA9PiB7XG4qICAgdGVzdCgnc2hvdWxkIGFkZCB0d28gbnVtYmVycycsICgpID0+IHtcbiogICAgIGV4cGVjdChhZGQoMSwgMikpLnRvQmUoMyk7XG4qICAgfSk7XG4qXG4qICAgdGVzdCgnc2hvdWxkIHN1YnRyYWN0IHR3byBudW1iZXJzJywgKCkgPT4ge1xuKiAgICAgZXhwZWN0KHN1YnRyYWN0KDUsIDIpKS50b0JlKDMpO1xuKiAgIH0pO1xuKiB9KTtcbiogYGBgXG4qIEBleGFtcGxlXG4qIGBgYHRzXG4qIC8vIERlZmluZSBuZXN0ZWQgc3VpdGVzXG4qIGRlc2NyaWJlKCdTdHJpbmcgb3BlcmF0aW9ucycsICgpID0+IHtcbiogICBkZXNjcmliZSgnVHJpbW1pbmcnLCAoKSA9PiB7XG4qICAgICB0ZXN0KCdzaG91bGQgdHJpbSB3aGl0ZXNwYWNlIGZyb20gc3RhcnQgYW5kIGVuZCcsICgpID0+IHtcbiogICAgICAgZXhwZWN0KCcgIGhlbGxvICAnLnRyaW0oKSkudG9CZSgnaGVsbG8nKTtcbiogICAgIH0pO1xuKiAgIH0pO1xuKlxuKiAgIGRlc2NyaWJlKCdDb25jYXRlbmF0aW9uJywgKCkgPT4ge1xuKiAgICAgdGVzdCgnc2hvdWxkIGNvbmNhdGVuYXRlIHR3byBzdHJpbmdzJywgKCkgPT4ge1xuKiAgICAgICBleHBlY3QoJ2hlbGxvJyArICcgJyArICd3b3JsZCcpLnRvQmUoJ2hlbGxvIHdvcmxkJyk7XG4qICAgICB9KTtcbiogICB9KTtcbiogfSk7XG4qIGBgYFxuKi9cbmNvbnN0IGRlc2NyaWJlID0gc3VpdGU7XG4vKipcbiogRGVmaW5lcyBhIHRlc3QgY2FzZSB3aXRoIGEgZ2l2ZW4gbmFtZSBhbmQgdGVzdCBmdW5jdGlvbi4gVGhlIHRlc3QgZnVuY3Rpb24gY2FuIG9wdGlvbmFsbHkgYmUgY29uZmlndXJlZCB3aXRoIHRlc3Qgb3B0aW9ucy5cbipcbiogQHBhcmFtIHtzdHJpbmcgfCBGdW5jdGlvbn0gbmFtZSAtIFRoZSBuYW1lIG9mIHRoZSB0ZXN0IG9yIGEgZnVuY3Rpb24gdGhhdCB3aWxsIGJlIHVzZWQgYXMgYSB0ZXN0IG5hbWUuXG4qIEBwYXJhbSB7VGVzdE9wdGlvbnMgfCBUZXN0RnVuY3Rpb259IFtvcHRpb25zT3JGbl0gLSBPcHRpb25hbC4gVGhlIHRlc3Qgb3B0aW9ucyBvciB0aGUgdGVzdCBmdW5jdGlvbiBpZiBubyBleHBsaWNpdCBuYW1lIGlzIHByb3ZpZGVkLlxuKiBAcGFyYW0ge251bWJlciB8IFRlc3RPcHRpb25zIHwgVGVzdEZ1bmN0aW9ufSBbb3B0aW9uc09yVGVzdF0gLSBPcHRpb25hbC4gVGhlIHRlc3QgZnVuY3Rpb24gb3Igb3B0aW9ucywgZGVwZW5kaW5nIG9uIHRoZSBwcmV2aW91cyBwYXJhbWV0ZXJzLlxuKiBAdGhyb3dzIHtFcnJvcn0gSWYgY2FsbGVkIGluc2lkZSBhbm90aGVyIHRlc3QgZnVuY3Rpb24uXG4qIEBleGFtcGxlXG4qIGBgYHRzXG4qIC8vIERlZmluZSBhIHNpbXBsZSB0ZXN0XG4qIGl0KCdhZGRzIHR3byBudW1iZXJzJywgKCkgPT4ge1xuKiAgIGV4cGVjdChhZGQoMSwgMikpLnRvQmUoMyk7XG4qIH0pO1xuKiBgYGBcbiogQGV4YW1wbGVcbiogYGBgdHNcbiogLy8gRGVmaW5lIGEgdGVzdCB3aXRoIG9wdGlvbnNcbiogaXQoJ3N1YnRyYWN0cyB0d28gbnVtYmVycycsIHsgcmV0cnk6IDMgfSwgKCkgPT4ge1xuKiAgIGV4cGVjdChzdWJ0cmFjdCg1LCAyKSkudG9CZSgzKTtcbiogfSk7XG4qIGBgYFxuKi9cbmNvbnN0IGl0ID0gdGVzdDtcbmxldCBydW5uZXI7XG5sZXQgZGVmYXVsdFN1aXRlO1xubGV0IGN1cnJlbnRUZXN0RmlsZXBhdGg7XG5mdW5jdGlvbiBhc3NlcnQoY29uZGl0aW9uLCBtZXNzYWdlKSB7XG5cdGlmICghY29uZGl0aW9uKSB7XG5cdFx0dGhyb3cgbmV3IEVycm9yKGBWaXRlc3QgZmFpbGVkIHRvIGZpbmQgJHttZXNzYWdlfS4gT25lIG9mIHRoZSBmb2xsb3dpbmcgaXMgcG9zc2libGU6YCArIFwiXFxuLSBcXFwidml0ZXN0XFxcIiBpcyBpbXBvcnRlZCBkaXJlY3RseSB3aXRob3V0IHJ1bm5pbmcgXFxcInZpdGVzdFxcXCIgY29tbWFuZFwiICsgXCJcXG4tIFxcXCJ2aXRlc3RcXFwiIGlzIGltcG9ydGVkIGluc2lkZSBcXFwiZ2xvYmFsU2V0dXBcXFwiICh0byBmaXggdGhpcywgdXNlIFxcXCJzZXR1cEZpbGVzXFxcIiBpbnN0ZWFkLCBiZWNhdXNlIFxcXCJnbG9iYWxTZXR1cFxcXCIgcnVucyBpbiBhIGRpZmZlcmVudCBjb250ZXh0KVwiICsgXCJcXG4tIFxcXCJ2aXRlc3RcXFwiIGlzIGltcG9ydGVkIGluc2lkZSBWaXRlIC8gVml0ZXN0IGNvbmZpZyBmaWxlXCIgKyBcIlxcbi0gT3RoZXJ3aXNlLCBpdCBtaWdodCBiZSBhIFZpdGVzdCBidWcuIFBsZWFzZSByZXBvcnQgaXQgdG8gaHR0cHM6Ly9naXRodWIuY29tL3ZpdGVzdC1kZXYvdml0ZXN0L2lzc3Vlc1xcblwiKTtcblx0fVxufVxuZnVuY3Rpb24gZ2V0RGVmYXVsdFN1aXRlKCkge1xuXHRhc3NlcnQoZGVmYXVsdFN1aXRlLCBcInRoZSBkZWZhdWx0IHN1aXRlXCIpO1xuXHRyZXR1cm4gZGVmYXVsdFN1aXRlO1xufVxuZnVuY3Rpb24gZ2V0UnVubmVyKCkge1xuXHRhc3NlcnQocnVubmVyLCBcInRoZSBydW5uZXJcIik7XG5cdHJldHVybiBydW5uZXI7XG59XG5mdW5jdGlvbiBjcmVhdGVEZWZhdWx0U3VpdGUocnVubmVyKSB7XG5cdGNvbnN0IGNvbmZpZyA9IHJ1bm5lci5jb25maWcuc2VxdWVuY2U7XG5cdGNvbnN0IG9wdGlvbnMgPSB7fTtcblx0aWYgKGNvbmZpZy5jb25jdXJyZW50ICE9IG51bGwpIHtcblx0XHRvcHRpb25zLmNvbmN1cnJlbnQgPSBjb25maWcuY29uY3VycmVudDtcblx0fVxuXHRjb25zdCBjb2xsZWN0b3IgPSBzdWl0ZShcIlwiLCBvcHRpb25zLCAoKSA9PiB7fSk7XG5cdC8vIG5vIHBhcmVudCBzdWl0ZSBmb3IgdG9wLWxldmVsIHRlc3RzXG5cdGRlbGV0ZSBjb2xsZWN0b3Iuc3VpdGU7XG5cdHJldHVybiBjb2xsZWN0b3I7XG59XG5mdW5jdGlvbiBjbGVhckNvbGxlY3RvckNvbnRleHQoZmlsZSwgY3VycmVudFJ1bm5lcikge1xuXHRjdXJyZW50VGVzdEZpbGVwYXRoID0gZmlsZS5maWxlcGF0aDtcblx0cnVubmVyID0gY3VycmVudFJ1bm5lcjtcblx0aWYgKCFkZWZhdWx0U3VpdGUpIHtcblx0XHRkZWZhdWx0U3VpdGUgPSBjcmVhdGVEZWZhdWx0U3VpdGUoY3VycmVudFJ1bm5lcik7XG5cdH1cblx0ZGVmYXVsdFN1aXRlLmZpbGUgPSBmaWxlO1xuXHRjb2xsZWN0b3JDb250ZXh0LnRhc2tzLmxlbmd0aCA9IDA7XG5cdGRlZmF1bHRTdWl0ZS5jbGVhcigpO1xuXHRjb2xsZWN0b3JDb250ZXh0LmN1cnJlbnRTdWl0ZSA9IGRlZmF1bHRTdWl0ZTtcbn1cbmZ1bmN0aW9uIGdldEN1cnJlbnRTdWl0ZSgpIHtcblx0Y29uc3QgY3VycmVudFN1aXRlID0gY29sbGVjdG9yQ29udGV4dC5jdXJyZW50U3VpdGUgfHwgZGVmYXVsdFN1aXRlO1xuXHRhc3NlcnQoY3VycmVudFN1aXRlLCBcInRoZSBjdXJyZW50IHN1aXRlXCIpO1xuXHRyZXR1cm4gY3VycmVudFN1aXRlO1xufVxuZnVuY3Rpb24gY3JlYXRlU3VpdGVIb29rcygpIHtcblx0cmV0dXJuIHtcblx0XHRiZWZvcmVBbGw6IFtdLFxuXHRcdGFmdGVyQWxsOiBbXSxcblx0XHRiZWZvcmVFYWNoOiBbXSxcblx0XHRhZnRlckVhY2g6IFtdLFxuXHRcdGFyb3VuZEVhY2g6IFtdLFxuXHRcdGFyb3VuZEFsbDogW11cblx0fTtcbn1cbmNvbnN0IFBPU0lUSVZFX0lORklOSVRZID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuZnVuY3Rpb24gcGFyc2VBcmd1bWVudHMob3B0aW9uc09yRm4sIHRpbWVvdXRPclRlc3QpIHtcblx0aWYgKHRpbWVvdXRPclRlc3QgIT0gbnVsbCAmJiB0eXBlb2YgdGltZW91dE9yVGVzdCA9PT0gXCJvYmplY3RcIikge1xuXHRcdHRocm93IG5ldyBUeXBlRXJyb3IoYFNpZ25hdHVyZSBcInRlc3QobmFtZSwgZm4sIHsgLi4uIH0pXCIgd2FzIGRlcHJlY2F0ZWQgaW4gVml0ZXN0IDMgYW5kIHJlbW92ZWQgaW4gVml0ZXN0IDQuIFBsZWFzZSwgcHJvdmlkZSBvcHRpb25zIGFzIGEgc2Vjb25kIGFyZ3VtZW50IGluc3RlYWQuYCk7XG5cdH1cblx0bGV0IG9wdGlvbnMgPSB7fTtcblx0bGV0IGZuO1xuXHQvLyBpdCgnJywgKCkgPT4ge30sIDEwMDApXG5cdGlmICh0eXBlb2YgdGltZW91dE9yVGVzdCA9PT0gXCJudW1iZXJcIikge1xuXHRcdG9wdGlvbnMgPSB7IHRpbWVvdXQ6IHRpbWVvdXRPclRlc3QgfTtcblx0fSBlbHNlIGlmICh0eXBlb2Ygb3B0aW9uc09yRm4gPT09IFwib2JqZWN0XCIpIHtcblx0XHRvcHRpb25zID0gb3B0aW9uc09yRm47XG5cdH1cblx0aWYgKHR5cGVvZiBvcHRpb25zT3JGbiA9PT0gXCJmdW5jdGlvblwiKSB7XG5cdFx0aWYgKHR5cGVvZiB0aW1lb3V0T3JUZXN0ID09PSBcImZ1bmN0aW9uXCIpIHtcblx0XHRcdHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5ub3QgdXNlIHR3byBmdW5jdGlvbnMgYXMgYXJndW1lbnRzLiBQbGVhc2UgdXNlIHRoZSBzZWNvbmQgYXJndW1lbnQgZm9yIG9wdGlvbnMuXCIpO1xuXHRcdH1cblx0XHRmbiA9IG9wdGlvbnNPckZuO1xuXHR9IGVsc2UgaWYgKHR5cGVvZiB0aW1lb3V0T3JUZXN0ID09PSBcImZ1bmN0aW9uXCIpIHtcblx0XHRmbiA9IHRpbWVvdXRPclRlc3Q7XG5cdH1cblx0cmV0dXJuIHtcblx0XHRvcHRpb25zLFxuXHRcdGhhbmRsZXI6IGZuXG5cdH07XG59XG4vLyBpbXBsZW1lbnRhdGlvbnNcbmZ1bmN0aW9uIGNyZWF0ZVN1aXRlQ29sbGVjdG9yKG5hbWUsIGZhY3RvcnkgPSAoKSA9PiB7fSwgbW9kZSwgZWFjaCwgc3VpdGVPcHRpb25zKSB7XG5cdGNvbnN0IHRhc2tzID0gW107XG5cdGxldCBzdWl0ZTtcblx0aW5pdFN1aXRlKHRydWUpO1xuXHRjb25zdCB0YXNrID0gZnVuY3Rpb24obmFtZSA9IFwiXCIsIG9wdGlvbnMgPSB7fSkge1xuXHRcdGNvbnN0IGN1cnJlbnRTdWl0ZSA9IGNvbGxlY3RvckNvbnRleHQuY3VycmVudFN1aXRlPy5zdWl0ZTtcblx0XHRjb25zdCBwYXJlbnRUYXNrID0gY3VycmVudFN1aXRlID8/IGNvbGxlY3RvckNvbnRleHQuY3VycmVudFN1aXRlPy5maWxlO1xuXHRcdGNvbnN0IHBhcmVudFRhZ3MgPSBwYXJlbnRUYXNrPy50YWdzIHx8IFtdO1xuXHRcdGNvbnN0IHRlc3RUYWdzID0gdW5pcXVlKFsuLi5wYXJlbnRUYWdzLCAuLi50b0FycmF5KG9wdGlvbnMudGFncyldKTtcblx0XHRjb25zdCB0YWdzT3B0aW9ucyA9IHRlc3RUYWdzLm1hcCgodGFnKSA9PiB7XG5cdFx0XHRjb25zdCB0YWdEZWZpbml0aW9uID0gcnVubmVyLmNvbmZpZy50YWdzPy5maW5kKCh0KSA9PiB0Lm5hbWUgPT09IHRhZyk7XG5cdFx0XHRpZiAoIXRhZ0RlZmluaXRpb24gJiYgcnVubmVyLmNvbmZpZy5zdHJpY3RUYWdzKSB7XG5cdFx0XHRcdHRocm93IGNyZWF0ZU5vVGFnc0Vycm9yKHJ1bm5lci5jb25maWcudGFncywgdGFnKTtcblx0XHRcdH1cblx0XHRcdHJldHVybiB0YWdEZWZpbml0aW9uO1xuXHRcdH0pLmZpbHRlcigocikgPT4gciAhPSBudWxsKS5zb3J0KCh0YWcxLCB0YWcyKSA9PiAodGFnMi5wcmlvcml0eSA/PyBQT1NJVElWRV9JTkZJTklUWSkgLSAodGFnMS5wcmlvcml0eSA/PyBQT1NJVElWRV9JTkZJTklUWSkpLnJlZHVjZSgoYWNjLCB0YWcpID0+IHtcblx0XHRcdGNvbnN0IHsgbmFtZSwgZGVzY3JpcHRpb24sIHByaW9yaXR5LCBtZXRhLCAuLi5vcHRpb25zIH0gPSB0YWc7XG5cdFx0XHRPYmplY3QuYXNzaWduKGFjYywgb3B0aW9ucyk7XG5cdFx0XHRpZiAobWV0YSkge1xuXHRcdFx0XHRhY2MubWV0YSA9IE9iamVjdC5hc3NpZ24oYWNjLm1ldGEgPz8gT2JqZWN0LmNyZWF0ZShudWxsKSwgbWV0YSk7XG5cdFx0XHR9XG5cdFx0XHRyZXR1cm4gYWNjO1xuXHRcdH0sIHt9KTtcblx0XHRjb25zdCB0ZXN0T3duTWV0YSA9IG9wdGlvbnMubWV0YTtcblx0XHRvcHRpb25zID0ge1xuXHRcdFx0Li4udGFnc09wdGlvbnMsXG5cdFx0XHQuLi5vcHRpb25zXG5cdFx0fTtcblx0XHRjb25zdCB0aW1lb3V0ID0gb3B0aW9ucy50aW1lb3V0ID8/IHJ1bm5lci5jb25maWcudGVzdFRpbWVvdXQ7XG5cdFx0Y29uc3QgcGFyZW50TWV0YSA9IGN1cnJlbnRTdWl0ZT8ubWV0YTtcblx0XHRjb25zdCB0YWdNZXRhID0gdGFnc09wdGlvbnMubWV0YTtcblx0XHRjb25zdCB0ZXN0TWV0YSA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG5cdFx0aWYgKHRhZ01ldGEpIHtcblx0XHRcdE9iamVjdC5hc3NpZ24odGVzdE1ldGEsIHRhZ01ldGEpO1xuXHRcdH1cblx0XHRpZiAocGFyZW50TWV0YSkge1xuXHRcdFx0T2JqZWN0LmFzc2lnbih0ZXN0TWV0YSwgcGFyZW50TWV0YSk7XG5cdFx0fVxuXHRcdGlmICh0ZXN0T3duTWV0YSkge1xuXHRcdFx0T2JqZWN0LmFzc2lnbih0ZXN0TWV0YSwgdGVzdE93bk1ldGEpO1xuXHRcdH1cblx0XHRjb25zdCB0YXNrID0ge1xuXHRcdFx0aWQ6IFwiXCIsXG5cdFx0XHRuYW1lLFxuXHRcdFx0ZnVsbE5hbWU6IGNyZWF0ZVRhc2tOYW1lKFtjdXJyZW50U3VpdGU/LmZ1bGxOYW1lID8/IGNvbGxlY3RvckNvbnRleHQuY3VycmVudFN1aXRlPy5maWxlPy5mdWxsTmFtZSwgbmFtZV0pLFxuXHRcdFx0ZnVsbFRlc3ROYW1lOiBjcmVhdGVUYXNrTmFtZShbY3VycmVudFN1aXRlPy5mdWxsVGVzdE5hbWUsIG5hbWVdKSxcblx0XHRcdHN1aXRlOiBjdXJyZW50U3VpdGUsXG5cdFx0XHRlYWNoOiBvcHRpb25zLmVhY2gsXG5cdFx0XHRmYWlsczogb3B0aW9ucy5mYWlscyxcblx0XHRcdGNvbnRleHQ6IHVuZGVmaW5lZCxcblx0XHRcdHR5cGU6IFwidGVzdFwiLFxuXHRcdFx0ZmlsZTogY3VycmVudFN1aXRlPy5maWxlID8/IGNvbGxlY3RvckNvbnRleHQuY3VycmVudFN1aXRlPy5maWxlLFxuXHRcdFx0dGltZW91dCxcblx0XHRcdHJldHJ5OiBvcHRpb25zLnJldHJ5ID8/IHJ1bm5lci5jb25maWcucmV0cnksXG5cdFx0XHRyZXBlYXRzOiBvcHRpb25zLnJlcGVhdHMsXG5cdFx0XHRtb2RlOiBvcHRpb25zLm9ubHkgPyBcIm9ubHlcIiA6IG9wdGlvbnMuc2tpcCA/IFwic2tpcFwiIDogb3B0aW9ucy50b2RvID8gXCJ0b2RvXCIgOiBcInJ1blwiLFxuXHRcdFx0bWV0YTogdGVzdE1ldGEsXG5cdFx0XHRhbm5vdGF0aW9uczogW10sXG5cdFx0XHRhcnRpZmFjdHM6IFtdLFxuXHRcdFx0dGFnczogdGVzdFRhZ3Ncblx0XHR9O1xuXHRcdGNvbnN0IGhhbmRsZXIgPSBvcHRpb25zLmhhbmRsZXI7XG5cdFx0aWYgKHRhc2subW9kZSA9PT0gXCJydW5cIiAmJiAhaGFuZGxlcikge1xuXHRcdFx0dGFzay5tb2RlID0gXCJ0b2RvXCI7XG5cdFx0fVxuXHRcdGlmIChvcHRpb25zLmNvbmN1cnJlbnQgfHwgIW9wdGlvbnMuc2VxdWVudGlhbCAmJiBydW5uZXIuY29uZmlnLnNlcXVlbmNlLmNvbmN1cnJlbnQpIHtcblx0XHRcdHRhc2suY29uY3VycmVudCA9IHRydWU7XG5cdFx0fVxuXHRcdHRhc2suc2h1ZmZsZSA9IHN1aXRlT3B0aW9ucz8uc2h1ZmZsZTtcblx0XHRjb25zdCBjb250ZXh0ID0gY3JlYXRlVGVzdENvbnRleHQodGFzaywgcnVubmVyKTtcblx0XHQvLyBjcmVhdGUgdGVzdCBjb250ZXh0XG5cdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KHRhc2ssIFwiY29udGV4dFwiLCB7XG5cdFx0XHR2YWx1ZTogY29udGV4dCxcblx0XHRcdGVudW1lcmFibGU6IGZhbHNlXG5cdFx0fSk7XG5cdFx0c2V0VGVzdEZpeHR1cmUoY29udGV4dCwgb3B0aW9ucy5maXh0dXJlcyA/PyBuZXcgVGVzdEZpeHR1cmVzKCkpO1xuXHRcdGNvbnN0IGxpbWl0ID0gRXJyb3Iuc3RhY2tUcmFjZUxpbWl0O1xuXHRcdEVycm9yLnN0YWNrVHJhY2VMaW1pdCA9IDEwO1xuXHRcdGNvbnN0IHN0YWNrVHJhY2VFcnJvciA9IG5ldyBFcnJvcihcIlNUQUNLX1RSQUNFX0VSUk9SXCIpO1xuXHRcdEVycm9yLnN0YWNrVHJhY2VMaW1pdCA9IGxpbWl0O1xuXHRcdGlmIChoYW5kbGVyKSB7XG5cdFx0XHRzZXRGbih0YXNrLCB3aXRoVGltZW91dCh3aXRoQ2FuY2VsKHdpdGhBd2FpdEFzeW5jQXNzZXJ0aW9ucyh3aXRoRml4dHVyZXMoaGFuZGxlciwgeyBjb250ZXh0IH0pLCB0YXNrKSwgdGFzay5jb250ZXh0LnNpZ25hbCksIHRpbWVvdXQsIGZhbHNlLCBzdGFja1RyYWNlRXJyb3IsIChfLCBlcnJvcikgPT4gYWJvcnRJZlRpbWVvdXQoW2NvbnRleHRdLCBlcnJvcikpKTtcblx0XHR9XG5cdFx0aWYgKHJ1bm5lci5jb25maWcuaW5jbHVkZVRhc2tMb2NhdGlvbikge1xuXHRcdFx0Y29uc3QgZXJyb3IgPSBzdGFja1RyYWNlRXJyb3Iuc3RhY2s7XG5cdFx0XHRjb25zdCBzdGFjayA9IGZpbmRUZXN0RmlsZVN0YWNrVHJhY2UoY3VycmVudFRlc3RGaWxlcGF0aCwgZXJyb3IpO1xuXHRcdFx0aWYgKHN0YWNrKSB7XG5cdFx0XHRcdHRhc2subG9jYXRpb24gPSB7XG5cdFx0XHRcdFx0bGluZTogc3RhY2subGluZSxcblx0XHRcdFx0XHRjb2x1bW46IHN0YWNrLmNvbHVtblxuXHRcdFx0XHR9O1xuXHRcdFx0fVxuXHRcdH1cblx0XHR0YXNrcy5wdXNoKHRhc2spO1xuXHRcdHJldHVybiB0YXNrO1xuXHR9O1xuXHRjb25zdCB0ZXN0ID0gY3JlYXRlVGVzdChmdW5jdGlvbihuYW1lLCBvcHRpb25zT3JGbiwgdGltZW91dE9yVGVzdCkge1xuXHRcdGxldCB7IG9wdGlvbnMsIGhhbmRsZXIgfSA9IHBhcnNlQXJndW1lbnRzKG9wdGlvbnNPckZuLCB0aW1lb3V0T3JUZXN0KTtcblx0XHQvLyBpbmhlcml0IHJlcGVhdHMsIHJldHJ5LCB0aW1lb3V0IGZyb20gc3VpdGVcblx0XHRpZiAodHlwZW9mIHN1aXRlT3B0aW9ucyA9PT0gXCJvYmplY3RcIikge1xuXHRcdFx0b3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oe30sIHN1aXRlT3B0aW9ucywgb3B0aW9ucyk7XG5cdFx0fVxuXHRcdC8vIGluaGVyaXQgY29uY3VycmVudCAvIHNlcXVlbnRpYWwgZnJvbSBzdWl0ZVxuXHRcdGNvbnN0IGNvbmN1cnJlbnQgPSB0aGlzLmNvbmN1cnJlbnQgPz8gKCF0aGlzLnNlcXVlbnRpYWwgJiYgb3B0aW9ucz8uY29uY3VycmVudCk7XG5cdFx0aWYgKG9wdGlvbnMuY29uY3VycmVudCAhPSBudWxsICYmIGNvbmN1cnJlbnQgIT0gbnVsbCkge1xuXHRcdFx0b3B0aW9ucy5jb25jdXJyZW50ID0gY29uY3VycmVudDtcblx0XHR9XG5cdFx0Y29uc3Qgc2VxdWVudGlhbCA9IHRoaXMuc2VxdWVudGlhbCA/PyAoIXRoaXMuY29uY3VycmVudCAmJiBvcHRpb25zPy5zZXF1ZW50aWFsKTtcblx0XHRpZiAob3B0aW9ucy5zZXF1ZW50aWFsICE9IG51bGwgJiYgc2VxdWVudGlhbCAhPSBudWxsKSB7XG5cdFx0XHRvcHRpb25zLnNlcXVlbnRpYWwgPSBzZXF1ZW50aWFsO1xuXHRcdH1cblx0XHRjb25zdCB0ZXN0ID0gdGFzayhmb3JtYXROYW1lKG5hbWUpLCB7XG5cdFx0XHQuLi50aGlzLFxuXHRcdFx0Li4ub3B0aW9ucyxcblx0XHRcdGhhbmRsZXJcblx0XHR9KTtcblx0XHR0ZXN0LnR5cGUgPSBcInRlc3RcIjtcblx0fSk7XG5cdGNvbnN0IGNvbGxlY3RvciA9IHtcblx0XHR0eXBlOiBcImNvbGxlY3RvclwiLFxuXHRcdG5hbWUsXG5cdFx0bW9kZSxcblx0XHRzdWl0ZSxcblx0XHRvcHRpb25zOiBzdWl0ZU9wdGlvbnMsXG5cdFx0dGVzdCxcblx0XHRmaWxlOiBzdWl0ZS5maWxlLFxuXHRcdHRhc2tzLFxuXHRcdGNvbGxlY3QsXG5cdFx0dGFzayxcblx0XHRjbGVhcixcblx0XHRvbjogYWRkSG9va1xuXHR9O1xuXHRmdW5jdGlvbiBhZGRIb29rKG5hbWUsIC4uLmZuKSB7XG5cdFx0Z2V0SG9va3Moc3VpdGUpW25hbWVdLnB1c2goLi4uZm4pO1xuXHR9XG5cdGZ1bmN0aW9uIGluaXRTdWl0ZShpbmNsdWRlTG9jYXRpb24pIHtcblx0XHRpZiAodHlwZW9mIHN1aXRlT3B0aW9ucyA9PT0gXCJudW1iZXJcIikge1xuXHRcdFx0c3VpdGVPcHRpb25zID0geyB0aW1lb3V0OiBzdWl0ZU9wdGlvbnMgfTtcblx0XHR9XG5cdFx0Y29uc3QgY3VycmVudFN1aXRlID0gY29sbGVjdG9yQ29udGV4dC5jdXJyZW50U3VpdGU/LnN1aXRlO1xuXHRcdGNvbnN0IHBhcmVudFRhc2sgPSBjdXJyZW50U3VpdGUgPz8gY29sbGVjdG9yQ29udGV4dC5jdXJyZW50U3VpdGU/LmZpbGU7XG5cdFx0Y29uc3Qgc3VpdGVUYWdzID0gdG9BcnJheShzdWl0ZU9wdGlvbnM/LnRhZ3MpO1xuXHRcdHZhbGlkYXRlVGFncyhydW5uZXIuY29uZmlnLCBzdWl0ZVRhZ3MpO1xuXHRcdHN1aXRlID0ge1xuXHRcdFx0aWQ6IFwiXCIsXG5cdFx0XHR0eXBlOiBcInN1aXRlXCIsXG5cdFx0XHRuYW1lLFxuXHRcdFx0ZnVsbE5hbWU6IGNyZWF0ZVRhc2tOYW1lKFtjdXJyZW50U3VpdGU/LmZ1bGxOYW1lID8/IGNvbGxlY3RvckNvbnRleHQuY3VycmVudFN1aXRlPy5maWxlPy5mdWxsTmFtZSwgbmFtZV0pLFxuXHRcdFx0ZnVsbFRlc3ROYW1lOiBjcmVhdGVUYXNrTmFtZShbY3VycmVudFN1aXRlPy5mdWxsVGVzdE5hbWUsIG5hbWVdKSxcblx0XHRcdHN1aXRlOiBjdXJyZW50U3VpdGUsXG5cdFx0XHRtb2RlLFxuXHRcdFx0ZWFjaCxcblx0XHRcdGZpbGU6IGN1cnJlbnRTdWl0ZT8uZmlsZSA/PyBjb2xsZWN0b3JDb250ZXh0LmN1cnJlbnRTdWl0ZT8uZmlsZSxcblx0XHRcdHNodWZmbGU6IHN1aXRlT3B0aW9ucz8uc2h1ZmZsZSxcblx0XHRcdHRhc2tzOiBbXSxcblx0XHRcdG1ldGE6IHN1aXRlT3B0aW9ucz8ubWV0YSA/PyBPYmplY3QuY3JlYXRlKG51bGwpLFxuXHRcdFx0Y29uY3VycmVudDogc3VpdGVPcHRpb25zPy5jb25jdXJyZW50LFxuXHRcdFx0dGFnczogdW5pcXVlKFsuLi5wYXJlbnRUYXNrPy50YWdzIHx8IFtdLCAuLi5zdWl0ZVRhZ3NdKVxuXHRcdH07XG5cdFx0aWYgKHJ1bm5lciAmJiBpbmNsdWRlTG9jYXRpb24gJiYgcnVubmVyLmNvbmZpZy5pbmNsdWRlVGFza0xvY2F0aW9uKSB7XG5cdFx0XHRjb25zdCBsaW1pdCA9IEVycm9yLnN0YWNrVHJhY2VMaW1pdDtcblx0XHRcdEVycm9yLnN0YWNrVHJhY2VMaW1pdCA9IDE1O1xuXHRcdFx0Y29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoXCJzdGFja3RyYWNlXCIpLnN0YWNrO1xuXHRcdFx0RXJyb3Iuc3RhY2tUcmFjZUxpbWl0ID0gbGltaXQ7XG5cdFx0XHRjb25zdCBzdGFjayA9IGZpbmRUZXN0RmlsZVN0YWNrVHJhY2UoY3VycmVudFRlc3RGaWxlcGF0aCwgZXJyb3IpO1xuXHRcdFx0aWYgKHN0YWNrKSB7XG5cdFx0XHRcdHN1aXRlLmxvY2F0aW9uID0ge1xuXHRcdFx0XHRcdGxpbmU6IHN0YWNrLmxpbmUsXG5cdFx0XHRcdFx0Y29sdW1uOiBzdGFjay5jb2x1bW5cblx0XHRcdFx0fTtcblx0XHRcdH1cblx0XHR9XG5cdFx0c2V0SG9va3Moc3VpdGUsIGNyZWF0ZVN1aXRlSG9va3MoKSk7XG5cdH1cblx0ZnVuY3Rpb24gY2xlYXIoKSB7XG5cdFx0dGFza3MubGVuZ3RoID0gMDtcblx0XHRpbml0U3VpdGUoZmFsc2UpO1xuXHR9XG5cdGFzeW5jIGZ1bmN0aW9uIGNvbGxlY3QoZmlsZSkge1xuXHRcdGlmICghZmlsZSkge1xuXHRcdFx0dGhyb3cgbmV3IFR5cGVFcnJvcihcIkZpbGUgaXMgcmVxdWlyZWQgdG8gY29sbGVjdCB0YXNrcy5cIik7XG5cdFx0fVxuXHRcdGlmIChmYWN0b3J5KSB7XG5cdFx0XHRhd2FpdCBydW5XaXRoU3VpdGUoY29sbGVjdG9yLCAoKSA9PiBmYWN0b3J5KHRlc3QpKTtcblx0XHR9XG5cdFx0Y29uc3QgYWxsQ2hpbGRyZW4gPSBbXTtcblx0XHRmb3IgKGNvbnN0IGkgb2YgdGFza3MpIHtcblx0XHRcdGFsbENoaWxkcmVuLnB1c2goaS50eXBlID09PSBcImNvbGxlY3RvclwiID8gYXdhaXQgaS5jb2xsZWN0KGZpbGUpIDogaSk7XG5cdFx0fVxuXHRcdHN1aXRlLnRhc2tzID0gYWxsQ2hpbGRyZW47XG5cdFx0cmV0dXJuIHN1aXRlO1xuXHR9XG5cdGNvbGxlY3RUYXNrKGNvbGxlY3Rvcik7XG5cdHJldHVybiBjb2xsZWN0b3I7XG59XG5mdW5jdGlvbiB3aXRoQXdhaXRBc3luY0Fzc2VydGlvbnMoZm4sIHRhc2spIHtcblx0cmV0dXJuIChhc3luYyAoLi4uYXJncykgPT4ge1xuXHRcdGNvbnN0IGZuUmVzdWx0ID0gYXdhaXQgZm4oLi4uYXJncyk7XG5cdFx0Ly8gc29tZSBhc3luYyBleHBlY3Qgd2lsbCBiZSBhZGRlZCB0byB0aGlzIGFycmF5LCBpbiBjYXNlIHVzZXIgZm9yZ2V0IHRvIGF3YWl0IHRoZW1cblx0XHRpZiAodGFzay5wcm9taXNlcykge1xuXHRcdFx0Y29uc3QgcmVzdWx0ID0gYXdhaXQgUHJvbWlzZS5hbGxTZXR0bGVkKHRhc2sucHJvbWlzZXMpO1xuXHRcdFx0Y29uc3QgZXJyb3JzID0gcmVzdWx0Lm1hcCgocikgPT4gci5zdGF0dXMgPT09IFwicmVqZWN0ZWRcIiA/IHIucmVhc29uIDogdW5kZWZpbmVkKS5maWx0ZXIoQm9vbGVhbik7XG5cdFx0XHRpZiAoZXJyb3JzLmxlbmd0aCkge1xuXHRcdFx0XHR0aHJvdyBlcnJvcnM7XG5cdFx0XHR9XG5cdFx0fVxuXHRcdHJldHVybiBmblJlc3VsdDtcblx0fSk7XG59XG5mdW5jdGlvbiBjcmVhdGVTdWl0ZSgpIHtcblx0ZnVuY3Rpb24gc3VpdGVGbihuYW1lLCBmYWN0b3J5T3JPcHRpb25zLCBvcHRpb25zT3JGYWN0b3J5KSB7XG5cdFx0aWYgKGdldEN1cnJlbnRUZXN0KCkpIHtcblx0XHRcdHRocm93IG5ldyBFcnJvcihcIkNhbGxpbmcgdGhlIHN1aXRlIGZ1bmN0aW9uIGluc2lkZSB0ZXN0IGZ1bmN0aW9uIGlzIG5vdCBhbGxvd2VkLiBJdCBjYW4gYmUgb25seSBjYWxsZWQgYXQgdGhlIHRvcCBsZXZlbCBvciBpbnNpZGUgYW5vdGhlciBzdWl0ZSBmdW5jdGlvbi5cIik7XG5cdFx0fVxuXHRcdGNvbnN0IGN1cnJlbnRTdWl0ZSA9IGNvbGxlY3RvckNvbnRleHQuY3VycmVudFN1aXRlIHx8IGRlZmF1bHRTdWl0ZTtcblx0XHRsZXQgeyBvcHRpb25zLCBoYW5kbGVyOiBmYWN0b3J5IH0gPSBwYXJzZUFyZ3VtZW50cyhmYWN0b3J5T3JPcHRpb25zLCBvcHRpb25zT3JGYWN0b3J5KTtcblx0XHRjb25zdCBpc0NvbmN1cnJlbnRTcGVjaWZpZWQgPSBvcHRpb25zLmNvbmN1cnJlbnQgfHwgdGhpcy5jb25jdXJyZW50IHx8IG9wdGlvbnMuc2VxdWVudGlhbCA9PT0gZmFsc2U7XG5cdFx0Y29uc3QgaXNTZXF1ZW50aWFsU3BlY2lmaWVkID0gb3B0aW9ucy5zZXF1ZW50aWFsIHx8IHRoaXMuc2VxdWVudGlhbCB8fCBvcHRpb25zLmNvbmN1cnJlbnQgPT09IGZhbHNlO1xuXHRcdGNvbnN0IHsgbWV0YTogcGFyZW50TWV0YSwgLi4ucGFyZW50T3B0aW9ucyB9ID0gY3VycmVudFN1aXRlPy5vcHRpb25zIHx8IHt9O1xuXHRcdC8vIGluaGVyaXQgb3B0aW9ucyBmcm9tIGN1cnJlbnQgc3VpdGVcblx0XHRvcHRpb25zID0ge1xuXHRcdFx0Li4ucGFyZW50T3B0aW9ucyxcblx0XHRcdC4uLm9wdGlvbnNcblx0XHR9O1xuXHRcdGNvbnN0IHNodWZmbGUgPSB0aGlzLnNodWZmbGUgPz8gb3B0aW9ucy5zaHVmZmxlID8/IGN1cnJlbnRTdWl0ZT8ub3B0aW9ucz8uc2h1ZmZsZSA/PyBydW5uZXI/LmNvbmZpZy5zZXF1ZW5jZS5zaHVmZmxlO1xuXHRcdGlmIChzaHVmZmxlICE9IG51bGwpIHtcblx0XHRcdG9wdGlvbnMuc2h1ZmZsZSA9IHNodWZmbGU7XG5cdFx0fVxuXHRcdGxldCBtb2RlID0gdGhpcy5vbmx5ID8/IG9wdGlvbnMub25seSA/IFwib25seVwiIDogdGhpcy5za2lwID8/IG9wdGlvbnMuc2tpcCA/IFwic2tpcFwiIDogdGhpcy50b2RvID8/IG9wdGlvbnMudG9kbyA/IFwidG9kb1wiIDogXCJydW5cIjtcblx0XHQvLyBwYXNzZWQgYXMgdGVzdChuYW1lKSwgYXNzdW1lIGl0J3MgYSBcInRvZG9cIlxuXHRcdGlmIChtb2RlID09PSBcInJ1blwiICYmICFmYWN0b3J5KSB7XG5cdFx0XHRtb2RlID0gXCJ0b2RvXCI7XG5cdFx0fVxuXHRcdC8vIGluaGVyaXQgY29uY3VycmVudCAvIHNlcXVlbnRpYWwgZnJvbSBzdWl0ZVxuXHRcdGNvbnN0IGlzQ29uY3VycmVudCA9IGlzQ29uY3VycmVudFNwZWNpZmllZCB8fCBvcHRpb25zLmNvbmN1cnJlbnQgJiYgIWlzU2VxdWVudGlhbFNwZWNpZmllZDtcblx0XHRjb25zdCBpc1NlcXVlbnRpYWwgPSBpc1NlcXVlbnRpYWxTcGVjaWZpZWQgfHwgb3B0aW9ucy5zZXF1ZW50aWFsICYmICFpc0NvbmN1cnJlbnRTcGVjaWZpZWQ7XG5cdFx0aWYgKGlzQ29uY3VycmVudCAhPSBudWxsKSB7XG5cdFx0XHRvcHRpb25zLmNvbmN1cnJlbnQgPSBpc0NvbmN1cnJlbnQgJiYgIWlzU2VxdWVudGlhbDtcblx0XHR9XG5cdFx0aWYgKGlzU2VxdWVudGlhbCAhPSBudWxsKSB7XG5cdFx0XHRvcHRpb25zLnNlcXVlbnRpYWwgPSBpc1NlcXVlbnRpYWwgJiYgIWlzQ29uY3VycmVudDtcblx0XHR9XG5cdFx0aWYgKHBhcmVudE1ldGEpIHtcblx0XHRcdG9wdGlvbnMubWV0YSA9IE9iamVjdC5hc3NpZ24oT2JqZWN0LmNyZWF0ZShudWxsKSwgcGFyZW50TWV0YSwgb3B0aW9ucy5tZXRhKTtcblx0XHR9XG5cdFx0cmV0dXJuIGNyZWF0ZVN1aXRlQ29sbGVjdG9yKGZvcm1hdE5hbWUobmFtZSksIGZhY3RvcnksIG1vZGUsIHRoaXMuZWFjaCwgb3B0aW9ucyk7XG5cdH1cblx0c3VpdGVGbi5lYWNoID0gZnVuY3Rpb24oY2FzZXMsIC4uLmFyZ3MpIHtcblx0XHRjb25zdCBjb250ZXh0ID0gZ2V0Q2hhaW5hYmxlQ29udGV4dCh0aGlzKTtcblx0XHRjb25zdCBzdWl0ZSA9IGNvbnRleHQud2l0aENvbnRleHQoKTtcblx0XHRjb250ZXh0LnNldENvbnRleHQoXCJlYWNoXCIsIHRydWUpO1xuXHRcdGlmIChBcnJheS5pc0FycmF5KGNhc2VzKSAmJiBhcmdzLmxlbmd0aCkge1xuXHRcdFx0Y2FzZXMgPSBmb3JtYXRUZW1wbGF0ZVN0cmluZyhjYXNlcywgYXJncyk7XG5cdFx0fVxuXHRcdHJldHVybiAobmFtZSwgb3B0aW9uc09yRm4sIGZuT3JPcHRpb25zKSA9PiB7XG5cdFx0XHRjb25zdCBfbmFtZSA9IGZvcm1hdE5hbWUobmFtZSk7XG5cdFx0XHRjb25zdCBhcnJheU9ubHlDYXNlcyA9IGNhc2VzLmV2ZXJ5KEFycmF5LmlzQXJyYXkpO1xuXHRcdFx0Y29uc3QgeyBvcHRpb25zLCBoYW5kbGVyIH0gPSBwYXJzZUFyZ3VtZW50cyhvcHRpb25zT3JGbiwgZm5Pck9wdGlvbnMpO1xuXHRcdFx0Y29uc3QgZm5GaXJzdCA9IHR5cGVvZiBvcHRpb25zT3JGbiA9PT0gXCJmdW5jdGlvblwiO1xuXHRcdFx0Y2FzZXMuZm9yRWFjaCgoaSwgaWR4KSA9PiB7XG5cdFx0XHRcdGNvbnN0IGl0ZW1zID0gQXJyYXkuaXNBcnJheShpKSA/IGkgOiBbaV07XG5cdFx0XHRcdGlmIChmbkZpcnN0KSB7XG5cdFx0XHRcdFx0aWYgKGFycmF5T25seUNhc2VzKSB7XG5cdFx0XHRcdFx0XHRzdWl0ZShmb3JtYXRUaXRsZShfbmFtZSwgaXRlbXMsIGlkeCksIGhhbmRsZXIgPyAoKSA9PiBoYW5kbGVyKC4uLml0ZW1zKSA6IHVuZGVmaW5lZCwgb3B0aW9ucy50aW1lb3V0KTtcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0c3VpdGUoZm9ybWF0VGl0bGUoX25hbWUsIGl0ZW1zLCBpZHgpLCBoYW5kbGVyID8gKCkgPT4gaGFuZGxlcihpKSA6IHVuZGVmaW5lZCwgb3B0aW9ucy50aW1lb3V0KTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0aWYgKGFycmF5T25seUNhc2VzKSB7XG5cdFx0XHRcdFx0XHRzdWl0ZShmb3JtYXRUaXRsZShfbmFtZSwgaXRlbXMsIGlkeCksIG9wdGlvbnMsIGhhbmRsZXIgPyAoKSA9PiBoYW5kbGVyKC4uLml0ZW1zKSA6IHVuZGVmaW5lZCk7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdHN1aXRlKGZvcm1hdFRpdGxlKF9uYW1lLCBpdGVtcywgaWR4KSwgb3B0aW9ucywgaGFuZGxlciA/ICgpID0+IGhhbmRsZXIoaSkgOiB1bmRlZmluZWQpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0fSk7XG5cdFx0XHRjb250ZXh0LnNldENvbnRleHQoXCJlYWNoXCIsIHVuZGVmaW5lZCk7XG5cdFx0fTtcblx0fTtcblx0c3VpdGVGbi5mb3IgPSBmdW5jdGlvbihjYXNlcywgLi4uYXJncykge1xuXHRcdGlmIChBcnJheS5pc0FycmF5KGNhc2VzKSAmJiBhcmdzLmxlbmd0aCkge1xuXHRcdFx0Y2FzZXMgPSBmb3JtYXRUZW1wbGF0ZVN0cmluZyhjYXNlcywgYXJncyk7XG5cdFx0fVxuXHRcdHJldHVybiAobmFtZSwgb3B0aW9uc09yRm4sIGZuT3JPcHRpb25zKSA9PiB7XG5cdFx0XHRjb25zdCBuYW1lXyA9IGZvcm1hdE5hbWUobmFtZSk7XG5cdFx0XHRjb25zdCB7IG9wdGlvbnMsIGhhbmRsZXIgfSA9IHBhcnNlQXJndW1lbnRzKG9wdGlvbnNPckZuLCBmbk9yT3B0aW9ucyk7XG5cdFx0XHRjYXNlcy5mb3JFYWNoKChpdGVtLCBpZHgpID0+IHtcblx0XHRcdFx0c3VpdGUoZm9ybWF0VGl0bGUobmFtZV8sIHRvQXJyYXkoaXRlbSksIGlkeCksIG9wdGlvbnMsIGhhbmRsZXIgPyAoKSA9PiBoYW5kbGVyKGl0ZW0pIDogdW5kZWZpbmVkKTtcblx0XHRcdH0pO1xuXHRcdH07XG5cdH07XG5cdHN1aXRlRm4uc2tpcElmID0gKGNvbmRpdGlvbikgPT4gY29uZGl0aW9uID8gc3VpdGUuc2tpcCA6IHN1aXRlO1xuXHRzdWl0ZUZuLnJ1bklmID0gKGNvbmRpdGlvbikgPT4gY29uZGl0aW9uID8gc3VpdGUgOiBzdWl0ZS5za2lwO1xuXHRyZXR1cm4gY3JlYXRlQ2hhaW5hYmxlKFtcblx0XHRcImNvbmN1cnJlbnRcIixcblx0XHRcInNlcXVlbnRpYWxcIixcblx0XHRcInNodWZmbGVcIixcblx0XHRcInNraXBcIixcblx0XHRcIm9ubHlcIixcblx0XHRcInRvZG9cIlxuXHRdLCBzdWl0ZUZuKTtcbn1cbmZ1bmN0aW9uIGNyZWF0ZVRhc2tDb2xsZWN0b3IoZm4pIHtcblx0Y29uc3QgdGFza0ZuID0gZm47XG5cdHRhc2tGbi5lYWNoID0gZnVuY3Rpb24oY2FzZXMsIC4uLmFyZ3MpIHtcblx0XHRjb25zdCBjb250ZXh0ID0gZ2V0Q2hhaW5hYmxlQ29udGV4dCh0aGlzKTtcblx0XHRjb25zdCB0ZXN0ID0gY29udGV4dC53aXRoQ29udGV4dCgpO1xuXHRcdGNvbnRleHQuc2V0Q29udGV4dChcImVhY2hcIiwgdHJ1ZSk7XG5cdFx0aWYgKEFycmF5LmlzQXJyYXkoY2FzZXMpICYmIGFyZ3MubGVuZ3RoKSB7XG5cdFx0XHRjYXNlcyA9IGZvcm1hdFRlbXBsYXRlU3RyaW5nKGNhc2VzLCBhcmdzKTtcblx0XHR9XG5cdFx0cmV0dXJuIChuYW1lLCBvcHRpb25zT3JGbiwgZm5Pck9wdGlvbnMpID0+IHtcblx0XHRcdGNvbnN0IF9uYW1lID0gZm9ybWF0TmFtZShuYW1lKTtcblx0XHRcdGNvbnN0IGFycmF5T25seUNhc2VzID0gY2FzZXMuZXZlcnkoQXJyYXkuaXNBcnJheSk7XG5cdFx0XHRjb25zdCB7IG9wdGlvbnMsIGhhbmRsZXIgfSA9IHBhcnNlQXJndW1lbnRzKG9wdGlvbnNPckZuLCBmbk9yT3B0aW9ucyk7XG5cdFx0XHRjb25zdCBmbkZpcnN0ID0gdHlwZW9mIG9wdGlvbnNPckZuID09PSBcImZ1bmN0aW9uXCI7XG5cdFx0XHRjYXNlcy5mb3JFYWNoKChpLCBpZHgpID0+IHtcblx0XHRcdFx0Y29uc3QgaXRlbXMgPSBBcnJheS5pc0FycmF5KGkpID8gaSA6IFtpXTtcblx0XHRcdFx0aWYgKGZuRmlyc3QpIHtcblx0XHRcdFx0XHRpZiAoYXJyYXlPbmx5Q2FzZXMpIHtcblx0XHRcdFx0XHRcdHRlc3QoZm9ybWF0VGl0bGUoX25hbWUsIGl0ZW1zLCBpZHgpLCBoYW5kbGVyID8gKCkgPT4gaGFuZGxlciguLi5pdGVtcykgOiB1bmRlZmluZWQsIG9wdGlvbnMudGltZW91dCk7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdHRlc3QoZm9ybWF0VGl0bGUoX25hbWUsIGl0ZW1zLCBpZHgpLCBoYW5kbGVyID8gKCkgPT4gaGFuZGxlcihpKSA6IHVuZGVmaW5lZCwgb3B0aW9ucy50aW1lb3V0KTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0aWYgKGFycmF5T25seUNhc2VzKSB7XG5cdFx0XHRcdFx0XHR0ZXN0KGZvcm1hdFRpdGxlKF9uYW1lLCBpdGVtcywgaWR4KSwgb3B0aW9ucywgaGFuZGxlciA/ICgpID0+IGhhbmRsZXIoLi4uaXRlbXMpIDogdW5kZWZpbmVkKTtcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0dGVzdChmb3JtYXRUaXRsZShfbmFtZSwgaXRlbXMsIGlkeCksIG9wdGlvbnMsIGhhbmRsZXIgPyAoKSA9PiBoYW5kbGVyKGkpIDogdW5kZWZpbmVkKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH0pO1xuXHRcdFx0Y29udGV4dC5zZXRDb250ZXh0KFwiZWFjaFwiLCB1bmRlZmluZWQpO1xuXHRcdH07XG5cdH07XG5cdHRhc2tGbi5mb3IgPSBmdW5jdGlvbihjYXNlcywgLi4uYXJncykge1xuXHRcdGNvbnN0IGNvbnRleHQgPSBnZXRDaGFpbmFibGVDb250ZXh0KHRoaXMpO1xuXHRcdGNvbnN0IHRlc3QgPSBjb250ZXh0LndpdGhDb250ZXh0KCk7XG5cdFx0aWYgKEFycmF5LmlzQXJyYXkoY2FzZXMpICYmIGFyZ3MubGVuZ3RoKSB7XG5cdFx0XHRjYXNlcyA9IGZvcm1hdFRlbXBsYXRlU3RyaW5nKGNhc2VzLCBhcmdzKTtcblx0XHR9XG5cdFx0cmV0dXJuIChuYW1lLCBvcHRpb25zT3JGbiwgZm5Pck9wdGlvbnMpID0+IHtcblx0XHRcdGNvbnN0IF9uYW1lID0gZm9ybWF0TmFtZShuYW1lKTtcblx0XHRcdGNvbnN0IHsgb3B0aW9ucywgaGFuZGxlciB9ID0gcGFyc2VBcmd1bWVudHMob3B0aW9uc09yRm4sIGZuT3JPcHRpb25zKTtcblx0XHRcdGNhc2VzLmZvckVhY2goKGl0ZW0sIGlkeCkgPT4ge1xuXHRcdFx0XHQvLyBtb25rZXktcGF0Y2ggaGFuZGxlciB0byBhbGxvdyBwYXJzaW5nIGZpeHR1cmVcblx0XHRcdFx0Y29uc3QgaGFuZGxlcldyYXBwZXIgPSBoYW5kbGVyID8gKGN0eCkgPT4gaGFuZGxlcihpdGVtLCBjdHgpIDogdW5kZWZpbmVkO1xuXHRcdFx0XHRpZiAoaGFuZGxlcldyYXBwZXIpIHtcblx0XHRcdFx0XHRjb25maWd1cmVQcm9wcyhoYW5kbGVyV3JhcHBlciwge1xuXHRcdFx0XHRcdFx0aW5kZXg6IDEsXG5cdFx0XHRcdFx0XHRvcmlnaW5hbDogaGFuZGxlclxuXHRcdFx0XHRcdH0pO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHRlc3QoZm9ybWF0VGl0bGUoX25hbWUsIHRvQXJyYXkoaXRlbSksIGlkeCksIG9wdGlvbnMsIGhhbmRsZXJXcmFwcGVyKTtcblx0XHRcdH0pO1xuXHRcdH07XG5cdH07XG5cdHRhc2tGbi5za2lwSWYgPSBmdW5jdGlvbihjb25kaXRpb24pIHtcblx0XHRyZXR1cm4gY29uZGl0aW9uID8gdGhpcy5za2lwIDogdGhpcztcblx0fTtcblx0dGFza0ZuLnJ1bklmID0gZnVuY3Rpb24oY29uZGl0aW9uKSB7XG5cdFx0cmV0dXJuIGNvbmRpdGlvbiA/IHRoaXMgOiB0aGlzLnNraXA7XG5cdH07XG5cdC8qKlxuXHQqIFBhcnNlIGJ1aWxkZXIgcGF0dGVybiBhcmd1bWVudHMgaW50byBhIGZpeHR1cmVzIG9iamVjdC5cblx0KiBIYW5kbGVzIGJvdGggYnVpbGRlciBwYXR0ZXJuIChuYW1lLCBvcHRpb25zPywgdmFsdWUpIGFuZCBvYmplY3Qgc3ludGF4LlxuXHQqL1xuXHRmdW5jdGlvbiBwYXJzZUJ1aWxkZXJGaXh0dXJlcyhmaXh0dXJlc09yTmFtZSwgb3B0aW9uc09yRm4sIG1heWJlRm4pIHtcblx0XHQvLyBPYmplY3Qgc3ludGF4OiBqdXN0IHJldHVybiBhcy1pc1xuXHRcdGlmICh0eXBlb2YgZml4dHVyZXNPck5hbWUgIT09IFwic3RyaW5nXCIpIHtcblx0XHRcdHJldHVybiBmaXh0dXJlc09yTmFtZTtcblx0XHR9XG5cdFx0Y29uc3QgZml4dHVyZU5hbWUgPSBmaXh0dXJlc09yTmFtZTtcblx0XHRsZXQgZml4dHVyZU9wdGlvbnM7XG5cdFx0bGV0IGZpeHR1cmVWYWx1ZTtcblx0XHRpZiAobWF5YmVGbiAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0XHQvLyAobmFtZSwgb3B0aW9ucywgdmFsdWUpIG9yIChuYW1lLCBvcHRpb25zLCBmbilcblx0XHRcdGZpeHR1cmVPcHRpb25zID0gb3B0aW9uc09yRm47XG5cdFx0XHRmaXh0dXJlVmFsdWUgPSBtYXliZUZuO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHQvLyAobmFtZSwgdmFsdWUpIG9yIChuYW1lLCBmbilcblx0XHRcdC8vIENoZWNrIGlmIG9wdGlvbnNPckZuIGxvb2tzIGxpa2UgZml4dHVyZSBvcHRpb25zIChoYXMgc2NvcGUgb3IgYXV0bylcblx0XHRcdGlmIChvcHRpb25zT3JGbiAhPT0gbnVsbCAmJiB0eXBlb2Ygb3B0aW9uc09yRm4gPT09IFwib2JqZWN0XCIgJiYgIUFycmF5LmlzQXJyYXkob3B0aW9uc09yRm4pICYmIFRlc3RGaXh0dXJlcy5pc0ZpeHR1cmVPcHRpb25zKG9wdGlvbnNPckZuKSkge1xuXHRcdFx0XHQvLyAobmFtZSwgb3B0aW9ucykgd2l0aCBubyB2YWx1ZSAtIHRyZWF0IGFzIGVtcHR5IG9iamVjdCBmaXh0dXJlXG5cdFx0XHRcdGZpeHR1cmVPcHRpb25zID0gb3B0aW9uc09yRm47XG5cdFx0XHRcdGZpeHR1cmVWYWx1ZSA9IHt9O1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Ly8gKG5hbWUsIHZhbHVlKSBvciAobmFtZSwgZm4pXG5cdFx0XHRcdGZpeHR1cmVPcHRpb25zID0gdW5kZWZpbmVkO1xuXHRcdFx0XHRmaXh0dXJlVmFsdWUgPSBvcHRpb25zT3JGbjtcblx0XHRcdH1cblx0XHR9XG5cdFx0Ly8gRnVuY3Rpb24gdmFsdWU6IHdyYXAgd2l0aCBvbkNsZWFudXAgcGF0dGVyblxuXHRcdGlmICh0eXBlb2YgZml4dHVyZVZhbHVlID09PSBcImZ1bmN0aW9uXCIpIHtcblx0XHRcdGNvbnN0IGJ1aWxkZXJGbiA9IGZpeHR1cmVWYWx1ZTtcblx0XHRcdC8vIFdyYXAgYnVpbGRlciBwYXR0ZXJuIGZ1bmN0aW9uIChyZXR1cm5zIHZhbHVlKSB0byB1c2UoKSBwYXR0ZXJuXG5cdFx0XHRjb25zdCBmaXh0dXJlID0gYXN5bmMgKGN0eCwgdXNlKSA9PiB7XG5cdFx0XHRcdGxldCBjbGVhbnVwO1xuXHRcdFx0XHRjb25zdCBvbkNsZWFudXAgPSAoZm4pID0+IHtcblx0XHRcdFx0XHRpZiAoY2xlYW51cCAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0XHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoYG9uQ2xlYW51cCBjYW4gb25seSBiZSBjYWxsZWQgb25jZSBwZXIgZml4dHVyZS4gYCArIGBEZWZpbmUgc2VwYXJhdGUgZml4dHVyZXMgaWYgeW91IG5lZWQgbXVsdGlwbGUgY2xlYW51cCBmdW5jdGlvbnMuYCk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGNsZWFudXAgPSBmbjtcblx0XHRcdFx0fTtcblx0XHRcdFx0Y29uc3QgdmFsdWUgPSBhd2FpdCBidWlsZGVyRm4oY3R4LCB7IG9uQ2xlYW51cCB9KTtcblx0XHRcdFx0YXdhaXQgdXNlKHZhbHVlKTtcblx0XHRcdFx0aWYgKGNsZWFudXApIHtcblx0XHRcdFx0XHRhd2FpdCBjbGVhbnVwKCk7XG5cdFx0XHRcdH1cblx0XHRcdH07XG5cdFx0XHRjb25maWd1cmVQcm9wcyhmaXh0dXJlLCB7IG9yaWdpbmFsOiBidWlsZGVyRm4gfSk7XG5cdFx0XHRpZiAoZml4dHVyZU9wdGlvbnMpIHtcblx0XHRcdFx0cmV0dXJuIHsgW2ZpeHR1cmVOYW1lXTogW2ZpeHR1cmUsIGZpeHR1cmVPcHRpb25zXSB9O1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIHsgW2ZpeHR1cmVOYW1lXTogZml4dHVyZSB9O1xuXHRcdH1cblx0XHQvLyBOb24tZnVuY3Rpb24gdmFsdWU6IHVzZSBkaXJlY3RseVxuXHRcdGlmIChmaXh0dXJlT3B0aW9ucykge1xuXHRcdFx0cmV0dXJuIHsgW2ZpeHR1cmVOYW1lXTogW2ZpeHR1cmVWYWx1ZSwgZml4dHVyZU9wdGlvbnNdIH07XG5cdFx0fVxuXHRcdHJldHVybiB7IFtmaXh0dXJlTmFtZV06IGZpeHR1cmVWYWx1ZSB9O1xuXHR9XG5cdHRhc2tGbi5vdmVycmlkZSA9IGZ1bmN0aW9uKGZpeHR1cmVzT3JOYW1lLCBvcHRpb25zT3JGbiwgbWF5YmVGbikge1xuXHRcdGNvbnN0IHVzZXJGaXh0dXJlcyA9IHBhcnNlQnVpbGRlckZpeHR1cmVzKGZpeHR1cmVzT3JOYW1lLCBvcHRpb25zT3JGbiwgbWF5YmVGbik7XG5cdFx0Z2V0Q2hhaW5hYmxlQ29udGV4dCh0aGlzKS5nZXRGaXh0dXJlcygpLm92ZXJyaWRlKHJ1bm5lciwgdXNlckZpeHR1cmVzKTtcblx0XHRyZXR1cm4gdGhpcztcblx0fTtcblx0dGFza0ZuLnNjb3BlZCA9IGZ1bmN0aW9uKGZpeHR1cmVzKSB7XG5cdFx0Y29uc29sZS53YXJuKGB0ZXN0LnNjb3BlZCgpIGlzIGRlcHJlY2F0ZWQgYW5kIHdpbGwgYmUgcmVtb3ZlZCBpbiBmdXR1cmUgdmVyc2lvbnMuIFBsZWFzZSB1c2UgdGVzdC5vdmVycmlkZSgpIGluc3RlYWQuYCk7XG5cdFx0cmV0dXJuIHRoaXMub3ZlcnJpZGUoZml4dHVyZXMpO1xuXHR9O1xuXHR0YXNrRm4uZXh0ZW5kID0gZnVuY3Rpb24oZml4dHVyZXNPck5hbWUsIG9wdGlvbnNPckZuLCBtYXliZUZuKSB7XG5cdFx0Y29uc3QgdXNlckZpeHR1cmVzID0gcGFyc2VCdWlsZGVyRml4dHVyZXMoZml4dHVyZXNPck5hbWUsIG9wdGlvbnNPckZuLCBtYXliZUZuKTtcblx0XHRjb25zdCBmaXh0dXJlcyA9IGdldENoYWluYWJsZUNvbnRleHQodGhpcykuZ2V0Rml4dHVyZXMoKS5leHRlbmQocnVubmVyLCB1c2VyRml4dHVyZXMpO1xuXHRcdGNvbnN0IF90ZXN0ID0gY3JlYXRlVGVzdChmdW5jdGlvbihuYW1lLCBvcHRpb25zT3JGbiwgb3B0aW9uc09yVGVzdCkge1xuXHRcdFx0Zm4uY2FsbCh0aGlzLCBmb3JtYXROYW1lKG5hbWUpLCBvcHRpb25zT3JGbiwgb3B0aW9uc09yVGVzdCk7XG5cdFx0fSk7XG5cdFx0Z2V0Q2hhaW5hYmxlQ29udGV4dChfdGVzdCkubWVyZ2VDb250ZXh0KHsgZml4dHVyZXMgfSk7XG5cdFx0cmV0dXJuIF90ZXN0O1xuXHR9O1xuXHR0YXNrRm4uZGVzY3JpYmUgPSBzdWl0ZTtcblx0dGFza0ZuLnN1aXRlID0gc3VpdGU7XG5cdHRhc2tGbi5iZWZvcmVFYWNoID0gYmVmb3JlRWFjaDtcblx0dGFza0ZuLmFmdGVyRWFjaCA9IGFmdGVyRWFjaDtcblx0dGFza0ZuLmJlZm9yZUFsbCA9IGJlZm9yZUFsbDtcblx0dGFza0ZuLmFmdGVyQWxsID0gYWZ0ZXJBbGw7XG5cdHRhc2tGbi5hcm91bmRFYWNoID0gYXJvdW5kRWFjaDtcblx0dGFza0ZuLmFyb3VuZEFsbCA9IGFyb3VuZEFsbDtcblx0Y29uc3QgX3Rlc3QgPSBjcmVhdGVDaGFpbmFibGUoW1xuXHRcdFwiY29uY3VycmVudFwiLFxuXHRcdFwic2VxdWVudGlhbFwiLFxuXHRcdFwic2tpcFwiLFxuXHRcdFwib25seVwiLFxuXHRcdFwidG9kb1wiLFxuXHRcdFwiZmFpbHNcIlxuXHRdLCB0YXNrRm4sIHsgZml4dHVyZXM6IG5ldyBUZXN0Rml4dHVyZXMoKSB9KTtcblx0cmV0dXJuIF90ZXN0O1xufVxuZnVuY3Rpb24gY3JlYXRlVGVzdChmbikge1xuXHRyZXR1cm4gY3JlYXRlVGFza0NvbGxlY3Rvcihmbik7XG59XG5mdW5jdGlvbiBmb3JtYXROYW1lKG5hbWUpIHtcblx0cmV0dXJuIHR5cGVvZiBuYW1lID09PSBcInN0cmluZ1wiID8gbmFtZSA6IHR5cGVvZiBuYW1lID09PSBcImZ1bmN0aW9uXCIgPyBuYW1lLm5hbWUgfHwgXCI8YW5vbnltb3VzPlwiIDogU3RyaW5nKG5hbWUpO1xufVxuZnVuY3Rpb24gZm9ybWF0VGl0bGUodGVtcGxhdGUsIGl0ZW1zLCBpZHgpIHtcblx0aWYgKHRlbXBsYXRlLmluY2x1ZGVzKFwiJSNcIikgfHwgdGVtcGxhdGUuaW5jbHVkZXMoXCIlJFwiKSkge1xuXHRcdC8vICclIycgbWF0Y2ggaW5kZXggb2YgdGhlIHRlc3QgY2FzZVxuXHRcdHRlbXBsYXRlID0gdGVtcGxhdGUucmVwbGFjZSgvJSUvZywgXCJfX3ZpdGVzdF9lc2NhcGVkXyVfX1wiKS5yZXBsYWNlKC8lIy9nLCBgJHtpZHh9YCkucmVwbGFjZSgvJVxcJC9nLCBgJHtpZHggKyAxfWApLnJlcGxhY2UoL19fdml0ZXN0X2VzY2FwZWRfJV9fL2csIFwiJSVcIik7XG5cdH1cblx0Y29uc3QgY291bnQgPSB0ZW1wbGF0ZS5zcGxpdChcIiVcIikubGVuZ3RoIC0gMTtcblx0aWYgKHRlbXBsYXRlLmluY2x1ZGVzKFwiJWZcIikpIHtcblx0XHRjb25zdCBwbGFjZWhvbGRlcnMgPSB0ZW1wbGF0ZS5tYXRjaCgvJWYvZykgfHwgW107XG5cdFx0cGxhY2Vob2xkZXJzLmZvckVhY2goKF8sIGkpID0+IHtcblx0XHRcdGlmIChpc05lZ2F0aXZlTmFOKGl0ZW1zW2ldKSB8fCBPYmplY3QuaXMoaXRlbXNbaV0sIC0wKSkge1xuXHRcdFx0XHQvLyBSZXBsYWNlIHRoZSBpLXRoIG9jY3VycmVuY2Ugb2YgJyVmJyB3aXRoICctJWYnXG5cdFx0XHRcdGxldCBvY2N1cnJlbmNlID0gMDtcblx0XHRcdFx0dGVtcGxhdGUgPSB0ZW1wbGF0ZS5yZXBsYWNlKC8lZi9nLCAobWF0Y2gpID0+IHtcblx0XHRcdFx0XHRvY2N1cnJlbmNlKys7XG5cdFx0XHRcdFx0cmV0dXJuIG9jY3VycmVuY2UgPT09IGkgKyAxID8gXCItJWZcIiA6IG1hdGNoO1xuXHRcdFx0XHR9KTtcblx0XHRcdH1cblx0XHR9KTtcblx0fVxuXHRjb25zdCBpc09iamVjdEl0ZW0gPSBpc09iamVjdChpdGVtc1swXSk7XG5cdGZ1bmN0aW9uIGZvcm1hdEF0dHJpYnV0ZShzKSB7XG5cdFx0cmV0dXJuIHMucmVwbGFjZSgvXFwkKFskXFx3Ll0rKS9nLCAoXywga2V5KSA9PiB7XG5cdFx0XHRjb25zdCBpc0FycmF5S2V5ID0gL15cXGQrJC8udGVzdChrZXkpO1xuXHRcdFx0aWYgKCFpc09iamVjdEl0ZW0gJiYgIWlzQXJyYXlLZXkpIHtcblx0XHRcdFx0cmV0dXJuIGAkJHtrZXl9YDtcblx0XHRcdH1cblx0XHRcdGNvbnN0IGFycmF5RWxlbWVudCA9IGlzQXJyYXlLZXkgPyBvYmplY3RBdHRyKGl0ZW1zLCBrZXkpIDogdW5kZWZpbmVkO1xuXHRcdFx0Y29uc3QgdmFsdWUgPSBpc09iamVjdEl0ZW0gPyBvYmplY3RBdHRyKGl0ZW1zWzBdLCBrZXksIGFycmF5RWxlbWVudCkgOiBhcnJheUVsZW1lbnQ7XG5cdFx0XHRyZXR1cm4gb2JqRGlzcGxheSh2YWx1ZSwgeyB0cnVuY2F0ZTogcnVubmVyPy5jb25maWc/LmNoYWlDb25maWc/LnRydW5jYXRlVGhyZXNob2xkIH0pO1xuXHRcdH0pO1xuXHR9XG5cdGxldCBvdXRwdXQgPSBcIlwiO1xuXHRsZXQgaSA9IDA7XG5cdGhhbmRsZVJlZ2V4TWF0Y2goXG5cdFx0dGVtcGxhdGUsXG5cdFx0Zm9ybWF0UmVnRXhwLFxuXHRcdC8vIGZvcm1hdCBcIiVcIlxuXHRcdChtYXRjaCkgPT4ge1xuXHRcdFx0aWYgKGkgPCBjb3VudCkge1xuXHRcdFx0XHRvdXRwdXQgKz0gZm9ybWF0KG1hdGNoWzBdLCBpdGVtc1tpKytdKTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdG91dHB1dCArPSBtYXRjaFswXTtcblx0XHRcdH1cblx0XHR9LFxuXHRcdC8vIGZvcm1hdCBcIiRcIlxuXHRcdChub25NYXRjaCkgPT4ge1xuXHRcdFx0b3V0cHV0ICs9IGZvcm1hdEF0dHJpYnV0ZShub25NYXRjaCk7XG5cdFx0fVxuXHQpO1xuXHRyZXR1cm4gb3V0cHV0O1xufVxuLy8gYmFzZWQgb24gaHR0cHM6Ly9naXRodWIuY29tL3Vub2Nzcy91bm9jc3MvYmxvYi8yZTc0YjMxNjI1YmJlM2I5YzgzNTE1NzA3NDlhYTJkM2Y3OTlkOTE5L3BhY2thZ2VzL2F1dG9jb21wbGV0ZS9zcmMvcGFyc2UudHMjTDExXG5mdW5jdGlvbiBoYW5kbGVSZWdleE1hdGNoKGlucHV0LCByZWdleCwgb25NYXRjaCwgb25Ob25NYXRjaCkge1xuXHRsZXQgbGFzdEluZGV4ID0gMDtcblx0Zm9yIChjb25zdCBtIG9mIGlucHV0Lm1hdGNoQWxsKHJlZ2V4KSkge1xuXHRcdGlmIChsYXN0SW5kZXggPCBtLmluZGV4KSB7XG5cdFx0XHRvbk5vbk1hdGNoKGlucHV0LnNsaWNlKGxhc3RJbmRleCwgbS5pbmRleCkpO1xuXHRcdH1cblx0XHRvbk1hdGNoKG0pO1xuXHRcdGxhc3RJbmRleCA9IG0uaW5kZXggKyBtWzBdLmxlbmd0aDtcblx0fVxuXHRpZiAobGFzdEluZGV4IDwgaW5wdXQubGVuZ3RoKSB7XG5cdFx0b25Ob25NYXRjaChpbnB1dC5zbGljZShsYXN0SW5kZXgpKTtcblx0fVxufVxuZnVuY3Rpb24gZm9ybWF0VGVtcGxhdGVTdHJpbmcoY2FzZXMsIGFyZ3MpIHtcblx0Y29uc3QgaGVhZGVyID0gY2FzZXMuam9pbihcIlwiKS50cmltKCkucmVwbGFjZSgvIC9nLCBcIlwiKS5zcGxpdChcIlxcblwiKS5tYXAoKGkpID0+IGkuc3BsaXQoXCJ8XCIpKVswXTtcblx0Y29uc3QgcmVzID0gW107XG5cdGZvciAobGV0IGkgPSAwOyBpIDwgTWF0aC5mbG9vcihhcmdzLmxlbmd0aCAvIGhlYWRlci5sZW5ndGgpOyBpKyspIHtcblx0XHRjb25zdCBvbmVDYXNlID0ge307XG5cdFx0Zm9yIChsZXQgaiA9IDA7IGogPCBoZWFkZXIubGVuZ3RoOyBqKyspIHtcblx0XHRcdG9uZUNhc2VbaGVhZGVyW2pdXSA9IGFyZ3NbaSAqIGhlYWRlci5sZW5ndGggKyBqXTtcblx0XHR9XG5cdFx0cmVzLnB1c2gob25lQ2FzZSk7XG5cdH1cblx0cmV0dXJuIHJlcztcbn1cblxuY29uc3Qgbm93JDIgPSBnbG9iYWxUaGlzLnBlcmZvcm1hbmNlID8gZ2xvYmFsVGhpcy5wZXJmb3JtYW5jZS5ub3cuYmluZChnbG9iYWxUaGlzLnBlcmZvcm1hbmNlKSA6IERhdGUubm93O1xuY29uc3QgY29sbGVjdG9yQ29udGV4dCA9IHtcblx0dGFza3M6IFtdLFxuXHRjdXJyZW50U3VpdGU6IG51bGxcbn07XG5mdW5jdGlvbiBjb2xsZWN0VGFzayh0YXNrKSB7XG5cdGNvbGxlY3RvckNvbnRleHQuY3VycmVudFN1aXRlPy50YXNrcy5wdXNoKHRhc2spO1xufVxuYXN5bmMgZnVuY3Rpb24gcnVuV2l0aFN1aXRlKHN1aXRlLCBmbikge1xuXHRjb25zdCBwcmV2ID0gY29sbGVjdG9yQ29udGV4dC5jdXJyZW50U3VpdGU7XG5cdGNvbGxlY3RvckNvbnRleHQuY3VycmVudFN1aXRlID0gc3VpdGU7XG5cdGF3YWl0IGZuKCk7XG5cdGNvbGxlY3RvckNvbnRleHQuY3VycmVudFN1aXRlID0gcHJldjtcbn1cbmZ1bmN0aW9uIHdpdGhUaW1lb3V0KGZuLCB0aW1lb3V0LCBpc0hvb2sgPSBmYWxzZSwgc3RhY2tUcmFjZUVycm9yLCBvblRpbWVvdXQpIHtcblx0aWYgKHRpbWVvdXQgPD0gMCB8fCB0aW1lb3V0ID09PSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFkpIHtcblx0XHRyZXR1cm4gZm47XG5cdH1cblx0Y29uc3QgeyBzZXRUaW1lb3V0LCBjbGVhclRpbWVvdXQgfSA9IGdldFNhZmVUaW1lcnMoKTtcblx0Ly8gdGhpcyBmdW5jdGlvbiBuYW1lIGlzIHVzZWQgdG8gZmlsdGVyIGVycm9yIGluIHRlc3QvY2xpL3Rlc3QvZmFpbHMudGVzdC50c1xuXHRyZXR1cm4gKGZ1bmN0aW9uIHJ1bldpdGhUaW1lb3V0KC4uLmFyZ3MpIHtcblx0XHRjb25zdCBzdGFydFRpbWUgPSBub3ckMigpO1xuXHRcdGNvbnN0IHJ1bm5lciA9IGdldFJ1bm5lcigpO1xuXHRcdHJ1bm5lci5fY3VycmVudFRhc2tTdGFydFRpbWUgPSBzdGFydFRpbWU7XG5cdFx0cnVubmVyLl9jdXJyZW50VGFza1RpbWVvdXQgPSB0aW1lb3V0O1xuXHRcdHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZV8sIHJlamVjdF8pID0+IHtcblx0XHRcdGNvbnN0IHRpbWVyID0gc2V0VGltZW91dCgoKSA9PiB7XG5cdFx0XHRcdGNsZWFyVGltZW91dCh0aW1lcik7XG5cdFx0XHRcdHJlamVjdFRpbWVvdXRFcnJvcigpO1xuXHRcdFx0fSwgdGltZW91dCk7XG5cdFx0XHQvLyBgdW5yZWZgIG1pZ2h0IG5vdCBleGlzdCBpbiBicm93c2VyXG5cdFx0XHR0aW1lci51bnJlZj8uKCk7XG5cdFx0XHRmdW5jdGlvbiByZWplY3RUaW1lb3V0RXJyb3IoKSB7XG5cdFx0XHRcdGNvbnN0IGVycm9yID0gbWFrZVRpbWVvdXRFcnJvcihpc0hvb2ssIHRpbWVvdXQsIHN0YWNrVHJhY2VFcnJvcik7XG5cdFx0XHRcdG9uVGltZW91dD8uKGFyZ3MsIGVycm9yKTtcblx0XHRcdFx0cmVqZWN0XyhlcnJvcik7XG5cdFx0XHR9XG5cdFx0XHRmdW5jdGlvbiByZXNvbHZlKHJlc3VsdCkge1xuXHRcdFx0XHRydW5uZXIuX2N1cnJlbnRUYXNrU3RhcnRUaW1lID0gdW5kZWZpbmVkO1xuXHRcdFx0XHRydW5uZXIuX2N1cnJlbnRUYXNrVGltZW91dCA9IHVuZGVmaW5lZDtcblx0XHRcdFx0Y2xlYXJUaW1lb3V0KHRpbWVyKTtcblx0XHRcdFx0Ly8gaWYgdGVzdC9ob29rIHRvb2sgdG9vIGxvbmcgaW4gbWljcm90YXNrLCBzZXRUaW1lb3V0IHdvbid0IGJlIHRyaWdnZXJlZCxcblx0XHRcdFx0Ly8gYnV0IHdlIHN0aWxsIG5lZWQgdG8gZmFpbCB0aGUgdGVzdCwgc2VlXG5cdFx0XHRcdC8vIGh0dHBzOi8vZ2l0aHViLmNvbS92aXRlc3QtZGV2L3ZpdGVzdC9pc3N1ZXMvMjkyMFxuXHRcdFx0XHRpZiAobm93JDIoKSAtIHN0YXJ0VGltZSA+PSB0aW1lb3V0KSB7XG5cdFx0XHRcdFx0cmVqZWN0VGltZW91dEVycm9yKCk7XG5cdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHJlc29sdmVfKHJlc3VsdCk7XG5cdFx0XHR9XG5cdFx0XHRmdW5jdGlvbiByZWplY3QoZXJyb3IpIHtcblx0XHRcdFx0cnVubmVyLl9jdXJyZW50VGFza1N0YXJ0VGltZSA9IHVuZGVmaW5lZDtcblx0XHRcdFx0cnVubmVyLl9jdXJyZW50VGFza1RpbWVvdXQgPSB1bmRlZmluZWQ7XG5cdFx0XHRcdGNsZWFyVGltZW91dCh0aW1lcik7XG5cdFx0XHRcdHJlamVjdF8oZXJyb3IpO1xuXHRcdFx0fVxuXHRcdFx0Ly8gc3luYyB0ZXN0L2hvb2sgd2lsbCBiZSBjYXVnaHQgYnkgdHJ5L2NhdGNoXG5cdFx0XHR0cnkge1xuXHRcdFx0XHRjb25zdCByZXN1bHQgPSBmbiguLi5hcmdzKTtcblx0XHRcdFx0Ly8gdGhlIHJlc3VsdCBpcyBhIHRoZW5hYmxlLCB3ZSBkb24ndCB3cmFwIHRoaXMgaW4gUHJvbWlzZS5yZXNvbHZlXG5cdFx0XHRcdC8vIHRvIGF2b2lkIGNyZWF0aW5nIG5ldyBwcm9taXNlc1xuXHRcdFx0XHRpZiAodHlwZW9mIHJlc3VsdCA9PT0gXCJvYmplY3RcIiAmJiByZXN1bHQgIT0gbnVsbCAmJiB0eXBlb2YgcmVzdWx0LnRoZW4gPT09IFwiZnVuY3Rpb25cIikge1xuXHRcdFx0XHRcdHJlc3VsdC50aGVuKHJlc29sdmUsIHJlamVjdCk7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0cmVzb2x2ZShyZXN1bHQpO1xuXHRcdFx0XHR9XG5cdFx0XHR9IFxuXHRcdFx0Ly8gdXNlciBzeW5jIHRlc3QvaG9vayB0aHJvd3MgYW4gZXJyb3JcbmNhdGNoIChlcnJvcikge1xuXHRcdFx0XHRyZWplY3QoZXJyb3IpO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9KTtcbn1cbmZ1bmN0aW9uIHdpdGhDYW5jZWwoZm4sIHNpZ25hbCkge1xuXHRyZXR1cm4gKGZ1bmN0aW9uIHJ1bldpdGhDYW5jZWwoLi4uYXJncykge1xuXHRcdHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG5cdFx0XHRzaWduYWwuYWRkRXZlbnRMaXN0ZW5lcihcImFib3J0XCIsICgpID0+IHJlamVjdChzaWduYWwucmVhc29uKSk7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRjb25zdCByZXN1bHQgPSBmbiguLi5hcmdzKTtcblx0XHRcdFx0aWYgKHR5cGVvZiByZXN1bHQgPT09IFwib2JqZWN0XCIgJiYgcmVzdWx0ICE9IG51bGwgJiYgdHlwZW9mIHJlc3VsdC50aGVuID09PSBcImZ1bmN0aW9uXCIpIHtcblx0XHRcdFx0XHRyZXN1bHQudGhlbihyZXNvbHZlLCByZWplY3QpO1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdHJlc29sdmUocmVzdWx0KTtcblx0XHRcdFx0fVxuXHRcdFx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHRcdFx0cmVqZWN0KGVycm9yKTtcblx0XHRcdH1cblx0XHR9KTtcblx0fSk7XG59XG5jb25zdCBhYm9ydENvbnRyb2xsZXJzID0gbmV3IFdlYWtNYXAoKTtcbmZ1bmN0aW9uIGFib3J0SWZUaW1lb3V0KFtjb250ZXh0XSwgZXJyb3IpIHtcblx0aWYgKGNvbnRleHQpIHtcblx0XHRhYm9ydENvbnRleHRTaWduYWwoY29udGV4dCwgZXJyb3IpO1xuXHR9XG59XG5mdW5jdGlvbiBhYm9ydENvbnRleHRTaWduYWwoY29udGV4dCwgZXJyb3IpIHtcblx0Y29uc3QgYWJvcnRDb250cm9sbGVyID0gYWJvcnRDb250cm9sbGVycy5nZXQoY29udGV4dCk7XG5cdGFib3J0Q29udHJvbGxlcj8uYWJvcnQoZXJyb3IpO1xufVxuZnVuY3Rpb24gY3JlYXRlVGVzdENvbnRleHQodGVzdCwgcnVubmVyKSB7XG5cdGNvbnN0IGNvbnRleHQgPSBmdW5jdGlvbigpIHtcblx0XHR0aHJvdyBuZXcgRXJyb3IoXCJkb25lKCkgY2FsbGJhY2sgaXMgZGVwcmVjYXRlZCwgdXNlIHByb21pc2UgaW5zdGVhZFwiKTtcblx0fTtcblx0bGV0IGFib3J0Q29udHJvbGxlciA9IGFib3J0Q29udHJvbGxlcnMuZ2V0KGNvbnRleHQpO1xuXHRpZiAoIWFib3J0Q29udHJvbGxlcikge1xuXHRcdGFib3J0Q29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcblx0XHRhYm9ydENvbnRyb2xsZXJzLnNldChjb250ZXh0LCBhYm9ydENvbnRyb2xsZXIpO1xuXHR9XG5cdGNvbnRleHQuc2lnbmFsID0gYWJvcnRDb250cm9sbGVyLnNpZ25hbDtcblx0Y29udGV4dC50YXNrID0gdGVzdDtcblx0Y29udGV4dC5za2lwID0gKGNvbmRpdGlvbiwgbm90ZSkgPT4ge1xuXHRcdGlmIChjb25kaXRpb24gPT09IGZhbHNlKSB7XG5cdFx0XHQvLyBkbyBub3RoaW5nXG5cdFx0XHRyZXR1cm4gdW5kZWZpbmVkO1xuXHRcdH1cblx0XHR0ZXN0LnJlc3VsdCA/Pz0geyBzdGF0ZTogXCJza2lwXCIgfTtcblx0XHR0ZXN0LnJlc3VsdC5wZW5kaW5nID0gdHJ1ZTtcblx0XHR0aHJvdyBuZXcgUGVuZGluZ0Vycm9yKFwidGVzdCBpcyBza2lwcGVkOyBhYm9ydCBleGVjdXRpb25cIiwgdGVzdCwgdHlwZW9mIGNvbmRpdGlvbiA9PT0gXCJzdHJpbmdcIiA/IGNvbmRpdGlvbiA6IG5vdGUpO1xuXHR9O1xuXHRjb250ZXh0LmFubm90YXRlID0gKChtZXNzYWdlLCB0eXBlLCBhdHRhY2htZW50KSA9PiB7XG5cdFx0aWYgKHRlc3QucmVzdWx0ICYmIHRlc3QucmVzdWx0LnN0YXRlICE9PSBcInJ1blwiKSB7XG5cdFx0XHR0aHJvdyBuZXcgRXJyb3IoYENhbm5vdCBhbm5vdGF0ZSB0ZXN0cyBvdXRzaWRlIG9mIHRoZSB0ZXN0IHJ1bi4gVGhlIHRlc3QgXCIke3Rlc3QubmFtZX1cIiBmaW5pc2hlZCBydW5uaW5nIHdpdGggdGhlIFwiJHt0ZXN0LnJlc3VsdC5zdGF0ZX1cIiBzdGF0ZSBhbHJlYWR5LmApO1xuXHRcdH1cblx0XHRjb25zdCBhbm5vdGF0aW9uID0ge1xuXHRcdFx0bWVzc2FnZSxcblx0XHRcdHR5cGU6IHR5cGVvZiB0eXBlID09PSBcIm9iamVjdFwiIHx8IHR5cGUgPT09IHVuZGVmaW5lZCA/IFwibm90aWNlXCIgOiB0eXBlXG5cdFx0fTtcblx0XHRjb25zdCBhbm5vdGF0aW9uQXR0YWNobWVudCA9IHR5cGVvZiB0eXBlID09PSBcIm9iamVjdFwiID8gdHlwZSA6IGF0dGFjaG1lbnQ7XG5cdFx0aWYgKGFubm90YXRpb25BdHRhY2htZW50KSB7XG5cdFx0XHRhbm5vdGF0aW9uLmF0dGFjaG1lbnQgPSBhbm5vdGF0aW9uQXR0YWNobWVudDtcblx0XHRcdG1hbmFnZUFydGlmYWN0QXR0YWNobWVudChhbm5vdGF0aW9uLmF0dGFjaG1lbnQpO1xuXHRcdH1cblx0XHRyZXR1cm4gcmVjb3JkQXN5bmNPcGVyYXRpb24odGVzdCwgcmVjb3JkQXJ0aWZhY3QodGVzdCwge1xuXHRcdFx0dHlwZTogXCJpbnRlcm5hbDphbm5vdGF0aW9uXCIsXG5cdFx0XHRhbm5vdGF0aW9uXG5cdFx0fSkudGhlbihhc3luYyAoeyBhbm5vdGF0aW9uIH0pID0+IHtcblx0XHRcdGlmICghcnVubmVyLm9uVGVzdEFubm90YXRlKSB7XG5cdFx0XHRcdHRocm93IG5ldyBFcnJvcihgVGVzdCBydW5uZXIgZG9lc24ndCBzdXBwb3J0IHRlc3QgYW5ub3RhdGlvbnMuYCk7XG5cdFx0XHR9XG5cdFx0XHRhd2FpdCBmaW5pc2hTZW5kVGFza3NVcGRhdGUocnVubmVyKTtcblx0XHRcdGNvbnN0IHJlc29sdmVkQW5ub3RhdGlvbiA9IGF3YWl0IHJ1bm5lci5vblRlc3RBbm5vdGF0ZSh0ZXN0LCBhbm5vdGF0aW9uKTtcblx0XHRcdHRlc3QuYW5ub3RhdGlvbnMucHVzaChyZXNvbHZlZEFubm90YXRpb24pO1xuXHRcdFx0cmV0dXJuIHJlc29sdmVkQW5ub3RhdGlvbjtcblx0XHR9KSk7XG5cdH0pO1xuXHRjb250ZXh0Lm9uVGVzdEZhaWxlZCA9IChoYW5kbGVyLCB0aW1lb3V0KSA9PiB7XG5cdFx0dGVzdC5vbkZhaWxlZCB8fD0gW107XG5cdFx0dGVzdC5vbkZhaWxlZC5wdXNoKHdpdGhUaW1lb3V0KGhhbmRsZXIsIHRpbWVvdXQgPz8gcnVubmVyLmNvbmZpZy5ob29rVGltZW91dCwgdHJ1ZSwgbmV3IEVycm9yKFwiU1RBQ0tfVFJBQ0VfRVJST1JcIiksIChfLCBlcnJvcikgPT4gYWJvcnRDb250cm9sbGVyLmFib3J0KGVycm9yKSkpO1xuXHR9O1xuXHRjb250ZXh0Lm9uVGVzdEZpbmlzaGVkID0gKGhhbmRsZXIsIHRpbWVvdXQpID0+IHtcblx0XHR0ZXN0Lm9uRmluaXNoZWQgfHw9IFtdO1xuXHRcdHRlc3Qub25GaW5pc2hlZC5wdXNoKHdpdGhUaW1lb3V0KGhhbmRsZXIsIHRpbWVvdXQgPz8gcnVubmVyLmNvbmZpZy5ob29rVGltZW91dCwgdHJ1ZSwgbmV3IEVycm9yKFwiU1RBQ0tfVFJBQ0VfRVJST1JcIiksIChfLCBlcnJvcikgPT4gYWJvcnRDb250cm9sbGVyLmFib3J0KGVycm9yKSkpO1xuXHR9O1xuXHRyZXR1cm4gcnVubmVyLmV4dGVuZFRhc2tDb250ZXh0Py4oY29udGV4dCkgfHwgY29udGV4dDtcbn1cbmZ1bmN0aW9uIG1ha2VUaW1lb3V0RXJyb3IoaXNIb29rLCB0aW1lb3V0LCBzdGFja1RyYWNlRXJyb3IpIHtcblx0Y29uc3QgbWVzc2FnZSA9IGAke2lzSG9vayA/IFwiSG9va1wiIDogXCJUZXN0XCJ9IHRpbWVkIG91dCBpbiAke3RpbWVvdXR9bXMuXFxuSWYgdGhpcyBpcyBhIGxvbmctcnVubmluZyAke2lzSG9vayA/IFwiaG9va1wiIDogXCJ0ZXN0XCJ9LCBwYXNzIGEgdGltZW91dCB2YWx1ZSBhcyB0aGUgbGFzdCBhcmd1bWVudCBvciBjb25maWd1cmUgaXQgZ2xvYmFsbHkgd2l0aCBcIiR7aXNIb29rID8gXCJob29rVGltZW91dFwiIDogXCJ0ZXN0VGltZW91dFwifVwiLmA7XG5cdGNvbnN0IGVycm9yID0gbmV3IEVycm9yKG1lc3NhZ2UpO1xuXHRpZiAoc3RhY2tUcmFjZUVycm9yPy5zdGFjaykge1xuXHRcdGVycm9yLnN0YWNrID0gc3RhY2tUcmFjZUVycm9yLnN0YWNrLnJlcGxhY2UoZXJyb3IubWVzc2FnZSwgc3RhY2tUcmFjZUVycm9yLm1lc3NhZ2UpO1xuXHR9XG5cdHJldHVybiBlcnJvcjtcbn1cblxuYXN5bmMgZnVuY3Rpb24gcnVuU2V0dXBGaWxlcyhjb25maWcsIGZpbGVzLCBydW5uZXIpIHtcblx0aWYgKGNvbmZpZy5zZXF1ZW5jZS5zZXR1cEZpbGVzID09PSBcInBhcmFsbGVsXCIpIHtcblx0XHRhd2FpdCBQcm9taXNlLmFsbChmaWxlcy5tYXAoYXN5bmMgKGZzUGF0aCkgPT4ge1xuXHRcdFx0YXdhaXQgcnVubmVyLmltcG9ydEZpbGUoZnNQYXRoLCBcInNldHVwXCIpO1xuXHRcdH0pKTtcblx0fSBlbHNlIHtcblx0XHRmb3IgKGNvbnN0IGZzUGF0aCBvZiBmaWxlcykge1xuXHRcdFx0YXdhaXQgcnVubmVyLmltcG9ydEZpbGUoZnNQYXRoLCBcInNldHVwXCIpO1xuXHRcdH1cblx0fVxufVxuXG5jb25zdCBub3ckMSA9IGdsb2JhbFRoaXMucGVyZm9ybWFuY2UgPyBnbG9iYWxUaGlzLnBlcmZvcm1hbmNlLm5vdy5iaW5kKGdsb2JhbFRoaXMucGVyZm9ybWFuY2UpIDogRGF0ZS5ub3c7XG5hc3luYyBmdW5jdGlvbiBjb2xsZWN0VGVzdHMoc3BlY3MsIHJ1bm5lcikge1xuXHRjb25zdCBmaWxlcyA9IFtdO1xuXHRjb25zdCBjb25maWcgPSBydW5uZXIuY29uZmlnO1xuXHRjb25zdCAkID0gcnVubmVyLnRyYWNlO1xuXHRsZXQgZGVmYXVsdFRhZ3NGaWx0ZXI7XG5cdGZvciAoY29uc3Qgc3BlYyBvZiBzcGVjcykge1xuXHRcdGNvbnN0IGZpbGVwYXRoID0gdHlwZW9mIHNwZWMgPT09IFwic3RyaW5nXCIgPyBzcGVjIDogc3BlYy5maWxlcGF0aDtcblx0XHRhd2FpdCAkKFwiY29sbGVjdF9zcGVjXCIsIHsgXCJjb2RlLmZpbGUucGF0aFwiOiBmaWxlcGF0aCB9LCBhc3luYyAoKSA9PiB7XG5cdFx0XHRydW5uZXIuX2N1cnJlbnRTcGVjaWZpY2F0aW9uID0gdHlwZW9mIHNwZWMgPT09IFwic3RyaW5nXCIgPyB7IGZpbGVwYXRoOiBzcGVjIH0gOiBzcGVjO1xuXHRcdFx0Y29uc3QgdGVzdExvY2F0aW9ucyA9IHR5cGVvZiBzcGVjID09PSBcInN0cmluZ1wiID8gdW5kZWZpbmVkIDogc3BlYy50ZXN0TG9jYXRpb25zO1xuXHRcdFx0Y29uc3QgdGVzdE5hbWVQYXR0ZXJuID0gdHlwZW9mIHNwZWMgPT09IFwic3RyaW5nXCIgPyB1bmRlZmluZWQgOiBzcGVjLnRlc3ROYW1lUGF0dGVybjtcblx0XHRcdGNvbnN0IHRlc3RJZHMgPSB0eXBlb2Ygc3BlYyA9PT0gXCJzdHJpbmdcIiA/IHVuZGVmaW5lZCA6IHNwZWMudGVzdElkcztcblx0XHRcdGNvbnN0IHRlc3RUYWdzRmlsdGVyID0gdHlwZW9mIHNwZWMgPT09IFwib2JqZWN0XCIgJiYgc3BlYy50ZXN0VGFnc0ZpbHRlciA/IGNyZWF0ZVRhZ3NGaWx0ZXIoc3BlYy50ZXN0VGFnc0ZpbHRlciwgY29uZmlnLnRhZ3MpIDogdW5kZWZpbmVkO1xuXHRcdFx0Y29uc3QgZmlsZVRhZ3MgPSB0eXBlb2Ygc3BlYyA9PT0gXCJzdHJpbmdcIiA/IFtdIDogc3BlYy5maWxlVGFncyB8fCBbXTtcblx0XHRcdGNvbnN0IGZpbGUgPSBjcmVhdGVGaWxlVGFzayhmaWxlcGF0aCwgY29uZmlnLnJvb3QsIGNvbmZpZy5uYW1lLCBydW5uZXIucG9vbCwgcnVubmVyLnZpdGVFbnZpcm9ubWVudCk7XG5cdFx0XHRmaWxlLnRhZ3MgPSBmaWxlVGFncztcblx0XHRcdGZpbGUuc2h1ZmZsZSA9IGNvbmZpZy5zZXF1ZW5jZS5zaHVmZmxlO1xuXHRcdFx0dHJ5IHtcblx0XHRcdFx0dmFsaWRhdGVUYWdzKHJ1bm5lci5jb25maWcsIGZpbGVUYWdzKTtcblx0XHRcdFx0cnVubmVyLm9uQ29sbGVjdFN0YXJ0Py4oZmlsZSk7XG5cdFx0XHRcdGNsZWFyQ29sbGVjdG9yQ29udGV4dChmaWxlLCBydW5uZXIpO1xuXHRcdFx0XHRjb25zdCBzZXR1cEZpbGVzID0gdG9BcnJheShjb25maWcuc2V0dXBGaWxlcyk7XG5cdFx0XHRcdGlmIChzZXR1cEZpbGVzLmxlbmd0aCkge1xuXHRcdFx0XHRcdGNvbnN0IHNldHVwU3RhcnQgPSBub3ckMSgpO1xuXHRcdFx0XHRcdGF3YWl0IHJ1blNldHVwRmlsZXMoY29uZmlnLCBzZXR1cEZpbGVzLCBydW5uZXIpO1xuXHRcdFx0XHRcdGNvbnN0IHNldHVwRW5kID0gbm93JDEoKTtcblx0XHRcdFx0XHRmaWxlLnNldHVwRHVyYXRpb24gPSBzZXR1cEVuZCAtIHNldHVwU3RhcnQ7XG5cdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0ZmlsZS5zZXR1cER1cmF0aW9uID0gMDtcblx0XHRcdFx0fVxuXHRcdFx0XHRjb25zdCBjb2xsZWN0U3RhcnQgPSBub3ckMSgpO1xuXHRcdFx0XHRhd2FpdCBydW5uZXIuaW1wb3J0RmlsZShmaWxlcGF0aCwgXCJjb2xsZWN0XCIpO1xuXHRcdFx0XHRjb25zdCBkdXJhdGlvbnMgPSBydW5uZXIuZ2V0SW1wb3J0RHVyYXRpb25zPy4oKTtcblx0XHRcdFx0aWYgKGR1cmF0aW9ucykge1xuXHRcdFx0XHRcdGZpbGUuaW1wb3J0RHVyYXRpb25zID0gZHVyYXRpb25zO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGNvbnN0IGRlZmF1bHRUYXNrcyA9IGF3YWl0IGdldERlZmF1bHRTdWl0ZSgpLmNvbGxlY3QoZmlsZSk7XG5cdFx0XHRcdGNvbnN0IGZpbGVIb29rcyA9IGNyZWF0ZVN1aXRlSG9va3MoKTtcblx0XHRcdFx0bWVyZ2VIb29rcyhmaWxlSG9va3MsIGdldEhvb2tzKGRlZmF1bHRUYXNrcykpO1xuXHRcdFx0XHRmb3IgKGNvbnN0IGMgb2YgWy4uLmRlZmF1bHRUYXNrcy50YXNrcywgLi4uY29sbGVjdG9yQ29udGV4dC50YXNrc10pIHtcblx0XHRcdFx0XHRpZiAoYy50eXBlID09PSBcInRlc3RcIiB8fCBjLnR5cGUgPT09IFwic3VpdGVcIikge1xuXHRcdFx0XHRcdFx0ZmlsZS50YXNrcy5wdXNoKGMpO1xuXHRcdFx0XHRcdH0gZWxzZSBpZiAoYy50eXBlID09PSBcImNvbGxlY3RvclwiKSB7XG5cdFx0XHRcdFx0XHRjb25zdCBzdWl0ZSA9IGF3YWl0IGMuY29sbGVjdChmaWxlKTtcblx0XHRcdFx0XHRcdGlmIChzdWl0ZS5uYW1lIHx8IHN1aXRlLnRhc2tzLmxlbmd0aCkge1xuXHRcdFx0XHRcdFx0XHRtZXJnZUhvb2tzKGZpbGVIb29rcywgZ2V0SG9va3Moc3VpdGUpKTtcblx0XHRcdFx0XHRcdFx0ZmlsZS50YXNrcy5wdXNoKHN1aXRlKTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0Ly8gY2hlY2sgdGhhdCB0eXBlcyBhcmUgZXhoYXVzdGVkXG5cdFx0XHRcdFx0XHRjO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXHRcdFx0XHRzZXRIb29rcyhmaWxlLCBmaWxlSG9va3MpO1xuXHRcdFx0XHRmaWxlLmNvbGxlY3REdXJhdGlvbiA9IG5vdyQxKCkgLSBjb2xsZWN0U3RhcnQ7XG5cdFx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRcdGNvbnN0IGVycm9ycyA9IGUgaW5zdGFuY2VvZiBBZ2dyZWdhdGVFcnJvciA/IGUuZXJyb3JzLm1hcCgoZSkgPT4gcHJvY2Vzc0Vycm9yKGUsIHJ1bm5lci5jb25maWcuZGlmZk9wdGlvbnMpKSA6IFtwcm9jZXNzRXJyb3IoZSwgcnVubmVyLmNvbmZpZy5kaWZmT3B0aW9ucyldO1xuXHRcdFx0XHRmaWxlLnJlc3VsdCA9IHtcblx0XHRcdFx0XHRzdGF0ZTogXCJmYWlsXCIsXG5cdFx0XHRcdFx0ZXJyb3JzXG5cdFx0XHRcdH07XG5cdFx0XHRcdGNvbnN0IGR1cmF0aW9ucyA9IHJ1bm5lci5nZXRJbXBvcnREdXJhdGlvbnM/LigpO1xuXHRcdFx0XHRpZiAoZHVyYXRpb25zKSB7XG5cdFx0XHRcdFx0ZmlsZS5pbXBvcnREdXJhdGlvbnMgPSBkdXJhdGlvbnM7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHRcdGNhbGN1bGF0ZVN1aXRlSGFzaChmaWxlKTtcblx0XHRcdGNvbnN0IGhhc09ubHlUYXNrcyA9IHNvbWVUYXNrc0FyZU9ubHkoZmlsZSk7XG5cdFx0XHRpZiAoIXRlc3RUYWdzRmlsdGVyICYmICFkZWZhdWx0VGFnc0ZpbHRlciAmJiBjb25maWcudGFnc0ZpbHRlcikge1xuXHRcdFx0XHRkZWZhdWx0VGFnc0ZpbHRlciA9IGNyZWF0ZVRhZ3NGaWx0ZXIoY29uZmlnLnRhZ3NGaWx0ZXIsIGNvbmZpZy50YWdzKTtcblx0XHRcdH1cblx0XHRcdGludGVycHJldFRhc2tNb2RlcyhmaWxlLCB0ZXN0TmFtZVBhdHRlcm4gPz8gY29uZmlnLnRlc3ROYW1lUGF0dGVybiwgdGVzdExvY2F0aW9ucywgdGVzdElkcywgdGVzdFRhZ3NGaWx0ZXIgPz8gZGVmYXVsdFRhZ3NGaWx0ZXIsIGhhc09ubHlUYXNrcywgZmFsc2UsIGNvbmZpZy5hbGxvd09ubHkpO1xuXHRcdFx0aWYgKGZpbGUubW9kZSA9PT0gXCJxdWV1ZWRcIikge1xuXHRcdFx0XHRmaWxlLm1vZGUgPSBcInJ1blwiO1xuXHRcdFx0fVxuXHRcdFx0ZmlsZXMucHVzaChmaWxlKTtcblx0XHR9KTtcblx0fVxuXHRyZXR1cm4gZmlsZXM7XG59XG5mdW5jdGlvbiBtZXJnZUhvb2tzKGJhc2VIb29rcywgaG9va3MpIHtcblx0Zm9yIChjb25zdCBfa2V5IGluIGhvb2tzKSB7XG5cdFx0Y29uc3Qga2V5ID0gX2tleTtcblx0XHRiYXNlSG9va3Nba2V5XS5wdXNoKC4uLmhvb2tzW2tleV0pO1xuXHR9XG5cdHJldHVybiBiYXNlSG9va3M7XG59XG5cbmNvbnN0IG5vdyA9IGdsb2JhbFRoaXMucGVyZm9ybWFuY2UgPyBnbG9iYWxUaGlzLnBlcmZvcm1hbmNlLm5vdy5iaW5kKGdsb2JhbFRoaXMucGVyZm9ybWFuY2UpIDogRGF0ZS5ub3c7XG5jb25zdCB1bml4Tm93ID0gRGF0ZS5ub3c7XG5jb25zdCB7IGNsZWFyVGltZW91dCwgc2V0VGltZW91dCB9ID0gZ2V0U2FmZVRpbWVycygpO1xubGV0IGxpbWl0TWF4Q29uY3VycmVuY3k7XG4vKipcbiogTm9ybWFsaXplcyByZXRyeSBjb25maWd1cmF0aW9uIHRvIGV4dHJhY3QgaW5kaXZpZHVhbCB2YWx1ZXMuXG4qIEhhbmRsZXMgYm90aCBudW1iZXIgYW5kIG9iamVjdCBmb3Jtcy5cbiovXG5mdW5jdGlvbiBnZXRSZXRyeUNvdW50KHJldHJ5KSB7XG5cdGlmIChyZXRyeSA9PT0gdW5kZWZpbmVkKSB7XG5cdFx0cmV0dXJuIDA7XG5cdH1cblx0aWYgKHR5cGVvZiByZXRyeSA9PT0gXCJudW1iZXJcIikge1xuXHRcdHJldHVybiByZXRyeTtcblx0fVxuXHRyZXR1cm4gcmV0cnkuY291bnQgPz8gMDtcbn1cbmZ1bmN0aW9uIGdldFJldHJ5RGVsYXkocmV0cnkpIHtcblx0aWYgKHJldHJ5ID09PSB1bmRlZmluZWQpIHtcblx0XHRyZXR1cm4gMDtcblx0fVxuXHRpZiAodHlwZW9mIHJldHJ5ID09PSBcIm51bWJlclwiKSB7XG5cdFx0cmV0dXJuIDA7XG5cdH1cblx0cmV0dXJuIHJldHJ5LmRlbGF5ID8/IDA7XG59XG5mdW5jdGlvbiBnZXRSZXRyeUNvbmRpdGlvbihyZXRyeSkge1xuXHRpZiAocmV0cnkgPT09IHVuZGVmaW5lZCkge1xuXHRcdHJldHVybiB1bmRlZmluZWQ7XG5cdH1cblx0aWYgKHR5cGVvZiByZXRyeSA9PT0gXCJudW1iZXJcIikge1xuXHRcdHJldHVybiB1bmRlZmluZWQ7XG5cdH1cblx0cmV0dXJuIHJldHJ5LmNvbmRpdGlvbjtcbn1cbmZ1bmN0aW9uIHVwZGF0ZVN1aXRlSG9va1N0YXRlKHRhc2ssIG5hbWUsIHN0YXRlLCBydW5uZXIpIHtcblx0aWYgKCF0YXNrLnJlc3VsdCkge1xuXHRcdHRhc2sucmVzdWx0ID0geyBzdGF0ZTogXCJydW5cIiB9O1xuXHR9XG5cdGlmICghdGFzay5yZXN1bHQuaG9va3MpIHtcblx0XHR0YXNrLnJlc3VsdC5ob29rcyA9IHt9O1xuXHR9XG5cdGNvbnN0IHN1aXRlSG9va3MgPSB0YXNrLnJlc3VsdC5ob29rcztcblx0aWYgKHN1aXRlSG9va3MpIHtcblx0XHRzdWl0ZUhvb2tzW25hbWVdID0gc3RhdGU7XG5cdFx0bGV0IGV2ZW50ID0gc3RhdGUgPT09IFwicnVuXCIgPyBcImJlZm9yZS1ob29rLXN0YXJ0XCIgOiBcImJlZm9yZS1ob29rLWVuZFwiO1xuXHRcdGlmIChuYW1lID09PSBcImFmdGVyQWxsXCIgfHwgbmFtZSA9PT0gXCJhZnRlckVhY2hcIikge1xuXHRcdFx0ZXZlbnQgPSBzdGF0ZSA9PT0gXCJydW5cIiA/IFwiYWZ0ZXItaG9vay1zdGFydFwiIDogXCJhZnRlci1ob29rLWVuZFwiO1xuXHRcdH1cblx0XHR1cGRhdGVUYXNrKGV2ZW50LCB0YXNrLCBydW5uZXIpO1xuXHR9XG59XG5mdW5jdGlvbiBnZXRTdWl0ZUhvb2tzKHN1aXRlLCBuYW1lLCBzZXF1ZW5jZSkge1xuXHRjb25zdCBob29rcyA9IGdldEhvb2tzKHN1aXRlKVtuYW1lXTtcblx0aWYgKHNlcXVlbmNlID09PSBcInN0YWNrXCIgJiYgKG5hbWUgPT09IFwiYWZ0ZXJBbGxcIiB8fCBuYW1lID09PSBcImFmdGVyRWFjaFwiKSkge1xuXHRcdHJldHVybiBob29rcy5zbGljZSgpLnJldmVyc2UoKTtcblx0fVxuXHRyZXR1cm4gaG9va3M7XG59XG5hc3luYyBmdW5jdGlvbiBjYWxsVGVzdEhvb2tzKHJ1bm5lciwgdGVzdCwgaG9va3MsIHNlcXVlbmNlKSB7XG5cdGlmIChzZXF1ZW5jZSA9PT0gXCJzdGFja1wiKSB7XG5cdFx0aG9va3MgPSBob29rcy5zbGljZSgpLnJldmVyc2UoKTtcblx0fVxuXHRpZiAoIWhvb2tzLmxlbmd0aCkge1xuXHRcdHJldHVybjtcblx0fVxuXHRjb25zdCBjb250ZXh0ID0gdGVzdC5jb250ZXh0O1xuXHRjb25zdCBvblRlc3RGYWlsZWQgPSB0ZXN0LmNvbnRleHQub25UZXN0RmFpbGVkO1xuXHRjb25zdCBvblRlc3RGaW5pc2hlZCA9IHRlc3QuY29udGV4dC5vblRlc3RGaW5pc2hlZDtcblx0Y29udGV4dC5vblRlc3RGYWlsZWQgPSAoKSA9PiB7XG5cdFx0dGhyb3cgbmV3IEVycm9yKGBDYW5ub3QgY2FsbCBcIm9uVGVzdEZhaWxlZFwiIGluc2lkZSBhIHRlc3QgaG9vay5gKTtcblx0fTtcblx0Y29udGV4dC5vblRlc3RGaW5pc2hlZCA9ICgpID0+IHtcblx0XHR0aHJvdyBuZXcgRXJyb3IoYENhbm5vdCBjYWxsIFwib25UZXN0RmluaXNoZWRcIiBpbnNpZGUgYSB0ZXN0IGhvb2suYCk7XG5cdH07XG5cdGlmIChzZXF1ZW5jZSA9PT0gXCJwYXJhbGxlbFwiKSB7XG5cdFx0dHJ5IHtcblx0XHRcdGF3YWl0IFByb21pc2UuYWxsKGhvb2tzLm1hcCgoZm4pID0+IGxpbWl0TWF4Q29uY3VycmVuY3koKCkgPT4gZm4odGVzdC5jb250ZXh0KSkpKTtcblx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRmYWlsVGFzayh0ZXN0LnJlc3VsdCwgZSwgcnVubmVyLmNvbmZpZy5kaWZmT3B0aW9ucyk7XG5cdFx0fVxuXHR9IGVsc2Uge1xuXHRcdGZvciAoY29uc3QgZm4gb2YgaG9va3MpIHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGF3YWl0IGxpbWl0TWF4Q29uY3VycmVuY3koKCkgPT4gZm4odGVzdC5jb250ZXh0KSk7XG5cdFx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRcdGZhaWxUYXNrKHRlc3QucmVzdWx0LCBlLCBydW5uZXIuY29uZmlnLmRpZmZPcHRpb25zKTtcblx0XHRcdH1cblx0XHR9XG5cdH1cblx0Y29udGV4dC5vblRlc3RGYWlsZWQgPSBvblRlc3RGYWlsZWQ7XG5cdGNvbnRleHQub25UZXN0RmluaXNoZWQgPSBvblRlc3RGaW5pc2hlZDtcbn1cbmFzeW5jIGZ1bmN0aW9uIGNhbGxTdWl0ZUhvb2soc3VpdGUsIGN1cnJlbnRUYXNrLCBuYW1lLCBydW5uZXIsIGFyZ3MpIHtcblx0Y29uc3Qgc2VxdWVuY2UgPSBydW5uZXIuY29uZmlnLnNlcXVlbmNlLmhvb2tzO1xuXHRjb25zdCBjYWxsYmFja3MgPSBbXTtcblx0Ly8gc3RvcCBhdCBmaWxlIGxldmVsXG5cdGNvbnN0IHBhcmVudFN1aXRlID0gXCJmaWxlcGF0aFwiIGluIHN1aXRlID8gbnVsbCA6IHN1aXRlLnN1aXRlIHx8IHN1aXRlLmZpbGU7XG5cdGlmIChuYW1lID09PSBcImJlZm9yZUVhY2hcIiAmJiBwYXJlbnRTdWl0ZSkge1xuXHRcdGNhbGxiYWNrcy5wdXNoKC4uLmF3YWl0IGNhbGxTdWl0ZUhvb2socGFyZW50U3VpdGUsIGN1cnJlbnRUYXNrLCBuYW1lLCBydW5uZXIsIGFyZ3MpKTtcblx0fVxuXHRjb25zdCBob29rcyA9IGdldFN1aXRlSG9va3Moc3VpdGUsIG5hbWUsIHNlcXVlbmNlKTtcblx0aWYgKGhvb2tzLmxlbmd0aCA+IDApIHtcblx0XHR1cGRhdGVTdWl0ZUhvb2tTdGF0ZShjdXJyZW50VGFzaywgbmFtZSwgXCJydW5cIiwgcnVubmVyKTtcblx0fVxuXHRhc3luYyBmdW5jdGlvbiBydW5Ib29rKGhvb2spIHtcblx0XHRyZXR1cm4gbGltaXRNYXhDb25jdXJyZW5jeShhc3luYyAoKSA9PiB7XG5cdFx0XHRyZXR1cm4gZ2V0QmVmb3JlSG9va0NsZWFudXBDYWxsYmFjayhob29rLCBhd2FpdCBob29rKC4uLmFyZ3MpLCBuYW1lID09PSBcImJlZm9yZUVhY2hcIiA/IGFyZ3NbMF0gOiB1bmRlZmluZWQpO1xuXHRcdH0pO1xuXHR9XG5cdGlmIChzZXF1ZW5jZSA9PT0gXCJwYXJhbGxlbFwiKSB7XG5cdFx0Y2FsbGJhY2tzLnB1c2goLi4uYXdhaXQgUHJvbWlzZS5hbGwoaG9va3MubWFwKChob29rKSA9PiBydW5Ib29rKGhvb2spKSkpO1xuXHR9IGVsc2Uge1xuXHRcdGZvciAoY29uc3QgaG9vayBvZiBob29rcykge1xuXHRcdFx0Y2FsbGJhY2tzLnB1c2goYXdhaXQgcnVuSG9vayhob29rKSk7XG5cdFx0fVxuXHR9XG5cdGlmIChob29rcy5sZW5ndGggPiAwKSB7XG5cdFx0dXBkYXRlU3VpdGVIb29rU3RhdGUoY3VycmVudFRhc2ssIG5hbWUsIFwicGFzc1wiLCBydW5uZXIpO1xuXHR9XG5cdGlmIChuYW1lID09PSBcImFmdGVyRWFjaFwiICYmIHBhcmVudFN1aXRlKSB7XG5cdFx0Y2FsbGJhY2tzLnB1c2goLi4uYXdhaXQgY2FsbFN1aXRlSG9vayhwYXJlbnRTdWl0ZSwgY3VycmVudFRhc2ssIG5hbWUsIHJ1bm5lciwgYXJncykpO1xuXHR9XG5cdHJldHVybiBjYWxsYmFja3M7XG59XG5mdW5jdGlvbiBnZXRBcm91bmRFYWNoSG9va3Moc3VpdGUpIHtcblx0Y29uc3QgaG9va3MgPSBbXTtcblx0Y29uc3QgcGFyZW50U3VpdGUgPSBcImZpbGVwYXRoXCIgaW4gc3VpdGUgPyBudWxsIDogc3VpdGUuc3VpdGUgfHwgc3VpdGUuZmlsZTtcblx0aWYgKHBhcmVudFN1aXRlKSB7XG5cdFx0aG9va3MucHVzaCguLi5nZXRBcm91bmRFYWNoSG9va3MocGFyZW50U3VpdGUpKTtcblx0fVxuXHRob29rcy5wdXNoKC4uLmdldEhvb2tzKHN1aXRlKS5hcm91bmRFYWNoKTtcblx0cmV0dXJuIGhvb2tzO1xufVxuZnVuY3Rpb24gZ2V0QXJvdW5kQWxsSG9va3Moc3VpdGUpIHtcblx0cmV0dXJuIGdldEhvb2tzKHN1aXRlKS5hcm91bmRBbGw7XG59XG5mdW5jdGlvbiBtYWtlQXJvdW5kSG9va1RpbWVvdXRFcnJvcihob29rTmFtZSwgcGhhc2UsIHRpbWVvdXQsIHN0YWNrVHJhY2VFcnJvcikge1xuXHRjb25zdCBtZXNzYWdlID0gYFRoZSAke3BoYXNlfSBwaGFzZSBvZiBcIiR7aG9va05hbWV9XCIgaG9vayB0aW1lZCBvdXQgYWZ0ZXIgJHt0aW1lb3V0fW1zLmA7XG5cdGNvbnN0IEVycm9yQ2xhc3MgPSBwaGFzZSA9PT0gXCJzZXR1cFwiID8gQXJvdW5kSG9va1NldHVwRXJyb3IgOiBBcm91bmRIb29rVGVhcmRvd25FcnJvcjtcblx0Y29uc3QgZXJyb3IgPSBuZXcgRXJyb3JDbGFzcyhtZXNzYWdlKTtcblx0aWYgKHN0YWNrVHJhY2VFcnJvcj8uc3RhY2spIHtcblx0XHRlcnJvci5zdGFjayA9IHN0YWNrVHJhY2VFcnJvci5zdGFjay5yZXBsYWNlKHN0YWNrVHJhY2VFcnJvci5tZXNzYWdlLCBlcnJvci5tZXNzYWdlKTtcblx0fVxuXHRyZXR1cm4gZXJyb3I7XG59XG5hc3luYyBmdW5jdGlvbiBjYWxsQXJvdW5kSG9va3MocnVuSW5uZXIsIG9wdGlvbnMpIHtcblx0Y29uc3QgeyBob29rcywgaG9va05hbWUsIGNhbGxiYWNrTmFtZSwgb25UaW1lb3V0LCBpbnZva2VIb29rIH0gPSBvcHRpb25zO1xuXHRpZiAoIWhvb2tzLmxlbmd0aCkge1xuXHRcdGF3YWl0IHJ1bklubmVyKCk7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdGNvbnN0IGhvb2tFcnJvcnMgPSBbXTtcblx0Y29uc3QgY3JlYXRlVGltZW91dFByb21pc2UgPSAodGltZW91dCwgcGhhc2UsIHN0YWNrVHJhY2VFcnJvcikgPT4ge1xuXHRcdGxldCB0aW1lcjtcblx0XHRsZXQgdGltZWRvdXQgPSBmYWxzZTtcblx0XHRjb25zdCBwcm9taXNlID0gbmV3IFByb21pc2UoKF8sIHJlamVjdCkgPT4ge1xuXHRcdFx0aWYgKHRpbWVvdXQgPiAwICYmIHRpbWVvdXQgIT09IE51bWJlci5QT1NJVElWRV9JTkZJTklUWSkge1xuXHRcdFx0XHR0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4ge1xuXHRcdFx0XHRcdHRpbWVkb3V0ID0gdHJ1ZTtcblx0XHRcdFx0XHRjb25zdCBlcnJvciA9IG1ha2VBcm91bmRIb29rVGltZW91dEVycm9yKGhvb2tOYW1lLCBwaGFzZSwgdGltZW91dCwgc3RhY2tUcmFjZUVycm9yKTtcblx0XHRcdFx0XHRvblRpbWVvdXQ/LihlcnJvcik7XG5cdFx0XHRcdFx0cmVqZWN0KGVycm9yKTtcblx0XHRcdFx0fSwgdGltZW91dCk7XG5cdFx0XHRcdHRpbWVyLnVucmVmPy4oKTtcblx0XHRcdH1cblx0XHR9KTtcblx0XHRjb25zdCBjbGVhciA9ICgpID0+IHtcblx0XHRcdGlmICh0aW1lcikge1xuXHRcdFx0XHRjbGVhclRpbWVvdXQodGltZXIpO1xuXHRcdFx0XHR0aW1lciA9IHVuZGVmaW5lZDtcblx0XHRcdH1cblx0XHR9O1xuXHRcdHJldHVybiB7XG5cdFx0XHRwcm9taXNlLFxuXHRcdFx0Y2xlYXIsXG5cdFx0XHRpc1RpbWVkT3V0OiAoKSA9PiB0aW1lZG91dFxuXHRcdH07XG5cdH07XG5cdGNvbnN0IHJ1bk5leHRIb29rID0gYXN5bmMgKGluZGV4KSA9PiB7XG5cdFx0aWYgKGluZGV4ID49IGhvb2tzLmxlbmd0aCkge1xuXHRcdFx0cmV0dXJuIHJ1bklubmVyKCk7XG5cdFx0fVxuXHRcdGNvbnN0IGhvb2sgPSBob29rc1tpbmRleF07XG5cdFx0Y29uc3QgdGltZW91dCA9IGdldEFyb3VuZEhvb2tUaW1lb3V0KGhvb2spO1xuXHRcdGNvbnN0IHN0YWNrVHJhY2VFcnJvciA9IGdldEFyb3VuZEhvb2tTdGFja1RyYWNlKGhvb2spO1xuXHRcdGxldCB1c2VDYWxsZWQgPSBmYWxzZTtcblx0XHRsZXQgc2V0dXBUaW1lb3V0O1xuXHRcdGxldCB0ZWFyZG93blRpbWVvdXQ7XG5cdFx0bGV0IHNldHVwTGltaXRDb25jdXJyZW5jeVJlbGVhc2U7XG5cdFx0bGV0IHRlYXJkb3duTGltaXRDb25jdXJyZW5jeVJlbGVhc2U7XG5cdFx0Ly8gUHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdXNlKCkgaXMgY2FsbGVkIChzZXR1cCBwaGFzZSBjb21wbGV0ZSlcblx0XHRsZXQgcmVzb2x2ZVVzZUNhbGxlZDtcblx0XHRjb25zdCB1c2VDYWxsZWRQcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcblx0XHRcdHJlc29sdmVVc2VDYWxsZWQgPSByZXNvbHZlO1xuXHRcdH0pO1xuXHRcdC8vIFByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHVzZSgpIHJldHVybnMgKGlubmVyIGhvb2tzIGNvbXBsZXRlLCB0ZWFyZG93biBwaGFzZSBzdGFydHMpXG5cdFx0bGV0IHJlc29sdmVVc2VSZXR1cm5lZDtcblx0XHRjb25zdCB1c2VSZXR1cm5lZFByb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuXHRcdFx0cmVzb2x2ZVVzZVJldHVybmVkID0gcmVzb2x2ZTtcblx0XHR9KTtcblx0XHQvLyBQcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiBob29rIGNvbXBsZXRlc1xuXHRcdGxldCByZXNvbHZlSG9va0NvbXBsZXRlO1xuXHRcdGxldCByZWplY3RIb29rQ29tcGxldGU7XG5cdFx0Y29uc3QgaG9va0NvbXBsZXRlUHJvbWlzZSA9IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcblx0XHRcdHJlc29sdmVIb29rQ29tcGxldGUgPSByZXNvbHZlO1xuXHRcdFx0cmVqZWN0SG9va0NvbXBsZXRlID0gcmVqZWN0O1xuXHRcdH0pO1xuXHRcdGNvbnN0IHVzZSA9IGFzeW5jICgpID0+IHtcblx0XHRcdC8vIHNob3VsZG4ndCBjb250aW51ZSB0byBuZXh0IChydW5UZXN0L1N1aXRlIG9yIGlubmVyIGFyb3VuZEVhY2gvQWxsKSB3aGVuIGFyb3VuZEVhY2gvQWxsIHNldHVwIHRpbWVkIG91dC5cblx0XHRcdGlmIChzZXR1cFRpbWVvdXQuaXNUaW1lZE91dCgpKSB7XG5cdFx0XHRcdC8vIHdlIGNhbiB0aHJvdyBhbnkgZXJyb3IgdG8gYmFpbCBvdXQuXG5cdFx0XHRcdC8vIHRoaXMgZXJyb3IgaXMgbm90IHNlZW4gYnkgZW5kIHVzZXJzIHNpbmNlIGBydW5OZXh0SG9va2AgYWxyZWFkeSByZWplY3RlZCB3aXRoIHRpbWVvdXQgZXJyb3Jcblx0XHRcdFx0Ly8gYW5kIHRoaXMgZXJyb3IgaXMgY2F1Z2h0IGJ5IGByZWplY3RIb29rQ29tcGxldGVgLlxuXHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoXCJfX1ZJVEVTVF9JTlRFUk5BTF9BUk9VTkRfSE9PS19BQk9SVF9fXCIpO1xuXHRcdFx0fVxuXHRcdFx0aWYgKHVzZUNhbGxlZCkge1xuXHRcdFx0XHR0aHJvdyBuZXcgQXJvdW5kSG9va011bHRpcGxlQ2FsbHNFcnJvcihgVGhlIFxcYCR7Y2FsbGJhY2tOYW1lfVxcYCBjYWxsYmFjayB3YXMgY2FsbGVkIG11bHRpcGxlIHRpbWVzIGluIHRoZSBcXGAke2hvb2tOYW1lfVxcYCBob29rLiBgICsgYFRoZSBjYWxsYmFjayBjYW4gb25seSBiZSBjYWxsZWQgb25jZSBwZXIgaG9vay5gKTtcblx0XHRcdH1cblx0XHRcdHVzZUNhbGxlZCA9IHRydWU7XG5cdFx0XHRyZXNvbHZlVXNlQ2FsbGVkKCk7XG5cdFx0XHQvLyBTZXR1cCBwaGFzZSBjb21wbGV0ZWQgLSBjbGVhciBzZXR1cCB0aW1lclxuXHRcdFx0c2V0dXBUaW1lb3V0LmNsZWFyKCk7XG5cdFx0XHRzZXR1cExpbWl0Q29uY3VycmVuY3lSZWxlYXNlPy4oKTtcblx0XHRcdC8vIFJ1biBpbm5lciBob29rcyAtIGRvbid0IHRpbWUgdGhpcyBhZ2FpbnN0IG91ciB0ZWFyZG93biB0aW1lb3V0XG5cdFx0XHRhd2FpdCBydW5OZXh0SG9vayhpbmRleCArIDEpLmNhdGNoKChlKSA9PiBob29rRXJyb3JzLnB1c2goZSkpO1xuXHRcdFx0dGVhcmRvd25MaW1pdENvbmN1cnJlbmN5UmVsZWFzZSA9IGF3YWl0IGxpbWl0TWF4Q29uY3VycmVuY3kuYWNxdWlyZSgpO1xuXHRcdFx0Ly8gU3RhcnQgdGVhcmRvd24gdGltZXIgYWZ0ZXIgaW5uZXIgaG9va3MgY29tcGxldGUgLSBvbmx5IHRpbWVzIHRoaXMgaG9vaydzIHRlYXJkb3duIGNvZGVcblx0XHRcdHRlYXJkb3duVGltZW91dCA9IGNyZWF0ZVRpbWVvdXRQcm9taXNlKHRpbWVvdXQsIFwidGVhcmRvd25cIiwgc3RhY2tUcmFjZUVycm9yKTtcblx0XHRcdC8vIFNpZ25hbCB0aGF0IHVzZSgpIGlzIHJldHVybmluZyAodGVhcmRvd24gcGhhc2Ugc3RhcnRpbmcpXG5cdFx0XHRyZXNvbHZlVXNlUmV0dXJuZWQoKTtcblx0XHR9O1xuXHRcdHNldHVwTGltaXRDb25jdXJyZW5jeVJlbGVhc2UgPSBhd2FpdCBsaW1pdE1heENvbmN1cnJlbmN5LmFjcXVpcmUoKTtcblx0XHQvLyBTdGFydCBzZXR1cCB0aW1lb3V0XG5cdFx0c2V0dXBUaW1lb3V0ID0gY3JlYXRlVGltZW91dFByb21pc2UodGltZW91dCwgXCJzZXR1cFwiLCBzdGFja1RyYWNlRXJyb3IpO1xuXHRcdChhc3luYyAoKSA9PiB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRhd2FpdCBpbnZva2VIb29rKGhvb2ssIHVzZSk7XG5cdFx0XHRcdGlmICghdXNlQ2FsbGVkKSB7XG5cdFx0XHRcdFx0dGhyb3cgbmV3IEFyb3VuZEhvb2tTZXR1cEVycm9yKGBUaGUgXFxgJHtjYWxsYmFja05hbWV9XFxgIGNhbGxiYWNrIHdhcyBub3QgY2FsbGVkIGluIHRoZSBcXGAke2hvb2tOYW1lfVxcYCBob29rLiBgICsgYE1ha2Ugc3VyZSB0byBjYWxsIFxcYCR7Y2FsbGJhY2tOYW1lfVxcYCB0byBydW4gdGhlICR7aG9va05hbWUgPT09IFwiYXJvdW5kRWFjaFwiID8gXCJ0ZXN0XCIgOiBcInN1aXRlXCJ9LmApO1xuXHRcdFx0XHR9XG5cdFx0XHRcdHJlc29sdmVIb29rQ29tcGxldGUoKTtcblx0XHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHRcdHJlamVjdEhvb2tDb21wbGV0ZShlcnJvcik7XG5cdFx0XHR9IGZpbmFsbHkge1xuXHRcdFx0XHRzZXR1cExpbWl0Q29uY3VycmVuY3lSZWxlYXNlPy4oKTtcblx0XHRcdFx0dGVhcmRvd25MaW1pdENvbmN1cnJlbmN5UmVsZWFzZT8uKCk7XG5cdFx0XHR9XG5cdFx0fSkoKTtcblx0XHQvLyBXYWl0IGZvciBlaXRoZXI6IHVzZSgpIHRvIGJlIGNhbGxlZCBPUiBob29rIHRvIGNvbXBsZXRlIChlcnJvcikgT1Igc2V0dXAgdGltZW91dFxuXHRcdHRyeSB7XG5cdFx0XHRhd2FpdCBQcm9taXNlLnJhY2UoW1xuXHRcdFx0XHR1c2VDYWxsZWRQcm9taXNlLFxuXHRcdFx0XHRob29rQ29tcGxldGVQcm9taXNlLFxuXHRcdFx0XHRzZXR1cFRpbWVvdXQucHJvbWlzZVxuXHRcdFx0XSk7XG5cdFx0fSBmaW5hbGx5IHtcblx0XHRcdHNldHVwTGltaXRDb25jdXJyZW5jeVJlbGVhc2U/LigpO1xuXHRcdFx0c2V0dXBUaW1lb3V0LmNsZWFyKCk7XG5cdFx0fVxuXHRcdC8vIFdhaXQgZm9yIHVzZSgpIHRvIHJldHVybiAoaW5uZXIgaG9va3MgY29tcGxldGUpIE9SIGhvb2sgdG8gY29tcGxldGUgKGVycm9yIGR1cmluZyBpbm5lciBob29rcylcblx0XHRhd2FpdCBQcm9taXNlLnJhY2UoW3VzZVJldHVybmVkUHJvbWlzZSwgaG9va0NvbXBsZXRlUHJvbWlzZV0pO1xuXHRcdC8vIE5vdyB0ZWFyZG93blRpbWVvdXQgaXMgZ3VhcmFudGVlZCB0byBiZSBzZXRcblx0XHQvLyBXYWl0IGZvciBob29rIHRvIGNvbXBsZXRlICh0ZWFyZG93bikgT1IgdGVhcmRvd24gdGltZW91dFxuXHRcdHRyeSB7XG5cdFx0XHRhd2FpdCBQcm9taXNlLnJhY2UoW2hvb2tDb21wbGV0ZVByb21pc2UsIHRlYXJkb3duVGltZW91dD8ucHJvbWlzZV0pO1xuXHRcdH0gZmluYWxseSB7XG5cdFx0XHR0ZWFyZG93bkxpbWl0Q29uY3VycmVuY3lSZWxlYXNlPy4oKTtcblx0XHRcdHRlYXJkb3duVGltZW91dD8uY2xlYXIoKTtcblx0XHR9XG5cdH07XG5cdGF3YWl0IHJ1bk5leHRIb29rKDApLmNhdGNoKChlKSA9PiBob29rRXJyb3JzLnB1c2goZSkpO1xuXHRpZiAoaG9va0Vycm9ycy5sZW5ndGggPiAwKSB7XG5cdFx0dGhyb3cgaG9va0Vycm9ycztcblx0fVxufVxuYXN5bmMgZnVuY3Rpb24gY2FsbEFyb3VuZEFsbEhvb2tzKHN1aXRlLCBydW5TdWl0ZUlubmVyKSB7XG5cdGF3YWl0IGNhbGxBcm91bmRIb29rcyhydW5TdWl0ZUlubmVyLCB7XG5cdFx0aG9va3M6IGdldEFyb3VuZEFsbEhvb2tzKHN1aXRlKSxcblx0XHRob29rTmFtZTogXCJhcm91bmRBbGxcIixcblx0XHRjYWxsYmFja05hbWU6IFwicnVuU3VpdGUoKVwiLFxuXHRcdGludm9rZUhvb2s6IChob29rLCB1c2UpID0+IGhvb2sodXNlLCBzdWl0ZSlcblx0fSk7XG59XG5hc3luYyBmdW5jdGlvbiBjYWxsQXJvdW5kRWFjaEhvb2tzKHN1aXRlLCB0ZXN0LCBydW5UZXN0KSB7XG5cdGF3YWl0IGNhbGxBcm91bmRIb29rcyhcblx0XHQvLyBUYWtlIGNoZWNrcG9pbnQgcmlnaHQgYmVmb3JlIHJ1blRlc3QgLSBhdCB0aGlzIHBvaW50IGFsbCBhcm91bmRFYWNoIGZpeHR1cmVzXG5cdFx0Ly8gaGF2ZSBiZWVuIHJlc29sdmVkLCBzbyB3ZSBjYW4gY29ycmVjdGx5IGlkZW50aWZ5IHdoaWNoIGZpeHR1cmVzIGJlbG9uZyB0b1xuXHRcdC8vIGFyb3VuZEVhY2ggKGJlZm9yZSBjaGVja3BvaW50KSB2cyBpbnNpZGUgcnVuVGVzdCAoYWZ0ZXIgY2hlY2twb2ludClcblx0XHQoKSA9PiBydW5UZXN0KGdldEZpeHR1cmVDbGVhbnVwQ291bnQodGVzdC5jb250ZXh0KSksXG5cdFx0e1xuXHRcdFx0aG9va3M6IGdldEFyb3VuZEVhY2hIb29rcyhzdWl0ZSksXG5cdFx0XHRob29rTmFtZTogXCJhcm91bmRFYWNoXCIsXG5cdFx0XHRjYWxsYmFja05hbWU6IFwicnVuVGVzdCgpXCIsXG5cdFx0XHRvblRpbWVvdXQ6IChlcnJvcikgPT4gYWJvcnRDb250ZXh0U2lnbmFsKHRlc3QuY29udGV4dCwgZXJyb3IpLFxuXHRcdFx0aW52b2tlSG9vazogKGhvb2ssIHVzZSkgPT4gaG9vayh1c2UsIHRlc3QuY29udGV4dCwgc3VpdGUpXG5cdFx0fVxuXHQpO1xufVxuY29uc3QgcGFja3MgPSBuZXcgTWFwKCk7XG5jb25zdCBldmVudHNQYWNrcyA9IFtdO1xuY29uc3QgcGVuZGluZ1Rhc2tzVXBkYXRlcyA9IFtdO1xuZnVuY3Rpb24gc2VuZFRhc2tzVXBkYXRlKHJ1bm5lcikge1xuXHRpZiAocGFja3Muc2l6ZSkge1xuXHRcdGNvbnN0IHRhc2tQYWNrcyA9IEFycmF5LmZyb20ocGFja3MpLm1hcCgoW2lkLCB0YXNrXSkgPT4ge1xuXHRcdFx0cmV0dXJuIFtcblx0XHRcdFx0aWQsXG5cdFx0XHRcdHRhc2tbMF0sXG5cdFx0XHRcdHRhc2tbMV1cblx0XHRcdF07XG5cdFx0fSk7XG5cdFx0Y29uc3QgcCA9IHJ1bm5lci5vblRhc2tVcGRhdGU/Lih0YXNrUGFja3MsIGV2ZW50c1BhY2tzKTtcblx0XHRpZiAocCkge1xuXHRcdFx0cGVuZGluZ1Rhc2tzVXBkYXRlcy5wdXNoKHApO1xuXHRcdFx0Ly8gcmVtb3ZlIHN1Y2Nlc3NmdWwgcHJvbWlzZSB0byBub3QgZ3JvdyBhcnJheSBpbmRlZmluaXRlbHksXG5cdFx0XHQvLyBidXQga2VlcCByZWplY3Rpb25zIHNvIGZpbmlzaFNlbmRUYXNrc1VwZGF0ZSBjYW4gaGFuZGxlIHRoZW1cblx0XHRcdHAudGhlbigoKSA9PiBwZW5kaW5nVGFza3NVcGRhdGVzLnNwbGljZShwZW5kaW5nVGFza3NVcGRhdGVzLmluZGV4T2YocCksIDEpLCAoKSA9PiB7fSk7XG5cdFx0fVxuXHRcdGV2ZW50c1BhY2tzLmxlbmd0aCA9IDA7XG5cdFx0cGFja3MuY2xlYXIoKTtcblx0fVxufVxuYXN5bmMgZnVuY3Rpb24gZmluaXNoU2VuZFRhc2tzVXBkYXRlKHJ1bm5lcikge1xuXHRzZW5kVGFza3NVcGRhdGUocnVubmVyKTtcblx0YXdhaXQgUHJvbWlzZS5hbGwocGVuZGluZ1Rhc2tzVXBkYXRlcyk7XG59XG5mdW5jdGlvbiB0aHJvdHRsZShmbiwgbXMpIHtcblx0bGV0IGxhc3QgPSAwO1xuXHRsZXQgcGVuZGluZ0NhbGw7XG5cdHJldHVybiBmdW5jdGlvbiBjYWxsKC4uLmFyZ3MpIHtcblx0XHRjb25zdCBub3cgPSB1bml4Tm93KCk7XG5cdFx0aWYgKG5vdyAtIGxhc3QgPiBtcykge1xuXHRcdFx0bGFzdCA9IG5vdztcblx0XHRcdGNsZWFyVGltZW91dChwZW5kaW5nQ2FsbCk7XG5cdFx0XHRwZW5kaW5nQ2FsbCA9IHVuZGVmaW5lZDtcblx0XHRcdHJldHVybiBmbi5hcHBseSh0aGlzLCBhcmdzKTtcblx0XHR9XG5cdFx0Ly8gTWFrZSBzdXJlIGZuIGlzIHN0aWxsIGNhbGxlZCBldmVuIGlmIHRoZXJlIGFyZSBubyBmdXJ0aGVyIGNhbGxzXG5cdFx0cGVuZGluZ0NhbGwgPz89IHNldFRpbWVvdXQoKCkgPT4gY2FsbC5iaW5kKHRoaXMpKC4uLmFyZ3MpLCBtcyk7XG5cdH07XG59XG4vLyB0aHJvdHRsZSBiYXNlZCBvbiBzdW1tYXJ5IHJlcG9ydGVyJ3MgRFVSQVRJT05fVVBEQVRFX0lOVEVSVkFMX01TXG5jb25zdCBzZW5kVGFza3NVcGRhdGVUaHJvdHRsZWQgPSB0aHJvdHRsZShzZW5kVGFza3NVcGRhdGUsIDEwMCk7XG5mdW5jdGlvbiB1cGRhdGVUYXNrKGV2ZW50LCB0YXNrLCBydW5uZXIpIHtcblx0ZXZlbnRzUGFja3MucHVzaChbXG5cdFx0dGFzay5pZCxcblx0XHRldmVudCxcblx0XHR1bmRlZmluZWRcblx0XSk7XG5cdHBhY2tzLnNldCh0YXNrLmlkLCBbdGFzay5yZXN1bHQsIHRhc2subWV0YV0pO1xuXHRzZW5kVGFza3NVcGRhdGVUaHJvdHRsZWQocnVubmVyKTtcbn1cbmFzeW5jIGZ1bmN0aW9uIGNhbGxDbGVhbnVwSG9va3MocnVubmVyLCBjbGVhbnVwcykge1xuXHRjb25zdCBzZXF1ZW5jZSA9IHJ1bm5lci5jb25maWcuc2VxdWVuY2UuaG9va3M7XG5cdGlmIChzZXF1ZW5jZSA9PT0gXCJzdGFja1wiKSB7XG5cdFx0Y2xlYW51cHMgPSBjbGVhbnVwcy5zbGljZSgpLnJldmVyc2UoKTtcblx0fVxuXHRpZiAoc2VxdWVuY2UgPT09IFwicGFyYWxsZWxcIikge1xuXHRcdGF3YWl0IFByb21pc2UuYWxsKGNsZWFudXBzLm1hcChhc3luYyAoZm4pID0+IHtcblx0XHRcdGlmICh0eXBlb2YgZm4gIT09IFwiZnVuY3Rpb25cIikge1xuXHRcdFx0XHRyZXR1cm47XG5cdFx0XHR9XG5cdFx0XHRhd2FpdCBsaW1pdE1heENvbmN1cnJlbmN5KCgpID0+IGZuKCkpO1xuXHRcdH0pKTtcblx0fSBlbHNlIHtcblx0XHRmb3IgKGNvbnN0IGZuIG9mIGNsZWFudXBzKSB7XG5cdFx0XHRpZiAodHlwZW9mIGZuICE9PSBcImZ1bmN0aW9uXCIpIHtcblx0XHRcdFx0Y29udGludWU7XG5cdFx0XHR9XG5cdFx0XHRhd2FpdCBsaW1pdE1heENvbmN1cnJlbmN5KCgpID0+IGZuKCkpO1xuXHRcdH1cblx0fVxufVxuLyoqXG4qIERldGVybWluZXMgaWYgYSB0ZXN0IHNob3VsZCBiZSByZXRyaWVkIGJhc2VkIG9uIGl0cyByZXRyeUNvbmRpdGlvbiBjb25maWd1cmF0aW9uXG4qL1xuZnVuY3Rpb24gcGFzc2VzUmV0cnlDb25kaXRpb24odGVzdCwgZXJyb3JzKSB7XG5cdGNvbnN0IGNvbmRpdGlvbiA9IGdldFJldHJ5Q29uZGl0aW9uKHRlc3QucmV0cnkpO1xuXHRpZiAoIWVycm9ycyB8fCBlcnJvcnMubGVuZ3RoID09PSAwKSB7XG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9XG5cdGlmICghY29uZGl0aW9uKSB7XG5cdFx0cmV0dXJuIHRydWU7XG5cdH1cblx0Y29uc3QgZXJyb3IgPSBlcnJvcnNbZXJyb3JzLmxlbmd0aCAtIDFdO1xuXHRpZiAoY29uZGl0aW9uIGluc3RhbmNlb2YgUmVnRXhwKSB7XG5cdFx0cmV0dXJuIGNvbmRpdGlvbi50ZXN0KGVycm9yLm1lc3NhZ2UgfHwgXCJcIik7XG5cdH0gZWxzZSBpZiAodHlwZW9mIGNvbmRpdGlvbiA9PT0gXCJmdW5jdGlvblwiKSB7XG5cdFx0cmV0dXJuIGNvbmRpdGlvbihlcnJvcik7XG5cdH1cblx0cmV0dXJuIGZhbHNlO1xufVxuYXN5bmMgZnVuY3Rpb24gcnVuVGVzdCh0ZXN0LCBydW5uZXIpIHtcblx0YXdhaXQgcnVubmVyLm9uQmVmb3JlUnVuVGFzaz8uKHRlc3QpO1xuXHRpZiAodGVzdC5tb2RlICE9PSBcInJ1blwiICYmIHRlc3QubW9kZSAhPT0gXCJxdWV1ZWRcIikge1xuXHRcdHVwZGF0ZVRhc2soXCJ0ZXN0LXByZXBhcmVcIiwgdGVzdCwgcnVubmVyKTtcblx0XHR1cGRhdGVUYXNrKFwidGVzdC1maW5pc2hlZFwiLCB0ZXN0LCBydW5uZXIpO1xuXHRcdHJldHVybjtcblx0fVxuXHRpZiAodGVzdC5yZXN1bHQ/LnN0YXRlID09PSBcImZhaWxcIikge1xuXHRcdC8vIHNob3VsZCBub3QgYmUgcG9zc2libGUgdG8gZ2V0IGhlcmUsIEkgdGhpbmsgdGhpcyBpcyBqdXN0IGNvcHkgcGFzdGVkIGZyb20gc3VpdGVcblx0XHQvLyBUT0RPOiBtYXliZSBzb21lb25lIGZhaWxzIHRlc3RzIGluIGBiZWZvcmVBbGxgIGhvb2tzP1xuXHRcdC8vIGh0dHBzOi8vZ2l0aHViLmNvbS92aXRlc3QtZGV2L3ZpdGVzdC9wdWxsLzcwNjlcblx0XHR1cGRhdGVUYXNrKFwidGVzdC1mYWlsZWQtZWFybHlcIiwgdGVzdCwgcnVubmVyKTtcblx0XHRyZXR1cm47XG5cdH1cblx0Y29uc3Qgc3RhcnQgPSBub3coKTtcblx0dGVzdC5yZXN1bHQgPSB7XG5cdFx0c3RhdGU6IFwicnVuXCIsXG5cdFx0c3RhcnRUaW1lOiB1bml4Tm93KCksXG5cdFx0cmV0cnlDb3VudDogMFxuXHR9O1xuXHR1cGRhdGVUYXNrKFwidGVzdC1wcmVwYXJlXCIsIHRlc3QsIHJ1bm5lcik7XG5cdGNvbnN0IGNsZWFudXBSdW5uaW5nVGVzdCA9IGFkZFJ1bm5pbmdUZXN0KHRlc3QpO1xuXHRzZXRDdXJyZW50VGVzdCh0ZXN0KTtcblx0Y29uc3Qgc3VpdGUgPSB0ZXN0LnN1aXRlIHx8IHRlc3QuZmlsZTtcblx0Y29uc3QgJCA9IHJ1bm5lci50cmFjZTtcblx0Y29uc3QgcmVwZWF0cyA9IHRlc3QucmVwZWF0cyA/PyAwO1xuXHRmb3IgKGxldCByZXBlYXRDb3VudCA9IDA7IHJlcGVhdENvdW50IDw9IHJlcGVhdHM7IHJlcGVhdENvdW50KyspIHtcblx0XHRjb25zdCByZXRyeSA9IGdldFJldHJ5Q291bnQodGVzdC5yZXRyeSk7XG5cdFx0Zm9yIChsZXQgcmV0cnlDb3VudCA9IDA7IHJldHJ5Q291bnQgPD0gcmV0cnk7IHJldHJ5Q291bnQrKykge1xuXHRcdFx0bGV0IGJlZm9yZUVhY2hDbGVhbnVwcyA9IFtdO1xuXHRcdFx0Ly8gZml4dHVyZUNoZWNrcG9pbnQgaXMgcGFzc2VkIGJ5IGNhbGxBcm91bmRFYWNoSG9va3MgLSBpdCByZXByZXNlbnRzIHRoZSBjb3VudFxuXHRcdFx0Ly8gb2YgZml4dHVyZSBjbGVhbnVwIGZ1bmN0aW9ucyBBRlRFUiBhbGwgYXJvdW5kRWFjaCBmaXh0dXJlcyBoYXZlIGJlZW4gcmVzb2x2ZWRcblx0XHRcdC8vIGJ1dCBCRUZPUkUgdGhlIHRlc3QgcnVucy4gVGhpcyBhbGxvd3MgdXMgdG8gY2xlYW4gdXAgb25seSBmaXh0dXJlcyBjcmVhdGVkXG5cdFx0XHQvLyBpbnNpZGUgcnVuVGVzdCB3aGlsZSBwcmVzZXJ2aW5nIGFyb3VuZEVhY2ggZml4dHVyZXMgZm9yIHRlYXJkb3duLlxuXHRcdFx0YXdhaXQgY2FsbEFyb3VuZEVhY2hIb29rcyhzdWl0ZSwgdGVzdCwgYXN5bmMgKGZpeHR1cmVDaGVja3BvaW50KSA9PiB7XG5cdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0YXdhaXQgcnVubmVyLm9uQmVmb3JlVHJ5VGFzaz8uKHRlc3QsIHtcblx0XHRcdFx0XHRcdHJldHJ5OiByZXRyeUNvdW50LFxuXHRcdFx0XHRcdFx0cmVwZWF0czogcmVwZWF0Q291bnRcblx0XHRcdFx0XHR9KTtcblx0XHRcdFx0XHR0ZXN0LnJlc3VsdC5yZXBlYXRDb3VudCA9IHJlcGVhdENvdW50O1xuXHRcdFx0XHRcdGJlZm9yZUVhY2hDbGVhbnVwcyA9IGF3YWl0ICQoXCJ0ZXN0LmJlZm9yZUVhY2hcIiwgKCkgPT4gY2FsbFN1aXRlSG9vayhzdWl0ZSwgdGVzdCwgXCJiZWZvcmVFYWNoXCIsIHJ1bm5lciwgW3Rlc3QuY29udGV4dCwgc3VpdGVdKSk7XG5cdFx0XHRcdFx0aWYgKHJ1bm5lci5ydW5UYXNrKSB7XG5cdFx0XHRcdFx0XHRhd2FpdCAkKFwidGVzdC5jYWxsYmFja1wiLCAoKSA9PiBsaW1pdE1heENvbmN1cnJlbmN5KCgpID0+IHJ1bm5lci5ydW5UYXNrKHRlc3QpKSk7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdGNvbnN0IGZuID0gZ2V0Rm4odGVzdCk7XG5cdFx0XHRcdFx0XHRpZiAoIWZuKSB7XG5cdFx0XHRcdFx0XHRcdHRocm93IG5ldyBFcnJvcihcIlRlc3QgZnVuY3Rpb24gaXMgbm90IGZvdW5kLiBEaWQgeW91IGFkZCBpdCB1c2luZyBgc2V0Rm5gP1wiKTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdGF3YWl0ICQoXCJ0ZXN0LmNhbGxiYWNrXCIsICgpID0+IGxpbWl0TWF4Q29uY3VycmVuY3koKCkgPT4gZm4oKSkpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRhd2FpdCBydW5uZXIub25BZnRlclRyeVRhc2s/Lih0ZXN0LCB7XG5cdFx0XHRcdFx0XHRyZXRyeTogcmV0cnlDb3VudCxcblx0XHRcdFx0XHRcdHJlcGVhdHM6IHJlcGVhdENvdW50XG5cdFx0XHRcdFx0fSk7XG5cdFx0XHRcdFx0aWYgKHRlc3QucmVzdWx0LnN0YXRlICE9PSBcImZhaWxcIikge1xuXHRcdFx0XHRcdFx0dGVzdC5yZXN1bHQuc3RhdGUgPSBcInBhc3NcIjtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdFx0XHRmYWlsVGFzayh0ZXN0LnJlc3VsdCwgZSwgcnVubmVyLmNvbmZpZy5kaWZmT3B0aW9ucyk7XG5cdFx0XHRcdH1cblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRhd2FpdCBydW5uZXIub25UYXNrRmluaXNoZWQ/Lih0ZXN0KTtcblx0XHRcdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0XHRcdGZhaWxUYXNrKHRlc3QucmVzdWx0LCBlLCBydW5uZXIuY29uZmlnLmRpZmZPcHRpb25zKTtcblx0XHRcdFx0fVxuXHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdGF3YWl0ICQoXCJ0ZXN0LmFmdGVyRWFjaFwiLCAoKSA9PiBjYWxsU3VpdGVIb29rKHN1aXRlLCB0ZXN0LCBcImFmdGVyRWFjaFwiLCBydW5uZXIsIFt0ZXN0LmNvbnRleHQsIHN1aXRlXSkpO1xuXHRcdFx0XHRcdGlmIChiZWZvcmVFYWNoQ2xlYW51cHMubGVuZ3RoKSB7XG5cdFx0XHRcdFx0XHRhd2FpdCAkKFwidGVzdC5jbGVhbnVwXCIsICgpID0+IGNhbGxDbGVhbnVwSG9va3MocnVubmVyLCBiZWZvcmVFYWNoQ2xlYW51cHMpKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0Ly8gT25seSBjbGVhbiB1cCBmaXh0dXJlcyBjcmVhdGVkIGluc2lkZSBydW5UZXN0IChhZnRlciB0aGUgY2hlY2twb2ludClcblx0XHRcdFx0XHQvLyBGaXh0dXJlcyBjcmVhdGVkIGZvciBhcm91bmRFYWNoIHdpbGwgYmUgY2xlYW5lZCB1cCBhZnRlciBhcm91bmRFYWNoIHRlYXJkb3duXG5cdFx0XHRcdFx0YXdhaXQgY2FsbEZpeHR1cmVDbGVhbnVwRnJvbSh0ZXN0LmNvbnRleHQsIGZpeHR1cmVDaGVja3BvaW50KTtcblx0XHRcdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0XHRcdGZhaWxUYXNrKHRlc3QucmVzdWx0LCBlLCBydW5uZXIuY29uZmlnLmRpZmZPcHRpb25zKTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAodGVzdC5vbkZpbmlzaGVkPy5sZW5ndGgpIHtcblx0XHRcdFx0XHRhd2FpdCAkKFwidGVzdC5vbkZpbmlzaGVkXCIsICgpID0+IGNhbGxUZXN0SG9va3MocnVubmVyLCB0ZXN0LCB0ZXN0Lm9uRmluaXNoZWQsIFwic3RhY2tcIikpO1xuXHRcdFx0XHR9XG5cdFx0XHRcdGlmICh0ZXN0LnJlc3VsdC5zdGF0ZSA9PT0gXCJmYWlsXCIgJiYgdGVzdC5vbkZhaWxlZD8ubGVuZ3RoKSB7XG5cdFx0XHRcdFx0YXdhaXQgJChcInRlc3Qub25GYWlsZWRcIiwgKCkgPT4gY2FsbFRlc3RIb29rcyhydW5uZXIsIHRlc3QsIHRlc3Qub25GYWlsZWQsIHJ1bm5lci5jb25maWcuc2VxdWVuY2UuaG9va3MpKTtcblx0XHRcdFx0fVxuXHRcdFx0XHR0ZXN0Lm9uRmFpbGVkID0gdW5kZWZpbmVkO1xuXHRcdFx0XHR0ZXN0Lm9uRmluaXNoZWQgPSB1bmRlZmluZWQ7XG5cdFx0XHRcdGF3YWl0IHJ1bm5lci5vbkFmdGVyUmV0cnlUYXNrPy4odGVzdCwge1xuXHRcdFx0XHRcdHJldHJ5OiByZXRyeUNvdW50LFxuXHRcdFx0XHRcdHJlcGVhdHM6IHJlcGVhdENvdW50XG5cdFx0XHRcdH0pO1xuXHRcdFx0fSkuY2F0Y2goKGVycm9yKSA9PiB7XG5cdFx0XHRcdGZhaWxUYXNrKHRlc3QucmVzdWx0LCBlcnJvciwgcnVubmVyLmNvbmZpZy5kaWZmT3B0aW9ucyk7XG5cdFx0XHR9KTtcblx0XHRcdC8vIENsZWFuIHVwIGZpeHR1cmVzIHRoYXQgd2VyZSBjcmVhdGVkIGZvciBhcm91bmRFYWNoIChiZWZvcmUgdGhlIGNoZWNrcG9pbnQpXG5cdFx0XHQvLyBUaGlzIHJ1bnMgYWZ0ZXIgYXJvdW5kRWFjaCB0ZWFyZG93biBoYXMgY29tcGxldGVkXG5cdFx0XHR0cnkge1xuXHRcdFx0XHRhd2FpdCBjYWxsRml4dHVyZUNsZWFudXAodGVzdC5jb250ZXh0KTtcblx0XHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdFx0ZmFpbFRhc2sodGVzdC5yZXN1bHQsIGUsIHJ1bm5lci5jb25maWcuZGlmZk9wdGlvbnMpO1xuXHRcdFx0fVxuXHRcdFx0Ly8gc2tpcHBlZCB3aXRoIG5ldyBQZW5kaW5nRXJyb3Jcblx0XHRcdGlmICh0ZXN0LnJlc3VsdD8ucGVuZGluZyB8fCB0ZXN0LnJlc3VsdD8uc3RhdGUgPT09IFwic2tpcFwiKSB7XG5cdFx0XHRcdHRlc3QubW9kZSA9IFwic2tpcFwiO1xuXHRcdFx0XHR0ZXN0LnJlc3VsdCA9IHtcblx0XHRcdFx0XHRzdGF0ZTogXCJza2lwXCIsXG5cdFx0XHRcdFx0bm90ZTogdGVzdC5yZXN1bHQ/Lm5vdGUsXG5cdFx0XHRcdFx0cGVuZGluZzogdHJ1ZSxcblx0XHRcdFx0XHRkdXJhdGlvbjogbm93KCkgLSBzdGFydFxuXHRcdFx0XHR9O1xuXHRcdFx0XHR1cGRhdGVUYXNrKFwidGVzdC1maW5pc2hlZFwiLCB0ZXN0LCBydW5uZXIpO1xuXHRcdFx0XHRzZXRDdXJyZW50VGVzdCh1bmRlZmluZWQpO1xuXHRcdFx0XHRjbGVhbnVwUnVubmluZ1Rlc3QoKTtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXHRcdFx0aWYgKHRlc3QucmVzdWx0LnN0YXRlID09PSBcInBhc3NcIikge1xuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblx0XHRcdGlmIChyZXRyeUNvdW50IDwgcmV0cnkpIHtcblx0XHRcdFx0Y29uc3Qgc2hvdWxkUmV0cnkgPSBwYXNzZXNSZXRyeUNvbmRpdGlvbih0ZXN0LCB0ZXN0LnJlc3VsdC5lcnJvcnMpO1xuXHRcdFx0XHRpZiAoIXNob3VsZFJldHJ5KSB7XG5cdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdH1cblx0XHRcdFx0dGVzdC5yZXN1bHQuc3RhdGUgPSBcInJ1blwiO1xuXHRcdFx0XHR0ZXN0LnJlc3VsdC5yZXRyeUNvdW50ID0gKHRlc3QucmVzdWx0LnJldHJ5Q291bnQgPz8gMCkgKyAxO1xuXHRcdFx0XHRjb25zdCBkZWxheSA9IGdldFJldHJ5RGVsYXkodGVzdC5yZXRyeSk7XG5cdFx0XHRcdGlmIChkZWxheSA+IDApIHtcblx0XHRcdFx0XHRhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCBkZWxheSkpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0XHQvLyB1cGRhdGUgcmV0cnkgaW5mb1xuXHRcdFx0dXBkYXRlVGFzayhcInRlc3QtcmV0cmllZFwiLCB0ZXN0LCBydW5uZXIpO1xuXHRcdH1cblx0fVxuXHQvLyBpZiB0ZXN0IGlzIG1hcmtlZCB0byBiZSBmYWlsZWQsIGZsaXAgdGhlIHJlc3VsdFxuXHRpZiAodGVzdC5mYWlscykge1xuXHRcdGlmICh0ZXN0LnJlc3VsdC5zdGF0ZSA9PT0gXCJwYXNzXCIpIHtcblx0XHRcdGNvbnN0IGVycm9yID0gcHJvY2Vzc0Vycm9yKG5ldyBFcnJvcihcIkV4cGVjdCB0ZXN0IHRvIGZhaWxcIikpO1xuXHRcdFx0dGVzdC5yZXN1bHQuc3RhdGUgPSBcImZhaWxcIjtcblx0XHRcdHRlc3QucmVzdWx0LmVycm9ycyA9IFtlcnJvcl07XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRlc3QucmVzdWx0LnN0YXRlID0gXCJwYXNzXCI7XG5cdFx0XHR0ZXN0LnJlc3VsdC5lcnJvcnMgPSB1bmRlZmluZWQ7XG5cdFx0fVxuXHR9XG5cdGNsZWFudXBSdW5uaW5nVGVzdCgpO1xuXHRzZXRDdXJyZW50VGVzdCh1bmRlZmluZWQpO1xuXHR0ZXN0LnJlc3VsdC5kdXJhdGlvbiA9IG5vdygpIC0gc3RhcnQ7XG5cdGF3YWl0IHJ1bm5lci5vbkFmdGVyUnVuVGFzaz8uKHRlc3QpO1xuXHR1cGRhdGVUYXNrKFwidGVzdC1maW5pc2hlZFwiLCB0ZXN0LCBydW5uZXIpO1xufVxuZnVuY3Rpb24gZmFpbFRhc2socmVzdWx0LCBlcnIsIGRpZmZPcHRpb25zKSB7XG5cdGlmIChlcnIgaW5zdGFuY2VvZiBQZW5kaW5nRXJyb3IpIHtcblx0XHRyZXN1bHQuc3RhdGUgPSBcInNraXBcIjtcblx0XHRyZXN1bHQubm90ZSA9IGVyci5ub3RlO1xuXHRcdHJlc3VsdC5wZW5kaW5nID0gdHJ1ZTtcblx0XHRyZXR1cm47XG5cdH1cblx0aWYgKGVyciBpbnN0YW5jZW9mIFRlc3RSdW5BYm9ydEVycm9yKSB7XG5cdFx0cmVzdWx0LnN0YXRlID0gXCJza2lwXCI7XG5cdFx0cmVzdWx0Lm5vdGUgPSBlcnIubWVzc2FnZTtcblx0XHRyZXR1cm47XG5cdH1cblx0cmVzdWx0LnN0YXRlID0gXCJmYWlsXCI7XG5cdGNvbnN0IGVycm9ycyA9IEFycmF5LmlzQXJyYXkoZXJyKSA/IGVyciA6IFtlcnJdO1xuXHRmb3IgKGNvbnN0IGUgb2YgZXJyb3JzKSB7XG5cdFx0Y29uc3QgZXJyb3JzID0gZSBpbnN0YW5jZW9mIEFnZ3JlZ2F0ZUVycm9yID8gZS5lcnJvcnMubWFwKChlKSA9PiBwcm9jZXNzRXJyb3IoZSwgZGlmZk9wdGlvbnMpKSA6IFtwcm9jZXNzRXJyb3IoZSwgZGlmZk9wdGlvbnMpXTtcblx0XHRyZXN1bHQuZXJyb3JzID8/PSBbXTtcblx0XHRyZXN1bHQuZXJyb3JzLnB1c2goLi4uZXJyb3JzKTtcblx0fVxufVxuZnVuY3Rpb24gbWFya1Rhc2tzQXNTa2lwcGVkKHN1aXRlLCBydW5uZXIpIHtcblx0c3VpdGUudGFza3MuZm9yRWFjaCgodCkgPT4ge1xuXHRcdHQubW9kZSA9IFwic2tpcFwiO1xuXHRcdHQucmVzdWx0ID0ge1xuXHRcdFx0Li4udC5yZXN1bHQsXG5cdFx0XHRzdGF0ZTogXCJza2lwXCJcblx0XHR9O1xuXHRcdHVwZGF0ZVRhc2soXCJ0ZXN0LWZpbmlzaGVkXCIsIHQsIHJ1bm5lcik7XG5cdFx0aWYgKHQudHlwZSA9PT0gXCJzdWl0ZVwiKSB7XG5cdFx0XHRtYXJrVGFza3NBc1NraXBwZWQodCwgcnVubmVyKTtcblx0XHR9XG5cdH0pO1xufVxuZnVuY3Rpb24gbWFya1BlbmRpbmdUYXNrc0FzU2tpcHBlZChzdWl0ZSwgcnVubmVyLCBub3RlKSB7XG5cdHN1aXRlLnRhc2tzLmZvckVhY2goKHQpID0+IHtcblx0XHRpZiAoIXQucmVzdWx0IHx8IHQucmVzdWx0LnN0YXRlID09PSBcInJ1blwiKSB7XG5cdFx0XHR0Lm1vZGUgPSBcInNraXBcIjtcblx0XHRcdHQucmVzdWx0ID0ge1xuXHRcdFx0XHQuLi50LnJlc3VsdCxcblx0XHRcdFx0c3RhdGU6IFwic2tpcFwiLFxuXHRcdFx0XHRub3RlXG5cdFx0XHR9O1xuXHRcdFx0dXBkYXRlVGFzayhcInRlc3QtY2FuY2VsXCIsIHQsIHJ1bm5lcik7XG5cdFx0fVxuXHRcdGlmICh0LnR5cGUgPT09IFwic3VpdGVcIikge1xuXHRcdFx0bWFya1BlbmRpbmdUYXNrc0FzU2tpcHBlZCh0LCBydW5uZXIsIG5vdGUpO1xuXHRcdH1cblx0fSk7XG59XG5hc3luYyBmdW5jdGlvbiBydW5TdWl0ZShzdWl0ZSwgcnVubmVyKSB7XG5cdGF3YWl0IHJ1bm5lci5vbkJlZm9yZVJ1blN1aXRlPy4oc3VpdGUpO1xuXHRpZiAoc3VpdGUucmVzdWx0Py5zdGF0ZSA9PT0gXCJmYWlsXCIpIHtcblx0XHRtYXJrVGFza3NBc1NraXBwZWQoc3VpdGUsIHJ1bm5lcik7XG5cdFx0Ly8gZmFpbGVkIGR1cmluZyBjb2xsZWN0aW9uXG5cdFx0dXBkYXRlVGFzayhcInN1aXRlLWZhaWxlZC1lYXJseVwiLCBzdWl0ZSwgcnVubmVyKTtcblx0XHRyZXR1cm47XG5cdH1cblx0Y29uc3Qgc3RhcnQgPSBub3coKTtcblx0Y29uc3QgbW9kZSA9IHN1aXRlLm1vZGU7XG5cdHN1aXRlLnJlc3VsdCA9IHtcblx0XHRzdGF0ZTogbW9kZSA9PT0gXCJza2lwXCIgfHwgbW9kZSA9PT0gXCJ0b2RvXCIgPyBtb2RlIDogXCJydW5cIixcblx0XHRzdGFydFRpbWU6IHVuaXhOb3coKVxuXHR9O1xuXHRjb25zdCAkID0gcnVubmVyLnRyYWNlO1xuXHR1cGRhdGVUYXNrKFwic3VpdGUtcHJlcGFyZVwiLCBzdWl0ZSwgcnVubmVyKTtcblx0bGV0IGJlZm9yZUFsbENsZWFudXBzID0gW107XG5cdGlmIChzdWl0ZS5tb2RlID09PSBcInNraXBcIikge1xuXHRcdHN1aXRlLnJlc3VsdC5zdGF0ZSA9IFwic2tpcFwiO1xuXHRcdHVwZGF0ZVRhc2soXCJzdWl0ZS1maW5pc2hlZFwiLCBzdWl0ZSwgcnVubmVyKTtcblx0fSBlbHNlIGlmIChzdWl0ZS5tb2RlID09PSBcInRvZG9cIikge1xuXHRcdHN1aXRlLnJlc3VsdC5zdGF0ZSA9IFwidG9kb1wiO1xuXHRcdHVwZGF0ZVRhc2soXCJzdWl0ZS1maW5pc2hlZFwiLCBzdWl0ZSwgcnVubmVyKTtcblx0fSBlbHNlIHtcblx0XHRsZXQgc3VpdGVSYW4gPSBmYWxzZTtcblx0XHR0cnkge1xuXHRcdFx0YXdhaXQgY2FsbEFyb3VuZEFsbEhvb2tzKHN1aXRlLCBhc3luYyAoKSA9PiB7XG5cdFx0XHRcdHN1aXRlUmFuID0gdHJ1ZTtcblx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHQvLyBiZWZvcmVBbGxcblx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0YmVmb3JlQWxsQ2xlYW51cHMgPSBhd2FpdCAkKFwic3VpdGUuYmVmb3JlQWxsXCIsICgpID0+IGNhbGxTdWl0ZUhvb2soc3VpdGUsIHN1aXRlLCBcImJlZm9yZUFsbFwiLCBydW5uZXIsIFtzdWl0ZV0pKTtcblx0XHRcdFx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRcdFx0XHRmYWlsVGFzayhzdWl0ZS5yZXN1bHQsIGUsIHJ1bm5lci5jb25maWcuZGlmZk9wdGlvbnMpO1xuXHRcdFx0XHRcdFx0bWFya1Rhc2tzQXNTa2lwcGVkKHN1aXRlLCBydW5uZXIpO1xuXHRcdFx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHQvLyBydW4gc3VpdGUgY2hpbGRyZW5cblx0XHRcdFx0XHRpZiAocnVubmVyLnJ1blN1aXRlKSB7XG5cdFx0XHRcdFx0XHRhd2FpdCBydW5uZXIucnVuU3VpdGUoc3VpdGUpO1xuXHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRmb3IgKGxldCB0YXNrc0dyb3VwIG9mIHBhcnRpdGlvblN1aXRlQ2hpbGRyZW4oc3VpdGUpKSB7XG5cdFx0XHRcdFx0XHRcdGlmICh0YXNrc0dyb3VwWzBdLmNvbmN1cnJlbnQgPT09IHRydWUpIHtcblx0XHRcdFx0XHRcdFx0XHRhd2FpdCBQcm9taXNlLmFsbCh0YXNrc0dyb3VwLm1hcCgoYykgPT4gcnVuU3VpdGVDaGlsZChjLCBydW5uZXIpKSk7XG5cdFx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdFx0Y29uc3QgeyBzZXF1ZW5jZSB9ID0gcnVubmVyLmNvbmZpZztcblx0XHRcdFx0XHRcdFx0XHRpZiAoc3VpdGUuc2h1ZmZsZSkge1xuXHRcdFx0XHRcdFx0XHRcdFx0Ly8gcnVuIGRlc2NyaWJlIGJsb2NrIGluZGVwZW5kZW50bHkgZnJvbSB0ZXN0c1xuXHRcdFx0XHRcdFx0XHRcdFx0Y29uc3Qgc3VpdGVzID0gdGFza3NHcm91cC5maWx0ZXIoKGdyb3VwKSA9PiBncm91cC50eXBlID09PSBcInN1aXRlXCIpO1xuXHRcdFx0XHRcdFx0XHRcdFx0Y29uc3QgdGVzdHMgPSB0YXNrc0dyb3VwLmZpbHRlcigoZ3JvdXApID0+IGdyb3VwLnR5cGUgPT09IFwidGVzdFwiKTtcblx0XHRcdFx0XHRcdFx0XHRcdGNvbnN0IGdyb3VwcyA9IHNodWZmbGUoW3N1aXRlcywgdGVzdHNdLCBzZXF1ZW5jZS5zZWVkKTtcblx0XHRcdFx0XHRcdFx0XHRcdHRhc2tzR3JvdXAgPSBncm91cHMuZmxhdE1hcCgoZ3JvdXApID0+IHNodWZmbGUoZ3JvdXAsIHNlcXVlbmNlLnNlZWQpKTtcblx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdFx0Zm9yIChjb25zdCBjIG9mIHRhc2tzR3JvdXApIHtcblx0XHRcdFx0XHRcdFx0XHRcdGF3YWl0IHJ1blN1aXRlQ2hpbGQoYywgcnVubmVyKTtcblx0XHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0gZmluYWxseSB7XG5cdFx0XHRcdFx0Ly8gYWZ0ZXJBbGwgcnVucyBldmVuIGlmIGJlZm9yZUFsbCBvciBzdWl0ZSBjaGlsZHJlbiBmYWlsXG5cdFx0XHRcdFx0dHJ5IHtcblx0XHRcdFx0XHRcdGF3YWl0ICQoXCJzdWl0ZS5hZnRlckFsbFwiLCAoKSA9PiBjYWxsU3VpdGVIb29rKHN1aXRlLCBzdWl0ZSwgXCJhZnRlckFsbFwiLCBydW5uZXIsIFtzdWl0ZV0pKTtcblx0XHRcdFx0XHRcdGlmIChiZWZvcmVBbGxDbGVhbnVwcy5sZW5ndGgpIHtcblx0XHRcdFx0XHRcdFx0YXdhaXQgJChcInN1aXRlLmNsZWFudXBcIiwgKCkgPT4gY2FsbENsZWFudXBIb29rcyhydW5uZXIsIGJlZm9yZUFsbENsZWFudXBzKSk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRpZiAoc3VpdGUuZmlsZSA9PT0gc3VpdGUpIHtcblx0XHRcdFx0XHRcdFx0Y29uc3QgY29udGV4dHMgPSBUZXN0Rml4dHVyZXMuZ2V0RmlsZUNvbnRleHRzKHN1aXRlLmZpbGUpO1xuXHRcdFx0XHRcdFx0XHRhd2FpdCBQcm9taXNlLmFsbChjb250ZXh0cy5tYXAoKGNvbnRleHQpID0+IGNhbGxGaXh0dXJlQ2xlYW51cChjb250ZXh0KSkpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdFx0XHRcdGZhaWxUYXNrKHN1aXRlLnJlc3VsdCwgZSwgcnVubmVyLmNvbmZpZy5kaWZmT3B0aW9ucyk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9KTtcblx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHQvLyBtYXJrIHRhc2tzIGFzIHNraXBwZWQgaWYgYXJvdW5kQWxsIGZhaWxlZCBiZWZvcmUgdGhlIHN1aXRlIGNhbGxiYWNrIHdhcyBleGVjdXRlZFxuXHRcdFx0aWYgKCFzdWl0ZVJhbikge1xuXHRcdFx0XHRtYXJrVGFza3NBc1NraXBwZWQoc3VpdGUsIHJ1bm5lcik7XG5cdFx0XHR9XG5cdFx0XHRmYWlsVGFzayhzdWl0ZS5yZXN1bHQsIGUsIHJ1bm5lci5jb25maWcuZGlmZk9wdGlvbnMpO1xuXHRcdH1cblx0XHRpZiAoc3VpdGUubW9kZSA9PT0gXCJydW5cIiB8fCBzdWl0ZS5tb2RlID09PSBcInF1ZXVlZFwiKSB7XG5cdFx0XHRpZiAoIXJ1bm5lci5jb25maWcucGFzc1dpdGhOb1Rlc3RzICYmICFoYXNUZXN0cyhzdWl0ZSkpIHtcblx0XHRcdFx0c3VpdGUucmVzdWx0LnN0YXRlID0gXCJmYWlsXCI7XG5cdFx0XHRcdGlmICghc3VpdGUucmVzdWx0LmVycm9ycz8ubGVuZ3RoKSB7XG5cdFx0XHRcdFx0Y29uc3QgZXJyb3IgPSBwcm9jZXNzRXJyb3IobmV3IEVycm9yKGBObyB0ZXN0IGZvdW5kIGluIHN1aXRlICR7c3VpdGUubmFtZX1gKSk7XG5cdFx0XHRcdFx0c3VpdGUucmVzdWx0LmVycm9ycyA9IFtlcnJvcl07XG5cdFx0XHRcdH1cblx0XHRcdH0gZWxzZSBpZiAoaGFzRmFpbGVkKHN1aXRlKSkge1xuXHRcdFx0XHRzdWl0ZS5yZXN1bHQuc3RhdGUgPSBcImZhaWxcIjtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdHN1aXRlLnJlc3VsdC5zdGF0ZSA9IFwicGFzc1wiO1xuXHRcdFx0fVxuXHRcdH1cblx0XHRzdWl0ZS5yZXN1bHQuZHVyYXRpb24gPSBub3coKSAtIHN0YXJ0O1xuXHRcdGF3YWl0IHJ1bm5lci5vbkFmdGVyUnVuU3VpdGU/LihzdWl0ZSk7XG5cdFx0dXBkYXRlVGFzayhcInN1aXRlLWZpbmlzaGVkXCIsIHN1aXRlLCBydW5uZXIpO1xuXHR9XG59XG5hc3luYyBmdW5jdGlvbiBydW5TdWl0ZUNoaWxkKGMsIHJ1bm5lcikge1xuXHRjb25zdCAkID0gcnVubmVyLnRyYWNlO1xuXHRpZiAoYy50eXBlID09PSBcInRlc3RcIikge1xuXHRcdHJldHVybiAkKFwicnVuLnRlc3RcIiwge1xuXHRcdFx0XCJ2aXRlc3QudGVzdC5pZFwiOiBjLmlkLFxuXHRcdFx0XCJ2aXRlc3QudGVzdC5uYW1lXCI6IGMubmFtZSxcblx0XHRcdFwidml0ZXN0LnRlc3QubW9kZVwiOiBjLm1vZGUsXG5cdFx0XHRcInZpdGVzdC50ZXN0LnRpbWVvdXRcIjogYy50aW1lb3V0LFxuXHRcdFx0XCJjb2RlLmZpbGUucGF0aFwiOiBjLmZpbGUuZmlsZXBhdGgsXG5cdFx0XHRcImNvZGUubGluZS5udW1iZXJcIjogYy5sb2NhdGlvbj8ubGluZSxcblx0XHRcdFwiY29kZS5jb2x1bW4ubnVtYmVyXCI6IGMubG9jYXRpb24/LmNvbHVtblxuXHRcdH0sICgpID0+IHJ1blRlc3QoYywgcnVubmVyKSk7XG5cdH0gZWxzZSBpZiAoYy50eXBlID09PSBcInN1aXRlXCIpIHtcblx0XHRyZXR1cm4gJChcInJ1bi5zdWl0ZVwiLCB7XG5cdFx0XHRcInZpdGVzdC5zdWl0ZS5pZFwiOiBjLmlkLFxuXHRcdFx0XCJ2aXRlc3Quc3VpdGUubmFtZVwiOiBjLm5hbWUsXG5cdFx0XHRcInZpdGVzdC5zdWl0ZS5tb2RlXCI6IGMubW9kZSxcblx0XHRcdFwiY29kZS5maWxlLnBhdGhcIjogYy5maWxlLmZpbGVwYXRoLFxuXHRcdFx0XCJjb2RlLmxpbmUubnVtYmVyXCI6IGMubG9jYXRpb24/LmxpbmUsXG5cdFx0XHRcImNvZGUuY29sdW1uLm51bWJlclwiOiBjLmxvY2F0aW9uPy5jb2x1bW5cblx0XHR9LCAoKSA9PiBydW5TdWl0ZShjLCBydW5uZXIpKTtcblx0fVxufVxuYXN5bmMgZnVuY3Rpb24gcnVuRmlsZXMoZmlsZXMsIHJ1bm5lcikge1xuXHRsaW1pdE1heENvbmN1cnJlbmN5ID8/PSBsaW1pdENvbmN1cnJlbmN5KHJ1bm5lci5jb25maWcubWF4Q29uY3VycmVuY3kpO1xuXHRmb3IgKGNvbnN0IGZpbGUgb2YgZmlsZXMpIHtcblx0XHRpZiAoIWZpbGUudGFza3MubGVuZ3RoICYmICFydW5uZXIuY29uZmlnLnBhc3NXaXRoTm9UZXN0cykge1xuXHRcdFx0aWYgKCFmaWxlLnJlc3VsdD8uZXJyb3JzPy5sZW5ndGgpIHtcblx0XHRcdFx0Y29uc3QgZXJyb3IgPSBwcm9jZXNzRXJyb3IobmV3IEVycm9yKGBObyB0ZXN0IHN1aXRlIGZvdW5kIGluIGZpbGUgJHtmaWxlLmZpbGVwYXRofWApKTtcblx0XHRcdFx0ZmlsZS5yZXN1bHQgPSB7XG5cdFx0XHRcdFx0c3RhdGU6IFwiZmFpbFwiLFxuXHRcdFx0XHRcdGVycm9yczogW2Vycm9yXVxuXHRcdFx0XHR9O1xuXHRcdFx0fVxuXHRcdH1cblx0XHRhd2FpdCBydW5uZXIudHJhY2UoXCJydW4uc3BlY1wiLCB7XG5cdFx0XHRcImNvZGUuZmlsZS5wYXRoXCI6IGZpbGUuZmlsZXBhdGgsXG5cdFx0XHRcInZpdGVzdC5zdWl0ZS50YXNrcy5sZW5ndGhcIjogZmlsZS50YXNrcy5sZW5ndGhcblx0XHR9LCAoKSA9PiBydW5TdWl0ZShmaWxlLCBydW5uZXIpKTtcblx0fVxufVxuY29uc3Qgd29ya2VyUnVubmVycyA9IG5ldyBXZWFrU2V0KCk7XG5mdW5jdGlvbiBkZWZhdWx0VHJhY2UoXywgYXR0cmlidXRlcywgY2IpIHtcblx0aWYgKHR5cGVvZiBhdHRyaWJ1dGVzID09PSBcImZ1bmN0aW9uXCIpIHtcblx0XHRyZXR1cm4gYXR0cmlidXRlcygpO1xuXHR9XG5cdHJldHVybiBjYigpO1xufVxuYXN5bmMgZnVuY3Rpb24gc3RhcnRUZXN0cyhzcGVjcywgcnVubmVyKSB7XG5cdHJ1bm5lci50cmFjZSA/Pz0gZGVmYXVsdFRyYWNlO1xuXHRjb25zdCBjYW5jZWwgPSBydW5uZXIuY2FuY2VsPy5iaW5kKHJ1bm5lcik7XG5cdC8vIElkZWFsbHksIHdlIG5lZWQgdG8gaGF2ZSBhbiBldmVudCBsaXN0ZW5lciBmb3IgdGhpcywgYnV0IG9ubHkgaGF2ZSBhIHJ1bm5lciBoZXJlLlxuXHQvLyBBZGRpbmcgYW5vdGhlciBvbkNhbmNlbCBmZWx0IHdyb25nIChtYXliZSBpdCBuZWVkcyB0byBiZSByZWZhY3RvcmVkKVxuXHRydW5uZXIuY2FuY2VsID0gKHJlYXNvbikgPT4ge1xuXHRcdC8vIFdlIGludGVudGlvbmFsbHkgY3JlYXRlIG9ubHkgb25lIGVycm9yIHNpbmNlIHRoZXJlIGlzIG9ubHkgb25lIHRlc3QgcnVuIHRoYXQgY2FuIGJlIGNhbmNlbGxlZFxuXHRcdGNvbnN0IGVycm9yID0gbmV3IFRlc3RSdW5BYm9ydEVycm9yKFwiVGhlIHRlc3QgcnVuIHdhcyBhYm9ydGVkIGJ5IHRoZSB1c2VyLlwiLCByZWFzb24pO1xuXHRcdGdldFJ1bm5pbmdUZXN0cygpLmZvckVhY2goKHRlc3QpID0+IHtcblx0XHRcdGFib3J0Q29udGV4dFNpZ25hbCh0ZXN0LmNvbnRleHQsIGVycm9yKTtcblx0XHRcdG1hcmtQZW5kaW5nVGFza3NBc1NraXBwZWQodGVzdC5maWxlLCBydW5uZXIsIGVycm9yLm1lc3NhZ2UpO1xuXHRcdH0pO1xuXHRcdHJldHVybiBjYW5jZWw/LihyZWFzb24pO1xuXHR9O1xuXHRpZiAoIXdvcmtlclJ1bm5lcnMuaGFzKHJ1bm5lcikpIHtcblx0XHRydW5uZXIub25DbGVhbnVwV29ya2VyQ29udGV4dD8uKGFzeW5jICgpID0+IHtcblx0XHRcdGF3YWl0IFByb21pc2UuYWxsKFsuLi5UZXN0Rml4dHVyZXMuZ2V0V29ya2VyQ29udGV4dHMoKV0ubWFwKChjb250ZXh0KSA9PiBjYWxsRml4dHVyZUNsZWFudXAoY29udGV4dCkpKS5maW5hbGx5KCgpID0+IHtcblx0XHRcdFx0VGVzdEZpeHR1cmVzLmNsZWFyRGVmaW5pdGlvbnMoKTtcblx0XHRcdH0pO1xuXHRcdH0pO1xuXHRcdHdvcmtlclJ1bm5lcnMuYWRkKHJ1bm5lcik7XG5cdH1cblx0dHJ5IHtcblx0XHRjb25zdCBwYXRocyA9IHNwZWNzLm1hcCgoZikgPT4gdHlwZW9mIGYgPT09IFwic3RyaW5nXCIgPyBmIDogZi5maWxlcGF0aCk7XG5cdFx0YXdhaXQgcnVubmVyLm9uQmVmb3JlQ29sbGVjdD8uKHBhdGhzKTtcblx0XHRjb25zdCBmaWxlcyA9IGF3YWl0IGNvbGxlY3RUZXN0cyhzcGVjcywgcnVubmVyKTtcblx0XHRhd2FpdCBydW5uZXIub25Db2xsZWN0ZWQ/LihmaWxlcyk7XG5cdFx0YXdhaXQgcnVubmVyLm9uQmVmb3JlUnVuRmlsZXM/LihmaWxlcyk7XG5cdFx0YXdhaXQgcnVuRmlsZXMoZmlsZXMsIHJ1bm5lcik7XG5cdFx0YXdhaXQgcnVubmVyLm9uQWZ0ZXJSdW5GaWxlcz8uKGZpbGVzKTtcblx0XHRhd2FpdCBmaW5pc2hTZW5kVGFza3NVcGRhdGUocnVubmVyKTtcblx0XHRyZXR1cm4gZmlsZXM7XG5cdH0gZmluYWxseSB7XG5cdFx0cnVubmVyLmNhbmNlbCA9IGNhbmNlbDtcblx0fVxufVxuYXN5bmMgZnVuY3Rpb24gcHVibGljQ29sbGVjdChzcGVjcywgcnVubmVyKSB7XG5cdHJ1bm5lci50cmFjZSA/Pz0gZGVmYXVsdFRyYWNlO1xuXHRjb25zdCBwYXRocyA9IHNwZWNzLm1hcCgoZikgPT4gdHlwZW9mIGYgPT09IFwic3RyaW5nXCIgPyBmIDogZi5maWxlcGF0aCk7XG5cdGF3YWl0IHJ1bm5lci5vbkJlZm9yZUNvbGxlY3Q/LihwYXRocyk7XG5cdGNvbnN0IGZpbGVzID0gYXdhaXQgY29sbGVjdFRlc3RzKHNwZWNzLCBydW5uZXIpO1xuXHRhd2FpdCBydW5uZXIub25Db2xsZWN0ZWQ/LihmaWxlcyk7XG5cdHJldHVybiBmaWxlcztcbn1cblxuLyoqXG4qIEBleHBlcmltZW50YWxcbiogQGFkdmFuY2VkXG4qXG4qIFJlY29yZHMgYSBjdXN0b20gdGVzdCBhcnRpZmFjdCBkdXJpbmcgdGVzdCBleGVjdXRpb24uXG4qXG4qIFRoaXMgZnVuY3Rpb24gYWxsb3dzIHlvdSB0byBhdHRhY2ggc3RydWN0dXJlZCBkYXRhLCBmaWxlcywgb3IgbWV0YWRhdGEgdG8gYSB0ZXN0LlxuKlxuKiBWaXRlc3QgYXV0b21hdGljYWxseSBpbmplY3RzIHRoZSBzb3VyY2UgbG9jYXRpb24gd2hlcmUgdGhlIGFydGlmYWN0IHdhcyBjcmVhdGVkIGFuZCBtYW5hZ2VzIGFueSBhdHRhY2htZW50cyB5b3UgaW5jbHVkZS5cbipcbiogKipOb3RlOioqIGFydGlmYWN0cyBtdXN0IGJlIHJlY29yZGVkIGJlZm9yZSB0aGUgdGFzayBpcyByZXBvcnRlZC4gQW55IGFydGlmYWN0cyByZWNvcmRlZCBhZnRlciB0aGF0IHdpbGwgbm90IGJlIGluY2x1ZGVkIGluIHRoZSB0YXNrLlxuKlxuKiBAcGFyYW0gdGFzayAtIFRoZSB0ZXN0IHRhc2sgY29udGV4dCwgdHlwaWNhbGx5IGFjY2Vzc2VkIHZpYSBgdGhpcy50YXNrYCBpbiBjdXN0b20gbWF0Y2hlcnMgb3IgYGNvbnRleHQudGFza2AgaW4gdGVzdHNcbiogQHBhcmFtIGFydGlmYWN0IC0gVGhlIGFydGlmYWN0IHRvIHJlY29yZC4gTXVzdCBleHRlbmQge0BsaW5rY29kZSBUZXN0QXJ0aWZhY3RCYXNlfVxuKlxuKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB0byB0aGUgcmVjb3JkZWQgYXJ0aWZhY3Qgd2l0aCBsb2NhdGlvbiBpbmplY3RlZFxuKlxuKiBAdGhyb3dzIHtFcnJvcn0gSWYgdGhlIHRlc3QgcnVubmVyIGRvZXNuJ3Qgc3VwcG9ydCBhcnRpZmFjdHNcbipcbiogQGV4YW1wbGVcbiogYGBgdHNcbiogLy8gSW4gYSBjdXN0b20gYXNzZXJ0aW9uXG4qIGFzeW5jIGZ1bmN0aW9uIHRvSGF2ZVZhbGlkU2NoZW1hKHRoaXM6IE1hdGNoZXJTdGF0ZSwgYWN0dWFsOiB1bmtub3duKSB7XG4qICAgY29uc3QgdmFsaWRhdGlvbiA9IHZhbGlkYXRlU2NoZW1hKGFjdHVhbClcbipcbiogICBhd2FpdCByZWNvcmRBcnRpZmFjdCh0aGlzLnRhc2ssIHtcbiogICAgIHR5cGU6ICdteS1wbHVnaW46c2NoZW1hLXZhbGlkYXRpb24nLFxuKiAgICAgcGFzc2VkOiB2YWxpZGF0aW9uLnZhbGlkLFxuKiAgICAgZXJyb3JzOiB2YWxpZGF0aW9uLmVycm9ycyxcbiogICB9KVxuKlxuKiAgIHJldHVybiB7IHBhc3M6IHZhbGlkYXRpb24udmFsaWQsIG1lc3NhZ2U6ICgpID0+ICcuLi4nIH1cbiogfVxuKiBgYGBcbiovXG5hc3luYyBmdW5jdGlvbiByZWNvcmRBcnRpZmFjdCh0YXNrLCBhcnRpZmFjdCkge1xuXHRjb25zdCBydW5uZXIgPSBnZXRSdW5uZXIoKTtcblx0Y29uc3Qgc3RhY2sgPSBmaW5kVGVzdEZpbGVTdGFja1RyYWNlKHRhc2suZmlsZS5maWxlcGF0aCwgbmV3IEVycm9yKFwiU1RBQ0tfVFJBQ0VcIikuc3RhY2spO1xuXHRpZiAoc3RhY2spIHtcblx0XHRhcnRpZmFjdC5sb2NhdGlvbiA9IHtcblx0XHRcdGZpbGU6IHN0YWNrLmZpbGUsXG5cdFx0XHRsaW5lOiBzdGFjay5saW5lLFxuXHRcdFx0Y29sdW1uOiBzdGFjay5jb2x1bW5cblx0XHR9O1xuXHRcdGlmIChhcnRpZmFjdC50eXBlID09PSBcImludGVybmFsOmFubm90YXRpb25cIikge1xuXHRcdFx0YXJ0aWZhY3QuYW5ub3RhdGlvbi5sb2NhdGlvbiA9IGFydGlmYWN0LmxvY2F0aW9uO1xuXHRcdH1cblx0fVxuXHRpZiAoQXJyYXkuaXNBcnJheShhcnRpZmFjdC5hdHRhY2htZW50cykpIHtcblx0XHRmb3IgKGNvbnN0IGF0dGFjaG1lbnQgb2YgYXJ0aWZhY3QuYXR0YWNobWVudHMpIHtcblx0XHRcdG1hbmFnZUFydGlmYWN0QXR0YWNobWVudChhdHRhY2htZW50KTtcblx0XHR9XG5cdH1cblx0Ly8gYW5ub3RhdGlvbnMgd29uJ3QgcmVzb2x2ZSBhcyBhcnRpZmFjdHMgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5IHVudGlsIG5leHQgbWFqb3Jcblx0aWYgKGFydGlmYWN0LnR5cGUgPT09IFwiaW50ZXJuYWw6YW5ub3RhdGlvblwiKSB7XG5cdFx0cmV0dXJuIGFydGlmYWN0O1xuXHR9XG5cdGlmICghcnVubmVyLm9uVGVzdEFydGlmYWN0UmVjb3JkKSB7XG5cdFx0dGhyb3cgbmV3IEVycm9yKGBUZXN0IHJ1bm5lciBkb2Vzbid0IHN1cHBvcnQgdGVzdCBhcnRpZmFjdHMuYCk7XG5cdH1cblx0YXdhaXQgZmluaXNoU2VuZFRhc2tzVXBkYXRlKHJ1bm5lcik7XG5cdGNvbnN0IHJlc29sdmVkQXJ0aWZhY3QgPSBhd2FpdCBydW5uZXIub25UZXN0QXJ0aWZhY3RSZWNvcmQodGFzaywgYXJ0aWZhY3QpO1xuXHR0YXNrLmFydGlmYWN0cy5wdXNoKHJlc29sdmVkQXJ0aWZhY3QpO1xuXHRyZXR1cm4gcmVzb2x2ZWRBcnRpZmFjdDtcbn1cbmNvbnN0IHRhYmxlID0gW107XG5mb3IgKGxldCBpID0gNjU7IGkgPCA5MTsgaSsrKSB7XG5cdHRhYmxlLnB1c2goU3RyaW5nLmZyb21DaGFyQ29kZShpKSk7XG59XG5mb3IgKGxldCBpID0gOTc7IGkgPCAxMjM7IGkrKykge1xuXHR0YWJsZS5wdXNoKFN0cmluZy5mcm9tQ2hhckNvZGUoaSkpO1xufVxuZm9yIChsZXQgaSA9IDA7IGkgPCAxMDsgaSsrKSB7XG5cdHRhYmxlLnB1c2goaS50b1N0cmluZygxMCkpO1xufVxudGFibGUucHVzaChcIitcIiwgXCIvXCIpO1xuZnVuY3Rpb24gZW5jb2RlVWludDhBcnJheShieXRlcykge1xuXHRsZXQgYmFzZTY0ID0gXCJcIjtcblx0Y29uc3QgbGVuID0gYnl0ZXMuYnl0ZUxlbmd0aDtcblx0Zm9yIChsZXQgaSA9IDA7IGkgPCBsZW47IGkgKz0gMykge1xuXHRcdGlmIChsZW4gPT09IGkgKyAxKSB7XG5cdFx0XHRjb25zdCBhID0gKGJ5dGVzW2ldICYgMjUyKSA+PiAyO1xuXHRcdFx0Y29uc3QgYiA9IChieXRlc1tpXSAmIDMpIDw8IDQ7XG5cdFx0XHRiYXNlNjQgKz0gdGFibGVbYV07XG5cdFx0XHRiYXNlNjQgKz0gdGFibGVbYl07XG5cdFx0XHRiYXNlNjQgKz0gXCI9PVwiO1xuXHRcdH0gZWxzZSBpZiAobGVuID09PSBpICsgMikge1xuXHRcdFx0Y29uc3QgYSA9IChieXRlc1tpXSAmIDI1MikgPj4gMjtcblx0XHRcdGNvbnN0IGIgPSAoYnl0ZXNbaV0gJiAzKSA8PCA0IHwgKGJ5dGVzW2kgKyAxXSAmIDI0MCkgPj4gNDtcblx0XHRcdGNvbnN0IGMgPSAoYnl0ZXNbaSArIDFdICYgMTUpIDw8IDI7XG5cdFx0XHRiYXNlNjQgKz0gdGFibGVbYV07XG5cdFx0XHRiYXNlNjQgKz0gdGFibGVbYl07XG5cdFx0XHRiYXNlNjQgKz0gdGFibGVbY107XG5cdFx0XHRiYXNlNjQgKz0gXCI9XCI7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IGEgPSAoYnl0ZXNbaV0gJiAyNTIpID4+IDI7XG5cdFx0XHRjb25zdCBiID0gKGJ5dGVzW2ldICYgMykgPDwgNCB8IChieXRlc1tpICsgMV0gJiAyNDApID4+IDQ7XG5cdFx0XHRjb25zdCBjID0gKGJ5dGVzW2kgKyAxXSAmIDE1KSA8PCAyIHwgKGJ5dGVzW2kgKyAyXSAmIDE5MikgPj4gNjtcblx0XHRcdGNvbnN0IGQgPSBieXRlc1tpICsgMl0gJiA2Mztcblx0XHRcdGJhc2U2NCArPSB0YWJsZVthXTtcblx0XHRcdGJhc2U2NCArPSB0YWJsZVtiXTtcblx0XHRcdGJhc2U2NCArPSB0YWJsZVtjXTtcblx0XHRcdGJhc2U2NCArPSB0YWJsZVtkXTtcblx0XHR9XG5cdH1cblx0cmV0dXJuIGJhc2U2NDtcbn1cbi8qKlxuKiBSZWNvcmRzIGFuIGFzeW5jIG9wZXJhdGlvbiBhc3NvY2lhdGVkIHdpdGggYSB0ZXN0IHRhc2suXG4qXG4qIFRoaXMgZnVuY3Rpb24gdHJhY2tzIHByb21pc2VzIHRoYXQgc2hvdWxkIGJlIGF3YWl0ZWQgYmVmb3JlIGEgdGVzdCBjb21wbGV0ZXMuXG4qIFRoZSBwcm9taXNlIGlzIGF1dG9tYXRpY2FsbHkgcmVtb3ZlZCBmcm9tIHRoZSB0ZXN0J3MgcHJvbWlzZSBsaXN0IG9uY2UgaXQgc2V0dGxlcy5cbiovXG5mdW5jdGlvbiByZWNvcmRBc3luY09wZXJhdGlvbih0ZXN0LCBwcm9taXNlKSB7XG5cdC8vIGlmIHByb21pc2UgaXMgZXhwbGljaXRseSBhd2FpdGVkLCByZW1vdmUgaXQgZnJvbSB0aGUgbGlzdFxuXHRwcm9taXNlID0gcHJvbWlzZS5maW5hbGx5KCgpID0+IHtcblx0XHRpZiAoIXRlc3QucHJvbWlzZXMpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0Y29uc3QgaW5kZXggPSB0ZXN0LnByb21pc2VzLmluZGV4T2YocHJvbWlzZSk7XG5cdFx0aWYgKGluZGV4ICE9PSAtMSkge1xuXHRcdFx0dGVzdC5wcm9taXNlcy5zcGxpY2UoaW5kZXgsIDEpO1xuXHRcdH1cblx0fSk7XG5cdC8vIHJlY29yZCBwcm9taXNlXG5cdGlmICghdGVzdC5wcm9taXNlcykge1xuXHRcdHRlc3QucHJvbWlzZXMgPSBbXTtcblx0fVxuXHR0ZXN0LnByb21pc2VzLnB1c2gocHJvbWlzZSk7XG5cdHJldHVybiBwcm9taXNlO1xufVxuLyoqXG4qIFZhbGlkYXRlcyBhbmQgcHJlcGFyZXMgYSB0ZXN0IGF0dGFjaG1lbnQgZm9yIHNlcmlhbGl6YXRpb24uXG4qXG4qIFRoaXMgZnVuY3Rpb24gZW5zdXJlcyBhdHRhY2htZW50cyBoYXZlIGVpdGhlciBgYm9keWAgb3IgYHBhdGhgIHNldCAoYnV0IG5vdCBib3RoKSwgYW5kIGNvbnZlcnRzIGBVaW50OEFycmF5YCBib2RpZXMgdG8gYmFzZTY0LWVuY29kZWQgc3RyaW5ncyBmb3IgZWFzaWVyIHNlcmlhbGl6YXRpb24uXG4qXG4qIEBwYXJhbSBhdHRhY2htZW50IC0gVGhlIGF0dGFjaG1lbnQgdG8gdmFsaWRhdGUgYW5kIHByZXBhcmVcbipcbiogQHRocm93cyB7VHlwZUVycm9yfSBJZiBuZWl0aGVyIGBib2R5YCBub3IgYHBhdGhgIGlzIHByb3ZpZGVkXG4qIEB0aHJvd3Mge1R5cGVFcnJvcn0gSWYgYm90aCBgYm9keWAgYW5kIGBwYXRoYCBhcmUgcHJvdmlkZWRcbiovXG5mdW5jdGlvbiBtYW5hZ2VBcnRpZmFjdEF0dGFjaG1lbnQoYXR0YWNobWVudCkge1xuXHRpZiAoYXR0YWNobWVudC5ib2R5ID09IG51bGwgJiYgIWF0dGFjaG1lbnQucGF0aCkge1xuXHRcdHRocm93IG5ldyBUeXBlRXJyb3IoYFRlc3QgYXR0YWNobWVudCByZXF1aXJlcyBcImJvZHlcIiBvciBcInBhdGhcIiB0byBiZSBzZXQuIEJvdGggYXJlIG1pc3NpbmcuYCk7XG5cdH1cblx0aWYgKGF0dGFjaG1lbnQuYm9keSAmJiBhdHRhY2htZW50LnBhdGgpIHtcblx0XHR0aHJvdyBuZXcgVHlwZUVycm9yKGBUZXN0IGF0dGFjaG1lbnQgcmVxdWlyZXMgb25seSBvbmUgb2YgXCJib2R5XCIgb3IgXCJwYXRoXCIgdG8gYmUgc2V0LiBCb3RoIGFyZSBzcGVjaWZpZWQuYCk7XG5cdH1cblx0Ly8gY29udmVydCB0byBhIHN0cmluZyBzbyBpdCdzIGVhc2llciB0byBzZXJpYWxpc2Vcblx0aWYgKGF0dGFjaG1lbnQuYm9keSBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkpIHtcblx0XHRhdHRhY2htZW50LmJvZHkgPSBlbmNvZGVVaW50OEFycmF5KGF0dGFjaG1lbnQuYm9keSk7XG5cdH1cbn1cblxuZXhwb3J0IHsgY3JlYXRlVGFnc0ZpbHRlciBhcyBBLCBjcmVhdGVUYXNrTmFtZSBhcyBCLCBmaW5kVGVzdEZpbGVTdGFja1RyYWNlIGFzIEMsIGdlbmVyYXRlRmlsZUhhc2ggYXMgRCwgZ2VuZXJhdGVIYXNoIGFzIEUsIGdldEZ1bGxOYW1lIGFzIEYsIGdldE5hbWVzIGFzIEcsIGdldFN1aXRlcyBhcyBILCBnZXRUYXNrcyBhcyBJLCBnZXRUZXN0TmFtZSBhcyBKLCBnZXRUZXN0cyBhcyBLLCBoYXNGYWlsZWQgYXMgTCwgaGFzVGVzdHMgYXMgTSwgaW50ZXJwcmV0VGFza01vZGVzIGFzIE4sIGlzVGVzdENhc2UgYXMgTywgbGltaXRDb25jdXJyZW5jeSBhcyBQLCBtYXRjaGVzVGFncyBhcyBRLCBwYXJ0aXRpb25TdWl0ZUNoaWxkcmVuIGFzIFIsIHNvbWVUYXNrc0FyZU9ubHkgYXMgUywgdmFsaWRhdGVUYWdzIGFzIFQsIGFmdGVyQWxsIGFzIGEsIGFmdGVyRWFjaCBhcyBiLCBhcm91bmRBbGwgYXMgYywgYXJvdW5kRWFjaCBhcyBkLCBiZWZvcmVBbGwgYXMgZSwgYmVmb3JlRWFjaCBhcyBmLCBjcmVhdGVUYXNrQ29sbGVjdG9yIGFzIGcsIGRlc2NyaWJlIGFzIGgsIGdldEN1cnJlbnRTdWl0ZSBhcyBpLCBnZXRDdXJyZW50VGVzdCBhcyBqLCBnZXRGbiBhcyBrLCBnZXRIb29rcyBhcyBsLCBpdCBhcyBtLCBvblRlc3RGaW5pc2hlZCBhcyBuLCBvblRlc3RGYWlsZWQgYXMgbywgcHVibGljQ29sbGVjdCBhcyBwLCBzZXRIb29rcyBhcyBxLCByZWNvcmRBcnRpZmFjdCBhcyByLCBzZXRGbiBhcyBzLCBzdGFydFRlc3RzIGFzIHQsIHN1aXRlIGFzIHUsIHRlc3QgYXMgdiwgdXBkYXRlVGFzayBhcyB3LCBjYWxjdWxhdGVTdWl0ZUhhc2ggYXMgeCwgY3JlYXRlQ2hhaW5hYmxlIGFzIHksIGNyZWF0ZUZpbGVUYXNrIGFzIHogfTtcbiJdLCJ4X2dvb2dsZV9pZ25vcmVMaXN0IjpbMF0sIm1hcHBpbmdzIjoiOzs7Ozs7OztBQU9BLElBQU0sZUFBTixjQUEyQixNQUFNO0NBQ2hDLE9BQU87Q0FDUDtDQUNBLFlBQVksU0FBUyxNQUFNLE1BQU07QUFDaEMsUUFBTSxRQUFRO0FBQ2QsT0FBSyxVQUFVO0FBQ2YsT0FBSyxPQUFPO0FBQ1osT0FBSyxTQUFTLEtBQUs7OztBQUdyQixJQUFNLG9CQUFOLGNBQWdDLE1BQU07Q0FDckMsT0FBTztDQUNQO0NBQ0EsWUFBWSxTQUFTLFFBQVE7QUFDNUIsUUFBTSxRQUFRO0FBQ2QsT0FBSyxTQUFTOzs7QUFHaEIsSUFBTSx5QkFBTixjQUFxQyxNQUFNO0NBQzFDLE9BQU87O0FBRVIsSUFBTSxxQkFBTixjQUFpQyxNQUFNO0NBQ3RDLE9BQU87O0FBRVIsSUFBTSxvQkFBTixjQUFnQyxNQUFNO0NBQ3JDLE9BQU87O0FBRVIsSUFBTSx1QkFBTixjQUFtQyxNQUFNO0NBQ3hDLE9BQU87O0FBRVIsSUFBTSwwQkFBTixjQUFzQyxNQUFNO0NBQzNDLE9BQU87O0FBRVIsSUFBTSwrQkFBTixjQUEyQyxNQUFNO0NBQ2hELE9BQU87O0FBSVIsTUFBTSx3QkFBUSxJQUFJLFNBQVM7QUFDM0IsTUFBTSxpQ0FBaUIsSUFBSSxTQUFTO0FBQ3BDLE1BQU0sMkJBQVcsSUFBSSxTQUFTO0FBQzlCLFNBQVMsTUFBTSxLQUFLLElBQUk7QUFDdkIsT0FBTSxJQUFJLEtBQUssR0FBRzs7QUFFbkIsU0FBUyxNQUFNLEtBQUs7QUFDbkIsUUFBTyxNQUFNLElBQUksSUFBSTs7QUFFdEIsU0FBUyxlQUFlLEtBQUssU0FBUztBQUNyQyxnQkFBZSxJQUFJLEtBQUssUUFBUTs7QUFFakMsU0FBUyxnQkFBZ0IsS0FBSztBQUM3QixRQUFPLGVBQWUsSUFBSSxJQUFJOztBQUUvQixTQUFTLFNBQVMsS0FBSyxPQUFPO0FBQzdCLFVBQVMsSUFBSSxLQUFLLE1BQU07O0FBRXpCLFNBQVMsU0FBUyxLQUFLO0FBQ3RCLFFBQU8sU0FBUyxJQUFJLElBQUk7O0FBR3pCLE1BQU0sMEJBQTBCLE9BQU8sSUFBSSw2QkFBNkI7QUFDeEUsSUFBTSxlQUFOLE1BQU0sYUFBYTtDQUNsQjtDQUNBLDZCQUFhLElBQUksU0FBUztDQUMxQjtDQUNBLE9BQU8sZUFBZSxFQUFFO0NBQ3hCLE9BQU8sbUJBQW1CO0VBQ3pCO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0NBQ0QsT0FBTyxxQkFBcUI7RUFDM0I7RUFDQTtFQUNBO0VBQ0E7Q0FDRCxPQUFPLGlCQUFpQjtFQUN2QjtFQUNBO0VBQ0E7RUFDQTtDQUNELE9BQU8sc0JBQXNCLEVBQUUsTUFBTSxVQUFVO0NBQy9DLE9BQU8sbUJBQW1CO0FBQ3pCLGVBQWEsYUFBYSxTQUFTOztDQUVwQyxPQUFPLG9CQUFvQjtBQUMxQixTQUFPLGFBQWEsYUFBYSxLQUFLLE1BQU0sRUFBRSxrQkFBa0IsQ0FBQzs7Q0FFbEUsT0FBTyxnQkFBZ0IsTUFBTTtBQUM1QixTQUFPLGFBQWEsYUFBYSxLQUFLLE1BQU0sRUFBRSxlQUFlLEtBQUssQ0FBQzs7Q0FFcEUsT0FBTyxpQkFBaUIsS0FBSztBQUM1QixTQUFPLFNBQVMsSUFBSSxJQUFJLE9BQU8sS0FBSyxJQUFJLENBQUMsTUFBTSxRQUFRLGFBQWEsbUJBQW1CLFNBQVMsSUFBSSxDQUFDOztDQUV0RyxZQUFZLGVBQWU7QUFDMUIsT0FBSyxpQkFBaUIsaUNBQWlCLElBQUksS0FBSztBQUNoRCxPQUFLLGlDQUFpQixJQUFJLFNBQVM7QUFDbkMsZUFBYSxhQUFhLEtBQUssS0FBSzs7Q0FFckMsT0FBTyxRQUFRLGNBQWM7RUFDNUIsTUFBTSxFQUFFLFVBQVUsaUJBQWlCO0VBQ25DLE1BQU0sYUFBYSxDQUFDLFNBQVMsTUFBTSxTQUFTO0FBRTVDLFNBQU8sSUFBSSxhQURXLEtBQUssa0JBQWtCLFFBQVEsY0FBYyxXQUFXLENBQ3hDOztDQUV2QyxJQUFJLE9BQU87RUFDVixJQUFJLGVBQWU7QUFDbkIsU0FBTyxjQUFjO0dBQ3BCLE1BQU0sWUFBWSxLQUFLLFdBQVcsSUFBSSxhQUFhO0FBRW5ELE9BQUksVUFDSCxRQUFPO0FBRVIsT0FBSSxpQkFBaUIsYUFBYSxLQUNqQztBQUVELGtCQUFlLGFBQWEsU0FBUyxhQUFhOztBQUVuRCxTQUFPLEtBQUs7O0NBRWIsU0FBUyxRQUFRLGNBQWM7RUFDOUIsTUFBTSxFQUFFLE9BQU8sY0FBYyxTQUFTLGlCQUFpQjtFQUN2RCxNQUFNLFFBQVEsZ0JBQWdCO0VBQzlCLE1BQU0sYUFBYSxDQUFDLGdCQUFnQixhQUFhLFNBQVM7RUFHMUQsTUFBTSxxQkFBcUIsSUFBSSxJQUFJLEtBQUssSUFBSSxNQUFNLENBQUM7RUFDbkQsTUFBTSxnQkFBZ0IsS0FBSyxrQkFBa0IsUUFBUSxjQUFjLFlBQVksbUJBQW1CO0FBR2xHLE1BQUksV0FDSCxNQUFLLGlCQUFpQjtNQUV0QixNQUFLLFdBQVcsSUFBSSxPQUFPLGNBQWM7O0NBRzNDLGVBQWUsTUFBTTtBQUNwQixNQUFJLENBQUMsS0FBSyxlQUFlLElBQUksS0FBSyxDQUNqQyxNQUFLLGVBQWUsSUFBSSxNQUFNLE9BQU8sT0FBTyxLQUFLLENBQUM7QUFFbkQsU0FBTyxLQUFLLGVBQWUsSUFBSSxLQUFLOztDQUVyQyxtQkFBbUI7QUFDbEIsTUFBSSxDQUFDLEtBQUssZUFBZSxJQUFJLGFBQWEsb0JBQW9CLENBQzdELE1BQUssZUFBZSxJQUFJLGFBQWEscUJBQXFCLE9BQU8sT0FBTyxLQUFLLENBQUM7QUFFL0UsU0FBTyxLQUFLLGVBQWUsSUFBSSxhQUFhLG9CQUFvQjs7Q0FFakUsa0JBQWtCLFFBQVEsY0FBYyxnQkFBZ0IsZ0JBQWdCLElBQUksSUFBSSxLQUFLLGVBQWUsRUFBRTtFQUNyRyxNQUFNLFNBQVMsRUFBRTtBQUNqQixTQUFPLFFBQVEsYUFBYSxDQUFDLFNBQVMsQ0FBQyxNQUFNLFFBQVE7R0FDcEQsSUFBSTtHQUNKLElBQUk7R0FDSixJQUFJO0FBQ0osT0FBSSxNQUFNLFFBQVEsR0FBRyxJQUFJLEdBQUcsVUFBVSxLQUFLLGFBQWEsaUJBQWlCLEdBQUcsR0FBRyxFQUFFO0FBQ2hGLGVBQVcsR0FBRztBQUNkLGNBQVU7S0FDVCxNQUFNLFNBQVMsUUFBUTtLQUN2QixPQUFPLFNBQVMsU0FBUztLQUN6QixVQUFVLFNBQVMsWUFBWTtLQUMvQjtBQUNELFlBQVEsUUFBUSxXQUFXLE9BQU8sY0FBYyxLQUFLLElBQUksR0FBRyxLQUFLLEdBQUc7U0FFcEUsU0FBUTtHQUVULE1BQU0sU0FBUyxjQUFjLElBQUksS0FBSztBQUN0QyxPQUFJLFVBQVUsU0FBUztBQUN0QixRQUFJLE9BQU8sVUFBVSxRQUFRLE1BQzVCLFFBQU8sS0FBSyxJQUFJLHVCQUF1QixRQUFRLEtBQUssMkNBQTJDLFFBQVEsTUFBTSxVQUFVLENBQUM7QUFFekgsUUFBSSxPQUFPLFNBQVMsUUFBUSxLQUMzQixRQUFPLEtBQUssSUFBSSx1QkFBdUIsUUFBUSxLQUFLLDhDQUE4QyxRQUFRLEtBQUssS0FBSyxDQUFDO2NBRTVHLE9BQ1YsV0FBVTtJQUNULE1BQU0sT0FBTztJQUNiLE9BQU8sT0FBTztJQUNkLFVBQVUsT0FBTztJQUNqQjtZQUNTLENBQUMsUUFDWCxXQUFVO0lBQ1QsTUFBTTtJQUNOLFVBQVU7SUFDVixPQUFPO0lBQ1A7QUFFRixPQUFJLFFBQVEsU0FBUyxDQUFDLGFBQWEsZUFBZSxTQUFTLFFBQVEsTUFBTSxDQUN4RSxRQUFPLEtBQUssSUFBSSx1QkFBdUIsUUFBUSxLQUFLLCtCQUErQixRQUFRLE1BQU0sSUFBSSxDQUFDO0FBRXZHLE9BQUksQ0FBQyxrQkFBa0IsUUFBUSxVQUFVLE9BQ3hDLFFBQU8sS0FBSyxJQUFJLHVCQUF1QixRQUFRLEtBQUsscUNBQXFDLFFBQVEsTUFBTSxRQUFRLENBQUMsVUFBVSxTQUFTLFFBQVEsUUFBUSx1Q0FBdUMsR0FBRyw2RUFBNkUsQ0FBQztHQUU1USxNQUFNLE9BQU8sa0JBQWtCLE1BQU0sR0FBRyxhQUFhLE1BQU0sbUJBQUcsSUFBSSxLQUFLO0dBQ3ZFLE1BQU0sT0FBTztJQUNaO0lBQ0E7SUFDQSxNQUFNLFFBQVEsUUFBUTtJQUN0QixVQUFVLFFBQVEsWUFBWTtJQUM5QixPQUFPLFFBQVEsU0FBUztJQUN4QjtJQUNBO0lBQ0E7QUFDRCxPQUFJLGtCQUFrQixNQUFNLENBQzNCLFFBQU8sT0FBTyxPQUFPLEdBQUcsMENBQTBCLElBQUksTUFBTSxvQkFBb0IsRUFBRSxDQUFDO0FBRXBGLGlCQUFjLElBQUksTUFBTSxLQUFLO0FBQzdCLE9BQUksS0FBSyxVQUFVLGFBQWEsT0FBTyxTQUFTLGVBQWUsT0FBTyxTQUFTLFdBQzlFLE1BQUssUUFBUTtJQUViO0FBRUYsT0FBSyxNQUFNLFdBQVcsY0FBYyxRQUFRLENBQzNDLE1BQUssTUFBTSxXQUFXLFFBQVEsTUFBTTtBQUNuQyxPQUFJLGFBQWEsaUJBQWlCLFNBQVMsUUFBUSxDQUNsRDtHQUVELE1BQU0sTUFBTSxjQUFjLElBQUksUUFBUTtBQUN0QyxPQUFJLENBQUMsS0FBSztBQUNULFdBQU8sS0FBSyxJQUFJLHVCQUF1QixRQUFRLFFBQVEsS0FBSyx3Q0FBd0MsUUFBUSxJQUFJLENBQUM7QUFDakg7O0FBRUQsT0FBSSxZQUFZLFFBQVEsUUFBUSxDQUFDLFFBQVEsUUFBUTtBQUNoRCxXQUFPLEtBQUssSUFBSSx1QkFBdUIsUUFBUSxRQUFRLEtBQUssdUVBQXVFLENBQUM7QUFDcEk7O0FBRUQsT0FBSSxhQUFhLGVBQWUsUUFBUSxRQUFRLE1BQU0sR0FBRyxhQUFhLGVBQWUsUUFBUSxJQUFJLE1BQU0sRUFBRTtBQUN4RyxXQUFPLEtBQUssSUFBSSx1QkFBdUIsT0FBTyxRQUFRLE1BQU0sSUFBSSxRQUFRLEtBQUssK0JBQStCLElBQUksTUFBTSxZQUFZLElBQUksS0FBSyxJQUFJLENBQUM7QUFDaEo7OztBQUlILE1BQUksT0FBTyxXQUFXLEVBQ3JCLE9BQU0sT0FBTztXQUNILE9BQU8sU0FBUyxFQUMxQixPQUFNLElBQUksZUFBZSxRQUFRLGlFQUFpRTtBQUVuRyxTQUFPOzs7QUFHVCxNQUFNLG9DQUFvQixJQUFJLFNBQVM7QUFDdkMsZUFBZSxtQkFBbUIsU0FBUztDQUMxQyxNQUFNLGlCQUFpQixrQkFBa0IsSUFBSSxRQUFRLElBQUksRUFBRTtBQUMzRCxNQUFLLE1BQU0sV0FBVyxlQUFlLFNBQVMsQ0FDN0MsT0FBTSxTQUFTO0FBRWhCLG1CQUFrQixPQUFPLFFBQVE7Ozs7OztBQU1sQyxTQUFTLHVCQUF1QixTQUFTO0FBQ3hDLFFBQU8sa0JBQWtCLElBQUksUUFBUSxFQUFFLFVBQVU7Ozs7Ozs7QUFPbEQsZUFBZSx1QkFBdUIsU0FBUyxXQUFXO0NBQ3pELE1BQU0saUJBQWlCLGtCQUFrQixJQUFJLFFBQVE7QUFDckQsS0FBSSxDQUFDLGtCQUFrQixlQUFlLFVBQVUsVUFDL0M7Q0FHRCxNQUFNLFlBQVksZUFBZSxNQUFNLFVBQVU7QUFFakQsTUFBSyxNQUFNLFdBQVcsVUFBVSxTQUFTLENBQ3hDLE9BQU0sU0FBUztBQUdoQixnQkFBZSxTQUFTOztBQUV6QixNQUFNLDBDQUEwQixJQUFJLFNBQVM7QUFDN0MsU0FBUyxhQUFhLElBQUksU0FBUztDQUNsQyxNQUFNLFlBQVksaUJBQWlCO0NBQ25DLE1BQU0sUUFBUSxTQUFTLFNBQVMsVUFBVSxTQUFTLFVBQVU7QUFDN0QsUUFBTyxPQUFPLGdCQUFnQjtFQUM3QixNQUFNLFVBQVUsZUFBZSxTQUFTO0FBQ3hDLE1BQUksQ0FBQyxTQUFTO0FBQ2IsT0FBSSxTQUFTLFVBQ1osbUJBQWtCLElBQUksUUFBUSxXQUFXLFFBQVEsZ0JBQWdCO0FBRWxFLFVBQU8sR0FBRyxFQUFFLENBQUM7O0VBRWQsTUFBTSxXQUFXLFNBQVMsWUFBWSxnQkFBZ0IsUUFBUTtBQUM5RCxNQUFJLENBQUMsU0FDSixRQUFPLEdBQUcsUUFBUTtFQUVuQixNQUFNLGdCQUFnQixTQUFTLElBQUksTUFBTTtBQUN6QyxNQUFJLENBQUMsY0FBYyxLQUNsQixRQUFPLEdBQUcsUUFBUTtFQUVuQixNQUFNLGVBQWUsRUFBRTtFQUN2QixNQUFNLFlBQVksYUFBYSxHQUFHO0FBQ2xDLE9BQUssTUFBTSxXQUFXLGNBQWMsUUFBUSxDQUMzQyxLQUFJLFFBQVEsUUFBUSxVQUFVLElBQUksUUFBUSxLQUFLLENBQzlDLGNBQWEsS0FBSyxRQUFRO0FBRzVCLE1BQUksQ0FBQyxhQUFhLE9BQ2pCLFFBQU8sR0FBRyxRQUFRO0FBRW5CLE1BQUksQ0FBQyxrQkFBa0IsSUFBSSxRQUFRLENBQ2xDLG1CQUFrQixJQUFJLFNBQVMsRUFBRSxDQUFDO0VBRW5DLE1BQU0saUJBQWlCLGtCQUFrQixJQUFJLFFBQVE7RUFDckQsTUFBTSxrQkFBa0IsWUFBWSxjQUFjLGNBQWM7QUFDaEUsTUFBSSxDQUFDLGdCQUFnQixPQUNwQixRQUFPLEdBQUcsUUFBUTtBQUluQixNQUFJLFNBQVMsV0FBVztHQUN2QixNQUFNLHFCQUFxQixnQkFBZ0IsUUFBUSxNQUFNLEVBQUUsVUFBVSxPQUFPO0FBQzVFLE9BQUksbUJBQW1CLFNBQVMsR0FBRztJQUNsQyxNQUFNLGVBQWUsbUJBQW1CLEtBQUssTUFBTSxJQUFJLEVBQUUsS0FBSyxHQUFHLENBQUMsS0FBSyxLQUFLO0lBTTVFLE1BQU0sUUFBUSxJQUFJLHVCQUF1Qiw4Q0FBOEMsUUFBUSxVQUFVLGlEQUFzRCxhQUFhLHdGQUxwSjtLQUN2QixXQUFXO0tBQ1gsV0FBVztLQUNYLFVBQVU7S0FDVixDQUN3UixRQUFRLFdBQVcsUUFBUTtBQUVwVCxRQUFJLFFBQVEsaUJBQWlCLE1BQzVCLE9BQU0sUUFBUSxNQUFNLFVBQVUsUUFBUSxnQkFBZ0IsTUFBTSxRQUFRLFFBQVEsZ0JBQWdCLFNBQVMsR0FBRztBQUV6RyxVQUFNOzs7QUFHUixNQUFJLENBQUMsd0JBQXdCLElBQUksUUFBUSxDQUN4Qyx5QkFBd0IsSUFBSSx5QkFBUyxJQUFJLFNBQVMsQ0FBQztFQUVwRCxNQUFNLGlCQUFpQix3QkFBd0IsSUFBSSxRQUFRO0FBQzNELE9BQUssTUFBTSxXQUFXLGdCQUNyQixLQUFJLFFBQVEsVUFBVSxRQUFRO0FBSTdCLE9BQUksZUFBZSxJQUFJLFFBQVEsQ0FDOUI7QUFFRCxrQkFBZSxJQUFJLFFBQVE7R0FDM0IsTUFBTSxnQkFBZ0IsTUFBTSx3QkFBd0IsU0FBUyxTQUFTLGVBQWU7QUFDckYsV0FBUSxRQUFRLFFBQVE7QUFDeEIsa0JBQWUsV0FBVztBQUN6QixtQkFBZSxPQUFPLFFBQVE7S0FDN0I7U0FDSTtHQUNOLE1BQU0sZ0JBQWdCLE1BQU0seUJBQXlCLFVBQVUsT0FBTyxRQUFRO0FBQzlFLFdBQVEsUUFBUSxRQUFROztBQUcxQixTQUFPLEdBQUcsUUFBUTs7O0FBR3BCLFNBQVMsa0JBQWtCLE9BQU87QUFDakMsUUFBTyxPQUFPLFVBQVU7O0FBRXpCLFNBQVMsd0JBQXdCLFNBQVMsU0FBUyxnQkFBZ0I7QUFDbEUsS0FBSSxDQUFDLGtCQUFrQixRQUFRLE1BQU0sQ0FDcEMsUUFBTyxRQUFRO0FBRWhCLFFBQU8sdUJBQXVCLFFBQVEsT0FBTyxRQUFRLE1BQU0sU0FBUyxlQUFlOztBQUVwRixNQUFNLDRDQUE0QixJQUFJLFNBQVM7QUFDL0MsZUFBZSx5QkFBeUIsVUFBVSxPQUFPLFNBQVM7Q0FDakUsTUFBTSxnQkFBZ0IsU0FBUyxrQkFBa0I7Q0FDakQsTUFBTSxjQUFjLFNBQVMsZUFBZSxNQUFNLEtBQUs7Q0FDdkQsTUFBTSxpQkFBaUIsUUFBUSxVQUFVLFdBQVcsZ0JBQWdCO0FBQ3BFLEtBQUksQ0FBQyxrQkFBa0IsUUFBUSxNQUFNLEVBQUU7QUFDdEMsaUJBQWUsUUFBUSxRQUFRLFFBQVE7QUFDdkMsU0FBTyxRQUFROztBQUVoQixLQUFJLFFBQVEsUUFBUSxlQUNuQixRQUFPLGVBQWUsUUFBUTtBQUUvQixLQUFJLDBCQUEwQixJQUFJLFFBQVEsQ0FDekMsUUFBTywwQkFBMEIsSUFBSSxRQUFRO0FBRTlDLEtBQUksQ0FBQyxrQkFBa0IsSUFBSSxlQUFlLENBQ3pDLG1CQUFrQixJQUFJLGdCQUFnQixFQUFFLENBQUM7Q0FFMUMsTUFBTSxxQkFBcUIsa0JBQWtCLElBQUksZUFBZTtDQUNoRSxNQUFNLFVBQVUsdUJBQXVCLFFBQVEsT0FBTyxRQUFRLE1BQU0sUUFBUSxVQUFVLFNBQVM7RUFDOUYsR0FBRztFQUNILEdBQUc7RUFDSCxHQUFHLGdCQUFnQixtQkFBbUIsQ0FBQyxNQUFNLFVBQVU7QUFDdkQsaUJBQWUsUUFBUSxRQUFRO0FBQy9CLDRCQUEwQixPQUFPLFFBQVE7QUFDekMsU0FBTztHQUNOO0FBQ0YsMkJBQTBCLElBQUksU0FBUyxRQUFRO0FBQy9DLFFBQU87O0FBRVIsZUFBZSx1QkFBdUIsV0FBVyxhQUFhLFNBQVMsZ0JBQWdCO0NBRXRGLE1BQU0sa0JBQWtCLGFBQWE7Q0FDckMsTUFBTSxrQkFBa0IsMkJBQTJCLGFBQWEsVUFBVSxvQ0FBb0MsUUFBUSxVQUFVLDJCQUEyQjtDQUMzSixJQUFJLHFCQUFxQjtDQUN6QixNQUFNLGdCQUFnQixVQUFVLFNBQVMsT0FBTyxhQUFhO0FBRTVELHVCQUFxQjtBQUNyQixrQkFBZ0IsUUFBUSxTQUFTO0VBRWpDLE1BQU0sbUJBQW1CLGFBQWE7QUFDdEMsaUJBQWUsS0FBSyxZQUFZO0FBRS9CLG9CQUFpQixTQUFTO0FBRTFCLFNBQU07SUFDTDtBQUNGLFFBQU07R0FDTCxDQUFDLFdBQVc7QUFFYixNQUFJLENBQUMsb0JBQW9CO0dBQ3hCLE1BQU0sd0JBQVEsSUFBSSxNQUFNLFlBQVksWUFBWSx1R0FBdUc7QUFDdkosT0FBSSxpQkFBaUIsTUFDcEIsT0FBTSxRQUFRLE1BQU0sVUFBVSxnQkFBZ0IsTUFBTSxRQUFRLGdCQUFnQixTQUFTLEdBQUc7QUFFekYsbUJBQWdCLE9BQU8sTUFBTTs7R0FFN0IsQ0FBQyxPQUFPLE1BQU07QUFFZixNQUFJLENBQUMsb0JBQW9CO0FBQ3hCLG1CQUFnQixPQUFPLEVBQUU7QUFDekI7O0FBR0QsUUFBTTtHQUNMO0FBQ0YsUUFBTzs7QUFFUixTQUFTLFlBQVksY0FBYyxlQUFlLHlCQUFTLElBQUksS0FBSyxFQUFFLGtCQUFrQixFQUFFLEVBQUU7QUFDM0YsY0FBYSxTQUFTLFlBQVk7QUFDakMsTUFBSSxnQkFBZ0IsU0FBUyxRQUFRLENBQ3BDO0FBRUQsTUFBSSxDQUFDLGtCQUFrQixRQUFRLE1BQU0sSUFBSSxDQUFDLFFBQVEsTUFBTTtBQUN2RCxtQkFBZ0IsS0FBSyxRQUFRO0FBQzdCOztBQUVELE1BQUksT0FBTyxJQUFJLFFBQVEsQ0FDdEIsS0FBSSxRQUFRLE9BQ1gsV0FBVSxRQUFRO01BRWxCLE9BQU0sSUFBSSxNQUFNLHlDQUF5QyxRQUFRLEtBQUssTUFBTSxDQUFDLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxLQUFLLE1BQU0sRUFBRSxLQUFLLENBQUMsS0FBSyxPQUFPLEdBQUc7QUFHdEksU0FBTyxJQUFJLFFBQVE7QUFDbkIsY0FBWSxDQUFDLEdBQUcsUUFBUSxLQUFLLENBQUMsS0FBSyxNQUFNLE1BQU0sUUFBUSxPQUFPLFFBQVEsU0FBUyxjQUFjLElBQUksRUFBRSxDQUFDLENBQUMsUUFBUSxNQUFNLENBQUMsQ0FBQyxFQUFFLEVBQUUsZUFBZSxRQUFRLGdCQUFnQjtBQUNoSyxrQkFBZ0IsS0FBSyxRQUFRO0FBQzdCLFNBQU8sT0FBTztHQUNiO0FBQ0YsUUFBTzs7QUFFUixTQUFTLGtCQUFrQixJQUFJLE1BQU0sWUFBWTtDQUNoRCxNQUFNLFlBQVksYUFBYSxJQUFJO0VBQ2xDLGFBQWE7RUFDYixXQUFXO0VBQ1gsQ0FBQztBQUNGLEtBQUksVUFBVSxNQUFNO0VBQ25CLE1BQU0sUUFBUSxJQUFJLG1CQUFtQixPQUFPLEtBQUssdUJBQXVCLENBQUMsR0FBRyxVQUFVLENBQUMsS0FBSyxTQUFTLENBQUMsc0VBQTJFLEtBQUssa0JBQWtCLEtBQUssaUxBQTJMO0FBQ3hZLE1BQUksV0FDSCxPQUFNLFFBQVEsV0FBVyxPQUFPLFFBQVEsV0FBVyxTQUFTLE1BQU0sUUFBUTtBQUUzRSxRQUFNOzs7QUFHUixNQUFNLGVBQWUsT0FBTyx3QkFBd0I7QUFDcEQsTUFBTSxtQkFBbUIsT0FBTyw2QkFBNkI7QUFDN0QsU0FBUyxlQUFlLElBQUksU0FBUztBQUNwQyxRQUFPLGVBQWUsSUFBSSxjQUFjO0VBQ3ZDLE9BQU87RUFDUCxZQUFZO0VBQ1osQ0FBQzs7QUFFSCxTQUFTLFVBQVUsSUFBSSxPQUFPO0FBQzdCLElBQUcsb0JBQW9CO0FBQ3ZCLFFBQU87O0FBRVIsU0FBUyxhQUFhLElBQUksRUFBRSxhQUFhLGNBQWMsRUFBRSxFQUFFO0FBQzFELEtBQUksb0JBQW9CLEdBQ3ZCLFFBQU8sR0FBRztDQUVYLE1BQU0sRUFBRSxPQUFPLGdCQUFnQixHQUFHLFVBQVUsaUJBQWlCLE9BQU8sZ0JBQWdCLEtBQUssR0FBRyxnQkFBZ0IsRUFBRTtDQUM5RyxJQUFJLFdBQVcsa0JBQWtCLGVBQWUsVUFBVSxDQUFDO0FBTTNELEtBQUksdUVBQXVFLEtBQUssU0FBUyxDQUN4RixZQUFXLFNBQVMsTUFBTSwwQkFBMEIsQ0FBQztDQUV0RCxNQUFNLFFBQVEsU0FBUyxNQUFNLGlCQUFpQjtBQUM5QyxLQUFJLENBQUMsTUFDSixRQUFPLFVBQVUsb0JBQUksSUFBSSxLQUFLLENBQUM7Q0FFaEMsTUFBTSxPQUFPLGFBQWEsTUFBTSxHQUFHO0FBQ25DLEtBQUksQ0FBQyxLQUFLLE9BQ1QsUUFBTyxVQUFVLG9CQUFJLElBQUksS0FBSyxDQUFDO0NBRWhDLE1BQU0sbUJBQW1CLEtBQUs7QUFDOUIsS0FBSSxDQUFDLGlCQUNKLFFBQU8sVUFBVSxvQkFBSSxJQUFJLEtBQUssQ0FBQztBQUVoQyxLQUFJLEVBQUUsaUJBQWlCLE9BQU8sT0FBTyxpQkFBaUIsU0FBUyxJQUFJLEdBQUc7RUFDckUsTUFBTSxrQkFBa0IsUUFBUSxnQkFBZ0IsRUFBRTtFQUNsRCxNQUFNLFFBQVEsSUFBSSxrQkFBa0IsT0FBTyxnQkFBZ0IsOEdBQW1ILGlCQUFpQixJQUFTLFlBQVksNkNBQTZDLGdCQUFnQix5Q0FBeUMsUUFBUSxnQkFBZ0IsRUFBRSxDQUFDLHNCQUFzQixLQUFLO0FBQ2hYLE1BQUksWUFDSCxPQUFNLFFBQVEsWUFBWSxPQUFPLFFBQVEsWUFBWSxTQUFTLE1BQU0sUUFBUTtBQUU3RSxRQUFNOztDQUdQLE1BQU0sUUFBUSxhQURDLGlCQUFpQixNQUFNLEdBQUcsR0FBRyxDQUFDLFFBQVEsT0FBTyxHQUFHLENBQzdCLENBQUMsS0FBSyxTQUFTO0FBQ2hELFNBQU8sS0FBSyxRQUFRLFlBQVksR0FBRztHQUNsQztDQUNGLE1BQU0sT0FBTyxNQUFNLEdBQUcsR0FBRztBQUN6QixLQUFJLFFBQVEsS0FBSyxXQUFXLE1BQU0sRUFBRTtFQUNuQyxNQUFNLFFBQVEsSUFBSSxrQkFBa0IsNERBQTRELEtBQUssSUFBSTtBQUN6RyxNQUFJLFlBQ0gsT0FBTSxRQUFRLFlBQVksT0FBTyxRQUFRLFlBQVksU0FBUyxNQUFNLFFBQVE7QUFFN0UsUUFBTTs7QUFFUCxRQUFPLFVBQVUsSUFBSSxJQUFJLElBQUksTUFBTSxDQUFDOztBQUVyQyxTQUFTLGFBQWEsR0FBRztDQUN4QixNQUFNLFNBQVMsRUFBRTtDQUNqQixNQUFNLFFBQVEsRUFBRTtDQUNoQixJQUFJLFFBQVE7QUFDWixNQUFLLElBQUksSUFBSSxHQUFHLElBQUksRUFBRSxRQUFRLElBQzdCLEtBQUksRUFBRSxPQUFPLE9BQU8sRUFBRSxPQUFPLElBQzVCLE9BQU0sS0FBSyxFQUFFLE9BQU8sTUFBTSxNQUFNLElBQUk7VUFDMUIsRUFBRSxPQUFPLE1BQU0sR0FBRyxHQUFHLENBQy9CLE9BQU0sS0FBSztVQUNELENBQUMsTUFBTSxVQUFVLEVBQUUsT0FBTyxLQUFLO0VBQ3pDLE1BQU0sUUFBUSxFQUFFLFVBQVUsT0FBTyxFQUFFLENBQUMsTUFBTTtBQUMxQyxNQUFJLE1BQ0gsUUFBTyxLQUFLLE1BQU07QUFFbkIsVUFBUSxJQUFJOztDQUdkLE1BQU0sWUFBWSxFQUFFLFVBQVUsTUFBTSxDQUFDLE1BQU07QUFDM0MsS0FBSSxVQUNILFFBQU8sS0FBSyxVQUFVO0FBRXZCLFFBQU87O0FBR1IsSUFBSTtBQUNKLFNBQVMsZUFBZSxNQUFNO0FBQzdCLFNBQVE7O0FBRVQsU0FBUyxpQkFBaUI7QUFDekIsUUFBTzs7QUFFUixNQUFNLFFBQVEsRUFBRTtBQUNoQixTQUFTLGVBQWUsTUFBTTtBQUM3QixPQUFNLEtBQUssS0FBSztBQUNoQixjQUFhO0FBQ1osUUFBTSxPQUFPLE1BQU0sUUFBUSxLQUFLLENBQUM7OztBQUduQyxTQUFTLGtCQUFrQjtBQUMxQixRQUFPOztBQUdSLE1BQU0sb0JBQW9CLE9BQU8sb0JBQW9CO0FBQ3JELFNBQVMsb0JBQW9CLFdBQVc7QUFDdkMsUUFBTyxZQUFZOztBQUVwQixTQUFTLGdCQUFnQixNQUFNLElBQUksU0FBUztDQUMzQyxTQUFTLE9BQU8sU0FBUztFQUN4QixNQUFNLFFBQVEsU0FBUyxHQUFHLE1BQU07QUFDL0IsVUFBTyxHQUFHLE1BQU0sU0FBUyxLQUFLOztBQUUvQixTQUFPLE9BQU8sT0FBTyxHQUFHO0FBQ3hCLFNBQU8sZUFBZSxPQUFPLG1CQUFtQjtHQUMvQyxPQUFPO0lBQ04sbUJBQW1CLE1BQU0sS0FBSyxRQUFRO0lBQ3RDLG1CQUFtQixRQUFRO0lBQzNCLGFBQWEsS0FBSyxVQUFVO0FBQzNCLGFBQVEsT0FBTzs7SUFFaEIsZUFBZSxRQUFRO0FBQ3RCLFlBQU8sT0FBTyxTQUFTLElBQUk7O0lBRTVCO0dBQ0QsWUFBWTtHQUNaLENBQUM7QUFDRixPQUFLLE1BQU0sT0FBTyxLQUNqQixRQUFPLGVBQWUsT0FBTyxLQUFLLEVBQUUsTUFBTTtBQUN6QyxVQUFPLE9BQU87SUFDYixHQUFHO0tBQ0YsTUFBTTtJQUNQLENBQUM7S0FDQSxDQUFDO0FBRUwsU0FBTzs7Q0FFUixNQUFNLFFBQVEsT0FBTyxXQUFXLEVBQUUsQ0FBQztBQUNuQyxRQUFPLGVBQWUsT0FBTyxNQUFNO0VBQ2xDLE9BQU87RUFDUCxZQUFZO0VBQ1osQ0FBQztBQUNGLFFBQU87O0FBR1IsU0FBUyx3QkFBd0I7QUFDaEMsUUFBTyxXQUFXLENBQUMsT0FBTzs7QUFFM0IsTUFBTSxzQkFBc0IsT0FBTyxJQUFJLHlCQUF5QjtBQUNoRSxNQUFNLDBCQUEwQixPQUFPLElBQUksNkJBQTZCO0FBQ3hFLE1BQU0scUJBQXFCLE9BQU8sSUFBSSx3QkFBd0I7QUFDOUQsTUFBTSx5QkFBeUIsT0FBTyxJQUFJLDRCQUE0QjtBQUN0RSxTQUFTLDZCQUE2QixNQUFNLFFBQVEsU0FBUztBQUM1RCxLQUFJLE9BQU8sV0FBVyxXQUdyQixRQUFPLFlBQVksUUFGSCx1QkFBdUIsUUFBUSxPQUFPLEtBQUsseUJBQXlCLFdBQVcsS0FBSyx1QkFBdUIsdUJBQXVCLEVBRTlHLE1BRFosMkJBQTJCLFFBQVEsS0FBSyxvQ0FBb0MsUUFBUSxLQUFLLDJCQUEyQixTQUNoRixHQUFHLFVBQVU7QUFDeEUsTUFBSSxRQUNILG9CQUFtQixTQUFTLE1BQU07R0FFbEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFvQkosU0FBUyxVQUFVLElBQUksVUFBVSx1QkFBdUIsRUFBRTtBQUN6RCxhQUFZLElBQUksMEJBQTBCLENBQUMsV0FBVyxDQUFDO0NBQ3ZELE1BQU0sa0NBQWtCLElBQUksTUFBTSxvQkFBb0I7Q0FDdEQsTUFBTSxVQUFVLG9CQUFvQixLQUFLO0FBQ3pDLFFBQU8saUJBQWlCLENBQUMsR0FBRyxhQUFhLE9BQU8sT0FBTyxZQUFZLGtCQUFrQixhQUFhLElBQUksU0FBUyxnQkFBZ0IsRUFBRSxTQUFTLE1BQU0sZ0JBQWdCLEVBQUU7R0FDaEssc0JBQXNCO0dBQ3RCLDBCQUEwQjtFQUMzQixDQUFDLENBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFtQkosU0FBUyxTQUFTLElBQUksU0FBUztBQUM5QixhQUFZLElBQUkseUJBQXlCLENBQUMsV0FBVyxDQUFDO0NBQ3RELE1BQU0sVUFBVSxvQkFBb0IsS0FBSztDQUN6QyxNQUFNLGtDQUFrQixJQUFJLE1BQU0sb0JBQW9CO0FBQ3RELFFBQU8saUJBQWlCLENBQUMsR0FBRyxZQUFZLFlBQVksa0JBQWtCLFlBQVksSUFBSSxTQUFTLGdCQUFnQixFQUFFLFdBQVcsdUJBQXVCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW1CN0ssU0FBUyxXQUFXLElBQUksVUFBVSx1QkFBdUIsRUFBRTtBQUMxRCxhQUFZLElBQUksMkJBQTJCLENBQUMsV0FBVyxDQUFDO0NBQ3hELE1BQU0sa0NBQWtCLElBQUksTUFBTSxvQkFBb0I7Q0FDdEQsTUFBTSxXQUFXLFNBQVMsVUFBVTtBQUVuQyxTQUR3QixhQUFhLElBQUksRUFBRSxPQUFPLENBQUMsQ0FDNUIsUUFBUTs7QUFFaEMsUUFBTyxpQkFBaUIsQ0FBQyxHQUFHLGNBQWMsT0FBTyxPQUFPLFlBQVksU0FBUyxXQUFXLHVCQUF1QixFQUFFLE1BQU0saUJBQWlCLGVBQWUsRUFBRTtHQUN2SixzQkFBc0I7R0FDdEIsMEJBQTBCO0VBQzNCLENBQUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW1CSixTQUFTLFVBQVUsSUFBSSxTQUFTO0FBQy9CLGFBQVksSUFBSSwwQkFBMEIsQ0FBQyxXQUFXLENBQUM7Q0FDdkQsTUFBTSxXQUFXLFNBQVMsVUFBVTtBQUVuQyxTQUR3QixhQUFhLElBQUksRUFBRSxPQUFPLENBQUMsQ0FDNUIsUUFBUTs7QUFFaEMsUUFBTyxpQkFBaUIsQ0FBQyxHQUFHLGFBQWEsWUFBWSxTQUFTLFdBQVcsdUJBQXVCLEVBQUUsc0JBQU0sSUFBSSxNQUFNLG9CQUFvQixFQUFFLGVBQWUsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFvQnpKLE1BQU0sZUFBZSxlQUFlLGlCQUFpQixNQUFNLFNBQVMsWUFBWTtBQUMvRSxNQUFLLGFBQWEsRUFBRTtBQUNwQixNQUFLLFNBQVMsS0FBSyxZQUFZLFNBQVMsV0FBVyx1QkFBdUIsRUFBRSxzQkFBTSxJQUFJLE1BQU0sb0JBQW9CLEVBQUUsZUFBZSxDQUFDO0VBQ2pJOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUF3QkYsTUFBTSxpQkFBaUIsZUFBZSxtQkFBbUIsTUFBTSxTQUFTLFlBQVk7QUFDbkYsTUFBSyxlQUFlLEVBQUU7QUFDdEIsTUFBSyxXQUFXLEtBQUssWUFBWSxTQUFTLFdBQVcsdUJBQXVCLEVBQUUsc0JBQU0sSUFBSSxNQUFNLG9CQUFvQixFQUFFLGVBQWUsQ0FBQztFQUNuSTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTRCRixTQUFTLFVBQVUsSUFBSSxTQUFTO0FBQy9CLGFBQVksSUFBSSwwQkFBMEIsQ0FBQyxXQUFXLENBQUM7Q0FDdkQsTUFBTSxrQ0FBa0IsSUFBSSxNQUFNLG9CQUFvQjtDQUN0RCxNQUFNLGtCQUFrQixXQUFXLHVCQUF1QjtDQUMxRCxNQUFNLFVBQVUsb0JBQW9CLEtBQUs7QUFDekMsUUFBTyxpQkFBaUIsQ0FBQyxHQUFHLGFBQWEsT0FBTyxPQUFPLGtCQUFrQixhQUFhLElBQUksU0FBUyxpQkFBaUIsRUFBRSxFQUFFO0dBQ3RILHFCQUFxQjtHQUNyQix5QkFBeUI7RUFDMUIsQ0FBQyxDQUFDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBNEJKLFNBQVMsV0FBVyxJQUFJLFNBQVM7QUFDaEMsYUFBWSxJQUFJLDJCQUEyQixDQUFDLFdBQVcsQ0FBQztDQUN4RCxNQUFNLGtDQUFrQixJQUFJLE1BQU0sb0JBQW9CO0NBQ3RELE1BQU0sa0JBQWtCLFdBQVcsdUJBQXVCO0NBQzFELE1BQU0sV0FBVyxTQUFTLFNBQVMsVUFBVTtFQUM1QyxNQUFNLFdBQVcsUUFBUSxHQUFHLFNBQVMsS0FBSyxNQUFNO0FBQ2hELGlCQUFlLFNBQVM7R0FDdkIsT0FBTztHQUNQLFVBQVU7R0FDVixDQUFDO0FBRUYsU0FEd0IsYUFBYSxTQUFTLEVBQUUsT0FBTyxDQUFDLENBQ2pDLFFBQVE7O0FBRWhDLFFBQU8saUJBQWlCLENBQUMsR0FBRyxjQUFjLE9BQU8sT0FBTyxTQUFTO0dBQy9ELHFCQUFxQjtHQUNyQix5QkFBeUI7RUFDMUIsQ0FBQyxDQUFDOztBQUVKLFNBQVMsa0JBQWtCLFdBQVcsSUFBSSxTQUFTLGlCQUFpQixlQUFlLEdBQUc7QUFDckYsU0FBUSxHQUFHLFNBQVM7RUFDbkIsTUFBTSxRQUFRLEtBQUssR0FBRyxHQUFHO0VBQ3pCLE1BQU0sU0FBUyxLQUFLLE1BQU0sR0FBRyxHQUFHO0VBQ2hDLE1BQU0sV0FBVyxRQUFRLEdBQUcsR0FBRyxRQUFRLEtBQUssTUFBTTtBQUNsRCxpQkFBZSxTQUFTO0dBQ3ZCLE9BQU87R0FDUCxVQUFVO0dBQ1YsQ0FBQztFQUNGLE1BQU0sV0FBVyxTQUFTLGFBQWE7RUFDdkMsTUFBTSxjQUFjLFVBQVUsZUFBZSxNQUFNLEtBQUs7QUFPeEQsU0FOaUIsYUFBYSxTQUFTO0dBQ3RDO0dBQ0E7R0FDQSxTQUFTO0dBQ1Q7R0FDQSxDQUFDLEVBQ2U7OztBQUduQixTQUFTLHFCQUFxQixNQUFNO0FBQ25DLFFBQU8sc0JBQXNCLFFBQVEsT0FBTyxLQUFLLHdCQUF3QixXQUFXLEtBQUssc0JBQXNCLHVCQUF1Qjs7QUFFdkksU0FBUyx3QkFBd0IsTUFBTTtBQUN0QyxRQUFPLDBCQUEwQixRQUFRLEtBQUssbUNBQW1DLFFBQVEsS0FBSywwQkFBMEI7O0FBRXpILFNBQVMsZUFBZSxNQUFNLFNBQVM7QUFDdEMsU0FBUSxJQUFJLFlBQVk7QUFDdkIsY0FBWSxJQUFJLElBQUksS0FBSyxhQUFhLENBQUMsV0FBVyxDQUFDO0VBQ25ELE1BQU0sVUFBVSxnQkFBZ0I7QUFDaEMsTUFBSSxDQUFDLFFBQ0osT0FBTSxJQUFJLE1BQU0sUUFBUSxLQUFLLHFDQUFxQztBQUVuRSxTQUFPLFFBQVEsU0FBUyxJQUFJLFFBQVE7Ozs7OztBQU90QyxTQUFTLG1CQUFtQixNQUFNLGFBQWEsZUFBZSxTQUFTLGdCQUFnQixVQUFVLGNBQWMsV0FBVztDQUN6SCxNQUFNLG1CQUFtQixFQUFFO0NBQzNCLE1BQU0saUJBQWlCLE9BQU8sY0FBYyw4QkFBOEI7RUFDekUsTUFBTSxjQUFjLGdCQUFnQixNQUFNLFNBQVM7RUFFbkQsTUFBTSxtQkFBbUIsWUFBWSxNQUFNLE1BQU0sTUFBTSxNQUFNLEVBQUUsU0FBUyxVQUFVLEVBQUUsU0FBUyxXQUFXLGlCQUFpQixFQUFFLENBQUM7QUFDNUgsUUFBTSxNQUFNLFNBQVMsTUFBTTtHQUcxQixNQUFNLGNBQWMsbUJBQW1CLEVBQUUsU0FBUyxVQUFVLEVBQUUsU0FBUyxXQUFXLGlCQUFpQixFQUFFLEdBQUcsZUFBZSxFQUFFLFNBQVM7QUFDbEksT0FBSSxVQUNIO1FBQUksRUFBRSxTQUFTLFlBQVksZUFBZSxpQkFBaUIsRUFBRSxHQUU1RDtTQUFJLEVBQUUsU0FBUyxRQUFRO0FBQ3RCLHFCQUFlLEdBQUcsVUFBVTtBQUM1QixRQUFFLE9BQU87O2VBRUEsRUFBRSxTQUFTLFNBQVMsQ0FBQyxZQUMvQixHQUFFLE9BQU87YUFDQyxFQUFFLFNBQVMsUUFBUTtBQUM3QixvQkFBZSxHQUFHLFVBQVU7QUFDNUIsT0FBRSxPQUFPOzs7R0FHWCxJQUFJLG1CQUFtQjtBQUl2QixPQUFJLGtCQUFrQixVQUFhLGNBQWMsV0FBVyxHQUMzRDtRQUFJLEVBQUUsWUFBWSxlQUFlLFNBQVMsRUFBRSxTQUFTLEtBQUssRUFBRTtBQUMzRCxPQUFFLE9BQU87QUFDVCxzQkFBaUIsS0FBSyxFQUFFLFNBQVMsS0FBSztBQUN0Qyx3QkFBbUI7ZUFDVCwwQkFDVixHQUFFLE9BQU87YUFDQyxFQUFFLFNBQVMsT0FDckIsR0FBRSxPQUFPOztBQUdYLE9BQUksRUFBRSxTQUFTLFFBQVE7QUFDdEIsUUFBSSxlQUFlLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxNQUFNLFlBQVksQ0FDeEQsR0FBRSxPQUFPO0FBRVYsUUFBSSxXQUFXLENBQUMsUUFBUSxTQUFTLEVBQUUsR0FBRyxDQUNyQyxHQUFFLE9BQU87QUFFVixRQUFJLGtCQUFrQixDQUFDLGVBQWUsRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUNsRCxHQUFFLE9BQU87Y0FFQSxFQUFFLFNBQVMsUUFDckIsS0FBSSxFQUFFLFNBQVMsT0FDZCxjQUFhLEVBQUU7WUFDTCxFQUFFLFNBQVMsT0FDckIsY0FBYSxFQUFFO09BRWYsZUFBYyxHQUFHLGFBQWEsaUJBQWlCO0lBR2hEO0FBRUYsTUFBSSxNQUFNLFNBQVMsU0FBUyxNQUFNLFNBQVMsVUFDMUM7T0FBSSxNQUFNLE1BQU0sVUFBVSxNQUFNLE1BQU0sT0FBTyxNQUFNLEVBQUUsU0FBUyxTQUFTLEVBQUUsU0FBUyxTQUFTLENBQzFGLE9BQU0sT0FBTzs7O0FBSWhCLGVBQWMsTUFBTSxjQUFjLE1BQU07Q0FDeEMsTUFBTSxjQUFjLGVBQWUsUUFBUSxRQUFRLENBQUMsaUJBQWlCLFNBQVMsSUFBSSxDQUFDO0FBQ25GLEtBQUksZUFBZSxZQUFZLFdBQVcsR0FBRztFQUM1QyxNQUFNLFVBQVUsWUFBWSxXQUFXLElBQUksUUFBUSxZQUFZLE9BQU8sU0FBUyxZQUFZLEtBQUssS0FBSztBQUNyRyxNQUFJLEtBQUssV0FBVyxPQUNuQixNQUFLLFNBQVM7R0FDYixPQUFPO0dBQ1AsUUFBUSxFQUFFO0dBQ1Y7QUFFRixNQUFJLEtBQUssT0FBTyxXQUFXLE9BQzFCLE1BQUssT0FBTyxTQUFTLEVBQUU7QUFFeEIsT0FBSyxPQUFPLE9BQU8sS0FBSyw2QkFBYSxJQUFJLE1BQU0sb0JBQW9CLEtBQUssS0FBSyxNQUFNLFVBQVUsQ0FBQyxDQUFDOzs7QUFHakcsU0FBUyxnQkFBZ0IsTUFBTTtBQUM5QixRQUFPLEdBQUcsS0FBSyxRQUFRLEdBQUcsZ0JBQWdCLEtBQUssTUFBTSxDQUFDLEtBQUssS0FBSyxLQUFLOztBQUV0RSxTQUFTLGlCQUFpQixPQUFPO0FBQ2hDLFFBQU8sTUFBTSxNQUFNLE1BQU0sTUFBTSxFQUFFLFNBQVMsVUFBVSxFQUFFLFNBQVMsV0FBVyxpQkFBaUIsRUFBRSxDQUFDOztBQUUvRixTQUFTLGFBQWEsT0FBTztBQUM1QixPQUFNLE1BQU0sU0FBUyxNQUFNO0FBQzFCLE1BQUksRUFBRSxTQUFTLFNBQVMsRUFBRSxTQUFTLFVBQVU7QUFDNUMsS0FBRSxPQUFPO0FBQ1QsT0FBSSxFQUFFLFNBQVMsUUFDZCxjQUFhLEVBQUU7O0dBR2hCOztBQUVILFNBQVMsYUFBYSxPQUFPO0FBQzVCLE9BQU0sTUFBTSxTQUFTLE1BQU07QUFDMUIsTUFBSSxFQUFFLFNBQVMsU0FBUyxFQUFFLFNBQVMsVUFBVTtBQUM1QyxLQUFFLE9BQU87QUFDVCxPQUFJLEVBQUUsU0FBUyxRQUNkLGNBQWEsRUFBRTs7R0FHaEI7O0FBRUgsU0FBUyxlQUFlLE1BQU0sV0FBVztBQUN4QyxLQUFJLFVBQ0g7QUFHRCxNQUFLLFNBQVM7RUFDYixPQUFPO0VBQ1AsUUFBUSxDQUhLLDZCQUFhLElBQUksTUFBTSxrR0FBa0csQ0FBQyxDQUd4SDtFQUNmOzs7QUFHRixTQUFTLGFBQWEsS0FBSztDQUMxQixJQUFJLE9BQU87QUFDWCxLQUFJLElBQUksV0FBVyxFQUNsQixRQUFPLEdBQUc7QUFFWCxNQUFLLElBQUksSUFBSSxHQUFHLElBQUksSUFBSSxRQUFRLEtBQUs7RUFDcEMsTUFBTSxPQUFPLElBQUksV0FBVyxFQUFFO0FBQzlCLFVBQVEsUUFBUSxLQUFLLE9BQU87QUFDNUIsU0FBTyxPQUFPOztBQUVmLFFBQU8sR0FBRzs7QUFFWCxTQUFTLG1CQUFtQixRQUFRO0FBQ25DLFFBQU8sTUFBTSxTQUFTLEdBQUcsUUFBUTtBQUNoQyxJQUFFLEtBQUssR0FBRyxPQUFPLEdBQUcsR0FBRztBQUN2QixNQUFJLEVBQUUsU0FBUyxRQUNkLG9CQUFtQixFQUFFO0dBRXJCOztBQUVILFNBQVMsZUFBZSxVQUFVLE1BQU0sYUFBYSxNQUFNLGlCQUFpQjtDQUMzRSxNQUFNLE9BQU8sU0FBUyxNQUFNLFNBQVM7Q0FDckMsTUFBTSxPQUFPO0VBQ1osSUFBSSxpQ0FBaUIsTUFBTSxZQUFZO0VBQ3ZDLE1BQU07RUFDTixVQUFVO0VBQ1YsTUFBTTtFQUNOLE1BQU07RUFDTjtFQUNBLE9BQU8sRUFBRTtFQUNULE1BQU0sT0FBTyxPQUFPLEtBQUs7RUFDekI7RUFDQSxNQUFNO0VBQ047RUFDQTtFQUNBO0FBQ0QsTUFBSyxPQUFPO0FBQ1osUUFBTzs7Ozs7Ozs7QUFRUixTQUFTLGlCQUFpQixNQUFNLGFBQWE7QUFDNUMsUUFBdUIsNkJBQWEsR0FBRyxPQUFPLGVBQWUsS0FBSzs7QUFFbkUsU0FBUyx1QkFBdUIsY0FBYyxPQUFPO0NBRXBELE1BQU0sUUFBUSxNQUFNLE1BQU0sS0FBSyxDQUFDLE1BQU0sRUFBRTtBQUN4QyxNQUFLLE1BQU0sUUFBUSxPQUFPO0VBQ3pCLE1BQU0sUUFBUSxpQkFBaUIsS0FBSztBQUNwQyxNQUFJLFNBQVMsTUFBTSxTQUFTLGFBQzNCLFFBQU87Ozs7OztBQVFWLFNBQVMsaUJBQWlCLGNBQWMsVUFBVTtDQUVqRCxJQUFJLFFBQVE7Q0FJWixJQUFJO0NBQ0osSUFBSTtDQUVKLE1BQU0sZUFBZTtBQUNwQjtBQUVBLE1BQUksTUFBTTtBQUVULFFBQUssSUFBSTtBQUNULFVBQU8sS0FBSztBQUdaLFVBQU8sUUFBUTs7O0NBR2pCLE1BQU0sZ0JBQWdCO0VBQ3JCLElBQUksV0FBVztFQUNmLE1BQU0sZ0JBQWdCO0FBQ3JCLE9BQUksQ0FBQyxVQUFVO0FBQ2QsZUFBVztBQUNYLFlBQVE7OztBQUdWLE1BQUksVUFBVSxZQUNiLFFBQU87QUFFUixTQUFPLElBQUksU0FBUyxZQUFZO0FBQy9CLE9BQUksS0FFSCxRQUFPLEtBQUssS0FBSyxPQUFPLFFBQVEsUUFBUSxDQUFDO09BR3pDLFFBQU8sT0FBTyxPQUFPLFFBQVEsUUFBUSxDQUFDO0lBRXRDOztDQUVILE1BQU0sYUFBYSxNQUFNLEdBQUcsU0FBUztFQUNwQyxTQUFTLElBQUksU0FBUztBQUNyQixPQUFJO0lBQ0gsTUFBTSxTQUFTLEtBQUssR0FBRyxLQUFLO0FBQzVCLFFBQUksa0JBQWtCLFFBQ3JCLFFBQU8sT0FBTyxRQUFRLFFBQVE7QUFFL0IsYUFBUztBQUNULFdBQU8sUUFBUSxRQUFRLE9BQU87WUFDdEIsT0FBTztBQUNmLGFBQVM7QUFDVCxXQUFPLFFBQVEsT0FBTyxNQUFNOzs7RUFHOUIsTUFBTSxVQUFVLFNBQVM7QUFDekIsU0FBTyxtQkFBbUIsVUFBVSxRQUFRLEtBQUssSUFBSSxHQUFHLElBQUksUUFBUTs7QUFFckUsUUFBTyxPQUFPLE9BQU8sV0FBVyxFQUFFLFNBQVMsQ0FBQzs7Ozs7QUFNN0MsU0FBUyx1QkFBdUIsT0FBTztDQUN0QyxJQUFJLGFBQWEsRUFBRTtDQUNuQixNQUFNLGNBQWMsRUFBRTtBQUN0QixNQUFLLE1BQU0sS0FBSyxNQUFNLE1BQ3JCLEtBQUksV0FBVyxXQUFXLEtBQUssRUFBRSxlQUFlLFdBQVcsR0FBRyxXQUM3RCxZQUFXLEtBQUssRUFBRTtNQUNaO0FBQ04sY0FBWSxLQUFLLFdBQVc7QUFDNUIsZUFBYSxDQUFDLEVBQUU7O0FBR2xCLEtBQUksV0FBVyxTQUFTLEVBQ3ZCLGFBQVksS0FBSyxXQUFXO0FBRTdCLFFBQU87O0FBR1IsTUFBTSw0QkFBWSxJQUFJLFNBQVM7Ozs7QUFJL0IsU0FBUyxZQUFZLFVBQVU7Q0FDOUIsTUFBTSxTQUFTLFdBQVc7Q0FDMUIsTUFBTSxhQUFhLE9BQU8sdUJBQXVCLGtCQUFrQixPQUFPLE9BQU87QUFDakYsS0FBSSxDQUFDLFdBQ0osUUFBTztDQUVSLElBQUksc0JBQXNCLFVBQVUsSUFBSSxXQUFXO0FBQ25ELEtBQUksQ0FBQyxxQkFBcUI7QUFDekIsd0JBQXNCLGlCQUFpQixZQUFZLE9BQU8sT0FBTyxLQUFLO0FBQ3RFLFlBQVUsSUFBSSxZQUFZLG9CQUFvQjs7QUFFL0MsUUFBTyxvQkFBb0IsU0FBUzs7QUFFckMsU0FBUyxhQUFhLFFBQVEsTUFBTTtBQUNuQyxLQUFJLENBQUMsT0FBTyxXQUNYO0NBRUQsTUFBTSxnQkFBZ0IsSUFBSSxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLENBQUM7QUFDakUsTUFBSyxNQUFNLE9BQU8sS0FDakIsS0FBSSxDQUFDLGNBQWMsSUFBSSxJQUFJLENBQzFCLE9BQU0sa0JBQWtCLE9BQU8sTUFBTSxJQUFJOztBQUk1QyxTQUFTLGtCQUFrQixlQUFlLEtBQUssU0FBUyxPQUFPO0FBQzlELEtBQUksQ0FBQyxjQUFjLE9BQ2xCLE9BQU0sSUFBSSxNQUFNLDZEQUE2RCxJQUFJLElBQUksT0FBTyx5REFBeUQ7QUFFdEosT0FBTSxJQUFJLE1BQU0sT0FBTyxPQUFPLElBQUksSUFBSSw4REFBOEQsY0FBYyxLQUFLLE1BQU0sS0FBSyxFQUFFLE9BQU8sRUFBRSxjQUFjLEtBQUssRUFBRSxnQkFBZ0IsS0FBSyxDQUFDLEtBQUssS0FBSyxHQUFHOztBQUV0TSxTQUFTLGlCQUFpQixVQUFVLGVBQWU7Q0FDbEQsTUFBTSxXQUFXLFNBQVMsS0FBSyxTQUFTLG9CQUFvQixNQUFNLGNBQWMsQ0FBQztBQUNqRixTQUFRLGFBQWE7QUFDcEIsU0FBTyxTQUFTLE9BQU8sWUFBWSxRQUFRLFNBQVMsQ0FBQzs7O0FBR3ZELFNBQVMsb0JBQW9CLE1BQU0sZUFBZTtDQUVqRCxNQUFNLFNBQVMsSUFBSSxZQURKLFNBQVMsS0FBSyxFQUNVLEtBQUs7Q0FDNUMsTUFBTSxNQUFNLGtCQUFrQixRQUFRLGNBQWM7QUFDcEQsS0FBSSxPQUFPLE1BQU0sQ0FBQyxTQUFTLE1BQzFCLE9BQU0sSUFBSSxNQUFNLHdDQUF3QyxZQUFZLE9BQU8sTUFBTSxDQUFDLENBQUMsUUFBUSxLQUFLLEdBQUc7QUFFcEcsU0FBUSxTQUFTLGFBQWEsS0FBSyxLQUFLOztBQUV6QyxTQUFTLFlBQVksT0FBTztBQUMzQixTQUFRLE1BQU0sTUFBZDtFQUNDLEtBQUssTUFBTyxRQUFPLE1BQU07RUFDekIsUUFBUyxRQUFPLGdCQUFnQixNQUFNLEtBQUs7OztBQUc3QyxTQUFTLFNBQVMsTUFBTTtDQUN2QixNQUFNLFNBQVMsRUFBRTtDQUNqQixJQUFJLElBQUk7QUFDUixRQUFPLElBQUksS0FBSyxRQUFRO0FBQ3ZCLE1BQUksS0FBSyxPQUFPLE9BQU8sS0FBSyxPQUFPLEtBQUs7QUFDdkM7QUFDQTs7QUFFRCxNQUFJLEtBQUssT0FBTyxLQUFLO0FBQ3BCLFVBQU8sS0FBSyxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQy9CO0FBQ0E7O0FBRUQsTUFBSSxLQUFLLE9BQU8sS0FBSztBQUNwQixVQUFPLEtBQUssRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUMvQjtBQUNBOztBQUVELE1BQUksS0FBSyxPQUFPLEtBQUs7QUFDcEIsVUFBTyxLQUFLLEVBQUUsTUFBTSxPQUFPLENBQUM7QUFDNUI7QUFDQTs7QUFFRCxNQUFJLEtBQUssTUFBTSxHQUFHLElBQUksRUFBRSxLQUFLLE1BQU07QUFDbEMsVUFBTyxLQUFLLEVBQUUsTUFBTSxPQUFPLENBQUM7QUFDNUIsUUFBSztBQUNMOztBQUVELE1BQUksS0FBSyxNQUFNLEdBQUcsSUFBSSxFQUFFLEtBQUssTUFBTTtBQUNsQyxVQUFPLEtBQUssRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMzQixRQUFLO0FBQ0w7O0FBRUQsTUFBSSxtQkFBbUIsS0FBSyxLQUFLLE1BQU0sRUFBRSxDQUFDLEVBQUU7QUFDM0MsVUFBTyxLQUFLLEVBQUUsTUFBTSxPQUFPLENBQUM7QUFDNUIsUUFBSztBQUNMOztBQUVELE1BQUksa0JBQWtCLEtBQUssS0FBSyxNQUFNLEVBQUUsQ0FBQyxFQUFFO0FBQzFDLFVBQU8sS0FBSyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzNCLFFBQUs7QUFDTDs7QUFFRCxNQUFJLFVBQVUsS0FBSyxLQUFLLE1BQU0sRUFBRSxDQUFDLEVBQUU7QUFDbEMsVUFBTyxLQUFLLEVBQUUsTUFBTSxPQUFPLENBQUM7QUFDNUIsUUFBSztBQUNMOztFQUVELElBQUksTUFBTTtBQUNWLFNBQU8sSUFBSSxLQUFLLFVBQVUsS0FBSyxPQUFPLE9BQU8sS0FBSyxPQUFPLE9BQU8sS0FBSyxPQUFPLE9BQU8sS0FBSyxPQUFPLE9BQU8sS0FBSyxPQUFPLE9BQU8sS0FBSyxPQUFPLE9BQU8sS0FBSyxPQUFPLEtBQUs7R0FDNUosTUFBTSxZQUFZLEtBQUssTUFBTSxFQUFFO0FBRy9CLE9BQUksUUFBUSxPQUFPLG1CQUFtQixLQUFLLFVBQVUsSUFBSSxrQkFBa0IsS0FBSyxVQUFVLElBQUksVUFBVSxLQUFLLFVBQVUsRUFDdEg7QUFFRCxVQUFPLEtBQUs7QUFDWjs7QUFFRCxNQUFJLElBQ0gsUUFBTyxLQUFLO0dBQ1gsTUFBTTtHQUNOLE9BQU87R0FDUCxDQUFDOztBQUdKLFFBQU8sS0FBSyxFQUFFLE1BQU0sT0FBTyxDQUFDO0FBQzVCLFFBQU87O0FBRVIsSUFBTSxjQUFOLE1BQWtCO0NBQ2pCLE1BQU07Q0FDTixZQUFZLFFBQVEsTUFBTTtBQUN6QixPQUFLLFNBQVM7QUFDZCxPQUFLLE9BQU87O0NBRWIsT0FBTztBQUNOLFNBQU8sS0FBSyxPQUFPLEtBQUs7O0NBRXpCLE9BQU87QUFDTixTQUFPLEtBQUssT0FBTyxLQUFLOztDQUV6QixPQUFPLE1BQU07RUFDWixNQUFNLFFBQVEsS0FBSyxNQUFNO0FBQ3pCLE1BQUksTUFBTSxTQUFTLE1BQU07QUFDeEIsT0FBSSxTQUFTLFlBQVksTUFBTSxTQUFTLE1BQ3ZDLE9BQU0sSUFBSSxNQUFNLG9EQUFvRCxLQUFLLEtBQUssR0FBRztBQUVsRixTQUFNLElBQUksTUFBTSxzQ0FBc0MsZ0JBQWdCLEtBQUssQ0FBQyxhQUFhLFlBQVksTUFBTSxDQUFDLFFBQVEsS0FBSyxLQUFLLEdBQUc7O0FBRWxJLFNBQU87O0NBRVIsa0JBQWtCO0VBQ2pCLE1BQU0sUUFBUSxLQUFLLE1BQU07QUFDekIsTUFBSSxNQUFNLFNBQVMsTUFDbEIsT0FBTSxJQUFJLE1BQU0sNkRBQTZELEtBQUssS0FBSyxHQUFHO0FBRTNGLFFBQU0sSUFBSSxNQUFNLHdDQUF3QyxZQUFZLE1BQU0sQ0FBQyxRQUFRLEtBQUssS0FBSyxHQUFHOzs7QUFHbEcsU0FBUyxnQkFBZ0IsTUFBTTtBQUM5QixTQUFRLE1BQVI7RUFDQyxLQUFLLE1BQU8sUUFBTztFQUNuQixLQUFLLE1BQU8sUUFBTztFQUNuQixLQUFLLEtBQU0sUUFBTztFQUNsQixLQUFLLE1BQU8sUUFBTztFQUNuQixLQUFLLFNBQVUsUUFBTztFQUN0QixLQUFLLFNBQVUsUUFBTztFQUN0QixLQUFLLE1BQU8sUUFBTzs7O0FBR3JCLFNBQVMsa0JBQWtCLFFBQVEsZUFBZTtDQUNqRCxJQUFJLE9BQU8sbUJBQW1CLFFBQVEsY0FBYztBQUNwRCxRQUFPLE9BQU8sTUFBTSxDQUFDLFNBQVMsTUFBTTtBQUNuQyxTQUFPLE1BQU07RUFDYixNQUFNLFFBQVEsbUJBQW1CLFFBQVEsY0FBYztBQUN2RCxTQUFPO0dBQ04sTUFBTTtHQUNOO0dBQ0E7R0FDQTs7QUFFRixRQUFPOztBQUVSLFNBQVMsbUJBQW1CLFFBQVEsZUFBZTtDQUNsRCxJQUFJLE9BQU8scUJBQXFCLFFBQVEsY0FBYztBQUN0RCxRQUFPLE9BQU8sTUFBTSxDQUFDLFNBQVMsT0FBTztBQUNwQyxTQUFPLE1BQU07RUFDYixNQUFNLFFBQVEscUJBQXFCLFFBQVEsY0FBYztBQUN6RCxTQUFPO0dBQ04sTUFBTTtHQUNOO0dBQ0E7R0FDQTs7QUFFRixRQUFPOztBQUVSLFNBQVMscUJBQXFCLFFBQVEsZUFBZTtBQUNwRCxLQUFJLE9BQU8sTUFBTSxDQUFDLFNBQVMsT0FBTztBQUNqQyxTQUFPLE1BQU07QUFFYixTQUFPO0dBQ04sTUFBTTtHQUNOLFNBSGUscUJBQXFCLFFBQVEsY0FBYztHQUkxRDs7QUFFRixRQUFPLHVCQUF1QixRQUFRLGNBQWM7O0FBRXJELFNBQVMsdUJBQXVCLFFBQVEsZUFBZTtDQUN0RCxNQUFNLFFBQVEsT0FBTyxNQUFNO0FBQzNCLEtBQUksTUFBTSxTQUFTLFVBQVU7QUFDNUIsU0FBTyxNQUFNO0VBQ2IsTUFBTSxPQUFPLGtCQUFrQixRQUFRLGNBQWM7QUFDckQsU0FBTyxPQUFPLFNBQVM7QUFDdkIsU0FBTzs7QUFFUixLQUFJLE1BQU0sU0FBUyxPQUFPO0FBQ3pCLFNBQU8sTUFBTTtFQUNiLE1BQU0sV0FBVyxNQUFNO0FBRXZCLFNBQU87R0FDTixNQUFNO0dBQ04sT0FBTztHQUNQLFNBSmUsa0JBQWtCLFVBQVUsY0FBYztHQUt6RDs7QUFFRixRQUFPLGlCQUFpQjs7QUFFekIsU0FBUyxvQkFBb0IsU0FBUztBQUNyQyxRQUFPLElBQUksT0FBTyxJQUFJLFFBQVEsUUFBUSxzQkFBc0IsT0FBTyxDQUFDLFFBQVEsT0FBTyxLQUFLLENBQUMsR0FBRzs7QUFFN0YsU0FBUyxrQkFBa0IsWUFBWSxlQUFlO0FBQ3JELEtBQUksV0FBVyxTQUFTLElBQUksRUFBRTtFQUM3QixNQUFNLFFBQVEsb0JBQW9CLFdBQVc7QUFFN0MsTUFBSSxDQURhLGNBQWMsTUFBTSxRQUFRLE1BQU0sS0FBSyxJQUFJLEtBQUssQ0FBQyxDQUVqRSxPQUFNLGtCQUFrQixlQUFlLFlBQVksY0FBYztBQUVsRSxTQUFPOztBQUVSLEtBQUksQ0FBQyxjQUFjLFVBQVUsQ0FBQyxjQUFjLE1BQU0sUUFBUSxJQUFJLFNBQVMsV0FBVyxDQUNqRixPQUFNLGtCQUFrQixlQUFlLFlBQVksY0FBYztBQUVsRSxRQUFPOztBQUVSLFNBQVMsYUFBYSxNQUFNLE1BQU07QUFDakMsU0FBUSxLQUFLLE1BQWI7RUFDQyxLQUFLO0FBQ0osT0FBSSxLQUFLLFFBQ1IsUUFBTyxLQUFLLE1BQU0sUUFBUSxLQUFLLFFBQVEsS0FBSyxJQUFJLENBQUM7QUFFbEQsVUFBTyxLQUFLLFNBQVMsS0FBSyxNQUFNO0VBQ2pDLEtBQUssTUFBTyxRQUFPLENBQUMsYUFBYSxLQUFLLFNBQVMsS0FBSztFQUNwRCxLQUFLLE1BQU8sUUFBTyxhQUFhLEtBQUssTUFBTSxLQUFLLElBQUksYUFBYSxLQUFLLE9BQU8sS0FBSztFQUNsRixLQUFLLEtBQU0sUUFBTyxhQUFhLEtBQUssTUFBTSxLQUFLLElBQUksYUFBYSxLQUFLLE9BQU8sS0FBSzs7O0FBSW5GLFNBQVMsV0FBVyxHQUFHO0FBQ3RCLFFBQU8sRUFBRSxTQUFTOztBQUVuQixTQUFTLFNBQVMsT0FBTztDQUN4QixNQUFNLFFBQVEsRUFBRTtDQUNoQixNQUFNLGNBQWMsUUFBUSxNQUFNO0FBQ2xDLE1BQUssTUFBTSxLQUFLLFlBQ2YsS0FBSSxXQUFXLEVBQUUsQ0FDaEIsT0FBTSxLQUFLLEVBQUU7S0FFYixNQUFLLE1BQU0sUUFBUSxFQUFFLE1BQ3BCLEtBQUksV0FBVyxLQUFLLENBQ25CLE9BQU0sS0FBSyxLQUFLO01BQ1Y7RUFDTixNQUFNLFlBQVksU0FBUyxLQUFLO0FBQ2hDLE9BQUssTUFBTSxRQUFRLFVBQ2xCLE9BQU0sS0FBSyxLQUFLOztBQU1yQixRQUFPOztBQUVSLFNBQVMsU0FBUyxRQUFRLEVBQUUsRUFBRTtBQUM3QixRQUFPLFFBQVEsTUFBTSxDQUFDLFNBQVMsTUFBTSxXQUFXLEVBQUUsR0FBRyxDQUFDLEVBQUUsR0FBRyxDQUFDLEdBQUcsR0FBRyxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7O0FBRXRGLFNBQVMsVUFBVSxPQUFPO0FBQ3pCLFFBQU8sUUFBUSxNQUFNLENBQUMsU0FBUyxNQUFNLEVBQUUsU0FBUyxVQUFVLENBQUMsR0FBRyxHQUFHLFVBQVUsRUFBRSxNQUFNLENBQUMsR0FBRyxFQUFFLENBQUM7O0FBRTNGLFNBQVMsU0FBUyxPQUFPO0FBQ3hCLFFBQU8sUUFBUSxNQUFNLENBQUMsTUFBTSxNQUFNLEVBQUUsTUFBTSxNQUFNLE1BQU0sV0FBVyxFQUFFLElBQUksU0FBUyxFQUFFLENBQUMsQ0FBQzs7QUFFckYsU0FBUyxVQUFVLE9BQU87QUFDekIsUUFBTyxRQUFRLE1BQU0sQ0FBQyxNQUFNLE1BQU0sRUFBRSxRQUFRLFVBQVUsVUFBVSxFQUFFLFNBQVMsV0FBVyxVQUFVLEVBQUUsTUFBTSxDQUFDOztBQUUxRyxTQUFTLFNBQVMsTUFBTTtDQUN2QixNQUFNLFFBQVEsQ0FBQyxLQUFLLEtBQUs7Q0FDekIsSUFBSSxVQUFVO0FBQ2QsUUFBTyxTQUFTLE9BQU87QUFDdEIsWUFBVSxRQUFRO0FBQ2xCLE1BQUksU0FBUyxLQUNaLE9BQU0sUUFBUSxRQUFRLEtBQUs7O0FBRzdCLEtBQUksWUFBWSxLQUFLLEtBQ3BCLE9BQU0sUUFBUSxLQUFLLEtBQUssS0FBSztBQUU5QixRQUFPOztBQUVSLFNBQVMsWUFBWSxNQUFNLFlBQVksT0FBTztBQUM3QyxRQUFPLFNBQVMsS0FBSyxDQUFDLEtBQUssVUFBVTs7QUFFdEMsU0FBUyxZQUFZLE1BQU0sWUFBWSxPQUFPO0FBQzdDLFFBQU8sU0FBUyxLQUFLLENBQUMsTUFBTSxFQUFFLENBQUMsS0FBSyxVQUFVOztBQUUvQyxTQUFTLGVBQWUsT0FBTyxZQUFZLE9BQU87QUFDakQsUUFBTyxNQUFNLFFBQVEsU0FBUyxTQUFTLE9BQVUsQ0FBQyxLQUFLLFVBQVU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXdDbEUsTUFBTSxRQUFRLGFBQWE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBdUIzQixNQUFNLE9BQU8sV0FBVyxTQUFTLE1BQU0sYUFBYSxlQUFlO0FBQ2xFLEtBQUksZ0JBQWdCLENBQ25CLE9BQU0sSUFBSSxNQUFNLHlKQUF5SjtBQUUxSyxrQkFBaUIsQ0FBQyxLQUFLLEdBQUcsS0FBSyxNQUFNLFdBQVcsS0FBSyxFQUFFLGFBQWEsY0FBYztFQUNqRjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFzQ0YsTUFBTSxXQUFXOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXVCakIsTUFBTSxLQUFLO0FBQ1gsSUFBSTtBQUNKLElBQUk7QUFDSixJQUFJO0FBQ0osU0FBUyxPQUFPLFdBQVcsU0FBUztBQUNuQyxLQUFJLENBQUMsVUFDSixPQUFNLElBQUksTUFBTSx5QkFBeUIsUUFBUTs7Ozs7RUFBb2I7O0FBR3ZlLFNBQVMsa0JBQWtCO0FBQzFCLFFBQU8sY0FBYyxvQkFBb0I7QUFDekMsUUFBTzs7QUFFUixTQUFTLFlBQVk7QUFDcEIsUUFBTyxRQUFRLGFBQWE7QUFDNUIsUUFBTzs7QUFFUixTQUFTLG1CQUFtQixRQUFRO0NBQ25DLE1BQU0sU0FBUyxPQUFPLE9BQU87Q0FDN0IsTUFBTSxVQUFVLEVBQUU7QUFDbEIsS0FBSSxPQUFPLGNBQWMsS0FDeEIsU0FBUSxhQUFhLE9BQU87Q0FFN0IsTUFBTSxZQUFZLE1BQU0sSUFBSSxlQUFlLEdBQUc7QUFFOUMsUUFBTyxVQUFVO0FBQ2pCLFFBQU87O0FBRVIsU0FBUyxzQkFBc0IsTUFBTSxlQUFlO0FBQ25ELHVCQUFzQixLQUFLO0FBQzNCLFVBQVM7QUFDVCxLQUFJLENBQUMsYUFDSixnQkFBZSxtQkFBbUIsY0FBYztBQUVqRCxjQUFhLE9BQU87QUFDcEIsa0JBQWlCLE1BQU0sU0FBUztBQUNoQyxjQUFhLE9BQU87QUFDcEIsa0JBQWlCLGVBQWU7O0FBRWpDLFNBQVMsa0JBQWtCO0NBQzFCLE1BQU0sZUFBZSxpQkFBaUIsZ0JBQWdCO0FBQ3RELFFBQU8sY0FBYyxvQkFBb0I7QUFDekMsUUFBTzs7QUFFUixTQUFTLG1CQUFtQjtBQUMzQixRQUFPO0VBQ04sV0FBVyxFQUFFO0VBQ2IsVUFBVSxFQUFFO0VBQ1osWUFBWSxFQUFFO0VBQ2QsV0FBVyxFQUFFO0VBQ2IsWUFBWSxFQUFFO0VBQ2QsV0FBVyxFQUFFO0VBQ2I7O0FBRUYsTUFBTSxvQkFBb0IsT0FBTztBQUNqQyxTQUFTLGVBQWUsYUFBYSxlQUFlO0FBQ25ELEtBQUksaUJBQWlCLFFBQVEsT0FBTyxrQkFBa0IsU0FDckQsT0FBTSxJQUFJLFVBQVUsZ0pBQWdKO0NBRXJLLElBQUksVUFBVSxFQUFFO0NBQ2hCLElBQUk7QUFFSixLQUFJLE9BQU8sa0JBQWtCLFNBQzVCLFdBQVUsRUFBRSxTQUFTLGVBQWU7VUFDMUIsT0FBTyxnQkFBZ0IsU0FDakMsV0FBVTtBQUVYLEtBQUksT0FBTyxnQkFBZ0IsWUFBWTtBQUN0QyxNQUFJLE9BQU8sa0JBQWtCLFdBQzVCLE9BQU0sSUFBSSxVQUFVLHFGQUFxRjtBQUUxRyxPQUFLO1lBQ0ssT0FBTyxrQkFBa0IsV0FDbkMsTUFBSztBQUVOLFFBQU87RUFDTjtFQUNBLFNBQVM7RUFDVDs7QUFHRixTQUFTLHFCQUFxQixNQUFNLGdCQUFnQixJQUFJLE1BQU0sTUFBTSxjQUFjO0NBQ2pGLE1BQU0sUUFBUSxFQUFFO0NBQ2hCLElBQUk7QUFDSixXQUFVLEtBQUs7Q0FDZixNQUFNLE9BQU8sU0FBUyxPQUFPLElBQUksVUFBVSxFQUFFLEVBQUU7RUFDOUMsTUFBTSxlQUFlLGlCQUFpQixjQUFjO0VBR3BELE1BQU0sV0FBVyxPQUFPLENBQUMsSUFGTixnQkFBZ0IsaUJBQWlCLGNBQWMsT0FDbkMsUUFBUSxFQUFFLEVBQ0QsR0FBRyxRQUFRLFFBQVEsS0FBSyxDQUFDLENBQUM7RUFDbEUsTUFBTSxjQUFjLFNBQVMsS0FBSyxRQUFRO0dBQ3pDLE1BQU0sZ0JBQWdCLE9BQU8sT0FBTyxNQUFNLE1BQU0sTUFBTSxFQUFFLFNBQVMsSUFBSTtBQUNyRSxPQUFJLENBQUMsaUJBQWlCLE9BQU8sT0FBTyxXQUNuQyxPQUFNLGtCQUFrQixPQUFPLE9BQU8sTUFBTSxJQUFJO0FBRWpELFVBQU87SUFDTixDQUFDLFFBQVEsTUFBTSxLQUFLLEtBQUssQ0FBQyxNQUFNLE1BQU0sVUFBVSxLQUFLLFlBQVksc0JBQXNCLEtBQUssWUFBWSxtQkFBbUIsQ0FBQyxRQUFRLEtBQUssUUFBUTtHQUNsSixNQUFNLEVBQUUsTUFBTSxhQUFhLFVBQVUsTUFBTSxHQUFHLFlBQVk7QUFDMUQsVUFBTyxPQUFPLEtBQUssUUFBUTtBQUMzQixPQUFJLEtBQ0gsS0FBSSxPQUFPLE9BQU8sT0FBTyxJQUFJLFFBQVEsT0FBTyxPQUFPLEtBQUssRUFBRSxLQUFLO0FBRWhFLFVBQU87S0FDTCxFQUFFLENBQUM7RUFDTixNQUFNLGNBQWMsUUFBUTtBQUM1QixZQUFVO0dBQ1QsR0FBRztHQUNILEdBQUc7R0FDSDtFQUNELE1BQU0sVUFBVSxRQUFRLFdBQVcsT0FBTyxPQUFPO0VBQ2pELE1BQU0sYUFBYSxjQUFjO0VBQ2pDLE1BQU0sVUFBVSxZQUFZO0VBQzVCLE1BQU0sV0FBVyxPQUFPLE9BQU8sS0FBSztBQUNwQyxNQUFJLFFBQ0gsUUFBTyxPQUFPLFVBQVUsUUFBUTtBQUVqQyxNQUFJLFdBQ0gsUUFBTyxPQUFPLFVBQVUsV0FBVztBQUVwQyxNQUFJLFlBQ0gsUUFBTyxPQUFPLFVBQVUsWUFBWTtFQUVyQyxNQUFNLE9BQU87R0FDWixJQUFJO0dBQ0o7R0FDQSxVQUFVLGVBQWUsQ0FBQyxjQUFjLFlBQVksaUJBQWlCLGNBQWMsTUFBTSxVQUFVLEtBQUssQ0FBQztHQUN6RyxjQUFjLGVBQWUsQ0FBQyxjQUFjLGNBQWMsS0FBSyxDQUFDO0dBQ2hFLE9BQU87R0FDUCxNQUFNLFFBQVE7R0FDZCxPQUFPLFFBQVE7R0FDZixTQUFTO0dBQ1QsTUFBTTtHQUNOLE1BQU0sY0FBYyxRQUFRLGlCQUFpQixjQUFjO0dBQzNEO0dBQ0EsT0FBTyxRQUFRLFNBQVMsT0FBTyxPQUFPO0dBQ3RDLFNBQVMsUUFBUTtHQUNqQixNQUFNLFFBQVEsT0FBTyxTQUFTLFFBQVEsT0FBTyxTQUFTLFFBQVEsT0FBTyxTQUFTO0dBQzlFLE1BQU07R0FDTixhQUFhLEVBQUU7R0FDZixXQUFXLEVBQUU7R0FDYixNQUFNO0dBQ047RUFDRCxNQUFNLFVBQVUsUUFBUTtBQUN4QixNQUFJLEtBQUssU0FBUyxTQUFTLENBQUMsUUFDM0IsTUFBSyxPQUFPO0FBRWIsTUFBSSxRQUFRLGNBQWMsQ0FBQyxRQUFRLGNBQWMsT0FBTyxPQUFPLFNBQVMsV0FDdkUsTUFBSyxhQUFhO0FBRW5CLE9BQUssVUFBVSxjQUFjO0VBQzdCLE1BQU0sVUFBVSxrQkFBa0IsTUFBTSxPQUFPO0FBRS9DLFNBQU8sZUFBZSxNQUFNLFdBQVc7R0FDdEMsT0FBTztHQUNQLFlBQVk7R0FDWixDQUFDO0FBQ0YsaUJBQWUsU0FBUyxRQUFRLFlBQVksSUFBSSxjQUFjLENBQUM7RUFDL0QsTUFBTSxRQUFRLE1BQU07QUFDcEIsUUFBTSxrQkFBa0I7RUFDeEIsTUFBTSxrQ0FBa0IsSUFBSSxNQUFNLG9CQUFvQjtBQUN0RCxRQUFNLGtCQUFrQjtBQUN4QixNQUFJLFFBQ0gsT0FBTSxNQUFNLFlBQVksV0FBVyx5QkFBeUIsYUFBYSxTQUFTLEVBQUUsU0FBUyxDQUFDLEVBQUUsS0FBSyxFQUFFLEtBQUssUUFBUSxPQUFPLEVBQUUsU0FBUyxPQUFPLGtCQUFrQixHQUFHLFVBQVUsZUFBZSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsQ0FBQztBQUUvTSxNQUFJLE9BQU8sT0FBTyxxQkFBcUI7R0FDdEMsTUFBTSxRQUFRLGdCQUFnQjtHQUM5QixNQUFNLFFBQVEsdUJBQXVCLHFCQUFxQixNQUFNO0FBQ2hFLE9BQUksTUFDSCxNQUFLLFdBQVc7SUFDZixNQUFNLE1BQU07SUFDWixRQUFRLE1BQU07SUFDZDs7QUFHSCxRQUFNLEtBQUssS0FBSztBQUNoQixTQUFPOztDQUVSLE1BQU0sT0FBTyxXQUFXLFNBQVMsTUFBTSxhQUFhLGVBQWU7RUFDbEUsSUFBSSxFQUFFLFNBQVMsWUFBWSxlQUFlLGFBQWEsY0FBYztBQUVyRSxNQUFJLE9BQU8saUJBQWlCLFNBQzNCLFdBQVUsT0FBTyxPQUFPLEVBQUUsRUFBRSxjQUFjLFFBQVE7RUFHbkQsTUFBTSxhQUFhLEtBQUssZUFBZSxDQUFDLEtBQUssY0FBYyxTQUFTO0FBQ3BFLE1BQUksUUFBUSxjQUFjLFFBQVEsY0FBYyxLQUMvQyxTQUFRLGFBQWE7RUFFdEIsTUFBTSxhQUFhLEtBQUssZUFBZSxDQUFDLEtBQUssY0FBYyxTQUFTO0FBQ3BFLE1BQUksUUFBUSxjQUFjLFFBQVEsY0FBYyxLQUMvQyxTQUFRLGFBQWE7RUFFdEIsTUFBTSxPQUFPLEtBQUssV0FBVyxLQUFLLEVBQUU7R0FDbkMsR0FBRztHQUNILEdBQUc7R0FDSDtHQUNBLENBQUM7QUFDRixPQUFLLE9BQU87R0FDWDtDQUNGLE1BQU0sWUFBWTtFQUNqQixNQUFNO0VBQ047RUFDQTtFQUNBO0VBQ0EsU0FBUztFQUNUO0VBQ0EsTUFBTSxNQUFNO0VBQ1o7RUFDQTtFQUNBO0VBQ0E7RUFDQSxJQUFJO0VBQ0o7Q0FDRCxTQUFTLFFBQVEsTUFBTSxHQUFHLElBQUk7QUFDN0IsV0FBUyxNQUFNLENBQUMsTUFBTSxLQUFLLEdBQUcsR0FBRzs7Q0FFbEMsU0FBUyxVQUFVLGlCQUFpQjtBQUNuQyxNQUFJLE9BQU8saUJBQWlCLFNBQzNCLGdCQUFlLEVBQUUsU0FBUyxjQUFjO0VBRXpDLE1BQU0sZUFBZSxpQkFBaUIsY0FBYztFQUNwRCxNQUFNLGFBQWEsZ0JBQWdCLGlCQUFpQixjQUFjO0VBQ2xFLE1BQU0sWUFBWSxRQUFRLGNBQWMsS0FBSztBQUM3QyxlQUFhLE9BQU8sUUFBUSxVQUFVO0FBQ3RDLFVBQVE7R0FDUCxJQUFJO0dBQ0osTUFBTTtHQUNOO0dBQ0EsVUFBVSxlQUFlLENBQUMsY0FBYyxZQUFZLGlCQUFpQixjQUFjLE1BQU0sVUFBVSxLQUFLLENBQUM7R0FDekcsY0FBYyxlQUFlLENBQUMsY0FBYyxjQUFjLEtBQUssQ0FBQztHQUNoRSxPQUFPO0dBQ1A7R0FDQTtHQUNBLE1BQU0sY0FBYyxRQUFRLGlCQUFpQixjQUFjO0dBQzNELFNBQVMsY0FBYztHQUN2QixPQUFPLEVBQUU7R0FDVCxNQUFNLGNBQWMsUUFBUSxPQUFPLE9BQU8sS0FBSztHQUMvQyxZQUFZLGNBQWM7R0FDMUIsTUFBTSxPQUFPLENBQUMsR0FBRyxZQUFZLFFBQVEsRUFBRSxFQUFFLEdBQUcsVUFBVSxDQUFDO0dBQ3ZEO0FBQ0QsTUFBSSxVQUFVLG1CQUFtQixPQUFPLE9BQU8scUJBQXFCO0dBQ25FLE1BQU0sUUFBUSxNQUFNO0FBQ3BCLFNBQU0sa0JBQWtCO0dBQ3hCLE1BQU0seUJBQVEsSUFBSSxNQUFNLGFBQWEsRUFBQztBQUN0QyxTQUFNLGtCQUFrQjtHQUN4QixNQUFNLFFBQVEsdUJBQXVCLHFCQUFxQixNQUFNO0FBQ2hFLE9BQUksTUFDSCxPQUFNLFdBQVc7SUFDaEIsTUFBTSxNQUFNO0lBQ1osUUFBUSxNQUFNO0lBQ2Q7O0FBR0gsV0FBUyxPQUFPLGtCQUFrQixDQUFDOztDQUVwQyxTQUFTLFFBQVE7QUFDaEIsUUFBTSxTQUFTO0FBQ2YsWUFBVSxNQUFNOztDQUVqQixlQUFlLFFBQVEsTUFBTTtBQUM1QixNQUFJLENBQUMsS0FDSixPQUFNLElBQUksVUFBVSxxQ0FBcUM7QUFFMUQsTUFBSSxRQUNILE9BQU0sYUFBYSxpQkFBaUIsUUFBUSxLQUFLLENBQUM7RUFFbkQsTUFBTSxjQUFjLEVBQUU7QUFDdEIsT0FBSyxNQUFNLEtBQUssTUFDZixhQUFZLEtBQUssRUFBRSxTQUFTLGNBQWMsTUFBTSxFQUFFLFFBQVEsS0FBSyxHQUFHLEVBQUU7QUFFckUsUUFBTSxRQUFRO0FBQ2QsU0FBTzs7QUFFUixhQUFZLFVBQVU7QUFDdEIsUUFBTzs7QUFFUixTQUFTLHlCQUF5QixJQUFJLE1BQU07QUFDM0MsU0FBUSxPQUFPLEdBQUcsU0FBUztFQUMxQixNQUFNLFdBQVcsTUFBTSxHQUFHLEdBQUcsS0FBSztBQUVsQyxNQUFJLEtBQUssVUFBVTtHQUVsQixNQUFNLFVBRFMsTUFBTSxRQUFRLFdBQVcsS0FBSyxTQUFTLEVBQ2hDLEtBQUssTUFBTSxFQUFFLFdBQVcsYUFBYSxFQUFFLFNBQVMsT0FBVSxDQUFDLE9BQU8sUUFBUTtBQUNoRyxPQUFJLE9BQU8sT0FDVixPQUFNOztBQUdSLFNBQU87OztBQUdULFNBQVMsY0FBYztDQUN0QixTQUFTLFFBQVEsTUFBTSxrQkFBa0Isa0JBQWtCO0FBQzFELE1BQUksZ0JBQWdCLENBQ25CLE9BQU0sSUFBSSxNQUFNLDJJQUEySTtFQUU1SixNQUFNLGVBQWUsaUJBQWlCLGdCQUFnQjtFQUN0RCxJQUFJLEVBQUUsU0FBUyxTQUFTLFlBQVksZUFBZSxrQkFBa0IsaUJBQWlCO0VBQ3RGLE1BQU0sd0JBQXdCLFFBQVEsY0FBYyxLQUFLLGNBQWMsUUFBUSxlQUFlO0VBQzlGLE1BQU0sd0JBQXdCLFFBQVEsY0FBYyxLQUFLLGNBQWMsUUFBUSxlQUFlO0VBQzlGLE1BQU0sRUFBRSxNQUFNLFlBQVksR0FBRyxrQkFBa0IsY0FBYyxXQUFXLEVBQUU7QUFFMUUsWUFBVTtHQUNULEdBQUc7R0FDSCxHQUFHO0dBQ0g7RUFDRCxNQUFNLFVBQVUsS0FBSyxXQUFXLFFBQVEsV0FBVyxjQUFjLFNBQVMsV0FBVyxRQUFRLE9BQU8sU0FBUztBQUM3RyxNQUFJLFdBQVcsS0FDZCxTQUFRLFVBQVU7RUFFbkIsSUFBSSxPQUFPLEtBQUssUUFBUSxRQUFRLE9BQU8sU0FBUyxLQUFLLFFBQVEsUUFBUSxPQUFPLFNBQVMsS0FBSyxRQUFRLFFBQVEsT0FBTyxTQUFTO0FBRTFILE1BQUksU0FBUyxTQUFTLENBQUMsUUFDdEIsUUFBTztFQUdSLE1BQU0sZUFBZSx5QkFBeUIsUUFBUSxjQUFjLENBQUM7RUFDckUsTUFBTSxlQUFlLHlCQUF5QixRQUFRLGNBQWMsQ0FBQztBQUNyRSxNQUFJLGdCQUFnQixLQUNuQixTQUFRLGFBQWEsZ0JBQWdCLENBQUM7QUFFdkMsTUFBSSxnQkFBZ0IsS0FDbkIsU0FBUSxhQUFhLGdCQUFnQixDQUFDO0FBRXZDLE1BQUksV0FDSCxTQUFRLE9BQU8sT0FBTyxPQUFPLE9BQU8sT0FBTyxLQUFLLEVBQUUsWUFBWSxRQUFRLEtBQUs7QUFFNUUsU0FBTyxxQkFBcUIsV0FBVyxLQUFLLEVBQUUsU0FBUyxNQUFNLEtBQUssTUFBTSxRQUFROztBQUVqRixTQUFRLE9BQU8sU0FBUyxPQUFPLEdBQUcsTUFBTTtFQUN2QyxNQUFNLFVBQVUsb0JBQW9CLEtBQUs7RUFDekMsTUFBTSxRQUFRLFFBQVEsYUFBYTtBQUNuQyxVQUFRLFdBQVcsUUFBUSxLQUFLO0FBQ2hDLE1BQUksTUFBTSxRQUFRLE1BQU0sSUFBSSxLQUFLLE9BQ2hDLFNBQVEscUJBQXFCLE9BQU8sS0FBSztBQUUxQyxVQUFRLE1BQU0sYUFBYSxnQkFBZ0I7R0FDMUMsTUFBTSxRQUFRLFdBQVcsS0FBSztHQUM5QixNQUFNLGlCQUFpQixNQUFNLE1BQU0sTUFBTSxRQUFRO0dBQ2pELE1BQU0sRUFBRSxTQUFTLFlBQVksZUFBZSxhQUFhLFlBQVk7R0FDckUsTUFBTSxVQUFVLE9BQU8sZ0JBQWdCO0FBQ3ZDLFNBQU0sU0FBUyxHQUFHLFFBQVE7SUFDekIsTUFBTSxRQUFRLE1BQU0sUUFBUSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUU7QUFDeEMsUUFBSSxRQUNILEtBQUksZUFDSCxPQUFNLFlBQVksT0FBTyxPQUFPLElBQUksRUFBRSxnQkFBZ0IsUUFBUSxHQUFHLE1BQU0sR0FBRyxRQUFXLFFBQVEsUUFBUTtRQUVyRyxPQUFNLFlBQVksT0FBTyxPQUFPLElBQUksRUFBRSxnQkFBZ0IsUUFBUSxFQUFFLEdBQUcsUUFBVyxRQUFRLFFBQVE7YUFHM0YsZUFDSCxPQUFNLFlBQVksT0FBTyxPQUFPLElBQUksRUFBRSxTQUFTLGdCQUFnQixRQUFRLEdBQUcsTUFBTSxHQUFHLE9BQVU7UUFFN0YsT0FBTSxZQUFZLE9BQU8sT0FBTyxJQUFJLEVBQUUsU0FBUyxnQkFBZ0IsUUFBUSxFQUFFLEdBQUcsT0FBVTtLQUd2RjtBQUNGLFdBQVEsV0FBVyxRQUFRLE9BQVU7OztBQUd2QyxTQUFRLE1BQU0sU0FBUyxPQUFPLEdBQUcsTUFBTTtBQUN0QyxNQUFJLE1BQU0sUUFBUSxNQUFNLElBQUksS0FBSyxPQUNoQyxTQUFRLHFCQUFxQixPQUFPLEtBQUs7QUFFMUMsVUFBUSxNQUFNLGFBQWEsZ0JBQWdCO0dBQzFDLE1BQU0sUUFBUSxXQUFXLEtBQUs7R0FDOUIsTUFBTSxFQUFFLFNBQVMsWUFBWSxlQUFlLGFBQWEsWUFBWTtBQUNyRSxTQUFNLFNBQVMsTUFBTSxRQUFRO0FBQzVCLFVBQU0sWUFBWSxPQUFPLFFBQVEsS0FBSyxFQUFFLElBQUksRUFBRSxTQUFTLGdCQUFnQixRQUFRLEtBQUssR0FBRyxPQUFVO0tBQ2hHOzs7QUFHSixTQUFRLFVBQVUsY0FBYyxZQUFZLE1BQU0sT0FBTztBQUN6RCxTQUFRLFNBQVMsY0FBYyxZQUFZLFFBQVEsTUFBTTtBQUN6RCxRQUFPLGdCQUFnQjtFQUN0QjtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQSxFQUFFLFFBQVE7O0FBRVosU0FBUyxvQkFBb0IsSUFBSTtDQUNoQyxNQUFNLFNBQVM7QUFDZixRQUFPLE9BQU8sU0FBUyxPQUFPLEdBQUcsTUFBTTtFQUN0QyxNQUFNLFVBQVUsb0JBQW9CLEtBQUs7RUFDekMsTUFBTSxPQUFPLFFBQVEsYUFBYTtBQUNsQyxVQUFRLFdBQVcsUUFBUSxLQUFLO0FBQ2hDLE1BQUksTUFBTSxRQUFRLE1BQU0sSUFBSSxLQUFLLE9BQ2hDLFNBQVEscUJBQXFCLE9BQU8sS0FBSztBQUUxQyxVQUFRLE1BQU0sYUFBYSxnQkFBZ0I7R0FDMUMsTUFBTSxRQUFRLFdBQVcsS0FBSztHQUM5QixNQUFNLGlCQUFpQixNQUFNLE1BQU0sTUFBTSxRQUFRO0dBQ2pELE1BQU0sRUFBRSxTQUFTLFlBQVksZUFBZSxhQUFhLFlBQVk7R0FDckUsTUFBTSxVQUFVLE9BQU8sZ0JBQWdCO0FBQ3ZDLFNBQU0sU0FBUyxHQUFHLFFBQVE7SUFDekIsTUFBTSxRQUFRLE1BQU0sUUFBUSxFQUFFLEdBQUcsSUFBSSxDQUFDLEVBQUU7QUFDeEMsUUFBSSxRQUNILEtBQUksZUFDSCxNQUFLLFlBQVksT0FBTyxPQUFPLElBQUksRUFBRSxnQkFBZ0IsUUFBUSxHQUFHLE1BQU0sR0FBRyxRQUFXLFFBQVEsUUFBUTtRQUVwRyxNQUFLLFlBQVksT0FBTyxPQUFPLElBQUksRUFBRSxnQkFBZ0IsUUFBUSxFQUFFLEdBQUcsUUFBVyxRQUFRLFFBQVE7YUFHMUYsZUFDSCxNQUFLLFlBQVksT0FBTyxPQUFPLElBQUksRUFBRSxTQUFTLGdCQUFnQixRQUFRLEdBQUcsTUFBTSxHQUFHLE9BQVU7UUFFNUYsTUFBSyxZQUFZLE9BQU8sT0FBTyxJQUFJLEVBQUUsU0FBUyxnQkFBZ0IsUUFBUSxFQUFFLEdBQUcsT0FBVTtLQUd0RjtBQUNGLFdBQVEsV0FBVyxRQUFRLE9BQVU7OztBQUd2QyxRQUFPLE1BQU0sU0FBUyxPQUFPLEdBQUcsTUFBTTtFQUVyQyxNQUFNLE9BRFUsb0JBQW9CLEtBQUssQ0FDcEIsYUFBYTtBQUNsQyxNQUFJLE1BQU0sUUFBUSxNQUFNLElBQUksS0FBSyxPQUNoQyxTQUFRLHFCQUFxQixPQUFPLEtBQUs7QUFFMUMsVUFBUSxNQUFNLGFBQWEsZ0JBQWdCO0dBQzFDLE1BQU0sUUFBUSxXQUFXLEtBQUs7R0FDOUIsTUFBTSxFQUFFLFNBQVMsWUFBWSxlQUFlLGFBQWEsWUFBWTtBQUNyRSxTQUFNLFNBQVMsTUFBTSxRQUFRO0lBRTVCLE1BQU0saUJBQWlCLFdBQVcsUUFBUSxRQUFRLE1BQU0sSUFBSSxHQUFHO0FBQy9ELFFBQUksZUFDSCxnQkFBZSxnQkFBZ0I7S0FDOUIsT0FBTztLQUNQLFVBQVU7S0FDVixDQUFDO0FBRUgsU0FBSyxZQUFZLE9BQU8sUUFBUSxLQUFLLEVBQUUsSUFBSSxFQUFFLFNBQVMsZUFBZTtLQUNwRTs7O0FBR0osUUFBTyxTQUFTLFNBQVMsV0FBVztBQUNuQyxTQUFPLFlBQVksS0FBSyxPQUFPOztBQUVoQyxRQUFPLFFBQVEsU0FBUyxXQUFXO0FBQ2xDLFNBQU8sWUFBWSxPQUFPLEtBQUs7Ozs7OztDQU1oQyxTQUFTLHFCQUFxQixnQkFBZ0IsYUFBYSxTQUFTO0FBRW5FLE1BQUksT0FBTyxtQkFBbUIsU0FDN0IsUUFBTztFQUVSLE1BQU0sY0FBYztFQUNwQixJQUFJO0VBQ0osSUFBSTtBQUNKLE1BQUksWUFBWSxRQUFXO0FBRTFCLG9CQUFpQjtBQUNqQixrQkFBZTthQUlYLGdCQUFnQixRQUFRLE9BQU8sZ0JBQWdCLFlBQVksQ0FBQyxNQUFNLFFBQVEsWUFBWSxJQUFJLGFBQWEsaUJBQWlCLFlBQVksRUFBRTtBQUV6SSxvQkFBaUI7QUFDakIsa0JBQWUsRUFBRTtTQUNYO0FBRU4sb0JBQWlCO0FBQ2pCLGtCQUFlOztBQUlqQixNQUFJLE9BQU8saUJBQWlCLFlBQVk7R0FDdkMsTUFBTSxZQUFZO0dBRWxCLE1BQU0sVUFBVSxPQUFPLEtBQUssUUFBUTtJQUNuQyxJQUFJO0lBQ0osTUFBTSxhQUFhLE9BQU87QUFDekIsU0FBSSxZQUFZLE9BQ2YsT0FBTSxJQUFJLE1BQU0sa0hBQXVIO0FBRXhJLGVBQVU7O0FBR1gsVUFBTSxJQURRLE1BQU0sVUFBVSxLQUFLLEVBQUUsV0FBVyxDQUFDLENBQ2pDO0FBQ2hCLFFBQUksUUFDSCxPQUFNLFNBQVM7O0FBR2pCLGtCQUFlLFNBQVMsRUFBRSxVQUFVLFdBQVcsQ0FBQztBQUNoRCxPQUFJLGVBQ0gsUUFBTyxHQUFHLGNBQWMsQ0FBQyxTQUFTLGVBQWUsRUFBRTtBQUVwRCxVQUFPLEdBQUcsY0FBYyxTQUFTOztBQUdsQyxNQUFJLGVBQ0gsUUFBTyxHQUFHLGNBQWMsQ0FBQyxjQUFjLGVBQWUsRUFBRTtBQUV6RCxTQUFPLEdBQUcsY0FBYyxjQUFjOztBQUV2QyxRQUFPLFdBQVcsU0FBUyxnQkFBZ0IsYUFBYSxTQUFTO0VBQ2hFLE1BQU0sZUFBZSxxQkFBcUIsZ0JBQWdCLGFBQWEsUUFBUTtBQUMvRSxzQkFBb0IsS0FBSyxDQUFDLGFBQWEsQ0FBQyxTQUFTLFFBQVEsYUFBYTtBQUN0RSxTQUFPOztBQUVSLFFBQU8sU0FBUyxTQUFTLFVBQVU7QUFDbEMsVUFBUSxLQUFLLDBHQUEwRztBQUN2SCxTQUFPLEtBQUssU0FBUyxTQUFTOztBQUUvQixRQUFPLFNBQVMsU0FBUyxnQkFBZ0IsYUFBYSxTQUFTO0VBQzlELE1BQU0sZUFBZSxxQkFBcUIsZ0JBQWdCLGFBQWEsUUFBUTtFQUMvRSxNQUFNLFdBQVcsb0JBQW9CLEtBQUssQ0FBQyxhQUFhLENBQUMsT0FBTyxRQUFRLGFBQWE7RUFDckYsTUFBTSxRQUFRLFdBQVcsU0FBUyxNQUFNLGFBQWEsZUFBZTtBQUNuRSxNQUFHLEtBQUssTUFBTSxXQUFXLEtBQUssRUFBRSxhQUFhLGNBQWM7SUFDMUQ7QUFDRixzQkFBb0IsTUFBTSxDQUFDLGFBQWEsRUFBRSxVQUFVLENBQUM7QUFDckQsU0FBTzs7QUFFUixRQUFPLFdBQVc7QUFDbEIsUUFBTyxRQUFRO0FBQ2YsUUFBTyxhQUFhO0FBQ3BCLFFBQU8sWUFBWTtBQUNuQixRQUFPLFlBQVk7QUFDbkIsUUFBTyxXQUFXO0FBQ2xCLFFBQU8sYUFBYTtBQUNwQixRQUFPLFlBQVk7QUFTbkIsUUFSYyxnQkFBZ0I7RUFDN0I7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsRUFBRSxRQUFRLEVBQUUsVUFBVSxJQUFJLGNBQWMsRUFBRSxDQUFDOztBQUc3QyxTQUFTLFdBQVcsSUFBSTtBQUN2QixRQUFPLG9CQUFvQixHQUFHOztBQUUvQixTQUFTLFdBQVcsTUFBTTtBQUN6QixRQUFPLE9BQU8sU0FBUyxXQUFXLE9BQU8sT0FBTyxTQUFTLGFBQWEsS0FBSyxRQUFRLGdCQUFnQixPQUFPLEtBQUs7O0FBRWhILFNBQVMsWUFBWSxVQUFVLE9BQU8sS0FBSztBQUMxQyxLQUFJLFNBQVMsU0FBUyxLQUFLLElBQUksU0FBUyxTQUFTLEtBQUssQ0FFckQsWUFBVyxTQUFTLFFBQVEsT0FBTyx1QkFBdUIsQ0FBQyxRQUFRLE9BQU8sR0FBRyxNQUFNLENBQUMsUUFBUSxRQUFRLEdBQUcsTUFBTSxJQUFJLENBQUMsUUFBUSx5QkFBeUIsS0FBSztDQUV6SixNQUFNLFFBQVEsU0FBUyxNQUFNLElBQUksQ0FBQyxTQUFTO0FBQzNDLEtBQUksU0FBUyxTQUFTLEtBQUssQ0FFMUIsRUFEcUIsU0FBUyxNQUFNLE1BQU0sSUFBSSxFQUFFLEVBQ25DLFNBQVMsR0FBRyxNQUFNO0FBQzlCLE1BQUksY0FBYyxNQUFNLEdBQUcsSUFBSSxPQUFPLEdBQUcsTUFBTSxJQUFJLEdBQUcsRUFBRTtHQUV2RCxJQUFJLGFBQWE7QUFDakIsY0FBVyxTQUFTLFFBQVEsUUFBUSxVQUFVO0FBQzdDO0FBQ0EsV0FBTyxlQUFlLElBQUksSUFBSSxRQUFRO0tBQ3JDOztHQUVGO0NBRUgsTUFBTSxlQUFlLFNBQVMsTUFBTSxHQUFHO0NBQ3ZDLFNBQVMsZ0JBQWdCLEdBQUc7QUFDM0IsU0FBTyxFQUFFLFFBQVEsaUJBQWlCLEdBQUcsUUFBUTtHQUM1QyxNQUFNLGFBQWEsUUFBUSxLQUFLLElBQUk7QUFDcEMsT0FBSSxDQUFDLGdCQUFnQixDQUFDLFdBQ3JCLFFBQU8sSUFBSTtHQUVaLE1BQU0sZUFBZSxhQUFhLFdBQVcsT0FBTyxJQUFJLEdBQUc7QUFFM0QsVUFBTyxXQURPLGVBQWUsV0FBVyxNQUFNLElBQUksS0FBSyxhQUFhLEdBQUcsY0FDOUMsRUFBRSxVQUFVLFFBQVEsUUFBUSxZQUFZLG1CQUFtQixDQUFDO0lBQ3BGOztDQUVILElBQUksU0FBUztDQUNiLElBQUksSUFBSTtBQUNSLGtCQUNDLFVBQ0EsZUFFQyxVQUFVO0FBQ1YsTUFBSSxJQUFJLE1BQ1AsV0FBVSxPQUFPLE1BQU0sSUFBSSxNQUFNLEtBQUs7TUFFdEMsV0FBVSxNQUFNO0tBSWpCLGFBQWE7QUFDYixZQUFVLGdCQUFnQixTQUFTO0dBRXBDO0FBQ0QsUUFBTzs7QUFHUixTQUFTLGlCQUFpQixPQUFPLE9BQU8sU0FBUyxZQUFZO0NBQzVELElBQUksWUFBWTtBQUNoQixNQUFLLE1BQU0sS0FBSyxNQUFNLFNBQVMsTUFBTSxFQUFFO0FBQ3RDLE1BQUksWUFBWSxFQUFFLE1BQ2pCLFlBQVcsTUFBTSxNQUFNLFdBQVcsRUFBRSxNQUFNLENBQUM7QUFFNUMsVUFBUSxFQUFFO0FBQ1YsY0FBWSxFQUFFLFFBQVEsRUFBRSxHQUFHOztBQUU1QixLQUFJLFlBQVksTUFBTSxPQUNyQixZQUFXLE1BQU0sTUFBTSxVQUFVLENBQUM7O0FBR3BDLFNBQVMscUJBQXFCLE9BQU8sTUFBTTtDQUMxQyxNQUFNLFNBQVMsTUFBTSxLQUFLLEdBQUcsQ0FBQyxNQUFNLENBQUMsUUFBUSxNQUFNLEdBQUcsQ0FBQyxNQUFNLEtBQUssQ0FBQyxLQUFLLE1BQU0sRUFBRSxNQUFNLElBQUksQ0FBQyxDQUFDO0NBQzVGLE1BQU0sTUFBTSxFQUFFO0FBQ2QsTUFBSyxJQUFJLElBQUksR0FBRyxJQUFJLEtBQUssTUFBTSxLQUFLLFNBQVMsT0FBTyxPQUFPLEVBQUUsS0FBSztFQUNqRSxNQUFNLFVBQVUsRUFBRTtBQUNsQixPQUFLLElBQUksSUFBSSxHQUFHLElBQUksT0FBTyxRQUFRLElBQ2xDLFNBQVEsT0FBTyxNQUFNLEtBQUssSUFBSSxPQUFPLFNBQVM7QUFFL0MsTUFBSSxLQUFLLFFBQVE7O0FBRWxCLFFBQU87O0FBR1IsTUFBTSxRQUFRLFdBQVcsY0FBYyxXQUFXLFlBQVksSUFBSSxLQUFLLFdBQVcsWUFBWSxHQUFHLEtBQUs7QUFDdEcsTUFBTSxtQkFBbUI7Q0FDeEIsT0FBTyxFQUFFO0NBQ1QsY0FBYztDQUNkO0FBQ0QsU0FBUyxZQUFZLE1BQU07QUFDMUIsa0JBQWlCLGNBQWMsTUFBTSxLQUFLLEtBQUs7O0FBRWhELGVBQWUsYUFBYSxPQUFPLElBQUk7Q0FDdEMsTUFBTSxPQUFPLGlCQUFpQjtBQUM5QixrQkFBaUIsZUFBZTtBQUNoQyxPQUFNLElBQUk7QUFDVixrQkFBaUIsZUFBZTs7QUFFakMsU0FBUyxZQUFZLElBQUksU0FBUyxTQUFTLE9BQU8saUJBQWlCLFdBQVc7QUFDN0UsS0FBSSxXQUFXLEtBQUssWUFBWSxPQUFPLGtCQUN0QyxRQUFPO0NBRVIsTUFBTSxFQUFFLFlBQVksaUJBQWlCLGVBQWU7QUFFcEQsU0FBUSxTQUFTLGVBQWUsR0FBRyxNQUFNO0VBQ3hDLE1BQU0sWUFBWSxPQUFPO0VBQ3pCLE1BQU0sU0FBUyxXQUFXO0FBQzFCLFNBQU8sd0JBQXdCO0FBQy9CLFNBQU8sc0JBQXNCO0FBQzdCLFNBQU8sSUFBSSxTQUFTLFVBQVUsWUFBWTtHQUN6QyxNQUFNLFFBQVEsaUJBQWlCO0FBQzlCLGlCQUFhLE1BQU07QUFDbkIsd0JBQW9CO01BQ2xCLFFBQVE7QUFFWCxTQUFNLFNBQVM7R0FDZixTQUFTLHFCQUFxQjtJQUM3QixNQUFNLFFBQVEsaUJBQWlCLFFBQVEsU0FBUyxnQkFBZ0I7QUFDaEUsZ0JBQVksTUFBTSxNQUFNO0FBQ3hCLFlBQVEsTUFBTTs7R0FFZixTQUFTLFFBQVEsUUFBUTtBQUN4QixXQUFPLHdCQUF3QjtBQUMvQixXQUFPLHNCQUFzQjtBQUM3QixpQkFBYSxNQUFNO0FBSW5CLFFBQUksT0FBTyxHQUFHLGFBQWEsU0FBUztBQUNuQyx5QkFBb0I7QUFDcEI7O0FBRUQsYUFBUyxPQUFPOztHQUVqQixTQUFTLE9BQU8sT0FBTztBQUN0QixXQUFPLHdCQUF3QjtBQUMvQixXQUFPLHNCQUFzQjtBQUM3QixpQkFBYSxNQUFNO0FBQ25CLFlBQVEsTUFBTTs7QUFHZixPQUFJO0lBQ0gsTUFBTSxTQUFTLEdBQUcsR0FBRyxLQUFLO0FBRzFCLFFBQUksT0FBTyxXQUFXLFlBQVksVUFBVSxRQUFRLE9BQU8sT0FBTyxTQUFTLFdBQzFFLFFBQU8sS0FBSyxTQUFTLE9BQU87UUFFNUIsU0FBUSxPQUFPO1lBSWIsT0FBTztBQUNWLFdBQU8sTUFBTTs7SUFFYjs7O0FBR0osU0FBUyxXQUFXLElBQUksUUFBUTtBQUMvQixTQUFRLFNBQVMsY0FBYyxHQUFHLE1BQU07QUFDdkMsU0FBTyxJQUFJLFNBQVMsU0FBUyxXQUFXO0FBQ3ZDLFVBQU8saUJBQWlCLGVBQWUsT0FBTyxPQUFPLE9BQU8sQ0FBQztBQUM3RCxPQUFJO0lBQ0gsTUFBTSxTQUFTLEdBQUcsR0FBRyxLQUFLO0FBQzFCLFFBQUksT0FBTyxXQUFXLFlBQVksVUFBVSxRQUFRLE9BQU8sT0FBTyxTQUFTLFdBQzFFLFFBQU8sS0FBSyxTQUFTLE9BQU87UUFFNUIsU0FBUSxPQUFPO1lBRVIsT0FBTztBQUNmLFdBQU8sTUFBTTs7SUFFYjs7O0FBR0osTUFBTSxtQ0FBbUIsSUFBSSxTQUFTO0FBQ3RDLFNBQVMsZUFBZSxDQUFDLFVBQVUsT0FBTztBQUN6QyxLQUFJLFFBQ0gsb0JBQW1CLFNBQVMsTUFBTTs7QUFHcEMsU0FBUyxtQkFBbUIsU0FBUyxPQUFPO0FBRTNDLENBRHdCLGlCQUFpQixJQUFJLFFBQVEsRUFDcEMsTUFBTSxNQUFNOztBQUU5QixTQUFTLGtCQUFrQixNQUFNLFFBQVE7Q0FDeEMsTUFBTSxVQUFVLFdBQVc7QUFDMUIsUUFBTSxJQUFJLE1BQU0scURBQXFEOztDQUV0RSxJQUFJLGtCQUFrQixpQkFBaUIsSUFBSSxRQUFRO0FBQ25ELEtBQUksQ0FBQyxpQkFBaUI7QUFDckIsb0JBQWtCLElBQUksaUJBQWlCO0FBQ3ZDLG1CQUFpQixJQUFJLFNBQVMsZ0JBQWdCOztBQUUvQyxTQUFRLFNBQVMsZ0JBQWdCO0FBQ2pDLFNBQVEsT0FBTztBQUNmLFNBQVEsUUFBUSxXQUFXLFNBQVM7QUFDbkMsTUFBSSxjQUFjLE1BRWpCO0FBRUQsT0FBSyxXQUFXLEVBQUUsT0FBTyxRQUFRO0FBQ2pDLE9BQUssT0FBTyxVQUFVO0FBQ3RCLFFBQU0sSUFBSSxhQUFhLG9DQUFvQyxNQUFNLE9BQU8sY0FBYyxXQUFXLFlBQVksS0FBSzs7QUFFbkgsU0FBUSxhQUFhLFNBQVMsTUFBTSxlQUFlO0FBQ2xELE1BQUksS0FBSyxVQUFVLEtBQUssT0FBTyxVQUFVLE1BQ3hDLE9BQU0sSUFBSSxNQUFNLDREQUE0RCxLQUFLLEtBQUssK0JBQStCLEtBQUssT0FBTyxNQUFNLGtCQUFrQjtFQUUxSixNQUFNLGFBQWE7R0FDbEI7R0FDQSxNQUFNLE9BQU8sU0FBUyxZQUFZLFNBQVMsU0FBWSxXQUFXO0dBQ2xFO0VBQ0QsTUFBTSx1QkFBdUIsT0FBTyxTQUFTLFdBQVcsT0FBTztBQUMvRCxNQUFJLHNCQUFzQjtBQUN6QixjQUFXLGFBQWE7QUFDeEIsNEJBQXlCLFdBQVcsV0FBVzs7QUFFaEQsU0FBTyxxQkFBcUIsTUFBTSxlQUFlLE1BQU07R0FDdEQsTUFBTTtHQUNOO0dBQ0EsQ0FBQyxDQUFDLEtBQUssT0FBTyxFQUFFLGlCQUFpQjtBQUNqQyxPQUFJLENBQUMsT0FBTyxlQUNYLE9BQU0sSUFBSSxNQUFNLGdEQUFnRDtBQUVqRSxTQUFNLHNCQUFzQixPQUFPO0dBQ25DLE1BQU0scUJBQXFCLE1BQU0sT0FBTyxlQUFlLE1BQU0sV0FBVztBQUN4RSxRQUFLLFlBQVksS0FBSyxtQkFBbUI7QUFDekMsVUFBTztJQUNOLENBQUM7O0FBRUosU0FBUSxnQkFBZ0IsU0FBUyxZQUFZO0FBQzVDLE9BQUssYUFBYSxFQUFFO0FBQ3BCLE9BQUssU0FBUyxLQUFLLFlBQVksU0FBUyxXQUFXLE9BQU8sT0FBTyxhQUFhLHNCQUFNLElBQUksTUFBTSxvQkFBb0IsR0FBRyxHQUFHLFVBQVUsZ0JBQWdCLE1BQU0sTUFBTSxDQUFDLENBQUM7O0FBRWpLLFNBQVEsa0JBQWtCLFNBQVMsWUFBWTtBQUM5QyxPQUFLLGVBQWUsRUFBRTtBQUN0QixPQUFLLFdBQVcsS0FBSyxZQUFZLFNBQVMsV0FBVyxPQUFPLE9BQU8sYUFBYSxzQkFBTSxJQUFJLE1BQU0sb0JBQW9CLEdBQUcsR0FBRyxVQUFVLGdCQUFnQixNQUFNLE1BQU0sQ0FBQyxDQUFDOztBQUVuSyxRQUFPLE9BQU8sb0JBQW9CLFFBQVEsSUFBSTs7QUFFL0MsU0FBUyxpQkFBaUIsUUFBUSxTQUFTLGlCQUFpQjtDQUMzRCxNQUFNLFVBQVUsR0FBRyxTQUFTLFNBQVMsT0FBTyxnQkFBZ0IsUUFBUSxpQ0FBaUMsU0FBUyxTQUFTLE9BQU8sNkVBQTZFLFNBQVMsZ0JBQWdCLGNBQWM7Q0FDbFAsTUFBTSxRQUFRLElBQUksTUFBTSxRQUFRO0FBQ2hDLEtBQUksaUJBQWlCLE1BQ3BCLE9BQU0sUUFBUSxnQkFBZ0IsTUFBTSxRQUFRLE1BQU0sU0FBUyxnQkFBZ0IsUUFBUTtBQUVwRixRQUFPOztBQUdSLGVBQWUsY0FBYyxRQUFRLE9BQU8sUUFBUTtBQUNuRCxLQUFJLE9BQU8sU0FBUyxlQUFlLFdBQ2xDLE9BQU0sUUFBUSxJQUFJLE1BQU0sSUFBSSxPQUFPLFdBQVc7QUFDN0MsUUFBTSxPQUFPLFdBQVcsUUFBUSxRQUFRO0dBQ3ZDLENBQUM7S0FFSCxNQUFLLE1BQU0sVUFBVSxNQUNwQixPQUFNLE9BQU8sV0FBVyxRQUFRLFFBQVE7O0FBSzNDLE1BQU0sUUFBUSxXQUFXLGNBQWMsV0FBVyxZQUFZLElBQUksS0FBSyxXQUFXLFlBQVksR0FBRyxLQUFLO0FBQ3RHLGVBQWUsYUFBYSxPQUFPLFFBQVE7Q0FDMUMsTUFBTSxRQUFRLEVBQUU7Q0FDaEIsTUFBTSxTQUFTLE9BQU87Q0FDdEIsTUFBTSxJQUFJLE9BQU87Q0FDakIsSUFBSTtBQUNKLE1BQUssTUFBTSxRQUFRLE9BQU87RUFDekIsTUFBTSxXQUFXLE9BQU8sU0FBUyxXQUFXLE9BQU8sS0FBSztBQUN4RCxRQUFNLEVBQUUsZ0JBQWdCLEVBQUUsa0JBQWtCLFVBQVUsRUFBRSxZQUFZO0FBQ25FLFVBQU8sd0JBQXdCLE9BQU8sU0FBUyxXQUFXLEVBQUUsVUFBVSxNQUFNLEdBQUc7R0FDL0UsTUFBTSxnQkFBZ0IsT0FBTyxTQUFTLFdBQVcsU0FBWSxLQUFLO0dBQ2xFLE1BQU0sa0JBQWtCLE9BQU8sU0FBUyxXQUFXLFNBQVksS0FBSztHQUNwRSxNQUFNLFVBQVUsT0FBTyxTQUFTLFdBQVcsU0FBWSxLQUFLO0dBQzVELE1BQU0saUJBQWlCLE9BQU8sU0FBUyxZQUFZLEtBQUssaUJBQWlCLGlCQUFpQixLQUFLLGdCQUFnQixPQUFPLEtBQUssR0FBRztHQUM5SCxNQUFNLFdBQVcsT0FBTyxTQUFTLFdBQVcsRUFBRSxHQUFHLEtBQUssWUFBWSxFQUFFO0dBQ3BFLE1BQU0sT0FBTyxlQUFlLFVBQVUsT0FBTyxNQUFNLE9BQU8sTUFBTSxPQUFPLE1BQU0sT0FBTyxnQkFBZ0I7QUFDcEcsUUFBSyxPQUFPO0FBQ1osUUFBSyxVQUFVLE9BQU8sU0FBUztBQUMvQixPQUFJO0FBQ0gsaUJBQWEsT0FBTyxRQUFRLFNBQVM7QUFDckMsV0FBTyxpQkFBaUIsS0FBSztBQUM3QiwwQkFBc0IsTUFBTSxPQUFPO0lBQ25DLE1BQU0sYUFBYSxRQUFRLE9BQU8sV0FBVztBQUM3QyxRQUFJLFdBQVcsUUFBUTtLQUN0QixNQUFNLGFBQWEsT0FBTztBQUMxQixXQUFNLGNBQWMsUUFBUSxZQUFZLE9BQU87QUFFL0MsVUFBSyxnQkFEWSxPQUFPLEdBQ1E7VUFFaEMsTUFBSyxnQkFBZ0I7SUFFdEIsTUFBTSxlQUFlLE9BQU87QUFDNUIsVUFBTSxPQUFPLFdBQVcsVUFBVSxVQUFVO0lBQzVDLE1BQU0sWUFBWSxPQUFPLHNCQUFzQjtBQUMvQyxRQUFJLFVBQ0gsTUFBSyxrQkFBa0I7SUFFeEIsTUFBTSxlQUFlLE1BQU0saUJBQWlCLENBQUMsUUFBUSxLQUFLO0lBQzFELE1BQU0sWUFBWSxrQkFBa0I7QUFDcEMsZUFBVyxXQUFXLFNBQVMsYUFBYSxDQUFDO0FBQzdDLFNBQUssTUFBTSxLQUFLLENBQUMsR0FBRyxhQUFhLE9BQU8sR0FBRyxpQkFBaUIsTUFBTSxDQUNqRSxLQUFJLEVBQUUsU0FBUyxVQUFVLEVBQUUsU0FBUyxRQUNuQyxNQUFLLE1BQU0sS0FBSyxFQUFFO2FBQ1IsRUFBRSxTQUFTLGFBQWE7S0FDbEMsTUFBTSxRQUFRLE1BQU0sRUFBRSxRQUFRLEtBQUs7QUFDbkMsU0FBSSxNQUFNLFFBQVEsTUFBTSxNQUFNLFFBQVE7QUFDckMsaUJBQVcsV0FBVyxTQUFTLE1BQU0sQ0FBQztBQUN0QyxXQUFLLE1BQU0sS0FBSyxNQUFNOzs7QUFPekIsYUFBUyxNQUFNLFVBQVU7QUFDekIsU0FBSyxrQkFBa0IsT0FBTyxHQUFHO1lBQ3pCLEdBQUc7QUFFWCxTQUFLLFNBQVM7S0FDYixPQUFPO0tBQ1AsUUFIYyxhQUFhLGlCQUFpQixFQUFFLE9BQU8sS0FBSyxNQUFNLGFBQWEsR0FBRyxPQUFPLE9BQU8sWUFBWSxDQUFDLEdBQUcsQ0FBQyxhQUFhLEdBQUcsT0FBTyxPQUFPLFlBQVksQ0FBQztLQUkxSjtJQUNELE1BQU0sWUFBWSxPQUFPLHNCQUFzQjtBQUMvQyxRQUFJLFVBQ0gsTUFBSyxrQkFBa0I7O0FBR3pCLHNCQUFtQixLQUFLO0dBQ3hCLE1BQU0sZUFBZSxpQkFBaUIsS0FBSztBQUMzQyxPQUFJLENBQUMsa0JBQWtCLENBQUMscUJBQXFCLE9BQU8sV0FDbkQscUJBQW9CLGlCQUFpQixPQUFPLFlBQVksT0FBTyxLQUFLO0FBRXJFLHNCQUFtQixNQUFNLG1CQUFtQixPQUFPLGlCQUFpQixlQUFlLFNBQVMsa0JBQWtCLG1CQUFtQixjQUFjLE9BQU8sT0FBTyxVQUFVO0FBQ3ZLLE9BQUksS0FBSyxTQUFTLFNBQ2pCLE1BQUssT0FBTztBQUViLFNBQU0sS0FBSyxLQUFLO0lBQ2Y7O0FBRUgsUUFBTzs7QUFFUixTQUFTLFdBQVcsV0FBVyxPQUFPO0FBQ3JDLE1BQUssTUFBTSxRQUFRLE9BQU87RUFDekIsTUFBTSxNQUFNO0FBQ1osWUFBVSxLQUFLLEtBQUssR0FBRyxNQUFNLEtBQUs7O0FBRW5DLFFBQU87O0FBR1IsTUFBTSxNQUFNLFdBQVcsY0FBYyxXQUFXLFlBQVksSUFBSSxLQUFLLFdBQVcsWUFBWSxHQUFHLEtBQUs7QUFDcEcsTUFBTSxVQUFVLEtBQUs7QUFDckIsTUFBTSxFQUFFLGNBQWMsZUFBZSxlQUFlO0FBQ3BELElBQUk7Ozs7O0FBS0osU0FBUyxjQUFjLE9BQU87QUFDN0IsS0FBSSxVQUFVLE9BQ2IsUUFBTztBQUVSLEtBQUksT0FBTyxVQUFVLFNBQ3BCLFFBQU87QUFFUixRQUFPLE1BQU0sU0FBUzs7QUFFdkIsU0FBUyxjQUFjLE9BQU87QUFDN0IsS0FBSSxVQUFVLE9BQ2IsUUFBTztBQUVSLEtBQUksT0FBTyxVQUFVLFNBQ3BCLFFBQU87QUFFUixRQUFPLE1BQU0sU0FBUzs7QUFFdkIsU0FBUyxrQkFBa0IsT0FBTztBQUNqQyxLQUFJLFVBQVUsT0FDYjtBQUVELEtBQUksT0FBTyxVQUFVLFNBQ3BCO0FBRUQsUUFBTyxNQUFNOztBQUVkLFNBQVMscUJBQXFCLE1BQU0sTUFBTSxPQUFPLFFBQVE7QUFDeEQsS0FBSSxDQUFDLEtBQUssT0FDVCxNQUFLLFNBQVMsRUFBRSxPQUFPLE9BQU87QUFFL0IsS0FBSSxDQUFDLEtBQUssT0FBTyxNQUNoQixNQUFLLE9BQU8sUUFBUSxFQUFFO0NBRXZCLE1BQU0sYUFBYSxLQUFLLE9BQU87QUFDL0IsS0FBSSxZQUFZO0FBQ2YsYUFBVyxRQUFRO0VBQ25CLElBQUksUUFBUSxVQUFVLFFBQVEsc0JBQXNCO0FBQ3BELE1BQUksU0FBUyxjQUFjLFNBQVMsWUFDbkMsU0FBUSxVQUFVLFFBQVEscUJBQXFCO0FBRWhELGFBQVcsT0FBTyxNQUFNLE9BQU87OztBQUdqQyxTQUFTLGNBQWMsT0FBTyxNQUFNLFVBQVU7Q0FDN0MsTUFBTSxRQUFRLFNBQVMsTUFBTSxDQUFDO0FBQzlCLEtBQUksYUFBYSxZQUFZLFNBQVMsY0FBYyxTQUFTLGFBQzVELFFBQU8sTUFBTSxPQUFPLENBQUMsU0FBUztBQUUvQixRQUFPOztBQUVSLGVBQWUsY0FBYyxRQUFRLE1BQU0sT0FBTyxVQUFVO0FBQzNELEtBQUksYUFBYSxRQUNoQixTQUFRLE1BQU0sT0FBTyxDQUFDLFNBQVM7QUFFaEMsS0FBSSxDQUFDLE1BQU0sT0FDVjtDQUVELE1BQU0sVUFBVSxLQUFLO0NBQ3JCLE1BQU0sZUFBZSxLQUFLLFFBQVE7Q0FDbEMsTUFBTSxpQkFBaUIsS0FBSyxRQUFRO0FBQ3BDLFNBQVEscUJBQXFCO0FBQzVCLFFBQU0sSUFBSSxNQUFNLGlEQUFpRDs7QUFFbEUsU0FBUSx1QkFBdUI7QUFDOUIsUUFBTSxJQUFJLE1BQU0sbURBQW1EOztBQUVwRSxLQUFJLGFBQWEsV0FDaEIsS0FBSTtBQUNILFFBQU0sUUFBUSxJQUFJLE1BQU0sS0FBSyxPQUFPLDBCQUEwQixHQUFHLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQztVQUN6RSxHQUFHO0FBQ1gsV0FBUyxLQUFLLFFBQVEsR0FBRyxPQUFPLE9BQU8sWUFBWTs7S0FHcEQsTUFBSyxNQUFNLE1BQU0sTUFDaEIsS0FBSTtBQUNILFFBQU0sMEJBQTBCLEdBQUcsS0FBSyxRQUFRLENBQUM7VUFDekMsR0FBRztBQUNYLFdBQVMsS0FBSyxRQUFRLEdBQUcsT0FBTyxPQUFPLFlBQVk7O0FBSXRELFNBQVEsZUFBZTtBQUN2QixTQUFRLGlCQUFpQjs7QUFFMUIsZUFBZSxjQUFjLE9BQU8sYUFBYSxNQUFNLFFBQVEsTUFBTTtDQUNwRSxNQUFNLFdBQVcsT0FBTyxPQUFPLFNBQVM7Q0FDeEMsTUFBTSxZQUFZLEVBQUU7Q0FFcEIsTUFBTSxjQUFjLGNBQWMsUUFBUSxPQUFPLE1BQU0sU0FBUyxNQUFNO0FBQ3RFLEtBQUksU0FBUyxnQkFBZ0IsWUFDNUIsV0FBVSxLQUFLLEdBQUcsTUFBTSxjQUFjLGFBQWEsYUFBYSxNQUFNLFFBQVEsS0FBSyxDQUFDO0NBRXJGLE1BQU0sUUFBUSxjQUFjLE9BQU8sTUFBTSxTQUFTO0FBQ2xELEtBQUksTUFBTSxTQUFTLEVBQ2xCLHNCQUFxQixhQUFhLE1BQU0sT0FBTyxPQUFPO0NBRXZELGVBQWUsUUFBUSxNQUFNO0FBQzVCLFNBQU8sb0JBQW9CLFlBQVk7QUFDdEMsVUFBTyw2QkFBNkIsTUFBTSxNQUFNLEtBQUssR0FBRyxLQUFLLEVBQUUsU0FBUyxlQUFlLEtBQUssS0FBSyxPQUFVO0lBQzFHOztBQUVILEtBQUksYUFBYSxXQUNoQixXQUFVLEtBQUssR0FBRyxNQUFNLFFBQVEsSUFBSSxNQUFNLEtBQUssU0FBUyxRQUFRLEtBQUssQ0FBQyxDQUFDLENBQUM7S0FFeEUsTUFBSyxNQUFNLFFBQVEsTUFDbEIsV0FBVSxLQUFLLE1BQU0sUUFBUSxLQUFLLENBQUM7QUFHckMsS0FBSSxNQUFNLFNBQVMsRUFDbEIsc0JBQXFCLGFBQWEsTUFBTSxRQUFRLE9BQU87QUFFeEQsS0FBSSxTQUFTLGVBQWUsWUFDM0IsV0FBVSxLQUFLLEdBQUcsTUFBTSxjQUFjLGFBQWEsYUFBYSxNQUFNLFFBQVEsS0FBSyxDQUFDO0FBRXJGLFFBQU87O0FBRVIsU0FBUyxtQkFBbUIsT0FBTztDQUNsQyxNQUFNLFFBQVEsRUFBRTtDQUNoQixNQUFNLGNBQWMsY0FBYyxRQUFRLE9BQU8sTUFBTSxTQUFTLE1BQU07QUFDdEUsS0FBSSxZQUNILE9BQU0sS0FBSyxHQUFHLG1CQUFtQixZQUFZLENBQUM7QUFFL0MsT0FBTSxLQUFLLEdBQUcsU0FBUyxNQUFNLENBQUMsV0FBVztBQUN6QyxRQUFPOztBQUVSLFNBQVMsa0JBQWtCLE9BQU87QUFDakMsUUFBTyxTQUFTLE1BQU0sQ0FBQzs7QUFFeEIsU0FBUywyQkFBMkIsVUFBVSxPQUFPLFNBQVMsaUJBQWlCO0NBQzlFLE1BQU0sVUFBVSxPQUFPLE1BQU0sYUFBYSxTQUFTLHlCQUF5QixRQUFRO0NBRXBGLE1BQU0sUUFBUSxLQURLLFVBQVUsVUFBVSx1QkFBdUIseUJBQ2pDLFFBQVE7QUFDckMsS0FBSSxpQkFBaUIsTUFDcEIsT0FBTSxRQUFRLGdCQUFnQixNQUFNLFFBQVEsZ0JBQWdCLFNBQVMsTUFBTSxRQUFRO0FBRXBGLFFBQU87O0FBRVIsZUFBZSxnQkFBZ0IsVUFBVSxTQUFTO0NBQ2pELE1BQU0sRUFBRSxPQUFPLFVBQVUsY0FBYyxXQUFXLGVBQWU7QUFDakUsS0FBSSxDQUFDLE1BQU0sUUFBUTtBQUNsQixRQUFNLFVBQVU7QUFDaEI7O0NBRUQsTUFBTSxhQUFhLEVBQUU7Q0FDckIsTUFBTSx3QkFBd0IsU0FBUyxPQUFPLG9CQUFvQjtFQUNqRSxJQUFJO0VBQ0osSUFBSSxXQUFXO0VBQ2YsTUFBTSxVQUFVLElBQUksU0FBUyxHQUFHLFdBQVc7QUFDMUMsT0FBSSxVQUFVLEtBQUssWUFBWSxPQUFPLG1CQUFtQjtBQUN4RCxZQUFRLGlCQUFpQjtBQUN4QixnQkFBVztLQUNYLE1BQU0sUUFBUSwyQkFBMkIsVUFBVSxPQUFPLFNBQVMsZ0JBQWdCO0FBQ25GLGlCQUFZLE1BQU07QUFDbEIsWUFBTyxNQUFNO09BQ1gsUUFBUTtBQUNYLFVBQU0sU0FBUzs7SUFFZjtFQUNGLE1BQU0sY0FBYztBQUNuQixPQUFJLE9BQU87QUFDVixpQkFBYSxNQUFNO0FBQ25CLFlBQVE7OztBQUdWLFNBQU87R0FDTjtHQUNBO0dBQ0Esa0JBQWtCO0dBQ2xCOztDQUVGLE1BQU0sY0FBYyxPQUFPLFVBQVU7QUFDcEMsTUFBSSxTQUFTLE1BQU0sT0FDbEIsUUFBTyxVQUFVO0VBRWxCLE1BQU0sT0FBTyxNQUFNO0VBQ25CLE1BQU0sVUFBVSxxQkFBcUIsS0FBSztFQUMxQyxNQUFNLGtCQUFrQix3QkFBd0IsS0FBSztFQUNyRCxJQUFJLFlBQVk7RUFDaEIsSUFBSTtFQUNKLElBQUk7RUFDSixJQUFJO0VBQ0osSUFBSTtFQUVKLElBQUk7RUFDSixNQUFNLG1CQUFtQixJQUFJLFNBQVMsWUFBWTtBQUNqRCxzQkFBbUI7SUFDbEI7RUFFRixJQUFJO0VBQ0osTUFBTSxxQkFBcUIsSUFBSSxTQUFTLFlBQVk7QUFDbkQsd0JBQXFCO0lBQ3BCO0VBRUYsSUFBSTtFQUNKLElBQUk7RUFDSixNQUFNLHNCQUFzQixJQUFJLFNBQVMsU0FBUyxXQUFXO0FBQzVELHlCQUFzQjtBQUN0Qix3QkFBcUI7SUFDcEI7RUFDRixNQUFNLE1BQU0sWUFBWTtBQUV2QixPQUFJLGFBQWEsWUFBWSxDQUk1QixPQUFNLElBQUksTUFBTSx3Q0FBd0M7QUFFekQsT0FBSSxVQUNILE9BQU0sSUFBSSw2QkFBNkIsU0FBUyxhQUFhLGlEQUFpRCxTQUFTLHlEQUE4RDtBQUV0TCxlQUFZO0FBQ1oscUJBQWtCO0FBRWxCLGdCQUFhLE9BQU87QUFDcEIsbUNBQWdDO0FBRWhDLFNBQU0sWUFBWSxRQUFRLEVBQUUsQ0FBQyxPQUFPLE1BQU0sV0FBVyxLQUFLLEVBQUUsQ0FBQztBQUM3RCxxQ0FBa0MsTUFBTSxvQkFBb0IsU0FBUztBQUVyRSxxQkFBa0IscUJBQXFCLFNBQVMsWUFBWSxnQkFBZ0I7QUFFNUUsdUJBQW9COztBQUVyQixpQ0FBK0IsTUFBTSxvQkFBb0IsU0FBUztBQUVsRSxpQkFBZSxxQkFBcUIsU0FBUyxTQUFTLGdCQUFnQjtBQUN0RSxHQUFDLFlBQVk7QUFDWixPQUFJO0FBQ0gsVUFBTSxXQUFXLE1BQU0sSUFBSTtBQUMzQixRQUFJLENBQUMsVUFDSixPQUFNLElBQUkscUJBQXFCLFNBQVMsYUFBYSxzQ0FBc0MsU0FBUywrQkFBb0MsYUFBYSxnQkFBZ0IsYUFBYSxlQUFlLFNBQVMsUUFBUSxHQUFHO0FBRXROLHlCQUFxQjtZQUNiLE9BQU87QUFDZix1QkFBbUIsTUFBTTthQUNoQjtBQUNULG9DQUFnQztBQUNoQyx1Q0FBbUM7O01BRWpDO0FBRUosTUFBSTtBQUNILFNBQU0sUUFBUSxLQUFLO0lBQ2xCO0lBQ0E7SUFDQSxhQUFhO0lBQ2IsQ0FBQztZQUNPO0FBQ1QsbUNBQWdDO0FBQ2hDLGdCQUFhLE9BQU87O0FBR3JCLFFBQU0sUUFBUSxLQUFLLENBQUMsb0JBQW9CLG9CQUFvQixDQUFDO0FBRzdELE1BQUk7QUFDSCxTQUFNLFFBQVEsS0FBSyxDQUFDLHFCQUFxQixpQkFBaUIsUUFBUSxDQUFDO1lBQzFEO0FBQ1Qsc0NBQW1DO0FBQ25DLG9CQUFpQixPQUFPOzs7QUFHMUIsT0FBTSxZQUFZLEVBQUUsQ0FBQyxPQUFPLE1BQU0sV0FBVyxLQUFLLEVBQUUsQ0FBQztBQUNyRCxLQUFJLFdBQVcsU0FBUyxFQUN2QixPQUFNOztBQUdSLGVBQWUsbUJBQW1CLE9BQU8sZUFBZTtBQUN2RCxPQUFNLGdCQUFnQixlQUFlO0VBQ3BDLE9BQU8sa0JBQWtCLE1BQU07RUFDL0IsVUFBVTtFQUNWLGNBQWM7RUFDZCxhQUFhLE1BQU0sUUFBUSxLQUFLLEtBQUssTUFBTTtFQUMzQyxDQUFDOztBQUVILGVBQWUsb0JBQW9CLE9BQU8sTUFBTSxTQUFTO0FBQ3hELE9BQU0sc0JBSUMsUUFBUSx1QkFBdUIsS0FBSyxRQUFRLENBQUMsRUFDbkQ7RUFDQyxPQUFPLG1CQUFtQixNQUFNO0VBQ2hDLFVBQVU7RUFDVixjQUFjO0VBQ2QsWUFBWSxVQUFVLG1CQUFtQixLQUFLLFNBQVMsTUFBTTtFQUM3RCxhQUFhLE1BQU0sUUFBUSxLQUFLLEtBQUssS0FBSyxTQUFTLE1BQU07RUFDekQsQ0FDRDs7QUFFRixNQUFNLHdCQUFRLElBQUksS0FBSztBQUN2QixNQUFNLGNBQWMsRUFBRTtBQUN0QixNQUFNLHNCQUFzQixFQUFFO0FBQzlCLFNBQVMsZ0JBQWdCLFFBQVE7QUFDaEMsS0FBSSxNQUFNLE1BQU07RUFDZixNQUFNLFlBQVksTUFBTSxLQUFLLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxVQUFVO0FBQ3ZELFVBQU87SUFDTjtJQUNBLEtBQUs7SUFDTCxLQUFLO0lBQ0w7SUFDQTtFQUNGLE1BQU0sSUFBSSxPQUFPLGVBQWUsV0FBVyxZQUFZO0FBQ3ZELE1BQUksR0FBRztBQUNOLHVCQUFvQixLQUFLLEVBQUU7QUFHM0IsS0FBRSxXQUFXLG9CQUFvQixPQUFPLG9CQUFvQixRQUFRLEVBQUUsRUFBRSxFQUFFLFFBQVEsR0FBRzs7QUFFdEYsY0FBWSxTQUFTO0FBQ3JCLFFBQU0sT0FBTzs7O0FBR2YsZUFBZSxzQkFBc0IsUUFBUTtBQUM1QyxpQkFBZ0IsT0FBTztBQUN2QixPQUFNLFFBQVEsSUFBSSxvQkFBb0I7O0FBRXZDLFNBQVMsU0FBUyxJQUFJLElBQUk7Q0FDekIsSUFBSSxPQUFPO0NBQ1gsSUFBSTtBQUNKLFFBQU8sU0FBUyxLQUFLLEdBQUcsTUFBTTtFQUM3QixNQUFNLE1BQU0sU0FBUztBQUNyQixNQUFJLE1BQU0sT0FBTyxJQUFJO0FBQ3BCLFVBQU87QUFDUCxnQkFBYSxZQUFZO0FBQ3pCLGlCQUFjO0FBQ2QsVUFBTyxHQUFHLE1BQU0sTUFBTSxLQUFLOztBQUc1QixrQkFBZ0IsaUJBQWlCLEtBQUssS0FBSyxLQUFLLENBQUMsR0FBRyxLQUFLLEVBQUUsR0FBRzs7O0FBSWhFLE1BQU0sMkJBQTJCLFNBQVMsaUJBQWlCLElBQUk7QUFDL0QsU0FBUyxXQUFXLE9BQU8sTUFBTSxRQUFRO0FBQ3hDLGFBQVksS0FBSztFQUNoQixLQUFLO0VBQ0w7RUFDQTtFQUNBLENBQUM7QUFDRixPQUFNLElBQUksS0FBSyxJQUFJLENBQUMsS0FBSyxRQUFRLEtBQUssS0FBSyxDQUFDO0FBQzVDLDBCQUF5QixPQUFPOztBQUVqQyxlQUFlLGlCQUFpQixRQUFRLFVBQVU7Q0FDakQsTUFBTSxXQUFXLE9BQU8sT0FBTyxTQUFTO0FBQ3hDLEtBQUksYUFBYSxRQUNoQixZQUFXLFNBQVMsT0FBTyxDQUFDLFNBQVM7QUFFdEMsS0FBSSxhQUFhLFdBQ2hCLE9BQU0sUUFBUSxJQUFJLFNBQVMsSUFBSSxPQUFPLE9BQU87QUFDNUMsTUFBSSxPQUFPLE9BQU8sV0FDakI7QUFFRCxRQUFNLDBCQUEwQixJQUFJLENBQUM7R0FDcEMsQ0FBQztLQUVILE1BQUssTUFBTSxNQUFNLFVBQVU7QUFDMUIsTUFBSSxPQUFPLE9BQU8sV0FDakI7QUFFRCxRQUFNLDBCQUEwQixJQUFJLENBQUM7Ozs7OztBQU94QyxTQUFTLHFCQUFxQixNQUFNLFFBQVE7Q0FDM0MsTUFBTSxZQUFZLGtCQUFrQixLQUFLLE1BQU07QUFDL0MsS0FBSSxDQUFDLFVBQVUsT0FBTyxXQUFXLEVBQ2hDLFFBQU87QUFFUixLQUFJLENBQUMsVUFDSixRQUFPO0NBRVIsTUFBTSxRQUFRLE9BQU8sT0FBTyxTQUFTO0FBQ3JDLEtBQUkscUJBQXFCLE9BQ3hCLFFBQU8sVUFBVSxLQUFLLE1BQU0sV0FBVyxHQUFHO1VBQ2hDLE9BQU8sY0FBYyxXQUMvQixRQUFPLFVBQVUsTUFBTTtBQUV4QixRQUFPOztBQUVSLGVBQWUsUUFBUSxNQUFNLFFBQVE7QUFDcEMsT0FBTSxPQUFPLGtCQUFrQixLQUFLO0FBQ3BDLEtBQUksS0FBSyxTQUFTLFNBQVMsS0FBSyxTQUFTLFVBQVU7QUFDbEQsYUFBVyxnQkFBZ0IsTUFBTSxPQUFPO0FBQ3hDLGFBQVcsaUJBQWlCLE1BQU0sT0FBTztBQUN6Qzs7QUFFRCxLQUFJLEtBQUssUUFBUSxVQUFVLFFBQVE7QUFJbEMsYUFBVyxxQkFBcUIsTUFBTSxPQUFPO0FBQzdDOztDQUVELE1BQU0sUUFBUSxLQUFLO0FBQ25CLE1BQUssU0FBUztFQUNiLE9BQU87RUFDUCxXQUFXLFNBQVM7RUFDcEIsWUFBWTtFQUNaO0FBQ0QsWUFBVyxnQkFBZ0IsTUFBTSxPQUFPO0NBQ3hDLE1BQU0scUJBQXFCLGVBQWUsS0FBSztBQUMvQyxnQkFBZSxLQUFLO0NBQ3BCLE1BQU0sUUFBUSxLQUFLLFNBQVMsS0FBSztDQUNqQyxNQUFNLElBQUksT0FBTztDQUNqQixNQUFNLFVBQVUsS0FBSyxXQUFXO0FBQ2hDLE1BQUssSUFBSSxjQUFjLEdBQUcsZUFBZSxTQUFTLGVBQWU7RUFDaEUsTUFBTSxRQUFRLGNBQWMsS0FBSyxNQUFNO0FBQ3ZDLE9BQUssSUFBSSxhQUFhLEdBQUcsY0FBYyxPQUFPLGNBQWM7R0FDM0QsSUFBSSxxQkFBcUIsRUFBRTtBQUszQixTQUFNLG9CQUFvQixPQUFPLE1BQU0sT0FBTyxzQkFBc0I7QUFDbkUsUUFBSTtBQUNILFdBQU0sT0FBTyxrQkFBa0IsTUFBTTtNQUNwQyxPQUFPO01BQ1AsU0FBUztNQUNULENBQUM7QUFDRixVQUFLLE9BQU8sY0FBYztBQUMxQiwwQkFBcUIsTUFBTSxFQUFFLHlCQUF5QixjQUFjLE9BQU8sTUFBTSxjQUFjLFFBQVEsQ0FBQyxLQUFLLFNBQVMsTUFBTSxDQUFDLENBQUM7QUFDOUgsU0FBSSxPQUFPLFFBQ1YsT0FBTSxFQUFFLHVCQUF1QiwwQkFBMEIsT0FBTyxRQUFRLEtBQUssQ0FBQyxDQUFDO1VBQ3pFO01BQ04sTUFBTSxLQUFLLE1BQU0sS0FBSztBQUN0QixVQUFJLENBQUMsR0FDSixPQUFNLElBQUksTUFBTSw0REFBNEQ7QUFFN0UsWUFBTSxFQUFFLHVCQUF1QiwwQkFBMEIsSUFBSSxDQUFDLENBQUM7O0FBRWhFLFdBQU0sT0FBTyxpQkFBaUIsTUFBTTtNQUNuQyxPQUFPO01BQ1AsU0FBUztNQUNULENBQUM7QUFDRixTQUFJLEtBQUssT0FBTyxVQUFVLE9BQ3pCLE1BQUssT0FBTyxRQUFRO2FBRWIsR0FBRztBQUNYLGNBQVMsS0FBSyxRQUFRLEdBQUcsT0FBTyxPQUFPLFlBQVk7O0FBRXBELFFBQUk7QUFDSCxXQUFNLE9BQU8saUJBQWlCLEtBQUs7YUFDM0IsR0FBRztBQUNYLGNBQVMsS0FBSyxRQUFRLEdBQUcsT0FBTyxPQUFPLFlBQVk7O0FBRXBELFFBQUk7QUFDSCxXQUFNLEVBQUUsd0JBQXdCLGNBQWMsT0FBTyxNQUFNLGFBQWEsUUFBUSxDQUFDLEtBQUssU0FBUyxNQUFNLENBQUMsQ0FBQztBQUN2RyxTQUFJLG1CQUFtQixPQUN0QixPQUFNLEVBQUUsc0JBQXNCLGlCQUFpQixRQUFRLG1CQUFtQixDQUFDO0FBSTVFLFdBQU0sdUJBQXVCLEtBQUssU0FBUyxrQkFBa0I7YUFDckQsR0FBRztBQUNYLGNBQVMsS0FBSyxRQUFRLEdBQUcsT0FBTyxPQUFPLFlBQVk7O0FBRXBELFFBQUksS0FBSyxZQUFZLE9BQ3BCLE9BQU0sRUFBRSx5QkFBeUIsY0FBYyxRQUFRLE1BQU0sS0FBSyxZQUFZLFFBQVEsQ0FBQztBQUV4RixRQUFJLEtBQUssT0FBTyxVQUFVLFVBQVUsS0FBSyxVQUFVLE9BQ2xELE9BQU0sRUFBRSx1QkFBdUIsY0FBYyxRQUFRLE1BQU0sS0FBSyxVQUFVLE9BQU8sT0FBTyxTQUFTLE1BQU0sQ0FBQztBQUV6RyxTQUFLLFdBQVc7QUFDaEIsU0FBSyxhQUFhO0FBQ2xCLFVBQU0sT0FBTyxtQkFBbUIsTUFBTTtLQUNyQyxPQUFPO0tBQ1AsU0FBUztLQUNULENBQUM7S0FDRCxDQUFDLE9BQU8sVUFBVTtBQUNuQixhQUFTLEtBQUssUUFBUSxPQUFPLE9BQU8sT0FBTyxZQUFZO0tBQ3REO0FBR0YsT0FBSTtBQUNILFVBQU0sbUJBQW1CLEtBQUssUUFBUTtZQUM5QixHQUFHO0FBQ1gsYUFBUyxLQUFLLFFBQVEsR0FBRyxPQUFPLE9BQU8sWUFBWTs7QUFHcEQsT0FBSSxLQUFLLFFBQVEsV0FBVyxLQUFLLFFBQVEsVUFBVSxRQUFRO0FBQzFELFNBQUssT0FBTztBQUNaLFNBQUssU0FBUztLQUNiLE9BQU87S0FDUCxNQUFNLEtBQUssUUFBUTtLQUNuQixTQUFTO0tBQ1QsVUFBVSxLQUFLLEdBQUc7S0FDbEI7QUFDRCxlQUFXLGlCQUFpQixNQUFNLE9BQU87QUFDekMsbUJBQWUsT0FBVTtBQUN6Qix3QkFBb0I7QUFDcEI7O0FBRUQsT0FBSSxLQUFLLE9BQU8sVUFBVSxPQUN6QjtBQUVELE9BQUksYUFBYSxPQUFPO0FBRXZCLFFBQUksQ0FEZ0IscUJBQXFCLE1BQU0sS0FBSyxPQUFPLE9BQU8sQ0FFakU7QUFFRCxTQUFLLE9BQU8sUUFBUTtBQUNwQixTQUFLLE9BQU8sY0FBYyxLQUFLLE9BQU8sY0FBYyxLQUFLO0lBQ3pELE1BQU0sUUFBUSxjQUFjLEtBQUssTUFBTTtBQUN2QyxRQUFJLFFBQVEsRUFDWCxPQUFNLElBQUksU0FBUyxZQUFZLFdBQVcsU0FBUyxNQUFNLENBQUM7O0FBSTVELGNBQVcsZ0JBQWdCLE1BQU0sT0FBTzs7O0FBSTFDLEtBQUksS0FBSyxNQUNSLEtBQUksS0FBSyxPQUFPLFVBQVUsUUFBUTtFQUNqQyxNQUFNLFFBQVEsNkJBQWEsSUFBSSxNQUFNLHNCQUFzQixDQUFDO0FBQzVELE9BQUssT0FBTyxRQUFRO0FBQ3BCLE9BQUssT0FBTyxTQUFTLENBQUMsTUFBTTtRQUN0QjtBQUNOLE9BQUssT0FBTyxRQUFRO0FBQ3BCLE9BQUssT0FBTyxTQUFTOztBQUd2QixxQkFBb0I7QUFDcEIsZ0JBQWUsT0FBVTtBQUN6QixNQUFLLE9BQU8sV0FBVyxLQUFLLEdBQUc7QUFDL0IsT0FBTSxPQUFPLGlCQUFpQixLQUFLO0FBQ25DLFlBQVcsaUJBQWlCLE1BQU0sT0FBTzs7QUFFMUMsU0FBUyxTQUFTLFFBQVEsS0FBSyxhQUFhO0FBQzNDLEtBQUksZUFBZSxjQUFjO0FBQ2hDLFNBQU8sUUFBUTtBQUNmLFNBQU8sT0FBTyxJQUFJO0FBQ2xCLFNBQU8sVUFBVTtBQUNqQjs7QUFFRCxLQUFJLGVBQWUsbUJBQW1CO0FBQ3JDLFNBQU8sUUFBUTtBQUNmLFNBQU8sT0FBTyxJQUFJO0FBQ2xCOztBQUVELFFBQU8sUUFBUTtDQUNmLE1BQU0sU0FBUyxNQUFNLFFBQVEsSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJO0FBQy9DLE1BQUssTUFBTSxLQUFLLFFBQVE7RUFDdkIsTUFBTSxTQUFTLGFBQWEsaUJBQWlCLEVBQUUsT0FBTyxLQUFLLE1BQU0sYUFBYSxHQUFHLFlBQVksQ0FBQyxHQUFHLENBQUMsYUFBYSxHQUFHLFlBQVksQ0FBQztBQUMvSCxTQUFPLFdBQVcsRUFBRTtBQUNwQixTQUFPLE9BQU8sS0FBSyxHQUFHLE9BQU87OztBQUcvQixTQUFTLG1CQUFtQixPQUFPLFFBQVE7QUFDMUMsT0FBTSxNQUFNLFNBQVMsTUFBTTtBQUMxQixJQUFFLE9BQU87QUFDVCxJQUFFLFNBQVM7R0FDVixHQUFHLEVBQUU7R0FDTCxPQUFPO0dBQ1A7QUFDRCxhQUFXLGlCQUFpQixHQUFHLE9BQU87QUFDdEMsTUFBSSxFQUFFLFNBQVMsUUFDZCxvQkFBbUIsR0FBRyxPQUFPO0dBRTdCOztBQUVILFNBQVMsMEJBQTBCLE9BQU8sUUFBUSxNQUFNO0FBQ3ZELE9BQU0sTUFBTSxTQUFTLE1BQU07QUFDMUIsTUFBSSxDQUFDLEVBQUUsVUFBVSxFQUFFLE9BQU8sVUFBVSxPQUFPO0FBQzFDLEtBQUUsT0FBTztBQUNULEtBQUUsU0FBUztJQUNWLEdBQUcsRUFBRTtJQUNMLE9BQU87SUFDUDtJQUNBO0FBQ0QsY0FBVyxlQUFlLEdBQUcsT0FBTzs7QUFFckMsTUFBSSxFQUFFLFNBQVMsUUFDZCwyQkFBMEIsR0FBRyxRQUFRLEtBQUs7R0FFMUM7O0FBRUgsZUFBZSxTQUFTLE9BQU8sUUFBUTtBQUN0QyxPQUFNLE9BQU8sbUJBQW1CLE1BQU07QUFDdEMsS0FBSSxNQUFNLFFBQVEsVUFBVSxRQUFRO0FBQ25DLHFCQUFtQixPQUFPLE9BQU87QUFFakMsYUFBVyxzQkFBc0IsT0FBTyxPQUFPO0FBQy9DOztDQUVELE1BQU0sUUFBUSxLQUFLO0NBQ25CLE1BQU0sT0FBTyxNQUFNO0FBQ25CLE9BQU0sU0FBUztFQUNkLE9BQU8sU0FBUyxVQUFVLFNBQVMsU0FBUyxPQUFPO0VBQ25ELFdBQVcsU0FBUztFQUNwQjtDQUNELE1BQU0sSUFBSSxPQUFPO0FBQ2pCLFlBQVcsaUJBQWlCLE9BQU8sT0FBTztDQUMxQyxJQUFJLG9CQUFvQixFQUFFO0FBQzFCLEtBQUksTUFBTSxTQUFTLFFBQVE7QUFDMUIsUUFBTSxPQUFPLFFBQVE7QUFDckIsYUFBVyxrQkFBa0IsT0FBTyxPQUFPO1lBQ2pDLE1BQU0sU0FBUyxRQUFRO0FBQ2pDLFFBQU0sT0FBTyxRQUFRO0FBQ3JCLGFBQVcsa0JBQWtCLE9BQU8sT0FBTztRQUNyQztFQUNOLElBQUksV0FBVztBQUNmLE1BQUk7QUFDSCxTQUFNLG1CQUFtQixPQUFPLFlBQVk7QUFDM0MsZUFBVztBQUNYLFFBQUk7QUFFSCxTQUFJO0FBQ0gsMEJBQW9CLE1BQU0sRUFBRSx5QkFBeUIsY0FBYyxPQUFPLE9BQU8sYUFBYSxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7Y0FDdkcsR0FBRztBQUNYLGVBQVMsTUFBTSxRQUFRLEdBQUcsT0FBTyxPQUFPLFlBQVk7QUFDcEQseUJBQW1CLE9BQU8sT0FBTztBQUNqQzs7QUFHRCxTQUFJLE9BQU8sU0FDVixPQUFNLE9BQU8sU0FBUyxNQUFNO1NBRTVCLE1BQUssSUFBSSxjQUFjLHVCQUF1QixNQUFNLENBQ25ELEtBQUksV0FBVyxHQUFHLGVBQWUsS0FDaEMsT0FBTSxRQUFRLElBQUksV0FBVyxLQUFLLE1BQU0sY0FBYyxHQUFHLE9BQU8sQ0FBQyxDQUFDO1VBQzVEO01BQ04sTUFBTSxFQUFFLGFBQWEsT0FBTztBQUM1QixVQUFJLE1BQU0sUUFLVCxjQURlLFFBQVEsQ0FGUixXQUFXLFFBQVEsVUFBVSxNQUFNLFNBQVMsUUFBUSxFQUNyRCxXQUFXLFFBQVEsVUFBVSxNQUFNLFNBQVMsT0FBTyxDQUMzQixFQUFFLFNBQVMsS0FBSyxDQUNsQyxTQUFTLFVBQVUsUUFBUSxPQUFPLFNBQVMsS0FBSyxDQUFDO0FBRXRFLFdBQUssTUFBTSxLQUFLLFdBQ2YsT0FBTSxjQUFjLEdBQUcsT0FBTzs7Y0FLekI7QUFFVCxTQUFJO0FBQ0gsWUFBTSxFQUFFLHdCQUF3QixjQUFjLE9BQU8sT0FBTyxZQUFZLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUN6RixVQUFJLGtCQUFrQixPQUNyQixPQUFNLEVBQUUsdUJBQXVCLGlCQUFpQixRQUFRLGtCQUFrQixDQUFDO0FBRTVFLFVBQUksTUFBTSxTQUFTLE9BQU87T0FDekIsTUFBTSxXQUFXLGFBQWEsZ0JBQWdCLE1BQU0sS0FBSztBQUN6RCxhQUFNLFFBQVEsSUFBSSxTQUFTLEtBQUssWUFBWSxtQkFBbUIsUUFBUSxDQUFDLENBQUM7O2NBRWxFLEdBQUc7QUFDWCxlQUFTLE1BQU0sUUFBUSxHQUFHLE9BQU8sT0FBTyxZQUFZOzs7S0FHckQ7V0FDTSxHQUFHO0FBRVgsT0FBSSxDQUFDLFNBQ0osb0JBQW1CLE9BQU8sT0FBTztBQUVsQyxZQUFTLE1BQU0sUUFBUSxHQUFHLE9BQU8sT0FBTyxZQUFZOztBQUVyRCxNQUFJLE1BQU0sU0FBUyxTQUFTLE1BQU0sU0FBUyxTQUMxQyxLQUFJLENBQUMsT0FBTyxPQUFPLG1CQUFtQixDQUFDLFNBQVMsTUFBTSxFQUFFO0FBQ3ZELFNBQU0sT0FBTyxRQUFRO0FBQ3JCLE9BQUksQ0FBQyxNQUFNLE9BQU8sUUFBUSxRQUFRO0lBQ2pDLE1BQU0sUUFBUSw2QkFBYSxJQUFJLE1BQU0sMEJBQTBCLE1BQU0sT0FBTyxDQUFDO0FBQzdFLFVBQU0sT0FBTyxTQUFTLENBQUMsTUFBTTs7YUFFcEIsVUFBVSxNQUFNLENBQzFCLE9BQU0sT0FBTyxRQUFRO01BRXJCLE9BQU0sT0FBTyxRQUFRO0FBR3ZCLFFBQU0sT0FBTyxXQUFXLEtBQUssR0FBRztBQUNoQyxRQUFNLE9BQU8sa0JBQWtCLE1BQU07QUFDckMsYUFBVyxrQkFBa0IsT0FBTyxPQUFPOzs7QUFHN0MsZUFBZSxjQUFjLEdBQUcsUUFBUTtDQUN2QyxNQUFNLElBQUksT0FBTztBQUNqQixLQUFJLEVBQUUsU0FBUyxPQUNkLFFBQU8sRUFBRSxZQUFZO0VBQ3BCLGtCQUFrQixFQUFFO0VBQ3BCLG9CQUFvQixFQUFFO0VBQ3RCLG9CQUFvQixFQUFFO0VBQ3RCLHVCQUF1QixFQUFFO0VBQ3pCLGtCQUFrQixFQUFFLEtBQUs7RUFDekIsb0JBQW9CLEVBQUUsVUFBVTtFQUNoQyxzQkFBc0IsRUFBRSxVQUFVO0VBQ2xDLFFBQVEsUUFBUSxHQUFHLE9BQU8sQ0FBQztVQUNsQixFQUFFLFNBQVMsUUFDckIsUUFBTyxFQUFFLGFBQWE7RUFDckIsbUJBQW1CLEVBQUU7RUFDckIscUJBQXFCLEVBQUU7RUFDdkIscUJBQXFCLEVBQUU7RUFDdkIsa0JBQWtCLEVBQUUsS0FBSztFQUN6QixvQkFBb0IsRUFBRSxVQUFVO0VBQ2hDLHNCQUFzQixFQUFFLFVBQVU7RUFDbEMsUUFBUSxTQUFTLEdBQUcsT0FBTyxDQUFDOztBQUcvQixlQUFlLFNBQVMsT0FBTyxRQUFRO0FBQ3RDLHlCQUF3QixpQkFBaUIsT0FBTyxPQUFPLGVBQWU7QUFDdEUsTUFBSyxNQUFNLFFBQVEsT0FBTztBQUN6QixNQUFJLENBQUMsS0FBSyxNQUFNLFVBQVUsQ0FBQyxPQUFPLE9BQU8saUJBQ3hDO09BQUksQ0FBQyxLQUFLLFFBQVEsUUFBUSxPQUV6QixNQUFLLFNBQVM7SUFDYixPQUFPO0lBQ1AsUUFBUSxDQUhLLDZCQUFhLElBQUksTUFBTSwrQkFBK0IsS0FBSyxXQUFXLENBQUMsQ0FHckU7SUFDZjs7QUFHSCxRQUFNLE9BQU8sTUFBTSxZQUFZO0dBQzlCLGtCQUFrQixLQUFLO0dBQ3ZCLDZCQUE2QixLQUFLLE1BQU07R0FDeEMsUUFBUSxTQUFTLE1BQU0sT0FBTyxDQUFDOzs7QUFHbEMsTUFBTSxnQ0FBZ0IsSUFBSSxTQUFTO0FBQ25DLFNBQVMsYUFBYSxHQUFHLFlBQVksSUFBSTtBQUN4QyxLQUFJLE9BQU8sZUFBZSxXQUN6QixRQUFPLFlBQVk7QUFFcEIsUUFBTyxJQUFJOztBQUVaLGVBQWUsV0FBVyxPQUFPLFFBQVE7QUFDeEMsUUFBTyxVQUFVO0NBQ2pCLE1BQU0sU0FBUyxPQUFPLFFBQVEsS0FBSyxPQUFPO0FBRzFDLFFBQU8sVUFBVSxXQUFXO0VBRTNCLE1BQU0sUUFBUSxJQUFJLGtCQUFrQix5Q0FBeUMsT0FBTztBQUNwRixtQkFBaUIsQ0FBQyxTQUFTLFNBQVM7QUFDbkMsc0JBQW1CLEtBQUssU0FBUyxNQUFNO0FBQ3ZDLDZCQUEwQixLQUFLLE1BQU0sUUFBUSxNQUFNLFFBQVE7SUFDMUQ7QUFDRixTQUFPLFNBQVMsT0FBTzs7QUFFeEIsS0FBSSxDQUFDLGNBQWMsSUFBSSxPQUFPLEVBQUU7QUFDL0IsU0FBTyx5QkFBeUIsWUFBWTtBQUMzQyxTQUFNLFFBQVEsSUFBSSxDQUFDLEdBQUcsYUFBYSxtQkFBbUIsQ0FBQyxDQUFDLEtBQUssWUFBWSxtQkFBbUIsUUFBUSxDQUFDLENBQUMsQ0FBQyxjQUFjO0FBQ3BILGlCQUFhLGtCQUFrQjtLQUM5QjtJQUNEO0FBQ0YsZ0JBQWMsSUFBSSxPQUFPOztBQUUxQixLQUFJO0VBQ0gsTUFBTSxRQUFRLE1BQU0sS0FBSyxNQUFNLE9BQU8sTUFBTSxXQUFXLElBQUksRUFBRSxTQUFTO0FBQ3RFLFFBQU0sT0FBTyxrQkFBa0IsTUFBTTtFQUNyQyxNQUFNLFFBQVEsTUFBTSxhQUFhLE9BQU8sT0FBTztBQUMvQyxRQUFNLE9BQU8sY0FBYyxNQUFNO0FBQ2pDLFFBQU0sT0FBTyxtQkFBbUIsTUFBTTtBQUN0QyxRQUFNLFNBQVMsT0FBTyxPQUFPO0FBQzdCLFFBQU0sT0FBTyxrQkFBa0IsTUFBTTtBQUNyQyxRQUFNLHNCQUFzQixPQUFPO0FBQ25DLFNBQU87V0FDRTtBQUNULFNBQU8sU0FBUzs7O0FBR2xCLGVBQWUsY0FBYyxPQUFPLFFBQVE7QUFDM0MsUUFBTyxVQUFVO0NBQ2pCLE1BQU0sUUFBUSxNQUFNLEtBQUssTUFBTSxPQUFPLE1BQU0sV0FBVyxJQUFJLEVBQUUsU0FBUztBQUN0RSxPQUFNLE9BQU8sa0JBQWtCLE1BQU07Q0FDckMsTUFBTSxRQUFRLE1BQU0sYUFBYSxPQUFPLE9BQU87QUFDL0MsT0FBTSxPQUFPLGNBQWMsTUFBTTtBQUNqQyxRQUFPOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBc0NSLGVBQWUsZUFBZSxNQUFNLFVBQVU7Q0FDN0MsTUFBTSxTQUFTLFdBQVc7Q0FDMUIsTUFBTSxRQUFRLHVCQUF1QixLQUFLLEtBQUssMkJBQVUsSUFBSSxNQUFNLGNBQWMsRUFBQyxNQUFNO0FBQ3hGLEtBQUksT0FBTztBQUNWLFdBQVMsV0FBVztHQUNuQixNQUFNLE1BQU07R0FDWixNQUFNLE1BQU07R0FDWixRQUFRLE1BQU07R0FDZDtBQUNELE1BQUksU0FBUyxTQUFTLHNCQUNyQixVQUFTLFdBQVcsV0FBVyxTQUFTOztBQUcxQyxLQUFJLE1BQU0sUUFBUSxTQUFTLFlBQVksQ0FDdEMsTUFBSyxNQUFNLGNBQWMsU0FBUyxZQUNqQywwQkFBeUIsV0FBVztBQUl0QyxLQUFJLFNBQVMsU0FBUyxzQkFDckIsUUFBTztBQUVSLEtBQUksQ0FBQyxPQUFPLHFCQUNYLE9BQU0sSUFBSSxNQUFNLDhDQUE4QztBQUUvRCxPQUFNLHNCQUFzQixPQUFPO0NBQ25DLE1BQU0sbUJBQW1CLE1BQU0sT0FBTyxxQkFBcUIsTUFBTSxTQUFTO0FBQzFFLE1BQUssVUFBVSxLQUFLLGlCQUFpQjtBQUNyQyxRQUFPOztBQUVSLE1BQU0sUUFBUSxFQUFFO0FBQ2hCLEtBQUssSUFBSSxJQUFJLElBQUksSUFBSSxJQUFJLElBQ3hCLE9BQU0sS0FBSyxPQUFPLGFBQWEsRUFBRSxDQUFDO0FBRW5DLEtBQUssSUFBSSxJQUFJLElBQUksSUFBSSxLQUFLLElBQ3pCLE9BQU0sS0FBSyxPQUFPLGFBQWEsRUFBRSxDQUFDO0FBRW5DLEtBQUssSUFBSSxJQUFJLEdBQUcsSUFBSSxJQUFJLElBQ3ZCLE9BQU0sS0FBSyxFQUFFLFNBQVMsR0FBRyxDQUFDO0FBRTNCLE1BQU0sS0FBSyxLQUFLLElBQUk7QUFDcEIsU0FBUyxpQkFBaUIsT0FBTztDQUNoQyxJQUFJLFNBQVM7Q0FDYixNQUFNLE1BQU0sTUFBTTtBQUNsQixNQUFLLElBQUksSUFBSSxHQUFHLElBQUksS0FBSyxLQUFLLEVBQzdCLEtBQUksUUFBUSxJQUFJLEdBQUc7RUFDbEIsTUFBTSxLQUFLLE1BQU0sS0FBSyxRQUFRO0VBQzlCLE1BQU0sS0FBSyxNQUFNLEtBQUssTUFBTTtBQUM1QixZQUFVLE1BQU07QUFDaEIsWUFBVSxNQUFNO0FBQ2hCLFlBQVU7WUFDQSxRQUFRLElBQUksR0FBRztFQUN6QixNQUFNLEtBQUssTUFBTSxLQUFLLFFBQVE7RUFDOUIsTUFBTSxLQUFLLE1BQU0sS0FBSyxNQUFNLEtBQUssTUFBTSxJQUFJLEtBQUssUUFBUTtFQUN4RCxNQUFNLEtBQUssTUFBTSxJQUFJLEtBQUssT0FBTztBQUNqQyxZQUFVLE1BQU07QUFDaEIsWUFBVSxNQUFNO0FBQ2hCLFlBQVUsTUFBTTtBQUNoQixZQUFVO1FBQ0o7RUFDTixNQUFNLEtBQUssTUFBTSxLQUFLLFFBQVE7RUFDOUIsTUFBTSxLQUFLLE1BQU0sS0FBSyxNQUFNLEtBQUssTUFBTSxJQUFJLEtBQUssUUFBUTtFQUN4RCxNQUFNLEtBQUssTUFBTSxJQUFJLEtBQUssT0FBTyxLQUFLLE1BQU0sSUFBSSxLQUFLLFFBQVE7RUFDN0QsTUFBTSxJQUFJLE1BQU0sSUFBSSxLQUFLO0FBQ3pCLFlBQVUsTUFBTTtBQUNoQixZQUFVLE1BQU07QUFDaEIsWUFBVSxNQUFNO0FBQ2hCLFlBQVUsTUFBTTs7QUFHbEIsUUFBTzs7Ozs7Ozs7QUFRUixTQUFTLHFCQUFxQixNQUFNLFNBQVM7QUFFNUMsV0FBVSxRQUFRLGNBQWM7QUFDL0IsTUFBSSxDQUFDLEtBQUssU0FDVDtFQUVELE1BQU0sUUFBUSxLQUFLLFNBQVMsUUFBUSxRQUFRO0FBQzVDLE1BQUksVUFBVSxHQUNiLE1BQUssU0FBUyxPQUFPLE9BQU8sRUFBRTtHQUU5QjtBQUVGLEtBQUksQ0FBQyxLQUFLLFNBQ1QsTUFBSyxXQUFXLEVBQUU7QUFFbkIsTUFBSyxTQUFTLEtBQUssUUFBUTtBQUMzQixRQUFPOzs7Ozs7Ozs7Ozs7QUFZUixTQUFTLHlCQUF5QixZQUFZO0FBQzdDLEtBQUksV0FBVyxRQUFRLFFBQVEsQ0FBQyxXQUFXLEtBQzFDLE9BQU0sSUFBSSxVQUFVLHlFQUF5RTtBQUU5RixLQUFJLFdBQVcsUUFBUSxXQUFXLEtBQ2pDLE9BQU0sSUFBSSxVQUFVLHVGQUF1RjtBQUc1RyxLQUFJLFdBQVcsZ0JBQWdCLFdBQzlCLFlBQVcsT0FBTyxpQkFBaUIsV0FBVyxLQUFLIn0=
|