@dbx-tools/search 0.6.9
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 +297 -0
- package/index.ts +28 -0
- package/lib/index.d.ts +24 -0
- package/lib/index.js +22 -0
- package/lib/src/client.d.ts +304 -0
- package/lib/src/client.js +598 -0
- package/lib/src/config.d.ts +154 -0
- package/lib/src/config.js +225 -0
- package/lib/src/index-tools.d.ts +19 -0
- package/lib/src/index-tools.js +34 -0
- package/lib/src/lakebase.d.ts +86 -0
- package/lib/src/lakebase.js +218 -0
- package/lib/src/plugin.d.ts +216 -0
- package/lib/src/plugin.js +506 -0
- package/lib/src/query.d.ts +64 -0
- package/lib/src/query.js +109 -0
- package/lib/src/runtime.d.ts +38 -0
- package/lib/src/runtime.js +38 -0
- package/lib/src/schema.d.ts +83 -0
- package/lib/src/schema.js +64 -0
- package/lib/src/tool.d.ts +116 -0
- package/lib/src/tool.js +145 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +65 -0
- package/src/client.ts +848 -0
- package/src/config.ts +356 -0
- package/src/index-tools.ts +40 -0
- package/src/lakebase.ts +299 -0
- package/src/plugin.ts +582 -0
- package/src/query.ts +127 -0
- package/src/runtime.ts +64 -0
- package/src/schema.ts +75 -0
- package/src/tool.ts +169 -0
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small, Meilisearch-shaped client over Databricks AI Search (Vector
|
|
3
|
+
* Search). The whole point of this module is ergonomics: the Databricks SDK's
|
|
4
|
+
* `vectorSearchIndexes.queryIndex({ index_name, columns, query_text,
|
|
5
|
+
* query_type, num_results, filters_json })` is powerful but verbose, and the
|
|
6
|
+
* response is columnar. This client hides all of that behind two objects:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* const client = createSearchClient();
|
|
10
|
+
* const index = client.index("main.support.docs");
|
|
11
|
+
* const { hits } = await index.search("reset my password", { limit: 5 });
|
|
12
|
+
* await index.addDocuments([{ id: "42", title: "Reset", body: "..." }]);
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* `client.search(query, opts)` searches the default index; `client.index(name)`
|
|
16
|
+
* returns a handle bound to one index; `client.universalSearch(query)` fans a
|
|
17
|
+
* query across several indexes and merges the results (Meilisearch's federated
|
|
18
|
+
* / multi-search, the "universal search" the caller asked for). Everything is
|
|
19
|
+
* async and cancellable, resolves the OBO workspace client from the active
|
|
20
|
+
* AppKit execution context (falling back to a service-principal client outside
|
|
21
|
+
* a request), and returns the browser-safe shapes from
|
|
22
|
+
* `@dbx-tools/shared-search`.
|
|
23
|
+
*
|
|
24
|
+
* Autocomplete is just a search with a small `limit` and the raw query text -
|
|
25
|
+
* hybrid mode already prefix-matches, so no separate endpoint is needed; the
|
|
26
|
+
* {@link SearchIndex.autocomplete} helper is a thin, self-documenting alias.
|
|
27
|
+
*
|
|
28
|
+
* @module
|
|
29
|
+
*/
|
|
30
|
+
import { ExecutionError, getExecutionContext } from "@databricks/appkit";
|
|
31
|
+
import { appkit, databricks } from "@dbx-tools/appkit";
|
|
32
|
+
import { invoke, resolve as modelResolve, serving } from "@dbx-tools/model";
|
|
33
|
+
import { async as asyncUtil, json, log, string } from "@dbx-tools/shared-core";
|
|
34
|
+
import { ModelClass } from "@dbx-tools/shared-model";
|
|
35
|
+
import { DEFAULT_MODE, DEFAULT_PAGE_SIZE, DEFAULT_TIMEOUT_MS, indexConfigFor, resolveIndexName, } from "./config.js";
|
|
36
|
+
import { compileFilter, toHits, toQueryType, toRequestColumns, } from "./query.js";
|
|
37
|
+
const logger = log.logger("search/client");
|
|
38
|
+
/**
|
|
39
|
+
* A handle bound to one index. Modeled on Meilisearch's `client.index(uid)`:
|
|
40
|
+
* `search` / `autocomplete` read, `addDocuments` / `deleteDocuments` write (to
|
|
41
|
+
* a direct-access index), and `info` fetches the live definition.
|
|
42
|
+
*/
|
|
43
|
+
export class SearchIndex {
|
|
44
|
+
name;
|
|
45
|
+
client;
|
|
46
|
+
constructor(name, client) {
|
|
47
|
+
this.name = name;
|
|
48
|
+
this.client = client;
|
|
49
|
+
}
|
|
50
|
+
/** Search this index. See {@link SearchClient.search}. */
|
|
51
|
+
search(query, options) {
|
|
52
|
+
return this.client.search(query, { ...options, index: this.name });
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Autocomplete against this index: a search with a small default `limit` and
|
|
56
|
+
* the raw prefix as the query. Hybrid mode already handles prefixes, so this
|
|
57
|
+
* is a self-documenting alias rather than a separate code path.
|
|
58
|
+
*/
|
|
59
|
+
autocomplete(prefix, options) {
|
|
60
|
+
return this.search(prefix, { limit: 5, ...options });
|
|
61
|
+
}
|
|
62
|
+
/** Add or update documents in this (direct-access) index. */
|
|
63
|
+
addDocuments(documents, signal) {
|
|
64
|
+
return this.client.addDocuments(this.name, documents, signal);
|
|
65
|
+
}
|
|
66
|
+
/** Delete documents from this (direct-access) index by primary key. */
|
|
67
|
+
deleteDocuments(ids, signal) {
|
|
68
|
+
return this.client.deleteDocuments(this.name, ids, signal);
|
|
69
|
+
}
|
|
70
|
+
/** Fetch this index's live definition (primary key, columns, readiness). */
|
|
71
|
+
info(signal) {
|
|
72
|
+
return this.client.getIndex(this.name, signal);
|
|
73
|
+
}
|
|
74
|
+
/** Trigger a sync of this (Delta Sync) index from its source table. */
|
|
75
|
+
sync(signal) {
|
|
76
|
+
return this.client.syncIndex(this.name, signal);
|
|
77
|
+
}
|
|
78
|
+
/** Delete this index. */
|
|
79
|
+
delete(signal) {
|
|
80
|
+
return this.client.deleteIndex(this.name, signal);
|
|
81
|
+
}
|
|
82
|
+
/** Create this index if it does not exist, otherwise return the existing one. */
|
|
83
|
+
ensure(options) {
|
|
84
|
+
return this.client.ensureIndex(this.name, options);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The AI Search client. Construct it with {@link createSearchClient} (which
|
|
89
|
+
* reads a resolved config) or directly for one-off use. All reads resolve the
|
|
90
|
+
* OBO workspace client from the active execution context.
|
|
91
|
+
*/
|
|
92
|
+
export class SearchClient {
|
|
93
|
+
config;
|
|
94
|
+
workspaceClientFactory;
|
|
95
|
+
lakebase;
|
|
96
|
+
constructor(config = {
|
|
97
|
+
indexes: [],
|
|
98
|
+
pageSize: DEFAULT_PAGE_SIZE,
|
|
99
|
+
mode: DEFAULT_MODE,
|
|
100
|
+
basePath: "/api/search",
|
|
101
|
+
timeoutMs: DEFAULT_TIMEOUT_MS,
|
|
102
|
+
allowWrite: false,
|
|
103
|
+
}, workspaceClientFactory = defaultWorkspaceClient,
|
|
104
|
+
/**
|
|
105
|
+
* Optional Lakebase full-text FALLBACK backend. Present only when no Vector
|
|
106
|
+
* Search endpoint is configured but a Lakebase pool is available; when set,
|
|
107
|
+
* search / provision / write operations delegate to it and return the exact
|
|
108
|
+
* same shapes, so nothing downstream can tell which backend answered.
|
|
109
|
+
*/
|
|
110
|
+
lakebase) {
|
|
111
|
+
this.config = config;
|
|
112
|
+
this.workspaceClientFactory = workspaceClientFactory;
|
|
113
|
+
this.lakebase = lakebase;
|
|
114
|
+
}
|
|
115
|
+
/** True when this client is answering out of the Lakebase fallback backend. */
|
|
116
|
+
get usesLakebase() {
|
|
117
|
+
return this.lakebase !== undefined;
|
|
118
|
+
}
|
|
119
|
+
/** A handle bound to one index (by full UC name or configured alias). */
|
|
120
|
+
index(reference) {
|
|
121
|
+
const name = resolveIndexName(this.config, reference) ?? reference;
|
|
122
|
+
return new SearchIndex(name, this);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Search one index. `index` may be a full UC name, a configured alias, or
|
|
126
|
+
* omitted to use the default index. Returns hits sorted most-relevant-first.
|
|
127
|
+
*/
|
|
128
|
+
async search(query, options = {}) {
|
|
129
|
+
const text = string.trimToEmpty(query);
|
|
130
|
+
const name = resolveIndexName(this.config, options.index);
|
|
131
|
+
if (name === null) {
|
|
132
|
+
throw new ExecutionError("search: no index configured; set a default index or pass one", {
|
|
133
|
+
context: { operation: "search" },
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (this.lakebase) {
|
|
137
|
+
return this.lakebase.search(name, text, {
|
|
138
|
+
limit: options.limit ?? this.config.pageSize,
|
|
139
|
+
...(options.scoreThreshold !== undefined ? { scoreThreshold: options.scoreThreshold } : {}),
|
|
140
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const known = indexConfigFor(this.config, name);
|
|
144
|
+
const primaryKey = known?.primaryKey;
|
|
145
|
+
const columns = toRequestColumns(options.columns, known?.columns ?? this.config.columns, primaryKey);
|
|
146
|
+
const mode = options.mode ?? this.config.mode;
|
|
147
|
+
const limit = options.limit ?? this.config.pageSize;
|
|
148
|
+
// Databricks only manages embeddings for Delta Sync indexes, so a query
|
|
149
|
+
// against a DIRECT_ACCESS index must carry a query VECTOR, not text. Embed
|
|
150
|
+
// the query client-side in that case (the index type is cached after the
|
|
151
|
+
// first lookup so this costs one extra call per index, not per search).
|
|
152
|
+
const directAccess = await this.isDirectAccess(name, options.signal);
|
|
153
|
+
const queryVector = directAccess
|
|
154
|
+
? (await this.embed([text], this.config.embeddingModel, options.signal))[0]
|
|
155
|
+
: undefined;
|
|
156
|
+
const response = await this.withClient("search", options.signal, async (client, context) => {
|
|
157
|
+
return client.vectorSearchIndexes.queryIndex({
|
|
158
|
+
index_name: name,
|
|
159
|
+
columns,
|
|
160
|
+
...(queryVector ? { query_vector: queryVector } : { query_text: text }),
|
|
161
|
+
query_type: toQueryType(mode),
|
|
162
|
+
num_results: limit,
|
|
163
|
+
...(compileFilter(options.filter) ? { filters_json: compileFilter(options.filter) } : {}),
|
|
164
|
+
...(options.scoreThreshold !== undefined
|
|
165
|
+
? { score_threshold: options.scoreThreshold }
|
|
166
|
+
: {}),
|
|
167
|
+
}, context);
|
|
168
|
+
});
|
|
169
|
+
const hits = toHits(response, primaryKey);
|
|
170
|
+
return { query: text, index: name, hits, count: hits.length };
|
|
171
|
+
}
|
|
172
|
+
/** Cache of index-name -> is-DIRECT_ACCESS, so a search embeds its query only when needed. */
|
|
173
|
+
directAccessCache = new Map();
|
|
174
|
+
/** Whether an index is DIRECT_ACCESS (memoized); a lookup failure assumes Delta Sync. */
|
|
175
|
+
async isDirectAccess(name, signal) {
|
|
176
|
+
const cached = this.directAccessCache.get(name);
|
|
177
|
+
if (cached !== undefined)
|
|
178
|
+
return cached;
|
|
179
|
+
try {
|
|
180
|
+
const info = await this.getIndex(name, signal);
|
|
181
|
+
const value = info.directAccess ?? false;
|
|
182
|
+
this.directAccessCache.set(name, value);
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Fan a query across several indexes and merge the hits, sorted by score -
|
|
191
|
+
* the "universal search" a single box over many collections needs. Each
|
|
192
|
+
* index is searched concurrently; an index that errors is logged and skipped
|
|
193
|
+
* so one bad index does not sink the whole search.
|
|
194
|
+
*/
|
|
195
|
+
async universalSearch(query, options = {}) {
|
|
196
|
+
const text = string.trimToEmpty(query);
|
|
197
|
+
const names = options.indexes && options.indexes.length > 0
|
|
198
|
+
? options.indexes.map((ref) => resolveIndexName(this.config, ref) ?? ref)
|
|
199
|
+
: this.config.indexes.map((i) => i.name);
|
|
200
|
+
const perIndex = options.limit ?? this.config.pageSize;
|
|
201
|
+
const settled = await Promise.allSettled(names.map(async (name) => {
|
|
202
|
+
const result = await this.search(text, {
|
|
203
|
+
index: name,
|
|
204
|
+
limit: perIndex,
|
|
205
|
+
...(options.mode ? { mode: options.mode } : {}),
|
|
206
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
207
|
+
});
|
|
208
|
+
return result.hits.map((hit) => ({ ...hit, index: name }));
|
|
209
|
+
}));
|
|
210
|
+
const hits = settled
|
|
211
|
+
.flatMap((outcome, i) => {
|
|
212
|
+
if (outcome.status === "fulfilled")
|
|
213
|
+
return outcome.value;
|
|
214
|
+
logger.warn("universal-index-failed", { index: names[i] });
|
|
215
|
+
return [];
|
|
216
|
+
})
|
|
217
|
+
.sort((a, b) => b.score - a.score);
|
|
218
|
+
return { query: text, hits, count: hits.length };
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Resolve an index reference (full UC name or configured alias) to the name
|
|
222
|
+
* the API expects, failing fast when it resolves to nothing. Without this a
|
|
223
|
+
* blank reference - what an omitted `index` becomes when no default is
|
|
224
|
+
* configured - reaches the SDK as an empty `index_name`, which builds a
|
|
225
|
+
* URL with the name segment missing and comes back as a confusing
|
|
226
|
+
* `ENDPOINT_NOT_FOUND` instead of naming the real problem.
|
|
227
|
+
*/
|
|
228
|
+
requireIndexName(reference, operation) {
|
|
229
|
+
const name = string.trimToNull(resolveIndexName(this.config, reference) ?? reference);
|
|
230
|
+
if (name === null) {
|
|
231
|
+
throw new ExecutionError(`search: no index configured; set a default index or pass one to ${operation}`, { context: { operation } });
|
|
232
|
+
}
|
|
233
|
+
return name;
|
|
234
|
+
}
|
|
235
|
+
/** Fetch an index's live definition. */
|
|
236
|
+
async getIndex(reference, signal) {
|
|
237
|
+
const name = this.requireIndexName(reference, "getIndex");
|
|
238
|
+
const index = await this.withClient("getIndex", signal, (client, context) => client.vectorSearchIndexes.getIndex({ index_name: name }, context));
|
|
239
|
+
const directAccess = index.direct_access_index_spec !== undefined;
|
|
240
|
+
const spec = index.delta_sync_index_spec ?? index.direct_access_index_spec;
|
|
241
|
+
// Delta Sync surfaces its embedding source columns directly; a direct-access
|
|
242
|
+
// index carries its columns in schema_json (minus the vector column).
|
|
243
|
+
let columns = (spec?.embedding_source_columns ?? []).map((c) => c.name ?? "").filter(Boolean);
|
|
244
|
+
const schemaJson = index.direct_access_index_spec?.schema_json;
|
|
245
|
+
if (directAccess && schemaJson) {
|
|
246
|
+
const schema = json.parseRecord(schemaJson) ?? {};
|
|
247
|
+
const vectorNames = new Set((index.direct_access_index_spec?.embedding_vector_columns ?? [])
|
|
248
|
+
.map((c) => c.name)
|
|
249
|
+
.filter((n) => Boolean(n)));
|
|
250
|
+
columns = Object.keys(schema).filter((c) => !vectorNames.has(c));
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
name: index.name ?? name,
|
|
254
|
+
...(index.endpoint_name ? { endpoint: index.endpoint_name } : {}),
|
|
255
|
+
...(index.primary_key ? { primaryKey: index.primary_key } : {}),
|
|
256
|
+
columns,
|
|
257
|
+
ready: index.status?.ready ?? false,
|
|
258
|
+
...(index.status?.indexed_row_count !== undefined
|
|
259
|
+
? { rowCount: index.status.indexed_row_count }
|
|
260
|
+
: {}),
|
|
261
|
+
...(directAccess ? { directAccess: true } : {}),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/** Add or update documents in a direct-access index. */
|
|
265
|
+
async addDocuments(reference, documents, signal) {
|
|
266
|
+
const name = this.requireIndexName(reference, "addDocuments");
|
|
267
|
+
if (this.lakebase) {
|
|
268
|
+
return this.lakebase.addDocuments(name, documents, this.config.ensureOnSetup?.textColumn ?? "text", signal);
|
|
269
|
+
}
|
|
270
|
+
await this.withClient("addDocuments", signal, (client, context) => client.vectorSearchIndexes.upsertDataVectorIndex({ index_name: name, inputs_json: JSON.stringify(documents) }, context));
|
|
271
|
+
return { index: name, count: documents.length };
|
|
272
|
+
}
|
|
273
|
+
/** Delete documents from a direct-access index by primary key. */
|
|
274
|
+
async deleteDocuments(reference, ids, signal) {
|
|
275
|
+
const name = this.requireIndexName(reference, "deleteDocuments");
|
|
276
|
+
if (this.lakebase) {
|
|
277
|
+
return this.lakebase.deleteDocuments(name, ids, signal);
|
|
278
|
+
}
|
|
279
|
+
await this.withClient("deleteDocuments", signal, (client, context) => client.vectorSearchIndexes.deleteDataVectorIndex({ index_name: name, primary_keys: ids.map(String) }, context));
|
|
280
|
+
return { index: name, count: ids.length };
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Resolve an embedding endpoint id for creating a Delta Sync index. Reuses
|
|
284
|
+
* the model resolver: a configured / passed name is fuzzy-matched against the
|
|
285
|
+
* live catalogue, otherwise the highest-ranked embedding endpoint is chosen.
|
|
286
|
+
*/
|
|
287
|
+
async resolveEmbeddingModel(requested, signal) {
|
|
288
|
+
const explicit = string.trimToNull(requested ?? this.config.embeddingModel);
|
|
289
|
+
// An explicit name that already looks like an endpoint id (no whitespace)
|
|
290
|
+
// is used verbatim - no need to fetch and fuzzy-match the live catalogue.
|
|
291
|
+
// A genuinely loose name (e.g. "gte large") still resolves against it.
|
|
292
|
+
if (explicit && !/\s/.test(explicit))
|
|
293
|
+
return explicit;
|
|
294
|
+
return this.withClient("resolveEmbeddingModel", signal, async (client) => {
|
|
295
|
+
const host = (await client.config.getHost()).toString();
|
|
296
|
+
const endpoints = await serving.listServingEndpoints(client, host);
|
|
297
|
+
const { modelId } = modelResolve.resolveModel(endpoints, {
|
|
298
|
+
...(explicit ? { explicit } : { modelClass: ModelClass.Embedding }),
|
|
299
|
+
});
|
|
300
|
+
return string.trimToNull(modelId);
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Embed text via a Databricks embedding serving endpoint, returning one
|
|
305
|
+
* vector per input. Used to seed a direct-access index and to turn a search
|
|
306
|
+
* query into a query vector - Databricks only manages embeddings for Delta
|
|
307
|
+
* Sync indexes, so a direct-access index (no Delta table, no warehouse) needs
|
|
308
|
+
* the client to embed on write and on query. The endpoint is resolved the
|
|
309
|
+
* same way as for index creation when not named.
|
|
310
|
+
*/
|
|
311
|
+
async embed(texts, model, signal) {
|
|
312
|
+
if (texts.length === 0)
|
|
313
|
+
return [];
|
|
314
|
+
const endpoint = await this.resolveEmbeddingModel(model, signal);
|
|
315
|
+
if (!endpoint) {
|
|
316
|
+
throw new ExecutionError("search: could not resolve an embedding model to embed text", {
|
|
317
|
+
context: { operation: "embed" },
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
return this.withClient("embed", signal, async (client) => {
|
|
321
|
+
const host = (await client.config.getHost()).toString();
|
|
322
|
+
const url = invoke.invocationsUrl(host, endpoint);
|
|
323
|
+
const headers = await invoke.authHeaders(client);
|
|
324
|
+
const response = await fetch(url, {
|
|
325
|
+
method: "POST",
|
|
326
|
+
headers: { ...headers, "content-type": "application/json" },
|
|
327
|
+
body: JSON.stringify({ input: texts }),
|
|
328
|
+
...(signal ? { signal } : {}),
|
|
329
|
+
});
|
|
330
|
+
if (!response.ok) {
|
|
331
|
+
throw new ExecutionError(`search: embedding endpoint ${endpoint} failed (${response.status})`, { context: { operation: "embed" } });
|
|
332
|
+
}
|
|
333
|
+
const body = (await response.json());
|
|
334
|
+
const vectors = (body.data ?? []).map((row) => row.embedding);
|
|
335
|
+
if (vectors.length !== texts.length) {
|
|
336
|
+
throw new ExecutionError("search: embedding response did not match the input count", {
|
|
337
|
+
context: { operation: "embed" },
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
return vectors;
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
/** The vector dimension a model produces (embeds a probe string once). */
|
|
344
|
+
async embeddingDimension(model, signal) {
|
|
345
|
+
const [vector] = await this.embed(["dimension probe"], model, signal);
|
|
346
|
+
if (!vector || vector.length === 0) {
|
|
347
|
+
throw new ExecutionError("search: could not determine the embedding dimension", {
|
|
348
|
+
context: { operation: "createIndex" },
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return vector.length;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Create an AI Search index with as little ceremony as possible. Two shapes:
|
|
355
|
+
*
|
|
356
|
+
* - **Delta Sync** (the default): pass `sourceTable`; Databricks computes
|
|
357
|
+
* embeddings from the text column and keeps the index synced. The
|
|
358
|
+
* embedding model is resolved automatically when not named.
|
|
359
|
+
* - **Direct Access**: omit `sourceTable` and pass `embeddingDimension`;
|
|
360
|
+
* you write vectors yourself via {@link addDocuments}.
|
|
361
|
+
*
|
|
362
|
+
* Everything else infers: the endpoint from the plugin config, the primary
|
|
363
|
+
* key (`id`), the text column (`text` / `content` / `body`), and the vector
|
|
364
|
+
* column (`embedding`). Returns the created index's {@link IndexInfo}.
|
|
365
|
+
*/
|
|
366
|
+
async createIndex(name, options = {}) {
|
|
367
|
+
const endpoint = options.endpoint ?? this.config.endpoint;
|
|
368
|
+
if (!endpoint) {
|
|
369
|
+
throw new ExecutionError("search: no Vector Search endpoint configured; pass `endpoint` or set it on the plugin", { context: { operation: "createIndex" } });
|
|
370
|
+
}
|
|
371
|
+
const primaryKey = options.primaryKey ?? "id";
|
|
372
|
+
const direct = !options.sourceTable;
|
|
373
|
+
const sourceColumn = options.embeddingSourceColumn ?? "text";
|
|
374
|
+
const vectorColumn = options.embeddingVectorColumn ?? "embedding";
|
|
375
|
+
// Delta Sync lets Databricks embed a source column, so it needs an embedding
|
|
376
|
+
// model. A direct-access index stores vectors YOU supply (Databricks only
|
|
377
|
+
// supports managed embeddings on Delta Sync), so it needs a dimension; we
|
|
378
|
+
// resolve one from the embedding model when not given so a direct-access
|
|
379
|
+
// index still works with zero extra config (embed via `embed()` on seed +
|
|
380
|
+
// query - see `provision` / `search`).
|
|
381
|
+
const embeddingModel = (await this.resolveEmbeddingModel(options.embeddingModel, options.signal)) ?? undefined;
|
|
382
|
+
if (!embeddingModel) {
|
|
383
|
+
throw new ExecutionError("search: could not resolve an embedding model; pass `embeddingModel`", { context: { operation: "createIndex" } });
|
|
384
|
+
}
|
|
385
|
+
const dimension = options.embeddingDimension ?? (await this.embeddingDimension(embeddingModel, options.signal));
|
|
386
|
+
const schema = {
|
|
387
|
+
[primaryKey]: "string",
|
|
388
|
+
[sourceColumn]: "string",
|
|
389
|
+
...(options.schema ?? {}),
|
|
390
|
+
};
|
|
391
|
+
const directSpec = {
|
|
392
|
+
direct_access_index_spec: {
|
|
393
|
+
embedding_vector_columns: [{ name: vectorColumn, embedding_dimension: dimension }],
|
|
394
|
+
schema_json: JSON.stringify({ ...schema, [vectorColumn]: `array<float>` }),
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
await this.withClient("createIndex", options.signal, (client, context) => client.vectorSearchIndexes.createIndex({
|
|
398
|
+
name,
|
|
399
|
+
endpoint_name: endpoint,
|
|
400
|
+
primary_key: primaryKey,
|
|
401
|
+
index_type: direct ? "DIRECT_ACCESS" : "DELTA_SYNC",
|
|
402
|
+
...(direct
|
|
403
|
+
? directSpec
|
|
404
|
+
: {
|
|
405
|
+
delta_sync_index_spec: {
|
|
406
|
+
source_table: options.sourceTable,
|
|
407
|
+
pipeline_type: options.pipelineType ?? "TRIGGERED",
|
|
408
|
+
embedding_source_columns: [
|
|
409
|
+
{ name: sourceColumn, embedding_model_endpoint_name: embeddingModel },
|
|
410
|
+
],
|
|
411
|
+
...(options.columnsToSync && options.columnsToSync.length > 0
|
|
412
|
+
? { columns_to_sync: options.columnsToSync }
|
|
413
|
+
: {}),
|
|
414
|
+
},
|
|
415
|
+
}),
|
|
416
|
+
}, context));
|
|
417
|
+
logger.info("index-created", {
|
|
418
|
+
index: name,
|
|
419
|
+
endpoint,
|
|
420
|
+
type: direct ? "DIRECT_ACCESS" : "DELTA_SYNC",
|
|
421
|
+
});
|
|
422
|
+
return this.getIndex(name, options.signal);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Create the index if it does not already exist, otherwise return the
|
|
426
|
+
* existing one. Idempotent - safe to call on every boot to guarantee an
|
|
427
|
+
* index is present.
|
|
428
|
+
*/
|
|
429
|
+
async ensureIndex(name, options = {}) {
|
|
430
|
+
try {
|
|
431
|
+
return await this.getIndex(name, options.signal);
|
|
432
|
+
}
|
|
433
|
+
catch {
|
|
434
|
+
return this.createIndex(name, options);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Ensure an index exists, is online, and (optionally) holds seed data - the
|
|
439
|
+
* "wire up a real index on boot" path. Idempotent and cheap to call every
|
|
440
|
+
* boot: it creates the endpoint and index only if missing, waits for them to
|
|
441
|
+
* come online, and seeds documents ONLY when the index is still empty.
|
|
442
|
+
*
|
|
443
|
+
* For the demo/dummy-data case this needs no Delta table and no warehouse:
|
|
444
|
+
* the default is a MANAGED direct-access index (Databricks embeds the `text`
|
|
445
|
+
* column on write and query), so seeding is just an `addDocuments` of plain
|
|
446
|
+
* rows and search-by-text works immediately.
|
|
447
|
+
*/
|
|
448
|
+
async provision(name, options = {}) {
|
|
449
|
+
if (this.lakebase) {
|
|
450
|
+
// Lakebase fallback: no endpoint, no embeddings, no vectors - just a
|
|
451
|
+
// Postgres full-text table. Returns an IndexInfo-shaped result so the
|
|
452
|
+
// caller's logging + readiness handling is identical to Vector Search.
|
|
453
|
+
const rowCount = await this.lakebase.provision(name, {
|
|
454
|
+
textColumn: options.embeddingSourceColumn ?? "text",
|
|
455
|
+
...(options.seed ? { seed: options.seed } : {}),
|
|
456
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
457
|
+
});
|
|
458
|
+
return {
|
|
459
|
+
name,
|
|
460
|
+
primaryKey: options.primaryKey ?? "id",
|
|
461
|
+
columns: [],
|
|
462
|
+
ready: true,
|
|
463
|
+
rowCount,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
const wait = options.wait ?? true;
|
|
467
|
+
const timeoutMs = options.timeoutMs ?? 20 * 60 * 1000;
|
|
468
|
+
const endpoint = options.endpoint ?? this.config.endpoint;
|
|
469
|
+
if (endpoint)
|
|
470
|
+
await this.ensureEndpoint(endpoint, { wait, signal: options.signal });
|
|
471
|
+
const { seed: _seed, wait: _wait, timeoutMs: _timeoutMs, ...createOptions } = options;
|
|
472
|
+
let info = await this.ensureIndex(name, createOptions);
|
|
473
|
+
if (wait && !info.ready)
|
|
474
|
+
info = await this.waitForIndexReady(name, timeoutMs, options.signal);
|
|
475
|
+
const seed = options.seed ?? [];
|
|
476
|
+
if (seed.length > 0 && (info.rowCount ?? 0) === 0) {
|
|
477
|
+
const sourceColumn = options.embeddingSourceColumn ?? "text";
|
|
478
|
+
const vectorColumn = options.embeddingVectorColumn ?? "embedding";
|
|
479
|
+
// Direct-access indexes store vectors we supply, so embed the text column
|
|
480
|
+
// for any seed row that did not already carry a vector.
|
|
481
|
+
const needEmbed = seed.some((doc) => doc[vectorColumn] === undefined);
|
|
482
|
+
let rows = seed;
|
|
483
|
+
if (needEmbed) {
|
|
484
|
+
const texts = seed.map((doc) => string.trimToEmpty(String(doc[sourceColumn] ?? "")));
|
|
485
|
+
const vectors = await this.embed(texts, options.embeddingModel, options.signal);
|
|
486
|
+
rows = seed.map((doc, i) => doc[vectorColumn] === undefined ? { ...doc, [vectorColumn]: vectors[i] } : doc);
|
|
487
|
+
}
|
|
488
|
+
await this.addDocuments(name, rows, options.signal);
|
|
489
|
+
logger.info("index-seeded", { index: name, count: rows.length });
|
|
490
|
+
info = await this.getIndex(name, options.signal);
|
|
491
|
+
}
|
|
492
|
+
return info;
|
|
493
|
+
}
|
|
494
|
+
/** Poll an index until it reports ready, or throw after {@link timeoutMs}. */
|
|
495
|
+
async waitForIndexReady(name, timeoutMs, signal) {
|
|
496
|
+
const deadline = Date.now() + timeoutMs;
|
|
497
|
+
let info = await this.getIndex(name, signal);
|
|
498
|
+
while (!info.ready) {
|
|
499
|
+
if (Date.now() > deadline) {
|
|
500
|
+
throw new ExecutionError(`search: index ${name} did not come online in time`, {
|
|
501
|
+
context: { operation: "provision" },
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
await asyncUtil.sleep(5000, signal);
|
|
505
|
+
info = await this.getIndex(name, signal);
|
|
506
|
+
}
|
|
507
|
+
return info;
|
|
508
|
+
}
|
|
509
|
+
/** Trigger a sync of a Delta Sync index from its source table. */
|
|
510
|
+
async syncIndex(reference, signal) {
|
|
511
|
+
const name = this.requireIndexName(reference, "syncIndex");
|
|
512
|
+
await this.withClient("syncIndex", signal, (client, context) => client.vectorSearchIndexes.syncIndex({ index_name: name }, context));
|
|
513
|
+
logger.info("index-synced", { index: name });
|
|
514
|
+
}
|
|
515
|
+
/** Delete an index. */
|
|
516
|
+
async deleteIndex(reference, signal) {
|
|
517
|
+
const name = this.requireIndexName(reference, "deleteIndex");
|
|
518
|
+
await this.withClient("deleteIndex", signal, (client, context) => client.vectorSearchIndexes.deleteIndex({ index_name: name }, context));
|
|
519
|
+
logger.info("index-deleted", { index: name });
|
|
520
|
+
}
|
|
521
|
+
/** List the indexes hosted on a Vector Search endpoint (name + type only). */
|
|
522
|
+
async listIndexes(endpoint, signal) {
|
|
523
|
+
const endpointName = endpoint ?? this.config.endpoint;
|
|
524
|
+
if (!endpointName) {
|
|
525
|
+
throw new ExecutionError("search: no endpoint configured to list indexes", {
|
|
526
|
+
context: { operation: "listIndexes" },
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
return this.withClient("listIndexes", signal, async (client, context) => {
|
|
530
|
+
const names = [];
|
|
531
|
+
for await (const index of client.vectorSearchIndexes.listIndexes({ endpoint_name: endpointName }, context)) {
|
|
532
|
+
if (index.name)
|
|
533
|
+
names.push(index.name);
|
|
534
|
+
}
|
|
535
|
+
return names;
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Ensure a Vector Search endpoint exists, creating a `STANDARD` one when it
|
|
540
|
+
* does not. Optionally wait for it to come online. Idempotent.
|
|
541
|
+
*/
|
|
542
|
+
async ensureEndpoint(name, options = {}) {
|
|
543
|
+
const endpoint = name ?? this.config.endpoint;
|
|
544
|
+
if (!endpoint) {
|
|
545
|
+
throw new ExecutionError("search: no endpoint name to ensure", {
|
|
546
|
+
context: { operation: "ensureEndpoint" },
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
await this.withClient("ensureEndpoint", options.signal, async (client, context) => {
|
|
550
|
+
try {
|
|
551
|
+
await client.vectorSearchEndpoints.getEndpoint({ endpoint_name: endpoint }, context);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
const waiter = await client.vectorSearchEndpoints.createEndpoint({ name: endpoint, endpoint_type: "STANDARD" }, context);
|
|
556
|
+
logger.info("endpoint-created", { endpoint });
|
|
557
|
+
if (options.wait)
|
|
558
|
+
await waiter.wait();
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Resolve the workspace client and run one call under a bounded timeout. The
|
|
564
|
+
* caller's signal (if any) and a timeout are merged into one SDK `Context`
|
|
565
|
+
* via {@link appkit.databricks.toContext}, so either unwinds the request.
|
|
566
|
+
*/
|
|
567
|
+
async withClient(operation, signal, fn) {
|
|
568
|
+
const client = this.workspaceClientFactory();
|
|
569
|
+
const controller = new AbortController();
|
|
570
|
+
const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
571
|
+
const context = databricks.toContext(controller, signal);
|
|
572
|
+
try {
|
|
573
|
+
return await fn(client, context);
|
|
574
|
+
}
|
|
575
|
+
catch (err) {
|
|
576
|
+
if (signal?.aborted)
|
|
577
|
+
throw ExecutionError.canceled();
|
|
578
|
+
logger.warn("execution-failed", { operation });
|
|
579
|
+
throw err;
|
|
580
|
+
}
|
|
581
|
+
finally {
|
|
582
|
+
clearTimeout(timer);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
/** The OBO workspace client from the active context, or a service-principal client. */
|
|
587
|
+
function defaultWorkspaceClient() {
|
|
588
|
+
const ctx = appkit.tryGetExecutionContext();
|
|
589
|
+
if (ctx?.client)
|
|
590
|
+
return ctx.client;
|
|
591
|
+
// Outside a request scope (a script, a test): a fresh env-auth client.
|
|
592
|
+
return getExecutionContext().client;
|
|
593
|
+
}
|
|
594
|
+
/** Construct a {@link SearchClient} from a resolved config. */
|
|
595
|
+
export function createSearchClient(config, workspaceClientFactory, lakebase) {
|
|
596
|
+
return new SearchClient(config, workspaceClientFactory, lakebase);
|
|
597
|
+
}
|
|
598
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTRCRztBQUVILE9BQU8sRUFBRSxjQUFjLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUV6RSxPQUFPLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxNQUFNLEVBQUUsT0FBTyxJQUFJLFlBQVksRUFBRSxPQUFPLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQVE1RSxPQUFPLEVBQUUsS0FBSyxJQUFJLFNBQVMsRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQy9FLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNyRCxPQUFPLEVBQ0wsWUFBWSxFQUNaLGlCQUFpQixFQUNqQixrQkFBa0IsRUFDbEIsY0FBYyxFQUNkLGdCQUFnQixHQUVqQixNQUFNLGFBQWEsQ0FBQztBQUNyQixPQUFPLEVBQ0wsYUFBYSxFQUNiLE1BQU0sRUFDTixXQUFXLEVBQ1gsZ0JBQWdCLEdBRWpCLE1BQU0sWUFBWSxDQUFDO0FBSXBCLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUM7QUE4QjNDOzs7O0dBSUc7QUFDSCxNQUFNLE9BQU8sV0FBVztJQUVYO0lBQ1E7SUFGbkIsWUFDVyxJQUFZLEVBQ0osTUFBb0I7UUFENUIsU0FBSSxHQUFKLElBQUksQ0FBUTtRQUNKLFdBQU0sR0FBTixNQUFNLENBQWM7SUFDcEMsQ0FBQztJQUVKLDBEQUEwRDtJQUMxRCxNQUFNLENBQUMsS0FBYSxFQUFFLE9BQXVCO1FBQzNDLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLEVB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Qy9DLE1BQU0sR0FBRyxDQUFDO1FBQ1osQ0FBQztnQkFBUyxDQUFDO1lBQ1QsWUFBWSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3RCLENBQUM7SUFDSCxDQUFDO0NBQ0Y7QUFFRCx1RkFBdUY7QUFDdkYsU0FBUyxzQkFBc0I7SUFDN0IsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLHNCQUFzQixFQUFFLENBQUM7SUFDNUMsSUFBSSxHQUFHLEVBQUUsTUFBTTtRQUFFLE9BQU8sR0FBRyxDQUFDLE1BQU0sQ0FBQztJQUNuQyx1RUFBdUU7SUFDdkUsT0FBTyxtQkFBbUIsRUFBRSxDQUFDLE1BQU0sQ0FBQztBQUN0QyxDQUFDO0FBRUQsK0RBQStEO0FBQy9ELE1BQU0sVUFBVSxrQkFBa0IsQ0FDaEMsTUFBNkIsRUFDN0Isc0JBQWtELEVBQ2xELFFBQWdDO0lBRWhDLE9BQU8sSUFBSSxZQUFZLENBQUMsTUFBTSxFQUFFLHNCQUFzQixFQUFFLFFBQVEsQ0FBQyxDQUFDO0FBQ3BFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEEgc21hbGwsIE1laWxpc2VhcmNoLXNoYXBlZCBjbGllbnQgb3ZlciBEYXRhYnJpY2tzIEFJIFNlYXJjaCAoVmVjdG9yXG4gKiBTZWFyY2gpLiBUaGUgd2hvbGUgcG9pbnQgb2YgdGhpcyBtb2R1bGUgaXMgZXJnb25vbWljczogdGhlIERhdGFicmlja3MgU0RLJ3NcbiAqIGB2ZWN0b3JTZWFyY2hJbmRleGVzLnF1ZXJ5SW5kZXgoeyBpbmRleF9uYW1lLCBjb2x1bW5zLCBxdWVyeV90ZXh0LFxuICogcXVlcnlfdHlwZSwgbnVtX3Jlc3VsdHMsIGZpbHRlcnNfanNvbiB9KWAgaXMgcG93ZXJmdWwgYnV0IHZlcmJvc2UsIGFuZCB0aGVcbiAqIHJlc3BvbnNlIGlzIGNvbHVtbmFyLiBUaGlzIGNsaWVudCBoaWRlcyBhbGwgb2YgdGhhdCBiZWhpbmQgdHdvIG9iamVjdHM6XG4gKlxuICogYGBgdHNcbiAqIGNvbnN0IGNsaWVudCA9IGNyZWF0ZVNlYXJjaENsaWVudCgpO1xuICogY29uc3QgaW5kZXggPSBjbGllbnQuaW5kZXgoXCJtYWluLnN1cHBvcnQuZG9jc1wiKTtcbiAqIGNvbnN0IHsgaGl0cyB9ID0gYXdhaXQgaW5kZXguc2VhcmNoKFwicmVzZXQgbXkgcGFzc3dvcmRcIiwgeyBsaW1pdDogNSB9KTtcbiAqIGF3YWl0IGluZGV4LmFkZERvY3VtZW50cyhbeyBpZDogXCI0MlwiLCB0aXRsZTogXCJSZXNldFwiLCBib2R5OiBcIi4uLlwiIH1dKTtcbiAqIGBgYFxuICpcbiAqIGBjbGllbnQuc2VhcmNoKHF1ZXJ5LCBvcHRzKWAgc2VhcmNoZXMgdGhlIGRlZmF1bHQgaW5kZXg7IGBjbGllbnQuaW5kZXgobmFtZSlgXG4gKiByZXR1cm5zIGEgaGFuZGxlIGJvdW5kIHRvIG9uZSBpbmRleDsgYGNsaWVudC51bml2ZXJzYWxTZWFyY2gocXVlcnkpYCBmYW5zIGFcbiAqIHF1ZXJ5IGFjcm9zcyBzZXZlcmFsIGluZGV4ZXMgYW5kIG1lcmdlcyB0aGUgcmVzdWx0cyAoTWVpbGlzZWFyY2gncyBmZWRlcmF0ZWRcbiAqIC8gbXVsdGktc2VhcmNoLCB0aGUgXCJ1bml2ZXJzYWwgc2VhcmNoXCIgdGhlIGNhbGxlciBhc2tlZCBmb3IpLiBFdmVyeXRoaW5nIGlzXG4gKiBhc3luYyBhbmQgY2FuY2VsbGFibGUsIHJlc29sdmVzIHRoZSBPQk8gd29ya3NwYWNlIGNsaWVudCBmcm9tIHRoZSBhY3RpdmVcbiAqIEFwcEtpdCBleGVjdXRpb24gY29udGV4dCAoZmFsbGluZyBiYWNrIHRvIGEgc2VydmljZS1wcmluY2lwYWwgY2xpZW50IG91dHNpZGVcbiAqIGEgcmVxdWVzdCksIGFuZCByZXR1cm5zIHRoZSBicm93c2VyLXNhZmUgc2hhcGVzIGZyb21cbiAqIGBAZGJ4LXRvb2xzL3NoYXJlZC1zZWFyY2hgLlxuICpcbiAqIEF1dG9jb21wbGV0ZSBpcyBqdXN0IGEgc2VhcmNoIHdpdGggYSBzbWFsbCBgbGltaXRgIGFuZCB0aGUgcmF3IHF1ZXJ5IHRleHQgLVxuICogaHlicmlkIG1vZGUgYWxyZWFkeSBwcmVmaXgtbWF0Y2hlcywgc28gbm8gc2VwYXJhdGUgZW5kcG9pbnQgaXMgbmVlZGVkOyB0aGVcbiAqIHtAbGluayBTZWFyY2hJbmRleC5hdXRvY29tcGxldGV9IGhlbHBlciBpcyBhIHRoaW4sIHNlbGYtZG9jdW1lbnRpbmcgYWxpYXMuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IEV4ZWN1dGlvbkVycm9yLCBnZXRFeGVjdXRpb25Db250ZXh0IH0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdFwiO1xuaW1wb3J0IHsgQ29udGV4dCB9IGZyb20gXCJAZGF0YWJyaWNrcy9zZGstZXhwZXJpbWVudGFsXCI7XG5pbXBvcnQgeyBhcHBraXQsIGRhdGFicmlja3MgfSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbmltcG9ydCB7IGludm9rZSwgcmVzb2x2ZSBhcyBtb2RlbFJlc29sdmUsIHNlcnZpbmcgfSBmcm9tIFwiQGRieC10b29scy9tb2RlbFwiO1xuaW1wb3J0IHR5cGUge1xuICBTZWFyY2hEb2N1bWVudCxcbiAgU2VhcmNoSGl0LFxuICBTZWFyY2hNb2RlLFxuICBTZWFyY2hSZXN1bHQsXG4gIFVwc2VydFJlc3VsdCxcbn0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLXNlYXJjaFwiO1xuaW1wb3J0IHsgYXN5bmMgYXMgYXN5bmNVdGlsLCBqc29uLCBsb2csIHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyBNb2RlbENsYXNzIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLW1vZGVsXCI7XG5pbXBvcnQge1xuICBERUZBVUxUX01PREUsXG4gIERFRkFVTFRfUEFHRV9TSVpFLFxuICBERUZBVUxUX1RJTUVPVVRfTVMsXG4gIGluZGV4Q29uZmlnRm9yLFxuICByZXNvbHZlSW5kZXhOYW1lLFxuICB0eXBlIFJlc29sdmVkU2VhcmNoQ29uZmlnLFxufSBmcm9tIFwiLi9jb25maWcudHNcIjtcbmltcG9ydCB7XG4gIGNvbXBpbGVGaWx0ZXIsXG4gIHRvSGl0cyxcbiAgdG9RdWVyeVR5cGUsXG4gIHRvUmVxdWVzdENvbHVtbnMsXG4gIHR5cGUgUXVlcnlSZXNwb25zZUxpa2UsXG59IGZyb20gXCIuL3F1ZXJ5LnRzXCI7XG5pbXBvcnQgdHlwZSB7IExha2ViYXNlU2VhcmNoQmFja2VuZCB9IGZyb20gXCIuL2xha2ViYXNlLnRzXCI7XG5cbnR5cGUgV29ya3NwYWNlQ2xpZW50TGlrZSA9IGFwcGtpdC5Xb3Jrc3BhY2VDbGllbnRMaWtlO1xuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcInNlYXJjaC9jbGllbnRcIik7XG5cbi8qKiBPcHRpb25zIGFjY2VwdGVkIGJ5IGEgc2luZ2xlLWluZGV4IHNlYXJjaC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU2VhcmNoT3B0aW9ucyB7XG4gIC8qKiBNYXhpbXVtIGhpdHMgdG8gcmV0dXJuLiBEZWZhdWx0cyB0byB0aGUgY29uZmlndXJlZCBwYWdlIHNpemUuICovXG4gIGxpbWl0PzogbnVtYmVyO1xuICAvKiogTWF0Y2ggbW9kZS4gRGVmYXVsdHMgdG8gdGhlIGNvbmZpZ3VyZWQgbW9kZSAoaHlicmlkKS4gKi9cbiAgbW9kZT86IFNlYXJjaE1vZGU7XG4gIC8qKiBDb2x1bW5zIHRvIHJldHVybiBwZXIgaGl0LiBEZWZhdWx0cyB0byB0aGUgaW5kZXgncyBjb25maWd1cmVkIGNvbHVtbnMuICovXG4gIGNvbHVtbnM/OiByZWFkb25seSBzdHJpbmdbXTtcbiAgLyoqIEF0dHJpYnV0ZSBmaWx0ZXJzIGFzIGB7IGNvbHVtbjogdmFsdWUgfWAgb3IgYHsgY29sdW1uOiB7IFwiPj1cIjogbiB9IH1gLiAqL1xuICBmaWx0ZXI/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgLyoqIERyb3AgaGl0cyBiZWxvdyB0aGlzIHNjb3JlLiAqL1xuICBzY29yZVRocmVzaG9sZD86IG51bWJlcjtcbiAgLyoqIEV4dGVybmFsIGNhbmNlbGxhdGlvbi4gKi9cbiAgc2lnbmFsPzogQWJvcnRTaWduYWw7XG59XG5cbi8qKiBPcHRpb25zIGFjY2VwdGVkIGJ5IGEgdW5pdmVyc2FsIChmZWRlcmF0ZWQpIHNlYXJjaC4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVW5pdmVyc2FsU2VhcmNoT3B0aW9ucyB7XG4gIC8qKiBJbmRleGVzIHRvIHNlYXJjaC4gRGVmYXVsdHMgdG8gZXZlcnkga25vd24gaW5kZXguICovXG4gIGluZGV4ZXM/OiByZWFkb25seSBzdHJpbmdbXTtcbiAgLyoqIE1heGltdW0gaGl0cyBwZXIgaW5kZXggYmVmb3JlIG1lcmdpbmcuICovXG4gIGxpbWl0PzogbnVtYmVyO1xuICAvKiogTWF0Y2ggbW9kZS4gKi9cbiAgbW9kZT86IFNlYXJjaE1vZGU7XG4gIC8qKiBFeHRlcm5hbCBjYW5jZWxsYXRpb24uICovXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsO1xufVxuXG4vKipcbiAqIEEgaGFuZGxlIGJvdW5kIHRvIG9uZSBpbmRleC4gTW9kZWxlZCBvbiBNZWlsaXNlYXJjaCdzIGBjbGllbnQuaW5kZXgodWlkKWA6XG4gKiBgc2VhcmNoYCAvIGBhdXRvY29tcGxldGVgIHJlYWQsIGBhZGREb2N1bWVudHNgIC8gYGRlbGV0ZURvY3VtZW50c2Agd3JpdGUgKHRvXG4gKiBhIGRpcmVjdC1hY2Nlc3MgaW5kZXgpLCBhbmQgYGluZm9gIGZldGNoZXMgdGhlIGxpdmUgZGVmaW5pdGlvbi5cbiAqL1xuZXhwb3J0IGNsYXNzIFNlYXJjaEluZGV4IHtcbiAgY29uc3RydWN0b3IoXG4gICAgcmVhZG9ubHkgbmFtZTogc3RyaW5nLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgY2xpZW50OiBTZWFyY2hDbGllbnQsXG4gICkge31cblxuICAvKiogU2VhcmNoIHRoaXMgaW5kZXguIFNlZSB7QGxpbmsgU2VhcmNoQ2xpZW50LnNlYXJjaH0uICovXG4gIHNlYXJjaChxdWVyeTogc3RyaW5nLCBvcHRpb25zPzogU2VhcmNoT3B0aW9ucyk6IFByb21pc2U8U2VhcmNoUmVzdWx0PiB7XG4gICAgcmV0dXJuIHRoaXMuY2xpZW50LnNlYXJjaChxdWVyeSwgeyAuLi5vcHRpb25zLCBpbmRleDogdGhpcy5uYW1lIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEF1dG9jb21wbGV0ZSBhZ2FpbnN0IHRoaXMgaW5kZXg6IGEgc2VhcmNoIHdpdGggYSBzbWFsbCBkZWZhdWx0IGBsaW1pdGAgYW5kXG4gICAqIHRoZSByYXcgcHJlZml4IGFzIHRoZSBxdWVyeS4gSHlicmlkIG1vZGUgYWxyZWFkeSBoYW5kbGVzIHByZWZpeGVzLCBzbyB0aGlzXG4gICAqIGlzIGEgc2VsZi1kb2N1bWVudGluZyBhbGlhcyByYXRoZXIgdGhhbiBhIHNlcGFyYXRlIGNvZGUgcGF0aC5cbiAgICovXG4gIGF1dG9jb21wbGV0ZShwcmVmaXg6IHN0cmluZywgb3B0aW9ucz86IFNlYXJjaE9wdGlvbnMpOiBQcm9taXNlPFNlYXJjaFJlc3VsdD4ge1xuICAgIHJldHVybiB0aGlzLnNlYXJjaChwcmVmaXgsIHsgbGltaXQ6IDUsIC4uLm9wdGlvbnMgfSk7XG4gIH1cblxuICAvKiogQWRkIG9yIHVwZGF0ZSBkb2N1bWVudHMgaW4gdGhpcyAoZGlyZWN0LWFjY2VzcykgaW5kZXguICovXG4gIGFkZERvY3VtZW50cyhkb2N1bWVudHM6IFNlYXJjaERvY3VtZW50W10sIHNpZ25hbD86IEFib3J0U2lnbmFsKTogUHJvbWlzZTxVcHNlcnRSZXN1bHQ+IHtcbiAgICByZXR1cm4gdGhpcy5jbGllbnQuYWRkRG9jdW1lbnRzKHRoaXMubmFtZSwgZG9jdW1lbnRzLCBzaWduYWwpO1xuICB9XG5cbiAgLyoqIERlbGV0ZSBkb2N1bWVudHMgZnJvbSB0aGlzIChkaXJlY3QtYWNjZXNzKSBpbmRleCBieSBwcmltYXJ5IGtleS4gKi9cbiAgZGVsZXRlRG9jdW1lbnRzKGlkczogQXJyYXk8c3RyaW5nIHwgbnVtYmVyPiwgc2lnbmFsPzogQWJvcnRTaWduYWwpOiBQcm9taXNlPFVwc2VydFJlc3VsdD4ge1xuICAgIHJldHVybiB0aGlzLmNsaWVudC5kZWxldGVEb2N1bWVudHModGhpcy5uYW1lLCBpZHMsIHNpZ25hbCk7XG4gIH1cblxuICAvKiogRmV0Y2ggdGhpcyBpbmRleCdzIGxpdmUgZGVmaW5pdGlvbiAocHJpbWFyeSBrZXksIGNvbHVtbnMsIHJlYWRpbmVzcykuICovXG4gIGluZm8oc2lnbmFsPzogQWJvcnRTaWduYWwpOiBQcm9taXNlPEluZGV4SW5mbz4ge1xuICAgIHJldHVybiB0aGlzLmNsaWVudC5nZXRJbmRleCh0aGlzLm5hbWUsIHNpZ25hbCk7XG4gIH1cblxuICAvKiogVHJpZ2dlciBhIHN5bmMgb2YgdGhpcyAoRGVsdGEgU3luYykgaW5kZXggZnJvbSBpdHMgc291cmNlIHRhYmxlLiAqL1xuICBzeW5jKHNpZ25hbD86IEFib3J0U2lnbmFsKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuY2xpZW50LnN5bmNJbmRleCh0aGlzLm5hbWUsIHNpZ25hbCk7XG4gIH1cblxuICAvKiogRGVsZXRlIHRoaXMgaW5kZXguICovXG4gIGRlbGV0ZShzaWduYWw/OiBBYm9ydFNpZ25hbCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmNsaWVudC5kZWxldGVJbmRleCh0aGlzLm5hbWUsIHNpZ25hbCk7XG4gIH1cblxuICAvKiogQ3JlYXRlIHRoaXMgaW5kZXggaWYgaXQgZG9lcyBub3QgZXhpc3QsIG90aGVyd2lzZSByZXR1cm4gdGhlIGV4aXN0aW5nIG9uZS4gKi9cbiAgZW5zdXJlKG9wdGlvbnM/OiBDcmVhdGVJbmRleE9wdGlvbnMpOiBQcm9taXNlPEluZGV4SW5mbz4ge1xuICAgIHJldHVybiB0aGlzLmNsaWVudC5lbnN1cmVJbmRleCh0aGlzLm5hbWUsIG9wdGlvbnMpO1xuICB9XG59XG5cbi8qKiBBIHJlc29sdmVkIGxpdmUgaW5kZXggZGVmaW5pdGlvbi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSW5kZXhJbmZvIHtcbiAgbmFtZTogc3RyaW5nO1xuICBlbmRwb2ludD86IHN0cmluZztcbiAgcHJpbWFyeUtleT86IHN0cmluZztcbiAgY29sdW1uczogc3RyaW5nW107XG4gIHJlYWR5OiBib29sZWFuO1xuICByb3dDb3VudD86IG51bWJlcjtcbiAgLyoqIFRydWUgZm9yIGEgRElSRUNUX0FDQ0VTUyBpbmRleCAoeW91IHN1cHBseSB2ZWN0b3JzOyBxdWVyaWVzIGVtYmVkIGNsaWVudC1zaWRlKS4gKi9cbiAgZGlyZWN0QWNjZXNzPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBPcHRpb25zIGZvciB7QGxpbmsgU2VhcmNoQ2xpZW50LmNyZWF0ZUluZGV4fSAvIHtAbGluayBTZWFyY2hDbGllbnQuZW5zdXJlSW5kZXh9LlxuICogVGhlIGdvYWwgaXMgdGhlIHNhbWUgYXMgdGhlIHJlc3Qgb2YgdGhlIGNsaWVudDogbmFtZSBhIHNvdXJjZSB0YWJsZSBhbmQgYVxuICogdGV4dCBjb2x1bW4sIGFuZCBldmVyeXRoaW5nIGVsc2UgLSB0aGUgZW5kcG9pbnQsIHRoZSBlbWJlZGRpbmcgbW9kZWwsIHRoZVxuICogaW5kZXggdHlwZSwgdGhlIHN5bmMgbW9kZSAtIGhhcyBhIHNlbnNpYmxlIGRlZmF1bHQgdGhhdCBpbmZlcnMgZnJvbSB0aGVcbiAqIHdvcmtzcGFjZSwgb3ZlcnJpZGFibGUgd2hlbiBhIGRlcGxveW1lbnQgbmVlZHMgdG8gZ28gZGVlcGVyLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIENyZWF0ZUluZGV4T3B0aW9ucyB7XG4gIC8qKlxuICAgKiBTb3VyY2UgRGVsdGEgdGFibGUgKGNhdGFsb2cuc2NoZW1hLnRhYmxlKSBmb3IgYSBEZWx0YSBTeW5jIGluZGV4LiBQcm92aWRlXG4gICAqIHRoaXMgZm9yIHRoZSBjb21tb24gY2FzZTogRGF0YWJyaWNrcyBjb21wdXRlcyBhbmQgc3luY3MgZW1iZWRkaW5ncyBmcm9tIGl0LlxuICAgKiBPbWl0IGl0IChhbmQgcGFzcyB7QGxpbmsgZW1iZWRkaW5nRGltZW5zaW9ufSkgdG8gY3JlYXRlIGEgZGlyZWN0LWFjY2Vzc1xuICAgKiBpbmRleCB5b3Ugd3JpdGUgdmVjdG9ycyB0byB5b3Vyc2VsZi5cbiAgICovXG4gIHNvdXJjZVRhYmxlPzogc3RyaW5nO1xuICAvKiogUHJpbWFyeS1rZXkgY29sdW1uLiBEZWZhdWx0cyB0byBgaWRgLiAqL1xuICBwcmltYXJ5S2V5Pzogc3RyaW5nO1xuICAvKipcbiAgICogVGhlIHRleHQgY29sdW1uIGVtYmVkZGluZ3MgYXJlIGNvbXB1dGVkIGZyb20gKERlbHRhIFN5bmMpLiBEZWZhdWx0cyB0byB0aGVcbiAgICogZmlyc3Qgb2YgYHRleHRgIC8gYGNvbnRlbnRgIC8gYGJvZHlgIHByZXNlbnQsIGVsc2UgdGhlIGNhbGxlciBtdXN0IHNldCBpdC5cbiAgICovXG4gIGVtYmVkZGluZ1NvdXJjZUNvbHVtbj86IHN0cmluZztcbiAgLyoqXG4gICAqIEVtYmVkZGluZyBtb2RlbCBlbmRwb2ludC4gQSBsb29zZSBuYW1lIGlzIGZ1enp5LW1hdGNoZWQ7IHdoZW4gb21pdHRlZCB0aGVcbiAgICogYmVzdCBlbWJlZGRpbmcgZW5kcG9pbnQgaW4gdGhlIHdvcmtzcGFjZSBpcyBjaG9zZW4gKHtAbGluayByZXNvbHZlRW1iZWRkaW5nTW9kZWx9KS5cbiAgICovXG4gIGVtYmVkZGluZ01vZGVsPzogc3RyaW5nO1xuICAvKiogVmVjdG9yIFNlYXJjaCBlbmRwb2ludCB0byBob3N0IHRoZSBpbmRleCBvbi4gRGVmYXVsdHMgdG8gdGhlIHBsdWdpbidzIGBlbmRwb2ludGAuICovXG4gIGVuZHBvaW50Pzogc3RyaW5nO1xuICAvKiogRm9yIGEgZGlyZWN0LWFjY2VzcyBpbmRleDogdGhlIGVtYmVkZGluZyB2ZWN0b3IgZGltZW5zaW9uIChzZWxmLW1hbmFnZWQgdmVjdG9ycywgbm8gc291cmNlIHRhYmxlKS4gKi9cbiAgZW1iZWRkaW5nRGltZW5zaW9uPzogbnVtYmVyO1xuICAvKiogRm9yIGEgZGlyZWN0LWFjY2VzcyBpbmRleDogdGhlIGNvbHVtbiB0aGUgdmVjdG9yIGlzIHN0b3JlZCBpbi4gRGVmYXVsdHMgdG8gYGVtYmVkZGluZ2AuICovXG4gIGVtYmVkZGluZ1ZlY3RvckNvbHVtbj86IHN0cmluZztcbiAgLyoqXG4gICAqIFRoZSBjb2x1bW4tbmFtZSAtPiB0eXBlIG1hcCB1c2VkIHRvIGJ1aWxkIGEgZGlyZWN0LWFjY2VzcyBpbmRleCdzXG4gICAqIGBzY2hlbWFfanNvbmAgKHR5cGVzOiBgc3RyaW5nYCwgYGludGAsIGBsb25nYCwgYGZsb2F0YCwgYGRvdWJsZWAsXG4gICAqIGBib29sZWFuYCwgYGRhdGVgLCBgdGltZXN0YW1wYCkuIE11c3QgaW5jbHVkZSB0aGUgcHJpbWFyeSBrZXkgYW5kIGFueVxuICAgKiBjb2x1bW5zIHlvdSB1cHNlcnQuIERlZmF1bHRzIHRvIGB7IGlkOiBcInN0cmluZ1wiLCB0ZXh0OiBcInN0cmluZ1wiIH1gLiBUaGVcbiAgICogZW1iZWRkaW5nIHZlY3RvciBjb2x1bW4gaXMgYWRkZWQgYXV0b21hdGljYWxseS5cbiAgICovXG4gIHNjaGVtYT86IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG4gIC8qKiBTeW5jIG1vZGUgZm9yIGEgRGVsdGEgU3luYyBpbmRleC4gYFRSSUdHRVJFRGAgKGRlZmF1bHQpIHN5bmNzIG9uIGRlbWFuZDsgYENPTlRJTlVPVVNgIGtlZXBzIGZyZXNoLiAqL1xuICBwaXBlbGluZVR5cGU/OiBcIlRSSUdHRVJFRFwiIHwgXCJDT05USU5VT1VTXCI7XG4gIC8qKiBFeHRyYSBjb2x1bW5zIHRvIHN5bmMgYWxvbmdzaWRlIHRoZSBlbWJlZGRpbmcgc291cmNlIChEZWx0YSBTeW5jKS4gKi9cbiAgY29sdW1uc1RvU3luYz86IHN0cmluZ1tdO1xuICAvKiogRXh0ZXJuYWwgY2FuY2VsbGF0aW9uLiAqL1xuICBzaWduYWw/OiBBYm9ydFNpZ25hbDtcbn1cblxuLyoqIE9wdGlvbnMgZm9yIHtAbGluayBTZWFyY2hDbGllbnQuZW5zdXJlRW5kcG9pbnR9LiAqL1xuZXhwb3J0IGludGVyZmFjZSBFbnN1cmVFbmRwb2ludE9wdGlvbnMge1xuICAvKiogV2FpdCBmb3IgdGhlIGVuZHBvaW50IHRvIGNvbWUgb25saW5lIGJlZm9yZSByZXR1cm5pbmcuIERlZmF1bHRzIHRvIGZhbHNlLiAqL1xuICB3YWl0PzogYm9vbGVhbjtcbiAgLyoqIEV4dGVybmFsIGNhbmNlbGxhdGlvbi4gKi9cbiAgc2lnbmFsPzogQWJvcnRTaWduYWw7XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3Ige0BsaW5rIFNlYXJjaENsaWVudC5wcm92aXNpb259IC0gYSBvbmUtY2FsbCBcIm1ha2UgdGhpcyBpbmRleCByZWFsXG4gKiBhbmQgc2VhcmNoYWJsZVwiIHVzZWQgYXQgYm9vdCBvciBpbiBhIHNlZWQgc2NyaXB0LiBJdCBlbnN1cmVzIHRoZSBlbmRwb2ludCxcbiAqIGVuc3VyZXMgdGhlIGluZGV4LCBhbmQgKG9wdGlvbmFsbHkpIHNlZWRzIGRvY3VtZW50cyB3aGVuIHRoZSBpbmRleCBpcyBlbXB0eS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBQcm92aXNpb25PcHRpb25zIGV4dGVuZHMgQ3JlYXRlSW5kZXhPcHRpb25zIHtcbiAgLyoqIERvY3VtZW50cyB0byBzZWVkIHdoZW4gdGhlIGluZGV4IGhhcyBubyByb3dzIHlldC4gU2tpcHBlZCBpZiBpdCBhbHJlYWR5IGhhcyBkYXRhLiAqL1xuICBzZWVkPzogU2VhcmNoRG9jdW1lbnRbXTtcbiAgLyoqXG4gICAqIFdhaXQgZm9yIHRoZSBlbmRwb2ludCBBTkQgaW5kZXggdG8gY29tZSBvbmxpbmUgYmVmb3JlIHJldHVybmluZyAobmVlZGVkXG4gICAqIGJlZm9yZSBzZWVkaW5nKS4gRGVmYXVsdHMgdG8gdHJ1ZS4gRW5kcG9pbnQgY3JlYXRpb24gY2FuIHRha2UgbWFueSBtaW51dGVzLlxuICAgKi9cbiAgd2FpdD86IGJvb2xlYW47XG4gIC8qKiBIb3cgbG9uZyB0byB3YWl0IGZvciByZWFkaW5lc3MgYmVmb3JlIGdpdmluZyB1cC4gRGVmYXVsdHMgdG8gMjAgbWludXRlcy4gKi9cbiAgdGltZW91dE1zPzogbnVtYmVyO1xufVxuXG4vKipcbiAqIFRoZSBBSSBTZWFyY2ggY2xpZW50LiBDb25zdHJ1Y3QgaXQgd2l0aCB7QGxpbmsgY3JlYXRlU2VhcmNoQ2xpZW50fSAod2hpY2hcbiAqIHJlYWRzIGEgcmVzb2x2ZWQgY29uZmlnKSBvciBkaXJlY3RseSBmb3Igb25lLW9mZiB1c2UuIEFsbCByZWFkcyByZXNvbHZlIHRoZVxuICogT0JPIHdvcmtzcGFjZSBjbGllbnQgZnJvbSB0aGUgYWN0aXZlIGV4ZWN1dGlvbiBjb250ZXh0LlxuICovXG5leHBvcnQgY2xhc3MgU2VhcmNoQ2xpZW50IHtcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBjb25maWc6IFJlc29sdmVkU2VhcmNoQ29uZmlnID0ge1xuICAgICAgaW5kZXhlczogW10sXG4gICAgICBwYWdlU2l6ZTogREVGQVVMVF9QQUdFX1NJWkUsXG4gICAgICBtb2RlOiBERUZBVUxUX01PREUsXG4gICAgICBiYXNlUGF0aDogXCIvYXBpL3NlYXJjaFwiLFxuICAgICAgdGltZW91dE1zOiBERUZBVUxUX1RJTUVPVVRfTVMsXG4gICAgICBhbGxvd1dyaXRlOiBmYWxzZSxcbiAgICB9LFxuICAgIHByaXZhdGUgcmVhZG9ubHkgd29ya3NwYWNlQ2xpZW50RmFjdG9yeTogKCkgPT4gV29ya3NwYWNlQ2xpZW50TGlrZSA9IGRlZmF1bHRXb3Jrc3BhY2VDbGllbnQsXG4gICAgLyoqXG4gICAgICogT3B0aW9uYWwgTGFrZWJhc2UgZnVsbC10ZXh0IEZBTExCQUNLIGJhY2tlbmQuIFByZXNlbnQgb25seSB3aGVuIG5vIFZlY3RvclxuICAgICAqIFNlYXJjaCBlbmRwb2ludCBpcyBjb25maWd1cmVkIGJ1dCBhIExha2ViYXNlIHBvb2wgaXMgYXZhaWxhYmxlOyB3aGVuIHNldCxcbiAgICAgKiBzZWFyY2ggLyBwcm92aXNpb24gLyB3cml0ZSBvcGVyYXRpb25zIGRlbGVnYXRlIHRvIGl0IGFuZCByZXR1cm4gdGhlIGV4YWN0XG4gICAgICogc2FtZSBzaGFwZXMsIHNvIG5vdGhpbmcgZG93bnN0cmVhbSBjYW4gdGVsbCB3aGljaCBiYWNrZW5kIGFuc3dlcmVkLlxuICAgICAqL1xuICAgIHByaXZhdGUgcmVhZG9ubHkgbGFrZWJhc2U/OiBMYWtlYmFzZVNlYXJjaEJhY2tlbmQsXG4gICkge31cblxuICAvKiogVHJ1ZSB3aGVuIHRoaXMgY2xpZW50IGlzIGFuc3dlcmluZyBvdXQgb2YgdGhlIExha2ViYXNlIGZhbGxiYWNrIGJhY2tlbmQuICovXG4gIGdldCB1c2VzTGFrZWJhc2UoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMubGFrZWJhc2UgIT09IHVuZGVmaW5lZDtcbiAgfVxuXG4gIC8qKiBBIGhhbmRsZSBib3VuZCB0byBvbmUgaW5kZXggKGJ5IGZ1bGwgVUMgbmFtZSBvciBjb25maWd1cmVkIGFsaWFzKS4gKi9cbiAgaW5kZXgocmVmZXJlbmNlOiBzdHJpbmcpOiBTZWFyY2hJbmRleCB7XG4gICAgY29uc3QgbmFtZSA9IHJlc29sdmVJbmRleE5hbWUodGhpcy5jb25maWcsIHJlZmVyZW5jZSkgPz8gcmVmZXJlbmNlO1xuICAgIHJldHVybiBuZXcgU2VhcmNoSW5kZXgobmFtZSwgdGhpcyk7XG4gIH1cblxuICAvKipcbiAgICogU2VhcmNoIG9uZSBpbmRleC4gYGluZGV4YCBtYXkgYmUgYSBmdWxsIFVDIG5hbWUsIGEgY29uZmlndXJlZCBhbGlhcywgb3JcbiAgICogb21pdHRlZCB0byB1c2UgdGhlIGRlZmF1bHQgaW5kZXguIFJldHVybnMgaGl0cyBzb3J0ZWQgbW9zdC1yZWxldmFudC1maXJzdC5cbiAgICovXG4gIGFzeW5jIHNlYXJjaChcbiAgICBxdWVyeTogc3RyaW5nLFxuICAgIG9wdGlvbnM6IFNlYXJjaE9wdGlvbnMgJiB7IGluZGV4Pzogc3RyaW5nIH0gPSB7fSxcbiAgKTogUHJvbWlzZTxTZWFyY2hSZXN1bHQ+IHtcbiAgICBjb25zdCB0ZXh0ID0gc3RyaW5nLnRyaW1Ub0VtcHR5KHF1ZXJ5KTtcbiAgICBjb25zdCBuYW1lID0gcmVzb2x2ZUluZGV4TmFtZSh0aGlzLmNvbmZpZywgb3B0aW9ucy5pbmRleCk7XG4gICAgaWYgKG5hbWUgPT09IG51bGwpIHtcbiAgICAgIHRocm93IG5ldyBFeGVjdXRpb25FcnJvcihcInNlYXJjaDogbm8gaW5kZXggY29uZmlndXJlZDsgc2V0IGEgZGVmYXVsdCBpbmRleCBvciBwYXNzIG9uZVwiLCB7XG4gICAgICAgIGNvbnRleHQ6IHsgb3BlcmF0aW9uOiBcInNlYXJjaFwiIH0sXG4gICAgICB9KTtcbiAgICB9XG4gICAgaWYgKHRoaXMubGFrZWJhc2UpIHtcbiAgICAgIHJldHVybiB0aGlzLmxha2ViYXNlLnNlYXJjaChuYW1lLCB0ZXh0LCB7XG4gICAgICAgIGxpbWl0OiBvcHRpb25zLmxpbWl0ID8/IHRoaXMuY29uZmlnLnBhZ2VTaXplLFxuICAgICAgICAuLi4ob3B0aW9ucy5zY29yZVRocmVzaG9sZCAhPT0gdW5kZWZpbmVkID8geyBzY29yZVRocmVzaG9sZDogb3B0aW9ucy5zY29yZVRocmVzaG9sZCB9IDoge30pLFxuICAgICAgICAuLi4ob3B0aW9ucy5zaWduYWwgPyB7IHNpZ25hbDogb3B0aW9ucy5zaWduYWwgfSA6IHt9KSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICBjb25zdCBrbm93biA9IGluZGV4Q29uZmlnRm9yKHRoaXMuY29uZmlnLCBuYW1lKTtcbiAgICBjb25zdCBwcmltYXJ5S2V5ID0ga25vd24/LnByaW1hcnlLZXk7XG4gICAgY29uc3QgY29sdW1ucyA9IHRvUmVxdWVzdENvbHVtbnMoXG4gICAgICBvcHRpb25zLmNvbHVtbnMsXG4gICAgICBrbm93bj8uY29sdW1ucyA/PyB0aGlzLmNvbmZpZy5jb2x1bW5zLFxuICAgICAgcHJpbWFyeUtleSxcbiAgICApO1xuICAgIGNvbnN0IG1vZGUgPSBvcHRpb25zLm1vZGUgPz8gdGhpcy5jb25maWcubW9kZTtcbiAgICBjb25zdCBsaW1pdCA9IG9wdGlvbnMubGltaXQgPz8gdGhpcy5jb25maWcucGFnZVNpemU7XG5cbiAgICAvLyBEYXRhYnJpY2tzIG9ubHkgbWFuYWdlcyBlbWJlZGRpbmdzIGZvciBEZWx0YSBTeW5jIGluZGV4ZXMsIHNvIGEgcXVlcnlcbiAgICAvLyBhZ2FpbnN0IGEgRElSRUNUX0FDQ0VTUyBpbmRleCBtdXN0IGNhcnJ5IGEgcXVlcnkgVkVDVE9SLCBub3QgdGV4dC4gRW1iZWRcbiAgICAvLyB0aGUgcXVlcnkgY2xpZW50LXNpZGUgaW4gdGhhdCBjYXNlICh0aGUgaW5kZXggdHlwZSBpcyBjYWNoZWQgYWZ0ZXIgdGhlXG4gICAgLy8gZmlyc3QgbG9va3VwIHNvIHRoaXMgY29zdHMgb25lIGV4dHJhIGNhbGwgcGVyIGluZGV4LCBub3QgcGVyIHNlYXJjaCkuXG4gICAgY29uc3QgZGlyZWN0QWNjZXNzID0gYXdhaXQgdGhpcy5pc0RpcmVjdEFjY2VzcyhuYW1lLCBvcHRpb25zLnNpZ25hbCk7XG4gICAgY29uc3QgcXVlcnlWZWN0b3IgPSBkaXJlY3RBY2Nlc3NcbiAgICAgID8gKGF3YWl0IHRoaXMuZW1iZWQoW3RleHRdLCB0aGlzLmNvbmZpZy5lbWJlZGRpbmdNb2RlbCwgb3B0aW9ucy5zaWduYWwpKVswXVxuICAgICAgOiB1bmRlZmluZWQ7XG5cbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IHRoaXMud2l0aENsaWVudChcInNlYXJjaFwiLCBvcHRpb25zLnNpZ25hbCwgYXN5bmMgKGNsaWVudCwgY29udGV4dCkgPT4ge1xuICAgICAgcmV0dXJuIGNsaWVudC52ZWN0b3JTZWFyY2hJbmRleGVzLnF1ZXJ5SW5kZXgoXG4gICAgICAgIHtcbiAgICAgICAgICBpbmRleF9uYW1lOiBuYW1lLFxuICAgICAgICAgIGNvbHVtbnMsXG4gICAgICAgICAgLi4uKHF1ZXJ5VmVjdG9yID8geyBxdWVyeV92ZWN0b3I6IHF1ZXJ5VmVjdG9yIH0gOiB7IHF1ZXJ5X3RleHQ6IHRleHQgfSksXG4gICAgICAgICAgcXVlcnlfdHlwZTogdG9RdWVyeVR5cGUobW9kZSksXG4gICAgICAgICAgbnVtX3Jlc3VsdHM6IGxpbWl0LFxuICAgICAgICAgIC4uLihjb21waWxlRmlsdGVyKG9wdGlvbnMuZmlsdGVyKSA/IHsgZmlsdGVyc19qc29uOiBjb21waWxlRmlsdGVyKG9wdGlvbnMuZmlsdGVyKSB9IDoge30pLFxuICAgICAgICAgIC4uLihvcHRpb25zLnNjb3JlVGhyZXNob2xkICE9PSB1bmRlZmluZWRcbiAgICAgICAgICAgID8geyBzY29yZV90aHJlc2hvbGQ6IG9wdGlvbnMuc2NvcmVUaHJlc2hvbGQgfVxuICAgICAgICAgICAgOiB7fSksXG4gICAgICAgIH0sXG4gICAgICAgIGNvbnRleHQsXG4gICAgICApO1xuICAgIH0pO1xuXG4gICAgY29uc3QgaGl0cyA9IHRvSGl0cyhyZXNwb25zZSBhcyBRdWVyeVJlc3BvbnNlTGlrZSwgcHJpbWFyeUtleSk7XG4gICAgcmV0dXJuIHsgcXVlcnk6IHRleHQsIGluZGV4OiBuYW1lLCBoaXRzLCBjb3VudDogaGl0cy5sZW5ndGggfTtcbiAgfVxuXG4gIC8qKiBDYWNoZSBvZiBpbmRleC1uYW1lIC0+IGlzLURJUkVDVF9BQ0NFU1MsIHNvIGEgc2VhcmNoIGVtYmVkcyBpdHMgcXVlcnkgb25seSB3aGVuIG5lZWRlZC4gKi9cbiAgcHJpdmF0ZSByZWFkb25seSBkaXJlY3RBY2Nlc3NDYWNoZSA9IG5ldyBNYXA8c3RyaW5nLCBib29sZWFuPigpO1xuXG4gIC8qKiBXaGV0aGVyIGFuIGluZGV4IGlzIERJUkVDVF9BQ0NFU1MgKG1lbW9pemVkKTsgYSBsb29rdXAgZmFpbHVyZSBhc3N1bWVzIERlbHRhIFN5bmMuICovXG4gIHByaXZhdGUgYXN5bmMgaXNEaXJlY3RBY2Nlc3MobmFtZTogc3RyaW5nLCBzaWduYWw/OiBBYm9ydFNpZ25hbCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IGNhY2hlZCA9IHRoaXMuZGlyZWN0QWNjZXNzQ2FjaGUuZ2V0KG5hbWUpO1xuICAgIGlmIChjYWNoZWQgIT09IHVuZGVmaW5lZCkgcmV0dXJuIGNhY2hlZDtcbiAgICB0cnkge1xuICAgICAgY29uc3QgaW5mbyA9IGF3YWl0IHRoaXMuZ2V0SW5kZXgobmFtZSwgc2lnbmFsKTtcbiAgICAgIGNvbnN0IHZhbHVlID0gaW5mby5kaXJlY3RBY2Nlc3MgPz8gZmFsc2U7XG4gICAgICB0aGlzLmRpcmVjdEFjY2Vzc0NhY2hlLnNldChuYW1lLCB2YWx1ZSk7XG4gICAgICByZXR1cm4gdmFsdWU7XG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEZhbiBhIHF1ZXJ5IGFjcm9zcyBzZXZlcmFsIGluZGV4ZXMgYW5kIG1lcmdlIHRoZSBoaXRzLCBzb3J0ZWQgYnkgc2NvcmUgLVxuICAgKiB0aGUgXCJ1bml2ZXJzYWwgc2VhcmNoXCIgYSBzaW5nbGUgYm94IG92ZXIgbWFueSBjb2xsZWN0aW9ucyBuZWVkcy4gRWFjaFxuICAgKiBpbmRleCBpcyBzZWFyY2hlZCBjb25jdXJyZW50bHk7IGFuIGluZGV4IHRoYXQgZXJyb3JzIGlzIGxvZ2dlZCBhbmQgc2tpcHBlZFxuICAgKiBzbyBvbmUgYmFkIGluZGV4IGRvZXMgbm90IHNpbmsgdGhlIHdob2xlIHNlYXJjaC5cbiAgICovXG4gIGFzeW5jIHVuaXZlcnNhbFNlYXJjaChcbiAgICBxdWVyeTogc3RyaW5nLFxuICAgIG9wdGlvbnM6IFVuaXZlcnNhbFNlYXJjaE9wdGlvbnMgPSB7fSxcbiAgKTogUHJvbWlzZTxTZWFyY2hSZXN1bHQ+IHtcbiAgICBjb25zdCB0ZXh0ID0gc3RyaW5nLnRyaW1Ub0VtcHR5KHF1ZXJ5KTtcbiAgICBjb25zdCBuYW1lcyA9XG4gICAgICBvcHRpb25zLmluZGV4ZXMgJiYgb3B0aW9ucy5pbmRleGVzLmxlbmd0aCA+IDBcbiAgICAgICAgPyBvcHRpb25zLmluZGV4ZXMubWFwKChyZWYpID0+IHJlc29sdmVJbmRleE5hbWUodGhpcy5jb25maWcsIHJlZikgPz8gcmVmKVxuICAgICAgICA6IHRoaXMuY29uZmlnLmluZGV4ZXMubWFwKChpKSA9PiBpLm5hbWUpO1xuICAgIGNvbnN0IHBlckluZGV4ID0gb3B0aW9ucy5saW1pdCA/PyB0aGlzLmNvbmZpZy5wYWdlU2l6ZTtcblxuICAgIGNvbnN0IHNldHRsZWQgPSBhd2FpdCBQcm9taXNlLmFsbFNldHRsZWQoXG4gICAgICBuYW1lcy5tYXAoYXN5bmMgKG5hbWUpID0+IHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5zZWFyY2godGV4dCwge1xuICAgICAgICAgIGluZGV4OiBuYW1lLFxuICAgICAgICAgIGxpbWl0OiBwZXJJbmRleCxcbiAgICAgICAgICAuLi4ob3B0aW9ucy5tb2RlID8geyBtb2RlOiBvcHRpb25zLm1vZGUgfSA6IHt9KSxcbiAgICAgICAgICAuLi4ob3B0aW9ucy5zaWduYWwgPyB7IHNpZ25hbDogb3B0aW9ucy5zaWduYWwgfSA6IHt9KSxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiByZXN1bHQuaGl0cy5tYXAoKGhpdCk6IFNlYXJjaEhpdCA9PiAoeyAuLi5oaXQsIGluZGV4OiBuYW1lIH0pKTtcbiAgICAgIH0pLFxuICAgICk7XG5cbiAgICBjb25zdCBoaXRzID0gc2V0dGxlZFxuICAgICAgLmZsYXRNYXAoKG91dGNvbWUsIGkpID0+IHtcbiAgICAgICAgaWYgKG91dGNvbWUuc3RhdHVzID09PSBcImZ1bGZpbGxlZFwiKSByZXR1cm4gb3V0Y29tZS52YWx1ZTtcbiAgICAgICAgbG9nZ2VyLndhcm4oXCJ1bml2ZXJzYWwtaW5kZXgtZmFpbGVkXCIsIHsgaW5kZXg6IG5hbWVzW2ldIH0pO1xuICAgICAgICByZXR1cm4gW107XG4gICAgICB9KVxuICAgICAgLnNvcnQoKGEsIGIpID0+IGIuc2NvcmUgLSBhLnNjb3JlKTtcblxuICAgIHJldHVybiB7IHF1ZXJ5OiB0ZXh0LCBoaXRzLCBjb3VudDogaGl0cy5sZW5ndGggfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNvbHZlIGFuIGluZGV4IHJlZmVyZW5jZSAoZnVsbCBVQyBuYW1lIG9yIGNvbmZpZ3VyZWQgYWxpYXMpIHRvIHRoZSBuYW1lXG4gICAqIHRoZSBBUEkgZXhwZWN0cywgZmFpbGluZyBmYXN0IHdoZW4gaXQgcmVzb2x2ZXMgdG8gbm90aGluZy4gV2l0aG91dCB0aGlzIGFcbiAgICogYmxhbmsgcmVmZXJlbmNlIC0gd2hhdCBhbiBvbWl0dGVkIGBpbmRleGAgYmVjb21lcyB3aGVuIG5vIGRlZmF1bHQgaXNcbiAgICogY29uZmlndXJlZCAtIHJlYWNoZXMgdGhlIFNESyBhcyBhbiBlbXB0eSBgaW5kZXhfbmFtZWAsIHdoaWNoIGJ1aWxkcyBhXG4gICAqIFVSTCB3aXRoIHRoZSBuYW1lIHNlZ21lbnQgbWlzc2luZyBhbmQgY29tZXMgYmFjayBhcyBhIGNvbmZ1c2luZ1xuICAgKiBgRU5EUE9JTlRfTk9UX0ZPVU5EYCBpbnN0ZWFkIG9mIG5hbWluZyB0aGUgcmVhbCBwcm9ibGVtLlxuICAgKi9cbiAgcHJpdmF0ZSByZXF1aXJlSW5kZXhOYW1lKHJlZmVyZW5jZTogc3RyaW5nLCBvcGVyYXRpb246IHN0cmluZyk6IHN0cmluZyB7XG4gICAgY29uc3QgbmFtZSA9IHN0cmluZy50cmltVG9OdWxsKHJlc29sdmVJbmRleE5hbWUodGhpcy5jb25maWcsIHJlZmVyZW5jZSkgPz8gcmVmZXJlbmNlKTtcbiAgICBpZiAobmFtZSA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IEV4ZWN1dGlvbkVycm9yKFxuICAgICAgICBgc2VhcmNoOiBubyBpbmRleCBjb25maWd1cmVkOyBzZXQgYSBkZWZhdWx0IGluZGV4IG9yIHBhc3Mgb25lIHRvICR7b3BlcmF0aW9ufWAsXG4gICAgICAgIHsgY29udGV4dDogeyBvcGVyYXRpb24gfSB9LFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIG5hbWU7XG4gIH1cblxuICAvKiogRmV0Y2ggYW4gaW5kZXgncyBsaXZlIGRlZmluaXRpb24uICovXG4gIGFzeW5jIGdldEluZGV4KHJlZmVyZW5jZTogc3RyaW5nLCBzaWduYWw/OiBBYm9ydFNpZ25hbCk6IFByb21pc2U8SW5kZXhJbmZvPiB7XG4gICAgY29uc3QgbmFtZSA9IHRoaXMucmVxdWlyZUluZGV4TmFtZShyZWZlcmVuY2UsIFwiZ2V0SW5kZXhcIik7XG4gICAgY29uc3QgaW5kZXggPSBhd2FpdCB0aGlzLndpdGhDbGllbnQoXCJnZXRJbmRleFwiLCBzaWduYWwsIChjbGllbnQsIGNvbnRleHQpID0+XG4gICAgICBjbGllbnQudmVjdG9yU2VhcmNoSW5kZXhlcy5nZXRJbmRleCh7IGluZGV4X25hbWU6IG5hbWUgfSwgY29udGV4dCksXG4gICAgKTtcbiAgICBjb25zdCBkaXJlY3RBY2Nlc3MgPSBpbmRleC5kaXJlY3RfYWNjZXNzX2luZGV4X3NwZWMgIT09IHVuZGVmaW5lZDtcbiAgICBjb25zdCBzcGVjID0gaW5kZXguZGVsdGFfc3luY19pbmRleF9zcGVjID8/IGluZGV4LmRpcmVjdF9hY2Nlc3NfaW5kZXhfc3BlYztcbiAgICAvLyBEZWx0YSBTeW5jIHN1cmZhY2VzIGl0cyBlbWJlZGRpbmcgc291cmNlIGNvbHVtbnMgZGlyZWN0bHk7IGEgZGlyZWN0LWFjY2Vzc1xuICAgIC8vIGluZGV4IGNhcnJpZXMgaXRzIGNvbHVtbnMgaW4gc2NoZW1hX2pzb24gKG1pbnVzIHRoZSB2ZWN0b3IgY29sdW1uKS5cbiAgICBsZXQgY29sdW1ucyA9IChzcGVjPy5lbWJlZGRpbmdfc291cmNlX2NvbHVtbnMgPz8gW10pLm1hcCgoYykgPT4gYy5uYW1lID8/IFwiXCIpLmZpbHRlcihCb29sZWFuKTtcbiAgICBjb25zdCBzY2hlbWFKc29uID0gaW5kZXguZGlyZWN0X2FjY2Vzc19pbmRleF9zcGVjPy5zY2hlbWFfanNvbjtcbiAgICBpZiAoZGlyZWN0QWNjZXNzICYmIHNjaGVtYUpzb24pIHtcbiAgICAgIGNvbnN0IHNjaGVtYSA9IGpzb24ucGFyc2VSZWNvcmQoc2NoZW1hSnNvbikgPz8ge307XG4gICAgICBjb25zdCB2ZWN0b3JOYW1lcyA9IG5ldyBTZXQoXG4gICAgICAgIChpbmRleC5kaXJlY3RfYWNjZXNzX2luZGV4X3NwZWM/LmVtYmVkZGluZ192ZWN0b3JfY29sdW1ucyA/PyBbXSlcbiAgICAgICAgICAubWFwKChjKSA9PiBjLm5hbWUpXG4gICAgICAgICAgLmZpbHRlcigobik6IG4gaXMgc3RyaW5nID0+IEJvb2xlYW4obikpLFxuICAgICAgKTtcbiAgICAgIGNvbHVtbnMgPSBPYmplY3Qua2V5cyhzY2hlbWEpLmZpbHRlcigoYykgPT4gIXZlY3Rvck5hbWVzLmhhcyhjKSk7XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBuYW1lOiBpbmRleC5uYW1lID8/IG5hbWUsXG4gICAgICAuLi4oaW5kZXguZW5kcG9pbnRfbmFtZSA/IHsgZW5kcG9pbnQ6IGluZGV4LmVuZHBvaW50X25hbWUgfSA6IHt9KSxcbiAgICAgIC4uLihpbmRleC5wcmltYXJ5X2tleSA/IHsgcHJpbWFyeUtleTogaW5kZXgucHJpbWFyeV9rZXkgfSA6IHt9KSxcbiAgICAgIGNvbHVtbnMsXG4gICAgICByZWFkeTogaW5kZXguc3RhdHVzPy5yZWFkeSA/PyBmYWxzZSxcbiAgICAgIC4uLihpbmRleC5zdGF0dXM/LmluZGV4ZWRfcm93X2NvdW50ICE9PSB1bmRlZmluZWRcbiAgICAgICAgPyB7IHJvd0NvdW50OiBpbmRleC5zdGF0dXMuaW5kZXhlZF9yb3dfY291bnQgfVxuICAgICAgICA6IHt9KSxcbiAgICAgIC4uLihkaXJlY3RBY2Nlc3MgPyB7IGRpcmVjdEFjY2VzczogdHJ1ZSB9IDoge30pLFxuICAgIH07XG4gIH1cblxuICAvKiogQWRkIG9yIHVwZGF0ZSBkb2N1bWVudHMgaW4gYSBkaXJlY3QtYWNjZXNzIGluZGV4LiAqL1xuICBhc3luYyBhZGREb2N1bWVudHMoXG4gICAgcmVmZXJlbmNlOiBzdHJpbmcsXG4gICAgZG9jdW1lbnRzOiBTZWFyY2hEb2N1bWVudFtdLFxuICAgIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuICApOiBQcm9taXNlPFVwc2VydFJlc3VsdD4ge1xuICAgIGNvbnN0IG5hbWUgPSB0aGlzLnJlcXVpcmVJbmRleE5hbWUocmVmZXJlbmNlLCBcImFkZERvY3VtZW50c1wiKTtcbiAgICBpZiAodGhpcy5sYWtlYmFzZSkge1xuICAgICAgcmV0dXJuIHRoaXMubGFrZWJhc2UuYWRkRG9jdW1lbnRzKFxuICAgICAgICBuYW1lLFxuICAgICAgICBkb2N1bWVudHMsXG4gICAgICAgIHRoaXMuY29uZmlnLmVuc3VyZU9uU2V0dXA/LnRleHRDb2x1bW4gPz8gXCJ0ZXh0XCIsXG4gICAgICAgIHNpZ25hbCxcbiAgICAgICk7XG4gICAgfVxuICAgIGF3YWl0IHRoaXMud2l0aENsaWVudChcImFkZERvY3VtZW50c1wiLCBzaWduYWwsIChjbGllbnQsIGNvbnRleHQpID0+XG4gICAgICBjbGllbnQudmVjdG9yU2VhcmNoSW5kZXhlcy51cHNlcnREYXRhVmVjdG9ySW5kZXgoXG4gICAgICAgIHsgaW5kZXhfbmFtZTogbmFtZSwgaW5wdXRzX2pzb246IEpTT04uc3RyaW5naWZ5KGRvY3VtZW50cykgfSxcbiAgICAgICAgY29udGV4dCxcbiAgICAgICksXG4gICAgKTtcbiAgICByZXR1cm4geyBpbmRleDogbmFtZSwgY291bnQ6IGRvY3VtZW50cy5sZW5ndGggfTtcbiAgfVxuXG4gIC8qKiBEZWxldGUgZG9jdW1lbnRzIGZyb20gYSBkaXJlY3QtYWNjZXNzIGluZGV4IGJ5IHByaW1hcnkga2V5LiAqL1xuICBhc3luYyBkZWxldGVEb2N1bWVudHMoXG4gICAgcmVmZXJlbmNlOiBzdHJpbmcsXG4gICAgaWRzOiBBcnJheTxzdHJpbmcgfCBudW1iZXI+LFxuICAgIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuICApOiBQcm9taXNlPFVwc2VydFJlc3VsdD4ge1xuICAgIGNvbnN0IG5hbWUgPSB0aGlzLnJlcXVpcmVJbmRleE5hbWUocmVmZXJlbmNlLCBcImRlbGV0ZURvY3VtZW50c1wiKTtcbiAgICBpZiAodGhpcy5sYWtlYmFzZSkge1xuICAgICAgcmV0dXJuIHRoaXMubGFrZWJhc2UuZGVsZXRlRG9jdW1lbnRzKG5hbWUsIGlkcywgc2lnbmFsKTtcbiAgICB9XG4gICAgYXdhaXQgdGhpcy53aXRoQ2xpZW50KFwiZGVsZXRlRG9jdW1lbnRzXCIsIHNpZ25hbCwgKGNsaWVudCwgY29udGV4dCkgPT5cbiAgICAgIGNsaWVudC52ZWN0b3JTZWFyY2hJbmRleGVzLmRlbGV0ZURhdGFWZWN0b3JJbmRleChcbiAgICAgICAgeyBpbmRleF9uYW1lOiBuYW1lLCBwcmltYXJ5X2tleXM6IGlkcy5tYXAoU3RyaW5nKSB9LFxuICAgICAgICBjb250ZXh0LFxuICAgICAgKSxcbiAgICApO1xuICAgIHJldHVybiB7IGluZGV4OiBuYW1lLCBjb3VudDogaWRzLmxlbmd0aCB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc29sdmUgYW4gZW1iZWRkaW5nIGVuZHBvaW50IGlkIGZvciBjcmVhdGluZyBhIERlbHRhIFN5bmMgaW5kZXguIFJldXNlc1xuICAgKiB0aGUgbW9kZWwgcmVzb2x2ZXI6IGEgY29uZmlndXJlZCAvIHBhc3NlZCBuYW1lIGlzIGZ1enp5LW1hdGNoZWQgYWdhaW5zdCB0aGVcbiAgICogbGl2ZSBjYXRhbG9ndWUsIG90aGVyd2lzZSB0aGUgaGlnaGVzdC1yYW5rZWQgZW1iZWRkaW5nIGVuZHBvaW50IGlzIGNob3Nlbi5cbiAgICovXG4gIGFzeW5jIHJlc29sdmVFbWJlZGRpbmdNb2RlbChyZXF1ZXN0ZWQ/OiBzdHJpbmcsIHNpZ25hbD86IEFib3J0U2lnbmFsKTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB7XG4gICAgY29uc3QgZXhwbGljaXQgPSBzdHJpbmcudHJpbVRvTnVsbChyZXF1ZXN0ZWQgPz8gdGhpcy5jb25maWcuZW1iZWRkaW5nTW9kZWwpO1xuICAgIC8vIEFuIGV4cGxpY2l0IG5hbWUgdGhhdCBhbHJlYWR5IGxvb2tzIGxpa2UgYW4gZW5kcG9pbnQgaWQgKG5vIHdoaXRlc3BhY2UpXG4gICAgLy8gaXMgdXNlZCB2ZXJiYXRpbSAtIG5vIG5lZWQgdG8gZmV0Y2ggYW5kIGZ1enp5LW1hdGNoIHRoZSBsaXZlIGNhdGFsb2d1ZS5cbiAgICAvLyBBIGdlbnVpbmVseSBsb29zZSBuYW1lIChlLmcuIFwiZ3RlIGxhcmdlXCIpIHN0aWxsIHJlc29sdmVzIGFnYWluc3QgaXQuXG4gICAgaWYgKGV4cGxpY2l0ICYmICEvXFxzLy50ZXN0KGV4cGxpY2l0KSkgcmV0dXJuIGV4cGxpY2l0O1xuICAgIHJldHVybiB0aGlzLndpdGhDbGllbnQoXCJyZXNvbHZlRW1iZWRkaW5nTW9kZWxcIiwgc2lnbmFsLCBhc3luYyAoY2xpZW50KSA9PiB7XG4gICAgICBjb25zdCBob3N0ID0gKGF3YWl0IGNsaWVudC5jb25maWcuZ2V0SG9zdCgpKS50b1N0cmluZygpO1xuICAgICAgY29uc3QgZW5kcG9pbnRzID0gYXdhaXQgc2VydmluZy5saXN0U2VydmluZ0VuZHBvaW50cyhjbGllbnQsIGhvc3QpO1xuICAgICAgY29uc3QgeyBtb2RlbElkIH0gPSBtb2RlbFJlc29sdmUucmVzb2x2ZU1vZGVsKGVuZHBvaW50cywge1xuICAgICAgICAuLi4oZXhwbGljaXQgPyB7IGV4cGxpY2l0IH0gOiB7IG1vZGVsQ2xhc3M6IE1vZGVsQ2xhc3MuRW1iZWRkaW5nIH0pLFxuICAgICAgfSk7XG4gICAgICByZXR1cm4gc3RyaW5nLnRyaW1Ub051bGwobW9kZWxJZCk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogRW1iZWQgdGV4dCB2aWEgYSBEYXRhYnJpY2tzIGVtYmVkZGluZyBzZXJ2aW5nIGVuZHBvaW50LCByZXR1cm5pbmcgb25lXG4gICAqIHZlY3RvciBwZXIgaW5wdXQuIFVzZWQgdG8gc2VlZCBhIGRpcmVjdC1hY2Nlc3MgaW5kZXggYW5kIHRvIHR1cm4gYSBzZWFyY2hcbiAgICogcXVlcnkgaW50byBhIHF1ZXJ5IHZlY3RvciAtIERhdGFicmlja3Mgb25seSBtYW5hZ2VzIGVtYmVkZGluZ3MgZm9yIERlbHRhXG4gICAqIFN5bmMgaW5kZXhlcywgc28gYSBkaXJlY3QtYWNjZXNzIGluZGV4IChubyBEZWx0YSB0YWJsZSwgbm8gd2FyZWhvdXNlKSBuZWVkc1xuICAgKiB0aGUgY2xpZW50IHRvIGVtYmVkIG9uIHdyaXRlIGFuZCBvbiBxdWVyeS4gVGhlIGVuZHBvaW50IGlzIHJlc29sdmVkIHRoZVxuICAgKiBzYW1lIHdheSBhcyBmb3IgaW5kZXggY3JlYXRpb24gd2hlbiBub3QgbmFtZWQuXG4gICAqL1xuICBhc3luYyBlbWJlZCh0ZXh0czogc3RyaW5nW10sIG1vZGVsPzogc3RyaW5nLCBzaWduYWw/OiBBYm9ydFNpZ25hbCk6IFByb21pc2U8bnVtYmVyW11bXT4ge1xuICAgIGlmICh0ZXh0cy5sZW5ndGggPT09IDApIHJldHVybiBbXTtcbiAgICBjb25zdCBlbmRwb2ludCA9IGF3YWl0IHRoaXMucmVzb2x2ZUVtYmVkZGluZ01vZGVsKG1vZGVsLCBzaWduYWwpO1xuICAgIGlmICghZW5kcG9pbnQpIHtcbiAgICAgIHRocm93IG5ldyBFeGVjdXRpb25FcnJvcihcInNlYXJjaDogY291bGQgbm90IHJlc29sdmUgYW4gZW1iZWRkaW5nIG1vZGVsIHRvIGVtYmVkIHRleHRcIiwge1xuICAgICAgICBjb250ZXh0OiB7IG9wZXJhdGlvbjogXCJlbWJlZFwiIH0sXG4gICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMud2l0aENsaWVudChcImVtYmVkXCIsIHNpZ25hbCwgYXN5bmMgKGNsaWVudCkgPT4ge1xuICAgICAgY29uc3QgaG9zdCA9IChhd2FpdCBjbGllbnQuY29uZmlnLmdldEhvc3QoKSkudG9TdHJpbmcoKTtcbiAgICAgIGNvbnN0IHVybCA9IGludm9rZS5pbnZvY2F0aW9uc1VybChob3N0LCBlbmRwb2ludCk7XG4gICAgICBjb25zdCBoZWFkZXJzID0gYXdhaXQgaW52b2tlLmF1dGhIZWFkZXJzKGNsaWVudCk7XG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHVybCwge1xuICAgICAgICBtZXRob2Q6IFwiUE9TVFwiLFxuICAgICAgICBoZWFkZXJzOiB7IC4uLmhlYWRlcnMsIFwiY29udGVudC10eXBlXCI6IFwiYXBwbGljYXRpb24vanNvblwiIH0sXG4gICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgaW5wdXQ6IHRleHRzIH0pLFxuICAgICAgICAuLi4oc2lnbmFsID8geyBzaWduYWwgfSA6IHt9KSxcbiAgICAgIH0pO1xuICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICB0aHJvdyBuZXcgRXhlY3V0aW9uRXJyb3IoXG4gICAgICAgICAgYHNlYXJjaDogZW1iZWRkaW5nIGVuZHBvaW50ICR7ZW5kcG9pbnR9IGZhaWxlZCAoJHtyZXNwb25zZS5zdGF0dXN9KWAsXG4gICAgICAgICAgeyBjb250ZXh0OiB7IG9wZXJhdGlvbjogXCJlbWJlZFwiIH0gfSxcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGJvZHkgPSAoYXdhaXQgcmVzcG9uc2UuanNvbigpKSBhcyB7IGRhdGE/OiBBcnJheTx7IGVtYmVkZGluZzogbnVtYmVyW10gfT4gfTtcbiAgICAgIGNvbnN0IHZlY3RvcnMgPSAoYm9keS5kYXRhID8/IFtdKS5tYXAoKHJvdykgPT4gcm93LmVtYmVkZGluZyk7XG4gICAgICBpZiAodmVjdG9ycy5sZW5ndGggIT09IHRleHRzLmxlbmd0aCkge1xuICAgICAgICB0aHJvdyBuZXcgRXhlY3V0aW9uRXJyb3IoXCJzZWFyY2g6IGVtYmVkZGluZyByZXNwb25zZSBkaWQgbm90IG1hdGNoIHRoZSBpbnB1dCBjb3VudFwiLCB7XG4gICAgICAgICAgY29udGV4dDogeyBvcGVyYXRpb246IFwiZW1iZWRcIiB9LFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB2ZWN0b3JzO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqIFRoZSB2ZWN0b3IgZGltZW5zaW9uIGEgbW9kZWwgcHJvZHVjZXMgKGVtYmVkcyBhIHByb2JlIHN0cmluZyBvbmNlKS4gKi9cbiAgcHJpdmF0ZSBhc3luYyBlbWJlZGRpbmdEaW1lbnNpb24obW9kZWw/OiBzdHJpbmcsIHNpZ25hbD86IEFib3J0U2lnbmFsKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICBjb25zdCBbdmVjdG9yXSA9IGF3YWl0IHRoaXMuZW1iZWQoW1wiZGltZW5zaW9uIHByb2JlXCJdLCBtb2RlbCwgc2lnbmFsKTtcbiAgICBpZiAoIXZlY3RvciB8fCB2ZWN0b3IubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXhlY3V0aW9uRXJyb3IoXCJzZWFyY2g6IGNvdWxkIG5vdCBkZXRlcm1pbmUgdGhlIGVtYmVkZGluZyBkaW1lbnNpb25cIiwge1xuICAgICAgICBjb250ZXh0OiB7IG9wZXJhdGlvbjogXCJjcmVhdGVJbmRleFwiIH0sXG4gICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIHZlY3Rvci5sZW5ndGg7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGFuIEFJIFNlYXJjaCBpbmRleCB3aXRoIGFzIGxpdHRsZSBjZXJlbW9ueSBhcyBwb3NzaWJsZS4gVHdvIHNoYXBlczpcbiAgICpcbiAgICogICAtICoqRGVsdGEgU3luYyoqICh0aGUgZGVmYXVsdCk6IHBhc3MgYHNvdXJjZVRhYmxlYDsgRGF0YWJyaWNrcyBjb21wdXRlc1xuICAgKiAgICAgZW1iZWRkaW5ncyBmcm9tIHRoZSB0ZXh0IGNvbHVtbiBhbmQga2VlcHMgdGhlIGluZGV4IHN5bmNlZC4gVGhlXG4gICAqICAgICBlbWJlZGRpbmcgbW9kZWwgaXMgcmVzb2x2ZWQgYXV0b21hdGljYWxseSB3aGVuIG5vdCBuYW1lZC5cbiAgICogICAtICoqRGlyZWN0IEFjY2VzcyoqOiBvbWl0IGBzb3VyY2VUYWJsZWAgYW5kIHBhc3MgYGVtYmVkZGluZ0RpbWVuc2lvbmA7XG4gICAqICAgICB5b3Ugd3JpdGUgdmVjdG9ycyB5b3Vyc2VsZiB2aWEge0BsaW5rIGFkZERvY3VtZW50c30uXG4gICAqXG4gICAqIEV2ZXJ5dGhpbmcgZWxzZSBpbmZlcnM6IHRoZSBlbmRwb2ludCBmcm9tIHRoZSBwbHVnaW4gY29uZmlnLCB0aGUgcHJpbWFyeVxuICAgKiBrZXkgKGBpZGApLCB0aGUgdGV4dCBjb2x1bW4gKGB0ZXh0YCAvIGBjb250ZW50YCAvIGBib2R5YCksIGFuZCB0aGUgdmVjdG9yXG4gICAqIGNvbHVtbiAoYGVtYmVkZGluZ2ApLiBSZXR1cm5zIHRoZSBjcmVhdGVkIGluZGV4J3Mge0BsaW5rIEluZGV4SW5mb30uXG4gICAqL1xuICBhc3luYyBjcmVhdGVJbmRleChuYW1lOiBzdHJpbmcsIG9wdGlvbnM6IENyZWF0ZUluZGV4T3B0aW9ucyA9IHt9KTogUHJvbWlzZTxJbmRleEluZm8+IHtcbiAgICBjb25zdCBlbmRwb2ludCA9IG9wdGlvbnMuZW5kcG9pbnQgPz8gdGhpcy5jb25maWcuZW5kcG9pbnQ7XG4gICAgaWYgKCFlbmRwb2ludCkge1xuICAgICAgdGhyb3cgbmV3IEV4ZWN1dGlvbkVycm9yKFxuICAgICAgICBcInNlYXJjaDogbm8gVmVjdG9yIFNlYXJjaCBlbmRwb2ludCBjb25maWd1cmVkOyBwYXNzIGBlbmRwb2ludGAgb3Igc2V0IGl0IG9uIHRoZSBwbHVnaW5cIixcbiAgICAgICAgeyBjb250ZXh0OiB7IG9wZXJhdGlvbjogXCJjcmVhdGVJbmRleFwiIH0gfSxcbiAgICAgICk7XG4gICAgfVxuICAgIGNvbnN0IHByaW1hcnlLZXkgPSBvcHRpb25zLnByaW1hcnlLZXkgPz8gXCJpZFwiO1xuICAgIGNvbnN0IGRpcmVjdCA9ICFvcHRpb25zLnNvdXJjZVRhYmxlO1xuICAgIGNvbnN0IHNvdXJjZUNvbHVtbiA9IG9wdGlvbnMuZW1iZWRkaW5nU291cmNlQ29sdW1uID8/IFwidGV4dFwiO1xuICAgIGNvbnN0IHZlY3RvckNvbHVtbiA9IG9wdGlvbnMuZW1iZWRkaW5nVmVjdG9yQ29sdW1uID8/IFwiZW1iZWRkaW5nXCI7XG5cbiAgICAvLyBEZWx0YSBTeW5jIGxldHMgRGF0YWJyaWNrcyBlbWJlZCBhIHNvdXJjZSBjb2x1bW4sIHNvIGl0IG5lZWRzIGFuIGVtYmVkZGluZ1xuICAgIC8vIG1vZGVsLiBBIGRpcmVjdC1hY2Nlc3MgaW5kZXggc3RvcmVzIHZlY3RvcnMgWU9VIHN1cHBseSAoRGF0YWJyaWNrcyBvbmx5XG4gICAgLy8gc3VwcG9ydHMgbWFuYWdlZCBlbWJlZGRpbmdzIG9uIERlbHRhIFN5bmMpLCBzbyBpdCBuZWVkcyBhIGRpbWVuc2lvbjsgd2VcbiAgICAvLyByZXNvbHZlIG9uZSBmcm9tIHRoZSBlbWJlZGRpbmcgbW9kZWwgd2hlbiBub3QgZ2l2ZW4gc28gYSBkaXJlY3QtYWNjZXNzXG4gICAgLy8gaW5kZXggc3RpbGwgd29ya3Mgd2l0aCB6ZXJvIGV4dHJhIGNvbmZpZyAoZW1iZWQgdmlhIGBlbWJlZCgpYCBvbiBzZWVkICtcbiAgICAvLyBxdWVyeSAtIHNlZSBgcHJvdmlzaW9uYCAvIGBzZWFyY2hgKS5cbiAgICBjb25zdCBlbWJlZGRpbmdNb2RlbCA9XG4gICAgICAoYXdhaXQgdGhpcy5yZXNvbHZlRW1iZWRkaW5nTW9kZWwob3B0aW9ucy5lbWJlZGRpbmdNb2RlbCwgb3B0aW9ucy5zaWduYWwpKSA/PyB1bmRlZmluZWQ7XG4gICAgaWYgKCFlbWJlZGRpbmdNb2RlbCkge1xuICAgICAgdGhyb3cgbmV3IEV4ZWN1dGlvbkVycm9yKFxuICAgICAgICBcInNlYXJjaDogY291bGQgbm90IHJlc29sdmUgYW4gZW1iZWRkaW5nIG1vZGVsOyBwYXNzIGBlbWJlZGRpbmdNb2RlbGBcIixcbiAgICAgICAgeyBjb250ZXh0OiB7IG9wZXJhdGlvbjogXCJjcmVhdGVJbmRleFwiIH0gfSxcbiAgICAgICk7XG4gICAgfVxuICAgIGNvbnN0IGRpbWVuc2lvbiA9XG4gICAgICBvcHRpb25zLmVtYmVkZGluZ0RpbWVuc2lvbiA/PyAoYXdhaXQgdGhpcy5lbWJlZGRpbmdEaW1lbnNpb24oZW1iZWRkaW5nTW9kZWwsIG9wdGlvbnMuc2lnbmFsKSk7XG5cbiAgICBjb25zdCBzY2hlbWEgPSB7XG4gICAgICBbcHJpbWFyeUtleV06IFwic3RyaW5nXCIsXG4gICAgICBbc291cmNlQ29sdW1uXTogXCJzdHJpbmdcIixcbiAgICAgIC4uLihvcHRpb25zLnNjaGVtYSA/PyB7fSksXG4gICAgfTtcblxuICAgIGNvbnN0IGRpcmVjdFNwZWMgPSB7XG4gICAgICBkaXJlY3RfYWNjZXNzX2luZGV4X3NwZWM6IHtcbiAgICAgICAgZW1iZWRkaW5nX3ZlY3Rvcl9jb2x1bW5zOiBbeyBuYW1lOiB2ZWN0b3JDb2x1bW4sIGVtYmVkZGluZ19kaW1lbnNpb246IGRpbWVuc2lvbiB9XSxcbiAgICAgICAgc2NoZW1hX2pzb246IEpTT04uc3RyaW5naWZ5KHsgLi4uc2NoZW1hLCBbdmVjdG9yQ29sdW1uXTogYGFycmF5PGZsb2F0PmAgfSksXG4gICAgICB9LFxuICAgIH07XG5cbiAgICBhd2FpdCB0aGlzLndpdGhDbGllbnQoXCJjcmVhdGVJbmRleFwiLCBvcHRpb25zLnNpZ25hbCwgKGNsaWVudCwgY29udGV4dCkgPT5cbiAgICAgIGNsaWVudC52ZWN0b3JTZWFyY2hJbmRleGVzLmNyZWF0ZUluZGV4KFxuICAgICAgICB7XG4gICAgICAgICAgbmFtZSxcbiAgICAgICAgICBlbmRwb2ludF9uYW1lOiBlbmRwb2ludCxcbiAgICAgICAgICBwcmltYXJ5X2tleTogcHJpbWFyeUtleSxcbiAgICAgICAgICBpbmRleF90eXBlOiBkaXJlY3QgPyBcIkRJUkVDVF9BQ0NFU1NcIiA6IFwiREVMVEFfU1lOQ1wiLFxuICAgICAgICAgIC4uLihkaXJlY3RcbiAgICAgICAgICAgID8gZGlyZWN0U3BlY1xuICAgICAgICAgICAgOiB7XG4gICAgICAgICAgICAgICAgZGVsdGFfc3luY19pbmRleF9zcGVjOiB7XG4gICAgICAgICAgICAgICAgICBzb3VyY2VfdGFibGU6IG9wdGlvbnMuc291cmNlVGFibGUsXG4gICAgICAgICAgICAgICAgICBwaXBlbGluZV90eXBlOiBvcHRpb25zLnBpcGVsaW5lVHlwZSA/PyBcIlRSSUdHRVJFRFwiLFxuICAgICAgICAgICAgICAgICAgZW1iZWRkaW5nX3NvdXJjZV9jb2x1bW5zOiBbXG4gICAgICAgICAgICAgICAgICAgIHsgbmFtZTogc291cmNlQ29sdW1uLCBlbWJlZGRpbmdfbW9kZWxfZW5kcG9pbnRfbmFtZTogZW1iZWRkaW5nTW9kZWwgfSxcbiAgICAgICAgICAgICAgICAgIF0sXG4gICAgICAgICAgICAgICAgICAuLi4ob3B0aW9ucy5jb2x1bW5zVG9TeW5jICYmIG9wdGlvbnMuY29sdW1uc1RvU3luYy5sZW5ndGggPiAwXG4gICAgICAgICAgICAgICAgICAgID8geyBjb2x1bW5zX3RvX3N5bmM6IG9wdGlvbnMuY29sdW1uc1RvU3luYyB9XG4gICAgICAgICAgICAgICAgICAgIDoge30pLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIH0pLFxuICAgICAgICB9LFxuICAgICAgICBjb250ZXh0LFxuICAgICAgKSxcbiAgICApO1xuICAgIGxvZ2dlci5pbmZvKFwiaW5kZXgtY3JlYXRlZFwiLCB7XG4gICAgICBpbmRleDogbmFtZSxcbiAgICAgIGVuZHBvaW50LFxuICAgICAgdHlwZTogZGlyZWN0ID8gXCJESVJFQ1RfQUNDRVNTXCIgOiBcIkRFTFRBX1NZTkNcIixcbiAgICB9KTtcbiAgICByZXR1cm4gdGhpcy5nZXRJbmRleChuYW1lLCBvcHRpb25zLnNpZ25hbCk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIHRoZSBpbmRleCBpZiBpdCBkb2VzIG5vdCBhbHJlYWR5IGV4aXN0LCBvdGhlcndpc2UgcmV0dXJuIHRoZVxuICAgKiBleGlzdGluZyBvbmUuIElkZW1wb3RlbnQgLSBzYWZlIHRvIGNhbGwgb24gZXZlcnkgYm9vdCB0byBndWFyYW50ZWUgYW5cbiAgICogaW5kZXggaXMgcHJlc2VudC5cbiAgICovXG4gIGFzeW5jIGVuc3VyZUluZGV4KG5hbWU6IHN0cmluZywgb3B0aW9uczogQ3JlYXRlSW5kZXhPcHRpb25zID0ge30pOiBQcm9taXNlPEluZGV4SW5mbz4ge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgdGhpcy5nZXRJbmRleChuYW1lLCBvcHRpb25zLnNpZ25hbCk7XG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gdGhpcy5jcmVhdGVJbmRleChuYW1lLCBvcHRpb25zKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRW5zdXJlIGFuIGluZGV4IGV4aXN0cywgaXMgb25saW5lLCBhbmQgKG9wdGlvbmFsbHkpIGhvbGRzIHNlZWQgZGF0YSAtIHRoZVxuICAgKiBcIndpcmUgdXAgYSByZWFsIGluZGV4IG9uIGJvb3RcIiBwYXRoLiBJZGVtcG90ZW50IGFuZCBjaGVhcCB0byBjYWxsIGV2ZXJ5XG4gICAqIGJvb3Q6IGl0IGNyZWF0ZXMgdGhlIGVuZHBvaW50IGFuZCBpbmRleCBvbmx5IGlmIG1pc3NpbmcsIHdhaXRzIGZvciB0aGVtIHRvXG4gICAqIGNvbWUgb25saW5lLCBhbmQgc2VlZHMgZG9jdW1lbnRzIE9OTFkgd2hlbiB0aGUgaW5kZXggaXMgc3RpbGwgZW1wdHkuXG4gICAqXG4gICAqIEZvciB0aGUgZGVtby9kdW1teS1kYXRhIGNhc2UgdGhpcyBuZWVkcyBubyBEZWx0YSB0YWJsZSBhbmQgbm8gd2FyZWhvdXNlOlxuICAgKiB0aGUgZGVmYXVsdCBpcyBhIE1BTkFHRUQgZGlyZWN0LWFjY2VzcyBpbmRleCAoRGF0YWJyaWNrcyBlbWJlZHMgdGhlIGB0ZXh0YFxuICAgKiBjb2x1bW4gb24gd3JpdGUgYW5kIHF1ZXJ5KSwgc28gc2VlZGluZyBpcyBqdXN0IGFuIGBhZGREb2N1bWVudHNgIG9mIHBsYWluXG4gICAqIHJvd3MgYW5kIHNlYXJjaC1ieS10ZXh0IHdvcmtzIGltbWVkaWF0ZWx5LlxuICAgKi9cbiAgYXN5bmMgcHJvdmlzaW9uKG5hbWU6IHN0cmluZywgb3B0aW9uczogUHJvdmlzaW9uT3B0aW9ucyA9IHt9KTogUHJvbWlzZTxJbmRleEluZm8+IHtcbiAgICBpZiAodGhpcy5sYWtlYmFzZSkge1xuICAgICAgLy8gTGFrZWJhc2UgZmFsbGJhY2s6IG5vIGVuZHBvaW50LCBubyBlbWJlZGRpbmdzLCBubyB2ZWN0b3JzIC0ganVzdCBhXG4gICAgICAvLyBQb3N0Z3JlcyBmdWxsLXRleHQgdGFibGUuIFJldHVybnMgYW4gSW5kZXhJbmZvLXNoYXBlZCByZXN1bHQgc28gdGhlXG4gICAgICAvLyBjYWxsZXIncyBsb2dnaW5nICsgcmVhZGluZXNzIGhhbmRsaW5nIGlzIGlkZW50aWNhbCB0byBWZWN0b3IgU2VhcmNoLlxuICAgICAgY29uc3Qgcm93Q291bnQgPSBhd2FpdCB0aGlzLmxha2ViYXNlLnByb3Zpc2lvbihuYW1lLCB7XG4gICAgICAgIHRleHRDb2x1bW46IG9wdGlvbnMuZW1iZWRkaW5nU291cmNlQ29sdW1uID8/IFwidGV4dFwiLFxuICAgICAgICAuLi4ob3B0aW9ucy5zZWVkID8geyBzZWVkOiBvcHRpb25zLnNlZWQgfSA6IHt9KSxcbiAgICAgICAgLi4uKG9wdGlvbnMuc2lnbmFsID8geyBzaWduYWw6IG9wdGlvbnMuc2lnbmFsIH0gOiB7fSksXG4gICAgICB9KTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIG5hbWUsXG4gICAgICAgIHByaW1hcnlLZXk6IG9wdGlvbnMucHJpbWFyeUtleSA/PyBcImlkXCIsXG4gICAgICAgIGNvbHVtbnM6IFtdLFxuICAgICAgICByZWFkeTogdHJ1ZSxcbiAgICAgICAgcm93Q291bnQsXG4gICAgICB9O1xuICAgIH1cbiAgICBjb25zdCB3YWl0ID0gb3B0aW9ucy53YWl0ID8/IHRydWU7XG4gICAgY29uc3QgdGltZW91dE1zID0gb3B0aW9ucy50aW1lb3V0TXMgPz8gMjAgKiA2MCAqIDEwMDA7XG4gICAgY29uc3QgZW5kcG9pbnQgPSBvcHRpb25zLmVuZHBvaW50ID8/IHRoaXMuY29uZmlnLmVuZHBvaW50O1xuICAgIGlmIChlbmRwb2ludCkgYXdhaXQgdGhpcy5lbnN1cmVFbmRwb2ludChlbmRwb2ludCwgeyB3YWl0LCBzaWduYWw6IG9wdGlvbnMuc2lnbmFsIH0pO1xuXG4gICAgY29uc3QgeyBzZWVkOiBfc2VlZCwgd2FpdDogX3dhaXQsIHRpbWVvdXRNczogX3RpbWVvdXRNcywgLi4uY3JlYXRlT3B0aW9ucyB9ID0gb3B0aW9ucztcbiAgICBsZXQgaW5mbyA9IGF3YWl0IHRoaXMuZW5zdXJlSW5kZXgobmFtZSwgY3JlYXRlT3B0aW9ucyk7XG5cbiAgICBpZiAod2FpdCAmJiAhaW5mby5yZWFkeSkgaW5mbyA9IGF3YWl0IHRoaXMud2FpdEZvckluZGV4UmVhZHkobmFtZSwgdGltZW91dE1zLCBvcHRpb25zLnNpZ25hbCk7XG5cbiAgICBjb25zdCBzZWVkID0gb3B0aW9ucy5zZWVkID8/IFtdO1xuICAgIGlmIChzZWVkLmxlbmd0aCA+IDAgJiYgKGluZm8ucm93Q291bnQgPz8gMCkgPT09IDApIHtcbiAgICAgIGNvbnN0IHNvdXJjZUNvbHVtbiA9IG9wdGlvbnMuZW1iZWRkaW5nU291cmNlQ29sdW1uID8/IFwidGV4dFwiO1xuICAgICAgY29uc3QgdmVjdG9yQ29sdW1uID0gb3B0aW9ucy5lbWJlZGRpbmdWZWN0b3JDb2x1bW4gPz8gXCJlbWJlZGRpbmdcIjtcbiAgICAgIC8vIERpcmVjdC1hY2Nlc3MgaW5kZXhlcyBzdG9yZSB2ZWN0b3JzIHdlIHN1cHBseSwgc28gZW1iZWQgdGhlIHRleHQgY29sdW1uXG4gICAgICAvLyBmb3IgYW55IHNlZWQgcm93IHRoYXQgZGlkIG5vdCBhbHJlYWR5IGNhcnJ5IGEgdmVjdG9yLlxuICAgICAgY29uc3QgbmVlZEVtYmVkID0gc2VlZC5zb21lKChkb2MpID0+IGRvY1t2ZWN0b3JDb2x1bW5dID09PSB1bmRlZmluZWQpO1xuICAgICAgbGV0IHJvd3MgPSBzZWVkO1xuICAgICAgaWYgKG5lZWRFbWJlZCkge1xuICAgICAgICBjb25zdCB0ZXh0cyA9IHNlZWQubWFwKChkb2MpID0+IHN0cmluZy50cmltVG9FbXB0eShTdHJpbmcoZG9jW3NvdXJjZUNvbHVtbl0gPz8gXCJcIikpKTtcbiAgICAgICAgY29uc3QgdmVjdG9ycyA9IGF3YWl0IHRoaXMuZW1iZWQodGV4dHMsIG9wdGlvbnMuZW1iZWRkaW5nTW9kZWwsIG9wdGlvbnMuc2lnbmFsKTtcbiAgICAgICAgcm93cyA9IHNlZWQubWFwKChkb2MsIGkpID0+XG4gICAgICAgICAgZG9jW3ZlY3RvckNvbHVtbl0gPT09IHVuZGVmaW5lZCA/IHsgLi4uZG9jLCBbdmVjdG9yQ29sdW1uXTogdmVjdG9yc1tpXSB9IDogZG9jLFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgYXdhaXQgdGhpcy5hZGREb2N1bWVudHMobmFtZSwgcm93cywgb3B0aW9ucy5zaWduYWwpO1xuICAgICAgbG9nZ2VyLmluZm8oXCJpbmRleC1zZWVkZWRcIiwgeyBpbmRleDogbmFtZSwgY291bnQ6IHJvd3MubGVuZ3RoIH0pO1xuICAgICAgaW5mbyA9IGF3YWl0IHRoaXMuZ2V0SW5kZXgobmFtZSwgb3B0aW9ucy5zaWduYWwpO1xuICAgIH1cbiAgICByZXR1cm4gaW5mbztcbiAgfVxuXG4gIC8qKiBQb2xsIGFuIGluZGV4IHVudGlsIGl0IHJlcG9ydHMgcmVhZHksIG9yIHRocm93IGFmdGVyIHtAbGluayB0aW1lb3V0TXN9LiAqL1xuICBwcml2YXRlIGFzeW5jIHdhaXRGb3JJbmRleFJlYWR5KFxuICAgIG5hbWU6IHN0cmluZyxcbiAgICB0aW1lb3V0TXM6IG51bWJlcixcbiAgICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbiAgKTogUHJvbWlzZTxJbmRleEluZm8+IHtcbiAgICBjb25zdCBkZWFkbGluZSA9IERhdGUubm93KCkgKyB0aW1lb3V0TXM7XG4gICAgbGV0IGluZm8gPSBhd2FpdCB0aGlzLmdldEluZGV4KG5hbWUsIHNpZ25hbCk7XG4gICAgd2hpbGUgKCFpbmZvLnJlYWR5KSB7XG4gICAgICBpZiAoRGF0ZS5ub3coKSA+IGRlYWRsaW5lKSB7XG4gICAgICAgIHRocm93IG5ldyBFeGVjdXRpb25FcnJvcihgc2VhcmNoOiBpbmRleCAke25hbWV9IGRpZCBub3QgY29tZSBvbmxpbmUgaW4gdGltZWAsIHtcbiAgICAgICAgICBjb250ZXh0OiB7IG9wZXJhdGlvbjogXCJwcm92aXNpb25cIiB9LFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICAgIGF3YWl0IGFzeW5jVXRpbC5zbGVlcCg1MDAwLCBzaWduYWwpO1xuICAgICAgaW5mbyA9IGF3YWl0IHRoaXMuZ2V0SW5kZXgobmFtZSwgc2lnbmFsKTtcbiAgICB9XG4gICAgcmV0dXJuIGluZm87XG4gIH1cblxuICAvKiogVHJpZ2dlciBhIHN5bmMgb2YgYSBEZWx0YSBTeW5jIGluZGV4IGZyb20gaXRzIHNvdXJjZSB0YWJsZS4gKi9cbiAgYXN5bmMgc3luY0luZGV4KHJlZmVyZW5jZTogc3RyaW5nLCBzaWduYWw/OiBBYm9ydFNpZ25hbCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IG5hbWUgPSB0aGlzLnJlcXVpcmVJbmRleE5hbWUocmVmZXJlbmNlLCBcInN5bmNJbmRleFwiKTtcbiAgICBhd2FpdCB0aGlzLndpdGhDbGllbnQoXCJzeW5jSW5kZXhcIiwgc2lnbmFsLCAoY2xpZW50LCBjb250ZXh0KSA9PlxuICAgICAgY2xpZW50LnZlY3RvclNlYXJjaEluZGV4ZXMuc3luY0luZGV4KHsgaW5kZXhfbmFtZTogbmFtZSB9LCBjb250ZXh0KSxcbiAgICApO1xuICAgIGxvZ2dlci5pbmZvKFwiaW5kZXgtc3luY2VkXCIsIHsgaW5kZXg6IG5hbWUgfSk7XG4gIH1cblxuICAvKiogRGVsZXRlIGFuIGluZGV4LiAqL1xuICBhc3luYyBkZWxldGVJbmRleChyZWZlcmVuY2U6IHN0cmluZywgc2lnbmFsPzogQWJvcnRTaWduYWwpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBuYW1lID0gdGhpcy5yZXF1aXJlSW5kZXhOYW1lKHJlZmVyZW5jZSwgXCJkZWxldGVJbmRleFwiKTtcbiAgICBhd2FpdCB0aGlzLndpdGhDbGllbnQoXCJkZWxldGVJbmRleFwiLCBzaWduYWwsIChjbGllbnQsIGNvbnRleHQpID0+XG4gICAgICBjbGllbnQudmVjdG9yU2VhcmNoSW5kZXhlcy5kZWxldGVJbmRleCh7IGluZGV4X25hbWU6IG5hbWUgfSwgY29udGV4dCksXG4gICAgKTtcbiAgICBsb2dnZXIuaW5mbyhcImluZGV4LWRlbGV0ZWRcIiwgeyBpbmRleDogbmFtZSB9KTtcbiAgfVxuXG4gIC8qKiBMaXN0IHRoZSBpbmRleGVzIGhvc3RlZCBvbiBhIFZlY3RvciBTZWFyY2ggZW5kcG9pbnQgKG5hbWUgKyB0eXBlIG9ubHkpLiAqL1xuICBhc3luYyBsaXN0SW5kZXhlcyhlbmRwb2ludD86IHN0cmluZywgc2lnbmFsPzogQWJvcnRTaWduYWwpOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gICAgY29uc3QgZW5kcG9pbnROYW1lID0gZW5kcG9pbnQgPz8gdGhpcy5jb25maWcuZW5kcG9pbnQ7XG4gICAgaWYgKCFlbmRwb2ludE5hbWUpIHtcbiAgICAgIHRocm93IG5ldyBFeGVjdXRpb25FcnJvcihcInNlYXJjaDogbm8gZW5kcG9pbnQgY29uZmlndXJlZCB0byBsaXN0IGluZGV4ZXNcIiwge1xuICAgICAgICBjb250ZXh0OiB7IG9wZXJhdGlvbjogXCJsaXN0SW5kZXhlc1wiIH0sXG4gICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMud2l0aENsaWVudChcImxpc3RJbmRleGVzXCIsIHNpZ25hbCwgYXN5bmMgKGNsaWVudCwgY29udGV4dCkgPT4ge1xuICAgICAgY29uc3QgbmFtZXM6IHN0cmluZ1tdID0gW107XG4gICAgICBmb3IgYXdhaXQgKGNvbnN0IGluZGV4IG9mIGNsaWVudC52ZWN0b3JTZWFyY2hJbmRleGVzLmxpc3RJbmRleGVzKFxuICAgICAgICB7IGVuZHBvaW50X25hbWU6IGVuZHBvaW50TmFtZSB9LFxuICAgICAgICBjb250ZXh0LFxuICAgICAgKSkge1xuICAgICAgICBpZiAoaW5kZXgubmFtZSkgbmFtZXMucHVzaChpbmRleC5uYW1lKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBuYW1lcztcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbnN1cmUgYSBWZWN0b3IgU2VhcmNoIGVuZHBvaW50IGV4aXN0cywgY3JlYXRpbmcgYSBgU1RBTkRBUkRgIG9uZSB3aGVuIGl0XG4gICAqIGRvZXMgbm90LiBPcHRpb25hbGx5IHdhaXQgZm9yIGl0IHRvIGNvbWUgb25saW5lLiBJZGVtcG90ZW50LlxuICAgKi9cbiAgYXN5bmMgZW5zdXJlRW5kcG9pbnQobmFtZT86IHN0cmluZywgb3B0aW9uczogRW5zdXJlRW5kcG9pbnRPcHRpb25zID0ge30pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBlbmRwb2ludCA9IG5hbWUgPz8gdGhpcy5jb25maWcuZW5kcG9pbnQ7XG4gICAgaWYgKCFlbmRwb2ludCkge1xuICAgICAgdGhyb3cgbmV3IEV4ZWN1dGlvbkVycm9yKFwic2VhcmNoOiBubyBlbmRwb2ludCBuYW1lIHRvIGVuc3VyZVwiLCB7XG4gICAgICAgIGNvbnRleHQ6IHsgb3BlcmF0aW9uOiBcImVuc3VyZUVuZHBvaW50XCIgfSxcbiAgICAgIH0pO1xuICAgIH1cbiAgICBhd2FpdCB0aGlzLndpdGhDbGllbnQoXCJlbnN1cmVFbmRwb2ludFwiLCBvcHRpb25zLnNpZ25hbCwgYXN5bmMgKGNsaWVudCwgY29udGV4dCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgY2xpZW50LnZlY3RvclNlYXJjaEVuZHBvaW50cy5nZXRFbmRwb2ludCh7IGVuZHBvaW50X25hbWU6IGVuZHBvaW50IH0sIGNvbnRleHQpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgY29uc3Qgd2FpdGVyID0gYXdhaXQgY2xpZW50LnZlY3RvclNlYXJjaEVuZHBvaW50cy5jcmVhdGVFbmRwb2ludChcbiAgICAgICAgICB7IG5hbWU6IGVuZHBvaW50LCBlbmRwb2ludF90eXBlOiBcIlNUQU5EQVJEXCIgfSxcbiAgICAgICAgICBjb250ZXh0LFxuICAgICAgICApO1xuICAgICAgICBsb2dnZXIuaW5mbyhcImVuZHBvaW50LWNyZWF0ZWRcIiwgeyBlbmRwb2ludCB9KTtcbiAgICAgICAgaWYgKG9wdGlvbnMud2FpdCkgYXdhaXQgd2FpdGVyLndhaXQoKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNvbHZlIHRoZSB3b3Jrc3BhY2UgY2xpZW50IGFuZCBydW4gb25lIGNhbGwgdW5kZXIgYSBib3VuZGVkIHRpbWVvdXQuIFRoZVxuICAgKiBjYWxsZXIncyBzaWduYWwgKGlmIGFueSkgYW5kIGEgdGltZW91dCBhcmUgbWVyZ2VkIGludG8gb25lIFNESyBgQ29udGV4dGBcbiAgICogdmlhIHtAbGluayBhcHBraXQuZGF0YWJyaWNrcy50b0NvbnRleHR9LCBzbyBlaXRoZXIgdW53aW5kcyB0aGUgcmVxdWVzdC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgd2l0aENsaWVudDxUPihcbiAgICBvcGVyYXRpb246IHN0cmluZyxcbiAgICBzaWduYWw6IEFib3J0U2lnbmFsIHwgdW5kZWZpbmVkLFxuICAgIGZuOiAoY2xpZW50OiBXb3Jrc3BhY2VDbGllbnRMaWtlLCBjb250ZXh0PzogQ29udGV4dCkgPT4gUHJvbWlzZTxUPixcbiAgKTogUHJvbWlzZTxUPiB7XG4gICAgY29uc3QgY2xpZW50ID0gdGhpcy53b3Jrc3BhY2VDbGllbnRGYWN0b3J5KCk7XG4gICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gY29udHJvbGxlci5hYm9ydCgpLCB0aGlzLmNvbmZpZy50aW1lb3V0TXMpO1xuICAgIGNvbnN0IGNvbnRleHQgPSBkYXRhYnJpY2tzLnRvQ29udGV4dChjb250cm9sbGVyLCBzaWduYWwpO1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgZm4oY2xpZW50LCBjb250ZXh0KTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGlmIChzaWduYWw/LmFib3J0ZWQpIHRocm93IEV4ZWN1dGlvbkVycm9yLmNhbmNlbGVkKCk7XG4gICAgICBsb2dnZXIud2FybihcImV4ZWN1dGlvbi1mYWlsZWRcIiwgeyBvcGVyYXRpb24gfSk7XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lcik7XG4gICAgfVxuICB9XG59XG5cbi8qKiBUaGUgT0JPIHdvcmtzcGFjZSBjbGllbnQgZnJvbSB0aGUgYWN0aXZlIGNvbnRleHQsIG9yIGEgc2VydmljZS1wcmluY2lwYWwgY2xpZW50LiAqL1xuZnVuY3Rpb24gZGVmYXVsdFdvcmtzcGFjZUNsaWVudCgpOiBXb3Jrc3BhY2VDbGllbnRMaWtlIHtcbiAgY29uc3QgY3R4ID0gYXBwa2l0LnRyeUdldEV4ZWN1dGlvbkNvbnRleHQoKTtcbiAgaWYgKGN0eD8uY2xpZW50KSByZXR1cm4gY3R4LmNsaWVudDtcbiAgLy8gT3V0c2lkZSBhIHJlcXVlc3Qgc2NvcGUgKGEgc2NyaXB0LCBhIHRlc3QpOiBhIGZyZXNoIGVudi1hdXRoIGNsaWVudC5cbiAgcmV0dXJuIGdldEV4ZWN1dGlvbkNvbnRleHQoKS5jbGllbnQ7XG59XG5cbi8qKiBDb25zdHJ1Y3QgYSB7QGxpbmsgU2VhcmNoQ2xpZW50fSBmcm9tIGEgcmVzb2x2ZWQgY29uZmlnLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVNlYXJjaENsaWVudChcbiAgY29uZmlnPzogUmVzb2x2ZWRTZWFyY2hDb25maWcsXG4gIHdvcmtzcGFjZUNsaWVudEZhY3Rvcnk/OiAoKSA9PiBXb3Jrc3BhY2VDbGllbnRMaWtlLFxuICBsYWtlYmFzZT86IExha2ViYXNlU2VhcmNoQmFja2VuZCxcbik6IFNlYXJjaENsaWVudCB7XG4gIHJldHVybiBuZXcgU2VhcmNoQ2xpZW50KGNvbmZpZywgd29ya3NwYWNlQ2xpZW50RmFjdG9yeSwgbGFrZWJhc2UpO1xufVxuIl19
|