@discordeno/gateway 19.0.0-next.fe00a6f → 19.0.0-next.ffdef6c
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/README.md +16 -13
- package/dist/cjs/Shard.cjs +649 -0
- package/dist/cjs/index.cjs +22 -0
- package/dist/cjs/manager.cjs +503 -0
- package/dist/cjs/types.cjs +64 -0
- package/dist/esm/Shard.js +585 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/manager.js +488 -0
- package/dist/esm/types.js +43 -0
- package/dist/{Shard.d.ts → types/Shard.d.ts} +43 -95
- package/dist/types/Shard.d.ts.map +1 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/{manager.d.ts → types/manager.d.ts} +111 -12
- package/dist/types/manager.d.ts.map +1 -0
- package/dist/{types.d.ts → types/types.d.ts} +39 -5
- package/dist/types/types.d.ts.map +1 -0
- package/package.json +35 -28
- package/dist/Shard.d.ts.map +0 -1
- package/dist/Shard.js +0 -615
- package/dist/Shard.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -5
- package/dist/index.js.map +0 -1
- package/dist/manager.d.ts.map +0 -1
- package/dist/manager.js +0 -243
- package/dist/manager.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -23
- package/dist/types.js.map +0 -1
- /package/dist/{index.d.ts → types/index.d.ts} +0 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export * from './manager.js';
|
|
2
|
+
export * from './Shard.js';
|
|
3
|
+
export * from './types.js';
|
|
4
|
+
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9pbmRleC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgKiBmcm9tICcuL21hbmFnZXIuanMnXG5leHBvcnQgKiBmcm9tICcuL1NoYXJkLmpzJ1xuZXhwb3J0ICogZnJvbSAnLi90eXBlcy5qcydcbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGVBQWM7QUFDNUIsY0FBYyxhQUFZO0FBQzFCLGNBQWMsYUFBWSJ9
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { GatewayIntents, GatewayOpcodes } from '@discordeno/types';
|
|
2
|
+
import { Collection, delay, logger } from '@discordeno/utils';
|
|
3
|
+
import Shard from './Shard.js';
|
|
4
|
+
import { ShardSocketCloseCodes } from './types.js';
|
|
5
|
+
export function createGatewayManager(options) {
|
|
6
|
+
const connectionOptions = options.connection ?? {
|
|
7
|
+
url: 'wss://gateway.discord.gg',
|
|
8
|
+
shards: 1,
|
|
9
|
+
sessionStartLimit: {
|
|
10
|
+
maxConcurrency: 1,
|
|
11
|
+
remaining: 1000,
|
|
12
|
+
total: 1000,
|
|
13
|
+
resetAfter: 1000 * 60 * 60 * 24
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const gateway = {
|
|
17
|
+
events: options.events ?? {},
|
|
18
|
+
compress: options.compress ?? false,
|
|
19
|
+
transportCompression: options.transportCompression ?? null,
|
|
20
|
+
intents: options.intents ?? 0,
|
|
21
|
+
properties: {
|
|
22
|
+
os: options.properties?.os ?? process.platform,
|
|
23
|
+
browser: options.properties?.browser ?? 'Discordeno',
|
|
24
|
+
device: options.properties?.device ?? 'Discordeno'
|
|
25
|
+
},
|
|
26
|
+
token: options.token,
|
|
27
|
+
url: options.url ?? connectionOptions.url ?? 'wss://gateway.discord.gg',
|
|
28
|
+
version: options.version ?? 10,
|
|
29
|
+
connection: connectionOptions,
|
|
30
|
+
totalShards: options.totalShards ?? connectionOptions.shards ?? 1,
|
|
31
|
+
lastShardId: options.lastShardId ?? (options.totalShards ? options.totalShards - 1 : connectionOptions ? connectionOptions.shards - 1 : 0),
|
|
32
|
+
firstShardId: options.firstShardId ?? 0,
|
|
33
|
+
totalWorkers: options.totalWorkers ?? 4,
|
|
34
|
+
shardsPerWorker: options.shardsPerWorker ?? 25,
|
|
35
|
+
spawnShardDelay: options.spawnShardDelay ?? 5300,
|
|
36
|
+
preferSnakeCase: options.preferSnakeCase ?? false,
|
|
37
|
+
shards: new Map(),
|
|
38
|
+
buckets: new Map(),
|
|
39
|
+
cache: {
|
|
40
|
+
requestMembers: {
|
|
41
|
+
enabled: options.cache?.requestMembers?.enabled ?? false,
|
|
42
|
+
pending: new Collection()
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
logger: options.logger ?? logger,
|
|
46
|
+
makePresence: options.makePresence ?? (()=>Promise.resolve(undefined)),
|
|
47
|
+
resharding: {
|
|
48
|
+
enabled: options.resharding?.enabled ?? true,
|
|
49
|
+
shardsFullPercentage: options.resharding?.shardsFullPercentage ?? 80,
|
|
50
|
+
checkInterval: options.resharding?.checkInterval ?? 28800000,
|
|
51
|
+
shards: new Collection(),
|
|
52
|
+
pendingShards: new Collection(),
|
|
53
|
+
getSessionInfo: options.resharding?.getSessionInfo,
|
|
54
|
+
updateGuildsShardId: options.resharding?.updateGuildsShardId,
|
|
55
|
+
async checkIfReshardingIsNeeded () {
|
|
56
|
+
gateway.logger.debug('[Resharding] Checking if resharding is needed.');
|
|
57
|
+
if (!gateway.resharding.enabled) {
|
|
58
|
+
gateway.logger.debug('[Resharding] Resharding is disabled.');
|
|
59
|
+
return {
|
|
60
|
+
needed: false
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (!gateway.resharding.getSessionInfo) {
|
|
64
|
+
throw new Error("[Resharding] Resharding is enabled but no 'resharding.getSessionInfo()' is not provided.");
|
|
65
|
+
}
|
|
66
|
+
gateway.logger.debug('[Resharding] Resharding is enabled.');
|
|
67
|
+
const sessionInfo = await gateway.resharding.getSessionInfo();
|
|
68
|
+
gateway.logger.debug(`[Resharding] Session info retrieved: ${JSON.stringify(sessionInfo)}`);
|
|
69
|
+
// Don't have enough identify limits to try resharding
|
|
70
|
+
if (sessionInfo.sessionStartLimit.remaining < sessionInfo.shards) {
|
|
71
|
+
gateway.logger.debug('[Resharding] Not enough session start limits left to reshard.');
|
|
72
|
+
return {
|
|
73
|
+
needed: false,
|
|
74
|
+
info: sessionInfo
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
gateway.logger.debug('[Resharding] Able to reshard, checking whether necessary now.');
|
|
78
|
+
// 2500 is the max amount of guilds a single shard can handle
|
|
79
|
+
// 1000 is the amount of guilds discord uses to determine how many shards to recommend.
|
|
80
|
+
// This algo helps check if your bot has grown enough to reshard.
|
|
81
|
+
// While this is imprecise as discord changes the recommended number of shard every 1000 guilds it is good enough
|
|
82
|
+
// The alternative is to store the guild count for each shard and require the Guilds intent for `GUILD_CREATE` and `GUILD_DELETE` events
|
|
83
|
+
const percentage = sessionInfo.shards / (gateway.totalShards * 2500 / 1000) * 100;
|
|
84
|
+
// Less than necessary% being used so do nothing
|
|
85
|
+
if (percentage < gateway.resharding.shardsFullPercentage) {
|
|
86
|
+
gateway.logger.debug('[Resharding] Resharding not needed.');
|
|
87
|
+
return {
|
|
88
|
+
needed: false,
|
|
89
|
+
info: sessionInfo
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
gateway.logger.info('[Resharding] Resharding is needed.');
|
|
93
|
+
return {
|
|
94
|
+
needed: true,
|
|
95
|
+
info: sessionInfo
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
async reshard (info) {
|
|
99
|
+
gateway.logger.info(`[Resharding] Starting the reshard process. Previous total shards: ${gateway.totalShards}`);
|
|
100
|
+
// Set values on gateway
|
|
101
|
+
gateway.totalShards = info.shards;
|
|
102
|
+
// Handles preparing mid sized bots for LBS
|
|
103
|
+
gateway.totalShards = gateway.calculateTotalShards();
|
|
104
|
+
// Set first shard id if provided in info
|
|
105
|
+
if (typeof info.firstShardId === 'number') gateway.firstShardId = info.firstShardId;
|
|
106
|
+
// Set last shard id if provided in info
|
|
107
|
+
if (typeof info.lastShardId === 'number') gateway.lastShardId = info.lastShardId;
|
|
108
|
+
gateway.logger.info(`[Resharding] Starting the reshard process. New total shards: ${gateway.totalShards}`);
|
|
109
|
+
// Resetting buckets
|
|
110
|
+
gateway.buckets.clear();
|
|
111
|
+
// Refilling buckets with new values
|
|
112
|
+
gateway.prepareBuckets();
|
|
113
|
+
// SPREAD THIS OUT TO DIFFERENT WORKERS TO BEGIN STARTING UP
|
|
114
|
+
gateway.buckets.forEach(async (bucket, bucketId)=>{
|
|
115
|
+
for (const worker of bucket.workers){
|
|
116
|
+
for (const shardId of worker.queue){
|
|
117
|
+
await gateway.resharding.tellWorkerToPrepare(worker.id, shardId, bucketId);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
async tellWorkerToPrepare (workerId, shardId, bucketId) {
|
|
123
|
+
gateway.logger.debug(`[Resharding] Telling worker to prepare. Worker: ${workerId} | Shard: ${shardId} | Bucket: ${bucketId}.`);
|
|
124
|
+
const shard = new Shard({
|
|
125
|
+
id: shardId,
|
|
126
|
+
connection: {
|
|
127
|
+
compress: gateway.compress,
|
|
128
|
+
transportCompression: gateway.transportCompression ?? null,
|
|
129
|
+
intents: gateway.intents,
|
|
130
|
+
properties: gateway.properties,
|
|
131
|
+
token: gateway.token,
|
|
132
|
+
totalShards: gateway.totalShards,
|
|
133
|
+
url: gateway.url,
|
|
134
|
+
version: gateway.version
|
|
135
|
+
},
|
|
136
|
+
// Ignore events until we are ready
|
|
137
|
+
events: {
|
|
138
|
+
async message (_shard, payload) {
|
|
139
|
+
if (payload.t === 'READY') {
|
|
140
|
+
await gateway.resharding.updateGuildsShardId?.(payload.d.guilds.map((g)=>g.id), shardId);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
logger: gateway.logger,
|
|
145
|
+
requestIdentify: async ()=>{
|
|
146
|
+
await gateway.identify(shardId);
|
|
147
|
+
},
|
|
148
|
+
shardIsReady: async ()=>{
|
|
149
|
+
gateway.logger.debug(`[Shard] Shard #${shardId} is ready`);
|
|
150
|
+
await delay(gateway.spawnShardDelay);
|
|
151
|
+
gateway.logger.debug(`[Shard] Resolving shard identify request`);
|
|
152
|
+
gateway.buckets.get(shardId % gateway.connection.sessionStartLimit.maxConcurrency).identifyRequests.shift()?.();
|
|
153
|
+
},
|
|
154
|
+
makePresence: gateway.makePresence
|
|
155
|
+
});
|
|
156
|
+
if (gateway.preferSnakeCase) {
|
|
157
|
+
shard.forwardToBot = async (payload)=>{
|
|
158
|
+
shard.events?.message?.(shard, payload);
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
gateway.resharding.shards.set(shardId, shard);
|
|
162
|
+
const bucket = gateway.buckets.get(shardId % gateway.connection.sessionStartLimit.maxConcurrency);
|
|
163
|
+
if (!bucket) return;
|
|
164
|
+
return await new Promise((resolve)=>{
|
|
165
|
+
// Mark that we are making an identify request so another is not made.
|
|
166
|
+
bucket.identifyRequests.push(resolve);
|
|
167
|
+
gateway.logger.debug(`[Gateway] Identifying Shard #${shardId}.`);
|
|
168
|
+
// This will trigger identify and when READY is received it will resolve the above request.
|
|
169
|
+
shard?.identify().then(async ()=>{
|
|
170
|
+
// Tell the manager that this shard is online
|
|
171
|
+
return await gateway.resharding.shardIsPending(shard);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
async shardIsPending (shard) {
|
|
176
|
+
// Save this in pending at the moment, until all shards are online
|
|
177
|
+
gateway.resharding.pendingShards.set(shard.id, shard);
|
|
178
|
+
gateway.logger.debug(`[Resharding] Shard #${shard.id} is now pending.`);
|
|
179
|
+
// Check if all shards are now online.
|
|
180
|
+
if (gateway.lastShardId - gateway.firstShardId >= gateway.resharding.pendingShards.size) return;
|
|
181
|
+
gateway.logger.info(`[Resharding] All shards are now online.`);
|
|
182
|
+
// New shards start processing events
|
|
183
|
+
for (const shard of gateway.resharding.shards.values()){
|
|
184
|
+
for(const event in options.events){
|
|
185
|
+
shard.events[event] = options.events[event];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Old shards stop processing events
|
|
189
|
+
for (const shard of gateway.shards.values()){
|
|
190
|
+
const oldHandler = shard.events.message;
|
|
191
|
+
// Change with spread operator to not affect new shards, as changing anything on shard.events will directly change options.events, which changes new shards' events
|
|
192
|
+
shard.events = {
|
|
193
|
+
...shard.events,
|
|
194
|
+
message: async function(_, message) {
|
|
195
|
+
// Member checks need to continue but others can stop
|
|
196
|
+
if (message.t === 'GUILD_MEMBERS_CHUNK') {
|
|
197
|
+
oldHandler?.(shard, message);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
gateway.logger.info(`[Resharding] Shutting down old shards.`);
|
|
203
|
+
// Close old shards
|
|
204
|
+
await gateway.shutdown(ShardSocketCloseCodes.Resharded, 'Resharded!', false);
|
|
205
|
+
gateway.logger.info(`[Resharding] Completed.`);
|
|
206
|
+
// Replace old shards
|
|
207
|
+
gateway.shards = new Collection(gateway.resharding.shards);
|
|
208
|
+
// Clear our collections and keep only one reference to the shards, the one in gateway.shards
|
|
209
|
+
gateway.resharding.shards.clear();
|
|
210
|
+
gateway.resharding.pendingShards.clear();
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
calculateTotalShards () {
|
|
214
|
+
// Bots under 100k servers do not have access to LBS.
|
|
215
|
+
if (gateway.totalShards < 100) {
|
|
216
|
+
gateway.logger.debug(`[Gateway] Calculating total shards: ${gateway.totalShards}`);
|
|
217
|
+
return gateway.totalShards;
|
|
218
|
+
}
|
|
219
|
+
gateway.logger.debug(`[Gateway] Calculating total shards`, gateway.totalShards, gateway.connection.sessionStartLimit.maxConcurrency);
|
|
220
|
+
// Calculate a multiple of `maxConcurrency` which can be used to connect to the gateway.
|
|
221
|
+
return Math.ceil(gateway.totalShards / // If `maxConcurrency` is 1, we can safely use 16 to get `totalShards` to be in a multiple of 16 so that we can prepare bots with 100k servers for LBS.
|
|
222
|
+
(gateway.connection.sessionStartLimit.maxConcurrency === 1 ? 16 : gateway.connection.sessionStartLimit.maxConcurrency)) * (gateway.connection.sessionStartLimit.maxConcurrency === 1 ? 16 : gateway.connection.sessionStartLimit.maxConcurrency);
|
|
223
|
+
},
|
|
224
|
+
calculateWorkerId (shardId) {
|
|
225
|
+
const workerId = Math.min(shardId % gateway.shardsPerWorker, gateway.totalWorkers - 1);
|
|
226
|
+
gateway.logger.debug(`[Gateway] Calculating workerId: Shard: ${shardId} -> Worker: ${workerId} -> Per Worker: ${gateway.shardsPerWorker} -> Total: ${gateway.totalWorkers}`);
|
|
227
|
+
return workerId;
|
|
228
|
+
},
|
|
229
|
+
prepareBuckets () {
|
|
230
|
+
for(let i = 0; i < gateway.connection.sessionStartLimit.maxConcurrency; ++i){
|
|
231
|
+
gateway.logger.debug(`[Gateway] Preparing buckets for concurrency: ${i}`);
|
|
232
|
+
gateway.buckets.set(i, {
|
|
233
|
+
workers: [],
|
|
234
|
+
identifyRequests: []
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
// ORGANIZE ALL SHARDS INTO THEIR OWN BUCKETS
|
|
238
|
+
for(let shardId = gateway.firstShardId; shardId <= gateway.lastShardId; ++shardId){
|
|
239
|
+
gateway.logger.debug(`[Gateway] Preparing buckets for shard: ${shardId}`);
|
|
240
|
+
if (shardId >= gateway.totalShards) {
|
|
241
|
+
throw new Error(`Shard (id: ${shardId}) is bigger or equal to the used amount of used shards which is ${gateway.totalShards}`);
|
|
242
|
+
}
|
|
243
|
+
const bucketId = shardId % gateway.connection.sessionStartLimit.maxConcurrency;
|
|
244
|
+
const bucket = gateway.buckets.get(bucketId);
|
|
245
|
+
if (!bucket) {
|
|
246
|
+
throw new Error(`Shard (id: ${shardId}) got assigned to an illegal bucket id: ${bucketId}, expected a bucket id between 0 and ${gateway.connection.sessionStartLimit.maxConcurrency - 1}`);
|
|
247
|
+
}
|
|
248
|
+
// FIND A QUEUE IN THIS BUCKET THAT HAS SPACE
|
|
249
|
+
// const worker = bucket.workers.find((w) => w.queue.length < gateway.shardsPerWorker);
|
|
250
|
+
const workerId = gateway.calculateWorkerId(shardId);
|
|
251
|
+
const worker = bucket.workers.find((w)=>w.id === workerId);
|
|
252
|
+
if (worker) {
|
|
253
|
+
// IF THE QUEUE HAS SPACE JUST ADD IT TO THIS QUEUE
|
|
254
|
+
worker.queue.push(shardId);
|
|
255
|
+
} else {
|
|
256
|
+
bucket.workers.push({
|
|
257
|
+
id: workerId,
|
|
258
|
+
queue: [
|
|
259
|
+
shardId
|
|
260
|
+
]
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
for (const bucket of gateway.buckets.values()){
|
|
265
|
+
for (const worker of bucket.workers.values()){
|
|
266
|
+
worker.queue = worker.queue.sort((a, b)=>a - b);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
async spawnShards () {
|
|
271
|
+
// PREPARES ALL SHARDS IN SPECIFIC BUCKETS
|
|
272
|
+
gateway.prepareBuckets();
|
|
273
|
+
// Prefer concurrency of forEach instead of forof
|
|
274
|
+
await Promise.all([
|
|
275
|
+
...gateway.buckets.entries()
|
|
276
|
+
].map(async ([bucketId, bucket])=>{
|
|
277
|
+
for (const worker of bucket.workers){
|
|
278
|
+
for (const shardId of worker.queue){
|
|
279
|
+
await gateway.tellWorkerToIdentify(worker.id, shardId, bucketId);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}));
|
|
283
|
+
// Check and reshard automatically if auto resharding is enabled.
|
|
284
|
+
if (gateway.resharding.enabled && gateway.resharding.checkInterval !== -1) {
|
|
285
|
+
// It is better to ensure there is always only one
|
|
286
|
+
clearInterval(gateway.resharding.checkIntervalId);
|
|
287
|
+
if (!gateway.resharding.getSessionInfo) {
|
|
288
|
+
gateway.resharding.enabled = false;
|
|
289
|
+
gateway.logger.warn("[Resharding] Resharding is enabled but 'resharding.getSessionInfo()' was not provided. Disabling resharding.");
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
gateway.resharding.checkIntervalId = setInterval(async ()=>{
|
|
293
|
+
const reshardingInfo = await gateway.resharding.checkIfReshardingIsNeeded();
|
|
294
|
+
if (reshardingInfo.needed && reshardingInfo.info) await gateway.resharding.reshard(reshardingInfo.info);
|
|
295
|
+
}, gateway.resharding.checkInterval);
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
async shutdown (code, reason, clearReshardingInterval = true) {
|
|
299
|
+
gateway.shards.forEach((shard)=>shard.close(code, reason));
|
|
300
|
+
if (clearReshardingInterval) clearInterval(gateway.resharding.checkIntervalId);
|
|
301
|
+
await delay(5000);
|
|
302
|
+
},
|
|
303
|
+
async sendPayload (shardId, payload) {
|
|
304
|
+
const shard = gateway.shards.get(shardId);
|
|
305
|
+
if (!shard) {
|
|
306
|
+
throw new Error(`Shard (id: ${shardId} not found`);
|
|
307
|
+
}
|
|
308
|
+
await shard.send(payload);
|
|
309
|
+
},
|
|
310
|
+
async tellWorkerToIdentify (workerId, shardId, bucketId) {
|
|
311
|
+
gateway.logger.debug(`[Gateway] Tell worker to identify (${workerId}, ${shardId}, ${bucketId})`);
|
|
312
|
+
await gateway.identify(shardId);
|
|
313
|
+
},
|
|
314
|
+
async identify (shardId) {
|
|
315
|
+
let shard = this.shards.get(shardId);
|
|
316
|
+
gateway.logger.debug(`[Gateway] Identifying ${shard ? 'existing' : 'new'} shard (${shardId})`);
|
|
317
|
+
if (!shard) {
|
|
318
|
+
shard = new Shard({
|
|
319
|
+
id: shardId,
|
|
320
|
+
connection: {
|
|
321
|
+
compress: this.compress,
|
|
322
|
+
transportCompression: gateway.transportCompression,
|
|
323
|
+
intents: this.intents,
|
|
324
|
+
properties: this.properties,
|
|
325
|
+
token: this.token,
|
|
326
|
+
totalShards: this.totalShards,
|
|
327
|
+
url: this.url,
|
|
328
|
+
version: this.version
|
|
329
|
+
},
|
|
330
|
+
events: options.events ?? {},
|
|
331
|
+
logger: this.logger,
|
|
332
|
+
requestIdentify: async ()=>{
|
|
333
|
+
await gateway.identify(shardId);
|
|
334
|
+
},
|
|
335
|
+
shardIsReady: async ()=>{
|
|
336
|
+
gateway.logger.debug(`[Shard] Shard #${shardId} is ready`);
|
|
337
|
+
await delay(gateway.spawnShardDelay);
|
|
338
|
+
gateway.logger.debug(`[Shard] Resolving shard identify request`);
|
|
339
|
+
gateway.buckets.get(shardId % gateway.connection.sessionStartLimit.maxConcurrency).identifyRequests.shift()?.();
|
|
340
|
+
},
|
|
341
|
+
makePresence: gateway.makePresence
|
|
342
|
+
});
|
|
343
|
+
if (this.preferSnakeCase) {
|
|
344
|
+
shard.forwardToBot = async (payload)=>{
|
|
345
|
+
shard.events.message?.(shard, payload);
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
this.shards.set(shardId, shard);
|
|
349
|
+
}
|
|
350
|
+
const bucket = gateway.buckets.get(shardId % gateway.connection.sessionStartLimit.maxConcurrency);
|
|
351
|
+
if (!bucket) return;
|
|
352
|
+
return await new Promise((resolve)=>{
|
|
353
|
+
// Mark that we are making an identify request so another is not made.
|
|
354
|
+
bucket.identifyRequests.push(resolve);
|
|
355
|
+
gateway.logger.debug(`[Gateway] Identifying Shard #${shardId}.`);
|
|
356
|
+
// This will trigger identify and when READY is received it will resolve the above request.
|
|
357
|
+
shard?.identify();
|
|
358
|
+
});
|
|
359
|
+
},
|
|
360
|
+
async kill (shardId) {
|
|
361
|
+
const shard = this.shards.get(shardId);
|
|
362
|
+
if (!shard) {
|
|
363
|
+
return gateway.logger.debug(`[Gateway] A kill for Shard #${shardId} was requested, but the shard could not be found`);
|
|
364
|
+
}
|
|
365
|
+
gateway.logger.debug(`[Gateway] Killing Shard #${shardId}`);
|
|
366
|
+
this.shards.delete(shardId);
|
|
367
|
+
await shard.shutdown();
|
|
368
|
+
},
|
|
369
|
+
async requestIdentify (_shardId) {
|
|
370
|
+
gateway.logger.debug(`[Gateway] Requesting identify`);
|
|
371
|
+
},
|
|
372
|
+
// Helpers methods below this
|
|
373
|
+
calculateShardId (guildId, totalShards) {
|
|
374
|
+
// If none is provided, use the total shards number from gateway object.
|
|
375
|
+
if (!totalShards) totalShards = gateway.totalShards;
|
|
376
|
+
// If it is only 1 shard, it will always be shard id 0
|
|
377
|
+
if (totalShards === 1) {
|
|
378
|
+
gateway.logger.debug(`[Gateway] calculateShardId (1 shard)`);
|
|
379
|
+
return 0;
|
|
380
|
+
}
|
|
381
|
+
gateway.logger.debug(`[Gateway] calculateShardId (guildId: ${guildId}, totalShards: ${totalShards})`);
|
|
382
|
+
return Number((BigInt(guildId) >> 22n) % BigInt(totalShards));
|
|
383
|
+
},
|
|
384
|
+
async joinVoiceChannel (guildId, channelId, options) {
|
|
385
|
+
const shardId = gateway.calculateShardId(guildId);
|
|
386
|
+
gateway.logger.debug(`[Gateway] joinVoiceChannel guildId: ${guildId} channelId: ${channelId}`);
|
|
387
|
+
await gateway.sendPayload(shardId, {
|
|
388
|
+
op: GatewayOpcodes.VoiceStateUpdate,
|
|
389
|
+
d: {
|
|
390
|
+
guild_id: guildId.toString(),
|
|
391
|
+
channel_id: channelId.toString(),
|
|
392
|
+
self_mute: options?.selfMute ?? false,
|
|
393
|
+
self_deaf: options?.selfDeaf ?? true
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
},
|
|
397
|
+
async editBotStatus (data) {
|
|
398
|
+
gateway.logger.debug(`[Gateway] editBotStatus data: ${JSON.stringify(data)}`);
|
|
399
|
+
await Promise.all([
|
|
400
|
+
...gateway.shards.values()
|
|
401
|
+
].map(async (shard)=>{
|
|
402
|
+
gateway.editShardStatus(shard.id, data);
|
|
403
|
+
}));
|
|
404
|
+
},
|
|
405
|
+
async editShardStatus (shardId, data) {
|
|
406
|
+
gateway.logger.debug(`[Gateway] editShardStatus shardId: ${shardId} -> data: ${JSON.stringify(data)}`);
|
|
407
|
+
await gateway.sendPayload(shardId, {
|
|
408
|
+
op: GatewayOpcodes.PresenceUpdate,
|
|
409
|
+
d: {
|
|
410
|
+
since: null,
|
|
411
|
+
afk: false,
|
|
412
|
+
activities: data.activities,
|
|
413
|
+
status: data.status
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
},
|
|
417
|
+
async requestMembers (guildId, options) {
|
|
418
|
+
const shardId = gateway.calculateShardId(guildId);
|
|
419
|
+
if (gateway.intents && (!options?.limit || options.limit > 1) && !(gateway.intents & GatewayIntents.GuildMembers)) throw new Error('Cannot fetch more then 1 member without the GUILD_MEMBERS intent');
|
|
420
|
+
gateway.logger.debug(`[Gateway] requestMembers guildId: ${guildId} -> data: ${JSON.stringify(options)}`);
|
|
421
|
+
if (options?.userIds?.length) {
|
|
422
|
+
gateway.logger.debug(`[Gateway] requestMembers guildId: ${guildId} -> setting user limit based on userIds length: ${options.userIds.length}`);
|
|
423
|
+
options.limit = options.userIds.length;
|
|
424
|
+
}
|
|
425
|
+
const members = !gateway.cache.requestMembers.enabled || !options?.nonce ? [] : new Promise((resolve, reject)=>{
|
|
426
|
+
// Should never happen.
|
|
427
|
+
if (!gateway.cache.requestMembers.enabled || !options?.nonce) {
|
|
428
|
+
reject(new Error("Can't request the members without the nonce or with the feature disabled."));
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
gateway.cache.requestMembers.pending.set(options.nonce, {
|
|
432
|
+
nonce: options.nonce,
|
|
433
|
+
resolve,
|
|
434
|
+
members: []
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
await gateway.sendPayload(shardId, {
|
|
438
|
+
op: GatewayOpcodes.RequestGuildMembers,
|
|
439
|
+
d: {
|
|
440
|
+
guild_id: guildId.toString(),
|
|
441
|
+
// If a query is provided use it, OR if a limit is NOT provided use ""
|
|
442
|
+
query: options?.query ?? (options?.limit ? undefined : ''),
|
|
443
|
+
limit: options?.limit ?? 0,
|
|
444
|
+
presences: options?.presences ?? false,
|
|
445
|
+
user_ids: options?.userIds?.map((id)=>id.toString()),
|
|
446
|
+
nonce: options?.nonce
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
return await members;
|
|
450
|
+
},
|
|
451
|
+
async leaveVoiceChannel (guildId) {
|
|
452
|
+
const shardId = gateway.calculateShardId(guildId);
|
|
453
|
+
gateway.logger.debug(`[Gateway] leaveVoiceChannel guildId: ${guildId} Shard ${shardId}`);
|
|
454
|
+
await gateway.sendPayload(shardId, {
|
|
455
|
+
op: GatewayOpcodes.VoiceStateUpdate,
|
|
456
|
+
d: {
|
|
457
|
+
guild_id: guildId.toString(),
|
|
458
|
+
channel_id: null,
|
|
459
|
+
self_mute: false,
|
|
460
|
+
self_deaf: false
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
},
|
|
464
|
+
async requestSoundboardSounds (guildIds) {
|
|
465
|
+
/**
|
|
466
|
+
* Discord will send the events for the guilds that are "under the shard" that sends the opcode.
|
|
467
|
+
* For this reason we need to group the ids with the shard the calculateShardId method gives
|
|
468
|
+
*/ const map = new Map();
|
|
469
|
+
for (const guildId of guildIds){
|
|
470
|
+
const shardId = gateway.calculateShardId(guildId);
|
|
471
|
+
const ids = map.get(shardId) ?? [];
|
|
472
|
+
map.set(shardId, ids);
|
|
473
|
+
ids.push(guildId);
|
|
474
|
+
}
|
|
475
|
+
await Promise.all([
|
|
476
|
+
...map.entries()
|
|
477
|
+
].map(([shardId, ids])=>gateway.sendPayload(shardId, {
|
|
478
|
+
op: GatewayOpcodes.RequestSoundboardSounds,
|
|
479
|
+
d: {
|
|
480
|
+
guild_ids: ids
|
|
481
|
+
}
|
|
482
|
+
})));
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
return gateway;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9tYW5hZ2VyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIHR5cGUgQXRMZWFzdE9uZSxcbiAgdHlwZSBCaWdTdHJpbmcsXG4gIHR5cGUgQ2FtZWxpemUsXG4gIHR5cGUgRGlzY29yZEdldEdhdGV3YXlCb3QsXG4gIHR5cGUgRGlzY29yZE1lbWJlcldpdGhVc2VyLFxuICB0eXBlIERpc2NvcmRSZWFkeSxcbiAgR2F0ZXdheUludGVudHMsXG4gIEdhdGV3YXlPcGNvZGVzLFxuICB0eXBlIFJlcXVlc3RHdWlsZE1lbWJlcnMsXG59IGZyb20gJ0BkaXNjb3JkZW5vL3R5cGVzJ1xuaW1wb3J0IHsgQ29sbGVjdGlvbiwgZGVsYXksIGxvZ2dlciB9IGZyb20gJ0BkaXNjb3JkZW5vL3V0aWxzJ1xuaW1wb3J0IFNoYXJkIGZyb20gJy4vU2hhcmQuanMnXG5pbXBvcnQge1xuICB0eXBlIEJvdFN0YXR1c1VwZGF0ZSxcbiAgdHlwZSBTaGFyZEV2ZW50cyxcbiAgU2hhcmRTb2NrZXRDbG9zZUNvZGVzLFxuICB0eXBlIFNoYXJkU29ja2V0UmVxdWVzdCxcbiAgdHlwZSBTdGF0dXNVcGRhdGUsXG4gIHR5cGUgVHJhbnNwb3J0Q29tcHJlc3Npb24sXG4gIHR5cGUgVXBkYXRlVm9pY2VTdGF0ZSxcbn0gZnJvbSAnLi90eXBlcy5qcydcblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUdhdGV3YXlNYW5hZ2VyKG9wdGlvbnM6IENyZWF0ZUdhdGV3YXlNYW5hZ2VyT3B0aW9ucyk6IEdhdGV3YXlNYW5hZ2VyIHtcbiAgY29uc3QgY29ubmVjdGlvbk9wdGlvbnMgPSBvcHRpb25zLmNvbm5lY3Rpb24gPz8ge1xuICAgIHVybDogJ3dzczovL2dhdGV3YXkuZGlzY29yZC5nZycsXG4gICAgc2hhcmRzOiAxLFxuICAgIHNlc3Npb25TdGFydExpbWl0OiB7XG4gICAgICBtYXhDb25jdXJyZW5jeTogMSxcbiAgICAgIHJlbWFpbmluZzogMTAwMCxcbiAgICAgIHRvdGFsOiAxMDAwLFxuICAgICAgcmVzZXRBZnRlcjogMTAwMCAqIDYwICogNjAgKiAyNCxcbiAgICB9LFxuICB9XG5cbiAgY29uc3QgZ2F0ZXdheTogR2F0ZXdheU1hbmFnZXIgPSB7XG4gICAgZXZlbnRzOiBvcHRpb25zLmV2ZW50cyA/PyB7fSxcbiAgICBjb21wcmVzczogb3B0aW9ucy5jb21wcmVzcyA/PyBmYWxzZSxcbiAgICB0cmFuc3BvcnRDb21wcmVzc2lvbjogb3B0aW9ucy50cmFuc3BvcnRDb21wcmVzc2lvbiA/PyBudWxsLFxuICAgIGludGVudHM6IG9wdGlvbnMuaW50ZW50cyA/PyAwLFxuICAgIHByb3BlcnRpZXM6IHtcbiAgICAgIG9zOiBvcHRpb25zLnByb3BlcnRpZXM/Lm9zID8/IHByb2Nlc3MucGxhdGZvcm0sXG4gICAgICBicm93c2VyOiBvcHRpb25zLnByb3BlcnRpZXM/LmJyb3dzZXIgPz8gJ0Rpc2NvcmRlbm8nLFxuICAgICAgZGV2aWNlOiBvcHRpb25zLnByb3BlcnRpZXM/LmRldmljZSA/PyAnRGlzY29yZGVubycsXG4gICAgfSxcbiAgICB0b2tlbjogb3B0aW9ucy50b2tlbixcbiAgICB1cmw6IG9wdGlvbnMudXJsID8/IGNvbm5lY3Rpb25PcHRpb25zLnVybCA/PyAnd3NzOi8vZ2F0ZXdheS5kaXNjb3JkLmdnJyxcbiAgICB2ZXJzaW9uOiBvcHRpb25zLnZlcnNpb24gPz8gMTAsXG4gICAgY29ubmVjdGlvbjogY29ubmVjdGlvbk9wdGlvbnMsXG4gICAgdG90YWxTaGFyZHM6IG9wdGlvbnMudG90YWxTaGFyZHMgPz8gY29ubmVjdGlvbk9wdGlvbnMuc2hhcmRzID8/IDEsXG4gICAgbGFzdFNoYXJkSWQ6IG9wdGlvbnMubGFzdFNoYXJkSWQgPz8gKG9wdGlvbnMudG90YWxTaGFyZHMgPyBvcHRpb25zLnRvdGFsU2hhcmRzIC0gMSA6IGNvbm5lY3Rpb25PcHRpb25zID8gY29ubmVjdGlvbk9wdGlvbnMuc2hhcmRzIC0gMSA6IDApLFxuICAgIGZpcnN0U2hhcmRJZDogb3B0aW9ucy5maXJzdFNoYXJkSWQgPz8gMCxcbiAgICB0b3RhbFdvcmtlcnM6IG9wdGlvbnMudG90YWxXb3JrZXJzID8/IDQsXG4gICAgc2hhcmRzUGVyV29ya2VyOiBvcHRpb25zLnNoYXJkc1BlcldvcmtlciA/PyAyNSxcbiAgICBzcGF3blNoYXJkRGVsYXk6IG9wdGlvbnMuc3Bhd25TaGFyZERlbGF5ID8/IDUzMDAsXG4gICAgcHJlZmVyU25ha2VDYXNlOiBvcHRpb25zLnByZWZlclNuYWtlQ2FzZSA/PyBmYWxzZSxcbiAgICBzaGFyZHM6IG5ldyBNYXAoKSxcbiAgICBidWNrZXRzOiBuZXcgTWFwKCksXG4gICAgY2FjaGU6IHtcbiAgICAgIHJlcXVlc3RNZW1iZXJzOiB7XG4gICAgICAgIGVuYWJsZWQ6IG9wdGlvbnMuY2FjaGU/LnJlcXVlc3RNZW1iZXJzPy5lbmFibGVkID8/IGZhbHNlLFxuICAgICAgICBwZW5kaW5nOiBuZXcgQ29sbGVjdGlvbigpLFxuICAgICAgfSxcbiAgICB9LFxuICAgIGxvZ2dlcjogb3B0aW9ucy5sb2dnZXIgPz8gbG9nZ2VyLFxuICAgIG1ha2VQcmVzZW5jZTogb3B0aW9ucy5tYWtlUHJlc2VuY2UgPz8gKCgpID0+IFByb21pc2UucmVzb2x2ZSh1bmRlZmluZWQpKSxcbiAgICByZXNoYXJkaW5nOiB7XG4gICAgICBlbmFibGVkOiBvcHRpb25zLnJlc2hhcmRpbmc/LmVuYWJsZWQgPz8gdHJ1ZSxcbiAgICAgIHNoYXJkc0Z1bGxQZXJjZW50YWdlOiBvcHRpb25zLnJlc2hhcmRpbmc/LnNoYXJkc0Z1bGxQZXJjZW50YWdlID8/IDgwLFxuICAgICAgY2hlY2tJbnRlcnZhbDogb3B0aW9ucy5yZXNoYXJkaW5nPy5jaGVja0ludGVydmFsID8/IDI4ODAwMDAwLCAvLyA4IGhvdXJzXG4gICAgICBzaGFyZHM6IG5ldyBDb2xsZWN0aW9uKCksXG4gICAgICBwZW5kaW5nU2hhcmRzOiBuZXcgQ29sbGVjdGlvbigpLFxuICAgICAgZ2V0U2Vzc2lvbkluZm86IG9wdGlvbnMucmVzaGFyZGluZz8uZ2V0U2Vzc2lvbkluZm8sXG4gICAgICB1cGRhdGVHdWlsZHNTaGFyZElkOiBvcHRpb25zLnJlc2hhcmRpbmc/LnVwZGF0ZUd1aWxkc1NoYXJkSWQsXG4gICAgICBhc3luYyBjaGVja0lmUmVzaGFyZGluZ0lzTmVlZGVkKCkge1xuICAgICAgICBnYXRld2F5LmxvZ2dlci5kZWJ1ZygnW1Jlc2hhcmRpbmddIENoZWNraW5nIGlmIHJlc2hhcmRpbmcgaXMgbmVlZGVkLicpXG5cbiAgICAgICAgaWYgKCFnYXRld2F5LnJlc2hhcmRpbmcuZW5hYmxlZCkge1xuICAgICAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKCdbUmVzaGFyZGluZ10gUmVzaGFyZGluZyBpcyBkaXNhYmxlZC4nKVxuXG4gICAgICAgICAgcmV0dXJuIHsgbmVlZGVkOiBmYWxzZSB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIWdhdGV3YXkucmVzaGFyZGluZy5nZXRTZXNzaW9uSW5mbykge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIltSZXNoYXJkaW5nXSBSZXNoYXJkaW5nIGlzIGVuYWJsZWQgYnV0IG5vICdyZXNoYXJkaW5nLmdldFNlc3Npb25JbmZvKCknIGlzIG5vdCBwcm92aWRlZC5cIilcbiAgICAgICAgfVxuXG4gICAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKCdbUmVzaGFyZGluZ10gUmVzaGFyZGluZyBpcyBlbmFibGVkLicpXG5cbiAgICAgICAgY29uc3Qgc2Vzc2lvbkluZm8gPSBhd2FpdCBnYXRld2F5LnJlc2hhcmRpbmcuZ2V0U2Vzc2lvbkluZm8oKVxuXG4gICAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKGBbUmVzaGFyZGluZ10gU2Vzc2lvbiBpbmZvIHJldHJpZXZlZDogJHtKU09OLnN0cmluZ2lmeShzZXNzaW9uSW5mbyl9YClcblxuICAgICAgICAvLyBEb24ndCBoYXZlIGVub3VnaCBpZGVudGlmeSBsaW1pdHMgdG8gdHJ5IHJlc2hhcmRpbmdcbiAgICAgICAgaWYgKHNlc3Npb25JbmZvLnNlc3Npb25TdGFydExpbWl0LnJlbWFpbmluZyA8IHNlc3Npb25JbmZvLnNoYXJkcykge1xuICAgICAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKCdbUmVzaGFyZGluZ10gTm90IGVub3VnaCBzZXNzaW9uIHN0YXJ0IGxpbWl0cyBsZWZ0IHRvIHJlc2hhcmQuJylcblxuICAgICAgICAgIHJldHVybiB7IG5lZWRlZDogZmFsc2UsIGluZm86IHNlc3Npb25JbmZvIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKCdbUmVzaGFyZGluZ10gQWJsZSB0byByZXNoYXJkLCBjaGVja2luZyB3aGV0aGVyIG5lY2Vzc2FyeSBub3cuJylcblxuICAgICAgICAvLyAyNTAwIGlzIHRoZSBtYXggYW1vdW50IG9mIGd1aWxkcyBhIHNpbmdsZSBzaGFyZCBjYW4gaGFuZGxlXG4gICAgICAgIC8vIDEwMDAgaXMgdGhlIGFtb3VudCBvZiBndWlsZHMgZGlzY29yZCB1c2VzIHRvIGRldGVybWluZSBob3cgbWFueSBzaGFyZHMgdG8gcmVjb21tZW5kLlxuICAgICAgICAvLyBUaGlzIGFsZ28gaGVscHMgY2hlY2sgaWYgeW91ciBib3QgaGFzIGdyb3duIGVub3VnaCB0byByZXNoYXJkLlxuICAgICAgICAvLyBXaGlsZSB0aGlzIGlzIGltcHJlY2lzZSBhcyBkaXNjb3JkIGNoYW5nZXMgdGhlIHJlY29tbWVuZGVkIG51bWJlciBvZiBzaGFyZCBldmVyeSAxMDAwIGd1aWxkcyBpdCBpcyBnb29kIGVub3VnaFxuICAgICAgICAvLyBUaGUgYWx0ZXJuYXRpdmUgaXMgdG8gc3RvcmUgdGhlIGd1aWxkIGNvdW50IGZvciBlYWNoIHNoYXJkIGFuZCByZXF1aXJlIHRoZSBHdWlsZHMgaW50ZW50IGZvciBgR1VJTERfQ1JFQVRFYCBhbmQgYEdVSUxEX0RFTEVURWAgZXZlbnRzXG4gICAgICAgIGNvbnN0IHBlcmNlbnRhZ2UgPSAoc2Vzc2lvbkluZm8uc2hhcmRzIC8gKChnYXRld2F5LnRvdGFsU2hhcmRzICogMjUwMCkgLyAxMDAwKSkgKiAxMDBcblxuICAgICAgICAvLyBMZXNzIHRoYW4gbmVjZXNzYXJ5JSBiZWluZyB1c2VkIHNvIGRvIG5vdGhpbmdcbiAgICAgICAgaWYgKHBlcmNlbnRhZ2UgPCBnYXRld2F5LnJlc2hhcmRpbmcuc2hhcmRzRnVsbFBlcmNlbnRhZ2UpIHtcbiAgICAgICAgICBnYXRld2F5LmxvZ2dlci5kZWJ1ZygnW1Jlc2hhcmRpbmddIFJlc2hhcmRpbmcgbm90IG5lZWRlZC4nKVxuXG4gICAgICAgICAgcmV0dXJuIHsgbmVlZGVkOiBmYWxzZSwgaW5mbzogc2Vzc2lvbkluZm8gfVxuICAgICAgICB9XG5cbiAgICAgICAgZ2F0ZXdheS5sb2dnZXIuaW5mbygnW1Jlc2hhcmRpbmddIFJlc2hhcmRpbmcgaXMgbmVlZGVkLicpXG5cbiAgICAgICAgcmV0dXJuIHsgbmVlZGVkOiB0cnVlLCBpbmZvOiBzZXNzaW9uSW5mbyB9XG4gICAgICB9LFxuICAgICAgYXN5bmMgcmVzaGFyZChpbmZvKSB7XG4gICAgICAgIGdhdGV3YXkubG9nZ2VyLmluZm8oYFtSZXNoYXJkaW5nXSBTdGFydGluZyB0aGUgcmVzaGFyZCBwcm9jZXNzLiBQcmV2aW91cyB0b3RhbCBzaGFyZHM6ICR7Z2F0ZXdheS50b3RhbFNoYXJkc31gKVxuICAgICAgICAvLyBTZXQgdmFsdWVzIG9uIGdhdGV3YXlcbiAgICAgICAgZ2F0ZXdheS50b3RhbFNoYXJkcyA9IGluZm8uc2hhcmRzXG4gICAgICAgIC8vIEhhbmRsZXMgcHJlcGFyaW5nIG1pZCBzaXplZCBib3RzIGZvciBMQlNcbiAgICAgICAgZ2F0ZXdheS50b3RhbFNoYXJkcyA9IGdhdGV3YXkuY2FsY3VsYXRlVG90YWxTaGFyZHMoKVxuICAgICAgICAvLyBTZXQgZmlyc3Qgc2hhcmQgaWQgaWYgcHJvdmlkZWQgaW4gaW5mb1xuICAgICAgICBpZiAodHlwZW9mIGluZm8uZmlyc3RTaGFyZElkID09PSAnbnVtYmVyJykgZ2F0ZXdheS5maXJzdFNoYXJkSWQgPSBpbmZvLmZpcnN0U2hhcmRJZFxuICAgICAgICAvLyBTZXQgbGFzdCBzaGFyZCBpZCBpZiBwcm92aWRlZCBpbiBpbmZvXG4gICAgICAgIGlmICh0eXBlb2YgaW5mby5sYXN0U2hhcmRJZCA9PT0gJ251bWJlcicpIGdhdGV3YXkubGFzdFNoYXJkSWQgPSBpbmZvLmxhc3RTaGFyZElkXG4gICAgICAgIGdhdGV3YXkubG9nZ2VyLmluZm8oYFtSZXNoYXJkaW5nXSBTdGFydGluZyB0aGUgcmVzaGFyZCBwcm9jZXNzLiBOZXcgdG90YWwgc2hhcmRzOiAke2dhdGV3YXkudG90YWxTaGFyZHN9YClcblxuICAgICAgICAvLyBSZXNldHRpbmcgYnVja2V0c1xuICAgICAgICBnYXRld2F5LmJ1Y2tldHMuY2xlYXIoKVxuICAgICAgICAvLyBSZWZpbGxpbmcgYnVja2V0cyB3aXRoIG5ldyB2YWx1ZXNcbiAgICAgICAgZ2F0ZXdheS5wcmVwYXJlQnVja2V0cygpXG5cbiAgICAgICAgLy8gU1BSRUFEIFRISVMgT1VUIFRPIERJRkZFUkVOVCBXT1JLRVJTIFRPIEJFR0lOIFNUQVJUSU5HIFVQXG4gICAgICAgIGdhdGV3YXkuYnVja2V0cy5mb3JFYWNoKGFzeW5jIChidWNrZXQsIGJ1Y2tldElkKSA9PiB7XG4gICAgICAgICAgZm9yIChjb25zdCB3b3JrZXIgb2YgYnVja2V0LndvcmtlcnMpIHtcbiAgICAgICAgICAgIGZvciAoY29uc3Qgc2hhcmRJZCBvZiB3b3JrZXIucXVldWUpIHtcbiAgICAgICAgICAgICAgYXdhaXQgZ2F0ZXdheS5yZXNoYXJkaW5nLnRlbGxXb3JrZXJUb1ByZXBhcmUod29ya2VyLmlkLCBzaGFyZElkLCBidWNrZXRJZClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgICB9LFxuICAgICAgYXN5bmMgdGVsbFdvcmtlclRvUHJlcGFyZSh3b3JrZXJJZCwgc2hhcmRJZCwgYnVja2V0SWQpIHtcbiAgICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtSZXNoYXJkaW5nXSBUZWxsaW5nIHdvcmtlciB0byBwcmVwYXJlLiBXb3JrZXI6ICR7d29ya2VySWR9IHwgU2hhcmQ6ICR7c2hhcmRJZH0gfCBCdWNrZXQ6ICR7YnVja2V0SWR9LmApXG4gICAgICAgIGNvbnN0IHNoYXJkID0gbmV3IFNoYXJkKHtcbiAgICAgICAgICBpZDogc2hhcmRJZCxcbiAgICAgICAgICBjb25uZWN0aW9uOiB7XG4gICAgICAgICAgICBjb21wcmVzczogZ2F0ZXdheS5jb21wcmVzcyxcbiAgICAgICAgICAgIHRyYW5zcG9ydENvbXByZXNzaW9uOiBnYXRld2F5LnRyYW5zcG9ydENvbXByZXNzaW9uID8/IG51bGwsXG4gICAgICAgICAgICBpbnRlbnRzOiBnYXRld2F5LmludGVudHMsXG4gICAgICAgICAgICBwcm9wZXJ0aWVzOiBnYXRld2F5LnByb3BlcnRpZXMsXG4gICAgICAgICAgICB0b2tlbjogZ2F0ZXdheS50b2tlbixcbiAgICAgICAgICAgIHRvdGFsU2hhcmRzOiBnYXRld2F5LnRvdGFsU2hhcmRzLFxuICAgICAgICAgICAgdXJsOiBnYXRld2F5LnVybCxcbiAgICAgICAgICAgIHZlcnNpb246IGdhdGV3YXkudmVyc2lvbixcbiAgICAgICAgICB9LFxuICAgICAgICAgIC8vIElnbm9yZSBldmVudHMgdW50aWwgd2UgYXJlIHJlYWR5XG4gICAgICAgICAgZXZlbnRzOiB7XG4gICAgICAgICAgICBhc3luYyBtZXNzYWdlKF9zaGFyZCwgcGF5bG9hZCkge1xuICAgICAgICAgICAgICBpZiAocGF5bG9hZC50ID09PSAnUkVBRFknKSB7XG4gICAgICAgICAgICAgICAgYXdhaXQgZ2F0ZXdheS5yZXNoYXJkaW5nLnVwZGF0ZUd1aWxkc1NoYXJkSWQ/LihcbiAgICAgICAgICAgICAgICAgIChwYXlsb2FkLmQgYXMgRGlzY29yZFJlYWR5KS5ndWlsZHMubWFwKChnKSA9PiBnLmlkKSxcbiAgICAgICAgICAgICAgICAgIHNoYXJkSWQsXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0sXG4gICAgICAgICAgbG9nZ2VyOiBnYXRld2F5LmxvZ2dlcixcbiAgICAgICAgICByZXF1ZXN0SWRlbnRpZnk6IGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgIGF3YWl0IGdhdGV3YXkuaWRlbnRpZnkoc2hhcmRJZClcbiAgICAgICAgICB9LFxuICAgICAgICAgIHNoYXJkSXNSZWFkeTogYXN5bmMgKCkgPT4ge1xuICAgICAgICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtTaGFyZF0gU2hhcmQgIyR7c2hhcmRJZH0gaXMgcmVhZHlgKVxuICAgICAgICAgICAgYXdhaXQgZGVsYXkoZ2F0ZXdheS5zcGF3blNoYXJkRGVsYXkpXG4gICAgICAgICAgICBnYXRld2F5LmxvZ2dlci5kZWJ1ZyhgW1NoYXJkXSBSZXNvbHZpbmcgc2hhcmQgaWRlbnRpZnkgcmVxdWVzdGApXG4gICAgICAgICAgICBnYXRld2F5LmJ1Y2tldHMuZ2V0KHNoYXJkSWQgJSBnYXRld2F5LmNvbm5lY3Rpb24uc2Vzc2lvblN0YXJ0TGltaXQubWF4Q29uY3VycmVuY3kpIS5pZGVudGlmeVJlcXVlc3RzLnNoaWZ0KCk/LigpXG4gICAgICAgICAgfSxcbiAgICAgICAgICBtYWtlUHJlc2VuY2U6IGdhdGV3YXkubWFrZVByZXNlbmNlLFxuICAgICAgICB9KVxuXG4gICAgICAgIGlmIChnYXRld2F5LnByZWZlclNuYWtlQ2FzZSkge1xuICAgICAgICAgIHNoYXJkLmZvcndhcmRUb0JvdCA9IGFzeW5jIChwYXlsb2FkKSA9PiB7XG4gICAgICAgICAgICBzaGFyZC5ldmVudHM/Lm1lc3NhZ2U/LihzaGFyZCwgcGF5bG9hZClcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBnYXRld2F5LnJlc2hhcmRpbmcuc2hhcmRzLnNldChzaGFyZElkLCBzaGFyZClcblxuICAgICAgICBjb25zdCBidWNrZXQgPSBnYXRld2F5LmJ1Y2tldHMuZ2V0KHNoYXJkSWQgJSBnYXRld2F5LmNvbm5lY3Rpb24uc2Vzc2lvblN0YXJ0TGltaXQubWF4Q29uY3VycmVuY3kpXG4gICAgICAgIGlmICghYnVja2V0KSByZXR1cm5cblxuICAgICAgICByZXR1cm4gYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAvLyBNYXJrIHRoYXQgd2UgYXJlIG1ha2luZyBhbiBpZGVudGlmeSByZXF1ZXN0IHNvIGFub3RoZXIgaXMgbm90IG1hZGUuXG4gICAgICAgICAgYnVja2V0LmlkZW50aWZ5UmVxdWVzdHMucHVzaChyZXNvbHZlKVxuICAgICAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKGBbR2F0ZXdheV0gSWRlbnRpZnlpbmcgU2hhcmQgIyR7c2hhcmRJZH0uYClcbiAgICAgICAgICAvLyBUaGlzIHdpbGwgdHJpZ2dlciBpZGVudGlmeSBhbmQgd2hlbiBSRUFEWSBpcyByZWNlaXZlZCBpdCB3aWxsIHJlc29sdmUgdGhlIGFib3ZlIHJlcXVlc3QuXG4gICAgICAgICAgc2hhcmQ/LmlkZW50aWZ5KCkudGhlbihhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAvLyBUZWxsIHRoZSBtYW5hZ2VyIHRoYXQgdGhpcyBzaGFyZCBpcyBvbmxpbmVcbiAgICAgICAgICAgIHJldHVybiBhd2FpdCBnYXRld2F5LnJlc2hhcmRpbmcuc2hhcmRJc1BlbmRpbmcoc2hhcmQpXG4gICAgICAgICAgfSlcbiAgICAgICAgfSlcbiAgICAgIH0sXG4gICAgICBhc3luYyBzaGFyZElzUGVuZGluZyhzaGFyZCkge1xuICAgICAgICAvLyBTYXZlIHRoaXMgaW4gcGVuZGluZyBhdCB0aGUgbW9tZW50LCB1bnRpbCBhbGwgc2hhcmRzIGFyZSBvbmxpbmVcbiAgICAgICAgZ2F0ZXdheS5yZXNoYXJkaW5nLnBlbmRpbmdTaGFyZHMuc2V0KHNoYXJkLmlkLCBzaGFyZClcbiAgICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtSZXNoYXJkaW5nXSBTaGFyZCAjJHtzaGFyZC5pZH0gaXMgbm93IHBlbmRpbmcuYClcblxuICAgICAgICAvLyBDaGVjayBpZiBhbGwgc2hhcmRzIGFyZSBub3cgb25saW5lLlxuICAgICAgICBpZiAoZ2F0ZXdheS5sYXN0U2hhcmRJZCAtIGdhdGV3YXkuZmlyc3RTaGFyZElkID49IGdhdGV3YXkucmVzaGFyZGluZy5wZW5kaW5nU2hhcmRzLnNpemUpIHJldHVyblxuXG4gICAgICAgIGdhdGV3YXkubG9nZ2VyLmluZm8oYFtSZXNoYXJkaW5nXSBBbGwgc2hhcmRzIGFyZSBub3cgb25saW5lLmApXG5cbiAgICAgICAgLy8gTmV3IHNoYXJkcyBzdGFydCBwcm9jZXNzaW5nIGV2ZW50c1xuICAgICAgICBmb3IgKGNvbnN0IHNoYXJkIG9mIGdhdGV3YXkucmVzaGFyZGluZy5zaGFyZHMudmFsdWVzKCkpIHtcbiAgICAgICAgICBmb3IgKGNvbnN0IGV2ZW50IGluIG9wdGlvbnMuZXZlbnRzKSB7XG4gICAgICAgICAgICBzaGFyZC5ldmVudHNbZXZlbnQgYXMga2V5b2YgU2hhcmRFdmVudHNdID0gb3B0aW9ucy5ldmVudHNbZXZlbnQgYXMga2V5b2YgU2hhcmRFdmVudHNdIGFzICguLi5hcmdzOiB1bmtub3duW10pID0+IHVua25vd25cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBPbGQgc2hhcmRzIHN0b3AgcHJvY2Vzc2luZyBldmVudHNcbiAgICAgICAgZm9yIChjb25zdCBzaGFyZCBvZiBnYXRld2F5LnNoYXJkcy52YWx1ZXMoKSkge1xuICAgICAgICAgIGNvbnN0IG9sZEhhbmRsZXIgPSBzaGFyZC5ldmVudHMubWVzc2FnZVxuXG4gICAgICAgICAgLy8gQ2hhbmdlIHdpdGggc3ByZWFkIG9wZXJhdG9yIHRvIG5vdCBhZmZlY3QgbmV3IHNoYXJkcywgYXMgY2hhbmdpbmcgYW55dGhpbmcgb24gc2hhcmQuZXZlbnRzIHdpbGwgZGlyZWN0bHkgY2hhbmdlIG9wdGlvbnMuZXZlbnRzLCB3aGljaCBjaGFuZ2VzIG5ldyBzaGFyZHMnIGV2ZW50c1xuICAgICAgICAgIHNoYXJkLmV2ZW50cyA9IHtcbiAgICAgICAgICAgIC4uLnNoYXJkLmV2ZW50cyxcbiAgICAgICAgICAgIG1lc3NhZ2U6IGFzeW5jIGZ1bmN0aW9uIChfLCBtZXNzYWdlKSB7XG4gICAgICAgICAgICAgIC8vIE1lbWJlciBjaGVja3MgbmVlZCB0byBjb250aW51ZSBidXQgb3RoZXJzIGNhbiBzdG9wXG4gICAgICAgICAgICAgIGlmIChtZXNzYWdlLnQgPT09ICdHVUlMRF9NRU1CRVJTX0NIVU5LJykge1xuICAgICAgICAgICAgICAgIG9sZEhhbmRsZXI/LihzaGFyZCwgbWVzc2FnZSlcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBnYXRld2F5LmxvZ2dlci5pbmZvKGBbUmVzaGFyZGluZ10gU2h1dHRpbmcgZG93biBvbGQgc2hhcmRzLmApXG4gICAgICAgIC8vIENsb3NlIG9sZCBzaGFyZHNcbiAgICAgICAgYXdhaXQgZ2F0ZXdheS5zaHV0ZG93bihTaGFyZFNvY2tldENsb3NlQ29kZXMuUmVzaGFyZGVkLCAnUmVzaGFyZGVkIScsIGZhbHNlKVxuXG4gICAgICAgIGdhdGV3YXkubG9nZ2VyLmluZm8oYFtSZXNoYXJkaW5nXSBDb21wbGV0ZWQuYClcblxuICAgICAgICAvLyBSZXBsYWNlIG9sZCBzaGFyZHNcbiAgICAgICAgZ2F0ZXdheS5zaGFyZHMgPSBuZXcgQ29sbGVjdGlvbihnYXRld2F5LnJlc2hhcmRpbmcuc2hhcmRzKVxuXG4gICAgICAgIC8vIENsZWFyIG91ciBjb2xsZWN0aW9ucyBhbmQga2VlcCBvbmx5IG9uZSByZWZlcmVuY2UgdG8gdGhlIHNoYXJkcywgdGhlIG9uZSBpbiBnYXRld2F5LnNoYXJkc1xuICAgICAgICBnYXRld2F5LnJlc2hhcmRpbmcuc2hhcmRzLmNsZWFyKClcbiAgICAgICAgZ2F0ZXdheS5yZXNoYXJkaW5nLnBlbmRpbmdTaGFyZHMuY2xlYXIoKVxuICAgICAgfSxcbiAgICB9LFxuXG4gICAgY2FsY3VsYXRlVG90YWxTaGFyZHMoKSB7XG4gICAgICAvLyBCb3RzIHVuZGVyIDEwMGsgc2VydmVycyBkbyBub3QgaGF2ZSBhY2Nlc3MgdG8gTEJTLlxuICAgICAgaWYgKGdhdGV3YXkudG90YWxTaGFyZHMgPCAxMDApIHtcbiAgICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtHYXRld2F5XSBDYWxjdWxhdGluZyB0b3RhbCBzaGFyZHM6ICR7Z2F0ZXdheS50b3RhbFNoYXJkc31gKVxuICAgICAgICByZXR1cm4gZ2F0ZXdheS50b3RhbFNoYXJkc1xuICAgICAgfVxuXG4gICAgICBnYXRld2F5LmxvZ2dlci5kZWJ1ZyhgW0dhdGV3YXldIENhbGN1bGF0aW5nIHRvdGFsIHNoYXJkc2AsIGdhdGV3YXkudG90YWxTaGFyZHMsIGdhdGV3YXkuY29ubmVjdGlvbi5zZXNzaW9uU3RhcnRMaW1pdC5tYXhDb25jdXJyZW5jeSlcbiAgICAgIC8vIENhbGN1bGF0ZSBhIG11bHRpcGxlIG9mIGBtYXhDb25jdXJyZW5jeWAgd2hpY2ggY2FuIGJlIHVzZWQgdG8gY29ubmVjdCB0byB0aGUgZ2F0ZXdheS5cbiAgICAgIHJldHVybiAoXG4gICAgICAgIE1hdGguY2VpbChcbiAgICAgICAgICBnYXRld2F5LnRvdGFsU2hhcmRzIC9cbiAgICAgICAgICAgIC8vIElmIGBtYXhDb25jdXJyZW5jeWAgaXMgMSwgd2UgY2FuIHNhZmVseSB1c2UgMTYgdG8gZ2V0IGB0b3RhbFNoYXJkc2AgdG8gYmUgaW4gYSBtdWx0aXBsZSBvZiAxNiBzbyB0aGF0IHdlIGNhbiBwcmVwYXJlIGJvdHMgd2l0aCAxMDBrIHNlcnZlcnMgZm9yIExCUy5cbiAgICAgICAgICAgIChnYXRld2F5LmNvbm5lY3Rpb24uc2Vzc2lvblN0YXJ0TGltaXQubWF4Q29uY3VycmVuY3kgPT09IDEgPyAxNiA6IGdhdGV3YXkuY29ubmVjdGlvbi5zZXNzaW9uU3RhcnRMaW1pdC5tYXhDb25jdXJyZW5jeSksXG4gICAgICAgICkgKiAoZ2F0ZXdheS5jb25uZWN0aW9uLnNlc3Npb25TdGFydExpbWl0Lm1heENvbmN1cnJlbmN5ID09PSAxID8gMTYgOiBnYXRld2F5LmNvbm5lY3Rpb24uc2Vzc2lvblN0YXJ0TGltaXQubWF4Q29uY3VycmVuY3kpXG4gICAgICApXG4gICAgfSxcbiAgICBjYWxjdWxhdGVXb3JrZXJJZChzaGFyZElkKSB7XG4gICAgICBjb25zdCB3b3JrZXJJZCA9IE1hdGgubWluKHNoYXJkSWQgJSBnYXRld2F5LnNoYXJkc1BlcldvcmtlciwgZ2F0ZXdheS50b3RhbFdvcmtlcnMgLSAxKVxuICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoXG4gICAgICAgIGBbR2F0ZXdheV0gQ2FsY3VsYXRpbmcgd29ya2VySWQ6IFNoYXJkOiAke3NoYXJkSWR9IC0+IFdvcmtlcjogJHt3b3JrZXJJZH0gLT4gUGVyIFdvcmtlcjogJHtnYXRld2F5LnNoYXJkc1Blcldvcmtlcn0gLT4gVG90YWw6ICR7Z2F0ZXdheS50b3RhbFdvcmtlcnN9YCxcbiAgICAgIClcbiAgICAgIHJldHVybiB3b3JrZXJJZFxuICAgIH0sXG4gICAgcHJlcGFyZUJ1Y2tldHMoKSB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGdhdGV3YXkuY29ubmVjdGlvbi5zZXNzaW9uU3RhcnRMaW1pdC5tYXhDb25jdXJyZW5jeTsgKytpKSB7XG4gICAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKGBbR2F0ZXdheV0gUHJlcGFyaW5nIGJ1Y2tldHMgZm9yIGNvbmN1cnJlbmN5OiAke2l9YClcbiAgICAgICAgZ2F0ZXdheS5idWNrZXRzLnNldChpLCB7XG4gICAgICAgICAgd29ya2VyczogW10sXG4gICAgICAgICAgaWRlbnRpZnlSZXF1ZXN0czogW10sXG4gICAgICAgIH0pXG4gICAgICB9XG5cbiAgICAgIC8vIE9SR0FOSVpFIEFMTCBTSEFSRFMgSU5UTyBUSEVJUiBPV04gQlVDS0VUU1xuICAgICAgZm9yIChsZXQgc2hhcmRJZCA9IGdhdGV3YXkuZmlyc3RTaGFyZElkOyBzaGFyZElkIDw9IGdhdGV3YXkubGFzdFNoYXJkSWQ7ICsrc2hhcmRJZCkge1xuICAgICAgICBnYXRld2F5LmxvZ2dlci5kZWJ1ZyhgW0dhdGV3YXldIFByZXBhcmluZyBidWNrZXRzIGZvciBzaGFyZDogJHtzaGFyZElkfWApXG4gICAgICAgIGlmIChzaGFyZElkID49IGdhdGV3YXkudG90YWxTaGFyZHMpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFNoYXJkIChpZDogJHtzaGFyZElkfSkgaXMgYmlnZ2VyIG9yIGVxdWFsIHRvIHRoZSB1c2VkIGFtb3VudCBvZiB1c2VkIHNoYXJkcyB3aGljaCBpcyAke2dhdGV3YXkudG90YWxTaGFyZHN9YClcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGJ1Y2tldElkID0gc2hhcmRJZCAlIGdhdGV3YXkuY29ubmVjdGlvbi5zZXNzaW9uU3RhcnRMaW1pdC5tYXhDb25jdXJyZW5jeVxuICAgICAgICBjb25zdCBidWNrZXQgPSBnYXRld2F5LmJ1Y2tldHMuZ2V0KGJ1Y2tldElkKVxuICAgICAgICBpZiAoIWJ1Y2tldCkge1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBTaGFyZCAoaWQ6ICR7c2hhcmRJZH0pIGdvdCBhc3NpZ25lZCB0byBhbiBpbGxlZ2FsIGJ1Y2tldCBpZDogJHtidWNrZXRJZH0sIGV4cGVjdGVkIGEgYnVja2V0IGlkIGJldHdlZW4gMCBhbmQgJHtcbiAgICAgICAgICAgICAgZ2F0ZXdheS5jb25uZWN0aW9uLnNlc3Npb25TdGFydExpbWl0Lm1heENvbmN1cnJlbmN5IC0gMVxuICAgICAgICAgICAgfWAsXG4gICAgICAgICAgKVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gRklORCBBIFFVRVVFIElOIFRISVMgQlVDS0VUIFRIQVQgSEFTIFNQQUNFXG4gICAgICAgIC8vIGNvbnN0IHdvcmtlciA9IGJ1Y2tldC53b3JrZXJzLmZpbmQoKHcpID0+IHcucXVldWUubGVuZ3RoIDwgZ2F0ZXdheS5zaGFyZHNQZXJXb3JrZXIpO1xuICAgICAgICBjb25zdCB3b3JrZXJJZCA9IGdhdGV3YXkuY2FsY3VsYXRlV29ya2VySWQoc2hhcmRJZClcbiAgICAgICAgY29uc3Qgd29ya2VyID0gYnVja2V0LndvcmtlcnMuZmluZCgodykgPT4gdy5pZCA9PT0gd29ya2VySWQpXG4gICAgICAgIGlmICh3b3JrZXIpIHtcbiAgICAgICAgICAvLyBJRiBUSEUgUVVFVUUgSEFTIFNQQUNFIEpVU1QgQUREIElUIFRPIFRISVMgUVVFVUVcbiAgICAgICAgICB3b3JrZXIucXVldWUucHVzaChzaGFyZElkKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGJ1Y2tldC53b3JrZXJzLnB1c2goeyBpZDogd29ya2VySWQsIHF1ZXVlOiBbc2hhcmRJZF0gfSlcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBmb3IgKGNvbnN0IGJ1Y2tldCBvZiBnYXRld2F5LmJ1Y2tldHMudmFsdWVzKCkpIHtcbiAgICAgICAgZm9yIChjb25zdCB3b3JrZXIgb2YgYnVja2V0LndvcmtlcnMudmFsdWVzKCkpIHtcbiAgICAgICAgICB3b3JrZXIucXVldWUgPSB3b3JrZXIucXVldWUuc29ydCgoYSwgYikgPT4gYSAtIGIpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuICAgIGFzeW5jIHNwYXduU2hhcmRzKCkge1xuICAgICAgLy8gUFJFUEFSRVMgQUxMIFNIQVJEUyBJTiBTUEVDSUZJQyBCVUNLRVRTXG4gICAgICBnYXRld2F5LnByZXBhcmVCdWNrZXRzKClcblxuICAgICAgLy8gUHJlZmVyIGNvbmN1cnJlbmN5IG9mIGZvckVhY2ggaW5zdGVhZCBvZiBmb3JvZlxuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIFsuLi5nYXRld2F5LmJ1Y2tldHMuZW50cmllcygpXS5tYXAoYXN5bmMgKFtidWNrZXRJZCwgYnVja2V0XSkgPT4ge1xuICAgICAgICAgIGZvciAoY29uc3Qgd29ya2VyIG9mIGJ1Y2tldC53b3JrZXJzKSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IHNoYXJkSWQgb2Ygd29ya2VyLnF1ZXVlKSB7XG4gICAgICAgICAgICAgIGF3YWl0IGdhdGV3YXkudGVsbFdvcmtlclRvSWRlbnRpZnkod29ya2VyLmlkLCBzaGFyZElkLCBidWNrZXRJZClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0pLFxuICAgICAgKVxuXG4gICAgICAvLyBDaGVjayBhbmQgcmVzaGFyZCBhdXRvbWF0aWNhbGx5IGlmIGF1dG8gcmVzaGFyZGluZyBpcyBlbmFibGVkLlxuICAgICAgaWYgKGdhdGV3YXkucmVzaGFyZGluZy5lbmFibGVkICYmIGdhdGV3YXkucmVzaGFyZGluZy5jaGVja0ludGVydmFsICE9PSAtMSkge1xuICAgICAgICAvLyBJdCBpcyBiZXR0ZXIgdG8gZW5zdXJlIHRoZXJlIGlzIGFsd2F5cyBvbmx5IG9uZVxuICAgICAgICBjbGVhckludGVydmFsKGdhdGV3YXkucmVzaGFyZGluZy5jaGVja0ludGVydmFsSWQpXG5cbiAgICAgICAgaWYgKCFnYXRld2F5LnJlc2hhcmRpbmcuZ2V0U2Vzc2lvbkluZm8pIHtcbiAgICAgICAgICBnYXRld2F5LnJlc2hhcmRpbmcuZW5hYmxlZCA9IGZhbHNlXG4gICAgICAgICAgZ2F0ZXdheS5sb2dnZXIud2FybihcIltSZXNoYXJkaW5nXSBSZXNoYXJkaW5nIGlzIGVuYWJsZWQgYnV0ICdyZXNoYXJkaW5nLmdldFNlc3Npb25JbmZvKCknIHdhcyBub3QgcHJvdmlkZWQuIERpc2FibGluZyByZXNoYXJkaW5nLlwiKVxuXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICBnYXRld2F5LnJlc2hhcmRpbmcuY2hlY2tJbnRlcnZhbElkID0gc2V0SW50ZXJ2YWwoYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIGNvbnN0IHJlc2hhcmRpbmdJbmZvID0gYXdhaXQgZ2F0ZXdheS5yZXNoYXJkaW5nLmNoZWNrSWZSZXNoYXJkaW5nSXNOZWVkZWQoKVxuXG4gICAgICAgICAgaWYgKHJlc2hhcmRpbmdJbmZvLm5lZWRlZCAmJiByZXNoYXJkaW5nSW5mby5pbmZvKSBhd2FpdCBnYXRld2F5LnJlc2hhcmRpbmcucmVzaGFyZChyZXNoYXJkaW5nSW5mby5pbmZvKVxuICAgICAgICB9LCBnYXRld2F5LnJlc2hhcmRpbmcuY2hlY2tJbnRlcnZhbClcbiAgICAgIH1cbiAgICB9LFxuICAgIGFzeW5jIHNodXRkb3duKGNvZGUsIHJlYXNvbiwgY2xlYXJSZXNoYXJkaW5nSW50ZXJ2YWwgPSB0cnVlKSB7XG4gICAgICBnYXRld2F5LnNoYXJkcy5mb3JFYWNoKChzaGFyZCkgPT4gc2hhcmQuY2xvc2UoY29kZSwgcmVhc29uKSlcblxuICAgICAgaWYgKGNsZWFyUmVzaGFyZGluZ0ludGVydmFsKSBjbGVhckludGVydmFsKGdhdGV3YXkucmVzaGFyZGluZy5jaGVja0ludGVydmFsSWQpXG5cbiAgICAgIGF3YWl0IGRlbGF5KDUwMDApXG4gICAgfSxcbiAgICBhc3luYyBzZW5kUGF5bG9hZChzaGFyZElkLCBwYXlsb2FkKSB7XG4gICAgICBjb25zdCBzaGFyZCA9IGdhdGV3YXkuc2hhcmRzLmdldChzaGFyZElkKVxuXG4gICAgICBpZiAoIXNoYXJkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgU2hhcmQgKGlkOiAke3NoYXJkSWR9IG5vdCBmb3VuZGApXG4gICAgICB9XG5cbiAgICAgIGF3YWl0IHNoYXJkLnNlbmQocGF5bG9hZClcbiAgICB9LFxuICAgIGFzeW5jIHRlbGxXb3JrZXJUb0lkZW50aWZ5KHdvcmtlcklkLCBzaGFyZElkLCBidWNrZXRJZCkge1xuICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtHYXRld2F5XSBUZWxsIHdvcmtlciB0byBpZGVudGlmeSAoJHt3b3JrZXJJZH0sICR7c2hhcmRJZH0sICR7YnVja2V0SWR9KWApXG4gICAgICBhd2FpdCBnYXRld2F5LmlkZW50aWZ5KHNoYXJkSWQpXG4gICAgfSxcbiAgICBhc3luYyBpZGVudGlmeShzaGFyZElkOiBudW1iZXIpIHtcbiAgICAgIGxldCBzaGFyZCA9IHRoaXMuc2hhcmRzLmdldChzaGFyZElkKVxuICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtHYXRld2F5XSBJZGVudGlmeWluZyAke3NoYXJkID8gJ2V4aXN0aW5nJyA6ICduZXcnfSBzaGFyZCAoJHtzaGFyZElkfSlgKVxuXG4gICAgICBpZiAoIXNoYXJkKSB7XG4gICAgICAgIHNoYXJkID0gbmV3IFNoYXJkKHtcbiAgICAgICAgICBpZDogc2hhcmRJZCxcbiAgICAgICAgICBjb25uZWN0aW9uOiB7XG4gICAgICAgICAgICBjb21wcmVzczogdGhpcy5jb21wcmVzcyxcbiAgICAgICAgICAgIHRyYW5zcG9ydENvbXByZXNzaW9uOiBnYXRld2F5LnRyYW5zcG9ydENvbXByZXNzaW9uLFxuICAgICAgICAgICAgaW50ZW50czogdGhpcy5pbnRlbnRzLFxuICAgICAgICAgICAgcHJvcGVydGllczogdGhpcy5wcm9wZXJ0aWVzLFxuICAgICAgICAgICAgdG9rZW46IHRoaXMudG9rZW4sXG4gICAgICAgICAgICB0b3RhbFNoYXJkczogdGhpcy50b3RhbFNoYXJkcyxcbiAgICAgICAgICAgIHVybDogdGhpcy51cmwsXG4gICAgICAgICAgICB2ZXJzaW9uOiB0aGlzLnZlcnNpb24sXG4gICAgICAgICAgfSxcbiAgICAgICAgICBldmVudHM6IG9wdGlvbnMuZXZlbnRzID8/IHt9LFxuICAgICAgICAgIGxvZ2dlcjogdGhpcy5sb2dnZXIsXG4gICAgICAgICAgcmVxdWVzdElkZW50aWZ5OiBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICBhd2FpdCBnYXRld2F5LmlkZW50aWZ5KHNoYXJkSWQpXG4gICAgICAgICAgfSxcbiAgICAgICAgICBzaGFyZElzUmVhZHk6IGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKGBbU2hhcmRdIFNoYXJkICMke3NoYXJkSWR9IGlzIHJlYWR5YClcbiAgICAgICAgICAgIGF3YWl0IGRlbGF5KGdhdGV3YXkuc3Bhd25TaGFyZERlbGF5KVxuICAgICAgICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtTaGFyZF0gUmVzb2x2aW5nIHNoYXJkIGlkZW50aWZ5IHJlcXVlc3RgKVxuICAgICAgICAgICAgZ2F0ZXdheS5idWNrZXRzLmdldChzaGFyZElkICUgZ2F0ZXdheS5jb25uZWN0aW9uLnNlc3Npb25TdGFydExpbWl0Lm1heENvbmN1cnJlbmN5KSEuaWRlbnRpZnlSZXF1ZXN0cy5zaGlmdCgpPy4oKVxuICAgICAgICAgIH0sXG4gICAgICAgICAgbWFrZVByZXNlbmNlOiBnYXRld2F5Lm1ha2VQcmVzZW5jZSxcbiAgICAgICAgfSlcblxuICAgICAgICBpZiAodGhpcy5wcmVmZXJTbmFrZUNhc2UpIHtcbiAgICAgICAgICBzaGFyZC5mb3J3YXJkVG9Cb3QgPSBhc3luYyAocGF5bG9hZCkgPT4ge1xuICAgICAgICAgICAgc2hhcmQhLmV2ZW50cy5tZXNzYWdlPy4oc2hhcmQhLCBwYXlsb2FkKVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc2hhcmRzLnNldChzaGFyZElkLCBzaGFyZClcbiAgICAgIH1cblxuICAgICAgY29uc3QgYnVja2V0ID0gZ2F0ZXdheS5idWNrZXRzLmdldChzaGFyZElkICUgZ2F0ZXdheS5jb25uZWN0aW9uLnNlc3Npb25TdGFydExpbWl0Lm1heENvbmN1cnJlbmN5KVxuICAgICAgaWYgKCFidWNrZXQpIHJldHVyblxuXG4gICAgICByZXR1cm4gYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgLy8gTWFyayB0aGF0IHdlIGFyZSBtYWtpbmcgYW4gaWRlbnRpZnkgcmVxdWVzdCBzbyBhbm90aGVyIGlzIG5vdCBtYWRlLlxuICAgICAgICBidWNrZXQuaWRlbnRpZnlSZXF1ZXN0cy5wdXNoKHJlc29sdmUpXG4gICAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKGBbR2F0ZXdheV0gSWRlbnRpZnlpbmcgU2hhcmQgIyR7c2hhcmRJZH0uYClcbiAgICAgICAgLy8gVGhpcyB3aWxsIHRyaWdnZXIgaWRlbnRpZnkgYW5kIHdoZW4gUkVBRFkgaXMgcmVjZWl2ZWQgaXQgd2lsbCByZXNvbHZlIHRoZSBhYm92ZSByZXF1ZXN0LlxuICAgICAgICBzaGFyZD8uaWRlbnRpZnkoKVxuICAgICAgfSlcbiAgICB9LFxuICAgIGFzeW5jIGtpbGwoc2hhcmRJZDogbnVtYmVyKSB7XG4gICAgICBjb25zdCBzaGFyZCA9IHRoaXMuc2hhcmRzLmdldChzaGFyZElkKVxuICAgICAgaWYgKCFzaGFyZCkge1xuICAgICAgICByZXR1cm4gZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtHYXRld2F5XSBBIGtpbGwgZm9yIFNoYXJkICMke3NoYXJkSWR9IHdhcyByZXF1ZXN0ZWQsIGJ1dCB0aGUgc2hhcmQgY291bGQgbm90IGJlIGZvdW5kYClcbiAgICAgIH1cblxuICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtHYXRld2F5XSBLaWxsaW5nIFNoYXJkICMke3NoYXJkSWR9YClcbiAgICAgIHRoaXMuc2hhcmRzLmRlbGV0ZShzaGFyZElkKVxuICAgICAgYXdhaXQgc2hhcmQuc2h1dGRvd24oKVxuICAgIH0sXG4gICAgYXN5bmMgcmVxdWVzdElkZW50aWZ5KF9zaGFyZElkOiBudW1iZXIpIHtcbiAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKGBbR2F0ZXdheV0gUmVxdWVzdGluZyBpZGVudGlmeWApXG4gICAgfSxcblxuICAgIC8vIEhlbHBlcnMgbWV0aG9kcyBiZWxvdyB0aGlzXG5cbiAgICBjYWxjdWxhdGVTaGFyZElkKGd1aWxkSWQsIHRvdGFsU2hhcmRzKSB7XG4gICAgICAvLyBJZiBub25lIGlzIHByb3ZpZGVkLCB1c2UgdGhlIHRvdGFsIHNoYXJkcyBudW1iZXIgZnJvbSBnYXRld2F5IG9iamVjdC5cbiAgICAgIGlmICghdG90YWxTaGFyZHMpIHRvdGFsU2hhcmRzID0gZ2F0ZXdheS50b3RhbFNoYXJkc1xuICAgICAgLy8gSWYgaXQgaXMgb25seSAxIHNoYXJkLCBpdCB3aWxsIGFsd2F5cyBiZSBzaGFyZCBpZCAwXG4gICAgICBpZiAodG90YWxTaGFyZHMgPT09IDEpIHtcbiAgICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtHYXRld2F5XSBjYWxjdWxhdGVTaGFyZElkICgxIHNoYXJkKWApXG4gICAgICAgIHJldHVybiAwXG4gICAgICB9XG5cbiAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKGBbR2F0ZXdheV0gY2FsY3VsYXRlU2hhcmRJZCAoZ3VpbGRJZDogJHtndWlsZElkfSwgdG90YWxTaGFyZHM6ICR7dG90YWxTaGFyZHN9KWApXG4gICAgICByZXR1cm4gTnVtYmVyKChCaWdJbnQoZ3VpbGRJZCkgPj4gMjJuKSAlIEJpZ0ludCh0b3RhbFNoYXJkcykpXG4gICAgfSxcblxuICAgIGFzeW5jIGpvaW5Wb2ljZUNoYW5uZWwoZ3VpbGRJZCwgY2hhbm5lbElkLCBvcHRpb25zKSB7XG4gICAgICBjb25zdCBzaGFyZElkID0gZ2F0ZXdheS5jYWxjdWxhdGVTaGFyZElkKGd1aWxkSWQpXG5cbiAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKGBbR2F0ZXdheV0gam9pblZvaWNlQ2hhbm5lbCBndWlsZElkOiAke2d1aWxkSWR9IGNoYW5uZWxJZDogJHtjaGFubmVsSWR9YClcblxuICAgICAgYXdhaXQgZ2F0ZXdheS5zZW5kUGF5bG9hZChzaGFyZElkLCB7XG4gICAgICAgIG9wOiBHYXRld2F5T3Bjb2Rlcy5Wb2ljZVN0YXRlVXBkYXRlLFxuICAgICAgICBkOiB7XG4gICAgICAgICAgZ3VpbGRfaWQ6IGd1aWxkSWQudG9TdHJpbmcoKSxcbiAgICAgICAgICBjaGFubmVsX2lkOiBjaGFubmVsSWQudG9TdHJpbmcoKSxcbiAgICAgICAgICBzZWxmX211dGU6IG9wdGlvbnM/LnNlbGZNdXRlID8/IGZhbHNlLFxuICAgICAgICAgIHNlbGZfZGVhZjogb3B0aW9ucz8uc2VsZkRlYWYgPz8gdHJ1ZSxcbiAgICAgICAgfSxcbiAgICAgIH0pXG4gICAgfSxcblxuICAgIGFzeW5jIGVkaXRCb3RTdGF0dXMoZGF0YSkge1xuICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtHYXRld2F5XSBlZGl0Qm90U3RhdHVzIGRhdGE6ICR7SlNPTi5zdHJpbmdpZnkoZGF0YSl9YClcblxuICAgICAgYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgIFsuLi5nYXRld2F5LnNoYXJkcy52YWx1ZXMoKV0ubWFwKGFzeW5jIChzaGFyZCkgPT4ge1xuICAgICAgICAgIGdhdGV3YXkuZWRpdFNoYXJkU3RhdHVzKHNoYXJkLmlkLCBkYXRhKVxuICAgICAgICB9KSxcbiAgICAgIClcbiAgICB9LFxuXG4gICAgYXN5bmMgZWRpdFNoYXJkU3RhdHVzKHNoYXJkSWQsIGRhdGEpIHtcbiAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKGBbR2F0ZXdheV0gZWRpdFNoYXJkU3RhdHVzIHNoYXJkSWQ6ICR7c2hhcmRJZH0gLT4gZGF0YTogJHtKU09OLnN0cmluZ2lmeShkYXRhKX1gKVxuXG4gICAgICBhd2FpdCBnYXRld2F5LnNlbmRQYXlsb2FkKHNoYXJkSWQsIHtcbiAgICAgICAgb3A6IEdhdGV3YXlPcGNvZGVzLlByZXNlbmNlVXBkYXRlLFxuICAgICAgICBkOiB7XG4gICAgICAgICAgc2luY2U6IG51bGwsXG4gICAgICAgICAgYWZrOiBmYWxzZSxcbiAgICAgICAgICBhY3Rpdml0aWVzOiBkYXRhLmFjdGl2aXRpZXMsXG4gICAgICAgICAgc3RhdHVzOiBkYXRhLnN0YXR1cyxcbiAgICAgICAgfSxcbiAgICAgIH0pXG4gICAgfSxcblxuICAgIGFzeW5jIHJlcXVlc3RNZW1iZXJzKGd1aWxkSWQsIG9wdGlvbnMpIHtcbiAgICAgIGNvbnN0IHNoYXJkSWQgPSBnYXRld2F5LmNhbGN1bGF0ZVNoYXJkSWQoZ3VpbGRJZClcblxuICAgICAgaWYgKGdhdGV3YXkuaW50ZW50cyAmJiAoIW9wdGlvbnM/LmxpbWl0IHx8IG9wdGlvbnMubGltaXQgPiAxKSAmJiAhKGdhdGV3YXkuaW50ZW50cyAmIEdhdGV3YXlJbnRlbnRzLkd1aWxkTWVtYmVycykpXG4gICAgICAgIHRocm93IG5ldyBFcnJvcignQ2Fubm90IGZldGNoIG1vcmUgdGhlbiAxIG1lbWJlciB3aXRob3V0IHRoZSBHVUlMRF9NRU1CRVJTIGludGVudCcpXG5cbiAgICAgIGdhdGV3YXkubG9nZ2VyLmRlYnVnKGBbR2F0ZXdheV0gcmVxdWVzdE1lbWJlcnMgZ3VpbGRJZDogJHtndWlsZElkfSAtPiBkYXRhOiAke0pTT04uc3RyaW5naWZ5KG9wdGlvbnMpfWApXG5cbiAgICAgIGlmIChvcHRpb25zPy51c2VySWRzPy5sZW5ndGgpIHtcbiAgICAgICAgZ2F0ZXdheS5sb2dnZXIuZGVidWcoYFtHYXRld2F5XSByZXF1ZXN0TWVtYmVycyBndWlsZElkOiAke2d1aWxkSWR9IC0+IHNldHRpbmcgdXNlciBsaW1pdCBiYXNlZCBvbiB1c2VySWRzIGxlbmd0aDogJHtvcHRpb25zLnVzZXJJZHMubGVuZ3RofWApXG5cbiAgICAgICAgb3B0aW9ucy5saW1pdCA9IG9wdGlvbnMudXNlcklkcy5sZW5ndGhcbiAgICAgIH1cblxuICAgICAgY29uc3QgbWVtYmVycyA9XG4gICAgICAgICFnYXRld2F5LmNhY2hlLnJlcXVlc3RNZW1iZXJzLmVuYWJsZWQgfHwgIW9wdGlvbnM/Lm5vbmNlXG4gICAgICAgICAgPyBbXVxuICAgICAgICAgIDogbmV3IFByb21pc2U8Q2FtZWxpemU8RGlzY29yZE1lbWJlcldpdGhVc2VyW10+PigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgICAgIC8vIFNob3VsZCBuZXZlciBoYXBwZW4uXG4gICAgICAgICAgICAgIGlmICghZ2F0ZXdheS5jYWNoZS5yZXF1ZXN0TWVtYmVycy5lbmFibGVkIHx8ICFvcHRpb25zPy5ub25jZSkge1xuICAgICAgICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IoXCJDYW4ndCByZXF1ZXN0IHRoZSBtZW1iZXJzIHdpdGhvdXQgdGhlIG5vbmNlIG9yIHdpdGggdGhlIGZlYXR1cmUgZGlzYWJsZWQuXCIpKVxuICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgZ2F0ZXdheS5jYWNoZS5yZXF1ZXN0TWVtYmVycy5wZW5kaW5nLnNldChvcHRpb25zLm5vbmNlLCB7XG4gICAgICAgICAgICAgICAgbm9uY2U6IG9wdGlvbnMubm9uY2UsXG4gICAgICAgICAgICAgICAgcmVzb2x2ZSxcbiAgICAgICAgICAgICAgICBtZW1iZXJzOiBbXSxcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0pXG5cbiAgICAgIGF3YWl0IGdhdGV3YXkuc2VuZFBheWxvYWQoc2hhcmRJZCwge1xuICAgICAgICBvcDogR2F0ZXdheU9wY29kZXMuUmVxdWVzdEd1aWxkTWVtYmVycyxcbiAgICAgICAgZDoge1xuICAgICAgICAgIGd1aWxkX2lkOiBndWlsZElkLnRvU3RyaW5nKCksXG4gICAgICAgICAgLy8gSWYgYSBxdWVyeSBpcyBwcm92aWRlZCB1c2UgaXQsIE9SIGlmIGEgbGltaXQgaXMgTk9UIHByb3ZpZGVkIHVzZSBcIlwiXG4gICAgICAgICAgcXVlcnk6IG9wdGlvbnM/LnF1ZXJ5ID8/IChvcHRpb25zPy5saW1pdCA/IHVuZGVmaW5lZCA6ICcnKSxcbiAgICAgICAgICBsaW1pdDogb3B0aW9ucz8ubGltaXQgPz8gMCxcbiAgICAgICAgICBwcmVzZW5jZXM6IG9wdGlvbnM/LnByZXNlbmNlcyA/PyBmYWxzZSxcbiAgICAgICAgICB1c2VyX2lkczogb3B0aW9ucz8udXNlcklkcz8ubWFwKChpZCkgPT4gaWQudG9TdHJpbmcoKSksXG4gICAgICAgICAgbm9uY2U6IG9wdGlvbnM/Lm5vbmNlLFxuICAgICAgICB9LFxuICAgICAgfSlcblxuICAgICAgcmV0dXJuIGF3YWl0IG1lbWJlcnNcbiAgICB9LFxuXG4gICAgYXN5bmMgbGVhdmVWb2ljZUNoYW5uZWwoZ3VpbGRJZCkge1xuICAgICAgY29uc3Qgc2hhcmRJZCA9IGdhdGV3YXkuY2FsY3VsYXRlU2hhcmRJZChndWlsZElkKVxuXG4gICAgICBnYXRld2F5LmxvZ2dlci5kZWJ1ZyhgW0dhdGV3YXldIGxlYXZlVm9pY2VDaGFubmVsIGd1aWxkSWQ6ICR7Z3VpbGRJZH0gU2hhcmQgJHtzaGFyZElkfWApXG5cbiAgICAgIGF3YWl0IGdhdGV3YXkuc2VuZFBheWxvYWQoc2hhcmRJZCwge1xuICAgICAgICBvcDogR2F0ZXdheU9wY29kZXMuVm9pY2VTdGF0ZVVwZGF0ZSxcbiAgICAgICAgZDoge1xuICAgICAgICAgIGd1aWxkX2lkOiBndWlsZElkLnRvU3RyaW5nKCksXG4gICAgICAgICAgY2hhbm5lbF9pZDogbnVsbCxcbiAgICAgICAgICBzZWxmX211dGU6IGZhbHNlLFxuICAgICAgICAgIHNlbGZfZGVhZjogZmFsc2UsXG4gICAgICAgIH0sXG4gICAgICB9KVxuICAgIH0sXG5cbiAgICBhc3luYyByZXF1ZXN0U291bmRib2FyZFNvdW5kcyhndWlsZElkcykge1xuICAgICAgLyoqXG4gICAgICAgKiBEaXNjb3JkIHdpbGwgc2VuZCB0aGUgZXZlbnRzIGZvciB0aGUgZ3VpbGRzIHRoYXQgYXJlIFwidW5kZXIgdGhlIHNoYXJkXCIgdGhhdCBzZW5kcyB0aGUgb3Bjb2RlLlxuICAgICAgICogRm9yIHRoaXMgcmVhc29uIHdlIG5lZWQgdG8gZ3JvdXAgdGhlIGlkcyB3aXRoIHRoZSBzaGFyZCB0aGUgY2FsY3VsYXRlU2hhcmRJZCBtZXRob2QgZ2l2ZXNcbiAgICAgICAqL1xuXG4gICAgICBjb25zdCBtYXAgPSBuZXcgTWFwPG51bWJlciwgQmlnU3RyaW5nW10+KClcblxuICAgICAgZm9yIChjb25zdCBndWlsZElkIG9mIGd1aWxkSWRzKSB7XG4gICAgICAgIGNvbnN0IHNoYXJkSWQgPSBnYXRld2F5LmNhbGN1bGF0ZVNoYXJkSWQoZ3VpbGRJZClcblxuICAgICAgICBjb25zdCBpZHMgPSBtYXAuZ2V0KHNoYXJkSWQpID8/IFtdXG4gICAgICAgIG1hcC5zZXQoc2hhcmRJZCwgaWRzKVxuXG4gICAgICAgIGlkcy5wdXNoKGd1aWxkSWQpXG4gICAgICB9XG5cbiAgICAgIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICBbLi4ubWFwLmVudHJpZXMoKV0ubWFwKChbc2hhcmRJZCwgaWRzXSkgPT5cbiAgICAgICAgICBnYXRld2F5LnNlbmRQYXlsb2FkKHNoYXJkSWQsIHtcbiAgICAgICAgICAgIG9wOiBHYXRld2F5T3Bjb2Rlcy5SZXF1ZXN0U291bmRib2FyZFNvdW5kcyxcbiAgICAgICAgICAgIGQ6IHtcbiAgICAgICAgICAgICAgZ3VpbGRfaWRzOiBpZHMsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0pLFxuICAgICAgICApLFxuICAgICAgKVxuICAgIH0sXG4gIH1cblxuICByZXR1cm4gZ2F0ZXdheVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIENyZWF0ZUdhdGV3YXlNYW5hZ2VyT3B0aW9ucyB7XG4gIC8qKlxuICAgKiBJZCBvZiB0aGUgZmlyc3QgU2hhcmQgd2hpY2ggc2hvdWxkIGdldCBjb250cm9sbGVkIGJ5IHRoaXMgbWFuYWdlci5cbiAgICogQGRlZmF1bHQgMFxuICAgKi9cbiAgZmlyc3RTaGFyZElkPzogbnVtYmVyXG4gIC8qKlxuICAgKiBJZCBvZiB0aGUgbGFzdCBTaGFyZCB3aGljaCBzaG91bGQgZ2V0IGNvbnRyb2xsZWQgYnkgdGhpcyBtYW5hZ2VyLlxuICAgKiBAZGVmYXVsdCAwXG4gICAqL1xuICBsYXN0U2hhcmRJZD86IG51bWJlclxuICAvKipcbiAgICogRGVsYXkgaW4gbWlsbGlzZWNvbmRzIHRvIHdhaXQgYmVmb3JlIHNwYXduaW5nIG5leHQgc2hhcmQuIE9QVElNQUwgSVMgQUJPVkUgNTEwMC4gWU9VIERPTidUIFdBTlQgVE8gSElUIFRIRSBSQVRFIExJTUlUISEhXG4gICAqIEBkZWZhdWx0IDUzMDBcbiAgICovXG4gIHNwYXduU2hhcmREZWxheT86IG51bWJlclxuICAvKipcbiAgICogV2hldGhlciB0byBzZW5kIHRoZSBkaXNjb3JkIHBhY2tldHMgaW4gc25ha2UgY2FzZSBmb3JtLlxuICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgKi9cbiAgcHJlZmVyU25ha2VDYXNlPzogYm9vbGVhblxuICAvKipcbiAgICogVG90YWwgYW1vdW50IG9mIHNoYXJkcyB5b3VyIGJvdCB1c2VzLiBVc2VmdWwgZm9yIHplcm8tZG93bnRpbWUgdXBkYXRlcyBvciByZXNoYXJkaW5nLlxuICAgKiBAZGVmYXVsdCAxXG4gICAqL1xuICB0b3RhbFNoYXJkcz86IG51bWJlclxuICAvKipcbiAgICogVGhlIGFtb3VudCBvZiBzaGFyZHMgdG8gbG9hZCBwZXIgd29ya2VyLlxuICAgKiBAZGVmYXVsdCAyNVxuICAgKi9cbiAgc2hhcmRzUGVyV29ya2VyPzogbnVtYmVyXG4gIC8qKlxuICAgKiBUaGUgdG90YWwgYW1vdW50IG9mIHdvcmtlcnMgdG8gdXNlIGZvciB5b3VyIGJvdC5cbiAgICogQGRlZmF1bHQgNFxuICAgKi9cbiAgdG90YWxXb3JrZXJzPzogbnVtYmVyXG4gIC8qKiBJbXBvcnRhbnQgZGF0YSB3aGljaCBpcyB1c2VkIGJ5IHRoZSBtYW5hZ2VyIHRvIGNvbm5lY3Qgc2hhcmRzIHRvIHRoZSBnYXRld2F5LiAqL1xuICBjb25uZWN0aW9uPzogQ2FtZWxpemU8RGlzY29yZEdldEdhdGV3YXlCb3Q+XG4gIC8qKiBXaGV0aGVyIGluY29taW5nIHBheWxvYWRzIGFyZSBjb21wcmVzc2VkIHVzaW5nIHpsaWIuXG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICBjb21wcmVzcz86IGJvb2xlYW5cbiAgLyoqIFdoYXQgdHJhbnNwb3J0IGNvbXByZXNzaW9uIHNob3VsZCBiZSB1c2VkICovXG4gIHRyYW5zcG9ydENvbXByZXNzaW9uPzogVHJhbnNwb3J0Q29tcHJlc3Npb24gfCBudWxsXG4gIC8qKiBUaGUgY2FsY3VsYXRlZCBpbnRlbnQgdmFsdWUgb2YgdGhlIGV2ZW50cyB3aGljaCB0aGUgc2hhcmQgc2hvdWxkIHJlY2VpdmUuXG4gICAqXG4gICAqIEBkZWZhdWx0IDBcbiAgICovXG4gIGludGVudHM/OiBudW1iZXJcbiAgLyoqIElkZW50aWZ5IHByb3BlcnRpZXMgdG8gdXNlICovXG4gIHByb3BlcnRpZXM/OiB7XG4gICAgLyoqIE9wZXJhdGluZyBzeXN0ZW0gdGhlIHNoYXJkIHJ1bnMgb24uXG4gICAgICpcbiAgICAgKiBAZGVmYXVsdCBcImRhcndpblwiIHwgXCJsaW51eFwiIHwgXCJ3aW5kb3dzXCJcbiAgICAgKi9cbiAgICBvczogc3RyaW5nXG4gICAgLyoqIFRoZSBcImJyb3dzZXJcIiB3aGVyZSB0aGlzIHNoYXJkIGlzIHJ1bm5pbmcgb24uXG4gICAgICpcbiAgICAgKiBAZGVmYXVsdCBcIkRpc2NvcmRlbm9cIlxuICAgICAqL1xuICAgIGJyb3dzZXI6IHN0cmluZ1xuICAgIC8qKiBUaGUgZGV2aWNlIG9uIHdoaWNoIHRoZSBzaGFyZCBpcyBydW5uaW5nLlxuICAgICAqXG4gICAgICogQGRlZmF1bHQgXCJEaXNjb3JkZW5vXCJcbiAgICAgKi9cbiAgICBkZXZpY2U6IHN0cmluZ1xuICB9XG4gIC8qKiBCb3QgdG9rZW4gd2hpY2ggaXMgdXNlZCB0byBjb25uZWN0IHRvIERpc2NvcmQgKi9cbiAgdG9rZW46IHN0cmluZ1xuICAvKiogVGhlIFVSTCBvZiB0aGUgZ2F0ZXdheSB3aGljaCBzaG91bGQgYmUgY29ubmVjdGVkIHRvLlxuICAgKlxuICAgKiBAZGVmYXVsdCBcIndzczovL2dhdGV3YXkuZGlzY29yZC5nZ1wiXG4gICAqL1xuICB1cmw/OiBzdHJpbmdcbiAgLyoqIFRoZSBnYXRld2F5IHZlcnNpb24gd2hpY2ggc2hvdWxkIGJlIHVzZWQuXG4gICAqXG4gICAqIEBkZWZhdWx0IDEwXG4gICAqL1xuICB2ZXJzaW9uPzogbnVtYmVyXG4gIC8qKiBUaGUgZXZlbnRzIGhhbmRsZXJzICovXG4gIGV2ZW50cz86IFNoYXJkRXZlbnRzXG4gIC8qKiBUaGlzIG1hbmFnZXJzIGNhY2hlIHJlbGF0ZWQgc2V0dGluZ3MuICovXG4gIGNhY2hlPzoge1xuICAgIHJlcXVlc3RNZW1iZXJzPzoge1xuICAgICAgLyoqXG4gICAgICAgKiBXaGV0aGVyIG9yIG5vdCByZXF1ZXN0IG1lbWJlciByZXF1ZXN0cyBzaG91bGQgYmUgY2FjaGVkLlxuICAgICAgICogQGRlZmF1bHQgZmFsc2VcbiAgICAgICAqL1xuICAgICAgZW5hYmxlZD86IGJvb2xlYW5cbiAgICB9XG4gIH1cbiAgLyoqXG4gICAqIFRoZSBsb2dnZXIgdGhhdCB0aGUgZ2F0ZXdheSBtYW5hZ2VyIHdpbGwgdXNlLlxuICAgKiBAZGVmYXVsdCBsb2dnZXIgLy8gVGhlIGxvZ2dlciBleHBvcnRlZCBieSBgQGRpc2NvcmRlbm8vdXRpbHNgXG4gICAqL1xuICBsb2dnZXI/OiBQaWNrPHR5cGVvZiBsb2dnZXIsICdkZWJ1ZycgfCAnaW5mbycgfCAnd2FybicgfCAnZXJyb3InIHwgJ2ZhdGFsJz5cbiAgLyoqXG4gICAqIE1ha2UgdGhlIHByZXNlbmNlIGZvciB3aGVuIHRoZSBib3QgY29ubmVjdHMgdG8gdGhlIGdhdGV3YXlcbiAgICpcbiAgICogQHJlbWFya3NcbiAgICogVGhpcyBmdW5jdGlvbiB3aWxsIGJlIGNhbGxlZCBlYWNoIHRpbWUgYSBTaGFyZCBpcyBnb2luZyB0byBpZGVudGlmeVxuICAgKi9cbiAgbWFrZVByZXNlbmNlPzogKCkgPT4gUHJvbWlzZTxCb3RTdGF0dXNVcGRhdGUgfCB1bmRlZmluZWQ+XG4gIC8qKiBPcHRpb25zIHJlbGF0ZWQgdG8gcmVzaGFyZGluZy4gKi9cbiAgcmVzaGFyZGluZz86IHtcbiAgICAvKipcbiAgICAgKiBXaGV0aGVyIG9yIG5vdCBhdXRvbWF0ZWQgcmVzaGFyZGluZyBzaG91bGQgYmUgZW5hYmxlZC5cbiAgICAgKiBAZGVmYXVsdCB0cnVlXG4gICAgICovXG4gICAgZW5hYmxlZDogYm9vbGVhblxuICAgIC8qKlxuICAgICAqIFRoZSAlIG9mIGhvdyBmdWxsIGEgc2hhcmQgaXMgd2hlbiByZXNoYXJkaW5nIHNob3VsZCBiZSB0cmlnZ2VyZWQuXG4gICAgICpcbiAgICAgKiBAcmVtYXJrc1xuICAgICAqIFdlIHVzZSBkaXNjb3JkIHJlY29tbWVuZGVkIHNoYXJkIHZhbHVlIHRvIGdldCBhbiAqKmFwcHJveGltYXRpb24qKiBvZiB0aGUgc2hhcmQgZnVsbCBwZXJjZW50YWdlIHRvIGNvbXBhcmUgd2l0aCB0aGlzIHZhbHVlIHNvIHRoZSBib3QgbWF5IG5vdCByZXNoYXJkIGF0IHRoZSBleGFjdCBwZXJjZW50YWdlIHByb3ZpZGVkIGJ1dCBtYXkgcmVzaGFyZCB3aGVuIGl0IGlzIGEgYml0IGhpZ2hlciB0aGFuIHRoZSBwcm92aWRlZCBwZXJjZW50YWdlLlxuICAgICAqIEZvciBhY2N1cmF0ZSBjYWxjdWxhdGlvbiwgeW91IG1heSBvdmVycmlkZSB0aGUgYGNoZWNrSWZSZXNoYXJkaW5nSXNOZWVkZWRgIGZ1bmN0aW9uXG4gICAgICpcbiAgICAgKiBAZGVmYXVsdCA4MCBhcyBpbiA4MCVcbiAgICAgKi9cbiAgICBzaGFyZHNGdWxsUGVyY2VudGFnZTogbnVtYmVyXG4gICAgLyoqXG4gICAgICogVGhlIGludGVydmFsIGluIG1pbGxpc2Vjb25kcywgb2YgaG93IG9mdGVuIHRvIGNoZWNrIHdoZXRoZXIgcmVzaGFyZGluZyBpcyBuZWVkZWQgYW5kIHJlc2hhcmQgYXV0b21hdGljYWxseS4gU2V0IHRvIC0xIHRvIGRpc2FibGUgYXV0byByZXNoYXJkaW5nLlxuICAgICAqIEBkZWZhdWx0IDI4ODAwMDAwICg4IGhvdXJzKVxuICAgICAqL1xuICAgIGNoZWNrSW50ZXJ2YWw6IG51bWJlclxuICAgIC8qKiBIYW5kbGVyIHRvIGdldCBzaGFyZCBjb3VudCBhbmQgb3RoZXIgc2Vzc2lvbiBpbmZvLiAqL1xuICAgIGdldFNlc3Npb25JbmZvPzogKCkgPT4gUHJvbWlzZTxDYW1lbGl6ZTxEaXNjb3JkR2V0R2F0ZXdheUJvdD4+XG4gICAgLyoqIEhhbmRsZXIgdG8gZWRpdCB0aGUgc2hhcmQgaWQgb24gYW55IGNhY2hlZCBndWlsZHMuICovXG4gICAgdXBkYXRlR3VpbGRzU2hhcmRJZD86IChndWlsZElkczogc3RyaW5nW10sIHNoYXJkSWQ6IG51bWJlcikgPT4gUHJvbWlzZTx2b2lkPlxuICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgR2F0ZXdheU1hbmFnZXIgZXh0ZW5kcyBSZXF1aXJlZDxDcmVhdGVHYXRld2F5TWFuYWdlck9wdGlvbnM+IHtcbiAgLyoqIFRoZSBtYXggY29uY3VycmVuY3kgYnVja2V0cy4gVGhvc2Ugd2lsbCBiZSBjcmVhdGVkIHdoZW4gdGhlIGBzcGF3blNoYXJkc2AgKHdoaWNoIGNhbGxzIGBwcmVwYXJlQnVja2V0c2AgdW5kZXIgdGhlIGhvb2QpIGZ1bmN0aW9uIGdldHMgY2FsbGVkLiAqL1xuICBidWNrZXRzOiBNYXA8XG4gICAgbnVtYmVyLFxuICAgIHtcbiAgICAgIHdvcmtlcnM6IEFycmF5PHsgaWQ6IG51bWJlcjsgcXVldWU6IG51bWJlcltdIH0+XG4gICAgICAvKiogUmVxdWVzdHMgdG8gaWRlbnRpZnkgc2hhcmRzIGFyZSBtYWRlIGJhc2VkIG9uIHdoZXRoZXIgaXQgaXMgYXZhaWxhYmxlIHRvIGJlIG1hZGUuICovXG4gICAgICBpZGVudGlmeVJlcXVlc3RzOiBBcnJheTwodmFsdWU6IHZvaWQgfCBQcm9taXNlTGlrZTx2b2lkPikgPT4gdm9pZD5cbiAgICB9XG4gID5cbiAgLyoqIFRoZSBzaGFyZHMgdGhhdCBhcmUgY3JlYXRlZC4gKi9cbiAgc2hhcmRzOiBNYXA8bnVtYmVyLCBTaGFyZD5cbiAgLyoqIFRoZSBsb2dnZXIgZm9yIHRoZSBnYXRld2F5IG1hbmFnZXIuICovXG4gIGxvZ2dlcjogUGljazx0eXBlb2YgbG9nZ2VyLCAnZGVidWcnIHwgJ2luZm8nIHwgJ3dhcm4nIHwgJ2Vycm9yJyB8ICdmYXRhbCc+XG4gIC8qKiBFdmVyeXRoaW5nIHJlbGF0ZWQgdG8gcmVzaGFyZGluZy4gKi9cbiAgcmVzaGFyZGluZzogQ3JlYXRlR2F0ZXdheU1hbmFnZXJPcHRpb25zWydyZXNoYXJkaW5nJ10gJiB7XG4gICAgLyoqXG4gICAgICogVGhlIGludGVydmFsIGlkIG9mIHRoZSBjaGVjayBpbnRlcnZhbC4gVGhpcyBpcyB1c2VkIHRvIGNsZWFyIHRoZSBpbnRlcnZhbCB3aGVuIHRoZSBtYW5hZ2VyIGlzIHNodXRkb3duLlxuICAgICAqL1xuICAgIGNoZWNrSW50ZXJ2YWxJZD86IE5vZGVKUy5UaW1lb3V0IHwgdW5kZWZpbmVkXG4gICAgLyoqIEhvbGRzIHRoZSBzaGFyZHMgdGhhdCByZXNoYXJkaW5nIGhhcyBjcmVhdGVkLiBPbmNlIHJlc2hhcmRpbmcgaXMgZG9uZSwgdGhpcyByZXBsYWNlcyB0aGUgZ2F0ZXdheS5zaGFyZHMgKi9cbiAgICBzaGFyZHM6IENvbGxlY3Rpb248bnVtYmVyLCBTaGFyZD5cbiAgICAvKiogSG9sZHMgdGhlIHBlbmRpbmcgc2hhcmRzIHRoYXQgaGF2ZSBiZWVuIGNyZWF0ZWQgYW5kIGFyZSBwZW5kaW5nIGFsbCBzaGFyZHMgZmluaXNoIGxvYWRpbmcuICovXG4gICAgcGVuZGluZ1NoYXJkczogQ29sbGVjdGlvbjxudW1iZXIsIFNoYXJkPlxuICAgIC8qKiBIYW5kbGVyIHRvIGNoZWNrIGlmIHJlc2hhcmRpbmcgaXMgbmVjZXNzYXJ5LiAqL1xuICAgIGNoZWNrSWZSZXNoYXJkaW5nSXNOZWVkZWQ6ICgpID0+IFByb21pc2U8eyBuZWVkZWQ6IGJvb2xlYW47IGluZm8/OiBDYW1lbGl6ZTxEaXNjb3JkR2V0R2F0ZXdheUJvdD4gfT5cbiAgICAvKiogSGFuZGxlciB0byBiZWdpbiByZXNoYXJkaW5nLiAqL1xuICAgIHJlc2hhcmQ6IChpbmZvOiBDYW1lbGl6ZTxEaXNjb3JkR2V0R2F0ZXdheUJvdD4gJiB7IGZpcnN0U2hhcmRJZD86IG51bWJlcjsgbGFzdFNoYXJkSWQ/OiBudW1iZXIgfSkgPT4gUHJvbWlzZTx2b2lkPlxuICAgIC8qKiBIYW5kbGVyIHRvIGNvbW11bmljYXRlIHRvIGEgd29ya2VyIHRoYXQgYSBzaGFyZCBuZWVkcyB0byBiZSBjcmVhdGVkLiAqL1xuICAgIHRlbGxXb3JrZXJUb1ByZXBhcmU6ICh3b3JrZXJJZDogbnVtYmVyLCBzaGFyZElkOiBudW1iZXIsIGJ1Y2tldElkOiBudW1iZXIpID0+IFByb21pc2U8dm9pZD5cbiAgICAvKiogSGFuZGxlciB0byBhbGVydCB0aGUgZ2F0ZXdheSB0aGF0IGEgc2hhcmQocmVzaGFyZGVkKSBpcyBvbmxpbmUuIEl0IHNob3VsZCBub3cgd2FpdCBmb3IgYWxsIHNoYXJkcyB0byBiZSBwZW5kaW5nIGJlZm9yZSBzaHV0dGluZyBvZmYgb2xkIHNoYXJkcy4gKi9cbiAgICBzaGFyZElzUGVuZGluZzogKHNoYXJkOiBTaGFyZCkgPT4gUHJvbWlzZTx2b2lkPlxuICB9XG4gIC8qKiBEZXRlcm1pbmUgbWF4IG51bWJlciBvZiBzaGFyZHMgdG8gdXNlIGJhc2VkIHVwb24gdGhlIG1heCBjb25jdXJyZW5jeS4gKi9cbiAgY2FsY3VsYXRlVG90YWxTaGFyZHM6ICgpID0+IG51bWJlclxuICAvKiogRGV0ZXJtaW5lIHRoZSBpZCBvZiB0aGUgd29ya2VyIHdoaWNoIGlzIGhhbmRsaW5nIGEgc2hhcmQuICovXG4gIGNhbGN1bGF0ZVdvcmtlcklkOiAoc2hhcmRJZDogbnVtYmVyKSA9PiBudW1iZXJcbiAgLyoqIFByZXBhcmVzIGFsbCB0aGUgYnVja2V0cyB0aGF0IGFyZSBhdmFpbGFibGUgZm9yIGlkZW50aWZ5aW5nIHRoZSBzaGFyZHMuICovXG4gIHByZXBhcmVCdWNrZXRzOiAoKSA9PiB2b2lkXG4gIC8qKiBTdGFydCBpZGVudGlmeWluZyBhbGwgdGhlIHNoYXJkcy4gKi9cbiAgc3Bhd25TaGFyZHM6ICgpID0+IFByb21pc2U8dm9pZD5cbiAgLyoqIFNodXRkb3duIGFsbCBzaGFyZHMuICovXG4gIHNodXRkb3duOiAoY29kZTogbnVtYmVyLCByZWFzb246IHN0cmluZywgY2xlYXJSZXNoYXJkaW5nSW50ZXJ2YWw/OiBib29sZWFuKSA9PiBQcm9taXNlPHZvaWQ+XG4gIHNlbmRQYXlsb2FkOiAoc2hhcmRJZDogbnVtYmVyLCBwYXlsb2FkOiBTaGFyZFNvY2tldFJlcXVlc3QpID0+IFByb21pc2U8dm9pZD5cbiAgLyoqIEFsbG93cyB1c2VycyB0byBob29rIGluIGFuZCBjaGFuZ2UgdG8gY29tbXVuaWNhdGUgdG8gZGlmZmVyZW50IHdvcmtlcnMgYWNyb3NzIGRpZmZlcmVudCBzZXJ2ZXJzIG9yIGFueXRoaW5nIHRoZXkgbGlrZS4gRm9yIGV4YW1wbGUgdXNpbmcgcmVkaXMgcHVic3ViIHRvIHRhbGsgdG8gb3RoZXIgc2VydmVycy4gKi9cbiAgdGVsbFdvcmtlclRvSWRlbnRpZnk6ICh3b3JrZXJJZDogbnVtYmVyLCBzaGFyZElkOiBudW1iZXIsIGJ1Y2tldElkOiBudW1iZXIpID0+IFByb21pc2U8dm9pZD5cbiAgLyoqIFRlbGwgdGhlIG1hbmFnZXIgdG8gaWRlbnRpZnkgYSBTaGFyZC4gSWYgdGhpcyBTaGFyZCBpcyBub3QgYWxyZWFkeSBtYW5hZ2VkIHRoaXMgd2lsbCBhbHNvIGFkZCB0aGUgU2hhcmQgdG8gdGhlIG1hbmFnZXIuICovXG4gIGlkZW50aWZ5OiAoc2hhcmRJZDogbnVtYmVyKSA9PiBQcm9taXNlPHZvaWQ+XG4gIC8qKiBLaWxsIGEgc2hhcmQuIENsb3NlIGEgc2hhcmRzIGNvbm5lY3Rpb24gdG8gRGlzY29yZCdzIGdhdGV3YXkgKGlmIGFueSkgYW5kIHJlbW92ZSBpdCBmcm9tIHRoZSBtYW5hZ2VyLiAqL1xuICBraWxsOiAoc2hhcmRJZDogbnVtYmVyKSA9PiBQcm9taXNlPHZvaWQ+XG4gIC8qKiBUaGlzIGZ1bmN0aW9uIG1ha2VzIHN1cmUgdGhhdCB0aGUgYnVja2V0IGlzIGFsbG93ZWQgdG8gbWFrZSB0aGUgbmV4dCBpZGVudGlmeSByZXF1ZXN0LiAqL1xuICByZXF1ZXN0SWRlbnRpZnk6IChzaGFyZElkOiBudW1iZXIpID0+IFByb21pc2U8dm9pZD5cbiAgLyoqIENhbGN1bGF0ZXMgdGhlIG51bWJlciBvZiBzaGFyZHMgYmFzZWQgb24gdGhlIGd1aWxkIGlkIGFuZCB0b3RhbCBzaGFyZHMuICovXG4gIGNhbGN1bGF0ZVNoYXJkSWQ6IChndWlsZElkOiBCaWdTdHJpbmcsIHRvdGFsU2hhcmRzPzogbnVtYmVyKSA9PiBudW1iZXJcbiAgLyoqXG4gICAqIENvbm5lY3RzIHRoZSBib3QgdXNlciB0byBhIHZvaWNlIG9yIHN0YWdlIGNoYW5uZWwuXG4gICAqXG4gICAqIFRoaXMgZnVuY3Rpb24gc2VuZHMgdGhlIF9VcGRhdGUgVm9pY2UgU3RhdGVfIGdhdGV3YXkgY29tbWFuZCBvdmVyIHRoZSBnYXRld2F5IGJlaGluZCB0aGUgc2NlbmVzLlxuICAgKlxuICAgKiBAcGFyYW0gZ3VpbGRJZCAtIFRoZSBJRCBvZiB0aGUgZ3VpbGQgdGhlIHZvaWNlIGNoYW5uZWwgdG8gbGVhdmUgaXMgaW4uXG4gICAqIEBwYXJhbSBjaGFubmVsSWQgLSBUaGUgSUQgb2YgdGhlIGNoYW5uZWwgeW91IHdhbnQgdG8gam9pbi5cbiAgICpcbiAgICogQHJlbWFya3NcbiAgICogUmVxdWlyZXMgdGhlIGBDT05ORUNUYCBwZXJtaXNzaW9uLlxuICAgKlxuICAgKiBGaXJlcyBhIF9Wb2ljZSBTdGF0ZSBVcGRhdGVfIGdhdGV3YXkgZXZlbnQuXG4gICAqXG4gICAqIEBzZWUge0BsaW5rIGh0dHBzOi8vZGlzY29yZC5jb20vZGV2ZWxvcGVycy9kb2NzL3RvcGljcy9nYXRld2F5I3VwZGF0ZS12b2ljZS1zdGF0ZX1cbiAgICovXG4gIGpvaW5Wb2ljZUNoYW5uZWw6IChndWlsZElkOiBCaWdTdHJpbmcsIGNoYW5uZWxJZDogQmlnU3RyaW5nLCBvcHRpb25zPzogQXRMZWFzdE9uZTxPbWl0PFVwZGF0ZVZvaWNlU3RhdGUsICdndWlsZElkJyB8ICdjaGFubmVsSWQnPj4pID0+IFByb21pc2U8dm9pZD5cbiAgLyoqXG4gICAqIEVkaXRzIHRoZSBib3Qgc3RhdHVzIGluIGFsbCBzaGFyZHMgdGhhdCB0aGlzIGdhdGV3YXkgbWFuYWdlcy5cbiAgICpcbiAgICogQHBhcmFtIGRhdGEgVGhlIHN0YXR1cyBkYXRhIHRvIHNldCB0aGUgYm90cyBzdGF0dXMgdG8uXG4gICAqIEByZXR1cm5zIG5vdGhpbmdcbiAgICovXG4gIGVkaXRCb3RTdGF0dXM6IChkYXRhOiBTdGF0dXNVcGRhdGUpID0+IFByb21pc2U8dm9pZD5cbiAgLyoqXG4gICAqIEVkaXRzIHRoZSBib3QncyBzdGF0dXMgb24gb25lIHNoYXJkLlxuICAgKlxuICAgKiBAcGFyYW0gc2hhcmRJZCBUaGUgc2hhcmQgaWQgdG8gZWRpdCB0aGUgc3RhdHVzIGZvci5cbiAgICogQHBhcmFtIGRhdGEgVGhlIHN0YXR1cyBkYXRhIHRvIHNldCB0aGUgYm90cyBzdGF0dXMgdG8uXG4gICAqIEByZXR1cm5zIG5vdGhpbmdcbiAgICovXG4gIGVkaXRTaGFyZFN0YXR1czogKHNoYXJkSWQ6IG51bWJlciwgZGF0YTogU3RhdHVzVXBkYXRlKSA9PiBQcm9taXNlPHZvaWQ+XG4gIC8qKlxuICAgKiBGZXRjaGVzIHRoZSBsaXN0IG9mIG1lbWJlcnMgZm9yIGEgZ3VpbGQgb3ZlciB0aGUgZ2F0ZXdheS5cbiAgICpcbiAgICogQHBhcmFtIGd1aWxkSWQgLSBUaGUgSUQgb2YgdGhlIGd1aWxkIHRvIGdldCB0aGUgbGlzdCBvZiBtZW1iZXJzIGZvci5cbiAgICogQHBhcmFtIG9wdGlvbnMgLSBUaGUgcGFyYW1ldGVycyBmb3IgdGhlIGZldGNoaW5nIG9mIHRoZSBtZW1iZXJzLlxuICAgKlxuICAgKiBAcmVtYXJrc1xuICAgKiBJZiByZXF1ZXN0aW5nIHRoZSBlbnRpcmUgbWVtYmVyIGxpc3Q6XG4gICAqIC0gUmVxdWlyZXMgdGhlIGBHVUlMRF9NRU1CRVJTYCBpbnRlbnQuXG4gICAqXG4gICAqIElmIHJlcXVlc3RpbmcgcHJlc2VuY2VzICh7QGxpbmsgUmVxdWVzdEd1aWxkTWVtYmVycy5wcmVzZW5jZXMgfCBwcmVzZW5jZXN9IHNldCB0byBgdHJ1ZWApOlxuICAgKiAtIFJlcXVpcmVzIHRoZSBgR1VJTERfUFJFU0VOQ0VTYCBpbnRlbnQuXG4gICAqXG4gICAqIElmIHJlcXVlc3RpbmcgYSBwcmVmaXggKHtAbGluayBSZXF1ZXN0R3VpbGRNZW1iZXJzLnF1ZXJ5IHwgcXVlcnl9IG5vbi1gdW5kZWZpbmVkYCk6XG4gICAqIC0gUmV0dXJucyBhIG1heGltdW0gb2YgMTAwIG1lbWJlcnMuXG4gICAqXG4gICAqIElmIHJlcXVlc3RpbmcgYSB1c2VycyBieSBJRCAoe0BsaW5rIFJlcXVlc3RHdWlsZE1lbWJlcnMudXNlcklkcyB8IHVzZXJJZHN9IG5vbi1gdW5kZWZpbmVkYCk6XG4gICAqIC0gUmV0dXJucyBhIG1heGltdW0gb2YgMTAwIG1lbWJlcnMuXG4gICAqXG4gICAqIEZpcmVzIGEgX0d1aWxkIE1lbWJlcnMgQ2h1bmtfIGdhdGV3YXkgZXZlbnQgZm9yIGV2ZXJ5IDEwMDAgbWVtYmVycyBmZXRjaGVkLlxuICAgKlxuICAgKiBAc2VlIHtAbGluayBodHRwczovL2Rpc2NvcmQuY29tL2RldmVsb3BlcnMvZG9jcy90b3BpY3MvZ2F0ZXdheSNyZXF1ZXN0LWd1aWxkLW1lbWJlcnN9XG4gICAqL1xuICByZXF1ZXN0TWVtYmVyczogKGd1aWxkSWQ6IEJpZ1N0cmluZywgb3B0aW9ucz86IE9taXQ8UmVxdWVzdEd1aWxkTWVtYmVycywgJ2d1aWxkSWQnPikgPT4gUHJvbWlzZTxDYW1lbGl6ZTxEaXNjb3JkTWVtYmVyV2l0aFVzZXJbXT4+XG4gIC8qKlxuICAgKiBMZWF2ZXMgdGhlIHZvaWNlIGNoYW5uZWwgdGhlIGJvdCB1c2VyIGlzIGN1cnJlbnRseSBpbi5cbiAgICpcbiAgICogVGhpcyBmdW5jdGlvbiBzZW5kcyB0aGUgX1VwZGF0ZSBWb2ljZSBTdGF0ZV8gZ2F0ZXdheSBjb21tYW5kIG92ZXIgdGhlIGdhdGV3YXkgYmVoaW5kIHRoZSBzY2VuZXMuXG4gICAqXG4gICAqIEBwYXJhbSBndWlsZElkIC0gVGhlIElEIG9mIHRoZSBndWlsZCB0aGUgdm9pY2UgY2hhbm5lbCB0byBsZWF2ZSBpcyBpbi5cbiAgICpcbiAgICogQHJlbWFya3NcbiAgICogRmlyZXMgYSBfVm9pY2UgU3RhdGUgVXBkYXRlXyBnYXRld2F5IGV2ZW50LlxuICAgKlxuICAgKiBAc2VlIHtAbGluayBodHRwczovL2Rpc2NvcmQuY29tL2RldmVsb3BlcnMvZG9jcy90b3BpY3MvZ2F0ZXdheSN1cGRhdGUtdm9pY2Utc3RhdGV9XG4gICAqL1xuICBsZWF2ZVZvaWNlQ2hhbm5lbDogKGd1aWxkSWQ6IEJpZ1N0cmluZykgPT4gUHJvbWlzZTx2b2lkPlxuICAvKipcbiAgICogVXNlZCB0byByZXF1ZXN0IHNvdW5kYm9hcmQgc291bmRzIGZvciBhIGxpc3Qgb2YgZ3VpbGRzLlxuICAgKlxuICAgKiBUaGlzIGZ1bmN0aW9uIHNlbmRzIG11bHRpcGxlIChzZWUgcmVtYXJrcykgX1JlcXVlc3QgU291bmRib2FyZCBTb3VuZHNfIGdhdGV3YXkgY29tbWFuZCBvdmVyIHRoZSBnYXRld2F5IGJlaGluZCB0aGUgc2NlbmVzLlxuICAgKlxuICAgKiBAcGFyYW0gZ3VpbGRJZHMgLSBUaGUgZ3VpbGRzIHRvIGdldCB0aGUgc291bmRzIGZyb21cbiAgICpcbiAgICogQHJlbWFya3NcbiAgICogRmlyZXMgYSBfU291bmRib2FyZCBTb3VuZHNfIGdhdGV3YXkgZXZlbnQuXG4gICAqXG4gICAqIOKaoO+4jyBEaXNjb3JkIHdpbGwgc2VuZCB0aGUgX1NvdW5kYm9hcmQgU291bmRzXyBmb3IgZWFjaCBvZiB0aGUgZ3VpbGQgaWRzXG4gICAqIGhvd2V2ZXIgeW91IG1heSBub3QgcmVjZWl2ZSB0aGUgc2FtZSBudW1iZXIgb2YgZXZlbnRzIGFzIHRoZSBpZHMgcGFzc2VkIHRvIF9SZXF1ZXN0IFNvdW5kYm9hcmQgU291bmRzXyBmb3Igb25lIG9mIHRoZSBmb2xsb3dpbmcgcmVhc29uczpcbiAgICogLSBUaGUgYm90IGlzIG5vdCBpbiB0aGUgc2VydmVyIHByb3ZpZGVkXG4gICAqIC0gVGhlIHNoYXJkIHRoZSBtZXNzYWdlIGhhcyBiZWVuIHNlbnQgZnJvbSBkb2VzIG5vdCByZWNlaXZlIGV2ZW50cyBmb3IgdGhlIHNwZWNpZmllZCBndWlsZFxuICAgKlxuICAgKiBUbyBhdm9pZCB0aGlzIERpc2NvcmRlbm8gd2lsbCBhdXRvbWF0aWNhbGx5IHRyeSB0byBncm91cCB0aGUgaWRzIGJhc2VkIG9uIHdoYXQgc2hhcmQgdGhleSB3aWxsIG5lZWQgdG8gYmUgc2VudCwgYnV0IHRoaXMgaW52b2x2ZXMgc2VuZGluZyBtdWx0aXBsZSBtZXNzYWdlcyBpbiBtdWx0aXBsZSBzaGFyZHNcbiAgICpcbiAgICogQHNlZSB7QGxpbmsgaHR0cHM6Ly9kaXNjb3JkLmNvbS9kZXZlbG9wZXJzL2RvY3MvdG9waWNzL2dhdGV3YXktZXZlbnRzI3JlcXVlc3Qtc291bmRib2FyZC1zb3VuZHN9XG4gICAqL1xuICByZXF1ZXN0U291bmRib2FyZFNvdW5kczogKGd1aWxkSWRzOiBCaWdTdHJpbmdbXSkgPT4gUHJvbWlzZTx2b2lkPlxuICAvKiogVGhpcyBtYW5hZ2VycyBjYWNoZSByZWxhdGVkIHNldHRpbmdzLiAqL1xuICBjYWNoZToge1xuICAgIHJlcXVlc3RNZW1iZXJzOiB7XG4gICAgICAvKipcbiAgICAgICAqIFdoZXRoZXIgb3Igbm90IHJlcXVlc3QgbWVtYmVyIHJlcXVlc3RzIHNob3VsZCBiZSBjYWNoZWQuXG4gICAgICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgICAgICovXG4gICAgICBlbmFibGVkOiBib29sZWFuXG4gICAgICAvKiogVGhlIHBlbmRpbmcgcmVxdWVzdHMuICovXG4gICAgICBwZW5kaW5nOiBDb2xsZWN0aW9uPHN0cmluZywgUmVxdWVzdE1lbWJlclJlcXVlc3Q+XG4gICAgfVxuICB9XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmVxdWVzdE1lbWJlclJlcXVlc3Qge1xuICAvKiogVGhlIHVuaXF1ZSBub25jZSBmb3IgdGhpcyByZXF1ZXN0LiAqL1xuICBub25jZTogc3RyaW5nXG4gIC8qKiBUaGUgcmVzb2x2ZXIgaGFuZGxlciB0byBydW4gd2hlbiBhbGwgbWVtYmVycyBhcnJpdmUuICovXG4gIHJlc29sdmU6ICh2YWx1ZTogQ2FtZWxpemU8RGlzY29yZE1lbWJlcldpdGhVc2VyW10+IHwgUHJvbWlzZUxpa2U8Q2FtZWxpemU8RGlzY29yZE1lbWJlcldpdGhVc2VyW10+PikgPT4gdm9pZFxuICAvKiogVGhlIG1lbWJlcnMgdGhhdCBoYXZlIGFscmVhZHkgYXJyaXZlZCBmb3IgdGhpcyByZXF1ZXN0LiAqL1xuICBtZW1iZXJzOiBEaXNjb3JkTWVtYmVyV2l0aFVzZXJbXVxufVxuIl0sIm5hbWVzIjpbIkdhdGV3YXlJbnRlbnRzIiwiR2F0ZXdheU9wY29kZXMiLCJDb2xsZWN0aW9uIiwiZGVsYXkiLCJsb2dnZXIiLCJTaGFyZCIsIlNoYXJkU29ja2V0Q2xvc2VDb2RlcyIsImNyZWF0ZUdhdGV3YXlNYW5hZ2VyIiwib3B0aW9ucyIsImNvbm5lY3Rpb25PcHRpb25zIiwiY29ubmVjdGlvbiIsInVybCIsInNoYXJkcyIsInNlc3Npb25TdGFydExpbWl0IiwibWF4Q29uY3VycmVuY3kiLCJyZW1haW5pbmciLCJ0b3RhbCIsInJlc2V0QWZ0ZXIiLCJnYXRld2F5IiwiZXZlbnRzIiwiY29tcHJlc3MiLCJ0cmFuc3BvcnRDb21wcmVzc2lvbiIsImludGVudHMiLCJwcm9wZXJ0aWVzIiwib3MiLCJwcm9jZXNzIiwicGxhdGZvcm0iLCJicm93c2VyIiwiZGV2aWNlIiwidG9rZW4iLCJ2ZXJzaW9uIiwidG90YWxTaGFyZHMiLCJsYXN0U2hhcmRJZCIsImZpcnN0U2hhcmRJZCIsInRvdGFsV29ya2VycyIsInNoYXJkc1BlcldvcmtlciIsInNwYXduU2hhcmREZWxheSIsInByZWZlclNuYWtlQ2FzZSIsIk1hcCIsImJ1Y2tldHMiLCJjYWNoZSIsInJlcXVlc3RNZW1iZXJzIiwiZW5hYmxlZCIsInBlbmRpbmciLCJtYWtlUHJlc2VuY2UiLCJQcm9taXNlIiwicmVzb2x2ZSIsInVuZGVmaW5lZCIsInJlc2hhcmRpbmciLCJzaGFyZHNGdWxsUGVyY2VudGFnZSIsImNoZWNrSW50ZXJ2YWwiLCJwZW5kaW5nU2hhcmRzIiwiZ2V0U2Vzc2lvbkluZm8iLCJ1cGRhdGVHdWlsZHNTaGFyZElkIiwiY2hlY2tJZlJlc2hhcmRpbmdJc05lZWRlZCIsImRlYnVnIiwibmVlZGVkIiwiRXJyb3IiLCJzZXNzaW9uSW5mbyIsIkpTT04iLCJzdHJpbmdpZnkiLCJpbmZvIiwicGVyY2VudGFnZSIsInJlc2hhcmQiLCJjYWxjdWxhdGVUb3RhbFNoYXJkcyIsImNsZWFyIiwicHJlcGFyZUJ1Y2tldHMiLCJmb3JFYWNoIiwiYnVja2V0IiwiYnVja2V0SWQiLCJ3b3JrZXIiLCJ3b3JrZXJzIiwic2hhcmRJZCIsInF1ZXVlIiwidGVsbFdvcmtlclRvUHJlcGFyZSIsImlkIiwid29ya2VySWQiLCJzaGFyZCIsIm1lc3NhZ2UiLCJfc2hhcmQiLCJwYXlsb2FkIiwidCIsImQiLCJndWlsZHMiLCJtYXAiLCJnIiwicmVxdWVzdElkZW50aWZ5IiwiaWRlbnRpZnkiLCJzaGFyZElzUmVhZHkiLCJnZXQiLCJpZGVudGlmeVJlcXVlc3RzIiwic2hpZnQiLCJmb3J3YXJkVG9Cb3QiLCJzZXQiLCJwdXNoIiwidGhlbiIsInNoYXJkSXNQZW5kaW5nIiwic2l6ZSIsInZhbHVlcyIsImV2ZW50Iiwib2xkSGFuZGxlciIsIl8iLCJzaHV0ZG93biIsIlJlc2hhcmRlZCIsIk1hdGgiLCJjZWlsIiwiY2FsY3VsYXRlV29ya2VySWQiLCJtaW4iLCJpIiwiZmluZCIsInciLCJzb3J0IiwiYSIsImIiLCJzcGF3blNoYXJkcyIsImFsbCIsImVudHJpZXMiLCJ0ZWxsV29ya2VyVG9JZGVudGlmeSIsImNsZWFySW50ZXJ2YWwiLCJjaGVja0ludGVydmFsSWQiLCJ3YXJuIiwic2V0SW50ZXJ2YWwiLCJyZXNoYXJkaW5nSW5mbyIsImNvZGUiLCJyZWFzb24iLCJjbGVhclJlc2hhcmRpbmdJbnRlcnZhbCIsImNsb3NlIiwic2VuZFBheWxvYWQiLCJzZW5kIiwia2lsbCIsImRlbGV0ZSIsIl9zaGFyZElkIiwiY2FsY3VsYXRlU2hhcmRJZCIsImd1aWxkSWQiLCJOdW1iZXIiLCJCaWdJbnQiLCJqb2luVm9pY2VDaGFubmVsIiwiY2hhbm5lbElkIiwib3AiLCJWb2ljZVN0YXRlVXBkYXRlIiwiZ3VpbGRfaWQiLCJ0b1N0cmluZyIsImNoYW5uZWxfaWQiLCJzZWxmX211dGUiLCJzZWxmTXV0ZSIsInNlbGZfZGVhZiIsInNlbGZEZWFmIiwiZWRpdEJvdFN0YXR1cyIsImRhdGEiLCJlZGl0U2hhcmRTdGF0dXMiLCJQcmVzZW5jZVVwZGF0ZSIsInNpbmNlIiwiYWZrIiwiYWN0aXZpdGllcyIsInN0YXR1cyIsImxpbWl0IiwiR3VpbGRNZW1iZXJzIiwidXNlcklkcyIsImxlbmd0aCIsIm1lbWJlcnMiLCJub25jZSIsInJlamVjdCIsIlJlcXVlc3RHdWlsZE1lbWJlcnMiLCJxdWVyeSIsInByZXNlbmNlcyIsInVzZXJfaWRzIiwibGVhdmVWb2ljZUNoYW5uZWwiLCJyZXF1ZXN0U291bmRib2FyZFNvdW5kcyIsImd1aWxkSWRzIiwiaWRzIiwiUmVxdWVzdFNvdW5kYm9hcmRTb3VuZHMiLCJndWlsZF9pZHMiXSwibWFwcGluZ3MiOiJBQUFBLFNBT0VBLGNBQWMsRUFDZEMsY0FBYyxRQUVULG9CQUFtQjtBQUMxQixTQUFTQyxVQUFVLEVBQUVDLEtBQUssRUFBRUMsTUFBTSxRQUFRLG9CQUFtQjtBQUM3RCxPQUFPQyxXQUFXLGFBQVk7QUFDOUIsU0FHRUMscUJBQXFCLFFBS2hCLGFBQVk7QUFFbkIsT0FBTyxTQUFTQyxxQkFBcUJDLE9BQW9DO0lBQ3ZFLE1BQU1DLG9CQUFvQkQsUUFBUUUsVUFBVSxJQUFJO1FBQzlDQyxLQUFLO1FBQ0xDLFFBQVE7UUFDUkMsbUJBQW1CO1lBQ2pCQyxnQkFBZ0I7WUFDaEJDLFdBQVc7WUFDWEMsT0FBTztZQUNQQyxZQUFZLE9BQU8sS0FBSyxLQUFLO1FBQy9CO0lBQ0Y7SUFFQSxNQUFNQyxVQUEwQjtRQUM5QkMsUUFBUVgsUUFBUVcsTUFBTSxJQUFJLENBQUM7UUFDM0JDLFVBQVVaLFFBQVFZLFFBQVEsSUFBSTtRQUM5QkMsc0JBQXNCYixRQUFRYSxvQkFBb0IsSUFBSTtRQUN0REMsU0FBU2QsUUFBUWMsT0FBTyxJQUFJO1FBQzVCQyxZQUFZO1lBQ1ZDLElBQUloQixRQUFRZSxVQUFVLEVBQUVDLE1BQU1DLFFBQVFDLFFBQVE7WUFDOUNDLFNBQVNuQixRQUFRZSxVQUFVLEVBQUVJLFdBQVc7WUFDeENDLFFBQVFwQixRQUFRZSxVQUFV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
|