@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.
@@ -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