mobx-keystone-yjs 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/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/mobx-keystone-yjs.esm.js +272 -0
- package/dist/mobx-keystone-yjs.esm.mjs +272 -0
- package/dist/mobx-keystone-yjs.umd.js +291 -0
- package/dist/types/binding/applyMobxKeystonePatchToYjsObject.d.ts +2 -0
- package/dist/types/binding/bindYjsToMobxKeystone.d.ts +11 -0
- package/dist/types/binding/convertYjsEventToPatches.d.ts +3 -0
- package/dist/types/binding/toYDataType.d.ts +3 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/jsonTypes.d.ts +7 -0
- package/dist/types/utils/error.d.ts +6 -0
- package/package.json +87 -0
- package/src/binding/applyMobxKeystonePatchToYjsObject.ts +92 -0
- package/src/binding/bindYjsToMobxKeystone.ts +142 -0
- package/src/binding/convertYjsEventToPatches.ts +85 -0
- package/src/binding/toYDataType.ts +41 -0
- package/src/index.ts +2 -0
- package/src/jsonTypes.ts +4 -0
- package/src/utils/error.ts +18 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Javier González Garcés
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<h1 align="center">mobx-keystone-yjs</h1>
|
|
3
|
+
</p>
|
|
4
|
+
<p align="center">
|
|
5
|
+
<i>Seamlessly integrate mobx-keystone models with the Y.js ecosystem for an unmatched toolkit to build dynamic, responsive, and collaborative web applications</i>
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a aria-label="NPM version" href="https://www.npmjs.com/package/mobx-keystone-yjs">
|
|
10
|
+
<img src="https://img.shields.io/npm/v/mobx-keystone-yjs.svg?style=for-the-badge&logo=npm&labelColor=333" />
|
|
11
|
+
</a>
|
|
12
|
+
<a aria-label="License" href="./LICENSE">
|
|
13
|
+
<img src="https://img.shields.io/npm/l/mobx-keystone-yjs.svg?style=for-the-badge&labelColor=333" />
|
|
14
|
+
</a>
|
|
15
|
+
<a aria-label="Types" href="./packages/mobx-keystone-yjs/tsconfig.json">
|
|
16
|
+
<img src="https://img.shields.io/npm/types/mobx-keystone-yjs.svg?style=for-the-badge&logo=typescript&labelColor=333" />
|
|
17
|
+
</a>
|
|
18
|
+
<br />
|
|
19
|
+
<a aria-label="CI" href="https://github.com/xaviergonz/mobx-keystone/actions/workflows/main.yml">
|
|
20
|
+
<img src="https://img.shields.io/github/actions/workflow/status/xaviergonz/mobx-keystone/main.yml?branch=master&label=CI&logo=github&style=for-the-badge&labelColor=333" />
|
|
21
|
+
</a>
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
> ## See a [working example](https://mobx-keystone.js.org/examples/yjs-binding) in [mobx-keystone.js.org](https://mobx-keystone.js.org)
|
|
25
|
+
|
|
26
|
+
## Introduction
|
|
27
|
+
|
|
28
|
+
`mobx-keystone-yjs` seamlessly integrates `mobx-keystone` models with the `Y.js` ecosystem, providing developers with an unmatched toolkit to build dynamic, responsive, and collaborative web applications. Some of the key advantages and capabilities that this integration brings to your projects are:
|
|
29
|
+
|
|
30
|
+
## Real-time Collaboration and Synchronization
|
|
31
|
+
|
|
32
|
+
`mobx-keystone-yjs` bridges the gap between local state management and remote synchronization, allowing multiple users to interact with the same application data in real-time. This synchronization is not just limited to client-server models but extends to peer-to-peer (P2P) environments as well. Whether you're building a collaborative text editor, a shared to-do list, or any interactive platform, this binding ensures that all participants can see and respond to changes instantly, no matter where they are, transparently.
|
|
33
|
+
|
|
34
|
+
## Offline Support and Optimistic UI Changes
|
|
35
|
+
|
|
36
|
+
One of the standout features of `Y.js` is its robust offline support. Users can continue to interact with the application even when disconnected from the network. Changes made offline are seamlessly integrated once the connection is reestablished, thanks to the conflict-free replicated data types (CRDTs) at the heart of `Y.js`. This functionality not only enhances the user experience but also ensures data integrity and consistency across all states. Binding this functionality to `mobx-keystone` models means that you can take advantage of these features without having to write any additional code.
|
|
37
|
+
|
|
38
|
+
Optimistic UI updates play a crucial role in making applications feel more responsive. With CRDTs changes made by users can be immediately reflected in the UI, without waiting for server confirmation. This approach minimizes perceived latency, providing a smoother and more engaging user experience.
|
|
39
|
+
|
|
40
|
+
## Client Synchronization and Data Integrity
|
|
41
|
+
|
|
42
|
+
Binding `Y.js` and `mobx-keystone` ensures that client states are synchronized efficiently and accurately. The models are automatically updated to reflect the latest shared state, ensuring that all users have a consistent view of the data. Furthermore, the use of CRDTs in `Y.js` guarantees that even in complex, multi-user scenarios, data integrity is maintained. Conflicts are resolved automatically, ensuring that the final state is always a true representation of all users' inputs.
|
|
43
|
+
|
|
44
|
+
## P2P Support and Scalability
|
|
45
|
+
|
|
46
|
+
By leveraging `Y.js`'s P2P capabilities, `mobx-keystone-yjs` enables direct client-to-client communication, bypassing traditional server-based data flow. This not only reduces server load but also opens up new possibilities for applications where direct user-to-user interaction is preferred. The scalability offered by this approach means that your application can support a growing number of users without a proportional increase in server resources.
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
> `npm install mobx-keystone-yjs`
|
|
51
|
+
|
|
52
|
+
> `yarn add mobx-keystone-yjs`
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { onPatches, onSnapshot, getParentToChildPath, onGlobalPatches, fromSnapshot, applyPatches } from "mobx-keystone";
|
|
2
|
+
import * as Y from "yjs";
|
|
3
|
+
class MobxKeystoneYjsError extends Error {
|
|
4
|
+
constructor(msg) {
|
|
5
|
+
super(msg);
|
|
6
|
+
Object.setPrototypeOf(this, MobxKeystoneYjsError.prototype);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function failure(msg) {
|
|
10
|
+
return new MobxKeystoneYjsError(msg);
|
|
11
|
+
}
|
|
12
|
+
function isJSONPrimitive(v) {
|
|
13
|
+
const t = typeof v;
|
|
14
|
+
return t === "string" || t === "number" || t === "boolean" || v === null;
|
|
15
|
+
}
|
|
16
|
+
function isJSONArray(v) {
|
|
17
|
+
return Array.isArray(v);
|
|
18
|
+
}
|
|
19
|
+
function isJSONObject(v) {
|
|
20
|
+
return !isJSONArray(v) && typeof v === "object";
|
|
21
|
+
}
|
|
22
|
+
function toYDataType(v) {
|
|
23
|
+
if (isJSONPrimitive(v)) {
|
|
24
|
+
return v;
|
|
25
|
+
} else if (isJSONArray(v)) {
|
|
26
|
+
const arr = new Y.Array();
|
|
27
|
+
applyJsonArray(arr, v);
|
|
28
|
+
return arr;
|
|
29
|
+
} else if (isJSONObject(v)) {
|
|
30
|
+
const map = new Y.Map();
|
|
31
|
+
applyJsonObject(map, v);
|
|
32
|
+
return map;
|
|
33
|
+
} else {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function applyJsonArray(dest, source) {
|
|
38
|
+
dest.push(source.map(toYDataType));
|
|
39
|
+
}
|
|
40
|
+
function applyJsonObject(dest, source) {
|
|
41
|
+
Object.entries(source).forEach(([k, v]) => {
|
|
42
|
+
dest.set(k, toYDataType(v));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function applyMobxKeystonePatchToYjsObject(patch, yjs) {
|
|
46
|
+
if (patch.path.length > 1) {
|
|
47
|
+
const [key, ...rest] = patch.path;
|
|
48
|
+
if (yjs instanceof Y.Map) {
|
|
49
|
+
const child = yjs.get(String(key));
|
|
50
|
+
if (child === void 0) {
|
|
51
|
+
throw failure(`invalid patch path, key "${key}" not found in Yjs map - patch: ${JSON.stringify(patch)}`);
|
|
52
|
+
}
|
|
53
|
+
applyMobxKeystonePatchToYjsObject({ ...patch, path: rest }, child);
|
|
54
|
+
} else if (yjs instanceof Y.Array) {
|
|
55
|
+
const child = yjs.get(Number(key));
|
|
56
|
+
if (child === void 0) {
|
|
57
|
+
throw failure(`invalid patch path, key "${key}" not found in Yjs array - patch: ${JSON.stringify(patch)}`);
|
|
58
|
+
}
|
|
59
|
+
applyMobxKeystonePatchToYjsObject({ ...patch, path: rest }, child);
|
|
60
|
+
} else {
|
|
61
|
+
throw failure(`invalid patch path, key "${key}" not found in unknown Yjs object - patch: ${JSON.stringify(patch)}`);
|
|
62
|
+
}
|
|
63
|
+
} else if (patch.path.length === 1) {
|
|
64
|
+
if (yjs instanceof Y.Map) {
|
|
65
|
+
const key = String(patch.path[0]);
|
|
66
|
+
switch (patch.op) {
|
|
67
|
+
case "add":
|
|
68
|
+
case "replace": {
|
|
69
|
+
yjs.set(key, toYDataType(patch.value));
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
case "remove": {
|
|
73
|
+
yjs.delete(key);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
default: {
|
|
77
|
+
throw failure(`invalid patch operation for map`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} else if (yjs instanceof Y.Array) {
|
|
81
|
+
const key = patch.path[0];
|
|
82
|
+
switch (patch.op) {
|
|
83
|
+
case "replace": {
|
|
84
|
+
if (key === "length") {
|
|
85
|
+
if (yjs.length > patch.value) {
|
|
86
|
+
const toDelete = yjs.length - patch.value;
|
|
87
|
+
yjs.delete(patch.value, toDelete);
|
|
88
|
+
} else if (yjs.length < patch.value) {
|
|
89
|
+
const toInsert = patch.value - yjs.length;
|
|
90
|
+
yjs.insert(yjs.length, Array(toInsert).fill(void 0));
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
yjs.delete(Number(key));
|
|
94
|
+
yjs.insert(Number(key), [toYDataType(patch.value)]);
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
case "add": {
|
|
99
|
+
yjs.insert(Number(key), [toYDataType(patch.value)]);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case "remove": {
|
|
103
|
+
yjs.delete(Number(key));
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
default: {
|
|
107
|
+
throw failure(`invalid patch operation for array`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
throw failure(`invalid patch path, the Yjs object is of an unkown type, so key "${patch.path[0]}" cannot be found in it`);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
throw failure(`invalid patch path, it cannot be empty`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function convertYjsEventToPatches(event) {
|
|
118
|
+
const patches = [];
|
|
119
|
+
if (event instanceof Y.YMapEvent) {
|
|
120
|
+
const source = event.target;
|
|
121
|
+
event.changes.keys.forEach((change, key) => {
|
|
122
|
+
const path = [...event.path, key];
|
|
123
|
+
switch (change.action) {
|
|
124
|
+
case "add":
|
|
125
|
+
patches.push({
|
|
126
|
+
op: "add",
|
|
127
|
+
path,
|
|
128
|
+
value: toPlainValue(source.get(key))
|
|
129
|
+
});
|
|
130
|
+
break;
|
|
131
|
+
case "update":
|
|
132
|
+
patches.push({
|
|
133
|
+
op: "replace",
|
|
134
|
+
path,
|
|
135
|
+
value: toPlainValue(source.get(key))
|
|
136
|
+
});
|
|
137
|
+
break;
|
|
138
|
+
case "delete":
|
|
139
|
+
patches.push({
|
|
140
|
+
op: "remove",
|
|
141
|
+
path
|
|
142
|
+
});
|
|
143
|
+
break;
|
|
144
|
+
default:
|
|
145
|
+
throw failure(`unsupported Yjs map event action: ${change.action}`);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
} else if (event instanceof Y.YArrayEvent) {
|
|
149
|
+
let retain = 0;
|
|
150
|
+
event.changes.delta.forEach((change) => {
|
|
151
|
+
if (change.retain) {
|
|
152
|
+
retain += change.retain;
|
|
153
|
+
}
|
|
154
|
+
if (change.delete) {
|
|
155
|
+
const path = [...event.path, retain];
|
|
156
|
+
for (let i = 0; i < change.delete; i++) {
|
|
157
|
+
patches.push({
|
|
158
|
+
op: "remove",
|
|
159
|
+
path
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (change.insert) {
|
|
164
|
+
const newValues = Array.isArray(change.insert) ? change.insert : [change.insert];
|
|
165
|
+
newValues.forEach((v) => {
|
|
166
|
+
const path = [...event.path, retain];
|
|
167
|
+
patches.push({
|
|
168
|
+
op: "add",
|
|
169
|
+
path,
|
|
170
|
+
value: toPlainValue(v)
|
|
171
|
+
});
|
|
172
|
+
retain++;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return patches;
|
|
178
|
+
}
|
|
179
|
+
function toPlainValue(v) {
|
|
180
|
+
if (v instanceof Y.Map || v instanceof Y.Array) {
|
|
181
|
+
return v.toJSON();
|
|
182
|
+
} else {
|
|
183
|
+
return v;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function bindYjsToMobxKeystone({ yjsDoc, yjsObject, mobxKeystoneType }) {
|
|
187
|
+
const yjsJson = yjsObject.toJSON();
|
|
188
|
+
const initializationGlobalPatches = [];
|
|
189
|
+
const createBoundObject = () => {
|
|
190
|
+
const disposeOnGlobalPatches = onGlobalPatches((target, patches) => {
|
|
191
|
+
initializationGlobalPatches.push({ target, patches });
|
|
192
|
+
});
|
|
193
|
+
try {
|
|
194
|
+
return fromSnapshot(mobxKeystoneType, yjsJson);
|
|
195
|
+
} finally {
|
|
196
|
+
disposeOnGlobalPatches();
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const boundObject = createBoundObject();
|
|
200
|
+
let applyingMobxKeystoneChanges = 0;
|
|
201
|
+
const yjsOrigin = Symbol("bindYjsToMobxKeystoneTransactionOrigin");
|
|
202
|
+
const observeDeepCb = (events) => {
|
|
203
|
+
const patches = [];
|
|
204
|
+
events.forEach((event) => {
|
|
205
|
+
if (event.transaction.origin !== yjsOrigin) {
|
|
206
|
+
patches.push(...convertYjsEventToPatches(event));
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
if (patches.length > 0) {
|
|
210
|
+
applyingMobxKeystoneChanges++;
|
|
211
|
+
try {
|
|
212
|
+
applyPatches(boundObject, patches);
|
|
213
|
+
} finally {
|
|
214
|
+
applyingMobxKeystoneChanges--;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
yjsObject.observeDeep(observeDeepCb);
|
|
219
|
+
let pendingPatches = [];
|
|
220
|
+
const disposeOnPatches = onPatches(boundObject, (patches) => {
|
|
221
|
+
if (applyingMobxKeystoneChanges > 0) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
pendingPatches.push(...patches);
|
|
225
|
+
});
|
|
226
|
+
const disposeOnSnapshot = onSnapshot(boundObject, () => {
|
|
227
|
+
if (pendingPatches.length === 0) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const patches = pendingPatches;
|
|
231
|
+
pendingPatches = [];
|
|
232
|
+
yjsDoc.transact(() => {
|
|
233
|
+
patches.forEach((patch) => {
|
|
234
|
+
applyMobxKeystonePatchToYjsObject(patch, yjsObject);
|
|
235
|
+
});
|
|
236
|
+
}, yjsOrigin);
|
|
237
|
+
});
|
|
238
|
+
yjsDoc.transact(() => {
|
|
239
|
+
let boundObjectFound = false;
|
|
240
|
+
initializationGlobalPatches.forEach(({ target, patches }) => {
|
|
241
|
+
if (!boundObjectFound) {
|
|
242
|
+
if (target !== boundObject) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
boundObjectFound = true;
|
|
246
|
+
}
|
|
247
|
+
const parentToChildPath = getParentToChildPath(boundObject, target);
|
|
248
|
+
if (parentToChildPath !== void 0) {
|
|
249
|
+
patches.forEach((patch) => {
|
|
250
|
+
applyMobxKeystonePatchToYjsObject({
|
|
251
|
+
...patch,
|
|
252
|
+
path: [...parentToChildPath, ...patch.path]
|
|
253
|
+
}, yjsObject);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}, yjsOrigin);
|
|
258
|
+
return {
|
|
259
|
+
boundObject,
|
|
260
|
+
dispose: () => {
|
|
261
|
+
disposeOnPatches();
|
|
262
|
+
disposeOnSnapshot();
|
|
263
|
+
yjsObject.unobserveDeep(observeDeepCb);
|
|
264
|
+
},
|
|
265
|
+
yjsOrigin
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
export {
|
|
269
|
+
MobxKeystoneYjsError,
|
|
270
|
+
bindYjsToMobxKeystone
|
|
271
|
+
};
|
|
272
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9ieC1rZXlzdG9uZS15anMuZXNtLm1qcyIsInNvdXJjZXMiOlsiLi4vc3JjL3V0aWxzL2Vycm9yLnRzIiwiLi4vc3JjL2JpbmRpbmcvdG9ZRGF0YVR5cGUudHMiLCIuLi9zcmMvYmluZGluZy9hcHBseU1vYnhLZXlzdG9uZVBhdGNoVG9ZanNPYmplY3QudHMiLCIuLi9zcmMvYmluZGluZy9jb252ZXJ0WWpzRXZlbnRUb1BhdGNoZXMudHMiLCIuLi9zcmMvYmluZGluZy9iaW5kWWpzVG9Nb2J4S2V5c3RvbmUudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIEEgbW9ieC1rZXlzdG9uZS15anMgZXJyb3IuXHJcbiAqL1xyXG5leHBvcnQgY2xhc3MgTW9ieEtleXN0b25lWWpzRXJyb3IgZXh0ZW5kcyBFcnJvciB7XHJcbiAgY29uc3RydWN0b3IobXNnOiBzdHJpbmcpIHtcclxuICAgIHN1cGVyKG1zZylcclxuXHJcbiAgICAvLyBTZXQgdGhlIHByb3RvdHlwZSBleHBsaWNpdGx5LlxyXG4gICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKHRoaXMsIE1vYnhLZXlzdG9uZVlqc0Vycm9yLnByb3RvdHlwZSlcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBAaW50ZXJuYWxcclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBmYWlsdXJlKG1zZzogc3RyaW5nKSB7XHJcbiAgcmV0dXJuIG5ldyBNb2J4S2V5c3RvbmVZanNFcnJvcihtc2cpXHJcbn1cclxuIiwiaW1wb3J0ICogYXMgWSBmcm9tIFwieWpzXCJcclxuaW1wb3J0IHsgSlNPTlZhbHVlLCBKU09OQXJyYXksIEpTT05PYmplY3QsIEpTT05QcmltaXRpdmUgfSBmcm9tIFwiLi4vanNvblR5cGVzXCJcclxuXHJcbmZ1bmN0aW9uIGlzSlNPTlByaW1pdGl2ZSh2OiBKU09OVmFsdWUpOiB2IGlzIEpTT05QcmltaXRpdmUge1xyXG4gIGNvbnN0IHQgPSB0eXBlb2YgdlxyXG4gIHJldHVybiB0ID09PSBcInN0cmluZ1wiIHx8IHQgPT09IFwibnVtYmVyXCIgfHwgdCA9PT0gXCJib29sZWFuXCIgfHwgdiA9PT0gbnVsbFxyXG59XHJcblxyXG5mdW5jdGlvbiBpc0pTT05BcnJheSh2OiBKU09OVmFsdWUpOiB2IGlzIEpTT05BcnJheSB7XHJcbiAgcmV0dXJuIEFycmF5LmlzQXJyYXkodilcclxufVxyXG5cclxuZnVuY3Rpb24gaXNKU09OT2JqZWN0KHY6IEpTT05WYWx1ZSk6IHYgaXMgSlNPTk9iamVjdCB7XHJcbiAgcmV0dXJuICFpc0pTT05BcnJheSh2KSAmJiB0eXBlb2YgdiA9PT0gXCJvYmplY3RcIlxyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gdG9ZRGF0YVR5cGUodjogSlNPTlZhbHVlKSB7XHJcbiAgaWYgKGlzSlNPTlByaW1pdGl2ZSh2KSkge1xyXG4gICAgcmV0dXJuIHZcclxuICB9IGVsc2UgaWYgKGlzSlNPTkFycmF5KHYpKSB7XHJcbiAgICBjb25zdCBhcnIgPSBuZXcgWS5BcnJheSgpXHJcbiAgICBhcHBseUpzb25BcnJheShhcnIsIHYpXHJcbiAgICByZXR1cm4gYXJyXHJcbiAgfSBlbHNlIGlmIChpc0pTT05PYmplY3QodikpIHtcclxuICAgIGNvbnN0IG1hcCA9IG5ldyBZLk1hcCgpXHJcbiAgICBhcHBseUpzb25PYmplY3QobWFwLCB2KVxyXG4gICAgcmV0dXJuIG1hcFxyXG4gIH0gZWxzZSB7XHJcbiAgICByZXR1cm4gdW5kZWZpbmVkXHJcbiAgfVxyXG59XHJcblxyXG5mdW5jdGlvbiBhcHBseUpzb25BcnJheShkZXN0OiBZLkFycmF5PHVua25vd24+LCBzb3VyY2U6IEpTT05BcnJheSkge1xyXG4gIGRlc3QucHVzaChzb3VyY2UubWFwKHRvWURhdGFUeXBlKSlcclxufVxyXG5cclxuZnVuY3Rpb24gYXBwbHlKc29uT2JqZWN0KGRlc3Q6IFkuTWFwPHVua25vd24+LCBzb3VyY2U6IEpTT05PYmplY3QpIHtcclxuICBPYmplY3QuZW50cmllcyhzb3VyY2UpLmZvckVhY2goKFtrLCB2XSkgPT4ge1xyXG4gICAgZGVzdC5zZXQoaywgdG9ZRGF0YVR5cGUodikpXHJcbiAgfSlcclxufVxyXG4iLCJpbXBvcnQgeyBQYXRjaCB9IGZyb20gXCJtb2J4LWtleXN0b25lXCJcclxuaW1wb3J0IHsgZmFpbHVyZSB9IGZyb20gXCIuLi91dGlscy9lcnJvclwiXHJcbmltcG9ydCAqIGFzIFkgZnJvbSBcInlqc1wiXHJcbmltcG9ydCB7IHRvWURhdGFUeXBlIH0gZnJvbSBcIi4vdG9ZRGF0YVR5cGVcIlxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5TW9ieEtleXN0b25lUGF0Y2hUb1lqc09iamVjdChwYXRjaDogUGF0Y2gsIHlqczogdW5rbm93bik6IHZvaWQge1xyXG4gIGlmIChwYXRjaC5wYXRoLmxlbmd0aCA+IDEpIHtcclxuICAgIGNvbnN0IFtrZXksIC4uLnJlc3RdID0gcGF0Y2gucGF0aFxyXG5cclxuICAgIGlmICh5anMgaW5zdGFuY2VvZiBZLk1hcCkge1xyXG4gICAgICBjb25zdCBjaGlsZCA9IHlqcy5nZXQoU3RyaW5nKGtleSkpXHJcbiAgICAgIGlmIChjaGlsZCA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgdGhyb3cgZmFpbHVyZShcclxuICAgICAgICAgIGBpbnZhbGlkIHBhdGNoIHBhdGgsIGtleSBcIiR7a2V5fVwiIG5vdCBmb3VuZCBpbiBZanMgbWFwIC0gcGF0Y2g6ICR7SlNPTi5zdHJpbmdpZnkocGF0Y2gpfWBcclxuICAgICAgICApXHJcbiAgICAgIH1cclxuICAgICAgYXBwbHlNb2J4S2V5c3RvbmVQYXRjaFRvWWpzT2JqZWN0KHsgLi4ucGF0Y2gsIHBhdGg6IHJlc3QgfSwgY2hpbGQpXHJcbiAgICB9IGVsc2UgaWYgKHlqcyBpbnN0YW5jZW9mIFkuQXJyYXkpIHtcclxuICAgICAgY29uc3QgY2hpbGQgPSB5anMuZ2V0KE51bWJlcihrZXkpKVxyXG4gICAgICBpZiAoY2hpbGQgPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIHRocm93IGZhaWx1cmUoXHJcbiAgICAgICAgICBgaW52YWxpZCBwYXRjaCBwYXRoLCBrZXkgXCIke2tleX1cIiBub3QgZm91bmQgaW4gWWpzIGFycmF5IC0gcGF0Y2g6ICR7SlNPTi5zdHJpbmdpZnkoXHJcbiAgICAgICAgICAgIHBhdGNoXHJcbiAgICAgICAgICApfWBcclxuICAgICAgICApXHJcbiAgICAgIH1cclxuICAgICAgYXBwbHlNb2J4S2V5c3RvbmVQYXRjaFRvWWpzT2JqZWN0KHsgLi4ucGF0Y2gsIHBhdGg6IHJlc3QgfSwgY2hpbGQpXHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aHJvdyBmYWlsdXJlKFxyXG4gICAgICAgIGBpbnZhbGlkIHBhdGNoIHBhdGgsIGtleSBcIiR7a2V5fVwiIG5vdCBmb3VuZCBpbiB1bmtub3duIFlqcyBvYmplY3QgLSBwYXRjaDogJHtKU09OLnN0cmluZ2lmeShcclxuICAgICAgICAgIHBhdGNoXHJcbiAgICAgICAgKX1gXHJcbiAgICAgIClcclxuICAgIH1cclxuICB9IGVsc2UgaWYgKHBhdGNoLnBhdGgubGVuZ3RoID09PSAxKSB7XHJcbiAgICBpZiAoeWpzIGluc3RhbmNlb2YgWS5NYXApIHtcclxuICAgICAgY29uc3Qga2V5ID0gU3RyaW5nKHBhdGNoLnBhdGhbMF0pXHJcblxyXG4gICAgICBzd2l0Y2ggKHBhdGNoLm9wKSB7XHJcbiAgICAgICAgY2FzZSBcImFkZFwiOlxyXG4gICAgICAgIGNhc2UgXCJyZXBsYWNlXCI6IHtcclxuICAgICAgICAgIHlqcy5zZXQoa2V5LCB0b1lEYXRhVHlwZShwYXRjaC52YWx1ZSkpXHJcbiAgICAgICAgICBicmVha1xyXG4gICAgICAgIH1cclxuICAgICAgICBjYXNlIFwicmVtb3ZlXCI6IHtcclxuICAgICAgICAgIHlqcy5kZWxldGUoa2V5KVxyXG4gICAgICAgICAgYnJlYWtcclxuICAgICAgICB9XHJcbiAgICAgICAgZGVmYXVsdDoge1xyXG4gICAgICAgICAgdGhyb3cgZmFpbHVyZShgaW52YWxpZCBwYXRjaCBvcGVyYXRpb24gZm9yIG1hcGApXHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9IGVsc2UgaWYgKHlqcyBpbnN0YW5jZW9mIFkuQXJyYXkpIHtcclxuICAgICAgY29uc3Qga2V5ID0gcGF0Y2gucGF0aFswXVxyXG5cclxuICAgICAgc3dpdGNoIChwYXRjaC5vcCkge1xyXG4gICAgICAgIGNhc2UgXCJyZXBsYWNlXCI6IHtcclxuICAgICAgICAgIGlmIChrZXkgPT09IFwibGVuZ3RoXCIpIHtcclxuICAgICAgICAgICAgaWYgKHlqcy5sZW5ndGggPiBwYXRjaC52YWx1ZSkge1xyXG4gICAgICAgICAgICAgIGNvbnN0IHRvRGVsZXRlID0geWpzLmxlbmd0aCAtIHBhdGNoLnZhbHVlXHJcbiAgICAgICAgICAgICAgeWpzLmRlbGV0ZShwYXRjaC52YWx1ZSwgdG9EZWxldGUpXHJcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoeWpzLmxlbmd0aCA8IHBhdGNoLnZhbHVlKSB7XHJcbiAgICAgICAgICAgICAgY29uc3QgdG9JbnNlcnQgPSBwYXRjaC52YWx1ZSAtIHlqcy5sZW5ndGhcclxuICAgICAgICAgICAgICB5anMuaW5zZXJ0KHlqcy5sZW5ndGgsIEFycmF5KHRvSW5zZXJ0KS5maWxsKHVuZGVmaW5lZCkpXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHlqcy5kZWxldGUoTnVtYmVyKGtleSkpXHJcbiAgICAgICAgICAgIHlqcy5pbnNlcnQoTnVtYmVyKGtleSksIFt0b1lEYXRhVHlwZShwYXRjaC52YWx1ZSldKVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgYnJlYWtcclxuICAgICAgICB9XHJcbiAgICAgICAgY2FzZSBcImFkZFwiOiB7XHJcbiAgICAgICAgICB5anMuaW5zZXJ0KE51bWJlcihrZXkpLCBbdG9ZRGF0YVR5cGUocGF0Y2gudmFsdWUpXSlcclxuICAgICAgICAgIGJyZWFrXHJcbiAgICAgICAgfVxyXG4gICAgICAgIGNhc2UgXCJyZW1vdmVcIjoge1xyXG4gICAgICAgICAgeWpzLmRlbGV0ZShOdW1iZXIoa2V5KSlcclxuICAgICAgICAgIGJyZWFrXHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlZmF1bHQ6IHtcclxuICAgICAgICAgIHRocm93IGZhaWx1cmUoYGludmFsaWQgcGF0Y2ggb3BlcmF0aW9uIGZvciBhcnJheWApXHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICB0aHJvdyBmYWlsdXJlKFxyXG4gICAgICAgIGBpbnZhbGlkIHBhdGNoIHBhdGgsIHRoZSBZanMgb2JqZWN0IGlzIG9mIGFuIHVua293biB0eXBlLCBzbyBrZXkgXCIke3BhdGNoLnBhdGhbMF19XCIgY2Fubm90IGJlIGZvdW5kIGluIGl0YFxyXG4gICAgICApXHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIHRocm93IGZhaWx1cmUoYGludmFsaWQgcGF0Y2ggcGF0aCwgaXQgY2Fubm90IGJlIGVtcHR5YClcclxuICB9XHJcbn1cclxuIiwiaW1wb3J0IHsgUGF0Y2ggfSBmcm9tIFwibW9ieC1rZXlzdG9uZVwiXHJcbmltcG9ydCAqIGFzIFkgZnJvbSBcInlqc1wiXHJcbmltcG9ydCB7IEpTT05BcnJheSwgSlNPTk9iamVjdCwgSlNPTlZhbHVlIH0gZnJvbSBcIi4uL2pzb25UeXBlc1wiXHJcbmltcG9ydCB7IGZhaWx1cmUgfSBmcm9tIFwiLi4vdXRpbHMvZXJyb3JcIlxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGNvbnZlcnRZanNFdmVudFRvUGF0Y2hlcyhldmVudDogWS5ZRXZlbnQ8YW55Pik6IFBhdGNoW10ge1xyXG4gIGNvbnN0IHBhdGNoZXM6IFBhdGNoW10gPSBbXVxyXG5cclxuICBpZiAoZXZlbnQgaW5zdGFuY2VvZiBZLllNYXBFdmVudCkge1xyXG4gICAgY29uc3Qgc291cmNlID0gZXZlbnQudGFyZ2V0IGFzIFkuTWFwPGFueT5cclxuXHJcbiAgICBldmVudC5jaGFuZ2VzLmtleXMuZm9yRWFjaCgoY2hhbmdlLCBrZXkpID0+IHtcclxuICAgICAgY29uc3QgcGF0aCA9IFsuLi5ldmVudC5wYXRoLCBrZXldXHJcblxyXG4gICAgICBzd2l0Y2ggKGNoYW5nZS5hY3Rpb24pIHtcclxuICAgICAgICBjYXNlIFwiYWRkXCI6XHJcbiAgICAgICAgICBwYXRjaGVzLnB1c2goe1xyXG4gICAgICAgICAgICBvcDogXCJhZGRcIixcclxuICAgICAgICAgICAgcGF0aCxcclxuICAgICAgICAgICAgdmFsdWU6IHRvUGxhaW5WYWx1ZShzb3VyY2UuZ2V0KGtleSkpLFxyXG4gICAgICAgICAgfSlcclxuICAgICAgICAgIGJyZWFrXHJcblxyXG4gICAgICAgIGNhc2UgXCJ1cGRhdGVcIjpcclxuICAgICAgICAgIHBhdGNoZXMucHVzaCh7XHJcbiAgICAgICAgICAgIG9wOiBcInJlcGxhY2VcIixcclxuICAgICAgICAgICAgcGF0aCxcclxuICAgICAgICAgICAgdmFsdWU6IHRvUGxhaW5WYWx1ZShzb3VyY2UuZ2V0KGtleSkpLFxyXG4gICAgICAgICAgfSlcclxuICAgICAgICAgIGJyZWFrXHJcblxyXG4gICAgICAgIGNhc2UgXCJkZWxldGVcIjpcclxuICAgICAgICAgIHBhdGNoZXMucHVzaCh7XHJcbiAgICAgICAgICAgIG9wOiBcInJlbW92ZVwiLFxyXG4gICAgICAgICAgICBwYXRoLFxyXG4gICAgICAgICAgfSlcclxuICAgICAgICAgIGJyZWFrXHJcblxyXG4gICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICB0aHJvdyBmYWlsdXJlKGB1bnN1cHBvcnRlZCBZanMgbWFwIGV2ZW50IGFjdGlvbjogJHtjaGFuZ2UuYWN0aW9ufWApXHJcbiAgICAgIH1cclxuICAgIH0pXHJcbiAgfSBlbHNlIGlmIChldmVudCBpbnN0YW5jZW9mIFkuWUFycmF5RXZlbnQpIHtcclxuICAgIGxldCByZXRhaW4gPSAwXHJcbiAgICBldmVudC5jaGFuZ2VzLmRlbHRhLmZvckVhY2goKGNoYW5nZSkgPT4ge1xyXG4gICAgICBpZiAoY2hhbmdlLnJldGFpbikge1xyXG4gICAgICAgIHJldGFpbiArPSBjaGFuZ2UucmV0YWluXHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChjaGFuZ2UuZGVsZXRlKSB7XHJcbiAgICAgICAgLy8gcmVtb3ZlIFggaXRlbXMgYXQgcmV0YWluIHBvc2l0aW9uXHJcbiAgICAgICAgY29uc3QgcGF0aCA9IFsuLi5ldmVudC5wYXRoLCByZXRhaW5dXHJcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjaGFuZ2UuZGVsZXRlOyBpKyspIHtcclxuICAgICAgICAgIHBhdGNoZXMucHVzaCh7XHJcbiAgICAgICAgICAgIG9wOiBcInJlbW92ZVwiLFxyXG4gICAgICAgICAgICBwYXRoLFxyXG4gICAgICAgICAgfSlcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChjaGFuZ2UuaW5zZXJ0KSB7XHJcbiAgICAgICAgY29uc3QgbmV3VmFsdWVzID0gQXJyYXkuaXNBcnJheShjaGFuZ2UuaW5zZXJ0KSA/IGNoYW5nZS5pbnNlcnQgOiBbY2hhbmdlLmluc2VydF1cclxuICAgICAgICBuZXdWYWx1ZXMuZm9yRWFjaCgodikgPT4ge1xyXG4gICAgICAgICAgY29uc3QgcGF0aCA9IFsuLi5ldmVudC5wYXRoLCByZXRhaW5dXHJcbiAgICAgICAgICBwYXRjaGVzLnB1c2goe1xyXG4gICAgICAgICAgICBvcDogXCJhZGRcIixcclxuICAgICAgICAgICAgcGF0aCxcclxuICAgICAgICAgICAgdmFsdWU6IHRvUGxhaW5WYWx1ZSh2KSxcclxuICAgICAgICAgIH0pXHJcbiAgICAgICAgICByZXRhaW4rK1xyXG4gICAgICAgIH0pXHJcbiAgICAgIH1cclxuICAgIH0pXHJcbiAgfVxyXG5cclxuICByZXR1cm4gcGF0Y2hlc1xyXG59XHJcblxyXG5mdW5jdGlvbiB0b1BsYWluVmFsdWUodjogWS5NYXA8YW55PiB8IFkuQXJyYXk8YW55PiB8IEpTT05WYWx1ZSkge1xyXG4gIGlmICh2IGluc3RhbmNlb2YgWS5NYXAgfHwgdiBpbnN0YW5jZW9mIFkuQXJyYXkpIHtcclxuICAgIHJldHVybiB2LnRvSlNPTigpIGFzIEpTT05PYmplY3QgfCBKU09OQXJyYXlcclxuICB9IGVsc2Uge1xyXG4gICAgcmV0dXJuIHZcclxuICB9XHJcbn1cclxuIiwiaW1wb3J0IHtcclxuICBBbnlEYXRhTW9kZWwsXHJcbiAgQW55TW9kZWwsXHJcbiAgQW55U3RhbmRhcmRUeXBlLFxyXG4gIE1vZGVsQ2xhc3MsXHJcbiAgUGF0Y2gsXHJcbiAgVHlwZVRvRGF0YSxcclxuICBhcHBseVBhdGNoZXMsXHJcbiAgZnJvbVNuYXBzaG90LFxyXG4gIGdldFBhcmVudFRvQ2hpbGRQYXRoLFxyXG4gIG9uR2xvYmFsUGF0Y2hlcyxcclxuICBvblBhdGNoZXMsXHJcbiAgb25TbmFwc2hvdCxcclxufSBmcm9tIFwibW9ieC1rZXlzdG9uZVwiXHJcbmltcG9ydCAqIGFzIFkgZnJvbSBcInlqc1wiXHJcbmltcG9ydCB7IGFwcGx5TW9ieEtleXN0b25lUGF0Y2hUb1lqc09iamVjdCB9IGZyb20gXCIuL2FwcGx5TW9ieEtleXN0b25lUGF0Y2hUb1lqc09iamVjdFwiXHJcbmltcG9ydCB7IGNvbnZlcnRZanNFdmVudFRvUGF0Y2hlcyB9IGZyb20gXCIuL2NvbnZlcnRZanNFdmVudFRvUGF0Y2hlc1wiXHJcblxyXG5leHBvcnQgZnVuY3Rpb24gYmluZFlqc1RvTW9ieEtleXN0b25lPFxyXG4gIFRUeXBlIGV4dGVuZHMgQW55U3RhbmRhcmRUeXBlIHwgTW9kZWxDbGFzczxBbnlNb2RlbD4gfCBNb2RlbENsYXNzPEFueURhdGFNb2RlbD4sXHJcbj4oe1xyXG4gIHlqc0RvYyxcclxuICB5anNPYmplY3QsXHJcbiAgbW9ieEtleXN0b25lVHlwZSxcclxufToge1xyXG4gIHlqc0RvYzogWS5Eb2NcclxuICB5anNPYmplY3Q6IFkuTWFwPHVua25vd24+IHwgWS5BcnJheTx1bmtub3duPlxyXG4gIG1vYnhLZXlzdG9uZVR5cGU6IFRUeXBlXHJcbn0pOiB7XHJcbiAgYm91bmRPYmplY3Q6IFR5cGVUb0RhdGE8VFR5cGU+XHJcbiAgZGlzcG9zZSgpOiB2b2lkXHJcbiAgeWpzT3JpZ2luOiBzeW1ib2xcclxufSB7XHJcbiAgY29uc3QgeWpzSnNvbiA9IHlqc09iamVjdC50b0pTT04oKVxyXG5cclxuICBjb25zdCBpbml0aWFsaXphdGlvbkdsb2JhbFBhdGNoZXM6IHsgdGFyZ2V0OiBvYmplY3Q7IHBhdGNoZXM6IFBhdGNoW10gfVtdID0gW11cclxuXHJcbiAgY29uc3QgY3JlYXRlQm91bmRPYmplY3QgPSAoKSA9PiB7XHJcbiAgICBjb25zdCBkaXNwb3NlT25HbG9iYWxQYXRjaGVzID0gb25HbG9iYWxQYXRjaGVzKCh0YXJnZXQsIHBhdGNoZXMpID0+IHtcclxuICAgICAgaW5pdGlhbGl6YXRpb25HbG9iYWxQYXRjaGVzLnB1c2goeyB0YXJnZXQsIHBhdGNoZXMgfSlcclxuICAgIH0pXHJcblxyXG4gICAgdHJ5IHtcclxuICAgICAgcmV0dXJuIGZyb21TbmFwc2hvdChtb2J4S2V5c3RvbmVUeXBlLCB5anNKc29uIGFzIGFueSlcclxuICAgIH0gZmluYWxseSB7XHJcbiAgICAgIGRpc3Bvc2VPbkdsb2JhbFBhdGNoZXMoKVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgY29uc3QgYm91bmRPYmplY3QgPSBjcmVhdGVCb3VuZE9iamVjdCgpXHJcblxyXG4gIGxldCBhcHBseWluZ01vYnhLZXlzdG9uZUNoYW5nZXMgPSAwXHJcbiAgY29uc3QgeWpzT3JpZ2luID0gU3ltYm9sKFwiYmluZFlqc1RvTW9ieEtleXN0b25lVHJhbnNhY3Rpb25PcmlnaW5cIilcclxuXHJcbiAgLy8gYmluZCBhbnkgY2hhbmdlcyBmcm9tIHlqcyB0byBtb2J4LWtleXN0b25lXHJcbiAgY29uc3Qgb2JzZXJ2ZURlZXBDYiA9IChldmVudHM6IFkuWUV2ZW50PGFueT5bXSkgPT4ge1xyXG4gICAgY29uc3QgcGF0Y2hlczogUGF0Y2hbXSA9IFtdXHJcbiAgICBldmVudHMuZm9yRWFjaCgoZXZlbnQpID0+IHtcclxuICAgICAgaWYgKGV2ZW50LnRyYW5zYWN0aW9uLm9yaWdpbiAhPT0geWpzT3JpZ2luKSB7XHJcbiAgICAgICAgcGF0Y2hlcy5wdXNoKC4uLmNvbnZlcnRZanNFdmVudFRvUGF0Y2hlcyhldmVudCkpXHJcbiAgICAgIH1cclxuICAgIH0pXHJcblxyXG4gICAgaWYgKHBhdGNoZXMubGVuZ3RoID4gMCkge1xyXG4gICAgICBhcHBseWluZ01vYnhLZXlzdG9uZUNoYW5nZXMrK1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGFwcGx5UGF0Y2hlcyhib3VuZE9iamVjdCwgcGF0Y2hlcylcclxuICAgICAgfSBmaW5hbGx5IHtcclxuICAgICAgICBhcHBseWluZ01vYnhLZXlzdG9uZUNoYW5nZXMtLVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICB5anNPYmplY3Qub2JzZXJ2ZURlZXAob2JzZXJ2ZURlZXBDYilcclxuXHJcbiAgLy8gYmluZCBhbnkgY2hhbmdlcyBmcm9tIG1vYngta2V5c3RvbmUgdG8geWpzXHJcbiAgbGV0IHBlbmRpbmdQYXRjaGVzOiBQYXRjaFtdID0gW11cclxuICBjb25zdCBkaXNwb3NlT25QYXRjaGVzID0gb25QYXRjaGVzKGJvdW5kT2JqZWN0LCAocGF0Y2hlcykgPT4ge1xyXG4gICAgaWYgKGFwcGx5aW5nTW9ieEtleXN0b25lQ2hhbmdlcyA+IDApIHtcclxuICAgICAgcmV0dXJuXHJcbiAgICB9XHJcblxyXG4gICAgcGVuZGluZ1BhdGNoZXMucHVzaCguLi5wYXRjaGVzKVxyXG4gIH0pXHJcblxyXG4gIC8vIHRoaXMgaXMgb25seSB1c2VkIHNvIHdlIGNhbiB0cmFuc2FjdCBhbGwgcGF0Y2hlcyB0byB0aGUgc25hcHNob3QgYm91bmRhcnlcclxuICBjb25zdCBkaXNwb3NlT25TbmFwc2hvdCA9IG9uU25hcHNob3QoYm91bmRPYmplY3QsICgpID0+IHtcclxuICAgIGlmIChwZW5kaW5nUGF0Y2hlcy5sZW5ndGggPT09IDApIHtcclxuICAgICAgcmV0dXJuXHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgcGF0Y2hlcyA9IHBlbmRpbmdQYXRjaGVzXHJcbiAgICBwZW5kaW5nUGF0Y2hlcyA9IFtdXHJcblxyXG4gICAgeWpzRG9jLnRyYW5zYWN0KCgpID0+IHtcclxuICAgICAgcGF0Y2hlcy5mb3JFYWNoKChwYXRjaCkgPT4ge1xyXG4gICAgICAgIGFwcGx5TW9ieEtleXN0b25lUGF0Y2hUb1lqc09iamVjdChwYXRjaCwgeWpzT2JqZWN0KVxyXG4gICAgICB9KVxyXG4gICAgfSwgeWpzT3JpZ2luKVxyXG4gIH0pXHJcblxyXG4gIC8vIHN5bmMgaW5pdGlhbCBwYXRjaGVzLCB0aGF0IG1pZ2h0IGluY2x1ZGUgc2V0dGluZyBkZWZhdWx0cywgSURzLCBldGNcclxuICB5anNEb2MudHJhbnNhY3QoKCkgPT4ge1xyXG4gICAgLy8gd2UgbmVlZCB0byBza2lwIGluaXRpYWxpemF0aW9ucyB1bnRpbCB3ZSBoaXQgdGhlIGluaXRpYWxpemF0aW9uIG9mIHRoZSBib3VuZCBvYmplY3RcclxuICAgIC8vIHRoaXMgaXMgYmVjYXVzZSBkZWZhdWx0IG9iamVjdHMgbWlnaHQgYmUgY3JlYXRlZCBhbmQgaW5pdGlhbGl6ZWQgYmVmb3JlIHRoZSBtYWluIG9iamVjdFxyXG4gICAgLy8gYnV0IHdlIGp1c3QgbmVlZCB0byBjYXRjaCB3aGVuIHRob3NlIGFyZSBhY3R1YWxseSBhc3NpZ25lZCB0byB0aGUgYm91bmQgb2JqZWN0XHJcbiAgICBsZXQgYm91bmRPYmplY3RGb3VuZCA9IGZhbHNlXHJcblxyXG4gICAgaW5pdGlhbGl6YXRpb25HbG9iYWxQYXRjaGVzLmZvckVhY2goKHsgdGFyZ2V0LCBwYXRjaGVzIH0pID0+IHtcclxuICAgICAgaWYgKCFib3VuZE9iamVjdEZvdW5kKSB7XHJcbiAgICAgICAgaWYgKHRhcmdldCAhPT0gYm91bmRPYmplY3QpIHtcclxuICAgICAgICAgIHJldHVybiAvLyBza2lwXHJcbiAgICAgICAgfVxyXG4gICAgICAgIGJvdW5kT2JqZWN0Rm91bmQgPSB0cnVlXHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGNvbnN0IHBhcmVudFRvQ2hpbGRQYXRoID0gZ2V0UGFyZW50VG9DaGlsZFBhdGgoYm91bmRPYmplY3QsIHRhcmdldClcclxuICAgICAgLy8gdGhpcyBpcyB1bmRlZmluZWQgb25seSBpZiB0YXJnZXQgaXMgbm90IGEgY2hpbGQgb2YgYm91bmRNb2RlbFxyXG4gICAgICBpZiAocGFyZW50VG9DaGlsZFBhdGggIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIHBhdGNoZXMuZm9yRWFjaCgocGF0Y2gpID0+IHtcclxuICAgICAgICAgIGFwcGx5TW9ieEtleXN0b25lUGF0Y2hUb1lqc09iamVjdChcclxuICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgIC4uLnBhdGNoLFxyXG4gICAgICAgICAgICAgIHBhdGg6IFsuLi5wYXJlbnRUb0NoaWxkUGF0aCwgLi4ucGF0Y2gucGF0aF0sXHJcbiAgICAgICAgICAgIH0sXHJcbiAgICAgICAgICAgIHlqc09iamVjdFxyXG4gICAgICAgICAgKVxyXG4gICAgICAgIH0pXHJcbiAgICAgIH1cclxuICAgIH0pXHJcbiAgfSwgeWpzT3JpZ2luKVxyXG5cclxuICByZXR1cm4ge1xyXG4gICAgYm91bmRPYmplY3QsXHJcbiAgICBkaXNwb3NlOiAoKSA9PiB7XHJcbiAgICAgIGRpc3Bvc2VPblBhdGNoZXMoKVxyXG4gICAgICBkaXNwb3NlT25TbmFwc2hvdCgpXHJcbiAgICAgIHlqc09iamVjdC51bm9ic2VydmVEZWVwKG9ic2VydmVEZWVwQ2IpXHJcbiAgICB9LFxyXG4gICAgeWpzT3JpZ2luLFxyXG4gIH1cclxufVxyXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFHTSxNQUFPLDZCQUE2QixNQUFLO0FBQUEsRUFDN0MsWUFBWSxLQUFXO0FBQ3JCLFVBQU0sR0FBRztBQUdGLFdBQUEsZUFBZSxNQUFNLHFCQUFxQixTQUFTO0FBQUEsRUFDNUQ7QUFDRDtBQUtLLFNBQVUsUUFBUSxLQUFXO0FBQzFCLFNBQUEsSUFBSSxxQkFBcUIsR0FBRztB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
|