nodeconfigloder 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +504 -0
- package/dist/configloder.bundle.js +8053 -0
- package/dist/conftool.bundle.js +11645 -0
- package/dist/finddifferences.bundle.js +396 -0
- package/dist/serializer.bundle.js +4248 -0
- package/dist/src/configloder/ConfigLoder.d.ts +92 -0
- package/dist/src/configloder/ConfigLoder.d.ts.map +1 -0
- package/dist/src/configloder/FindDifferences.d.ts +152 -0
- package/dist/src/configloder/FindDifferences.d.ts.map +1 -0
- package/dist/src/configloder/Serializer.d.ts +108 -0
- package/dist/src/configloder/Serializer.d.ts.map +1 -0
- package/dist/src/tools/index.d.ts +2 -0
- package/dist/src/tools/index.d.ts.map +1 -0
- package/dist/test/unit/configloder/ConfigLoder.test.d.ts +11 -0
- package/dist/test/unit/configloder/ConfigLoder.test.d.ts.map +1 -0
- package/dist/test/unit/configloder/FindDifferences.test.d.ts +11 -0
- package/dist/test/unit/configloder/FindDifferences.test.d.ts.map +1 -0
- package/dist/test/unit/configloder/Serializer.test.d.ts +11 -0
- package/dist/test/unit/configloder/Serializer.test.d.ts.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
(function webpackUniversalModuleDefinition(root, factory) {
|
|
3
|
+
if(typeof exports === 'object' && typeof module === 'object')
|
|
4
|
+
module.exports = factory();
|
|
5
|
+
else if(typeof define === 'function' && define.amd)
|
|
6
|
+
define([], factory);
|
|
7
|
+
else if(typeof exports === 'object')
|
|
8
|
+
exports["configloder"] = factory();
|
|
9
|
+
else
|
|
10
|
+
root["configloder"] = factory();
|
|
11
|
+
})(global, () => {
|
|
12
|
+
return /******/ (() => { // webpackBootstrap
|
|
13
|
+
/******/ "use strict";
|
|
14
|
+
/******/ var __webpack_modules__ = ({
|
|
15
|
+
|
|
16
|
+
/***/ "crypto"
|
|
17
|
+
/*!*************************!*\
|
|
18
|
+
!*** external "crypto" ***!
|
|
19
|
+
\*************************/
|
|
20
|
+
(module) {
|
|
21
|
+
|
|
22
|
+
module.exports = require("crypto");
|
|
23
|
+
|
|
24
|
+
/***/ },
|
|
25
|
+
|
|
26
|
+
/***/ "events"
|
|
27
|
+
/*!*************************!*\
|
|
28
|
+
!*** external "events" ***!
|
|
29
|
+
\*************************/
|
|
30
|
+
(module) {
|
|
31
|
+
|
|
32
|
+
module.exports = require("events");
|
|
33
|
+
|
|
34
|
+
/***/ }
|
|
35
|
+
|
|
36
|
+
/******/ });
|
|
37
|
+
/************************************************************************/
|
|
38
|
+
/******/ // The module cache
|
|
39
|
+
/******/ var __webpack_module_cache__ = {};
|
|
40
|
+
/******/
|
|
41
|
+
/******/ // The require function
|
|
42
|
+
/******/ function __webpack_require__(moduleId) {
|
|
43
|
+
/******/ // Check if module is in cache
|
|
44
|
+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
45
|
+
/******/ if (cachedModule !== undefined) {
|
|
46
|
+
/******/ return cachedModule.exports;
|
|
47
|
+
/******/ }
|
|
48
|
+
/******/ // Check if module exists (development only)
|
|
49
|
+
/******/ if (__webpack_modules__[moduleId] === undefined) {
|
|
50
|
+
/******/ var e = new Error("Cannot find module '" + moduleId + "'");
|
|
51
|
+
/******/ e.code = 'MODULE_NOT_FOUND';
|
|
52
|
+
/******/ throw e;
|
|
53
|
+
/******/ }
|
|
54
|
+
/******/ // Create a new module (and put it into the cache)
|
|
55
|
+
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
56
|
+
/******/ // no module.id needed
|
|
57
|
+
/******/ // no module.loaded needed
|
|
58
|
+
/******/ exports: {}
|
|
59
|
+
/******/ };
|
|
60
|
+
/******/
|
|
61
|
+
/******/ // Execute the module function
|
|
62
|
+
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
63
|
+
/******/
|
|
64
|
+
/******/ // Return the exports of the module
|
|
65
|
+
/******/ return module.exports;
|
|
66
|
+
/******/ }
|
|
67
|
+
/******/
|
|
68
|
+
/************************************************************************/
|
|
69
|
+
/******/ /* webpack/runtime/compat get default export */
|
|
70
|
+
/******/ (() => {
|
|
71
|
+
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
72
|
+
/******/ __webpack_require__.n = (module) => {
|
|
73
|
+
/******/ var getter = module && module.__esModule ?
|
|
74
|
+
/******/ () => (module['default']) :
|
|
75
|
+
/******/ () => (module);
|
|
76
|
+
/******/ __webpack_require__.d(getter, { a: getter });
|
|
77
|
+
/******/ return getter;
|
|
78
|
+
/******/ };
|
|
79
|
+
/******/ })();
|
|
80
|
+
/******/
|
|
81
|
+
/******/ /* webpack/runtime/define property getters */
|
|
82
|
+
/******/ (() => {
|
|
83
|
+
/******/ // define getter functions for harmony exports
|
|
84
|
+
/******/ __webpack_require__.d = (exports, definition) => {
|
|
85
|
+
/******/ for(var key in definition) {
|
|
86
|
+
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
87
|
+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
88
|
+
/******/ }
|
|
89
|
+
/******/ }
|
|
90
|
+
/******/ };
|
|
91
|
+
/******/ })();
|
|
92
|
+
/******/
|
|
93
|
+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
94
|
+
/******/ (() => {
|
|
95
|
+
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
96
|
+
/******/ })();
|
|
97
|
+
/******/
|
|
98
|
+
/******/ /* webpack/runtime/make namespace object */
|
|
99
|
+
/******/ (() => {
|
|
100
|
+
/******/ // define __esModule on exports
|
|
101
|
+
/******/ __webpack_require__.r = (exports) => {
|
|
102
|
+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
103
|
+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
104
|
+
/******/ }
|
|
105
|
+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
106
|
+
/******/ };
|
|
107
|
+
/******/ })();
|
|
108
|
+
/******/
|
|
109
|
+
/************************************************************************/
|
|
110
|
+
var __webpack_exports__ = {};
|
|
111
|
+
// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
|
|
112
|
+
(() => {
|
|
113
|
+
/*!****************************************!*\
|
|
114
|
+
!*** ./configloder/FindDifferences.ts ***!
|
|
115
|
+
\****************************************/
|
|
116
|
+
__webpack_require__.r(__webpack_exports__);
|
|
117
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
118
|
+
/* harmony export */ FindDifferences: () => (/* binding */ FindDifferences),
|
|
119
|
+
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
120
|
+
/* harmony export */ });
|
|
121
|
+
/* harmony import */ var events__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! events */ "events");
|
|
122
|
+
/* harmony import */ var events__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(events__WEBPACK_IMPORTED_MODULE_0__);
|
|
123
|
+
/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! crypto */ "crypto");
|
|
124
|
+
/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_1__);
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 処理名: 差分検出エンジン
|
|
129
|
+
*
|
|
130
|
+
* 処理概要:
|
|
131
|
+
* ハッシュツリーを使用して2つのJSON構造の差分を効率的に検出する。
|
|
132
|
+
* EventEmitterを継承し、差分を'difference'イベントで通知
|
|
133
|
+
*
|
|
134
|
+
* 実装理由:
|
|
135
|
+
* 大規模な設定ファイルの変更検出において、全体比較ではなく
|
|
136
|
+
* ハッシュツリーの活用で計算量を最小化するため
|
|
137
|
+
*/
|
|
138
|
+
class FindDifferences extends events__WEBPACK_IMPORTED_MODULE_0__.EventEmitter {
|
|
139
|
+
/**
|
|
140
|
+
* 処理名: コンストラクタ
|
|
141
|
+
*
|
|
142
|
+
* 処理概要:
|
|
143
|
+
* FindDifferencesインスタンスを初期化し、前回のハッシュツリーを空の状態で保持
|
|
144
|
+
*
|
|
145
|
+
* 実装理由:
|
|
146
|
+
* EventEmitterの初期化と、差分検出のための状態を保持するため
|
|
147
|
+
*/
|
|
148
|
+
constructor() {
|
|
149
|
+
super();
|
|
150
|
+
this.previousHashTree = { __hash: '' };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 処理名: ハッシュ値計算
|
|
154
|
+
*
|
|
155
|
+
* 処理概要:
|
|
156
|
+
* 与えられた値のSHA256ハッシュを計算する
|
|
157
|
+
*
|
|
158
|
+
* 実装理由:
|
|
159
|
+
* オブジェクトの内容をコンパクトに表現し、変更検出の基盤とするため
|
|
160
|
+
* @param {string} value ハッシュ対象の値
|
|
161
|
+
* @returns {string} ハッシュ文字列
|
|
162
|
+
* @private
|
|
163
|
+
*/
|
|
164
|
+
calculateHash(value) {
|
|
165
|
+
return crypto__WEBPACK_IMPORTED_MODULE_1__.createHash('sha256').update(value).digest('hex');
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 処理名: ハッシュツリー構築
|
|
169
|
+
*
|
|
170
|
+
* 処理概要:
|
|
171
|
+
* JSONデータから再帰的にハッシュツリーを構築する。
|
|
172
|
+
* 各ノードには__hashキーで当該ノード以下の変更を示すハッシュを保持
|
|
173
|
+
*
|
|
174
|
+
* 実装理由:
|
|
175
|
+
* 差分検出時に__hashの比較だけで下層の探索を短縮するため
|
|
176
|
+
* @param {unknown} data 構築対象のデータ
|
|
177
|
+
* @returns {HashTreeNode} ハッシュツリー
|
|
178
|
+
* @private
|
|
179
|
+
*/
|
|
180
|
+
buildHashTree(data) {
|
|
181
|
+
if (typeof data !== 'object' || data === null) {
|
|
182
|
+
// プリミティブ値の場合、そのままハッシュ化
|
|
183
|
+
return {
|
|
184
|
+
__hash: this.calculateHash(JSON.stringify(data)),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (Array.isArray(data)) {
|
|
188
|
+
// 配列の場合、各要素のハッシュ値を結合してハッシュ化
|
|
189
|
+
const childHashes = data.map((item) => this.buildHashTree(item));
|
|
190
|
+
const combinedHash = this.calculateHash(childHashes.map((h) => h.__hash).join(''));
|
|
191
|
+
return {
|
|
192
|
+
__hash: combinedHash,
|
|
193
|
+
...Object.fromEntries(childHashes.map((h, i) => [i.toString(), h])),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
if (typeof data === 'object' && data.type) {
|
|
197
|
+
// type値を持っているobjectの場合、そのままハッシュ化
|
|
198
|
+
return {
|
|
199
|
+
__hash: this.calculateHash(JSON.stringify(data)),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
// オブジェクトの場合、キーと値をソートしてハッシュ化
|
|
203
|
+
const childHashes = Object.keys(data)
|
|
204
|
+
.sort()
|
|
205
|
+
.reduce((tree, key) => {
|
|
206
|
+
tree[key] = this.buildHashTree(data[key]);
|
|
207
|
+
return tree;
|
|
208
|
+
}, {});
|
|
209
|
+
const combinedHash = this.calculateHash(Object.entries(childHashes)
|
|
210
|
+
.map(([key, value]) => `${key}:${value.__hash}`)
|
|
211
|
+
.join(''));
|
|
212
|
+
return { __hash: combinedHash, ...childHashes };
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* 処理名: 削除・更新キーの処理
|
|
216
|
+
* @param {HashTreeNode} hashTree ハッシュツリー
|
|
217
|
+
* @param {Record<string, unknown>} jsonData JSON データ
|
|
218
|
+
* @param {string} path パス
|
|
219
|
+
* @returns {void}
|
|
220
|
+
* @private
|
|
221
|
+
*/
|
|
222
|
+
processExistingKeys(hashTree, jsonData, path) {
|
|
223
|
+
for (const key in hashTree) {
|
|
224
|
+
if (key === '__hash')
|
|
225
|
+
continue;
|
|
226
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
227
|
+
if (!(key in jsonData)) {
|
|
228
|
+
this.emit('difference', {
|
|
229
|
+
type: 'removed',
|
|
230
|
+
path: currentPath,
|
|
231
|
+
});
|
|
232
|
+
delete hashTree[key];
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
this.processKeyValue(hashTree, jsonData, key, currentPath);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* 処理名: キー値の処理
|
|
241
|
+
* @param {HashTreeNode} hashTree ハッシュツリー
|
|
242
|
+
* @param {Record<string, unknown>} jsonData JSON データ
|
|
243
|
+
* @param {string} key キー
|
|
244
|
+
* @param {string} currentPath 現在のパス
|
|
245
|
+
* @returns {void}
|
|
246
|
+
* @private
|
|
247
|
+
*/
|
|
248
|
+
processKeyValue(hashTree, jsonData, key, currentPath) {
|
|
249
|
+
const jsonValue = jsonData[key];
|
|
250
|
+
if (typeof jsonValue === 'object' && jsonValue !== null) {
|
|
251
|
+
this.processObjectValue(hashTree, jsonValue, key, currentPath);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
this.processPrimitiveValue(hashTree, jsonValue, key, currentPath);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 処理名: オブジェクト値の処理
|
|
259
|
+
* @param {HashTreeNode} hashTree ハッシュツリー
|
|
260
|
+
* @param {Record<string, unknown>} jsonValue JSON 値
|
|
261
|
+
* @param {string} key キー
|
|
262
|
+
* @param {string} currentPath 現在のパス
|
|
263
|
+
* @returns {void}
|
|
264
|
+
* @private
|
|
265
|
+
*/
|
|
266
|
+
processObjectValue(hashTree, jsonValue, key, currentPath) {
|
|
267
|
+
if (jsonValue.type) {
|
|
268
|
+
const newPrimitiveHash = this.calculateHash(JSON.stringify(jsonValue));
|
|
269
|
+
if (hashTree[key].__hash !== newPrimitiveHash) {
|
|
270
|
+
this.emit('difference', {
|
|
271
|
+
type: 'modified',
|
|
272
|
+
path: currentPath,
|
|
273
|
+
});
|
|
274
|
+
hashTree[key].__hash = newPrimitiveHash;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
if (!hashTree[key] || typeof hashTree[key] !== 'object') {
|
|
279
|
+
hashTree[key] = { __hash: '' };
|
|
280
|
+
}
|
|
281
|
+
this.findDifferencesAndUpdate(hashTree[key], jsonValue, currentPath);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* 処理名: プリミティブ値の処理
|
|
286
|
+
* @param {HashTreeNode} hashTree ハッシュツリー
|
|
287
|
+
* @param {unknown} jsonValue JSON 値
|
|
288
|
+
* @param {string} key キー
|
|
289
|
+
* @param {string} currentPath 現在のパス
|
|
290
|
+
* @returns {void}
|
|
291
|
+
* @private
|
|
292
|
+
*/
|
|
293
|
+
processPrimitiveValue(hashTree, jsonValue, key, currentPath) {
|
|
294
|
+
const newPrimitiveHash = this.calculateHash(JSON.stringify(jsonValue));
|
|
295
|
+
if (hashTree[key].__hash !== newPrimitiveHash) {
|
|
296
|
+
this.emit('difference', {
|
|
297
|
+
type: 'modified',
|
|
298
|
+
path: currentPath,
|
|
299
|
+
});
|
|
300
|
+
hashTree[key].__hash = newPrimitiveHash;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* 処理名: 追加されたキーの処理
|
|
305
|
+
* @param {HashTreeNode} hashTree ハッシュツリー
|
|
306
|
+
* @param {Record<string, unknown>} jsonData JSON データ
|
|
307
|
+
* @param {string} path パス
|
|
308
|
+
* @returns {void}
|
|
309
|
+
* @private
|
|
310
|
+
*/
|
|
311
|
+
processAddedKeys(hashTree, jsonData, path) {
|
|
312
|
+
for (const key in jsonData) {
|
|
313
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
314
|
+
if (!(key in hashTree)) {
|
|
315
|
+
this.emit('difference', {
|
|
316
|
+
type: 'added',
|
|
317
|
+
path: currentPath,
|
|
318
|
+
});
|
|
319
|
+
const jsonValue = jsonData[key];
|
|
320
|
+
hashTree[key] =
|
|
321
|
+
typeof jsonValue === 'object' && jsonValue !== null
|
|
322
|
+
? this.buildHashTree(jsonValue)
|
|
323
|
+
: { __hash: this.calculateHash(JSON.stringify(jsonValue)) };
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* 処理名: 差分検出と更新
|
|
329
|
+
*
|
|
330
|
+
* 処理概要:
|
|
331
|
+
* 前回のハッシュツリーと新しいJSONデータを比較し、
|
|
332
|
+
* 追加・削除・更新されたパスを'difference'イベントで通知
|
|
333
|
+
*
|
|
334
|
+
* 実装理由:
|
|
335
|
+
* ハッシュ値の比較で不要な再帰を避け、変更箇所のみを検出するため
|
|
336
|
+
* @param {HashTreeNode} hashTree ハッシュツリー
|
|
337
|
+
* @param {unknown} jsonData JSON データ
|
|
338
|
+
* @param {string} path パス
|
|
339
|
+
* @returns {void}
|
|
340
|
+
* @private
|
|
341
|
+
*/
|
|
342
|
+
findDifferencesAndUpdate(hashTree, jsonData, path = '') {
|
|
343
|
+
const newHash = this.calculateHash(JSON.stringify(jsonData));
|
|
344
|
+
if (hashTree.__hash === newHash) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const data = jsonData;
|
|
348
|
+
this.processExistingKeys(hashTree, data, path);
|
|
349
|
+
this.processAddedKeys(hashTree, data, path);
|
|
350
|
+
hashTree.__hash = this.calculateHash(Object.entries(hashTree)
|
|
351
|
+
.filter(([key]) => key !== '__hash')
|
|
352
|
+
.map(([key, value]) => `${key}:${value.__hash}`)
|
|
353
|
+
.join(''));
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* 処理名: ハッシュツリー初期化
|
|
357
|
+
*
|
|
358
|
+
* 処理概要:
|
|
359
|
+
* 初回読み込み時にハッシュツリーを構築し、前回状態として保持
|
|
360
|
+
*
|
|
361
|
+
* 実装理由:
|
|
362
|
+
* 次回の差分検出の基準となるベースラインを作成するため
|
|
363
|
+
* @param {unknown} jsonData 初回のJSONデータ
|
|
364
|
+
* @returns {HashTreeNode} 構築されたハッシュツリー
|
|
365
|
+
*/
|
|
366
|
+
initialize(jsonData) {
|
|
367
|
+
return (this.previousHashTree = this.buildHashTree(jsonData)); // 初期ハッシュツリー構築
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* 処理名: 差分検出
|
|
371
|
+
*
|
|
372
|
+
* 処理概要:
|
|
373
|
+
* 新しいJSONデータと前回のハッシュツリーを比較し、
|
|
374
|
+
* 差分を検出して'difference'イベントで通知。
|
|
375
|
+
* 内部的にハッシュツリーを更新して次回検出に備える
|
|
376
|
+
*
|
|
377
|
+
* 実装理由:
|
|
378
|
+
* ファイル監視やリアルタイム変更検出のため、継続的に差分を検出するため
|
|
379
|
+
* @param {unknown} jsonData 新しいJSONデータ
|
|
380
|
+
* @returns {HashTreeNode} 更新上のハッシュツリー
|
|
381
|
+
*/
|
|
382
|
+
detectChanges(jsonData) {
|
|
383
|
+
this.findDifferencesAndUpdate(this.previousHashTree, jsonData);
|
|
384
|
+
return this.previousHashTree;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (FindDifferences);
|
|
388
|
+
|
|
389
|
+
})();
|
|
390
|
+
|
|
391
|
+
__webpack_exports__ = __webpack_exports__["default"];
|
|
392
|
+
/******/ return __webpack_exports__;
|
|
393
|
+
/******/ })()
|
|
394
|
+
;
|
|
395
|
+
});
|
|
396
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,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QUFHLEtBQUssUUFBUSxDQUFDO2FBQ25DLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxFQUFFLEVBQUUsQ0FBQyxHQUFHLEdBQUcsSUFBSyxLQUFzQixDQUFDLE1BQU0sRUFBRSxDQUFDO2FBQ2pFLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FDWixDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7Ozs7O09BVUc7SUFDSCxVQUFVLENBQUMsUUFBaUI7UUFDMUIsT0FBTyxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxjQUFjO0lBQy9FLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7O09BWUc7SUFDSCxhQUFhLENBQUMsUUFBaUI7UUFDN0IsSUFBSSxDQUFDLHdCQUF3QixDQUFDLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxRQUFRLENBQUMsQ0FBQztRQUMvRCxPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQztJQUMvQixDQUFDO0NBQ0Y7QUFFRCxpRUFBZSxlQUFlLEVBQUMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9jb25maWdsb2Rlci93ZWJwYWNrL3VuaXZlcnNhbE1vZHVsZURlZmluaXRpb24iLCJ3ZWJwYWNrOi8vY29uZmlnbG9kZXIvZXh0ZXJuYWwgbm9kZS1jb21tb25qcyBcImNyeXB0b1wiIiwid2VicGFjazovL2NvbmZpZ2xvZGVyL2V4dGVybmFsIG5vZGUtY29tbW9uanMgXCJldmVudHNcIiIsIndlYnBhY2s6Ly9jb25maWdsb2Rlci93ZWJwYWNrL2Jvb3RzdHJhcCIsIndlYnBhY2s6Ly9jb25maWdsb2Rlci93ZWJwYWNrL3J1bnRpbWUvY29tcGF0IGdldCBkZWZhdWx0IGV4cG9ydCIsIndlYnBhY2s6Ly9jb25maWdsb2Rlci93ZWJwYWNrL3J1bnRpbWUvZGVmaW5lIHByb3BlcnR5IGdldHRlcnMiLCJ3ZWJwYWNrOi8vY29uZmlnbG9kZXIvd2VicGFjay9ydW50aW1lL2hhc093blByb3BlcnR5IHNob3J0aGFuZCIsIndlYnBhY2s6Ly9jb25maWdsb2Rlci93ZWJwYWNrL3J1bnRpbWUvbWFrZSBuYW1lc3BhY2Ugb2JqZWN0Iiwid2VicGFjazovL2NvbmZpZ2xvZGVyLy4vY29uZmlnbG9kZXIvRmluZERpZmZlcmVuY2VzLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbiB3ZWJwYWNrVW5pdmVyc2FsTW9kdWxlRGVmaW5pdGlvbihyb290LCBmYWN0b3J5KSB7XG5cdGlmKHR5cGVvZiBleHBvcnRzID09PSAnb2JqZWN0JyAmJiB0eXBlb2YgbW9kdWxlID09PSAnb2JqZWN0Jylcblx0XHRtb2R1bGUuZXhwb3J0cyA9IGZhY3RvcnkoKTtcblx0ZWxzZSBpZih0eXBlb2YgZGVmaW5lID09PSAnZnVuY3Rpb24nICYmIGRlZmluZS5hbWQpXG5cdFx0ZGVmaW5lKFtdLCBmYWN0b3J5KTtcblx0ZWxzZSBpZih0eXBlb2YgZXhwb3J0cyA9PT0gJ29iamVjdCcpXG5cdFx0ZXhwb3J0c1tcImNvbmZpZ2xvZGVyXCJdID0gZmFjdG9yeSgpO1xuXHRlbHNlXG5cdFx0cm9vdFtcImNvbmZpZ2xvZGVyXCJdID0gZmFjdG9yeSgpO1xufSkoZ2xvYmFsLCAoKSA9PiB7XG5yZXR1cm4gIiwibW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKFwiY3J5cHRvXCIpOyIsIm1vZHVsZS5leHBvcnRzID0gcmVxdWlyZShcImV2ZW50c1wiKTsiLCIvLyBUaGUgbW9kdWxlIGNhY2hlXG52YXIgX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fID0ge307XG5cbi8vIFRoZSByZXF1aXJlIGZ1bmN0aW9uXG5mdW5jdGlvbiBfX3dlYnBhY2tfcmVxdWlyZV9fKG1vZHVsZUlkKSB7XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuXHR2YXIgY2FjaGVkTW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXTtcblx0aWYgKGNhY2hlZE1vZHVsZSAhPT0gdW5kZWZpbmVkKSB7XG5cdFx0cmV0dXJuIGNhY2hlZE1vZHVsZS5leHBvcnRzO1xuXHR9XG5cdC8vIENoZWNrIGlmIG1vZHVsZSBleGlzdHMgKGRldmVsb3BtZW50IG9ubHkpXG5cdGlmIChfX3dlYnBhY2tfbW9kdWxlc19fW21vZHVsZUlkXSA9PT0gdW5kZWZpbmVkKSB7XG5cdFx0dmFyIGUgPSBuZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiICsgbW9kdWxlSWQgKyBcIidcIik7XG5cdFx0ZS5jb2RlID0gJ01PRFVMRV9OT1RfRk9VTkQnO1xuXHRcdHRocm93IGU7XG5cdH1cblx0Ly8gQ3JlYXRlIGEgbmV3IG1vZHVsZSAoYW5kIHB1dCBpdCBpbnRvIHRoZSBjYWNoZSlcblx0dmFyIG1vZHVsZSA9IF9fd2VicGFja19tb2R1bGVfY2FjaGVfX1ttb2R1bGVJZF0gPSB7XG5cdFx0Ly8gbm8gbW9kdWxlLmlkIG5lZWRlZFxuXHRcdC8vIG5vIG1vZHVsZS5sb2FkZWQgbmVlZGVkXG5cdFx0ZXhwb3J0czoge31cblx0fTtcblxuXHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cblx0X193ZWJwYWNrX21vZHVsZXNfX1ttb2R1bGVJZF0obW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cblx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcblx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xufVxuXG4iLCIvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuX193ZWJwYWNrX3JlcXVpcmVfXy5uID0gKG1vZHVsZSkgPT4ge1xuXHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cblx0XHQoKSA9PiAobW9kdWxlWydkZWZhdWx0J10pIDpcblx0XHQoKSA9PiAobW9kdWxlKTtcblx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgeyBhOiBnZXR0ZXIgfSk7XG5cdHJldHVybiBnZXR0ZXI7XG59OyIsIi8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb25zIGZvciBoYXJtb255IGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uZCA9IChleHBvcnRzLCBkZWZpbml0aW9uKSA9PiB7XG5cdGZvcih2YXIga2V5IGluIGRlZmluaXRpb24pIHtcblx0XHRpZihfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZGVmaW5pdGlvbiwga2V5KSAmJiAhX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIGtleSkpIHtcblx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBrZXksIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBkZWZpbml0aW9uW2tleV0gfSk7XG5cdFx0fVxuXHR9XG59OyIsIl9fd2VicGFja19yZXF1aXJlX18ubyA9IChvYmosIHByb3ApID0+IChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBwcm9wKSkiLCIvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG5fX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSAoZXhwb3J0cykgPT4ge1xuXHRpZih0eXBlb2YgU3ltYm9sICE9PSAndW5kZWZpbmVkJyAmJiBTeW1ib2wudG9TdHJpbmdUYWcpIHtcblx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgU3ltYm9sLnRvU3RyaW5nVGFnLCB7IHZhbHVlOiAnTW9kdWxlJyB9KTtcblx0fVxuXHRPYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgJ19fZXNNb2R1bGUnLCB7IHZhbHVlOiB0cnVlIH0pO1xufTsiLCJpbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICdldmVudHMnO1xyXG5pbXBvcnQgKiBhcyBjcnlwdG8gZnJvbSAnY3J5cHRvJztcclxuXHJcbi8qKlxyXG4gKiDjg4/jg4Pjgrfjg6Xjg4Tjg6rjg7zjga7jg47jg7zjg4nlnotcclxuICovXHJcbmludGVyZmFjZSBIYXNoVHJlZU5vZGUge1xyXG4gIF9faGFzaDogc3RyaW5nO1xyXG4gIFtrZXk6IHN0cmluZ106IHVua25vd247XHJcbn1cclxuXHJcbi8qKlxyXG4gKiDlt67liIbjgqTjg5njg7Pjg4jjga7lnotcclxuICovXHJcbmludGVyZmFjZSBEaWZmZXJlbmNlRXZlbnQge1xyXG4gIHR5cGU6ICdhZGRlZCcgfCAncmVtb3ZlZCcgfCAnbW9kaWZpZWQnO1xyXG4gIHBhdGg6IHN0cmluZztcclxufVxyXG5cclxuLyoqXHJcbiAqIOWHpueQhuWQjTog5beu5YiG5qSc5Ye644Ko44Oz44K444OzXHJcbiAqXHJcbiAqIOWHpueQhuamguimgTpcclxuICog44OP44OD44K344Ol44OE44Oq44O844KS5L2/55So44GX44GmMuOBpOOBrkpTT07mp4vpgKDjga7lt67liIbjgpLlirnnjofnmoTjgavmpJzlh7rjgZnjgovjgIJcclxuICogRXZlbnRFbWl0dGVy44KS57aZ5om/44GX44CB5beu5YiG44KSJ2RpZmZlcmVuY2Un44Kk44OZ44Oz44OI44Gn6YCa55+lXHJcbiAqXHJcbiAqIOWun+ijheeQhueUsTpcclxuICog5aSn6KaP5qih44Gq6Kit5a6a44OV44Kh44Kk44Or44Gu5aSJ5pu05qSc5Ye644Gr44GK44GE44Gm44CB5YWo5L2T5q+U6LyD44Gn44Gv44Gq44GPXHJcbiAqIOODj+ODg+OCt+ODpeODhOODquODvOOBrua0u+eUqOOBp+ioiOeul+mHj+OCkuacgOWwj+WMluOBmeOCi+OBn+OCgVxyXG4gKi9cclxuZXhwb3J0IGNsYXNzIEZpbmREaWZmZXJlbmNlcyBleHRlbmRzIEV2ZW50RW1pdHRlciB7XHJcbiAgcHJpdmF0ZSBwcmV2aW91c0hhc2hUcmVlOiBIYXNoVHJlZU5vZGU7XHJcblxyXG4gIC8qKlxyXG4gICAqIOWHpueQhuWQjTog44Kz44Oz44K544OI44Op44Kv44K/XHJcbiAgICpcclxuICAgKiDlh6bnkIbmpoLopoE6XHJcbiAgICogRmluZERpZmZlcmVuY2Vz44Kk44Oz44K544K/44Oz44K544KS5Yid5pyf5YyW44GX44CB5YmN5Zue44Gu44OP44OD44K344Ol44OE44Oq44O844KS56m644Gu54q25oWL44Gn5L+d5oyBXHJcbiAgICpcclxuICAgKiDlrp/oo4XnkIbnlLE6XHJcbiAgICogRXZlbnRFbWl0dGVy44Gu5Yid5pyf5YyW44Go44CB5beu5YiG5qSc5Ye644Gu44Gf44KB44Gu54q25oWL44KS5L+d5oyB44GZ44KL44Gf44KBXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IoKSB7XHJcbiAgICBzdXBlcigpO1xyXG4gICAgdGhpcy5wcmV2aW91c0hhc2hUcmVlID0geyBfX2hhc2g6ICcnIH07XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDlh6bnkIblkI06IOODj+ODg+OCt+ODpeWApOioiOeul1xyXG4gICAqXHJcbiAgICog5Yem55CG5qaC6KaBOlxyXG4gICAqIOS4juOBiOOCieOCjOOBn+WApOOBrlNIQTI1NuODj+ODg+OCt+ODpeOCkuioiOeul+OBmeOCi1xyXG4gICAqXHJcbiAgICog5a6f6KOF55CG55SxOlxyXG4gICAqIOOCquODluOCuOOCp+OCr+ODiOOBruWGheWuueOCkuOCs+ODs+ODkeOCr+ODiOOBq+ihqOePvuOBl+OAgeWkieabtOaknOWHuuOBruWfuuebpOOBqOOBmeOCi+OBn+OCgVxyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZSDjg4/jg4Pjgrfjg6Xlr77osaHjga7lgKRcclxuICAgKiBAcmV0dXJucyB7c3RyaW5nfSDjg4/jg4Pjgrfjg6XmloflrZfliJdcclxuICAgKiBAcHJpdmF0ZVxyXG4gICAqL1xyXG4gIHByaXZhdGUgY2FsY3VsYXRlSGFzaCh2YWx1ZTogc3RyaW5nKTogc3RyaW5nIHtcclxuICAgIHJldHVybiBjcnlwdG8uY3JlYXRlSGFzaCgnc2hhMjU2JykudXBkYXRlKHZhbHVlKS5kaWdlc3QoJ2hleCcpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICog5Yem55CG5ZCNOiDjg4/jg4Pjgrfjg6Xjg4Tjg6rjg7zmp4vnr4lcclxuICAgKlxyXG4gICAqIOWHpueQhuamguimgTpcclxuICAgKiBKU09O44OH44O844K/44GL44KJ5YaN5biw55qE44Gr44OP44OD44K344Ol44OE44Oq44O844KS5qeL56+J44GZ44KL44CCXHJcbiAgICog5ZCE44OO44O844OJ44Gr44GvX19oYXNo44Kt44O844Gn5b2T6Kmy44OO44O844OJ5Lul5LiL44Gu5aSJ5pu044KS56S644GZ44OP44OD44K344Ol44KS5L+d5oyBXHJcbiAgICpcclxuICAgKiDlrp/oo4XnkIbnlLE6XHJcbiAgICog5beu5YiG5qSc5Ye65pmC44GrX19oYXNo44Gu5q+U6LyD44Gg44GR44Gn5LiL5bGk44Gu5o6i57Si44KS55+t57iu44GZ44KL44Gf44KBXHJcbiAgICogQHBhcmFtIHt1bmtub3dufSBkYXRhIOani+evieWvvuixoeOBruODh+ODvOOCv1xyXG4gICAqIEByZXR1cm5zIHtIYXNoVHJlZU5vZGV9IOODj+ODg+OCt+ODpeODhOODquODvFxyXG4gICAqIEBwcml2YXRlXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBidWlsZEhhc2hUcmVlKGRhdGE6IHVua25vd24pOiBIYXNoVHJlZU5vZGUge1xyXG4gICAgaWYgKHR5cGVvZiBkYXRhICE9PSAnb2JqZWN0JyB8fCBkYXRhID09PSBudWxsKSB7XHJcbiAgICAgIC8vIOODl+ODquODn+ODhuOCo+ODluWApOOBruWgtOWQiOOAgeOBneOBruOBvuOBvuODj+ODg+OCt+ODpeWMllxyXG4gICAgICByZXR1cm4ge1xyXG4gICAgICAgIF9faGFzaDogdGhpcy5jYWxjdWxhdGVIYXNoKEpTT04uc3RyaW5naWZ5KGRhdGEpKSxcclxuICAgICAgfTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoQXJyYXkuaXNBcnJheShkYXRhKSkge1xyXG4gICAgICAvLyDphY3liJfjga7loLTlkIjjgIHlkITopoHntKDjga7jg4/jg4Pjgrfjg6XlgKTjgpLntZDlkIjjgZfjgabjg4/jg4Pjgrfjg6XljJZcclxuICAgICAgY29uc3QgY2hpbGRIYXNoZXMgPSBkYXRhLm1hcCgoaXRlbSkgPT4gdGhpcy5idWlsZEhhc2hUcmVlKGl0ZW0pKTtcclxuICAgICAgY29uc3QgY29tYmluZWRIYXNoID0gdGhpcy5jYWxjdWxhdGVIYXNoKGNoaWxkSGFzaGVzLm1hcCgoaCkgPT4gaC5fX2hhc2gpLmpvaW4oJycpKTtcclxuICAgICAgcmV0dXJuIHtcclxuICAgICAgICBfX2hhc2g6IGNvbWJpbmVkSGFzaCxcclxuICAgICAgICAuLi5PYmplY3QuZnJvbUVudHJpZXMoY2hpbGRIYXNoZXMubWFwKChoLCBpKSA9PiBbaS50b1N0cmluZygpLCBoXSkpLFxyXG4gICAgICB9IGFzIEhhc2hUcmVlTm9kZTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAodHlwZW9mIGRhdGEgPT09ICdvYmplY3QnICYmIChkYXRhIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KS50eXBlKSB7XHJcbiAgICAgIC8vIHR5cGXlgKTjgpLmjIHjgaPjgabjgYTjgotvYmplY3Tjga7loLTlkIjjgIHjgZ3jga7jgb7jgb7jg4/jg4Pjgrfjg6XljJZcclxuICAgICAgcmV0dXJuIHtcclxuICAgICAgICBfX2hhc2g6IHRoaXMuY2FsY3VsYXRlSGFzaChKU09OLnN0cmluZ2lmeShkYXRhKSksXHJcbiAgICAgIH07XHJcbiAgICB9XHJcblxyXG4gICAgLy8g44Kq44OW44K444Kn44Kv44OI44Gu5aC05ZCI44CB44Kt44O844Go5YCk44KS44K944O844OI44GX44Gm44OP44OD44K344Ol5YyWXHJcbiAgICBjb25zdCBjaGlsZEhhc2hlcyA9IE9iamVjdC5rZXlzKGRhdGEpXHJcbiAgICAgIC5zb3J0KClcclxuICAgICAgLnJlZHVjZSgodHJlZTogUmVjb3JkPHN0cmluZywgSGFzaFRyZWVOb2RlPiwga2V5KSA9PiB7XHJcbiAgICAgICAgdHJlZVtrZXldID0gdGhpcy5idWlsZEhhc2hUcmVlKChkYXRhIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KVtrZXldKTtcclxuICAgICAgICByZXR1cm4gdHJlZTtcclxuICAgICAgfSwge30pO1xyXG5cclxuICAgIGNvbnN0IGNvbWJpbmVkSGFzaCA9IHRoaXMuY2FsY3VsYXRlSGFzaChcclxuICAgICAgT2JqZWN0LmVudHJpZXMoY2hpbGRIYXNoZXMpXHJcbiAgICAgICAgLm1hcCgoW2tleSwgdmFsdWVdKSA9PiBgJHtrZXl9OiR7dmFsdWUuX19oYXNofWApXHJcbiAgICAgICAgLmpvaW4oJycpXHJcbiAgICApO1xyXG4gICAgcmV0dXJuIHsgX19oYXNoOiBjb21iaW5lZEhhc2gsIC4uLmNoaWxkSGFzaGVzIH07XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDlh6bnkIblkI06IOWJiumZpOODu+abtOaWsOOCreODvOOBruWHpueQhlxyXG4gICAqIEBwYXJhbSB7SGFzaFRyZWVOb2RlfSBoYXNoVHJlZSDjg4/jg4Pjgrfjg6Xjg4Tjg6rjg7xcclxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSBqc29uRGF0YSBKU09OIOODh+ODvOOCv1xyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXRoIOODkeOCuVxyXG4gICAqIEByZXR1cm5zIHt2b2lkfVxyXG4gICAqIEBwcml2YXRlXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBwcm9jZXNzRXhpc3RpbmdLZXlzKFxyXG4gICAgaGFzaFRyZWU6IEhhc2hUcmVlTm9kZSxcclxuICAgIGpzb25EYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcclxuICAgIHBhdGg6IHN0cmluZ1xyXG4gICk6IHZvaWQge1xyXG4gICAgZm9yIChjb25zdCBrZXkgaW4gaGFzaFRyZWUpIHtcclxuICAgICAgaWYgKGtleSA9PT0gJ19faGFzaCcpIGNvbnRpbnVlO1xyXG4gICAgICBjb25zdCBjdXJyZW50UGF0aCA9IHBhdGggPyBgJHtwYXRofS4ke2tleX1gIDoga2V5O1xyXG5cclxuICAgICAgaWYgKCEoa2V5IGluIGpzb25EYXRhKSkge1xyXG4gICAgICAgIHRoaXMuZW1pdCgnZGlmZmVyZW5jZScsIHtcclxuICAgICAgICAgIHR5cGU6ICdyZW1vdmVkJyxcclxuICAgICAgICAgIHBhdGg6IGN1cnJlbnRQYXRoLFxyXG4gICAgICAgIH0gYXMgRGlmZmVyZW5jZUV2ZW50KTtcclxuICAgICAgICBkZWxldGUgaGFzaFRyZWVba2V5XTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICB0aGlzLnByb2Nlc3NLZXlWYWx1ZShoYXNoVHJlZSwganNvbkRhdGEsIGtleSwgY3VycmVudFBhdGgpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDlh6bnkIblkI06IOOCreODvOWApOOBruWHpueQhlxyXG4gICAqIEBwYXJhbSB7SGFzaFRyZWVOb2RlfSBoYXNoVHJlZSDjg4/jg4Pjgrfjg6Xjg4Tjg6rjg7xcclxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSBqc29uRGF0YSBKU09OIOODh+ODvOOCv1xyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBrZXkg44Kt44O8XHJcbiAgICogQHBhcmFtIHtzdHJpbmd9IGN1cnJlbnRQYXRoIOePvuWcqOOBruODkeOCuVxyXG4gICAqIEByZXR1cm5zIHt2b2lkfVxyXG4gICAqIEBwcml2YXRlXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBwcm9jZXNzS2V5VmFsdWUoXHJcbiAgICBoYXNoVHJlZTogSGFzaFRyZWVOb2RlLFxyXG4gICAganNvbkRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxyXG4gICAga2V5OiBzdHJpbmcsXHJcbiAgICBjdXJyZW50UGF0aDogc3RyaW5nXHJcbiAgKTogdm9pZCB7XHJcbiAgICBjb25zdCBqc29uVmFsdWUgPSBqc29uRGF0YVtrZXldO1xyXG4gICAgaWYgKHR5cGVvZiBqc29uVmFsdWUgPT09ICdvYmplY3QnICYmIGpzb25WYWx1ZSAhPT0gbnVsbCkge1xyXG4gICAgICB0aGlzLnByb2Nlc3NPYmplY3RWYWx1ZShoYXNoVHJlZSwganNvblZhbHVlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+LCBrZXksIGN1cnJlbnRQYXRoKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHRoaXMucHJvY2Vzc1ByaW1pdGl2ZVZhbHVlKGhhc2hUcmVlLCBqc29uVmFsdWUsIGtleSwgY3VycmVudFBhdGgpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICog5Yem55CG5ZCNOiDjgqrjg5bjgrjjgqfjgq/jg4jlgKTjga7lh6bnkIZcclxuICAgKiBAcGFyYW0ge0hhc2hUcmVlTm9kZX0gaGFzaFRyZWUg44OP44OD44K344Ol44OE44Oq44O8XHJcbiAgICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0ganNvblZhbHVlIEpTT04g5YCkXHJcbiAgICogQHBhcmFtIHtzdHJpbmd9IGtleSDjgq3jg7xcclxuICAgKiBAcGFyYW0ge3N0cmluZ30gY3VycmVudFBhdGgg54++5Zyo44Gu44OR44K5XHJcbiAgICogQHJldHVybnMge3ZvaWR9XHJcbiAgICogQHByaXZhdGVcclxuICAgKi9cclxuICBwcml2YXRlIHByb2Nlc3NPYmplY3RWYWx1ZShcclxuICAgIGhhc2hUcmVlOiBIYXNoVHJlZU5vZGUsXHJcbiAgICBqc29uVmFsdWU6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxyXG4gICAga2V5OiBzdHJpbmcsXHJcbiAgICBjdXJyZW50UGF0aDogc3RyaW5nXHJcbiAgKTogdm9pZCB7XHJcbiAgICBpZiAoanNvblZhbHVlLnR5cGUpIHtcclxuICAgICAgY29uc3QgbmV3UHJpbWl0aXZlSGFzaCA9IHRoaXMuY2FsY3VsYXRlSGFzaChKU09OLnN0cmluZ2lmeShqc29uVmFsdWUpKTtcclxuICAgICAgaWYgKChoYXNoVHJlZVtrZXldIGFzIEhhc2hUcmVlTm9kZSkuX19oYXNoICE9PSBuZXdQcmltaXRpdmVIYXNoKSB7XHJcbiAgICAgICAgdGhpcy5lbWl0KCdkaWZmZXJlbmNlJywge1xyXG4gICAgICAgICAgdHlwZTogJ21vZGlmaWVkJyxcclxuICAgICAgICAgIHBhdGg6IGN1cnJlbnRQYXRoLFxyXG4gICAgICAgIH0gYXMgRGlmZmVyZW5jZUV2ZW50KTtcclxuICAgICAgICAoaGFzaFRyZWVba2V5XSBhcyBIYXNoVHJlZU5vZGUpLl9faGFzaCA9IG5ld1ByaW1pdGl2ZUhhc2g7XHJcbiAgICAgIH1cclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGlmICghaGFzaFRyZWVba2V5XSB8fCB0eXBlb2YgaGFzaFRyZWVba2V5XSAhPT0gJ29iamVjdCcpIHtcclxuICAgICAgICBoYXNoVHJlZVtrZXldID0geyBfX2hhc2g6ICcnIH07XHJcbiAgICAgIH1cclxuICAgICAgdGhpcy5maW5kRGlmZmVyZW5jZXNBbmRVcGRhdGUoXHJcbiAgICAgICAgaGFzaFRyZWVba2V5XSBhcyBIYXNoVHJlZU5vZGUsXHJcbiAgICAgICAganNvblZhbHVlLFxyXG4gICAgICAgIGN1cnJlbnRQYXRoXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDlh6bnkIblkI06IOODl+ODquODn+ODhuOCo+ODluWApOOBruWHpueQhlxyXG4gICAqIEBwYXJhbSB7SGFzaFRyZWVOb2RlfSBoYXNoVHJlZSDjg4/jg4Pjgrfjg6Xjg4Tjg6rjg7xcclxuICAgKiBAcGFyYW0ge3Vua25vd259IGpzb25WYWx1ZSBKU09OIOWApFxyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBrZXkg44Kt44O8XHJcbiAgICogQHBhcmFtIHtzdHJpbmd9IGN1cnJlbnRQYXRoIOePvuWcqOOBruODkeOCuVxyXG4gICAqIEByZXR1cm5zIHt2b2lkfVxyXG4gICAqIEBwcml2YXRlXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBwcm9jZXNzUHJpbWl0aXZlVmFsdWUoXHJcbiAgICBoYXNoVHJlZTogSGFzaFRyZWVOb2RlLFxyXG4gICAganNvblZhbHVlOiB1bmtub3duLFxyXG4gICAga2V5OiBzdHJpbmcsXHJcbiAgICBjdXJyZW50UGF0aDogc3RyaW5nXHJcbiAgKTogdm9pZCB7XHJcbiAgICBjb25zdCBuZXdQcmltaXRpdmVIYXNoID0gdGhpcy5jYWxjdWxhdGVIYXNoKEpTT04uc3RyaW5naWZ5KGpzb25WYWx1ZSkpO1xyXG4gICAgaWYgKChoYXNoVHJlZVtrZXldIGFzIEhhc2hUcmVlTm9kZSkuX19oYXNoICE9PSBuZXdQcmltaXRpdmVIYXNoKSB7XHJcbiAgICAgIHRoaXMuZW1pdCgnZGlmZmVyZW5jZScsIHtcclxuICAgICAgICB0eXBlOiAnbW9kaWZpZWQnLFxyXG4gICAgICAgIHBhdGg6IGN1cnJlbnRQYXRoLFxyXG4gICAgICB9IGFzIERpZmZlcmVuY2VFdmVudCk7XHJcbiAgICAgIChoYXNoVHJlZVtrZXldIGFzIEhhc2hUcmVlTm9kZSkuX19oYXNoID0gbmV3UHJpbWl0aXZlSGFzaDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIOWHpueQhuWQjTog6L+95Yqg44GV44KM44Gf44Kt44O844Gu5Yem55CGXHJcbiAgICogQHBhcmFtIHtIYXNoVHJlZU5vZGV9IGhhc2hUcmVlIOODj+ODg+OCt+ODpeODhOODquODvFxyXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgdW5rbm93bj59IGpzb25EYXRhIEpTT04g44OH44O844K/XHJcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhdGgg44OR44K5XHJcbiAgICogQHJldHVybnMge3ZvaWR9XHJcbiAgICogQHByaXZhdGVcclxuICAgKi9cclxuICBwcml2YXRlIHByb2Nlc3NBZGRlZEtleXMoXHJcbiAgICBoYXNoVHJlZTogSGFzaFRyZWVOb2RlLFxyXG4gICAganNvbkRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxyXG4gICAgcGF0aDogc3RyaW5nXHJcbiAgKTogdm9pZCB7XHJcbiAgICBmb3IgKGNvbnN0IGtleSBpbiBqc29uRGF0YSkge1xyXG4gICAgICBjb25zdCBjdXJyZW50UGF0aCA9IHBhdGggPyBgJHtwYXRofS4ke2tleX1gIDoga2V5O1xyXG4gICAgICBpZiAoIShrZXkgaW4gaGFzaFRyZWUpKSB7XHJcbiAgICAgICAgdGhpcy5lbWl0KCdkaWZmZXJlbmNlJywge1xyXG4gICAgICAgICAgdHlwZTogJ2FkZGVkJyxcclxuICAgICAgICAgIHBhdGg6IGN1cnJlbnRQYXRoLFxyXG4gICAgICAgIH0gYXMgRGlmZmVyZW5jZUV2ZW50KTtcclxuICAgICAgICBjb25zdCBqc29uVmFsdWUgPSBqc29uRGF0YVtrZXldO1xyXG4gICAgICAgIGhhc2hUcmVlW2tleV0gPVxyXG4gICAgICAgICAgdHlwZW9mIGpzb25WYWx1ZSA9PT0gJ29iamVjdCcgJiYganNvblZhbHVlICE9PSBudWxsXHJcbiAgICAgICAgICAgID8gdGhpcy5idWlsZEhhc2hUcmVlKGpzb25WYWx1ZSlcclxuICAgICAgICAgICAgOiB7IF9faGFzaDogdGhpcy5jYWxjdWxhdGVIYXNoKEpTT04uc3RyaW5naWZ5KGpzb25WYWx1ZSkpIH07XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIOWHpueQhuWQjTog5beu5YiG5qSc5Ye644Go5pu05pawXHJcbiAgICpcclxuICAgKiDlh6bnkIbmpoLopoE6XHJcbiAgICog5YmN5Zue44Gu44OP44OD44K344Ol44OE44Oq44O844Go5paw44GX44GESlNPTuODh+ODvOOCv+OCkuavlOi8g+OBl+OAgVxyXG4gICAqIOi/veWKoOODu+WJiumZpOODu+abtOaWsOOBleOCjOOBn+ODkeOCueOCkidkaWZmZXJlbmNlJ+OCpOODmeODs+ODiOOBp+mAmuefpVxyXG4gICAqXHJcbiAgICog5a6f6KOF55CG55SxOlxyXG4gICAqIOODj+ODg+OCt+ODpeWApOOBruavlOi8g+OBp+S4jeimgeOBquWGjeW4sOOCkumBv+OBkeOAgeWkieabtOeuh+aJgOOBruOBv+OCkuaknOWHuuOBmeOCi+OBn+OCgVxyXG4gICAqIEBwYXJhbSB7SGFzaFRyZWVOb2RlfSBoYXNoVHJlZSDjg4/jg4Pjgrfjg6Xjg4Tjg6rjg7xcclxuICAgKiBAcGFyYW0ge3Vua25vd259IGpzb25EYXRhIEpTT04g44OH44O844K/XHJcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhdGgg44OR44K5XHJcbiAgICogQHJldHVybnMge3ZvaWR9XHJcbiAgICogQHByaXZhdGVcclxuICAgKi9cclxuICBwcml2YXRlIGZpbmREaWZmZXJlbmNlc0FuZFVwZGF0ZShcclxuICAgIGhhc2hUcmVlOiBIYXNoVHJlZU5vZGUsXHJcbiAgICBqc29uRGF0YTogdW5rbm93bixcclxuICAgIHBhdGggPSAnJ1xyXG4gICk6IHZvaWQge1xyXG4gICAgY29uc3QgbmV3SGFzaCA9IHRoaXMuY2FsY3VsYXRlSGFzaChKU09OLnN0cmluZ2lmeShqc29uRGF0YSkpO1xyXG4gICAgaWYgKGhhc2hUcmVlLl9faGFzaCA9PT0gbmV3SGFzaCkge1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgZGF0YSA9IGpzb25EYXRhIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xyXG4gICAgdGhpcy5wcm9jZXNzRXhpc3RpbmdLZXlzKGhhc2hUcmVlLCBkYXRhLCBwYXRoKTtcclxuICAgIHRoaXMucHJvY2Vzc0FkZGVkS2V5cyhoYXNoVHJlZSwgZGF0YSwgcGF0aCk7XHJcblxyXG4gICAgKGhhc2hUcmVlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KS5fX2hhc2ggPSB0aGlzLmNhbGN1bGF0ZUhhc2goXHJcbiAgICAgIE9iamVjdC5lbnRyaWVzKGhhc2hUcmVlKVxyXG4gICAgICAgIC5maWx0ZXIoKFtrZXldKSA9PiBrZXkgIT09ICdfX2hhc2gnKVxyXG4gICAgICAgIC5tYXAoKFtrZXksIHZhbHVlXSkgPT4gYCR7a2V5fTokeyh2YWx1ZSBhcyBIYXNoVHJlZU5vZGUpLl9faGFzaH1gKVxyXG4gICAgICAgIC5qb2luKCcnKVxyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIOWHpueQhuWQjTog44OP44OD44K344Ol44OE44Oq44O85Yid5pyf5YyWXHJcbiAgICpcclxuICAgKiDlh6bnkIbmpoLopoE6XHJcbiAgICog5Yid5Zue6Kqt44G/6L6844G/5pmC44Gr44OP44OD44K344Ol44OE44Oq44O844KS5qeL56+J44GX44CB5YmN5Zue54q25oWL44Go44GX44Gm5L+d5oyBXHJcbiAgICpcclxuICAgKiDlrp/oo4XnkIbnlLE6XHJcbiAgICog5qyh5Zue44Gu5beu5YiG5qSc5Ye644Gu5Z+65rqW44Go44Gq44KL44OZ44O844K544Op44Kk44Oz44KS5L2c5oiQ44GZ44KL44Gf44KBXHJcbiAgICogQHBhcmFtIHt1bmtub3dufSBqc29uRGF0YSDliJ3lm57jga5KU09O44OH44O844K/XHJcbiAgICogQHJldHVybnMge0hhc2hUcmVlTm9kZX0g5qeL56+J44GV44KM44Gf44OP44OD44K344Ol44OE44Oq44O8XHJcbiAgICovXHJcbiAgaW5pdGlhbGl6ZShqc29uRGF0YTogdW5rbm93bik6IEhhc2hUcmVlTm9kZSB7XHJcbiAgICByZXR1cm4gKHRoaXMucHJldmlvdXNIYXNoVHJlZSA9IHRoaXMuYnVpbGRIYXNoVHJlZShqc29uRGF0YSkpOyAvLyDliJ3mnJ/jg4/jg4Pjgrfjg6Xjg4Tjg6rjg7zmp4vnr4lcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIOWHpueQhuWQjTog5beu5YiG5qSc5Ye6XHJcbiAgICpcclxuICAgKiDlh6bnkIbmpoLopoE6XHJcbiAgICog5paw44GX44GESlNPTuODh+ODvOOCv+OBqOWJjeWbnuOBruODj+ODg+OCt+ODpeODhOODquODvOOCkuavlOi8g+OBl+OAgVxyXG4gICAqIOW3ruWIhuOCkuaknOWHuuOBl+OBpidkaWZmZXJlbmNlJ+OCpOODmeODs+ODiOOBp+mAmuefpeOAglxyXG4gICAqIOWGhemDqOeahOOBq+ODj+ODg+OCt+ODpeODhOODquODvOOCkuabtOaWsOOBl+OBpuasoeWbnuaknOWHuuOBq+WCmeOBiOOCi1xyXG4gICAqXHJcbiAgICog5a6f6KOF55CG55SxOlxyXG4gICAqIOODleOCoeOCpOODq+ebo+imluOChOODquOCouODq+OCv+OCpOODoOWkieabtOaknOWHuuOBruOBn+OCgeOAgee2mee2mueahOOBq+W3ruWIhuOCkuaknOWHuuOBmeOCi+OBn+OCgVxyXG4gICAqIEBwYXJhbSB7dW5rbm93bn0ganNvbkRhdGEg5paw44GX44GESlNPTuODh+ODvOOCv1xyXG4gICAqIEByZXR1cm5zIHtIYXNoVHJlZU5vZGV9IOabtOaWsOS4iuOBruODj+ODg+OCt+ODpeODhOODquODvFxyXG4gICAqL1xyXG4gIGRldGVjdENoYW5nZXMoanNvbkRhdGE6IHVua25vd24pOiBIYXNoVHJlZU5vZGUge1xyXG4gICAgdGhpcy5maW5kRGlmZmVyZW5jZXNBbmRVcGRhdGUodGhpcy5wcmV2aW91c0hhc2hUcmVlLCBqc29uRGF0YSk7XHJcbiAgICByZXR1cm4gdGhpcy5wcmV2aW91c0hhc2hUcmVlO1xyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IGRlZmF1bHQgRmluZERpZmZlcmVuY2VzO1xyXG4iXSwibmFtZXMiOltdLCJpZ25vcmVMaXN0IjpbXSwic291cmNlUm9vdCI6IiJ9
|