@discordeno/gateway 21.0.1-next.fca0b40 → 22.0.1-next.069e4fa
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/dist/{types/Shard.d.ts → Shard.d.ts} +7 -8
- package/dist/Shard.d.ts.map +1 -0
- package/dist/Shard.js +660 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/{types/manager.d.ts → manager.d.ts} +0 -5
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +453 -0
- package/dist/{types/types.d.ts → types.d.ts} +5 -3
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +45 -0
- package/package.json +18 -25
- package/dist/cjs/Shard.cjs +0 -691
- package/dist/cjs/index.cjs +0 -22
- package/dist/cjs/manager.cjs +0 -479
- package/dist/cjs/types.cjs +0 -64
- package/dist/esm/Shard.js +0 -627
- package/dist/esm/index.js +0 -5
- package/dist/esm/manager.js +0 -464
- package/dist/esm/types.js +0 -43
- package/dist/types/Shard.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/manager.d.ts.map +0 -1
- package/dist/types/types.d.ts.map +0 -1
- /package/dist/{types/index.d.ts → index.d.ts} +0 -0
package/dist/cjs/Shard.cjs
DELETED
|
@@ -1,691 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get DiscordenoShard () {
|
|
13
|
-
return DiscordenoShard;
|
|
14
|
-
},
|
|
15
|
-
get default () {
|
|
16
|
-
return _default;
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
const _nodebuffer = require("node:buffer");
|
|
20
|
-
const _nodezlib = require("node:zlib");
|
|
21
|
-
const _types = require("@discordeno/types");
|
|
22
|
-
const _utils = require("@discordeno/utils");
|
|
23
|
-
const _ws = /*#__PURE__*/ _interop_require_default(require("ws"));
|
|
24
|
-
const _types1 = require("./types.cjs");
|
|
25
|
-
function _interop_require_default(obj) {
|
|
26
|
-
return obj && obj.__esModule ? obj : {
|
|
27
|
-
default: obj
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
function _getRequireWildcardCache(nodeInterop) {
|
|
31
|
-
if (typeof WeakMap !== "function") return null;
|
|
32
|
-
var cacheBabelInterop = new WeakMap();
|
|
33
|
-
var cacheNodeInterop = new WeakMap();
|
|
34
|
-
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
35
|
-
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
36
|
-
})(nodeInterop);
|
|
37
|
-
}
|
|
38
|
-
function _interop_require_wildcard(obj, nodeInterop) {
|
|
39
|
-
if (!nodeInterop && obj && obj.__esModule) {
|
|
40
|
-
return obj;
|
|
41
|
-
}
|
|
42
|
-
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
43
|
-
return {
|
|
44
|
-
default: obj
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
var cache = _getRequireWildcardCache(nodeInterop);
|
|
48
|
-
if (cache && cache.has(obj)) {
|
|
49
|
-
return cache.get(obj);
|
|
50
|
-
}
|
|
51
|
-
var newObj = {
|
|
52
|
-
__proto__: null
|
|
53
|
-
};
|
|
54
|
-
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
55
|
-
for(var key in obj){
|
|
56
|
-
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
57
|
-
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
58
|
-
if (desc && (desc.get || desc.set)) {
|
|
59
|
-
Object.defineProperty(newObj, key, desc);
|
|
60
|
-
} else {
|
|
61
|
-
newObj[key] = obj[key];
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
newObj.default = obj;
|
|
66
|
-
if (cache) {
|
|
67
|
-
cache.set(obj, newObj);
|
|
68
|
-
}
|
|
69
|
-
return newObj;
|
|
70
|
-
}
|
|
71
|
-
const ZLIB_SYNC_FLUSH = new Uint8Array([
|
|
72
|
-
0x0,
|
|
73
|
-
0x0,
|
|
74
|
-
0xff,
|
|
75
|
-
0xff
|
|
76
|
-
]);
|
|
77
|
-
let fzstd;
|
|
78
|
-
/** Since fzstd is an optional dependency, we need to import it lazily. */ async function getFZStd() {
|
|
79
|
-
return fzstd ??= await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("fzstd")));
|
|
80
|
-
}
|
|
81
|
-
let DiscordenoShard = class DiscordenoShard {
|
|
82
|
-
constructor(options){
|
|
83
|
-
/** The maximum of requests which can be send to discord per rate limit tick. Typically this value should not be changed. */ this.maxRequestsPerRateLimitTick = 120;
|
|
84
|
-
/** The previous payload sequence number. */ this.previousSequenceNumber = null;
|
|
85
|
-
/** In which interval (in milliseconds) the gateway resets it's rate limit. */ this.rateLimitResetInterval = 60000;
|
|
86
|
-
/** Current internal state of the this. */ this.state = _types1.ShardState.Offline;
|
|
87
|
-
/** The url provided by discord to use when resuming a connection for this this. */ this.resumeGatewayUrl = '';
|
|
88
|
-
/** The shard related event handlers. */ this.events = {};
|
|
89
|
-
/** Cache for pending gateway requests which should have been send while the gateway went offline. */ this.offlineSendQueue = [];
|
|
90
|
-
/** Resolve internal waiting states. Mapped by SelectedEvents => ResolveFunction */ this.resolves = new Map();
|
|
91
|
-
/**
|
|
92
|
-
* Is the shard going offline?
|
|
93
|
-
*
|
|
94
|
-
* @remarks
|
|
95
|
-
* This will be true if the close method has been called with either 1000 or 1001
|
|
96
|
-
*
|
|
97
|
-
* @internal
|
|
98
|
-
* This is for internal purposes only, and subject to breaking changes.
|
|
99
|
-
*/ this.goingOffline = false;
|
|
100
|
-
/** Text decoder used for compressed payloads. */ this.textDecoder = new TextDecoder();
|
|
101
|
-
/** ZLib inflate buffer. */ this.inflateBuffer = null;
|
|
102
|
-
/** Queue for compressed payloads for Zstd Decompress */ this.decompressionPromisesQueue = [];
|
|
103
|
-
this.id = options.id;
|
|
104
|
-
this.connection = options.connection;
|
|
105
|
-
this.events = options.events;
|
|
106
|
-
this.logger = options.logger ?? _utils.logger;
|
|
107
|
-
this.heart = {
|
|
108
|
-
acknowledged: false,
|
|
109
|
-
interval: 45000
|
|
110
|
-
};
|
|
111
|
-
if (options.requestIdentify) this.requestIdentify = options.requestIdentify;
|
|
112
|
-
if (options.makePresence) this.makePresence = options.makePresence;
|
|
113
|
-
this.bucket = new _utils.LeakyBucket({
|
|
114
|
-
max: this.calculateSafeRequests(),
|
|
115
|
-
refillAmount: this.calculateSafeRequests(),
|
|
116
|
-
refillInterval: 60000,
|
|
117
|
-
logger: this.logger
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
/** The gateway configuration which is used to connect to Discord. */ get gatewayConfig() {
|
|
121
|
-
return this.connection;
|
|
122
|
-
}
|
|
123
|
-
/** The url to connect to. Initially this is the discord gateway url, and then is switched to resume gateway url once a READY is received. */ get connectionUrl() {
|
|
124
|
-
// Use || and not ?? here. ?? will cause a bug.
|
|
125
|
-
return this.resumeGatewayUrl || this.gatewayConfig.url;
|
|
126
|
-
}
|
|
127
|
-
/** Calculate the amount of requests which can safely be made per rate limit interval, before the gateway gets disconnected due to an exceeded rate limit. */ calculateSafeRequests() {
|
|
128
|
-
// * 2 adds extra safety layer for discords OP 1 requests that we need to respond to
|
|
129
|
-
const safeRequests = this.maxRequestsPerRateLimitTick - Math.ceil(this.rateLimitResetInterval / this.heart.interval) * 2;
|
|
130
|
-
return safeRequests < 0 ? 0 : safeRequests;
|
|
131
|
-
}
|
|
132
|
-
async checkOffline(highPriority) {
|
|
133
|
-
if (this.isOpen()) return;
|
|
134
|
-
return await new Promise((resolve)=>{
|
|
135
|
-
// Higher priority requests get added at the beginning of the array.
|
|
136
|
-
if (highPriority) this.offlineSendQueue.unshift(resolve);
|
|
137
|
-
else this.offlineSendQueue.push(resolve);
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
/** Close the socket connection to discord if present. */ async close(code, reason) {
|
|
141
|
-
this.logger.debug(`[Shard] Request for Shard #${this.id} to close the socket with code ${code}.`);
|
|
142
|
-
if (this.socket?.readyState !== _ws.default.OPEN) {
|
|
143
|
-
this.logger.debug(`[Shard] Shard #${this.id}'s ready state is ${this.socket?.readyState}, Unable to close.`);
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
this.goingOffline = code === _types.GatewayCloseEventCodes.NormalClosure || code === _types.GatewayCloseEventCodes.GoingAway;
|
|
147
|
-
// This has to be created before the actual call to socket.close as for example Bun calls socket.onclose immediately on the .close() call instead of waiting for the connection to end
|
|
148
|
-
const promise = new Promise((resolve)=>{
|
|
149
|
-
this.resolveAfterClose = resolve;
|
|
150
|
-
});
|
|
151
|
-
this.socket.close(code, reason);
|
|
152
|
-
this.logger.debug(`[Shard] Waiting for Shard #${this.id} to close the socket with code ${code}.`);
|
|
153
|
-
// We need to wait for the socket to be fully closed, otherwise there'll be race condition issues if we try to connect again, resulting in unexpected behavior.
|
|
154
|
-
await promise;
|
|
155
|
-
this.logger.debug(`[Shard] Shard #${this.id} closed the socket with code ${code}.`);
|
|
156
|
-
// Reset the resolveAfterClose function after it has been resolved.
|
|
157
|
-
this.resolveAfterClose = undefined;
|
|
158
|
-
}
|
|
159
|
-
/** Connect the shard with the gateway and start heartbeating. This will not identify the shard to the gateway. */ async connect() {
|
|
160
|
-
// Only set the shard to `Connecting` state,
|
|
161
|
-
// if the connection request does not come from an identify or resume action.
|
|
162
|
-
if (![
|
|
163
|
-
_types1.ShardState.Identifying,
|
|
164
|
-
_types1.ShardState.Resuming
|
|
165
|
-
].includes(this.state)) {
|
|
166
|
-
this.state = _types1.ShardState.Connecting;
|
|
167
|
-
}
|
|
168
|
-
this.logger.debug(`[Shard] Connecting Shard #${this.id} socket.`);
|
|
169
|
-
this.events.connecting?.(this);
|
|
170
|
-
const url = new URL(this.connectionUrl);
|
|
171
|
-
url.searchParams.set('v', this.gatewayConfig.version.toString());
|
|
172
|
-
url.searchParams.set('encoding', 'json');
|
|
173
|
-
// Set the compress url param and initialize the decompression contexts
|
|
174
|
-
if (this.gatewayConfig.transportCompression) {
|
|
175
|
-
url.searchParams.set('compress', this.gatewayConfig.transportCompression);
|
|
176
|
-
if (this.gatewayConfig.transportCompression === _types1.TransportCompression.zlib) {
|
|
177
|
-
this.inflateBuffer = null;
|
|
178
|
-
this.inflate = (0, _nodezlib.createInflate)({
|
|
179
|
-
finishFlush: _nodezlib.constants.Z_SYNC_FLUSH,
|
|
180
|
-
chunkSize: 64 * 1024
|
|
181
|
-
});
|
|
182
|
-
this.inflate.on('error', (e)=>{
|
|
183
|
-
this.logger.error('The was an error in decompressing a ZLib compressed payload', e);
|
|
184
|
-
});
|
|
185
|
-
this.inflate.on('data', (data)=>{
|
|
186
|
-
if (!(data instanceof Uint8Array)) return;
|
|
187
|
-
if (this.inflateBuffer) {
|
|
188
|
-
const newBuffer = new Uint8Array(this.inflateBuffer.byteLength + data.byteLength);
|
|
189
|
-
newBuffer.set(this.inflateBuffer);
|
|
190
|
-
newBuffer.set(data, this.inflateBuffer.byteLength);
|
|
191
|
-
this.inflateBuffer = newBuffer;
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
this.inflateBuffer = data;
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
if (this.gatewayConfig.transportCompression === _types1.TransportCompression.zstd) {
|
|
198
|
-
const fzstd = await getFZStd().catch(()=>{
|
|
199
|
-
this.logger.warn('[Shard] "fzstd" is not installed. Disabled transport compression.');
|
|
200
|
-
url.searchParams.delete('compress');
|
|
201
|
-
return null;
|
|
202
|
-
});
|
|
203
|
-
if (fzstd) {
|
|
204
|
-
this.zstdDecompress = new fzstd.Decompress((data)=>{
|
|
205
|
-
const decodedData = this.textDecoder.decode(data);
|
|
206
|
-
const parsedData = JSON.parse(decodedData);
|
|
207
|
-
this.decompressionPromisesQueue.shift()?.(parsedData);
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
if (this.gatewayConfig.compress && this.gatewayConfig.transportCompression) {
|
|
213
|
-
this.logger.warn('[Shard] Payload compression has been disabled since transport compression is enabled as well.');
|
|
214
|
-
this.gatewayConfig.compress = false;
|
|
215
|
-
}
|
|
216
|
-
// We check for built-in WebSocket implementations in Bun or Deno, NodeJS v22 has an implementation too but it seems to be less optimized so for now it is better to use the ws npm package
|
|
217
|
-
const shouldUseBuiltin = Reflect.has(globalThis, 'WebSocket') && (Reflect.has(globalThis, 'Bun') || Reflect.has(globalThis, 'Deno'));
|
|
218
|
-
// @ts-expect-error NodeWebSocket doesn't support "dispatchEvent", and while we don't use it, it is required on the "WebSocket" type
|
|
219
|
-
const socket = shouldUseBuiltin ? new WebSocket(url) : new _ws.default(url);
|
|
220
|
-
this.socket = socket;
|
|
221
|
-
// By default WebSocket will give us a Blob, this changes it so that it gives us an ArrayBuffer
|
|
222
|
-
socket.binaryType = 'arraybuffer';
|
|
223
|
-
socket.onerror = (event)=>this.handleError(event);
|
|
224
|
-
socket.onclose = (closeEvent)=>this.handleClose(closeEvent);
|
|
225
|
-
socket.onmessage = (messageEvent)=>this.handleMessage(messageEvent);
|
|
226
|
-
return await new Promise((resolve)=>{
|
|
227
|
-
socket.onopen = ()=>{
|
|
228
|
-
// Only set the shard to `Unidentified` state if the connection request does not come from an identify or resume action.
|
|
229
|
-
if (![
|
|
230
|
-
_types1.ShardState.Identifying,
|
|
231
|
-
_types1.ShardState.Resuming
|
|
232
|
-
].includes(this.state)) {
|
|
233
|
-
this.state = _types1.ShardState.Unidentified;
|
|
234
|
-
}
|
|
235
|
-
this.logger.debug(`[Shard] Shard #${this.id} socket connected.`);
|
|
236
|
-
this.events.connected?.(this);
|
|
237
|
-
resolve(this);
|
|
238
|
-
};
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Identify the shard to the gateway. If not connected, this will also connect the shard to the gateway.
|
|
243
|
-
* @param bypassRequest - Whether to bypass the requestIdentify handler and identify immediately. This should be used carefully as it can cause invalid sessions.
|
|
244
|
-
*/ async identify(bypassRequest = false) {
|
|
245
|
-
// A new identify has been requested even though there is already a connection open.
|
|
246
|
-
// Therefore we need to close the old connection and heartbeating before creating a new one.
|
|
247
|
-
if (this.isOpen()) {
|
|
248
|
-
this.logger.debug(`[Shard] Identifying open Shard #${this.id}, closing the connection`);
|
|
249
|
-
await this.close(_types1.ShardSocketCloseCodes.ReIdentifying, 'Re-identifying closure of old connection.');
|
|
250
|
-
}
|
|
251
|
-
if (!bypassRequest) {
|
|
252
|
-
await this.requestIdentify();
|
|
253
|
-
}
|
|
254
|
-
this.state = _types1.ShardState.Identifying;
|
|
255
|
-
this.events.identifying?.(this);
|
|
256
|
-
// It is possible that the shard is in Heartbeating state but not identified,
|
|
257
|
-
// so check whether there is already a gateway connection existing.
|
|
258
|
-
// If not we need to create one before we identify.
|
|
259
|
-
if (!this.isOpen()) {
|
|
260
|
-
await this.connect();
|
|
261
|
-
}
|
|
262
|
-
this.logger.debug(`[Shard] Sending Identify payload for Shard #${this.id}.`);
|
|
263
|
-
this.send({
|
|
264
|
-
op: _types.GatewayOpcodes.Identify,
|
|
265
|
-
d: {
|
|
266
|
-
token: `Bot ${this.gatewayConfig.token}`,
|
|
267
|
-
compress: this.gatewayConfig.compress,
|
|
268
|
-
properties: this.gatewayConfig.properties,
|
|
269
|
-
intents: this.gatewayConfig.intents,
|
|
270
|
-
shard: [
|
|
271
|
-
this.id,
|
|
272
|
-
this.gatewayConfig.totalShards
|
|
273
|
-
],
|
|
274
|
-
presence: await this.makePresence()
|
|
275
|
-
}
|
|
276
|
-
}, true);
|
|
277
|
-
return await new Promise((resolve)=>{
|
|
278
|
-
this.resolves.set('READY', ()=>{
|
|
279
|
-
resolve();
|
|
280
|
-
});
|
|
281
|
-
// When identifying too fast, Discord sends an invalid session payload.
|
|
282
|
-
// This can safely be ignored though and the shard starts a new identify action.
|
|
283
|
-
this.resolves.set('INVALID_SESSION', ()=>{
|
|
284
|
-
this.resolves.delete('READY');
|
|
285
|
-
resolve();
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
/** Check whether the connection to Discord is currently open. */ isOpen() {
|
|
290
|
-
return this.socket?.readyState === _ws.default.OPEN;
|
|
291
|
-
}
|
|
292
|
-
/** Attempt to resume the previous shards session with the gateway. */ async resume() {
|
|
293
|
-
this.logger.debug(`[Shard] Resuming Shard #${this.id}`);
|
|
294
|
-
// It has been requested to resume the Shards session.
|
|
295
|
-
// It's possible that the shard is still connected with Discord's gateway therefore we need to forcefully close it.
|
|
296
|
-
if (this.isOpen()) {
|
|
297
|
-
this.logger.debug(`[Shard] Resuming open Shard #${this.id}, closing the connection`);
|
|
298
|
-
await this.close(_types1.ShardSocketCloseCodes.ResumeClosingOldConnection, 'Reconnecting the shard, closing old connection.');
|
|
299
|
-
}
|
|
300
|
-
// Shard has never identified, so we cannot resume.
|
|
301
|
-
if (!this.sessionId) {
|
|
302
|
-
this.logger.debug(`[Shard] Trying to resume Shard #${this.id} without the session id. Identifying the shard instead.`);
|
|
303
|
-
await this.identify();
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
this.state = _types1.ShardState.Resuming;
|
|
307
|
-
// Before we can resume, we need to create a new connection with Discord's gateway.
|
|
308
|
-
await this.connect();
|
|
309
|
-
this.logger.debug(`[Shard] Resuming Shard #${this.id} connected. Session id: ${this.sessionId} | Sequence: ${this.previousSequenceNumber}`);
|
|
310
|
-
this.send({
|
|
311
|
-
op: _types.GatewayOpcodes.Resume,
|
|
312
|
-
d: {
|
|
313
|
-
token: `Bot ${this.gatewayConfig.token}`,
|
|
314
|
-
session_id: this.sessionId,
|
|
315
|
-
seq: this.previousSequenceNumber ?? 0
|
|
316
|
-
}
|
|
317
|
-
}, true);
|
|
318
|
-
return await new Promise((resolve)=>{
|
|
319
|
-
this.resolves.set('RESUMED', ()=>resolve());
|
|
320
|
-
// If it is attempted to resume with an invalid session id, Discord sends an invalid session payload
|
|
321
|
-
// Not erroring here since it is easy that this happens, also it would be not catchable
|
|
322
|
-
this.resolves.set('INVALID_SESSION', ()=>{
|
|
323
|
-
this.resolves.delete('RESUMED');
|
|
324
|
-
resolve();
|
|
325
|
-
});
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Send a message to Discord.
|
|
330
|
-
* @param highPriority - Whether this message should be send asap.
|
|
331
|
-
*/ async send(message, highPriority = false) {
|
|
332
|
-
// Before acquiring a token from the bucket, check whether the shard is currently offline or not.
|
|
333
|
-
// Else bucket and token wait time just get wasted.
|
|
334
|
-
await this.checkOffline(highPriority);
|
|
335
|
-
await this.bucket.acquire(highPriority);
|
|
336
|
-
// It's possible, that the shard went offline after a token has been acquired from the bucket.
|
|
337
|
-
await this.checkOffline(highPriority);
|
|
338
|
-
this.socket?.send(JSON.stringify(message));
|
|
339
|
-
}
|
|
340
|
-
/** Shutdown the this. Forcefully disconnect the shard from Discord. The shard may not attempt to reconnect with Discord. */ async shutdown() {
|
|
341
|
-
await this.close(_types1.ShardSocketCloseCodes.Shutdown, 'Shard shutting down.');
|
|
342
|
-
this.state = _types1.ShardState.Offline;
|
|
343
|
-
}
|
|
344
|
-
/** Handle a gateway connection error */ handleError(error) {
|
|
345
|
-
this.logger.error(`[Shard] There was an error connecting Shard #${this.id}.`, error);
|
|
346
|
-
}
|
|
347
|
-
/** Handle a gateway connection close. */ async handleClose(close) {
|
|
348
|
-
this.socket = undefined;
|
|
349
|
-
this.stopHeartbeating();
|
|
350
|
-
// Clear the zlib/zstd data
|
|
351
|
-
this.inflate = undefined;
|
|
352
|
-
this.zstdDecompress = undefined;
|
|
353
|
-
this.inflateBuffer = null;
|
|
354
|
-
this.decompressionPromisesQueue = [];
|
|
355
|
-
this.logger.debug(`[Shard] Shard #${this.id} closed with code ${close.code}${close.reason ? `, and reason: ${close.reason}` : ''}.`);
|
|
356
|
-
// Resolve the close promise if it exists
|
|
357
|
-
this.resolveAfterClose?.(close);
|
|
358
|
-
switch(close.code){
|
|
359
|
-
case _types1.ShardSocketCloseCodes.TestingFinished:
|
|
360
|
-
{
|
|
361
|
-
this.state = _types1.ShardState.Offline;
|
|
362
|
-
this.events.disconnected?.(this);
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
// On these codes a manual start will be done.
|
|
366
|
-
case _types1.ShardSocketCloseCodes.Shutdown:
|
|
367
|
-
case _types1.ShardSocketCloseCodes.ReIdentifying:
|
|
368
|
-
case _types1.ShardSocketCloseCodes.Resharded:
|
|
369
|
-
case _types1.ShardSocketCloseCodes.ResumeClosingOldConnection:
|
|
370
|
-
{
|
|
371
|
-
this.state = _types1.ShardState.Disconnected;
|
|
372
|
-
this.events.disconnected?.(this);
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
// When these codes are received something went really wrong.
|
|
376
|
-
// On those we cannot start a reconnect attempt.
|
|
377
|
-
case _types.GatewayCloseEventCodes.AuthenticationFailed:
|
|
378
|
-
case _types.GatewayCloseEventCodes.InvalidShard:
|
|
379
|
-
case _types.GatewayCloseEventCodes.ShardingRequired:
|
|
380
|
-
case _types.GatewayCloseEventCodes.InvalidApiVersion:
|
|
381
|
-
case _types.GatewayCloseEventCodes.InvalidIntents:
|
|
382
|
-
case _types.GatewayCloseEventCodes.DisallowedIntents:
|
|
383
|
-
{
|
|
384
|
-
this.state = _types1.ShardState.Offline;
|
|
385
|
-
this.events.disconnected?.(this);
|
|
386
|
-
throw new Error(close.reason || 'Discord gave no reason! GG! You broke Discord!');
|
|
387
|
-
}
|
|
388
|
-
// Gateway connection closes which require a new identify.
|
|
389
|
-
case _types.GatewayCloseEventCodes.NotAuthenticated:
|
|
390
|
-
case _types.GatewayCloseEventCodes.InvalidSeq:
|
|
391
|
-
case _types.GatewayCloseEventCodes.SessionTimedOut:
|
|
392
|
-
{
|
|
393
|
-
this.logger.debug(`[Shard] Shard #${this.id} closed requiring re-identify.`);
|
|
394
|
-
this.state = _types1.ShardState.Identifying;
|
|
395
|
-
this.events.disconnected?.(this);
|
|
396
|
-
await this.identify();
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
// NOTE: This case must always be right above the cases that runs with default case because of how switch works when you don't break / return, more info below.
|
|
400
|
-
case _types.GatewayCloseEventCodes.NormalClosure:
|
|
401
|
-
case _types.GatewayCloseEventCodes.GoingAway:
|
|
402
|
-
{
|
|
403
|
-
// If the shard is marked as goingOffline, it stays disconnected.
|
|
404
|
-
if (this.goingOffline) {
|
|
405
|
-
this.state = _types1.ShardState.Disconnected;
|
|
406
|
-
this.events.disconnected?.(this);
|
|
407
|
-
this.goingOffline = false;
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
// Otherwise, we want the shard to go through the default case where it gets resumed, as it might be an unexpected closure from Discord or Cloudflare for example, so we don't use break / return here.
|
|
411
|
-
}
|
|
412
|
-
// Gateway connection closes on which a resume is allowed.
|
|
413
|
-
case _types.GatewayCloseEventCodes.UnknownError:
|
|
414
|
-
case _types.GatewayCloseEventCodes.UnknownOpcode:
|
|
415
|
-
case _types.GatewayCloseEventCodes.DecodeError:
|
|
416
|
-
case _types.GatewayCloseEventCodes.RateLimited:
|
|
417
|
-
case _types.GatewayCloseEventCodes.AlreadyAuthenticated:
|
|
418
|
-
default:
|
|
419
|
-
{
|
|
420
|
-
// We don't want to get into an infinite loop where we resume forever, so if we were already resuming we identify instead
|
|
421
|
-
this.state = this.state === _types1.ShardState.Resuming ? _types1.ShardState.Identifying : _types1.ShardState.Resuming;
|
|
422
|
-
this.events.disconnected?.(this);
|
|
423
|
-
if (this.state === _types1.ShardState.Resuming) {
|
|
424
|
-
await this.resume();
|
|
425
|
-
} else {
|
|
426
|
-
await this.identify();
|
|
427
|
-
}
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
/** Handle an incoming gateway message. */ async handleMessage(message) {
|
|
433
|
-
// The ws npm package will use a Buffer, while the global built-in will use ArrayBuffer
|
|
434
|
-
const isCompressed = message.data instanceof ArrayBuffer || message.data instanceof _nodebuffer.Buffer;
|
|
435
|
-
const data = isCompressed ? await this.decompressPacket(message.data) : JSON.parse(message.data);
|
|
436
|
-
// Check if the decompression was not successful
|
|
437
|
-
if (!data) return;
|
|
438
|
-
await this.handleDiscordPacket(data);
|
|
439
|
-
}
|
|
440
|
-
/**
|
|
441
|
-
* Decompress a zlib/zstd compressed packet
|
|
442
|
-
*
|
|
443
|
-
* @private
|
|
444
|
-
*/ async decompressPacket(data) {
|
|
445
|
-
// A buffer is a Uint8Array under the hood. An ArrayBuffer is generic, so we need to create the Uint8Array that uses the whole ArrayBuffer
|
|
446
|
-
const compressedData = data instanceof _nodebuffer.Buffer ? data : new Uint8Array(data);
|
|
447
|
-
if (this.gatewayConfig.transportCompression === _types1.TransportCompression.zlib) {
|
|
448
|
-
if (!this.inflate) {
|
|
449
|
-
this.logger.fatal('[Shard] zlib-stream transport compression was enabled but no instance of Inflate was found.');
|
|
450
|
-
return null;
|
|
451
|
-
}
|
|
452
|
-
// Alias, used to avoid some null checks in the Promise constructor
|
|
453
|
-
const inflate = this.inflate;
|
|
454
|
-
const writePromise = new Promise((resolve, reject)=>{
|
|
455
|
-
inflate.write(compressedData, 'binary', (error)=>error ? reject(error) : resolve());
|
|
456
|
-
});
|
|
457
|
-
if (!endsWithMarker(compressedData, ZLIB_SYNC_FLUSH)) return null;
|
|
458
|
-
await writePromise;
|
|
459
|
-
if (!this.inflateBuffer) {
|
|
460
|
-
this.logger.warn('[Shard] The ZLib inflate buffer was cleared at an unexpected moment.');
|
|
461
|
-
return null;
|
|
462
|
-
}
|
|
463
|
-
const decodedData = this.textDecoder.decode(this.inflateBuffer);
|
|
464
|
-
this.inflateBuffer = null;
|
|
465
|
-
return JSON.parse(decodedData);
|
|
466
|
-
}
|
|
467
|
-
if (this.gatewayConfig.transportCompression === _types1.TransportCompression.zstd) {
|
|
468
|
-
if (!this.zstdDecompress) {
|
|
469
|
-
this.logger.fatal('[Shard] zstd-stream transport compression was enabled but no instance of Decompress was found.');
|
|
470
|
-
return null;
|
|
471
|
-
}
|
|
472
|
-
this.zstdDecompress.push(compressedData);
|
|
473
|
-
const decompressionPromise = new Promise((r)=>this.decompressionPromisesQueue.push(r));
|
|
474
|
-
return await decompressionPromise;
|
|
475
|
-
}
|
|
476
|
-
if (this.gatewayConfig.compress) {
|
|
477
|
-
const decompressed = (0, _nodezlib.inflateSync)(compressedData);
|
|
478
|
-
const decodedData = this.textDecoder.decode(decompressed);
|
|
479
|
-
return JSON.parse(decodedData);
|
|
480
|
-
}
|
|
481
|
-
return null;
|
|
482
|
-
}
|
|
483
|
-
/** Handles a incoming gateway packet. */ async handleDiscordPacket(packet) {
|
|
484
|
-
// Edge case start: https://github.com/discordeno/discordeno/issues/2311
|
|
485
|
-
this.heart.lastAck = Date.now();
|
|
486
|
-
this.heart.acknowledged = true;
|
|
487
|
-
// Edge case end!
|
|
488
|
-
switch(packet.op){
|
|
489
|
-
case _types.GatewayOpcodes.Heartbeat:
|
|
490
|
-
{
|
|
491
|
-
if (!this.isOpen()) return;
|
|
492
|
-
this.heart.lastBeat = Date.now();
|
|
493
|
-
// Discord randomly sends this requiring an immediate heartbeat back.
|
|
494
|
-
// Using a direct socket.send call here because heartbeat requests are reserved by us.
|
|
495
|
-
this.socket?.send(JSON.stringify({
|
|
496
|
-
op: _types.GatewayOpcodes.Heartbeat,
|
|
497
|
-
d: this.previousSequenceNumber
|
|
498
|
-
}));
|
|
499
|
-
this.events.heartbeat?.(this);
|
|
500
|
-
break;
|
|
501
|
-
}
|
|
502
|
-
case _types.GatewayOpcodes.Hello:
|
|
503
|
-
{
|
|
504
|
-
const interval = packet.d.heartbeat_interval;
|
|
505
|
-
this.logger.debug(`[Shard] Shard #${this.id} received Hello`);
|
|
506
|
-
this.startHeartbeating(interval);
|
|
507
|
-
if (this.state !== _types1.ShardState.Resuming) {
|
|
508
|
-
const currentQueue = [
|
|
509
|
-
...this.bucket.queue
|
|
510
|
-
];
|
|
511
|
-
// HELLO has been send on a non resume action.
|
|
512
|
-
// This means that the shard starts a new session,
|
|
513
|
-
// therefore the rate limit interval has been reset too.
|
|
514
|
-
this.bucket = new _utils.LeakyBucket({
|
|
515
|
-
max: this.calculateSafeRequests(),
|
|
516
|
-
refillInterval: 60000,
|
|
517
|
-
refillAmount: this.calculateSafeRequests(),
|
|
518
|
-
logger: this.logger
|
|
519
|
-
});
|
|
520
|
-
// Queue should not be lost on a re-identify.
|
|
521
|
-
this.bucket.queue.unshift(...currentQueue);
|
|
522
|
-
}
|
|
523
|
-
this.events.hello?.(this);
|
|
524
|
-
break;
|
|
525
|
-
}
|
|
526
|
-
case _types.GatewayOpcodes.HeartbeatACK:
|
|
527
|
-
{
|
|
528
|
-
// Manually calculating the round trip time for users who need it.
|
|
529
|
-
if (this.heart.lastBeat) {
|
|
530
|
-
this.heart.rtt = this.heart.lastAck - this.heart.lastBeat;
|
|
531
|
-
}
|
|
532
|
-
this.events.heartbeatAck?.(this);
|
|
533
|
-
break;
|
|
534
|
-
}
|
|
535
|
-
case _types.GatewayOpcodes.Reconnect:
|
|
536
|
-
{
|
|
537
|
-
this.logger.debug(`[Shard] Received a Reconnect for Shard #${this.id}`);
|
|
538
|
-
this.events.requestedReconnect?.(this);
|
|
539
|
-
await this.resume();
|
|
540
|
-
break;
|
|
541
|
-
}
|
|
542
|
-
case _types.GatewayOpcodes.InvalidSession:
|
|
543
|
-
{
|
|
544
|
-
const resumable = packet.d;
|
|
545
|
-
this.logger.debug(`[Shard] Received Invalid Session for Shard #${this.id} with resumable as ${resumable}`);
|
|
546
|
-
this.events.invalidSession?.(this, resumable);
|
|
547
|
-
// We need to wait for a random amount of time between 1 and 5
|
|
548
|
-
// Reference: https://discord.com/developers/docs/topics/gateway#resuming
|
|
549
|
-
await (0, _utils.delay)(Math.floor((Math.random() * 4 + 1) * 1000));
|
|
550
|
-
this.resolves.get('INVALID_SESSION')?.(packet);
|
|
551
|
-
this.resolves.delete('INVALID_SESSION');
|
|
552
|
-
// When resumable is false we need to re-identify
|
|
553
|
-
if (!resumable) {
|
|
554
|
-
await this.identify();
|
|
555
|
-
break;
|
|
556
|
-
}
|
|
557
|
-
// The session is invalid but apparently it is resumable
|
|
558
|
-
await this.resume();
|
|
559
|
-
break;
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
switch(packet.t){
|
|
563
|
-
case 'RESUMED':
|
|
564
|
-
this.state = _types1.ShardState.Connected;
|
|
565
|
-
this.events.resumed?.(this);
|
|
566
|
-
this.logger.debug(`[Shard] Shard #${this.id} received RESUMED`);
|
|
567
|
-
// Continue the requests which have been queued since the shard went offline.
|
|
568
|
-
this.offlineSendQueue.forEach((resolve)=>resolve());
|
|
569
|
-
// Setting the length to 0 will delete the elements in it
|
|
570
|
-
this.offlineSendQueue.length = 0;
|
|
571
|
-
this.resolves.get('RESUMED')?.(packet);
|
|
572
|
-
this.resolves.delete('RESUMED');
|
|
573
|
-
break;
|
|
574
|
-
case 'READY':
|
|
575
|
-
{
|
|
576
|
-
const payload = packet.d;
|
|
577
|
-
this.events.ready?.(this);
|
|
578
|
-
// Important for future resumes.
|
|
579
|
-
this.resumeGatewayUrl = payload.resume_gateway_url;
|
|
580
|
-
this.sessionId = payload.session_id;
|
|
581
|
-
this.state = _types1.ShardState.Connected;
|
|
582
|
-
this.logger.debug(`[Shard] Shard #${this.id} received READY`);
|
|
583
|
-
// Continue the requests which have been queued since the shard went offline.
|
|
584
|
-
// Important when this is a re-identify
|
|
585
|
-
this.offlineSendQueue.forEach((resolve)=>resolve());
|
|
586
|
-
// Setting the length to 0 will delete the elements in it
|
|
587
|
-
this.offlineSendQueue.length = 0;
|
|
588
|
-
this.resolves.get('READY')?.(packet);
|
|
589
|
-
this.resolves.delete('READY');
|
|
590
|
-
break;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
// Update the sequence number if it is present
|
|
594
|
-
// `s` can be either `null` or a `number`.
|
|
595
|
-
// In order to prevent update misses when `s` is `0` we check against null.
|
|
596
|
-
if (packet.s !== null) {
|
|
597
|
-
this.previousSequenceNumber = packet.s;
|
|
598
|
-
}
|
|
599
|
-
this.forwardToBot(packet);
|
|
600
|
-
}
|
|
601
|
-
forwardToBot(packet) {
|
|
602
|
-
// The necessary handling required for the Shards connection has been finished.
|
|
603
|
-
// Now the event can be safely forwarded.
|
|
604
|
-
this.events.message?.(this, (0, _utils.camelize)(packet));
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
|
-
* Override in order to make the shards presence.
|
|
608
|
-
* async in case devs create the presence based on eg. database values.
|
|
609
|
-
* Passing the shard's id there to make it easier for the dev to use this function.
|
|
610
|
-
*/ async makePresence() {
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
/**
|
|
614
|
-
* This function communicates with the management process, in order to know whether its free to identify.
|
|
615
|
-
* When this function resolves, this means that the shard is allowed to send an identify payload to discord.
|
|
616
|
-
*/ async requestIdentify() {}
|
|
617
|
-
/** Start sending heartbeat payloads to Discord in the provided interval. */ startHeartbeating(interval) {
|
|
618
|
-
this.logger.debug(`[Shard] Start heartbeating on Shard #${this.id}`);
|
|
619
|
-
// If old heartbeast exist like after resume, clear the old ones.
|
|
620
|
-
this.stopHeartbeating();
|
|
621
|
-
this.heart.interval = interval;
|
|
622
|
-
// Only set the shard's state to `Unidentified`
|
|
623
|
-
// if heartbeating has not been started due to an identify or resume action.
|
|
624
|
-
if ([
|
|
625
|
-
_types1.ShardState.Disconnected,
|
|
626
|
-
_types1.ShardState.Offline
|
|
627
|
-
].includes(this.state)) {
|
|
628
|
-
this.logger.debug(`[Shard] Shard is disconnected or offline but the heartbeat was started #${this.id}`);
|
|
629
|
-
this.state = _types1.ShardState.Unidentified;
|
|
630
|
-
}
|
|
631
|
-
// The first heartbeat needs to be send with a random delay between `0` and `interval`
|
|
632
|
-
// Using a `setTimeout(_, jitter)` here to accomplish that.
|
|
633
|
-
// `Math.random()` can be `0` so we use `0.5` if this happens
|
|
634
|
-
// Reference: https://discord.com/developers/docs/topics/gateway#heartbeating
|
|
635
|
-
const jitter = Math.ceil(this.heart.interval * (Math.random() || 0.5));
|
|
636
|
-
this.heart.timeoutId = setTimeout(()=>{
|
|
637
|
-
this.logger.debug(`[Shard] Beginning heartbeating process for Shard #${this.id}`);
|
|
638
|
-
if (!this.isOpen()) return;
|
|
639
|
-
this.logger.debug(`[Shard] Heartbeating on Shard #${this.id}. Previous sequence number: ${this.previousSequenceNumber}`);
|
|
640
|
-
// Using a direct socket.send call here because heartbeat requests are reserved by us.
|
|
641
|
-
this.socket?.send(JSON.stringify({
|
|
642
|
-
op: _types.GatewayOpcodes.Heartbeat,
|
|
643
|
-
d: this.previousSequenceNumber
|
|
644
|
-
}));
|
|
645
|
-
this.heart.lastBeat = Date.now();
|
|
646
|
-
this.heart.acknowledged = false;
|
|
647
|
-
// After the random heartbeat jitter we can start a normal interval.
|
|
648
|
-
this.heart.intervalId = setInterval(async ()=>{
|
|
649
|
-
if (!this.isOpen()) {
|
|
650
|
-
this.logger.debug(`[Shard] Shard #${this.id} is not open, but attempted heartbeat, ignoring.`);
|
|
651
|
-
return;
|
|
652
|
-
}
|
|
653
|
-
// The Shard did not receive a heartbeat ACK from Discord in time,
|
|
654
|
-
// therefore we have to assume that the connection has failed or got "zombied".
|
|
655
|
-
// The Shard needs to start a re-identify action accordingly.
|
|
656
|
-
// Reference: https://discord.com/developers/docs/topics/gateway#heartbeating-example-gateway-heartbeat-ack
|
|
657
|
-
if (!this.heart.acknowledged) {
|
|
658
|
-
this.logger.debug(`[Shard] Heartbeat not acknowledged for Shard #${this.id}. Assuming zombied connection.`);
|
|
659
|
-
await this.close(_types1.ShardSocketCloseCodes.ZombiedConnection, 'Zombied connection, did not receive an heartbeat ACK in time.');
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
this.logger.debug(`[Shard] Heartbeating on Shard #${this.id}. Previous sequence number: ${this.previousSequenceNumber}`);
|
|
663
|
-
// Using a direct socket.send call here because heartbeat requests are reserved by us.
|
|
664
|
-
this.socket?.send(JSON.stringify({
|
|
665
|
-
op: _types.GatewayOpcodes.Heartbeat,
|
|
666
|
-
d: this.previousSequenceNumber
|
|
667
|
-
}));
|
|
668
|
-
this.heart.lastBeat = Date.now();
|
|
669
|
-
this.heart.acknowledged = false;
|
|
670
|
-
this.events.heartbeat?.(this);
|
|
671
|
-
}, this.heart.interval);
|
|
672
|
-
}, jitter);
|
|
673
|
-
}
|
|
674
|
-
/** Stop the heartbeating process with discord. */ stopHeartbeating() {
|
|
675
|
-
// Clear the regular heartbeat interval.
|
|
676
|
-
clearInterval(this.heart.intervalId);
|
|
677
|
-
// It's possible that the Shard got closed before the first jittered heartbeat.
|
|
678
|
-
// To go safe we should clear the related timeout too.
|
|
679
|
-
clearTimeout(this.heart.timeoutId);
|
|
680
|
-
}
|
|
681
|
-
};
|
|
682
|
-
/** Check if the buffer ends with the marker */ function endsWithMarker(buffer, marker) {
|
|
683
|
-
if (buffer.length < marker.length) return false;
|
|
684
|
-
for(let i = 0; i < marker.length; i++){
|
|
685
|
-
if (buffer[buffer.length - marker.length + i] !== marker[i]) return false;
|
|
686
|
-
}
|
|
687
|
-
return true;
|
|
688
|
-
}
|
|
689
|
-
const _default = DiscordenoShard;
|
|
690
|
-
|
|
691
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9TaGFyZC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBCdWZmZXIgfSBmcm9tICdub2RlOmJ1ZmZlcidcbmltcG9ydCB7IGNyZWF0ZUluZmxhdGUsIHR5cGUgSW5mbGF0ZSwgaW5mbGF0ZVN5bmMsIGNvbnN0YW50cyBhcyB6bGliQ29uc3RhbnRzIH0gZnJvbSAnbm9kZTp6bGliJ1xuaW1wb3J0IHR5cGUgeyBEaXNjb3JkR2F0ZXdheVBheWxvYWQsIERpc2NvcmRIZWxsbywgRGlzY29yZFJlYWR5LCBEaXNjb3JkVXBkYXRlUHJlc2VuY2UgfSBmcm9tICdAZGlzY29yZGVuby90eXBlcydcbmltcG9ydCB7IEdhdGV3YXlDbG9zZUV2ZW50Q29kZXMsIEdhdGV3YXlPcGNvZGVzIH0gZnJvbSAnQGRpc2NvcmRlbm8vdHlwZXMnXG5pbXBvcnQgeyBjYW1lbGl6ZSwgZGVsYXksIExlYWt5QnVja2V0LCBsb2dnZXIgfSBmcm9tICdAZGlzY29yZGVuby91dGlscydcbmltcG9ydCB0eXBlIHsgRGVjb21wcmVzcyBhcyBac3RkRGVjb21wcmVzcyB9IGZyb20gJ2Z6c3RkJ1xuaW1wb3J0IE5vZGVXZWJTb2NrZXQgZnJvbSAnd3MnXG5pbXBvcnQge1xuICB0eXBlIFNoYXJkRXZlbnRzLFxuICB0eXBlIFNoYXJkR2F0ZXdheUNvbmZpZyxcbiAgdHlwZSBTaGFyZEhlYXJ0LFxuICBTaGFyZFNvY2tldENsb3NlQ29kZXMsXG4gIHR5cGUgU2hhcmRTb2NrZXRSZXF1ZXN0LFxuICBTaGFyZFN0YXRlLFxuICBUcmFuc3BvcnRDb21wcmVzc2lvbixcbn0gZnJvbSAnLi90eXBlcy5qcydcblxuY29uc3QgWkxJQl9TWU5DX0ZMVVNIID0gbmV3IFVpbnQ4QXJyYXkoWzB4MCwgMHgwLCAweGZmLCAweGZmXSlcblxubGV0IGZ6c3RkOiB0eXBlb2YgaW1wb3J0KCdmenN0ZCcpXG5cbi8qKiBTaW5jZSBmenN0ZCBpcyBhbiBvcHRpb25hbCBkZXBlbmRlbmN5LCB3ZSBuZWVkIHRvIGltcG9ydCBpdCBsYXppbHkuICovXG5hc3luYyBmdW5jdGlvbiBnZXRGWlN0ZCgpIHtcbiAgcmV0dXJuIChmenN0ZCA/Pz0gYXdhaXQgaW1wb3J0KCdmenN0ZCcpKVxufVxuXG5leHBvcnQgY2xhc3MgRGlzY29yZGVub1NoYXJkIHtcbiAgLyoqIFRoZSBpZCBvZiB0aGUgc2hhcmQuICovXG4gIGlkOiBudW1iZXJcbiAgLyoqIFRoZSBjb25uZWN0aW9uIGNvbmZpZyBkZXRhaWxzIHRoYXQgdGhpcyBzaGFyZCB3aWxsIHVzZWQgdG8gY29ubmVjdCB0byBkaXNjb3JkLiAqL1xuICBjb25uZWN0aW9uOiBTaGFyZEdhdGV3YXlDb25maWdcbiAgLyoqIFRoaXMgY29udGFpbnMgYWxsIHRoZSBoZWFydGJlYXQgaW5mb3JtYXRpb24gKi9cbiAgaGVhcnQ6IFNoYXJkSGVhcnRcbiAgLyoqIFRoZSBtYXhpbXVtIG9mIHJlcXVlc3RzIHdoaWNoIGNhbiBiZSBzZW5kIHRvIGRpc2NvcmQgcGVyIHJhdGUgbGltaXQgdGljay4gVHlwaWNhbGx5IHRoaXMgdmFsdWUgc2hvdWxkIG5vdCBiZSBjaGFuZ2VkLiAqL1xuICBtYXhSZXF1ZXN0c1BlclJhdGVMaW1pdFRpY2s6IG51bWJlciA9IDEyMFxuICAvKiogVGhlIHByZXZpb3VzIHBheWxvYWQgc2VxdWVuY2UgbnVtYmVyLiAqL1xuICBwcmV2aW91c1NlcXVlbmNlTnVtYmVyOiBudW1iZXIgfCBudWxsID0gbnVsbFxuICAvKiogSW4gd2hpY2ggaW50ZXJ2YWwgKGluIG1pbGxpc2Vjb25kcykgdGhlIGdhdGV3YXkgcmVzZXRzIGl0J3MgcmF0ZSBsaW1pdC4gKi9cbiAgcmF0ZUxpbWl0UmVzZXRJbnRlcnZhbDogbnVtYmVyID0gNjAwMDBcbiAgLyoqIEN1cnJlbnQgc2Vzc2lvbiBpZCBvZiB0aGUgc2hhcmQgaWYgcHJlc2VudC4gKi9cbiAgc2Vzc2lvbklkPzogc3RyaW5nXG4gIC8qKiBUaGlzIGNvbnRhaW5zIHRoZSBXZWJTb2NrZXQgY29ubmVjdGlvbiB0byBEaXNjb3JkLCBpZiBjdXJyZW50bHkgY29ubmVjdGVkLiAqL1xuICBzb2NrZXQ/OiBXZWJTb2NrZXRcbiAgLyoqIEN1cnJlbnQgaW50ZXJuYWwgc3RhdGUgb2YgdGhlIHRoaXMuICovXG4gIHN0YXRlID0gU2hhcmRTdGF0ZS5PZmZsaW5lXG4gIC8qKiBUaGUgdXJsIHByb3ZpZGVkIGJ5IGRpc2NvcmQgdG8gdXNlIHdoZW4gcmVzdW1pbmcgYSBjb25uZWN0aW9uIGZvciB0aGlzIHRoaXMuICovXG4gIHJlc3VtZUdhdGV3YXlVcmw6IHN0cmluZyA9ICcnXG4gIC8qKiBUaGUgc2hhcmQgcmVsYXRlZCBldmVudCBoYW5kbGVycy4gKi9cbiAgZXZlbnRzOiBTaGFyZEV2ZW50cyA9IHt9XG4gIC8qKiBDYWNoZSBmb3IgcGVuZGluZyBnYXRld2F5IHJlcXVlc3RzIHdoaWNoIHNob3VsZCBoYXZlIGJlZW4gc2VuZCB3aGlsZSB0aGUgZ2F0ZXdheSB3ZW50IG9mZmxpbmUuICovXG4gIG9mZmxpbmVTZW5kUXVldWU6ICgoKSA9PiB2b2lkKVtdID0gW11cbiAgLyoqIFJlc29sdmUgaW50ZXJuYWwgd2FpdGluZyBzdGF0ZXMuIE1hcHBlZCBieSBTZWxlY3RlZEV2ZW50cyA9PiBSZXNvbHZlRnVuY3Rpb24gKi9cbiAgcmVzb2x2ZXMgPSBuZXcgTWFwPCdSRUFEWScgfCAnUkVTVU1FRCcgfCAnSU5WQUxJRF9TRVNTSU9OJywgKHBheWxvYWQ6IERpc2NvcmRHYXRld2F5UGF5bG9hZCkgPT4gdm9pZD4oKVxuICAvKiogU2hhcmQgYnVja2V0LiBPbmx5IGFjY2VzcyB0aGlzIGlmIHlvdSBrbm93IHdoYXQgeW91IGFyZSBkb2luZy4gQnVja2V0IGZvciBoYW5kbGluZyBzaGFyZCByZXF1ZXN0IHJhdGUgbGltaXRzLiAqL1xuICBidWNrZXQ6IExlYWt5QnVja2V0XG4gIC8qKiBMb2dnZXIgZm9yIHRoZSBidWNrZXQuICovXG4gIGxvZ2dlcjogUGljazx0eXBlb2YgbG9nZ2VyLCAnZGVidWcnIHwgJ2luZm8nIHwgJ3dhcm4nIHwgJ2Vycm9yJyB8ICdmYXRhbCc+XG4gIC8qKlxuICAgKiBJcyB0aGUgc2hhcmQgZ29pbmcgb2ZmbGluZT9cbiAgICpcbiAgICogQHJlbWFya3NcbiAgICogVGhpcyB3aWxsIGJlIHRydWUgaWYgdGhlIGNsb3NlIG1ldGhvZCBoYXMgYmVlbiBjYWxsZWQgd2l0aCBlaXRoZXIgMTAwMCBvciAxMDAxXG4gICAqXG4gICAqIEBpbnRlcm5hbFxuICAgKiBUaGlzIGlzIGZvciBpbnRlcm5hbCBwdXJwb3NlcyBvbmx5LCBhbmQgc3ViamVjdCB0byBicmVha2luZyBjaGFuZ2VzLlxuICAgKi9cbiAgZ29pbmdPZmZsaW5lID0gZmFsc2VcbiAgLyoqIFRleHQgZGVjb2RlciB1c2VkIGZvciBjb21wcmVzc2VkIHBheWxvYWRzLiAqL1xuICB0ZXh0RGVjb2RlciA9IG5ldyBUZXh0RGVjb2RlcigpXG4gIC8qKiBaTGliIEluZmxhdGUgaW5zdGFuY2UgZm9yIFpMaWItc3RyZWFtIHRyYW5zcG9ydCBwYXlsb2Fkcy4gKi9cbiAgaW5mbGF0ZT86IEluZmxhdGVcbiAgLyoqIFpMaWIgaW5mbGF0ZSBidWZmZXIuICovXG4gIGluZmxhdGVCdWZmZXI6IFVpbnQ4QXJyYXkgfCBudWxsID0gbnVsbFxuICAvKiogWlN0ZCBEZWNvbXByZXNzIGluc3RhbmNlIGZvciBaU3RkLXN0cmVhbSB0cmFuc3BvcnQgcGF5bG9hZHMuICovXG4gIHpzdGREZWNvbXByZXNzPzogWnN0ZERlY29tcHJlc3NcbiAgLyoqIFF1ZXVlIGZvciBjb21wcmVzc2VkIHBheWxvYWRzIGZvciBac3RkIERlY29tcHJlc3MgKi9cbiAgZGVjb21wcmVzc2lvblByb21pc2VzUXVldWU6ICgoZGF0YTogRGlzY29yZEdhdGV3YXlQYXlsb2FkKSA9PiB2b2lkKVtdID0gW11cbiAgLyoqXG4gICAqIEEgZnVuY3Rpb24gdGhhdCB3aWxsIGJlIGNhbGxlZCBvbmNlIHRoZSBzb2NrZXQgaXMgY2xvc2VkIGFuZCBoYW5kbGVDbG9zZSgpIGhhcyBmaW5pc2hlZCB1cGRhdGluZyBpbnRlcm5hbCBzdGF0ZXMuXG4gICAqXG4gICAqIEBpbnRlcm5hbFxuICAgKiBUaGlzIGlzIGZvciBpbnRlcm5hbCBwdXJwb3NlcyBvbmx5LCBhbmQgc3ViamVjdCB0byBicmVha2luZyBjaGFuZ2VzLlxuICAgKi9cbiAgcmVzb2x2ZUFmdGVyQ2xvc2U/OiAoY2xvc2U6IENsb3NlRXZlbnQpID0+IHZvaWRcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBTaGFyZENyZWF0ZU9wdGlvbnMpIHtcbiAgICB0aGlzLmlkID0gb3B0aW9ucy5pZFxuICAgIHRoaXMuY29ubmVjdGlvbiA9IG9wdGlvbnMuY29ubmVjdGlvblxuICAgIHRoaXMuZXZlbnRzID0gb3B0aW9ucy5ldmVudHNcbiAgICB0aGlzLmxvZ2dlciA9IG9wdGlvbnMubG9nZ2VyID8/IGxvZ2dlclxuXG4gICAgdGhpcy5oZWFydCA9IHtcbiAgICAgIGFja25vd2xlZGdlZDogZmFsc2UsXG4gICAgICBpbnRlcnZhbDogNDUwMDAsXG4gICAgfVxuXG4gICAgaWYgKG9wdGlvbnMucmVxdWVzdElkZW50aWZ5KSB0aGlzLnJlcXVlc3RJZGVudGlmeSA9IG9wdGlvbnMucmVxdWVzdElkZW50aWZ5XG4gICAgaWYgKG9wdGlvbnMubWFrZVByZXNlbmNlKSB0aGlzLm1ha2VQcmVzZW5jZSA9IG9wdGlvbnMubWFrZVByZXNlbmNlXG5cbiAgICB0aGlzLmJ1Y2tldCA9IG5ldyBMZWFreUJ1Y2tldCh7XG4gICAgICBtYXg6IHRoaXMuY2FsY3VsYXRlU2FmZVJlcXVlc3RzKCksXG4gICAgICByZWZpbGxBbW91bnQ6IHRoaXMuY2FsY3VsYXRlU2FmZVJlcXVlc3RzKCksXG4gICAgICByZWZpbGxJbnRlcnZhbDogNjAwMDAsXG4gICAgICBsb2dnZXI6IHRoaXMubG9nZ2VyLFxuICAgIH0pXG4gIH1cblxuICAvKiogVGhlIGdhdGV3YXkgY29uZmlndXJhdGlvbiB3aGljaCBpcyB1c2VkIHRvIGNvbm5lY3QgdG8gRGlzY29yZC4gKi9cbiAgZ2V0IGdhdGV3YXlDb25maWcoKTogU2hhcmRHYXRld2F5Q29uZmlnIHtcbiAgICByZXR1cm4gdGhpcy5jb25uZWN0aW9uXG4gIH1cblxuICAvKiogVGhlIHVybCB0byBjb25uZWN0IHRvLiBJbml0aWFsbHkgdGhpcyBpcyB0aGUgZGlzY29yZCBnYXRld2F5IHVybCwgYW5kIHRoZW4gaXMgc3dpdGNoZWQgdG8gcmVzdW1lIGdhdGV3YXkgdXJsIG9uY2UgYSBSRUFEWSBpcyByZWNlaXZlZC4gKi9cbiAgZ2V0IGNvbm5lY3Rpb25VcmwoKTogc3RyaW5nIHtcbiAgICAvLyBVc2UgfHwgYW5kIG5vdCA/PyBoZXJlLiA/PyB3aWxsIGNhdXNlIGEgYnVnLlxuICAgIHJldHVybiB0aGlzLnJlc3VtZUdhdGV3YXlVcmwgfHwgdGhpcy5nYXRld2F5Q29uZmlnLnVybFxuICB9XG5cbiAgLyoqIENhbGN1bGF0ZSB0aGUgYW1vdW50IG9mIHJlcXVlc3RzIHdoaWNoIGNhbiBzYWZlbHkgYmUgbWFkZSBwZXIgcmF0ZSBsaW1pdCBpbnRlcnZhbCwgYmVmb3JlIHRoZSBnYXRld2F5IGdldHMgZGlzY29ubmVjdGVkIGR1ZSB0byBhbiBleGNlZWRlZCByYXRlIGxpbWl0LiAqL1xuICBjYWxjdWxhdGVTYWZlUmVxdWVzdHMoKTogbnVtYmVyIHtcbiAgICAvLyAqIDIgYWRkcyBleHRyYSBzYWZldHkgbGF5ZXIgZm9yIGRpc2NvcmRzIE9QIDEgcmVxdWVzdHMgdGhhdCB3ZSBuZWVkIHRvIHJlc3BvbmQgdG9cbiAgICBjb25zdCBzYWZlUmVxdWVzdHMgPSB0aGlzLm1heFJlcXVlc3RzUGVyUmF0ZUxpbWl0VGljayAtIE1hdGguY2VpbCh0aGlzLnJhdGVMaW1pdFJlc2V0SW50ZXJ2YWwgLyB0aGlzLmhlYXJ0LmludGVydmFsKSAqIDJcblxuICAgIHJldHVybiBzYWZlUmVxdWVzdHMgPCAwID8gMCA6IHNhZmVSZXF1ZXN0c1xuICB9XG5cbiAgYXN5bmMgY2hlY2tPZmZsaW5lKGhpZ2hQcmlvcml0eTogYm9vbGVhbik6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLmlzT3BlbigpKSByZXR1cm5cblxuICAgIHJldHVybiBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSkgPT4ge1xuICAgICAgLy8gSGlnaGVyIHByaW9yaXR5IHJlcXVlc3RzIGdldCBhZGRlZCBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBhcnJheS5cbiAgICAgIGlmIChoaWdoUHJpb3JpdHkpIHRoaXMub2ZmbGluZVNlbmRRdWV1ZS51bnNoaWZ0KHJlc29sdmUpXG4gICAgICBlbHNlIHRoaXMub2ZmbGluZVNlbmRRdWV1ZS5wdXNoKHJlc29sdmUpXG4gICAgfSlcbiAgfVxuXG4gIC8qKiBDbG9zZSB0aGUgc29ja2V0IGNvbm5lY3Rpb24gdG8gZGlzY29yZCBpZiBwcmVzZW50LiAqL1xuICBhc3luYyBjbG9zZShjb2RlOiBudW1iZXIsIHJlYXNvbjogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5sb2dnZXIuZGVidWcoYFtTaGFyZF0gUmVxdWVzdCBmb3IgU2hhcmQgIyR7dGhpcy5pZH0gdG8gY2xvc2UgdGhlIHNvY2tldCB3aXRoIGNvZGUgJHtjb2RlfS5gKVxuXG4gICAgaWYgKHRoaXMuc29ja2V0Py5yZWFkeVN0YXRlICE9PSBOb2RlV2ViU29ja2V0Lk9QRU4pIHtcbiAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBbU2hhcmRdIFNoYXJkICMke3RoaXMuaWR9J3MgcmVhZHkgc3RhdGUgaXMgJHt0aGlzLnNvY2tldD8ucmVhZHlTdGF0ZX0sIFVuYWJsZSB0byBjbG9zZS5gKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgdGhpcy5nb2luZ09mZmxpbmUgPSBjb2RlID09PSBHYXRld2F5Q2xvc2VFdmVudENvZGVzLk5vcm1hbENsb3N1cmUgfHwgY29kZSA9PT0gR2F0ZXdheUNsb3NlRXZlbnRDb2Rlcy5Hb2luZ0F3YXlcblxuICAgIC8vIFRoaXMgaGFzIHRvIGJlIGNyZWF0ZWQgYmVmb3JlIHRoZSBhY3R1YWwgY2FsbCB0byBzb2NrZXQuY2xvc2UgYXMgZm9yIGV4YW1wbGUgQnVuIGNhbGxzIHNvY2tldC5vbmNsb3NlIGltbWVkaWF0ZWx5IG9uIHRoZSAuY2xvc2UoKSBjYWxsIGluc3RlYWQgb2Ygd2FpdGluZyBmb3IgdGhlIGNvbm5lY3Rpb24gdG8gZW5kXG4gICAgY29uc3QgcHJvbWlzZSA9IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICB0aGlzLnJlc29sdmVBZnRlckNsb3NlID0gcmVzb2x2ZVxuICAgIH0pXG5cbiAgICB0aGlzLnNvY2tldC5jbG9zZShjb2RlLCByZWFzb24pXG5cbiAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBXYWl0aW5nIGZvciBTaGFyZCAjJHt0aGlzLmlkfSB0byBjbG9zZSB0aGUgc29ja2V0IHdpdGggY29kZSAke2NvZGV9LmApXG5cbiAgICAvLyBXZSBuZWVkIHRvIHdhaXQgZm9yIHRoZSBzb2NrZXQgdG8gYmUgZnVsbHkgY2xvc2VkLCBvdGhlcndpc2UgdGhlcmUnbGwgYmUgcmFjZSBjb25kaXRpb24gaXNzdWVzIGlmIHdlIHRyeSB0byBjb25uZWN0IGFnYWluLCByZXN1bHRpbmcgaW4gdW5leHBlY3RlZCBiZWhhdmlvci5cbiAgICBhd2FpdCBwcm9taXNlXG5cbiAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBTaGFyZCAjJHt0aGlzLmlkfSBjbG9zZWQgdGhlIHNvY2tldCB3aXRoIGNvZGUgJHtjb2RlfS5gKVxuXG4gICAgLy8gUmVzZXQgdGhlIHJlc29sdmVBZnRlckNsb3NlIGZ1bmN0aW9uIGFmdGVyIGl0IGhhcyBiZWVuIHJlc29sdmVkLlxuICAgIHRoaXMucmVzb2x2ZUFmdGVyQ2xvc2UgPSB1bmRlZmluZWRcbiAgfVxuXG4gIC8qKiBDb25uZWN0IHRoZSBzaGFyZCB3aXRoIHRoZSBnYXRld2F5IGFuZCBzdGFydCBoZWFydGJlYXRpbmcuIFRoaXMgd2lsbCBub3QgaWRlbnRpZnkgdGhlIHNoYXJkIHRvIHRoZSBnYXRld2F5LiAqL1xuICBhc3luYyBjb25uZWN0KCk6IFByb21pc2U8RGlzY29yZGVub1NoYXJkPiB7XG4gICAgLy8gT25seSBzZXQgdGhlIHNoYXJkIHRvIGBDb25uZWN0aW5nYCBzdGF0ZSxcbiAgICAvLyBpZiB0aGUgY29ubmVjdGlvbiByZXF1ZXN0IGRvZXMgbm90IGNvbWUgZnJvbSBhbiBpZGVudGlmeSBvciByZXN1bWUgYWN0aW9uLlxuICAgIGlmICghW1NoYXJkU3RhdGUuSWRlbnRpZnlpbmcsIFNoYXJkU3RhdGUuUmVzdW1pbmddLmluY2x1ZGVzKHRoaXMuc3RhdGUpKSB7XG4gICAgICB0aGlzLnN0YXRlID0gU2hhcmRTdGF0ZS5Db25uZWN0aW5nXG4gICAgfVxuXG4gICAgdGhpcy5sb2dnZXIuZGVidWcoYFtTaGFyZF0gQ29ubmVjdGluZyBTaGFyZCAjJHt0aGlzLmlkfSBzb2NrZXQuYClcblxuICAgIHRoaXMuZXZlbnRzLmNvbm5lY3Rpbmc/Lih0aGlzKVxuXG4gICAgY29uc3QgdXJsID0gbmV3IFVSTCh0aGlzLmNvbm5lY3Rpb25VcmwpXG4gICAgdXJsLnNlYXJjaFBhcmFtcy5zZXQoJ3YnLCB0aGlzLmdhdGV3YXlDb25maWcudmVyc2lvbi50b1N0cmluZygpKVxuICAgIHVybC5zZWFyY2hQYXJhbXMuc2V0KCdlbmNvZGluZycsICdqc29uJylcblxuICAgIC8vIFNldCB0aGUgY29tcHJlc3MgdXJsIHBhcmFtIGFuZCBpbml0aWFsaXplIHRoZSBkZWNvbXByZXNzaW9uIGNvbnRleHRzXG4gICAgaWYgKHRoaXMuZ2F0ZXdheUNvbmZpZy50cmFuc3BvcnRDb21wcmVzc2lvbikge1xuICAgICAgdXJsLnNlYXJjaFBhcmFtcy5zZXQoJ2NvbXByZXNzJywgdGhpcy5nYXRld2F5Q29uZmlnLnRyYW5zcG9ydENvbXByZXNzaW9uKVxuXG4gICAgICBpZiAodGhpcy5nYXRld2F5Q29uZmlnLnRyYW5zcG9ydENvbXByZXNzaW9uID09PSBUcmFuc3BvcnRDb21wcmVzc2lvbi56bGliKSB7XG4gICAgICAgIHRoaXMuaW5mbGF0ZUJ1ZmZlciA9IG51bGxcbiAgICAgICAgdGhpcy5pbmZsYXRlID0gY3JlYXRlSW5mbGF0ZSh7XG4gICAgICAgICAgZmluaXNoRmx1c2g6IHpsaWJDb25zdGFudHMuWl9TWU5DX0ZMVVNILFxuICAgICAgICAgIGNodW5rU2l6ZTogNjQgKiAxMDI0LFxuICAgICAgICB9KVxuXG4gICAgICAgIHRoaXMuaW5mbGF0ZS5vbignZXJyb3InLCAoZSkgPT4ge1xuICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKCdUaGUgd2FzIGFuIGVycm9yIGluIGRlY29tcHJlc3NpbmcgYSBaTGliIGNvbXByZXNzZWQgcGF5bG9hZCcsIGUpXG4gICAgICAgIH0pXG5cbiAgICAgICAgdGhpcy5pbmZsYXRlLm9uKCdkYXRhJywgKGRhdGEpID0+IHtcbiAgICAgICAgICBpZiAoIShkYXRhIGluc3RhbmNlb2YgVWludDhBcnJheSkpIHJldHVyblxuXG4gICAgICAgICAgaWYgKHRoaXMuaW5mbGF0ZUJ1ZmZlcikge1xuICAgICAgICAgICAgY29uc3QgbmV3QnVmZmVyID0gbmV3IFVpbnQ4QXJyYXkodGhpcy5pbmZsYXRlQnVmZmVyLmJ5dGVMZW5ndGggKyBkYXRhLmJ5dGVMZW5ndGgpXG4gICAgICAgICAgICBuZXdCdWZmZXIuc2V0KHRoaXMuaW5mbGF0ZUJ1ZmZlcilcbiAgICAgICAgICAgIG5ld0J1ZmZlci5zZXQoZGF0YSwgdGhpcy5pbmZsYXRlQnVmZmVyLmJ5dGVMZW5ndGgpXG4gICAgICAgICAgICB0aGlzLmluZmxhdGVCdWZmZXIgPSBuZXdCdWZmZXJcblxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdGhpcy5pbmZsYXRlQnVmZmVyID0gZGF0YVxuICAgICAgICB9KVxuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5nYXRld2F5Q29uZmlnLnRyYW5zcG9ydENvbXByZXNzaW9uID09PSBUcmFuc3BvcnRDb21wcmVzc2lvbi56c3RkKSB7XG4gICAgICAgIGNvbnN0IGZ6c3RkID0gYXdhaXQgZ2V0RlpTdGQoKS5jYXRjaCgoKSA9PiB7XG4gICAgICAgICAgdGhpcy5sb2dnZXIud2FybignW1NoYXJkXSBcImZ6c3RkXCIgaXMgbm90IGluc3RhbGxlZC4gRGlzYWJsZWQgdHJhbnNwb3J0IGNvbXByZXNzaW9uLicpXG4gICAgICAgICAgdXJsLnNlYXJjaFBhcmFtcy5kZWxldGUoJ2NvbXByZXNzJylcblxuICAgICAgICAgIHJldHVybiBudWxsXG4gICAgICAgIH0pXG5cbiAgICAgICAgaWYgKGZ6c3RkKSB7XG4gICAgICAgICAgdGhpcy56c3RkRGVjb21wcmVzcyA9IG5ldyBmenN0ZC5EZWNvbXByZXNzKChkYXRhKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBkZWNvZGVkRGF0YSA9IHRoaXMudGV4dERlY29kZXIuZGVjb2RlKGRhdGEpXG4gICAgICAgICAgICBjb25zdCBwYXJzZWREYXRhID0gSlNPTi5wYXJzZShkZWNvZGVkRGF0YSlcbiAgICAgICAgICAgIHRoaXMuZGVjb21wcmVzc2lvblByb21pc2VzUXVldWUuc2hpZnQoKT8uKHBhcnNlZERhdGEpXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGlmICh0aGlzLmdhdGV3YXlDb25maWcuY29tcHJlc3MgJiYgdGhpcy5nYXRld2F5Q29uZmlnLnRyYW5zcG9ydENvbXByZXNzaW9uKSB7XG4gICAgICB0aGlzLmxvZ2dlci53YXJuKCdbU2hhcmRdIFBheWxvYWQgY29tcHJlc3Npb24gaGFzIGJlZW4gZGlzYWJsZWQgc2luY2UgdHJhbnNwb3J0IGNvbXByZXNzaW9uIGlzIGVuYWJsZWQgYXMgd2VsbC4nKVxuICAgICAgdGhpcy5nYXRld2F5Q29uZmlnLmNvbXByZXNzID0gZmFsc2VcbiAgICB9XG5cbiAgICAvLyBXZSBjaGVjayBmb3IgYnVpbHQtaW4gV2ViU29ja2V0IGltcGxlbWVudGF0aW9ucyBpbiBCdW4gb3IgRGVubywgTm9kZUpTIHYyMiBoYXMgYW4gaW1wbGVtZW50YXRpb24gdG9vIGJ1dCBpdCBzZWVtcyB0byBiZSBsZXNzIG9wdGltaXplZCBzbyBmb3Igbm93IGl0IGlzIGJldHRlciB0byB1c2UgdGhlIHdzIG5wbSBwYWNrYWdlXG4gICAgY29uc3Qgc2hvdWxkVXNlQnVpbHRpbiA9IFJlZmxlY3QuaGFzKGdsb2JhbFRoaXMsICdXZWJTb2NrZXQnKSAmJiAoUmVmbGVjdC5oYXMoZ2xvYmFsVGhpcywgJ0J1bicpIHx8IFJlZmxlY3QuaGFzKGdsb2JhbFRoaXMsICdEZW5vJykpXG5cbiAgICAvLyBAdHMtZXhwZWN0LWVycm9yIE5vZGVXZWJTb2NrZXQgZG9lc24ndCBzdXBwb3J0IFwiZGlzcGF0Y2hFdmVudFwiLCBhbmQgd2hpbGUgd2UgZG9uJ3QgdXNlIGl0LCBpdCBpcyByZXF1aXJlZCBvbiB0aGUgXCJXZWJTb2NrZXRcIiB0eXBlXG4gICAgY29uc3Qgc29ja2V0OiBXZWJTb2NrZXQgPSBzaG91bGRVc2VCdWlsdGluID8gbmV3IFdlYlNvY2tldCh1cmwpIDogbmV3IE5vZGVXZWJTb2NrZXQodXJsKVxuICAgIHRoaXMuc29ja2V0ID0gc29ja2V0XG5cbiAgICAvLyBCeSBkZWZhdWx0IFdlYlNvY2tldCB3aWxsIGdpdmUgdXMgYSBCbG9iLCB0aGlzIGNoYW5nZXMgaXQgc28gdGhhdCBpdCBnaXZlcyB1cyBhbiBBcnJheUJ1ZmZlclxuICAgIHNvY2tldC5iaW5hcnlUeXBlID0gJ2FycmF5YnVmZmVyJ1xuXG4gICAgc29ja2V0Lm9uZXJyb3IgPSAoZXZlbnQpID0+IHRoaXMuaGFuZGxlRXJyb3IoZXZlbnQpXG4gICAgc29ja2V0Lm9uY2xvc2UgPSAoY2xvc2VFdmVudCkgPT4gdGhpcy5oYW5kbGVDbG9zZShjbG9zZUV2ZW50KVxuICAgIHNvY2tldC5vbm1lc3NhZ2UgPSAobWVzc2FnZUV2ZW50KSA9PiB0aGlzLmhhbmRsZU1lc3NhZ2UobWVzc2FnZUV2ZW50KVxuXG4gICAgcmV0dXJuIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICBzb2NrZXQub25vcGVuID0gKCkgPT4ge1xuICAgICAgICAvLyBPbmx5IHNldCB0aGUgc2hhcmQgdG8gYFVuaWRlbnRpZmllZGAgc3RhdGUgaWYgdGhlIGNvbm5lY3Rpb24gcmVxdWVzdCBkb2VzIG5vdCBjb21lIGZyb20gYW4gaWRlbnRpZnkgb3IgcmVzdW1lIGFjdGlvbi5cbiAgICAgICAgaWYgKCFbU2hhcmRTdGF0ZS5JZGVudGlmeWluZywgU2hhcmRTdGF0ZS5SZXN1bWluZ10uaW5jbHVkZXModGhpcy5zdGF0ZSkpIHtcbiAgICAgICAgICB0aGlzLnN0YXRlID0gU2hhcmRTdGF0ZS5VbmlkZW50aWZpZWRcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBbU2hhcmRdIFNoYXJkICMke3RoaXMuaWR9IHNvY2tldCBjb25uZWN0ZWQuYClcblxuICAgICAgICB0aGlzLmV2ZW50cy5jb25uZWN0ZWQ/Lih0aGlzKVxuXG4gICAgICAgIHJlc29sdmUodGhpcylcbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIElkZW50aWZ5IHRoZSBzaGFyZCB0byB0aGUgZ2F0ZXdheS4gSWYgbm90IGNvbm5lY3RlZCwgdGhpcyB3aWxsIGFsc28gY29ubmVjdCB0aGUgc2hhcmQgdG8gdGhlIGdhdGV3YXkuXG4gICAqIEBwYXJhbSBieXBhc3NSZXF1ZXN0IC0gV2hldGhlciB0byBieXBhc3MgdGhlIHJlcXVlc3RJZGVudGlmeSBoYW5kbGVyIGFuZCBpZGVudGlmeSBpbW1lZGlhdGVseS4gVGhpcyBzaG91bGQgYmUgdXNlZCBjYXJlZnVsbHkgYXMgaXQgY2FuIGNhdXNlIGludmFsaWQgc2Vzc2lvbnMuXG4gICAqL1xuICBhc3luYyBpZGVudGlmeShieXBhc3NSZXF1ZXN0ID0gZmFsc2UpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBBIG5ldyBpZGVudGlmeSBoYXMgYmVlbiByZXF1ZXN0ZWQgZXZlbiB0aG91Z2ggdGhlcmUgaXMgYWxyZWFkeSBhIGNvbm5lY3Rpb24gb3Blbi5cbiAgICAvLyBUaGVyZWZvcmUgd2UgbmVlZCB0byBjbG9zZSB0aGUgb2xkIGNvbm5lY3Rpb24gYW5kIGhlYXJ0YmVhdGluZyBiZWZvcmUgY3JlYXRpbmcgYSBuZXcgb25lLlxuICAgIGlmICh0aGlzLmlzT3BlbigpKSB7XG4gICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBJZGVudGlmeWluZyBvcGVuIFNoYXJkICMke3RoaXMuaWR9LCBjbG9zaW5nIHRoZSBjb25uZWN0aW9uYClcbiAgICAgIGF3YWl0IHRoaXMuY2xvc2UoU2hhcmRTb2NrZXRDbG9zZUNvZGVzLlJlSWRlbnRpZnlpbmcsICdSZS1pZGVudGlmeWluZyBjbG9zdXJlIG9mIG9sZCBjb25uZWN0aW9uLicpXG4gICAgfVxuXG4gICAgaWYgKCFieXBhc3NSZXF1ZXN0KSB7XG4gICAgICBhd2FpdCB0aGlzLnJlcXVlc3RJZGVudGlmeSgpXG4gICAgfVxuXG4gICAgdGhpcy5zdGF0ZSA9IFNoYXJkU3RhdGUuSWRlbnRpZnlpbmdcbiAgICB0aGlzLmV2ZW50cy5pZGVudGlmeWluZz8uKHRoaXMpXG5cbiAgICAvLyBJdCBpcyBwb3NzaWJsZSB0aGF0IHRoZSBzaGFyZCBpcyBpbiBIZWFydGJlYXRpbmcgc3RhdGUgYnV0IG5vdCBpZGVudGlmaWVkLFxuICAgIC8vIHNvIGNoZWNrIHdoZXRoZXIgdGhlcmUgaXMgYWxyZWFkeSBhIGdhdGV3YXkgY29ubmVjdGlvbiBleGlzdGluZy5cbiAgICAvLyBJZiBub3Qgd2UgbmVlZCB0byBjcmVhdGUgb25lIGJlZm9yZSB3ZSBpZGVudGlmeS5cbiAgICBpZiAoIXRoaXMuaXNPcGVuKCkpIHtcbiAgICAgIGF3YWl0IHRoaXMuY29ubmVjdCgpXG4gICAgfVxuXG4gICAgdGhpcy5sb2dnZXIuZGVidWcoYFtTaGFyZF0gU2VuZGluZyBJZGVudGlmeSBwYXlsb2FkIGZvciBTaGFyZCAjJHt0aGlzLmlkfS5gKVxuXG4gICAgdGhpcy5zZW5kKFxuICAgICAge1xuICAgICAgICBvcDogR2F0ZXdheU9wY29kZXMuSWRlbnRpZnksXG4gICAgICAgIGQ6IHtcbiAgICAgICAgICB0b2tlbjogYEJvdCAke3RoaXMuZ2F0ZXdheUNvbmZpZy50b2tlbn1gLFxuICAgICAgICAgIGNvbXByZXNzOiB0aGlzLmdhdGV3YXlDb25maWcuY29tcHJlc3MsXG4gICAgICAgICAgcHJvcGVydGllczogdGhpcy5nYXRld2F5Q29uZmlnLnByb3BlcnRpZXMsXG4gICAgICAgICAgaW50ZW50czogdGhpcy5nYXRld2F5Q29uZmlnLmludGVudHMsXG4gICAgICAgICAgc2hhcmQ6IFt0aGlzLmlkLCB0aGlzLmdhdGV3YXlDb25maWcudG90YWxTaGFyZHNdLFxuICAgICAgICAgIHByZXNlbmNlOiBhd2FpdCB0aGlzLm1ha2VQcmVzZW5jZSgpLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIHRydWUsXG4gICAgKVxuXG4gICAgcmV0dXJuIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICB0aGlzLnJlc29sdmVzLnNldCgnUkVBRFknLCAoKSA9PiB7XG4gICAgICAgIHJlc29sdmUoKVxuICAgICAgfSlcbiAgICAgIC8vIFdoZW4gaWRlbnRpZnlpbmcgdG9vIGZhc3QsIERpc2NvcmQgc2VuZHMgYW4gaW52YWxpZCBzZXNzaW9uIHBheWxvYWQuXG4gICAgICAvLyBUaGlzIGNhbiBzYWZlbHkgYmUgaWdub3JlZCB0aG91Z2ggYW5kIHRoZSBzaGFyZCBzdGFydHMgYSBuZXcgaWRlbnRpZnkgYWN0aW9uLlxuICAgICAgdGhpcy5yZXNvbHZlcy5zZXQoJ0lOVkFMSURfU0VTU0lPTicsICgpID0+IHtcbiAgICAgICAgdGhpcy5yZXNvbHZlcy5kZWxldGUoJ1JFQURZJylcbiAgICAgICAgcmVzb2x2ZSgpXG4gICAgICB9KVxuICAgIH0pXG4gIH1cblxuICAvKiogQ2hlY2sgd2hldGhlciB0aGUgY29ubmVjdGlvbiB0byBEaXNjb3JkIGlzIGN1cnJlbnRseSBvcGVuLiAqL1xuICBpc09wZW4oKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuc29ja2V0Py5yZWFkeVN0YXRlID09PSBOb2RlV2ViU29ja2V0Lk9QRU5cbiAgfVxuXG4gIC8qKiBBdHRlbXB0IHRvIHJlc3VtZSB0aGUgcHJldmlvdXMgc2hhcmRzIHNlc3Npb24gd2l0aCB0aGUgZ2F0ZXdheS4gKi9cbiAgYXN5bmMgcmVzdW1lKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBbU2hhcmRdIFJlc3VtaW5nIFNoYXJkICMke3RoaXMuaWR9YClcblxuICAgIC8vIEl0IGhhcyBiZWVuIHJlcXVlc3RlZCB0byByZXN1bWUgdGhlIFNoYXJkcyBzZXNzaW9uLlxuICAgIC8vIEl0J3MgcG9zc2libGUgdGhhdCB0aGUgc2hhcmQgaXMgc3RpbGwgY29ubmVjdGVkIHdpdGggRGlzY29yZCdzIGdhdGV3YXkgdGhlcmVmb3JlIHdlIG5lZWQgdG8gZm9yY2VmdWxseSBjbG9zZSBpdC5cbiAgICBpZiAodGhpcy5pc09wZW4oKSkge1xuICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYFtTaGFyZF0gUmVzdW1pbmcgb3BlbiBTaGFyZCAjJHt0aGlzLmlkfSwgY2xvc2luZyB0aGUgY29ubmVjdGlvbmApXG4gICAgICBhd2FpdCB0aGlzLmNsb3NlKFNoYXJkU29ja2V0Q2xvc2VDb2Rlcy5SZXN1bWVDbG9zaW5nT2xkQ29ubmVjdGlvbiwgJ1JlY29ubmVjdGluZyB0aGUgc2hhcmQsIGNsb3Npbmcgb2xkIGNvbm5lY3Rpb24uJylcbiAgICB9XG5cbiAgICAvLyBTaGFyZCBoYXMgbmV2ZXIgaWRlbnRpZmllZCwgc28gd2UgY2Fubm90IHJlc3VtZS5cbiAgICBpZiAoIXRoaXMuc2Vzc2lvbklkKSB7XG4gICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBUcnlpbmcgdG8gcmVzdW1lIFNoYXJkICMke3RoaXMuaWR9IHdpdGhvdXQgdGhlIHNlc3Npb24gaWQuIElkZW50aWZ5aW5nIHRoZSBzaGFyZCBpbnN0ZWFkLmApXG5cbiAgICAgIGF3YWl0IHRoaXMuaWRlbnRpZnkoKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgdGhpcy5zdGF0ZSA9IFNoYXJkU3RhdGUuUmVzdW1pbmdcblxuICAgIC8vIEJlZm9yZSB3ZSBjYW4gcmVzdW1lLCB3ZSBuZWVkIHRvIGNyZWF0ZSBhIG5ldyBjb25uZWN0aW9uIHdpdGggRGlzY29yZCdzIGdhdGV3YXkuXG4gICAgYXdhaXQgdGhpcy5jb25uZWN0KClcblxuICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBbU2hhcmRdIFJlc3VtaW5nIFNoYXJkICMke3RoaXMuaWR9IGNvbm5lY3RlZC4gU2Vzc2lvbiBpZDogJHt0aGlzLnNlc3Npb25JZH0gfCBTZXF1ZW5jZTogJHt0aGlzLnByZXZpb3VzU2VxdWVuY2VOdW1iZXJ9YClcblxuICAgIHRoaXMuc2VuZChcbiAgICAgIHtcbiAgICAgICAgb3A6IEdhdGV3YXlPcGNvZGVzLlJlc3VtZSxcbiAgICAgICAgZDoge1xuICAgICAgICAgIHRva2VuOiBgQm90ICR7dGhpcy5nYXRld2F5Q29uZmlnLnRva2VufWAsXG4gICAgICAgICAgc2Vzc2lvbl9pZDogdGhpcy5zZXNzaW9uSWQsXG4gICAgICAgICAgc2VxOiB0aGlzLnByZXZpb3VzU2VxdWVuY2VOdW1iZXIgPz8gMCxcbiAgICAgICAgfSxcbiAgICAgIH0sXG4gICAgICB0cnVlLFxuICAgIClcblxuICAgIHJldHVybiBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgdGhpcy5yZXNvbHZlcy5zZXQoJ1JFU1VNRUQnLCAoKSA9PiByZXNvbHZlKCkpXG5cbiAgICAgIC8vIElmIGl0IGlzIGF0dGVtcHRlZCB0byByZXN1bWUgd2l0aCBhbiBpbnZhbGlkIHNlc3Npb24gaWQsIERpc2NvcmQgc2VuZHMgYW4gaW52YWxpZCBzZXNzaW9uIHBheWxvYWRcbiAgICAgIC8vIE5vdCBlcnJvcmluZyBoZXJlIHNpbmNlIGl0IGlzIGVhc3kgdGhhdCB0aGlzIGhhcHBlbnMsIGFsc28gaXQgd291bGQgYmUgbm90IGNhdGNoYWJsZVxuICAgICAgdGhpcy5yZXNvbHZlcy5zZXQoJ0lOVkFMSURfU0VTU0lPTicsICgpID0+IHtcbiAgICAgICAgdGhpcy5yZXNvbHZlcy5kZWxldGUoJ1JFU1VNRUQnKVxuICAgICAgICByZXNvbHZlKClcbiAgICAgIH0pXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBTZW5kIGEgbWVzc2FnZSB0byBEaXNjb3JkLlxuICAgKiBAcGFyYW0gaGlnaFByaW9yaXR5IC0gV2hldGhlciB0aGlzIG1lc3NhZ2Ugc2hvdWxkIGJlIHNlbmQgYXNhcC5cbiAgICovXG4gIGFzeW5jIHNlbmQobWVzc2FnZTogU2hhcmRTb2NrZXRSZXF1ZXN0LCBoaWdoUHJpb3JpdHk6IGJvb2xlYW4gPSBmYWxzZSk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIEJlZm9yZSBhY3F1aXJpbmcgYSB0b2tlbiBmcm9tIHRoZSBidWNrZXQsIGNoZWNrIHdoZXRoZXIgdGhlIHNoYXJkIGlzIGN1cnJlbnRseSBvZmZsaW5lIG9yIG5vdC5cbiAgICAvLyBFbHNlIGJ1Y2tldCBhbmQgdG9rZW4gd2FpdCB0aW1lIGp1c3QgZ2V0IHdhc3RlZC5cbiAgICBhd2FpdCB0aGlzLmNoZWNrT2ZmbGluZShoaWdoUHJpb3JpdHkpXG5cbiAgICBhd2FpdCB0aGlzLmJ1Y2tldC5hY3F1aXJlKGhpZ2hQcmlvcml0eSlcblxuICAgIC8vIEl0J3MgcG9zc2libGUsIHRoYXQgdGhlIHNoYXJkIHdlbnQgb2ZmbGluZSBhZnRlciBhIHRva2VuIGhhcyBiZWVuIGFjcXVpcmVkIGZyb20gdGhlIGJ1Y2tldC5cbiAgICBhd2FpdCB0aGlzLmNoZWNrT2ZmbGluZShoaWdoUHJpb3JpdHkpXG5cbiAgICB0aGlzLnNvY2tldD8uc2VuZChKU09OLnN0cmluZ2lmeShtZXNzYWdlKSlcbiAgfVxuXG4gIC8qKiBTaHV0ZG93biB0aGUgdGhpcy4gRm9yY2VmdWxseSBkaXNjb25uZWN0IHRoZSBzaGFyZCBmcm9tIERpc2NvcmQuIFRoZSBzaGFyZCBtYXkgbm90IGF0dGVtcHQgdG8gcmVjb25uZWN0IHdpdGggRGlzY29yZC4gKi9cbiAgYXN5bmMgc2h1dGRvd24oKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgdGhpcy5jbG9zZShTaGFyZFNvY2tldENsb3NlQ29kZXMuU2h1dGRvd24sICdTaGFyZCBzaHV0dGluZyBkb3duLicpXG4gICAgdGhpcy5zdGF0ZSA9IFNoYXJkU3RhdGUuT2ZmbGluZVxuICB9XG5cbiAgLyoqIEhhbmRsZSBhIGdhdGV3YXkgY29ubmVjdGlvbiBlcnJvciAqL1xuICBoYW5kbGVFcnJvcihlcnJvcjogRXZlbnQpOiB2b2lkIHtcbiAgICB0aGlzLmxvZ2dlci5lcnJvcihgW1NoYXJkXSBUaGVyZSB3YXMgYW4gZXJyb3IgY29ubmVjdGluZyBTaGFyZCAjJHt0aGlzLmlkfS5gLCBlcnJvcilcbiAgfVxuXG4gIC8qKiBIYW5kbGUgYSBnYXRld2F5IGNvbm5lY3Rpb24gY2xvc2UuICovXG4gIGFzeW5jIGhhbmRsZUNsb3NlKGNsb3NlOiBDbG9zZUV2ZW50KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5zb2NrZXQgPSB1bmRlZmluZWRcbiAgICB0aGlzLnN0b3BIZWFydGJlYXRpbmcoKVxuXG4gICAgLy8gQ2xlYXIgdGhlIHpsaWIvenN0ZCBkYXRhXG4gICAgdGhpcy5pbmZsYXRlID0gdW5kZWZpbmVkXG4gICAgdGhpcy56c3RkRGVjb21wcmVzcyA9IHVuZGVmaW5lZFxuICAgIHRoaXMuaW5mbGF0ZUJ1ZmZlciA9IG51bGxcbiAgICB0aGlzLmRlY29tcHJlc3Npb25Qcm9taXNlc1F1ZXVlID0gW11cblxuICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBbU2hhcmRdIFNoYXJkICMke3RoaXMuaWR9IGNsb3NlZCB3aXRoIGNvZGUgJHtjbG9zZS5jb2RlfSR7Y2xvc2UucmVhc29uID8gYCwgYW5kIHJlYXNvbjogJHtjbG9zZS5yZWFzb259YCA6ICcnfS5gKVxuXG4gICAgLy8gUmVzb2x2ZSB0aGUgY2xvc2UgcHJvbWlzZSBpZiBpdCBleGlzdHNcbiAgICB0aGlzLnJlc29sdmVBZnRlckNsb3NlPy4oY2xvc2UpXG5cbiAgICBzd2l0Y2ggKGNsb3NlLmNvZGUpIHtcbiAgICAgIGNhc2UgU2hhcmRTb2NrZXRDbG9zZUNvZGVzLlRlc3RpbmdGaW5pc2hlZDoge1xuICAgICAgICB0aGlzLnN0YXRlID0gU2hhcmRTdGF0ZS5PZmZsaW5lXG4gICAgICAgIHRoaXMuZXZlbnRzLmRpc2Nvbm5lY3RlZD8uKHRoaXMpXG5cbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICAvLyBPbiB0aGVzZSBjb2RlcyBhIG1hbnVhbCBzdGFydCB3aWxsIGJlIGRvbmUuXG4gICAgICBjYXNlIFNoYXJkU29ja2V0Q2xvc2VDb2Rlcy5TaHV0ZG93bjpcbiAgICAgIGNhc2UgU2hhcmRTb2NrZXRDbG9zZUNvZGVzLlJlSWRlbnRpZnlpbmc6XG4gICAgICBjYXNlIFNoYXJkU29ja2V0Q2xvc2VDb2Rlcy5SZXNoYXJkZWQ6XG4gICAgICBjYXNlIFNoYXJkU29ja2V0Q2xvc2VDb2Rlcy5SZXN1bWVDbG9zaW5nT2xkQ29ubmVjdGlvbjoge1xuICAgICAgICB0aGlzLnN0YXRlID0gU2hhcmRTdGF0ZS5EaXNjb25uZWN0ZWRcbiAgICAgICAgdGhpcy5ldmVudHMuZGlzY29ubmVjdGVkPy4odGhpcylcblxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIC8vIFdoZW4gdGhlc2UgY29kZXMgYXJlIHJlY2VpdmVkIHNvbWV0aGluZyB3ZW50IHJlYWxseSB3cm9uZy5cbiAgICAgIC8vIE9uIHRob3NlIHdlIGNhbm5vdCBzdGFydCBhIHJlY29ubmVjdCBhdHRlbXB0LlxuICAgICAgY2FzZSBHYXRld2F5Q2xvc2VFdmVudENvZGVzLkF1dGhlbnRpY2F0aW9uRmFpbGVkOlxuICAgICAgY2FzZSBHYXRld2F5Q2xvc2VFdmVudENvZGVzLkludmFsaWRTaGFyZDpcbiAgICAgIGNhc2UgR2F0ZXdheUNsb3NlRXZlbnRDb2Rlcy5TaGFyZGluZ1JlcXVpcmVkOlxuICAgICAgY2FzZSBHYXRld2F5Q2xvc2VFdmVudENvZGVzLkludmFsaWRBcGlWZXJzaW9uOlxuICAgICAgY2FzZSBHYXRld2F5Q2xvc2VFdmVudENvZGVzLkludmFsaWRJbnRlbnRzOlxuICAgICAgY2FzZSBHYXRld2F5Q2xvc2VFdmVudENvZGVzLkRpc2FsbG93ZWRJbnRlbnRzOiB7XG4gICAgICAgIHRoaXMuc3RhdGUgPSBTaGFyZFN0YXRlLk9mZmxpbmVcbiAgICAgICAgdGhpcy5ldmVudHMuZGlzY29ubmVjdGVkPy4odGhpcylcblxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoY2xvc2UucmVhc29uIHx8ICdEaXNjb3JkIGdhdmUgbm8gcmVhc29uISBHRyEgWW91IGJyb2tlIERpc2NvcmQhJylcbiAgICAgIH1cbiAgICAgIC8vIEdhdGV3YXkgY29ubmVjdGlvbiBjbG9zZXMgd2hpY2ggcmVxdWlyZSBhIG5ldyBpZGVudGlmeS5cbiAgICAgIGNhc2UgR2F0ZXdheUNsb3NlRXZlbnRDb2Rlcy5Ob3RBdXRoZW50aWNhdGVkOlxuICAgICAgY2FzZSBHYXRld2F5Q2xvc2VFdmVudENvZGVzLkludmFsaWRTZXE6XG4gICAgICBjYXNlIEdhdGV3YXlDbG9zZUV2ZW50Q29kZXMuU2Vzc2lvblRpbWVkT3V0OiB7XG4gICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBbU2hhcmRdIFNoYXJkICMke3RoaXMuaWR9IGNsb3NlZCByZXF1aXJpbmcgcmUtaWRlbnRpZnkuYClcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFNoYXJkU3RhdGUuSWRlbnRpZnlpbmdcbiAgICAgICAgdGhpcy5ldmVudHMuZGlzY29ubmVjdGVkPy4odGhpcylcblxuICAgICAgICBhd2FpdCB0aGlzLmlkZW50aWZ5KClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICAvLyBOT1RFOiBUaGlzIGNhc2UgbXVzdCBhbHdheXMgYmUgcmlnaHQgYWJvdmUgdGhlIGNhc2VzIHRoYXQgcnVucyB3aXRoIGRlZmF1bHQgY2FzZSBiZWNhdXNlIG9mIGhvdyBzd2l0Y2ggd29ya3Mgd2hlbiB5b3UgZG9uJ3QgYnJlYWsgLyByZXR1cm4sIG1vcmUgaW5mbyBiZWxvdy5cbiAgICAgIGNhc2UgR2F0ZXdheUNsb3NlRXZlbnRDb2Rlcy5Ob3JtYWxDbG9zdXJlOlxuICAgICAgY2FzZSBHYXRld2F5Q2xvc2VFdmVudENvZGVzLkdvaW5nQXdheToge1xuICAgICAgICAvLyBJZiB0aGUgc2hhcmQgaXMgbWFya2VkIGFzIGdvaW5nT2ZmbGluZSwgaXQgc3RheXMgZGlzY29ubmVjdGVkLlxuICAgICAgICBpZiAodGhpcy5nb2luZ09mZmxpbmUpIHtcbiAgICAgICAgICB0aGlzLnN0YXRlID0gU2hhcmRTdGF0ZS5EaXNjb25uZWN0ZWRcbiAgICAgICAgICB0aGlzLmV2ZW50cy5kaXNjb25uZWN0ZWQ/Lih0aGlzKVxuXG4gICAgICAgICAgdGhpcy5nb2luZ09mZmxpbmUgPSBmYWxzZVxuXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICAvLyBPdGhlcndpc2UsIHdlIHdhbnQgdGhlIHNoYXJkIHRvIGdvIHRocm91Z2ggdGhlIGRlZmF1bHQgY2FzZSB3aGVyZSBpdCBnZXRzIHJlc3VtZWQsIGFzIGl0IG1pZ2h0IGJlIGFuIHVuZXhwZWN0ZWQgY2xvc3VyZSBmcm9tIERpc2NvcmQgb3IgQ2xvdWRmbGFyZSBmb3IgZXhhbXBsZSwgc28gd2UgZG9uJ3QgdXNlIGJyZWFrIC8gcmV0dXJuIGhlcmUuXG4gICAgICB9XG4gICAgICAvLyBHYXRld2F5IGNvbm5lY3Rpb24gY2xvc2VzIG9uIHdoaWNoIGEgcmVzdW1lIGlzIGFsbG93ZWQuXG4gICAgICBjYXNlIEdhdGV3YXlDbG9zZUV2ZW50Q29kZXMuVW5rbm93bkVycm9yOlxuICAgICAgY2FzZSBHYXRld2F5Q2xvc2VFdmVudENvZGVzLlVua25vd25PcGNvZGU6XG4gICAgICBjYXNlIEdhdGV3YXlDbG9zZUV2ZW50Q29kZXMuRGVjb2RlRXJyb3I6XG4gICAgICBjYXNlIEdhdGV3YXlDbG9zZUV2ZW50Q29kZXMuUmF0ZUxpbWl0ZWQ6XG4gICAgICBjYXNlIEdhdGV3YXlDbG9zZUV2ZW50Q29kZXMuQWxyZWFkeUF1dGhlbnRpY2F0ZWQ6XG4gICAgICBkZWZhdWx0OiB7XG4gICAgICAgIC8vIFdlIGRvbid0IHdhbnQgdG8gZ2V0IGludG8gYW4gaW5maW5pdGUgbG9vcCB3aGVyZSB3ZSByZXN1bWUgZm9yZXZlciwgc28gaWYgd2Ugd2VyZSBhbHJlYWR5IHJlc3VtaW5nIHdlIGlkZW50aWZ5IGluc3RlYWRcbiAgICAgICAgdGhpcy5zdGF0ZSA9IHRoaXMuc3RhdGUgPT09IFNoYXJkU3RhdGUuUmVzdW1pbmcgPyBTaGFyZFN0YXRlLklkZW50aWZ5aW5nIDogU2hhcmRTdGF0ZS5SZXN1bWluZ1xuICAgICAgICB0aGlzLmV2ZW50cy5kaXNjb25uZWN0ZWQ/Lih0aGlzKVxuXG4gICAgICAgIGlmICh0aGlzLnN0YXRlID09PSBTaGFyZFN0YXRlLlJlc3VtaW5nKSB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5yZXN1bWUoKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGF3YWl0IHRoaXMuaWRlbnRpZnkoKVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqIEhhbmRsZSBhbiBpbmNvbWluZyBnYXRld2F5IG1lc3NhZ2UuICovXG4gIGFzeW5jIGhhbmRsZU1lc3NhZ2UobWVzc2FnZTogTWVzc2FnZUV2ZW50KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gVGhlIHdzIG5wbSBwYWNrYWdlIHdpbGwgdXNlIGEgQnVmZmVyLCB3aGlsZSB0aGUgZ2xvYmFsIGJ1aWx0LWluIHdpbGwgdXNlIEFycmF5QnVmZmVyXG4gICAgY29uc3QgaXNDb21wcmVzc2VkID0gbWVzc2FnZS5kYXRhIGluc3RhbmNlb2YgQXJyYXlCdWZmZXIgfHwgbWVzc2FnZS5kYXRhIGluc3RhbmNlb2YgQnVmZmVyXG5cbiAgICBjb25zdCBkYXRhID0gaXNDb21wcmVzc2VkID8gYXdhaXQgdGhpcy5kZWNvbXByZXNzUGFja2V0KG1lc3NhZ2UuZGF0YSkgOiAoSlNPTi5wYXJzZShtZXNzYWdlLmRhdGEpIGFzIERpc2NvcmRHYXRld2F5UGF5bG9hZClcblxuICAgIC8vIENoZWNrIGlmIHRoZSBkZWNvbXByZXNzaW9uIHdhcyBub3Qgc3VjY2Vzc2Z1bFxuICAgIGlmICghZGF0YSkgcmV0dXJuXG5cbiAgICBhd2FpdCB0aGlzLmhhbmRsZURpc2NvcmRQYWNrZXQoZGF0YSlcbiAgfVxuXG4gIC8qKlxuICAgKiBEZWNvbXByZXNzIGEgemxpYi96c3RkIGNvbXByZXNzZWQgcGFja2V0XG4gICAqXG4gICAqIEBwcml2YXRlXG4gICAqL1xuICBhc3luYyBkZWNvbXByZXNzUGFja2V0KGRhdGE6IEFycmF5QnVmZmVyIHwgQnVmZmVyKTogUHJvbWlzZTxEaXNjb3JkR2F0ZXdheVBheWxvYWQgfCBudWxsPiB7XG4gICAgLy8gQSBidWZmZXIgaXMgYSBVaW50OEFycmF5IHVuZGVyIHRoZSBob29kLiBBbiBBcnJheUJ1ZmZlciBpcyBnZW5lcmljLCBzbyB3ZSBuZWVkIHRvIGNyZWF0ZSB0aGUgVWludDhBcnJheSB0aGF0IHVzZXMgdGhlIHdob2xlIEFycmF5QnVmZmVyXG4gICAgY29uc3QgY29tcHJlc3NlZERhdGE6IFVpbnQ4QXJyYXkgPSBkYXRhIGluc3RhbmNlb2YgQnVmZmVyID8gZGF0YSA6IG5ldyBVaW50OEFycmF5KGRhdGEpXG5cbiAgICBpZiAodGhpcy5nYXRld2F5Q29uZmlnLnRyYW5zcG9ydENvbXByZXNzaW9uID09PSBUcmFuc3BvcnRDb21wcmVzc2lvbi56bGliKSB7XG4gICAgICBpZiAoIXRoaXMuaW5mbGF0ZSkge1xuICAgICAgICB0aGlzLmxvZ2dlci5mYXRhbCgnW1NoYXJkXSB6bGliLXN0cmVhbSB0cmFuc3BvcnQgY29tcHJlc3Npb24gd2FzIGVuYWJsZWQgYnV0IG5vIGluc3RhbmNlIG9mIEluZmxhdGUgd2FzIGZvdW5kLicpXG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG5cbiAgICAgIC8vIEFsaWFzLCB1c2VkIHRvIGF2b2lkIHNvbWUgbnVsbCBjaGVja3MgaW4gdGhlIFByb21pc2UgY29uc3RydWN0b3JcbiAgICAgIGNvbnN0IGluZmxhdGUgPSB0aGlzLmluZmxhdGVcblxuICAgICAgY29uc3Qgd3JpdGVQcm9taXNlID0gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICBpbmZsYXRlLndyaXRlKGNvbXByZXNzZWREYXRhLCAnYmluYXJ5JywgKGVycm9yKSA9PiAoZXJyb3IgPyByZWplY3QoZXJyb3IpIDogcmVzb2x2ZSgpKSlcbiAgICAgIH0pXG5cbiAgICAgIGlmICghZW5kc1dpdGhNYXJrZXIoY29tcHJlc3NlZERhdGEsIFpMSUJfU1lOQ19GTFVTSCkpIHJldHVybiBudWxsXG5cbiAgICAgIGF3YWl0IHdyaXRlUHJvbWlzZVxuXG4gICAgICBpZiAoIXRoaXMuaW5mbGF0ZUJ1ZmZlcikge1xuICAgICAgICB0aGlzLmxvZ2dlci53YXJuKCdbU2hhcmRdIFRoZSBaTGliIGluZmxhdGUgYnVmZmVyIHdhcyBjbGVhcmVkIGF0IGFuIHVuZXhwZWN0ZWQgbW9tZW50LicpXG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGRlY29kZWREYXRhID0gdGhpcy50ZXh0RGVjb2Rlci5kZWNvZGUodGhpcy5pbmZsYXRlQnVmZmVyKVxuICAgICAgdGhpcy5pbmZsYXRlQnVmZmVyID0gbnVsbFxuXG4gICAgICByZXR1cm4gSlNPTi5wYXJzZShkZWNvZGVkRGF0YSlcbiAgICB9XG5cbiAgICBpZiAodGhpcy5nYXRld2F5Q29uZmlnLnRyYW5zcG9ydENvbXByZXNzaW9uID09PSBUcmFuc3BvcnRDb21wcmVzc2lvbi56c3RkKSB7XG4gICAgICBpZiAoIXRoaXMuenN0ZERlY29tcHJlc3MpIHtcbiAgICAgICAgdGhpcy5sb2dnZXIuZmF0YWwoJ1tTaGFyZF0genN0ZC1zdHJlYW0gdHJhbnNwb3J0IGNvbXByZXNzaW9uIHdhcyBlbmFibGVkIGJ1dCBubyBpbnN0YW5jZSBvZiBEZWNvbXByZXNzIHdhcyBmb3VuZC4nKVxuICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgfVxuXG4gICAgICB0aGlzLnpzdGREZWNvbXByZXNzLnB1c2goY29tcHJlc3NlZERhdGEpXG5cbiAgICAgIGNvbnN0IGRlY29tcHJlc3Npb25Qcm9taXNlID0gbmV3IFByb21pc2U8RGlzY29yZEdhdGV3YXlQYXlsb2FkPigocikgPT4gdGhpcy5kZWNvbXByZXNzaW9uUHJvbWlzZXNRdWV1ZS5wdXNoKHIpKVxuICAgICAgcmV0dXJuIGF3YWl0IGRlY29tcHJlc3Npb25Qcm9taXNlXG4gICAgfVxuXG4gICAgaWYgKHRoaXMuZ2F0ZXdheUNvbmZpZy5jb21wcmVzcykge1xuICAgICAgY29uc3QgZGVjb21wcmVzc2VkID0gaW5mbGF0ZVN5bmMoY29tcHJlc3NlZERhdGEpXG4gICAgICBjb25zdCBkZWNvZGVkRGF0YSA9IHRoaXMudGV4dERlY29kZXIuZGVjb2RlKGRlY29tcHJlc3NlZClcblxuICAgICAgcmV0dXJuIEpTT04ucGFyc2UoZGVjb2RlZERhdGEpXG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIC8qKiBIYW5kbGVzIGEgaW5jb21pbmcgZ2F0ZXdheSBwYWNrZXQuICovXG4gIGFzeW5jIGhhbmRsZURpc2NvcmRQYWNrZXQocGFja2V0OiBEaXNjb3JkR2F0ZXdheVBheWxvYWQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBFZGdlIGNhc2Ugc3RhcnQ6IGh0dHBzOi8vZ2l0aHViLmNvbS9kaXNjb3JkZW5vL2Rpc2NvcmRlbm8vaXNzdWVzLzIzMTFcbiAgICB0aGlzLmhlYXJ0Lmxhc3RBY2sgPSBEYXRlLm5vdygpXG4gICAgdGhpcy5oZWFydC5hY2tub3dsZWRnZWQgPSB0cnVlXG4gICAgLy8gRWRnZSBjYXNlIGVuZCFcblxuICAgIHN3aXRjaCAocGFja2V0Lm9wKSB7XG4gICAgICBjYXNlIEdhdGV3YXlPcGNvZGVzLkhlYXJ0YmVhdDoge1xuICAgICAgICBpZiAoIXRoaXMuaXNPcGVuKCkpIHJldHVyblxuXG4gICAgICAgIHRoaXMuaGVhcnQubGFzdEJlYXQgPSBEYXRlLm5vdygpXG4gICAgICAgIC8vIERpc2NvcmQgcmFuZG9tbHkgc2VuZHMgdGhpcyByZXF1aXJpbmcgYW4gaW1tZWRpYXRlIGhlYXJ0YmVhdCBiYWNrLlxuICAgICAgICAvLyBVc2luZyBhIGRpcmVjdCBzb2NrZXQuc2VuZCBjYWxsIGhlcmUgYmVjYXVzZSBoZWFydGJlYXQgcmVxdWVzdHMgYXJlIHJlc2VydmVkIGJ5IHVzLlxuICAgICAgICB0aGlzLnNvY2tldD8uc2VuZChcbiAgICAgICAgICBKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgICAgICBvcDogR2F0ZXdheU9wY29kZXMuSGVhcnRiZWF0LFxuICAgICAgICAgICAgZDogdGhpcy5wcmV2aW91c1NlcXVlbmNlTnVtYmVyLFxuICAgICAgICAgIH0pLFxuICAgICAgICApXG4gICAgICAgIHRoaXMuZXZlbnRzLmhlYXJ0YmVhdD8uKHRoaXMpXG5cbiAgICAgICAgYnJlYWtcbiAgICAgIH1cbiAgICAgIGNhc2UgR2F0ZXdheU9wY29kZXMuSGVsbG86IHtcbiAgICAgICAgY29uc3QgaW50ZXJ2YWwgPSAocGFja2V0LmQgYXMgRGlzY29yZEhlbGxvKS5oZWFydGJlYXRfaW50ZXJ2YWxcbiAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYFtTaGFyZF0gU2hhcmQgIyR7dGhpcy5pZH0gcmVjZWl2ZWQgSGVsbG9gKVxuICAgICAgICB0aGlzLnN0YXJ0SGVhcnRiZWF0aW5nKGludGVydmFsKVxuXG4gICAgICAgIGlmICh0aGlzLnN0YXRlICE9PSBTaGFyZFN0YXRlLlJlc3VtaW5nKSB7XG4gICAgICAgICAgY29uc3QgY3VycmVudFF1ZXVlID0gWy4uLnRoaXMuYnVja2V0LnF1ZXVlXVxuICAgICAgICAgIC8vIEhFTExPIGhhcyBiZWVuIHNlbmQgb24gYSBub24gcmVzdW1lIGFjdGlvbi5cbiAgICAgICAgICAvLyBUaGlzIG1lYW5zIHRoYXQgdGhlIHNoYXJkIHN0YXJ0cyBhIG5ldyBzZXNzaW9uLFxuICAgICAgICAgIC8vIHRoZXJlZm9yZSB0aGUgcmF0ZSBsaW1pdCBpbnRlcnZhbCBoYXMgYmVlbiByZXNldCB0b28uXG4gICAgICAgICAgdGhpcy5idWNrZXQgPSBuZXcgTGVha3lCdWNrZXQoe1xuICAgICAgICAgICAgbWF4OiB0aGlzLmNhbGN1bGF0ZVNhZmVSZXF1ZXN0cygpLFxuICAgICAgICAgICAgcmVmaWxsSW50ZXJ2YWw6IDYwMDAwLFxuICAgICAgICAgICAgcmVmaWxsQW1vdW50OiB0aGlzLmNhbGN1bGF0ZVNhZmVSZXF1ZXN0cygpLFxuICAgICAgICAgICAgbG9nZ2VyOiB0aGlzLmxvZ2dlcixcbiAgICAgICAgICB9KVxuXG4gICAgICAgICAgLy8gUXVldWUgc2hvdWxkIG5vdCBiZSBsb3N0IG9uIGEgcmUtaWRlbnRpZnkuXG4gICAgICAgICAgdGhpcy5idWNrZXQucXVldWUudW5zaGlmdCguLi5jdXJyZW50UXVldWUpXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmV2ZW50cy5oZWxsbz8uKHRoaXMpXG5cbiAgICAgICAgYnJlYWtcbiAgICAgIH1cbiAgICAgIGNhc2UgR2F0ZXdheU9wY29kZXMuSGVhcnRiZWF0QUNLOiB7XG4gICAgICAgIC8vIE1hbnVhbGx5IGNhbGN1bGF0aW5nIHRoZSByb3VuZCB0cmlwIHRpbWUgZm9yIHVzZXJzIHdobyBuZWVkIGl0LlxuICAgICAgICBpZiAodGhpcy5oZWFydC5sYXN0QmVhdCkge1xuICAgICAgICAgIHRoaXMuaGVhcnQucnR0ID0gdGhpcy5oZWFydC5sYXN0QWNrIC0gdGhpcy5oZWFydC5sYXN0QmVhdFxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5ldmVudHMuaGVhcnRiZWF0QWNrPy4odGhpcylcblxuICAgICAgICBicmVha1xuICAgICAgfVxuICAgICAgY2FzZSBHYXRld2F5T3Bjb2Rlcy5SZWNvbm5lY3Q6IHtcbiAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYFtTaGFyZF0gUmVjZWl2ZWQgYSBSZWNvbm5lY3QgZm9yIFNoYXJkICMke3RoaXMuaWR9YClcbiAgICAgICAgdGhpcy5ldmVudHMucmVxdWVzdGVkUmVjb25uZWN0Py4odGhpcylcblxuICAgICAgICBhd2FpdCB0aGlzLnJlc3VtZSgpXG5cbiAgICAgICAgYnJlYWtcbiAgICAgIH1cbiAgICAgIGNhc2UgR2F0ZXdheU9wY29kZXMuSW52YWxpZFNlc3Npb246IHtcbiAgICAgICAgY29uc3QgcmVzdW1hYmxlID0gcGFja2V0LmQgYXMgYm9vbGVhblxuICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBSZWNlaXZlZCBJbnZhbGlkIFNlc3Npb24gZm9yIFNoYXJkICMke3RoaXMuaWR9IHdpdGggcmVzdW1hYmxlIGFzICR7cmVzdW1hYmxlfWApXG5cbiAgICAgICAgdGhpcy5ldmVudHMuaW52YWxpZFNlc3Npb24/Lih0aGlzLCByZXN1bWFibGUpXG5cbiAgICAgICAgLy8gV2UgbmVlZCB0byB3YWl0IGZvciBhIHJhbmRvbSBhbW91bnQgb2YgdGltZSBiZXR3ZWVuIDEgYW5kIDVcbiAgICAgICAgLy8gUmVmZXJlbmNlOiBodHRwczovL2Rpc2NvcmQuY29tL2RldmVsb3BlcnMvZG9jcy90b3BpY3MvZ2F0ZXdheSNyZXN1bWluZ1xuICAgICAgICBhd2FpdCBkZWxheShNYXRoLmZsb29yKChNYXRoLnJhbmRvbSgpICogNCArIDEpICogMTAwMCkpXG5cbiAgICAgICAgdGhpcy5yZXNvbHZlcy5nZXQoJ0lOVkFMSURfU0VTU0lPTicpPy4ocGFja2V0KVxuICAgICAgICB0aGlzLnJlc29sdmVzLmRlbGV0ZSgnSU5WQUxJRF9TRVNTSU9OJylcblxuICAgICAgICAvLyBXaGVuIHJlc3VtYWJsZSBpcyBmYWxzZSB3ZSBuZWVkIHRvIHJlLWlkZW50aWZ5XG4gICAgICAgIGlmICghcmVzdW1hYmxlKSB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5pZGVudGlmeSgpXG5cbiAgICAgICAgICBicmVha1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVGhlIHNlc3Npb24gaXMgaW52YWxpZCBidXQgYXBwYXJlbnRseSBpdCBpcyByZXN1bWFibGVcbiAgICAgICAgYXdhaXQgdGhpcy5yZXN1bWUoKVxuXG4gICAgICAgIGJyZWFrXG4gICAgICB9XG4gICAgfVxuXG4gICAgc3dpdGNoIChwYWNrZXQudCkge1xuICAgICAgY2FzZSAnUkVTVU1FRCc6XG4gICAgICAgIHRoaXMuc3RhdGUgPSBTaGFyZFN0YXRlLkNvbm5lY3RlZFxuICAgICAgICB0aGlzLmV2ZW50cy5yZXN1bWVkPy4odGhpcylcblxuICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBTaGFyZCAjJHt0aGlzLmlkfSByZWNlaXZlZCBSRVNVTUVEYClcblxuICAgICAgICAvLyBDb250aW51ZSB0aGUgcmVxdWVzdHMgd2hpY2ggaGF2ZSBiZWVuIHF1ZXVlZCBzaW5jZSB0aGUgc2hhcmQgd2VudCBvZmZsaW5lLlxuICAgICAgICB0aGlzLm9mZmxpbmVTZW5kUXVldWUuZm9yRWFjaCgocmVzb2x2ZSkgPT4gcmVzb2x2ZSgpKVxuICAgICAgICAvLyBTZXR0aW5nIHRoZSBsZW5ndGggdG8gMCB3aWxsIGRlbGV0ZSB0aGUgZWxlbWVudHMgaW4gaXRcbiAgICAgICAgdGhpcy5vZmZsaW5lU2VuZFF1ZXVlLmxlbmd0aCA9IDBcblxuICAgICAgICB0aGlzLnJlc29sdmVzLmdldCgnUkVTVU1FRCcpPy4ocGFja2V0KVxuICAgICAgICB0aGlzLnJlc29sdmVzLmRlbGV0ZSgnUkVTVU1FRCcpXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdSRUFEWSc6IHtcbiAgICAgICAgY29uc3QgcGF5bG9hZCA9IHBhY2tldC5kIGFzIERpc2NvcmRSZWFkeVxuICAgICAgICB0aGlzLmV2ZW50cy5yZWFkeT8uKHRoaXMpXG5cbiAgICAgICAgLy8gSW1wb3J0YW50IGZvciBmdXR1cmUgcmVzdW1lcy5cbiAgICAgICAgdGhpcy5yZXN1bWVHYXRld2F5VXJsID0gcGF5bG9hZC5yZXN1bWVfZ2F0ZXdheV91cmxcbiAgICAgICAgdGhpcy5zZXNzaW9uSWQgPSBwYXlsb2FkLnNlc3Npb25faWRcblxuICAgICAgICB0aGlzLnN0YXRlID0gU2hhcmRTdGF0ZS5Db25uZWN0ZWRcblxuICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBTaGFyZCAjJHt0aGlzLmlkfSByZWNlaXZlZCBSRUFEWWApXG5cbiAgICAgICAgLy8gQ29udGludWUgdGhlIHJlcXVlc3RzIHdoaWNoIGhhdmUgYmVlbiBxdWV1ZWQgc2luY2UgdGhlIHNoYXJkIHdlbnQgb2ZmbGluZS5cbiAgICAgICAgLy8gSW1wb3J0YW50IHdoZW4gdGhpcyBpcyBhIHJlLWlkZW50aWZ5XG4gICAgICAgIHRoaXMub2ZmbGluZVNlbmRRdWV1ZS5mb3JFYWNoKChyZXNvbHZlKSA9PiByZXNvbHZlKCkpXG4gICAgICAgIC8vIFNldHRpbmcgdGhlIGxlbmd0aCB0byAwIHdpbGwgZGVsZXRlIHRoZSBlbGVtZW50cyBpbiBpdFxuICAgICAgICB0aGlzLm9mZmxpbmVTZW5kUXVldWUubGVuZ3RoID0gMFxuXG4gICAgICAgIHRoaXMucmVzb2x2ZXMuZ2V0KCdSRUFEWScpPy4ocGFja2V0KVxuICAgICAgICB0aGlzLnJlc29sdmVzLmRlbGV0ZSgnUkVBRFknKVxuICAgICAgICBicmVha1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFVwZGF0ZSB0aGUgc2VxdWVuY2UgbnVtYmVyIGlmIGl0IGlzIHByZXNlbnRcbiAgICAvLyBgc2AgY2FuIGJlIGVpdGhlciBgbnVsbGAgb3IgYSBgbnVtYmVyYC5cbiAgICAvLyBJbiBvcmRlciB0byBwcmV2ZW50IHVwZGF0ZSBtaXNzZXMgd2hlbiBgc2AgaXMgYDBgIHdlIGNoZWNrIGFnYWluc3QgbnVsbC5cbiAgICBpZiAocGFja2V0LnMgIT09IG51bGwpIHtcbiAgICAgIHRoaXMucHJldmlvdXNTZXF1ZW5jZU51bWJlciA9IHBhY2tldC5zXG4gICAgfVxuXG4gICAgdGhpcy5mb3J3YXJkVG9Cb3QocGFja2V0KVxuICB9XG5cbiAgZm9yd2FyZFRvQm90KHBhY2tldDogRGlzY29yZEdhdGV3YXlQYXlsb2FkKTogdm9pZCB7XG4gICAgLy8gVGhlIG5lY2Vzc2FyeSBoYW5kbGluZyByZXF1aXJlZCBmb3IgdGhlIFNoYXJkcyBjb25uZWN0aW9uIGhhcyBiZWVuIGZpbmlzaGVkLlxuICAgIC8vIE5vdyB0aGUgZXZlbnQgY2FuIGJlIHNhZmVseSBmb3J3YXJkZWQuXG4gICAgdGhpcy5ldmVudHMubWVzc2FnZT8uKHRoaXMsIGNhbWVsaXplKHBhY2tldCkpXG4gIH1cblxuICAvKipcbiAgICogT3ZlcnJpZGUgaW4gb3JkZXIgdG8gbWFrZSB0aGUgc2hhcmRzIHByZXNlbmNlLlxuICAgKiBhc3luYyBpbiBjYXNlIGRldnMgY3JlYXRlIHRoZSBwcmVzZW5jZSBiYXNlZCBvbiBlZy4gZGF0YWJhc2UgdmFsdWVzLlxuICAgKiBQYXNzaW5nIHRoZSBzaGFyZCdzIGlkIHRoZXJlIHRvIG1ha2UgaXQgZWFzaWVyIGZvciB0aGUgZGV2IHRvIHVzZSB0aGlzIGZ1bmN0aW9uLlxuICAgKi9cbiAgYXN5bmMgbWFrZVByZXNlbmNlKCk6IFByb21pc2U8RGlzY29yZFVwZGF0ZVByZXNlbmNlIHwgdW5kZWZpbmVkPiB7XG4gICAgcmV0dXJuXG4gIH1cblxuICAvKipcbiAgICogVGhpcyBmdW5jdGlvbiBjb21tdW5pY2F0ZXMgd2l0aCB0aGUgbWFuYWdlbWVudCBwcm9jZXNzLCBpbiBvcmRlciB0byBrbm93IHdoZXRoZXIgaXRzIGZyZWUgdG8gaWRlbnRpZnkuXG4gICAqIFdoZW4gdGhpcyBmdW5jdGlvbiByZXNvbHZlcywgdGhpcyBtZWFucyB0aGF0IHRoZSBzaGFyZCBpcyBhbGxvd2VkIHRvIHNlbmQgYW4gaWRlbnRpZnkgcGF5bG9hZCB0byBkaXNjb3JkLlxuICAgKi9cbiAgYXN5bmMgcmVxdWVzdElkZW50aWZ5KCk6IFByb21pc2U8dm9pZD4ge31cblxuICAvKiogU3RhcnQgc2VuZGluZyBoZWFydGJlYXQgcGF5bG9hZHMgdG8gRGlzY29yZCBpbiB0aGUgcHJvdmlkZWQgaW50ZXJ2YWwuICovXG4gIHN0YXJ0SGVhcnRiZWF0aW5nKGludGVydmFsOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBTdGFydCBoZWFydGJlYXRpbmcgb24gU2hhcmQgIyR7dGhpcy5pZH1gKVxuXG4gICAgLy8gSWYgb2xkIGhlYXJ0YmVhc3QgZXhpc3QgbGlrZSBhZnRlciByZXN1bWUsIGNsZWFyIHRoZSBvbGQgb25lcy5cbiAgICB0aGlzLnN0b3BIZWFydGJlYXRpbmcoKVxuXG4gICAgdGhpcy5oZWFydC5pbnRlcnZhbCA9IGludGVydmFsXG5cbiAgICAvLyBPbmx5IHNldCB0aGUgc2hhcmQncyBzdGF0ZSB0byBgVW5pZGVudGlmaWVkYFxuICAgIC8vIGlmIGhlYXJ0YmVhdGluZyBoYXMgbm90IGJlZW4gc3RhcnRlZCBkdWUgdG8gYW4gaWRlbnRpZnkgb3IgcmVzdW1lIGFjdGlvbi5cbiAgICBpZiAoW1NoYXJkU3RhdGUuRGlzY29ubmVjdGVkLCBTaGFyZFN0YXRlLk9mZmxpbmVdLmluY2x1ZGVzKHRoaXMuc3RhdGUpKSB7XG4gICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBTaGFyZCBpcyBkaXNjb25uZWN0ZWQgb3Igb2ZmbGluZSBidXQgdGhlIGhlYXJ0YmVhdCB3YXMgc3RhcnRlZCAjJHt0aGlzLmlkfWApXG4gICAgICB0aGlzLnN0YXRlID0gU2hhcmRTdGF0ZS5VbmlkZW50aWZpZWRcbiAgICB9XG5cbiAgICAvLyBUaGUgZmlyc3QgaGVhcnRiZWF0IG5lZWRzIHRvIGJlIHNlbmQgd2l0aCBhIHJhbmRvbSBkZWxheSBiZXR3ZWVuIGAwYCBhbmQgYGludGVydmFsYFxuICAgIC8vIFVzaW5nIGEgYHNldFRpbWVvdXQoXywgaml0dGVyKWAgaGVyZSB0byBhY2NvbXBsaXNoIHRoYXQuXG4gICAgLy8gYE1hdGgucmFuZG9tKClgIGNhbiBiZSBgMGAgc28gd2UgdXNlIGAwLjVgIGlmIHRoaXMgaGFwcGVuc1xuICAgIC8vIFJlZmVyZW5jZTogaHR0cHM6Ly9kaXNjb3JkLmNvbS9kZXZlbG9wZXJzL2RvY3MvdG9waWNzL2dhdGV3YXkjaGVhcnRiZWF0aW5nXG4gICAgY29uc3Qgaml0dGVyID0gTWF0aC5jZWlsKHRoaXMuaGVhcnQuaW50ZXJ2YWwgKiAoTWF0aC5yYW5kb20oKSB8fCAwLjUpKVxuXG4gICAgdGhpcy5oZWFydC50aW1lb3V0SWQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBbU2hhcmRdIEJlZ2lubmluZyBoZWFydGJlYXRpbmcgcHJvY2VzcyBmb3IgU2hhcmQgIyR7dGhpcy5pZH1gKVxuXG4gICAgICBpZiAoIXRoaXMuaXNPcGVuKCkpIHJldHVyblxuXG4gICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBIZWFydGJlYXRpbmcgb24gU2hhcmQgIyR7dGhpcy5pZH0uIFByZXZpb3VzIHNlcXVlbmNlIG51bWJlcjogJHt0aGlzLnByZXZpb3VzU2VxdWVuY2VOdW1iZXJ9YClcblxuICAgICAgLy8gVXNpbmcgYSBkaXJlY3Qgc29ja2V0LnNlbmQgY2FsbCBoZXJlIGJlY2F1c2UgaGVhcnRiZWF0IHJlcXVlc3RzIGFyZSByZXNlcnZlZCBieSB1cy5cbiAgICAgIHRoaXMuc29ja2V0Py5zZW5kKFxuICAgICAgICBKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgICAgb3A6IEdhdGV3YXlPcGNvZGVzLkhlYXJ0YmVhdCxcbiAgICAgICAgICBkOiB0aGlzLnByZXZpb3VzU2VxdWVuY2VOdW1iZXIsXG4gICAgICAgIH0pLFxuICAgICAgKVxuXG4gICAgICB0aGlzLmhlYXJ0Lmxhc3RCZWF0ID0gRGF0ZS5ub3coKVxuICAgICAgdGhpcy5oZWFydC5hY2tub3dsZWRnZWQgPSBmYWxzZVxuXG4gICAgICAvLyBBZnRlciB0aGUgcmFuZG9tIGhlYXJ0YmVhdCBqaXR0ZXIgd2UgY2FuIHN0YXJ0IGEgbm9ybWFsIGludGVydmFsLlxuICAgICAgdGhpcy5oZWFydC5pbnRlcnZhbElkID0gc2V0SW50ZXJ2YWwoYXN5bmMgKCkgPT4ge1xuICAgICAgICBpZiAoIXRoaXMuaXNPcGVuKCkpIHtcbiAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBTaGFyZCAjJHt0aGlzLmlkfSBpcyBub3Qgb3BlbiwgYnV0IGF0dGVtcHRlZCBoZWFydGJlYXQsIGlnbm9yaW5nLmApXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICAvLyBUaGUgU2hhcmQgZGlkIG5vdCByZWNlaXZlIGEgaGVhcnRiZWF0IEFDSyBmcm9tIERpc2NvcmQgaW4gdGltZSxcbiAgICAgICAgLy8gdGhlcmVmb3JlIHdlIGhhdmUgdG8gYXNzdW1lIHRoYXQgdGhlIGNvbm5lY3Rpb24gaGFzIGZhaWxlZCBvciBnb3QgXCJ6b21iaWVkXCIuXG4gICAgICAgIC8vIFRoZSBTaGFyZCBuZWVkcyB0byBzdGFydCBhIHJlLWlkZW50aWZ5IGFjdGlvbiBhY2NvcmRpbmdseS5cbiAgICAgICAgLy8gUmVmZXJlbmNlOiBodHRwczovL2Rpc2NvcmQuY29tL2RldmVsb3BlcnMvZG9jcy90b3BpY3MvZ2F0ZXdheSNoZWFydGJlYXRpbmctZXhhbXBsZS1nYXRld2F5LWhlYXJ0YmVhdC1hY2tcbiAgICAgICAgaWYgKCF0aGlzLmhlYXJ0LmFja25vd2xlZGdlZCkge1xuICAgICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBbU2hhcmRdIEhlYXJ0YmVhdCBub3QgYWNrbm93bGVkZ2VkIGZvciBTaGFyZCAjJHt0aGlzLmlkfS4gQXNzdW1pbmcgem9tYmllZCBjb25uZWN0aW9uLmApXG4gICAgICAgICAgYXdhaXQgdGhpcy5jbG9zZShTaGFyZFNvY2tldENsb3NlQ29kZXMuWm9tYmllZENvbm5lY3Rpb24sICdab21iaWVkIGNvbm5lY3Rpb24sIGRpZCBub3QgcmVjZWl2ZSBhbiBoZWFydGJlYXQgQUNLIGluIHRpbWUuJylcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBbU2hhcmRdIEhlYXJ0YmVhdGluZyBvbiBTaGFyZCAjJHt0aGlzLmlkfS4gUHJldmlvdXMgc2VxdWVuY2UgbnVtYmVyOiAke3RoaXMucHJldmlvdXNTZXF1ZW5jZU51bWJlcn1gKVxuXG4gICAgICAgIC8vIFVzaW5nIGEgZGlyZWN0IHNvY2tldC5zZW5kIGNhbGwgaGVyZSBiZWNhdXNlIGhlYXJ0YmVhdCByZXF1ZXN0cyBhcmUgcmVzZXJ2ZWQgYnkgdXMuXG4gICAgICAgIHRoaXMuc29ja2V0Py5zZW5kKFxuICAgICAgICAgIEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgICAgIG9wOiBHYXRld2F5T3Bjb2Rlcy5IZWFydGJlYXQsXG4gICAgICAgICAgICBkOiB0aGlzLnByZXZpb3VzU2VxdWVuY2VOdW1iZXIsXG4gICAgICAgICAgfSksXG4gICAgICAgIClcblxuICAgICAgICB0aGlzLmhlYXJ0Lmxhc3RCZWF0ID0gRGF0ZS5ub3coKVxuICAgICAgICB0aGlzLmhlYXJ0LmFja25vd2xlZGdlZCA9IGZhbHNlXG5cbiAgICAgICAgdGhpcy5ldmVudHMuaGVhcnRiZWF0Py4odGhpcylcbiAgICAgIH0sIHRoaXMuaGVhcnQuaW50ZXJ2YWwpXG4gICAgfSwgaml0dGVyKVxuICB9XG5cbiAgLyoqIFN0b3AgdGhlIGhlYXJ0YmVhdGluZyBwcm9jZXNzIHdpdGggZGlzY29yZC4gKi9cbiAgc3RvcEhlYXJ0YmVhdGluZygpOiB2b2lkIHtcbiAgICAvLyBDbGVhciB0aGUgcmVndWxhciBoZWFydGJlYXQgaW50ZXJ2YWwuXG4gICAgY2xlYXJJbnRlcnZhbCh0aGlzLmhlYXJ0LmludGVydmFsSWQpXG4gICAgLy8gSXQncyBwb3NzaWJsZSB0aGF0IHRoZSBTaGFyZCBnb3QgY2xvc2VkIGJlZm9yZSB0aGUgZmlyc3Qgaml0dGVyZWQgaGVhcnRiZWF0LlxuICAgIC8vIFRvIGdvIHNhZmUgd2Ugc2hvdWxkIGNsZWFyIHRoZSByZWxhdGVkIHRpbWVvdXQgdG9vLlxuICAgIGNsZWFyVGltZW91dCh0aGlzLmhlYXJ0LnRpbWVvdXRJZClcbiAgfVxufVxuXG4vKiogQ2hlY2sgaWYgdGhlIGJ1ZmZlciBlbmRzIHdpdGggdGhlIG1hcmtlciAqL1xuZnVuY3Rpb24gZW5kc1dpdGhNYXJrZXIoYnVmZmVyOiBVaW50OEFycmF5LCBtYXJrZXI6IFVpbnQ4QXJyYXkpIHtcbiAgaWYgKGJ1ZmZlci5sZW5ndGggPCBtYXJrZXIubGVuZ3RoKSByZXR1cm4gZmFsc2VcblxuICBmb3IgKGxldCBpID0gMDsgaSA8IG1hcmtlci5sZW5ndGg7IGkrKykge1xuICAgIGlmIChidWZmZXJbYnVmZmVyLmxlbmd0aCAtIG1hcmtlci5sZW5ndGggKyBpXSAhPT0gbWFya2VyW2ldKSByZXR1cm4gZmFsc2VcbiAgfVxuXG4gIHJldHVybiB0cnVlXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2hhcmRDcmVhdGVPcHRpb25zIHtcbiAgLyoqIFRoZSBzaGFyZCBpZCAqL1xuICBpZDogbnVtYmVyXG4gIC8qKiBUaGUgY29ubmVjdGlvbiBkZXRhaWxzICovXG4gIGNvbm5lY3Rpb246IFNoYXJkR2F0ZXdheUNvbmZpZ1xuICAvKiogVGhlIGV2ZW50IGhhbmRsZXJzIGZvciBldmVudHMgb24gdGhlIHNoYXJkLiAqL1xuICBldmVudHM6IFNoYXJkRXZlbnRzXG4gIC8qKiBUaGUgbG9nZ2VyIGZvciB0aGUgc2hhcmQgKi9cbiAgbG9nZ2VyPzogUGljazx0eXBlb2YgbG9nZ2VyLCAnZGVidWcnIHwgJ2luZm8nIHwgJ3dhcm4nIHwgJ2Vycm9yJyB8ICdmYXRhbCc+XG4gIC8qKiBUaGUgaGFuZGxlciB0byByZXF1ZXN0IGEgc3BhY2UgdG8gbWFrZSBhbiBpZGVudGlmeSByZXF1ZXN0LiAqL1xuICByZXF1ZXN0SWRlbnRpZnk/OiAoKSA9PiBQcm9taXNlPHZvaWQ+XG4gIC8qKiBGdW5jdGlvbiB0byBjcmVhdGUgdGhlIGJvdCBzdGF0dXMgdG8gc2VuZCBvbiBJZGVudGlmeSByZXF1ZXN0cyAqL1xuICBtYWtlUHJlc2VuY2U/OiAoKSA9PiBQcm9taXNlPERpc2NvcmRVcGRhdGVQcmVzZW5jZSB8IHVuZGVmaW5lZD5cbn1cblxuZXhwb3J0IGRlZmF1bHQgRGlzY29yZGVub1NoYXJkXG4iXSwibmFtZXMiOlsiRGlzY29yZGVub1NoYXJkIiwiWkxJQl9TWU5DX0ZMVVNIIiwiVWludDhBcnJheSIsImZ6c3RkIiwiZ2V0RlpTdGQiLCJvcHRpb25zIiwibWF4UmVxdWVzdHNQZXJSYXRlTGltaXRUaWNrIiwicHJldmlvdXNTZXF1ZW5jZU51bWJlciIsInJhdGVMaW1pdFJlc2V0SW50ZXJ2YWwiLCJzdGF0ZSIsIlNoYXJkU3RhdGUiLCJPZmZsaW5lIiwicmVzdW1lR2F0ZXdheVVybCIsImV2ZW50cyIsIm9mZmxpbmVTZW5kUXVldWUiLCJyZXNvbHZlcyIsIk1hcCIsImdvaW5nT2ZmbGluZSIsInRleHREZWNvZGVyIiwiVGV4dERlY29kZXIiLCJpbmZsYXRlQnVmZmVyIiwiZGVjb21wcmVzc2lvblByb21pc2VzUXVldWUiLCJpZCIsImNvbm5lY3Rpb24iLCJsb2dnZXIiLCJoZWFydCIsImFja25vd2xlZGdlZCIsImludGVydmFsIiwicmVxdWVzdElkZW50aWZ5IiwibWFrZVByZXNlbmNlIiwiYnVja2V0IiwiTGVha3lCdWNrZXQiLCJtYXgiLCJjYWxjdWxhdGVTYWZlUmVxdWVzdHMiLCJyZWZpbGxBbW91bnQiLCJyZWZpbGxJbnRlcnZhbCIsImdhdGV3YXlDb25maWciLCJjb25uZWN0aW9uVXJsIiwidXJsIiwic2FmZVJlcXVlc3RzIiwiTWF0aCIsImNlaWwiLCJjaGVja09mZmxpbmUiLCJoaWdoUHJpb3JpdHkiLCJpc09wZW4iLCJQcm9taXNlIiwicmVzb2x2ZSIsInVuc2hpZnQiLCJwdXNoIiwiY2xvc2UiLCJjb2RlIiwicmVhc29uIiwiZGVidWciLCJzb2NrZXQiLCJyZWFkeVN0YXRlIiwiTm9kZVdlYlNvY2tldCIsIk9QRU4iLCJHYXRld2F5Q2xvc2VFdmVudENvZGVzIiwiTm9ybWFsQ2xvc3VyZSIsIkdvaW5nQXdheSIsInByb21pc2UiLCJyZXNvbHZlQWZ0ZXJDbG9zZSIsInVuZGVmaW5lZCIsImNvbm5lY3QiLCJJZGVudGlmeWluZyIsIlJlc3VtaW5nIiwiaW5jbHVkZXMiLCJDb25uZWN0aW5nIiwiY29ubmVjdGluZyIsIlVSTCIsInNlYXJjaFBhcmFtcyIsInNldCIsInZlcnNpb24iLCJ0b1N0cmluZyIsInRyYW5zcG9ydENvbXByZXNzaW9uIiwiVHJhbnNwb3J0Q29tcHJlc3Npb24iLCJ6bGliIiwiaW5mbGF0ZSIsImNyZWF0ZUluZmxhdGUiLCJmaW5pc2hGbHVzaCIsInpsaWJDb25zdGFudHMiLCJaX1NZTkNfRkxVU0giLCJjaHVua1NpemUiLCJvbiIsImUiLCJlcnJvciIsImRhdGEiLCJuZXdCdWZmZXIiLCJieXRlTGVuZ3RoIiwienN0ZCIsImNhdGNoIiwid2FybiIsImRlbGV0ZSIsInpzdGREZWNvbXByZXNzIiwiRGVjb21wcmVzcyIsImRlY29kZWREYXRhIiwiZGVjb2RlIiwicGFyc2VkRGF0YSIsIkpTT04iLCJwYXJzZSIsInNoaWZ0IiwiY29tcHJlc3MiLCJzaG91bGRVc2VCdWlsdGluIiwiUmVmbGVjdCIsImhhcyIsImdsb2JhbFRoaXMiLCJXZWJTb2NrZXQiLCJiaW5hcnlUeXBlIiwib25lcnJvciIsImV2ZW50IiwiaGFuZGxlRXJyb3IiLCJvbmNsb3NlIiwiY2xvc2VFdmVudCIsImhhbmRsZUNsb3NlIiwib25tZXNzYWdlIiwibWVzc2FnZUV2ZW50IiwiaGFuZGxlTWVzc2FnZSIsIm9ub3BlbiIsIlVuaWRlbnRpZmllZCIsImNvbm5lY3RlZCIsImlkZW50aWZ5IiwiYnlwYXNzUmVxdWVzdCIsIlNoYXJkU29ja2V0Q2xvc2VDb2RlcyIsIlJlSWRlbnRpZnlpbmciLCJpZGVudGlmeWluZyIsInNlbmQiLCJvcCIsIkdhdGV3YXlPcGNvZGVzIiwiSWRlbnRpZnkiLCJkIiwidG9rZW4iLCJwcm9wZXJ0aWVzIiwiaW50ZW50cyIsInNoYXJkIiwidG90YWxTaGFyZHMiLCJwcmVzZW5jZSIsInJlc3VtZSIsIlJlc3VtZUNsb3NpbmdPbGRDb25uZWN0aW9uIiwic2Vzc2lvbklkIiwiUmVzdW1lIiwic2Vzc2lvbl9pZCIsInNlcSIsIm1lc3NhZ2UiLCJhY3F1aXJlIiwic3RyaW5naWZ5Iiwic2h1dGRvd24iLCJTaHV0ZG93biIsInN0b3BIZWFydGJlYXRpbmciLCJUZXN0aW5nRmluaXNoZWQiLCJkaXNjb25uZWN0ZWQiLCJSZXNoYXJkZWQiLCJEaXNjb25uZWN0ZWQiLCJBdXRoZW50aWNhdGlvbkZhaWxlZCIsIkludmFsaWRTaGFyZCIsIlNoYXJkaW5nUmVxdWlyZWQiLCJJbnZhbGlkQXBpVmVyc2lvbiIsIkludmFsaWRJbnRlbnRzIiwiRGlzYWxsb3dlZEludGVudHMiLCJFcnJvciIsIk5vdEF1dGhlbnRpY2F0ZWQiLCJJbnZhbGlkU2VxIiwiU2Vzc2lvblRpbWVkT3V0IiwiVW5rbm93bkVycm9yIiwiVW5rbm93bk9wY29kZSIsIkRlY29kZUVycm9yIiwiUmF0ZUxpbWl0ZWQiLCJBbHJlYWR5QXV0aGVudGljYXRlZCIsImlzQ29tcHJlc3NlZCIsIkFycmF5QnVmZmVyIiwiQnVmZmVyIiwiZGVjb21wcmVzc1BhY2tldCIsImhhbmRsZURpc2NvcmRQYWNrZXQiLCJjb21wcmVzc2VkRGF0YSIsImZhdGFsIiwid3JpdGVQcm9taXNlIiwicmVqZWN0Iiwid3JpdGUiLCJlbmRzV2l0aE1hcmtlciIsImRlY29tcHJlc3Npb25Qcm9taXNlIiwiciIsImRlY29tcHJlc3NlZCIsImluZmxhdGVTeW5jIiwicGFja2V0IiwibGFzdEFjayIsIkRhdGUiLCJub3ciLCJIZWFydGJlYXQiLCJsYXN0QmVhdCIsImhlYXJ0YmVhdCIsIkhlbGxvIiwiaGVhcnRiZWF0X2ludGVydmFsIiwic3RhcnRIZWFydGJlYXRpbmciLCJjdXJyZW50UXVldWUiLCJxdWV1ZSIsImhlbGxvIiwiSGVhcnRiZWF0QUNLIiwicnR0IiwiaGVhcnRiZWF0QWNrIiwiUmVjb25uZWN0IiwicmVxdWVzdGVkUmVjb25uZWN0IiwiSW52YWxpZFNlc3Npb24iLCJyZXN1bWFibGUiLCJpbnZhbGlkU2Vzc2lvbiIsImRlbGF5IiwiZmxvb3IiLCJyYW5kb20iLCJnZXQiLCJ0IiwiQ29ubmVjdGVkIiwicmVzdW1lZCIsImZvckVhY2giLCJsZW5ndGgiLCJwYXlsb2FkIiwicmVhZHkiLCJyZXN1bWVfZ2F0ZXdheV91cmwiLCJzIiwiZm9yd2FyZFRvQm90IiwiY2FtZWxpemUiLCJqaXR0ZXIiLCJ0aW1lb3V0SWQiLCJzZXRUaW1lb3V0IiwiaW50ZXJ2YWxJZCIsInNldEludGVydmFsIiwiWm9tYmllZENvbm5lY3Rpb24iLCJjbGVhckludGVydmFsIiwiY2xlYXJUaW1lb3V0IiwiYnVmZmVyIiwibWFya2VyIiwiaSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7UUEwQmFBO2VBQUFBOztRQW16QmI7ZUFBQTs7OzRCQTcwQnVCOzBCQUM4RDt1QkFFOUI7dUJBQ0Y7MkRBRTNCO3dCQVNuQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFFUCxNQUFNQyxrQkFBa0IsSUFBSUMsV0FBVztJQUFDO0lBQUs7SUFBSztJQUFNO0NBQUs7QUFFN0QsSUFBSUM7QUFFSix3RUFBd0UsR0FDeEUsZUFBZUM7SUFDYixPQUFRRCxVQUFVLE1BQU0sbUVBQUEsUUFBTztBQUNqQztBQUVPLElBQUEsQUFBTUgsa0JBQU4sTUFBTUE7SUEyRFgsWUFBWUssT0FBMkIsQ0FBRTtRQXBEekMsMEhBQTBILFFBQzFIQyw4QkFBc0M7UUFDdEMsMENBQTBDLFFBQzFDQyx5QkFBd0M7UUFDeEMsNEVBQTRFLFFBQzVFQyx5QkFBaUM7UUFLakMsd0NBQXdDLFFBQ3hDQyxRQUFRQyxrQkFBVSxDQUFDQyxPQUFPO1FBQzFCLGlGQUFpRixRQUNqRkMsbUJBQTJCO1FBQzNCLHNDQUFzQyxRQUN0Q0MsU0FBc0IsQ0FBQztRQUN2QixtR0FBbUcsUUFDbkdDLG1CQUFtQyxFQUFFO1FBQ3JDLGlGQUFpRixRQUNqRkMsV0FBVyxJQUFJQztRQUtmOzs7Ozs7OztHQVFDLFFBQ0RDLGVBQWU7UUFDZiwrQ0FBK0MsUUFDL0NDLGNBQWMsSUFBSUM7UUFHbEIseUJBQXlCLFFBQ3pCQyxnQkFBbUM7UUFHbkMsc0RBQXNELFFBQ3REQyw2QkFBd0UsRUFBRTtRQVV4RSxJQUFJLENBQUNDLEVBQUUsR0FBR2pCLFFB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
|