@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,506 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AppKit plugin (registered name: `search`) that turns a Databricks AI
|
|
3
|
+
* Search (Vector Search) index into a batteries-included search surface. It is
|
|
4
|
+
* the "shortcut" half of this package: register it with nothing but an index
|
|
5
|
+
* name and you get, all at once -
|
|
6
|
+
*
|
|
7
|
+
* - a `search` / `universal_search` / (opt-in) `add_documents` tool set for
|
|
8
|
+
* both Mastra and AppKit agents (autocomplete is a small-`limit` search);
|
|
9
|
+
* - HTTP routes under `/api/search` a browser search box calls directly
|
|
10
|
+
* (`POST /` search, `POST /universal` federated, `GET /indexes` catalogue,
|
|
11
|
+
* and `POST /documents` when writes are enabled);
|
|
12
|
+
* - a `clientConfig()` payload so a UI knows the indexes, default, and page
|
|
13
|
+
* size at boot with no round-trip;
|
|
14
|
+
* - `exports()` so app code can `appkit.search.search(...)` directly.
|
|
15
|
+
*
|
|
16
|
+
* Everything runs under the caller's OBO identity (routes wrap in `asUser`),
|
|
17
|
+
* so Unity Catalog ACLs on the index apply. Registering the plugin resolves
|
|
18
|
+
* and logs the effective config (default index, known indexes, page size,
|
|
19
|
+
* mode) so a misconfiguration shows up in the boot log rather than on the
|
|
20
|
+
* first search.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
import { lakebase, Plugin, ResourceType, toPlugin, } from "@databricks/appkit";
|
|
25
|
+
import { defineTool, executeFromRegistry, toolsFromRegistry, } from "@databricks/appkit/beta";
|
|
26
|
+
import { plugin as pluginLookup } from "@dbx-tools/appkit";
|
|
27
|
+
import { search as searchContract } from "@dbx-tools/shared-search";
|
|
28
|
+
import { error as errorUtil, log, string } from "@dbx-tools/shared-core";
|
|
29
|
+
import { SEARCH_CONFIG_SCHEMA, DATABRICKS_INDEX_ENV, ENDPOINT_ENV, INDEX_ENV, resolveSearchConfig, } from "./config.js";
|
|
30
|
+
import { LakebaseSearchBackend } from "./lakebase.js";
|
|
31
|
+
import { toCreateIndexOptions } from "./index-tools.js";
|
|
32
|
+
import { toDocumentArray } from "./query.js";
|
|
33
|
+
import { getSearchRuntime, resetSearchRuntime } from "./runtime.js";
|
|
34
|
+
import { ADD_DOCUMENTS_TOOL_DESCRIPTION, CREATE_INDEX_TOOL_DESCRIPTION, SEARCH_TOOL_DESCRIPTION, SYNC_INDEX_TOOL_DESCRIPTION, UNIVERSAL_SEARCH_TOOL_DESCRIPTION, } from "./schema.js";
|
|
35
|
+
const logger = log.logger("search");
|
|
36
|
+
/** Mount-relative route (under `/api/search`) for a single-index search. */
|
|
37
|
+
const SEARCH_ROUTE = "/";
|
|
38
|
+
/** Mount-relative route for a universal (federated) search across indexes. */
|
|
39
|
+
const UNIVERSAL_ROUTE = "/universal";
|
|
40
|
+
/** Mount-relative route serving the index catalogue a search box reads. */
|
|
41
|
+
const INDEXES_ROUTE = "/indexes";
|
|
42
|
+
/** Mount-relative route for adding documents to a direct-access index. */
|
|
43
|
+
const DOCUMENTS_ROUTE = "/documents";
|
|
44
|
+
/** Mount-relative route for creating a Vector Search index. */
|
|
45
|
+
const INDEX_ROUTE = "/index";
|
|
46
|
+
/** Mount-relative route for syncing a Delta Sync index from its source table. */
|
|
47
|
+
const INDEX_SYNC_ROUTE = "/index/sync";
|
|
48
|
+
/**
|
|
49
|
+
* The AI Search index resource. Declared optional because the plugin is happy
|
|
50
|
+
* with no default index (a caller can name one per request); it is promoted to
|
|
51
|
+
* required once a deployment pins one. `SELECT` is the permission a query needs.
|
|
52
|
+
*/
|
|
53
|
+
const INDEX_RESOURCE = {
|
|
54
|
+
type: ResourceType.VECTOR_SEARCH_INDEX,
|
|
55
|
+
alias: "AI Search Index",
|
|
56
|
+
resourceKey: "search-index",
|
|
57
|
+
description: "Databricks AI Search (Vector Search) index the app searches by default " +
|
|
58
|
+
"(catalog.schema.index). Optional: a request may name any index the caller can read.",
|
|
59
|
+
permission: "SELECT",
|
|
60
|
+
fields: {
|
|
61
|
+
name: {
|
|
62
|
+
env: INDEX_ENV,
|
|
63
|
+
description: `Default AI Search index. ${DATABRICKS_INDEX_ENV} is also honored.`,
|
|
64
|
+
discovery: {
|
|
65
|
+
type: "cli",
|
|
66
|
+
cliCommand: "databricks vector-search-indexes list-indexes --endpoint-name <ENDPOINT> --output json",
|
|
67
|
+
selectField: ".name",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* AppKit plugin exposing AI Search as a search box, an agent tool set, and a
|
|
74
|
+
* direct API.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* import { createApp, server } from "@databricks/appkit";
|
|
79
|
+
* import { plugin as searchPlugin } from "@dbx-tools/search";
|
|
80
|
+
*
|
|
81
|
+
* await createApp({
|
|
82
|
+
* plugins: [
|
|
83
|
+
* server(),
|
|
84
|
+
* // zero-config: reads DATABRICKS_VECTOR_SEARCH_INDEX
|
|
85
|
+
* searchPlugin.search(),
|
|
86
|
+
* // or go deeper:
|
|
87
|
+
* // searchPlugin.search({
|
|
88
|
+
* // index: "main.support.docs",
|
|
89
|
+
* // indexes: ["main.support.docs", "main.catalog.products"],
|
|
90
|
+
* // columns: ["title", "url", "body"],
|
|
91
|
+
* // mode: "hybrid",
|
|
92
|
+
* // }),
|
|
93
|
+
* ],
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export class SearchPlugin extends Plugin {
|
|
98
|
+
static manifest = {
|
|
99
|
+
name: "search",
|
|
100
|
+
displayName: "AI Search",
|
|
101
|
+
description: "Search Databricks AI Search (Vector Search) indexes: agent tools, HTTP routes for a " +
|
|
102
|
+
"search box, universal search across indexes, and an opt-in document write surface.",
|
|
103
|
+
stability: "beta",
|
|
104
|
+
resources: {
|
|
105
|
+
required: [],
|
|
106
|
+
optional: [INDEX_RESOURCE],
|
|
107
|
+
},
|
|
108
|
+
config: { schema: SEARCH_CONFIG_SCHEMA },
|
|
109
|
+
};
|
|
110
|
+
/** The base path AppKit mounts this plugin's routes under. */
|
|
111
|
+
get basePath() {
|
|
112
|
+
return `/api/${SearchPlugin.manifest.name}`;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Promote the index to a required resource once a deployment pins a default,
|
|
116
|
+
* through plugin config or either environment name.
|
|
117
|
+
*/
|
|
118
|
+
static getResourceRequirements(config) {
|
|
119
|
+
const pinned = string.trimToNull(config.index) ??
|
|
120
|
+
string.trimToNull(process.env[INDEX_ENV]) ??
|
|
121
|
+
string.trimToNull(process.env[DATABRICKS_INDEX_ENV]);
|
|
122
|
+
return pinned === null ? [] : [{ ...INDEX_RESOURCE, required: true }];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The tools this plugin offers to an AppKit agent. `search` /
|
|
126
|
+
* `universal_search` are reads; `add_documents` / `create_index` /
|
|
127
|
+
* `sync_index` are only offered when the write surface is enabled. None is
|
|
128
|
+
* autoInheritable: every tool runs under the caller's identity, so it must be
|
|
129
|
+
* granted explicitly.
|
|
130
|
+
*/
|
|
131
|
+
get tools() {
|
|
132
|
+
const { config } = getSearchRuntime({ config: this.config });
|
|
133
|
+
const registry = {
|
|
134
|
+
search: defineTool({
|
|
135
|
+
description: SEARCH_TOOL_DESCRIPTION,
|
|
136
|
+
schema: searchContract.searchRequestSchema,
|
|
137
|
+
annotations: { effect: "read", requiresUserContext: true },
|
|
138
|
+
autoInheritable: false,
|
|
139
|
+
execute: async (args, signal) => this.runSearch(args, signal),
|
|
140
|
+
}),
|
|
141
|
+
universal_search: defineTool({
|
|
142
|
+
description: UNIVERSAL_SEARCH_TOOL_DESCRIPTION,
|
|
143
|
+
schema: searchContract.universalSearchRequestSchema,
|
|
144
|
+
annotations: { effect: "read", requiresUserContext: true },
|
|
145
|
+
autoInheritable: false,
|
|
146
|
+
execute: async (args, signal) => this.runUniversalSearch(args, signal),
|
|
147
|
+
}),
|
|
148
|
+
};
|
|
149
|
+
if (config.allowWrite) {
|
|
150
|
+
registry.add_documents = defineTool({
|
|
151
|
+
description: ADD_DOCUMENTS_TOOL_DESCRIPTION,
|
|
152
|
+
schema: searchContract.searchRequestSchema
|
|
153
|
+
.pick({ index: true })
|
|
154
|
+
.extend({ documents: searchContract.searchDocumentSchema.array() }),
|
|
155
|
+
annotations: { effect: "write", requiresUserContext: true },
|
|
156
|
+
autoInheritable: false,
|
|
157
|
+
execute: async (args, signal) => this.runAddDocuments(args, signal),
|
|
158
|
+
});
|
|
159
|
+
registry.create_index = defineTool({
|
|
160
|
+
description: CREATE_INDEX_TOOL_DESCRIPTION,
|
|
161
|
+
schema: searchContract.createIndexRequestSchema,
|
|
162
|
+
annotations: { effect: "write", requiresUserContext: true },
|
|
163
|
+
autoInheritable: false,
|
|
164
|
+
execute: async (args, signal) => this.runCreateIndex(args, signal),
|
|
165
|
+
});
|
|
166
|
+
registry.sync_index = defineTool({
|
|
167
|
+
description: SYNC_INDEX_TOOL_DESCRIPTION,
|
|
168
|
+
schema: searchContract.syncIndexRequestSchema,
|
|
169
|
+
annotations: { effect: "write", requiresUserContext: true },
|
|
170
|
+
autoInheritable: false,
|
|
171
|
+
execute: async (args, signal) => this.runSyncIndex(args, signal),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return registry;
|
|
175
|
+
}
|
|
176
|
+
/** Prime the shared runtime from config and log the effective policy at boot. */
|
|
177
|
+
async setup() {
|
|
178
|
+
// Choose a backend. Vector Search is primary; when NO Vector Search
|
|
179
|
+
// endpoint is configured but the AppKit `lakebase` plugin is registered,
|
|
180
|
+
// fall back to a Postgres full-text index. Both answer with the identical
|
|
181
|
+
// shape, so this choice is invisible to tools, routes, and the UI.
|
|
182
|
+
const lakebaseBackend = this.resolveLakebaseBackend();
|
|
183
|
+
// The runtime may already have been built (config-only) when `tools()` ran
|
|
184
|
+
// during registration; rebuild it so it carries the chosen backend.
|
|
185
|
+
resetSearchRuntime();
|
|
186
|
+
const { config } = getSearchRuntime({
|
|
187
|
+
config: this.config,
|
|
188
|
+
...(lakebaseBackend ? { lakebase: lakebaseBackend } : {}),
|
|
189
|
+
});
|
|
190
|
+
logger.info("ready", {
|
|
191
|
+
backend: lakebaseBackend ? "lakebase" : "vector-search",
|
|
192
|
+
defaultIndex: config.defaultIndex ?? "(none - pass per request)",
|
|
193
|
+
indexes: config.indexes.map((i) => i.alias),
|
|
194
|
+
pageSize: config.pageSize,
|
|
195
|
+
mode: config.mode,
|
|
196
|
+
allowWrite: config.allowWrite,
|
|
197
|
+
basePath: this.basePath,
|
|
198
|
+
ensureOnSetup: config.ensureOnSetup
|
|
199
|
+
? (config.ensureOnSetup.index ?? config.defaultIndex)
|
|
200
|
+
: "off",
|
|
201
|
+
});
|
|
202
|
+
// Provision a real index in the BACKGROUND so a slow first-time endpoint or
|
|
203
|
+
// index build never blocks the server from coming up.
|
|
204
|
+
if (config.ensureOnSetup)
|
|
205
|
+
void this.runEnsureOnSetup(config);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Build the Lakebase FALLBACK backend, or return `undefined` when Vector
|
|
209
|
+
* Search should be used. Falls back only when there is NO Vector Search
|
|
210
|
+
* endpoint configured (plugin config or `SEARCH_ENDPOINT` /
|
|
211
|
+
* `DATABRICKS_VECTOR_SEARCH_ENDPOINT`) AND the sibling AppKit `lakebase`
|
|
212
|
+
* plugin is registered. The pg pool is built from that plugin's
|
|
213
|
+
* service-principal config exactly like `@dbx-tools/appkit-mastra`'s memory
|
|
214
|
+
* pool - no auth is re-implemented here.
|
|
215
|
+
*/
|
|
216
|
+
resolveLakebaseBackend() {
|
|
217
|
+
const config = resolveSearchConfig(this.config);
|
|
218
|
+
const hasEndpoint = config.endpoint !== undefined ||
|
|
219
|
+
string.trimToNull(process.env[ENDPOINT_ENV]) !== null ||
|
|
220
|
+
string.trimToNull(process.env.DATABRICKS_VECTOR_SEARCH_ENDPOINT) !== null;
|
|
221
|
+
if (hasEndpoint)
|
|
222
|
+
return undefined;
|
|
223
|
+
const lake = pluginLookup.instance(this.context, lakebase);
|
|
224
|
+
if (!lake)
|
|
225
|
+
return undefined;
|
|
226
|
+
logger.info("backend-lakebase", {
|
|
227
|
+
reason: "no Vector Search endpoint configured; using the Lakebase full-text fallback",
|
|
228
|
+
});
|
|
229
|
+
// `getPgConfig()` must be read OUTSIDE any asUser scope (as it is here at
|
|
230
|
+
// setup) so it carries the SP connection target + token-refresh callback.
|
|
231
|
+
return new LakebaseSearchBackend(() => lake.exports().getPgConfig());
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Ensure the endpoint + index exist and seed them, honoring `ensureOnSetup`.
|
|
235
|
+
* Uses boot-time SDK auth (env / config profile) via the client's
|
|
236
|
+
* out-of-request fallback. Failures are logged, never thrown - a search app
|
|
237
|
+
* should still start even if provisioning is slow or a permission is missing.
|
|
238
|
+
*/
|
|
239
|
+
async runEnsureOnSetup(config) {
|
|
240
|
+
const spec = config.ensureOnSetup;
|
|
241
|
+
if (!spec)
|
|
242
|
+
return;
|
|
243
|
+
const index = string.trimToNull(spec.index) ?? config.defaultIndex;
|
|
244
|
+
if (!index) {
|
|
245
|
+
logger.warn("ensure-skipped", {
|
|
246
|
+
reason: "no index name (set `index` or `ensureOnSetup.index`)",
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const documents = spec.documents ?? [];
|
|
251
|
+
// Infer a managed-direct-access schema from the first seed row when not given.
|
|
252
|
+
const schema = spec.schema ??
|
|
253
|
+
(documents.length > 0 && !spec.sourceTable
|
|
254
|
+
? this.inferSchema(documents[0], spec.primaryKey ?? "id", spec.textColumn ?? "text")
|
|
255
|
+
: undefined);
|
|
256
|
+
try {
|
|
257
|
+
logger.info("ensure-start", { index });
|
|
258
|
+
const { client } = getSearchRuntime();
|
|
259
|
+
const info = await client.provision(index, {
|
|
260
|
+
...(spec.endpoint ? { endpoint: spec.endpoint } : {}),
|
|
261
|
+
...(spec.primaryKey ? { primaryKey: spec.primaryKey } : {}),
|
|
262
|
+
...(spec.textColumn ? { embeddingSourceColumn: spec.textColumn } : {}),
|
|
263
|
+
...(spec.embeddingModel ? { embeddingModel: spec.embeddingModel } : {}),
|
|
264
|
+
...(spec.sourceTable ? { sourceTable: spec.sourceTable } : {}),
|
|
265
|
+
...(schema ? { schema } : {}),
|
|
266
|
+
...(spec.timeoutMs ? { timeoutMs: spec.timeoutMs } : {}),
|
|
267
|
+
...(documents.length > 0 ? { seed: documents } : {}),
|
|
268
|
+
});
|
|
269
|
+
logger.info("ensure-ready", {
|
|
270
|
+
index: info.name,
|
|
271
|
+
ready: info.ready,
|
|
272
|
+
rowCount: info.rowCount ?? 0,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
catch (cause) {
|
|
276
|
+
logger.warn("ensure-failed", { index, message: errorUtil.errorMessage(cause) });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/** Build a Vector Search `schema_json` map from a seed document's value types. */
|
|
280
|
+
inferSchema(doc, primaryKey, textColumn) {
|
|
281
|
+
const schema = { [primaryKey]: "string", [textColumn]: "string" };
|
|
282
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
283
|
+
if (key in schema)
|
|
284
|
+
continue;
|
|
285
|
+
schema[key] =
|
|
286
|
+
typeof value === "number"
|
|
287
|
+
? Number.isInteger(value)
|
|
288
|
+
? "int"
|
|
289
|
+
: "double"
|
|
290
|
+
: typeof value === "boolean"
|
|
291
|
+
? "boolean"
|
|
292
|
+
: "string";
|
|
293
|
+
}
|
|
294
|
+
return schema;
|
|
295
|
+
}
|
|
296
|
+
/** Drop the shared runtime so a restarted app re-resolves config. */
|
|
297
|
+
async shutdown() {
|
|
298
|
+
resetSearchRuntime();
|
|
299
|
+
}
|
|
300
|
+
abortActiveOperations() {
|
|
301
|
+
super.abortActiveOperations();
|
|
302
|
+
void this.shutdown();
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Mount the search routes under `/api/search`. Each is wrapped in
|
|
306
|
+
* `asUser(req)` so the query runs as the requesting user and the index's
|
|
307
|
+
* Unity Catalog ACLs apply. `GET /indexes` needs no user scope - it just
|
|
308
|
+
* echoes the configured catalogue.
|
|
309
|
+
*/
|
|
310
|
+
injectRoutes(router) {
|
|
311
|
+
this.route(router, {
|
|
312
|
+
name: "search",
|
|
313
|
+
method: "post",
|
|
314
|
+
path: SEARCH_ROUTE,
|
|
315
|
+
handler: async (req, res) => {
|
|
316
|
+
await this.respond(res, "search", () => {
|
|
317
|
+
const request = searchContract.searchRequestSchema.parse(req.body ?? {});
|
|
318
|
+
return this.asUser(req).runSearch(request);
|
|
319
|
+
});
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
this.route(router, {
|
|
323
|
+
name: "universalSearch",
|
|
324
|
+
method: "post",
|
|
325
|
+
path: UNIVERSAL_ROUTE,
|
|
326
|
+
handler: async (req, res) => {
|
|
327
|
+
await this.respond(res, "universalSearch", () => {
|
|
328
|
+
const request = searchContract.universalSearchRequestSchema.parse(req.body ?? {});
|
|
329
|
+
return this.asUser(req).runUniversalSearch(request);
|
|
330
|
+
});
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
this.route(router, {
|
|
334
|
+
name: "indexes",
|
|
335
|
+
method: "get",
|
|
336
|
+
path: INDEXES_ROUTE,
|
|
337
|
+
handler: async (_req, res) => {
|
|
338
|
+
res.json(this.clientConfig());
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
this.route(router, {
|
|
342
|
+
name: "documents",
|
|
343
|
+
method: "post",
|
|
344
|
+
path: DOCUMENTS_ROUTE,
|
|
345
|
+
handler: async (req, res) => {
|
|
346
|
+
const { config } = getSearchRuntime();
|
|
347
|
+
if (!config.allowWrite) {
|
|
348
|
+
res.status(403).json({ error: "the document write surface is disabled" });
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
await this.respond(res, "addDocuments", () => this.asUser(req).runAddDocuments(req.body ?? {}));
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
this.route(router, {
|
|
355
|
+
name: "createIndex",
|
|
356
|
+
method: "post",
|
|
357
|
+
path: INDEX_ROUTE,
|
|
358
|
+
handler: async (req, res) => {
|
|
359
|
+
const { config } = getSearchRuntime();
|
|
360
|
+
if (!config.allowWrite) {
|
|
361
|
+
res.status(403).json({ error: "the index write surface is disabled" });
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
await this.respond(res, "createIndex", () => this.asUser(req).runCreateIndex(req.body ?? {}));
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
this.route(router, {
|
|
368
|
+
name: "syncIndex",
|
|
369
|
+
method: "post",
|
|
370
|
+
path: INDEX_SYNC_ROUTE,
|
|
371
|
+
handler: async (req, res) => {
|
|
372
|
+
const { config } = getSearchRuntime();
|
|
373
|
+
if (!config.allowWrite) {
|
|
374
|
+
res.status(403).json({ error: "the index write surface is disabled" });
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
await this.respond(res, "syncIndex", () => this.asUser(req).runSyncIndex(req.body ?? {}));
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Run a route body, turning a failure into a JSON error response.
|
|
383
|
+
*
|
|
384
|
+
* AppKit's `route()` registers the handler as-is, so a rejection escapes as
|
|
385
|
+
* an unhandled promise rejection and takes the whole process down rather than
|
|
386
|
+
* failing the one request. Every route here goes through this, so a bad index
|
|
387
|
+
* name or a Vector Search API error returns 500 with a message instead of
|
|
388
|
+
* killing the server.
|
|
389
|
+
*/
|
|
390
|
+
async respond(res, operation, run) {
|
|
391
|
+
try {
|
|
392
|
+
res.json(await run());
|
|
393
|
+
}
|
|
394
|
+
catch (cause) {
|
|
395
|
+
const message = errorUtil.errorMessage(cause);
|
|
396
|
+
logger.warn("route-failed", { operation, message });
|
|
397
|
+
res.status(500).json({ error: message });
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/** Surface the index catalogue + defaults so a search box needs no round-trip. */
|
|
401
|
+
clientConfig() {
|
|
402
|
+
const { config } = getSearchRuntime({ config: this.config });
|
|
403
|
+
const payload = {
|
|
404
|
+
indexes: config.indexes.map((index) => ({
|
|
405
|
+
name: index.name,
|
|
406
|
+
alias: index.alias,
|
|
407
|
+
...(index.primaryKey ? { primaryKey: index.primaryKey } : {}),
|
|
408
|
+
...(index.columns ? { columns: index.columns } : {}),
|
|
409
|
+
...(index.name === config.defaultIndex ? { isDefault: true } : {}),
|
|
410
|
+
})),
|
|
411
|
+
...(config.defaultIndex ? { defaultIndex: config.defaultIndex } : {}),
|
|
412
|
+
pageSize: config.pageSize,
|
|
413
|
+
basePath: this.basePath,
|
|
414
|
+
};
|
|
415
|
+
return payload;
|
|
416
|
+
}
|
|
417
|
+
/** AppKit `ToolProvider`: the tool definitions offered to an agent. */
|
|
418
|
+
getAgentTools() {
|
|
419
|
+
return toolsFromRegistry(this.tools);
|
|
420
|
+
}
|
|
421
|
+
/** AppKit `ToolProvider`: run one tool call, validating input against its schema. */
|
|
422
|
+
async executeAgentTool(name, args, signal) {
|
|
423
|
+
return executeFromRegistry(this.tools, name, args, signal);
|
|
424
|
+
}
|
|
425
|
+
exports() {
|
|
426
|
+
return {
|
|
427
|
+
/** Search one index (default when omitted). Runs as the current context user. */
|
|
428
|
+
search: (request, signal) => this.runSearch(request, signal),
|
|
429
|
+
/** Search across every configured index and merge the hits. */
|
|
430
|
+
universalSearch: (request, signal) => this.runUniversalSearch(request, signal),
|
|
431
|
+
/** Add or update documents in a direct-access index (throws when writes are disabled). */
|
|
432
|
+
addDocuments: (request, signal) => this.runAddDocuments(request, signal),
|
|
433
|
+
/** Create a Vector Search index (throws when writes are disabled). */
|
|
434
|
+
createIndex: (request, signal) => this.runCreateIndex(request, signal),
|
|
435
|
+
/** Sync a Delta Sync index from its source table (throws when writes are disabled). */
|
|
436
|
+
syncIndex: (request, signal) => this.runSyncIndex(request, signal),
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
async runSearch(args, signal) {
|
|
440
|
+
const request = searchContract.searchRequestSchema.parse(args);
|
|
441
|
+
const { client } = getSearchRuntime();
|
|
442
|
+
return client.search(request.query, {
|
|
443
|
+
...(request.index ? { index: request.index } : {}),
|
|
444
|
+
...(request.limit ? { limit: request.limit } : {}),
|
|
445
|
+
...(request.mode ? { mode: request.mode } : {}),
|
|
446
|
+
...(request.columns ? { columns: request.columns } : {}),
|
|
447
|
+
...(request.filter ? { filter: request.filter } : {}),
|
|
448
|
+
...(request.scoreThreshold !== undefined ? { scoreThreshold: request.scoreThreshold } : {}),
|
|
449
|
+
...(signal ? { signal } : {}),
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
async runUniversalSearch(args, signal) {
|
|
453
|
+
const request = searchContract.universalSearchRequestSchema.parse(args);
|
|
454
|
+
const { client } = getSearchRuntime();
|
|
455
|
+
return client.universalSearch(request.query, {
|
|
456
|
+
...(request.indexes ? { indexes: request.indexes } : {}),
|
|
457
|
+
...(request.limit ? { limit: request.limit } : {}),
|
|
458
|
+
...(request.mode ? { mode: request.mode } : {}),
|
|
459
|
+
...(signal ? { signal } : {}),
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
async runAddDocuments(args, signal) {
|
|
463
|
+
const record = (args ?? {});
|
|
464
|
+
const { client, config } = getSearchRuntime();
|
|
465
|
+
const documents = toDocumentArray(record.documents);
|
|
466
|
+
const index = record.index ?? config.defaultIndex ?? "";
|
|
467
|
+
return client.addDocuments(index, documents, signal);
|
|
468
|
+
}
|
|
469
|
+
async runCreateIndex(args, signal) {
|
|
470
|
+
const request = searchContract.createIndexRequestSchema.parse(args);
|
|
471
|
+
const { client } = getSearchRuntime();
|
|
472
|
+
return client.createIndex(request.name, toCreateIndexOptions(request, signal));
|
|
473
|
+
}
|
|
474
|
+
async runSyncIndex(args, signal) {
|
|
475
|
+
const request = searchContract.syncIndexRequestSchema.parse(args);
|
|
476
|
+
const { client, config } = getSearchRuntime();
|
|
477
|
+
const index = request.index ?? config.defaultIndex ?? "";
|
|
478
|
+
await client.syncIndex(index, signal);
|
|
479
|
+
return { index, synced: true };
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Register the AI Search plugin with AppKit.
|
|
484
|
+
*
|
|
485
|
+
* @example
|
|
486
|
+
* ```ts
|
|
487
|
+
* import { createApp, server } from "@databricks/appkit";
|
|
488
|
+
* import { plugin as searchPlugin, tool as searchToolModule } from "@dbx-tools/search";
|
|
489
|
+
* import { agents, plugin as mastraPlugin } from "@dbx-tools/appkit-mastra";
|
|
490
|
+
*
|
|
491
|
+
* const support = agents.createAgent({
|
|
492
|
+
* instructions: "Answer from the docs; use `search` to find them.",
|
|
493
|
+
* tools: () => ({ search: searchToolModule.searchTool() }),
|
|
494
|
+
* });
|
|
495
|
+
*
|
|
496
|
+
* await createApp({
|
|
497
|
+
* plugins: [
|
|
498
|
+
* server(),
|
|
499
|
+
* searchPlugin.search({ index: "main.support.docs" }),
|
|
500
|
+
* mastraPlugin.mastra({ agents: support }),
|
|
501
|
+
* ],
|
|
502
|
+
* });
|
|
503
|
+
* ```
|
|
504
|
+
*/
|
|
505
|
+
export const search = toPlugin(SearchPlugin);
|
|
506
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXNCRztBQUVILE9BQU8sRUFDTCxRQUFRLEVBQ1IsTUFBTSxFQUNOLFlBQVksRUFDWixRQUFRLEdBSVQsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQ0wsVUFBVSxFQUNWLG1CQUFtQixFQUNuQixpQkFBaUIsR0FJbEIsTUFBTSx5QkFBeUIsQ0FBQztBQUNqQyxPQUFPLEVBQUUsTUFBTSxJQUFJLFlBQVksRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQzNELE9BQU8sRUFBRSxNQUFNLElBQUksY0FBYyxFQUEyQixNQUFNLDBCQUEwQixDQUFDO0FBQzdGLE9BQU8sRUFBRSxLQUFLLElBQUksU0FBUyxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN6RSxPQUFPLEVBQ0wsb0JBQW9CLEVBQ3BCLG9CQUFvQixFQUNwQixZQUFZLEVBQ1osU0FBUyxFQUNULG1CQUFtQixHQUVwQixNQUFNLGFBQWEsQ0FBQztBQUNyQixPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdEQsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUM3QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDcEUsT0FBTyxFQUNMLDhCQUE4QixFQUM5Qiw2QkFBNkIsRUFDN0IsdUJBQXVCLEVBQ3ZCLDJCQUEyQixFQUMzQixpQ0FBaUMsR0FDbEMsTUFBTSxhQUFhLENBQUM7QUFFckIsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUVwQyw0RUFBNEU7QUFD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QztRQUNqQixJQUFJLENBQUM7WUFDSCxNQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7WUFDdkMsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLGdCQUFnQixFQUFFLENBQUM7WUFDdEMsTUFBTSxJQUFJLEdBQUcsTUFBTSxNQUFNLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRTtnQkFDekMsR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUNyRCxHQUFHLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsRUFBRSxVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQzNELEdBQUcsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxFQUFFLHFCQUFxQixFQUFFLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUN0RSxHQUFHLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUMsRUFBRSxjQUFjLEVBQUUsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7Z0JBQ3ZFLEdBQUcsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxFQUFFLFdBQVcsRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDOUQsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUM3QixHQUFHLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENB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QztJQUVPLEtBQUssQ0FBQyxZQUFZLENBQUMsSUFBYSxFQUFFLE1BQW9CO1FBQzVELE1BQU0sT0FBTyxHQUFHLGNBQWMsQ0FBQyxzQkFBc0IsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbEUsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxnQkFBZ0IsRUFBRSxDQUFDO1FBQzlDLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxLQUFLLElBQUksTUFBTSxDQUFDLFlBQVksSUFBSSxFQUFFLENBQUM7UUFDekQsTUFBTSxNQUFNLENBQUMsU0FBUyxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQztRQUN0QyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUNqQyxDQUFDOztBQUdIOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0JHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sTUFBTSxHQUFHLFFBQVEsQ0FBQyxZQUFZLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQXBwS2l0IHBsdWdpbiAocmVnaXN0ZXJlZCBuYW1lOiBgc2VhcmNoYCkgdGhhdCB0dXJucyBhIERhdGFicmlja3MgQUlcbiAqIFNlYXJjaCAoVmVjdG9yIFNlYXJjaCkgaW5kZXggaW50byBhIGJhdHRlcmllcy1pbmNsdWRlZCBzZWFyY2ggc3VyZmFjZS4gSXQgaXNcbiAqIHRoZSBcInNob3J0Y3V0XCIgaGFsZiBvZiB0aGlzIHBhY2thZ2U6IHJlZ2lzdGVyIGl0IHdpdGggbm90aGluZyBidXQgYW4gaW5kZXhcbiAqIG5hbWUgYW5kIHlvdSBnZXQsIGFsbCBhdCBvbmNlIC1cbiAqXG4gKiAgIC0gYSBgc2VhcmNoYCAvIGB1bml2ZXJzYWxfc2VhcmNoYCAvIChvcHQtaW4pIGBhZGRfZG9jdW1lbnRzYCB0b29sIHNldCBmb3JcbiAqICAgICBib3RoIE1hc3RyYSBhbmQgQXBwS2l0IGFnZW50cyAoYXV0b2NvbXBsZXRlIGlzIGEgc21hbGwtYGxpbWl0YCBzZWFyY2gpO1xuICogICAtIEhUVFAgcm91dGVzIHVuZGVyIGAvYXBpL3NlYXJjaGAgYSBicm93c2VyIHNlYXJjaCBib3ggY2FsbHMgZGlyZWN0bHlcbiAqICAgICAoYFBPU1QgL2Agc2VhcmNoLCBgUE9TVCAvdW5pdmVyc2FsYCBmZWRlcmF0ZWQsIGBHRVQgL2luZGV4ZXNgIGNhdGFsb2d1ZSxcbiAqICAgICBhbmQgYFBPU1QgL2RvY3VtZW50c2Agd2hlbiB3cml0ZXMgYXJlIGVuYWJsZWQpO1xuICogICAtIGEgYGNsaWVudENvbmZpZygpYCBwYXlsb2FkIHNvIGEgVUkga25vd3MgdGhlIGluZGV4ZXMsIGRlZmF1bHQsIGFuZCBwYWdlXG4gKiAgICAgc2l6ZSBhdCBib290IHdpdGggbm8gcm91bmQtdHJpcDtcbiAqICAgLSBgZXhwb3J0cygpYCBzbyBhcHAgY29kZSBjYW4gYGFwcGtpdC5zZWFyY2guc2VhcmNoKC4uLilgIGRpcmVjdGx5LlxuICpcbiAqIEV2ZXJ5dGhpbmcgcnVucyB1bmRlciB0aGUgY2FsbGVyJ3MgT0JPIGlkZW50aXR5IChyb3V0ZXMgd3JhcCBpbiBgYXNVc2VyYCksXG4gKiBzbyBVbml0eSBDYXRhbG9nIEFDTHMgb24gdGhlIGluZGV4IGFwcGx5LiBSZWdpc3RlcmluZyB0aGUgcGx1Z2luIHJlc29sdmVzXG4gKiBhbmQgbG9ncyB0aGUgZWZmZWN0aXZlIGNvbmZpZyAoZGVmYXVsdCBpbmRleCwga25vd24gaW5kZXhlcywgcGFnZSBzaXplLFxuICogbW9kZSkgc28gYSBtaXNjb25maWd1cmF0aW9uIHNob3dzIHVwIGluIHRoZSBib290IGxvZyByYXRoZXIgdGhhbiBvbiB0aGVcbiAqIGZpcnN0IHNlYXJjaC5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHtcbiAgbGFrZWJhc2UsXG4gIFBsdWdpbixcbiAgUmVzb3VyY2VUeXBlLFxuICB0b1BsdWdpbixcbiAgdHlwZSBJQXBwUm91dGVyLFxuICB0eXBlIFBsdWdpbk1hbmlmZXN0LFxuICB0eXBlIFJlc291cmNlUmVxdWlyZW1lbnQsXG59IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7XG4gIGRlZmluZVRvb2wsXG4gIGV4ZWN1dGVGcm9tUmVnaXN0cnksXG4gIHRvb2xzRnJvbVJlZ2lzdHJ5LFxuICB0eXBlIEFnZW50VG9vbERlZmluaXRpb24sXG4gIHR5cGUgVG9vbFByb3ZpZGVyLFxuICB0eXBlIFRvb2xSZWdpc3RyeSxcbn0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdC9iZXRhXCI7XG5pbXBvcnQgeyBwbHVnaW4gYXMgcGx1Z2luTG9va3VwIH0gZnJvbSBcIkBkYngtdG9vbHMvYXBwa2l0XCI7XG5pbXBvcnQgeyBzZWFyY2ggYXMgc2VhcmNoQ29udHJhY3QsIHR5cGUgU2VhcmNoQ2xpZW50Q29uZmlnIH0gZnJvbSBcIkBkYngtdG9vbHMvc2hhcmVkLXNlYXJjaFwiO1xuaW1wb3J0IHsgZXJyb3IgYXMgZXJyb3JVdGlsLCBsb2csIHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQge1xuICBTRUFSQ0hfQ09ORklHX1NDSEVNQSxcbiAgREFUQUJSSUNLU19JTkRFWF9FTlYsXG4gIEVORFBPSU5UX0VOVixcbiAgSU5ERVhfRU5WLFxuICByZXNvbHZlU2VhcmNoQ29uZmlnLFxuICB0eXBlIFNlYXJjaFBsdWdpbkNvbmZpZyxcbn0gZnJvbSBcIi4vY29uZmlnLnRzXCI7XG5pbXBvcnQgeyBMYWtlYmFzZVNlYXJjaEJhY2tlbmQgfSBmcm9tIFwiLi9sYWtlYmFzZS50c1wiO1xuaW1wb3J0IHsgdG9DcmVhdGVJbmRleE9wdGlvbnMgfSBmcm9tIFwiLi9pbmRleC10b29scy50c1wiO1xuaW1wb3J0IHsgdG9Eb2N1bWVudEFycmF5IH0gZnJvbSBcIi4vcXVlcnkudHNcIjtcbmltcG9ydCB7IGdldFNlYXJjaFJ1bnRpbWUsIHJlc2V0U2VhcmNoUnVudGltZSB9IGZyb20gXCIuL3J1bnRpbWUudHNcIjtcbmltcG9ydCB7XG4gIEFERF9ET0NVTUVOVFNfVE9PTF9ERVNDUklQVElPTixcbiAgQ1JFQVRFX0lOREVYX1RPT0xfREVTQ1JJUFRJT04sXG4gIFNFQVJDSF9UT09MX0RFU0NSSVBUSU9OLFxuICBTWU5DX0lOREVYX1RPT0xfREVTQ1JJUFRJT04sXG4gIFVOSVZFUlNBTF9TRUFSQ0hfVE9PTF9ERVNDUklQVElPTixcbn0gZnJvbSBcIi4vc2NoZW1hLnRzXCI7XG5cbmNvbnN0IGxvZ2dlciA9IGxvZy5sb2dnZXIoXCJzZWFyY2hcIik7XG5cbi8qKiBNb3VudC1yZWxhdGl2ZSByb3V0ZSAodW5kZXIgYC9hcGkvc2VhcmNoYCkgZm9yIGEgc2luZ2xlLWluZGV4IHNlYXJjaC4gKi9cbmNvbnN0IFNFQVJDSF9ST1VURSA9IFwiL1wiO1xuXG4vKiogTW91bnQtcmVsYXRpdmUgcm91dGUgZm9yIGEgdW5pdmVyc2FsIChmZWRlcmF0ZWQpIHNlYXJjaCBhY3Jvc3MgaW5kZXhlcy4gKi9cbmNvbnN0IFVOSVZFUlNBTF9ST1VURSA9IFwiL3VuaXZlcnNhbFwiO1xuXG4vKiogTW91bnQtcmVsYXRpdmUgcm91dGUgc2VydmluZyB0aGUgaW5kZXggY2F0YWxvZ3VlIGEgc2VhcmNoIGJveCByZWFkcy4gKi9cbmNvbnN0IElOREVYRVNfUk9VVEUgPSBcIi9pbmRleGVzXCI7XG5cbi8qKiBNb3VudC1yZWxhdGl2ZSByb3V0ZSBmb3IgYWRkaW5nIGRvY3VtZW50cyB0byBhIGRpcmVjdC1hY2Nlc3MgaW5kZXguICovXG5jb25zdCBET0NVTUVOVFNfUk9VVEUgPSBcIi9kb2N1bWVudHNcIjtcblxuLyoqIE1vdW50LXJlbGF0aXZlIHJvdXRlIGZvciBjcmVhdGluZyBhIFZlY3RvciBTZWFyY2ggaW5kZXguICovXG5jb25zdCBJTkRFWF9ST1VURSA9IFwiL2luZGV4XCI7XG5cbi8qKiBNb3VudC1yZWxhdGl2ZSByb3V0ZSBmb3Igc3luY2luZyBhIERlbHRhIFN5bmMgaW5kZXggZnJvbSBpdHMgc291cmNlIHRhYmxlLiAqL1xuY29uc3QgSU5ERVhfU1lOQ19ST1VURSA9IFwiL2luZGV4L3N5bmNcIjtcblxuLyoqXG4gKiBUaGUgQUkgU2VhcmNoIGluZGV4IHJlc291cmNlLiBEZWNsYXJlZCBvcHRpb25hbCBiZWNhdXNlIHRoZSBwbHVnaW4gaXMgaGFwcHlcbiAqIHdpdGggbm8gZGVmYXVsdCBpbmRleCAoYSBjYWxsZXIgY2FuIG5hbWUgb25lIHBlciByZXF1ZXN0KTsgaXQgaXMgcHJvbW90ZWQgdG9cbiAqIHJlcXVpcmVkIG9uY2UgYSBkZXBsb3ltZW50IHBpbnMgb25lLiBgU0VMRUNUYCBpcyB0aGUgcGVybWlzc2lvbiBhIHF1ZXJ5IG5lZWRzLlxuICovXG5jb25zdCBJTkRFWF9SRVNPVVJDRSA9IHtcbiAgdHlwZTogUmVzb3VyY2VUeXBlLlZFQ1RPUl9TRUFSQ0hfSU5ERVgsXG4gIGFsaWFzOiBcIkFJIFNlYXJjaCBJbmRleFwiLFxuICByZXNvdXJjZUtleTogXCJzZWFyY2gtaW5kZXhcIixcbiAgZGVzY3JpcHRpb246XG4gICAgXCJEYXRhYnJpY2tzIEFJIFNlYXJjaCAoVmVjdG9yIFNlYXJjaCkgaW5kZXggdGhlIGFwcCBzZWFyY2hlcyBieSBkZWZhdWx0IFwiICtcbiAgICBcIihjYXRhbG9nLnNjaGVtYS5pbmRleCkuIE9wdGlvbmFsOiBhIHJlcXVlc3QgbWF5IG5hbWUgYW55IGluZGV4IHRoZSBjYWxsZXIgY2FuIHJlYWQuXCIsXG4gIHBlcm1pc3Npb246IFwiU0VMRUNUXCIsXG4gIGZpZWxkczoge1xuICAgIG5hbWU6IHtcbiAgICAgIGVudjogSU5ERVhfRU5WLFxuICAgICAgZGVzY3JpcHRpb246IGBEZWZhdWx0IEFJIFNlYXJjaCBpbmRleC4gJHtEQVRBQlJJQ0tTX0lOREVYX0VOVn0gaXMgYWxzbyBob25vcmVkLmAsXG4gICAgICBkaXNjb3Zlcnk6IHtcbiAgICAgICAgdHlwZTogXCJjbGlcIixcbiAgICAgICAgY2xpQ29tbWFuZDpcbiAgICAgICAgICBcImRhdGFicmlja3MgdmVjdG9yLXNlYXJjaC1pbmRleGVzIGxpc3QtaW5kZXhlcyAtLWVuZHBvaW50LW5hbWUgPEVORFBPSU5UPiAtLW91dHB1dCBqc29uXCIsXG4gICAgICAgIHNlbGVjdEZpZWxkOiBcIi5uYW1lXCIsXG4gICAgICB9LFxuICAgIH0sXG4gIH0sXG59IHNhdGlzZmllcyBPbWl0PFJlc291cmNlUmVxdWlyZW1lbnQsIFwicmVxdWlyZWRcIj47XG5cbi8qKlxuICogQXBwS2l0IHBsdWdpbiBleHBvc2luZyBBSSBTZWFyY2ggYXMgYSBzZWFyY2ggYm94LCBhbiBhZ2VudCB0b29sIHNldCwgYW5kIGFcbiAqIGRpcmVjdCBBUEkuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHRzXG4gKiBpbXBvcnQgeyBjcmVhdGVBcHAsIHNlcnZlciB9IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbiAqIGltcG9ydCB7IHBsdWdpbiBhcyBzZWFyY2hQbHVnaW4gfSBmcm9tIFwiQGRieC10b29scy9zZWFyY2hcIjtcbiAqXG4gKiBhd2FpdCBjcmVhdGVBcHAoe1xuICogICBwbHVnaW5zOiBbXG4gKiAgICAgc2VydmVyKCksXG4gKiAgICAgLy8gemVyby1jb25maWc6IHJlYWRzIERBVEFCUklDS1NfVkVDVE9SX1NFQVJDSF9JTkRFWFxuICogICAgIHNlYXJjaFBsdWdpbi5zZWFyY2goKSxcbiAqICAgICAvLyBvciBnbyBkZWVwZXI6XG4gKiAgICAgLy8gc2VhcmNoUGx1Z2luLnNlYXJjaCh7XG4gKiAgICAgLy8gICBpbmRleDogXCJtYWluLnN1cHBvcnQuZG9jc1wiLFxuICogICAgIC8vICAgaW5kZXhlczogW1wibWFpbi5zdXBwb3J0LmRvY3NcIiwgXCJtYWluLmNhdGFsb2cucHJvZHVjdHNcIl0sXG4gKiAgICAgLy8gICBjb2x1bW5zOiBbXCJ0aXRsZVwiLCBcInVybFwiLCBcImJvZHlcIl0sXG4gKiAgICAgLy8gICBtb2RlOiBcImh5YnJpZFwiLFxuICogICAgIC8vIH0pLFxuICogICBdLFxuICogfSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGNsYXNzIFNlYXJjaFBsdWdpbiBleHRlbmRzIFBsdWdpbjxTZWFyY2hQbHVnaW5Db25maWc+IGltcGxlbWVudHMgVG9vbFByb3ZpZGVyIHtcbiAgc3RhdGljIG1hbmlmZXN0ID0ge1xuICAgIG5hbWU6IFwic2VhcmNoXCIsXG4gICAgZGlzcGxheU5hbWU6IFwiQUkgU2VhcmNoXCIsXG4gICAgZGVzY3JpcHRpb246XG4gICAgICBcIlNlYXJjaCBEYXRhYnJpY2tzIEFJIFNlYXJjaCAoVmVjdG9yIFNlYXJjaCkgaW5kZXhlczogYWdlbnQgdG9vbHMsIEhUVFAgcm91dGVzIGZvciBhIFwiICtcbiAgICAgIFwic2VhcmNoIGJveCwgdW5pdmVyc2FsIHNlYXJjaCBhY3Jvc3MgaW5kZXhlcywgYW5kIGFuIG9wdC1pbiBkb2N1bWVudCB3cml0ZSBzdXJmYWNlLlwiLFxuICAgIHN0YWJpbGl0eTogXCJiZXRhXCIsXG4gICAgcmVzb3VyY2VzOiB7XG4gICAgICByZXF1aXJlZDogW10sXG4gICAgICBvcHRpb25hbDogW0lOREVYX1JFU09VUkNFXSxcbiAgICB9LFxuICAgIGNvbmZpZzogeyBzY2hlbWE6IFNFQVJDSF9DT05GSUdfU0NIRU1BIH0sXG4gIH0gc2F0aXNmaWVzIFBsdWdpbk1hbmlmZXN0PFwic2VhcmNoXCI+O1xuXG4gIC8qKiBUaGUgYmFzZSBwYXRoIEFwcEtpdCBtb3VudHMgdGhpcyBwbHVnaW4ncyByb3V0ZXMgdW5kZXIuICovXG4gIHByaXZhdGUgZ2V0IGJhc2VQYXRoKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGAvYXBpLyR7U2VhcmNoUGx1Z2luLm1hbmlmZXN0Lm5hbWV9YDtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcm9tb3RlIHRoZSBpbmRleCB0byBhIHJlcXVpcmVkIHJlc291cmNlIG9uY2UgYSBkZXBsb3ltZW50IHBpbnMgYSBkZWZhdWx0LFxuICAgKiB0aHJvdWdoIHBsdWdpbiBjb25maWcgb3IgZWl0aGVyIGVudmlyb25tZW50IG5hbWUuXG4gICAqL1xuICBzdGF0aWMgZ2V0UmVzb3VyY2VSZXF1aXJlbWVudHMoY29uZmlnOiBTZWFyY2hQbHVnaW5Db25maWcpOiBSZXNvdXJjZVJlcXVpcmVtZW50W10ge1xuICAgIGNvbnN0IHBpbm5lZCA9XG4gICAgICBzdHJpbmcudHJpbVRvTnVsbChjb25maWcuaW5kZXgpID8/XG4gICAgICBzdHJpbmcudHJpbVRvTnVsbChwcm9jZXNzLmVudltJTkRFWF9FTlZdKSA/P1xuICAgICAgc3RyaW5nLnRyaW1Ub051bGwocHJvY2Vzcy5lbnZbREFUQUJSSUNLU19JTkRFWF9FTlZdKTtcbiAgICByZXR1cm4gcGlubmVkID09PSBudWxsID8gW10gOiBbeyAuLi5JTkRFWF9SRVNPVVJDRSwgcmVxdWlyZWQ6IHRydWUgfV07XG4gIH1cblxuICAvKipcbiAgICogVGhlIHRvb2xzIHRoaXMgcGx1Z2luIG9mZmVycyB0byBhbiBBcHBLaXQgYWdlbnQuIGBzZWFyY2hgIC9cbiAgICogYHVuaXZlcnNhbF9zZWFyY2hgIGFyZSByZWFkczsgYGFkZF9kb2N1bWVudHNgIC8gYGNyZWF0ZV9pbmRleGAgL1xuICAgKiBgc3luY19pbmRleGAgYXJlIG9ubHkgb2ZmZXJlZCB3aGVuIHRoZSB3cml0ZSBzdXJmYWNlIGlzIGVuYWJsZWQuIE5vbmUgaXNcbiAgICogYXV0b0luaGVyaXRhYmxlOiBldmVyeSB0b29sIHJ1bnMgdW5kZXIgdGhlIGNhbGxlcidzIGlkZW50aXR5LCBzbyBpdCBtdXN0IGJlXG4gICAqIGdyYW50ZWQgZXhwbGljaXRseS5cbiAgICovXG4gIHByaXZhdGUgZ2V0IHRvb2xzKCk6IFRvb2xSZWdpc3RyeSB7XG4gICAgY29uc3QgeyBjb25maWcgfSA9IGdldFNlYXJjaFJ1bnRpbWUoeyBjb25maWc6IHRoaXMuY29uZmlnIH0pO1xuICAgIGNvbnN0IHJlZ2lzdHJ5OiBUb29sUmVnaXN0cnkgPSB7XG4gICAgICBzZWFyY2g6IGRlZmluZVRvb2woe1xuICAgICAgICBkZXNjcmlwdGlvbjogU0VBUkNIX1RPT0xfREVTQ1JJUFRJT04sXG4gICAgICAgIHNjaGVtYTogc2VhcmNoQ29udHJhY3Quc2VhcmNoUmVxdWVzdFNjaGVtYSxcbiAgICAgICAgYW5ub3RhdGlvbnM6IHsgZWZmZWN0OiBcInJlYWRcIiwgcmVxdWlyZXNVc2VyQ29udGV4dDogdHJ1ZSB9LFxuICAgICAgICBhdXRvSW5oZXJpdGFibGU6IGZhbHNlLFxuICAgICAgICBleGVjdXRlOiBhc3luYyAoYXJncywgc2lnbmFsKSA9PiB0aGlzLnJ1blNlYXJjaChhcmdzLCBzaWduYWwpLFxuICAgICAgfSksXG4gICAgICB1bml2ZXJzYWxfc2VhcmNoOiBkZWZpbmVUb29sKHtcbiAgICAgICAgZGVzY3JpcHRpb246IFVOSVZFUlNBTF9TRUFSQ0hfVE9PTF9ERVNDUklQVElPTixcbiAgICAgICAgc2NoZW1hOiBzZWFyY2hDb250cmFjdC51bml2ZXJzYWxTZWFyY2hSZXF1ZXN0U2NoZW1hLFxuICAgICAgICBhbm5vdGF0aW9uczogeyBlZmZlY3Q6IFwicmVhZFwiLCByZXF1aXJlc1VzZXJDb250ZXh0OiB0cnVlIH0sXG4gICAgICAgIGF1dG9Jbmhlcml0YWJsZTogZmFsc2UsXG4gICAgICAgIGV4ZWN1dGU6IGFzeW5jIChhcmdzLCBzaWduYWwpID0+IHRoaXMucnVuVW5pdmVyc2FsU2VhcmNoKGFyZ3MsIHNpZ25hbCksXG4gICAgICB9KSxcbiAgICB9O1xuICAgIGlmIChjb25maWcuYWxsb3dXcml0ZSkge1xuICAgICAgcmVnaXN0cnkuYWRkX2RvY3VtZW50cyA9IGRlZmluZVRvb2woe1xuICAgICAgICBkZXNjcmlwdGlvbjogQUREX0RPQ1VNRU5UU19UT09MX0RFU0NSSVBUSU9OLFxuICAgICAgICBzY2hlbWE6IHNlYXJjaENvbnRyYWN0LnNlYXJjaFJlcXVlc3RTY2hlbWFcbiAgICAgICAgICAucGljayh7IGluZGV4OiB0cnVlIH0pXG4gICAgICAgICAgLmV4dGVuZCh7IGRvY3VtZW50czogc2VhcmNoQ29udHJhY3Quc2VhcmNoRG9jdW1lbnRTY2hlbWEuYXJyYXkoKSB9KSxcbiAgICAgICAgYW5ub3RhdGlvbnM6IHsgZWZmZWN0OiBcIndyaXRlXCIsIHJlcXVpcmVzVXNlckNvbnRleHQ6IHRydWUgfSxcbiAgICAgICAgYXV0b0luaGVyaXRhYmxlOiBmYWxzZSxcbiAgICAgICAgZXhlY3V0ZTogYXN5bmMgKGFyZ3MsIHNpZ25hbCkgPT4gdGhpcy5ydW5BZGREb2N1bWVudHMoYXJncywgc2lnbmFsKSxcbiAgICAgIH0pO1xuICAgICAgcmVnaXN0cnkuY3JlYXRlX2luZGV4ID0gZGVmaW5lVG9vbCh7XG4gICAgICAgIGRlc2NyaXB0aW9uOiBDUkVBVEVfSU5ERVhfVE9PTF9ERVNDUklQVElPTixcbiAgICAgICAgc2NoZW1hOiBzZWFyY2hDb250cmFjdC5jcmVhdGVJbmRleFJlcXVlc3RTY2hlbWEsXG4gICAgICAgIGFubm90YXRpb25zOiB7IGVmZmVjdDogXCJ3cml0ZVwiLCByZXF1aXJlc1VzZXJDb250ZXh0OiB0cnVlIH0sXG4gICAgICAgIGF1dG9Jbmhlcml0YWJsZTogZmFsc2UsXG4gICAgICAgIGV4ZWN1dGU6IGFzeW5jIChhcmdzLCBzaWduYWwpID0+IHRoaXMucnVuQ3JlYXRlSW5kZXgoYXJncywgc2lnbmFsKSxcbiAgICAgIH0pO1xuICAgICAgcmVnaXN0cnkuc3luY19pbmRleCA9IGRlZmluZVRvb2woe1xuICAgICAgICBkZXNjcmlwdGlvbjogU1lOQ19JTkRFWF9UT09MX0RFU0NSSVBUSU9OLFxuICAgICAgICBzY2hlbWE6IHNlYXJjaENvbnRyYWN0LnN5bmNJbmRleFJlcXVlc3RTY2hlbWEsXG4gICAgICAgIGFubm90YXRpb25zOiB7IGVmZmVjdDogXCJ3cml0ZVwiLCByZXF1aXJlc1VzZXJDb250ZXh0OiB0cnVlIH0sXG4gICAgICAgIGF1dG9Jbmhlcml0YWJsZTogZmFsc2UsXG4gICAgICAgIGV4ZWN1dGU6IGFzeW5jIChhcmdzLCBzaWduYWwpID0+IHRoaXMucnVuU3luY0luZGV4KGFyZ3MsIHNpZ25hbCksXG4gICAgICB9KTtcbiAgICB9XG4gICAgcmV0dXJuIHJlZ2lzdHJ5O1xuICB9XG5cbiAgLyoqIFByaW1lIHRoZSBzaGFyZWQgcnVudGltZSBmcm9tIGNvbmZpZyBhbmQgbG9nIHRoZSBlZmZlY3RpdmUgcG9saWN5IGF0IGJvb3QuICovXG4gIG92ZXJyaWRlIGFzeW5jIHNldHVwKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIENob29zZSBhIGJhY2tlbmQuIFZlY3RvciBTZWFyY2ggaXMgcHJpbWFyeTsgd2hlbiBOTyBWZWN0b3IgU2VhcmNoXG4gICAgLy8gZW5kcG9pbnQgaXMgY29uZmlndXJlZCBidXQgdGhlIEFwcEtpdCBgbGFrZWJhc2VgIHBsdWdpbiBpcyByZWdpc3RlcmVkLFxuICAgIC8vIGZhbGwgYmFjayB0byBhIFBvc3RncmVzIGZ1bGwtdGV4dCBpbmRleC4gQm90aCBhbnN3ZXIgd2l0aCB0aGUgaWRlbnRpY2FsXG4gICAgLy8gc2hhcGUsIHNvIHRoaXMgY2hvaWNlIGlzIGludmlzaWJsZSB0byB0b29scywgcm91dGVzLCBhbmQgdGhlIFVJLlxuICAgIGNvbnN0IGxha2ViYXNlQmFja2VuZCA9IHRoaXMucmVzb2x2ZUxha2ViYXNlQmFja2VuZCgpO1xuICAgIC8vIFRoZSBydW50aW1lIG1heSBhbHJlYWR5IGhhdmUgYmVlbiBidWlsdCAoY29uZmlnLW9ubHkpIHdoZW4gYHRvb2xzKClgIHJhblxuICAgIC8vIGR1cmluZyByZWdpc3RyYXRpb247IHJlYnVpbGQgaXQgc28gaXQgY2FycmllcyB0aGUgY2hvc2VuIGJhY2tlbmQuXG4gICAgcmVzZXRTZWFyY2hSdW50aW1lKCk7XG4gICAgY29uc3QgeyBjb25maWcgfSA9IGdldFNlYXJjaFJ1bnRpbWUoe1xuICAgICAgY29uZmlnOiB0aGlzLmNvbmZpZyxcbiAgICAgIC4uLihsYWtlYmFzZUJhY2tlbmQgPyB7IGxha2ViYXNlOiBsYWtlYmFzZUJhY2tlbmQgfSA6IHt9KSxcbiAgICB9KTtcbiAgICBsb2dnZXIuaW5mbyhcInJlYWR5XCIsIHtcbiAgICAgIGJhY2tlbmQ6IGxha2ViYXNlQmFja2VuZCA/IFwibGFrZWJhc2VcIiA6IFwidmVjdG9yLXNlYXJjaFwiLFxuICAgICAgZGVmYXVsdEluZGV4OiBjb25maWcuZGVmYXVsdEluZGV4ID8/IFwiKG5vbmUgLSBwYXNzIHBlciByZXF1ZXN0KVwiLFxuICAgICAgaW5kZXhlczogY29uZmlnLmluZGV4ZXMubWFwKChpKSA9PiBpLmFsaWFzKSxcbiAgICAgIHBhZ2VTaXplOiBjb25maWcucGFnZVNpemUsXG4gICAgICBtb2RlOiBjb25maWcubW9kZSxcbiAgICAgIGFsbG93V3JpdGU6IGNvbmZpZy5hbGxvd1dyaXRlLFxuICAgICAgYmFzZVBhdGg6IHRoaXMuYmFzZVBhdGgsXG4gICAgICBlbnN1cmVPblNldHVwOiBjb25maWcuZW5zdXJlT25TZXR1cFxuICAgICAgICA/IChjb25maWcuZW5zdXJlT25TZXR1cC5pbmRleCA/PyBjb25maWcuZGVmYXVsdEluZGV4KVxuICAgICAgICA6IFwib2ZmXCIsXG4gICAgfSk7XG4gICAgLy8gUHJvdmlzaW9uIGEgcmVhbCBpbmRleCBpbiB0aGUgQkFDS0dST1VORCBzbyBhIHNsb3cgZmlyc3QtdGltZSBlbmRwb2ludCBvclxuICAgIC8vIGluZGV4IGJ1aWxkIG5ldmVyIGJsb2NrcyB0aGUgc2VydmVyIGZyb20gY29taW5nIHVwLlxuICAgIGlmIChjb25maWcuZW5zdXJlT25TZXR1cCkgdm9pZCB0aGlzLnJ1bkVuc3VyZU9uU2V0dXAoY29uZmlnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZCB0aGUgTGFrZWJhc2UgRkFMTEJBQ0sgYmFja2VuZCwgb3IgcmV0dXJuIGB1bmRlZmluZWRgIHdoZW4gVmVjdG9yXG4gICAqIFNlYXJjaCBzaG91bGQgYmUgdXNlZC4gRmFsbHMgYmFjayBvbmx5IHdoZW4gdGhlcmUgaXMgTk8gVmVjdG9yIFNlYXJjaFxuICAgKiBlbmRwb2ludCBjb25maWd1cmVkIChwbHVnaW4gY29uZmlnIG9yIGBTRUFSQ0hfRU5EUE9JTlRgIC9cbiAgICogYERBVEFCUklDS1NfVkVDVE9SX1NFQVJDSF9FTkRQT0lOVGApIEFORCB0aGUgc2libGluZyBBcHBLaXQgYGxha2ViYXNlYFxuICAgKiBwbHVnaW4gaXMgcmVnaXN0ZXJlZC4gVGhlIHBnIHBvb2wgaXMgYnVpbHQgZnJvbSB0aGF0IHBsdWdpbidzXG4gICAqIHNlcnZpY2UtcHJpbmNpcGFsIGNvbmZpZyBleGFjdGx5IGxpa2UgYEBkYngtdG9vbHMvYXBwa2l0LW1hc3RyYWAncyBtZW1vcnlcbiAgICogcG9vbCAtIG5vIGF1dGggaXMgcmUtaW1wbGVtZW50ZWQgaGVyZS5cbiAgICovXG4gIHByaXZhdGUgcmVzb2x2ZUxha2ViYXNlQmFja2VuZCgpOiBMYWtlYmFzZVNlYXJjaEJhY2tlbmQgfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IGNvbmZpZyA9IHJlc29sdmVTZWFyY2hDb25maWcodGhpcy5jb25maWcpO1xuICAgIGNvbnN0IGhhc0VuZHBvaW50ID1cbiAgICAgIGNvbmZpZy5lbmRwb2ludCAhPT0gdW5kZWZpbmVkIHx8XG4gICAgICBzdHJpbmcudHJpbVRvTnVsbChwcm9jZXNzLmVudltFTkRQT0lOVF9FTlZdKSAhPT0gbnVsbCB8fFxuICAgICAgc3RyaW5nLnRyaW1Ub051bGwocHJvY2Vzcy5lbnYuREFUQUJSSUNLU19WRUNUT1JfU0VBUkNIX0VORFBPSU5UKSAhPT0gbnVsbDtcbiAgICBpZiAoaGFzRW5kcG9pbnQpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgY29uc3QgbGFrZSA9IHBsdWdpbkxvb2t1cC5pbnN0YW5jZSh0aGlzLmNvbnRleHQsIGxha2ViYXNlKTtcbiAgICBpZiAoIWxha2UpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgbG9nZ2VyLmluZm8oXCJiYWNrZW5kLWxha2ViYXNlXCIsIHtcbiAgICAgIHJlYXNvbjogXCJubyBWZWN0b3IgU2VhcmNoIGVuZHBvaW50IGNvbmZpZ3VyZWQ7IHVzaW5nIHRoZSBMYWtlYmFzZSBmdWxsLXRleHQgZmFsbGJhY2tcIixcbiAgICB9KTtcbiAgICAvLyBgZ2V0UGdDb25maWcoKWAgbXVzdCBiZSByZWFkIE9VVFNJREUgYW55IGFzVXNlciBzY29wZSAoYXMgaXQgaXMgaGVyZSBhdFxuICAgIC8vIHNldHVwKSBzbyBpdCBjYXJyaWVzIHRoZSBTUCBjb25uZWN0aW9uIHRhcmdldCArIHRva2VuLXJlZnJlc2ggY2FsbGJhY2suXG4gICAgcmV0dXJuIG5ldyBMYWtlYmFzZVNlYXJjaEJhY2tlbmQoKCkgPT4gbGFrZS5leHBvcnRzKCkuZ2V0UGdDb25maWcoKSk7XG4gIH1cblxuICAvKipcbiAgICogRW5zdXJlIHRoZSBlbmRwb2ludCArIGluZGV4IGV4aXN0IGFuZCBzZWVkIHRoZW0sIGhvbm9yaW5nIGBlbnN1cmVPblNldHVwYC5cbiAgICogVXNlcyBib290LXRpbWUgU0RLIGF1dGggKGVudiAvIGNvbmZpZyBwcm9maWxlKSB2aWEgdGhlIGNsaWVudCdzXG4gICAqIG91dC1vZi1yZXF1ZXN0IGZhbGxiYWNrLiBGYWlsdXJlcyBhcmUgbG9nZ2VkLCBuZXZlciB0aHJvd24gLSBhIHNlYXJjaCBhcHBcbiAgICogc2hvdWxkIHN0aWxsIHN0YXJ0IGV2ZW4gaWYgcHJvdmlzaW9uaW5nIGlzIHNsb3cgb3IgYSBwZXJtaXNzaW9uIGlzIG1pc3NpbmcuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHJ1bkVuc3VyZU9uU2V0dXAoXG4gICAgY29uZmlnOiBSZXR1cm5UeXBlPHR5cGVvZiBnZXRTZWFyY2hSdW50aW1lPltcImNvbmZpZ1wiXSxcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgc3BlYyA9IGNvbmZpZy5lbnN1cmVPblNldHVwO1xuICAgIGlmICghc3BlYykgcmV0dXJuO1xuICAgIGNvbnN0IGluZGV4ID0gc3RyaW5nLnRyaW1Ub051bGwoc3BlYy5pbmRleCkgPz8gY29uZmlnLmRlZmF1bHRJbmRleDtcbiAgICBpZiAoIWluZGV4KSB7XG4gICAgICBsb2dnZXIud2FybihcImVuc3VyZS1za2lwcGVkXCIsIHtcbiAgICAgICAgcmVhc29uOiBcIm5vIGluZGV4IG5hbWUgKHNldCBgaW5kZXhgIG9yIGBlbnN1cmVPblNldHVwLmluZGV4YClcIixcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBkb2N1bWVudHMgPSBzcGVjLmRvY3VtZW50cyA/PyBbXTtcbiAgICAvLyBJbmZlciBhIG1hbmFnZWQtZGlyZWN0LWFjY2VzcyBzY2hlbWEgZnJvbSB0aGUgZmlyc3Qgc2VlZCByb3cgd2hlbiBub3QgZ2l2ZW4uXG4gICAgY29uc3Qgc2NoZW1hID1cbiAgICAgIHNwZWMuc2NoZW1hID8/XG4gICAgICAoZG9jdW1lbnRzLmxlbmd0aCA+IDAgJiYgIXNwZWMuc291cmNlVGFibGVcbiAgICAgICAgPyB0aGlzLmluZmVyU2NoZW1hKGRvY3VtZW50c1swXSwgc3BlYy5wcmltYXJ5S2V5ID8/IFwiaWRcIiwgc3BlYy50ZXh0Q29sdW1uID8/IFwidGV4dFwiKVxuICAgICAgICA6IHVuZGVmaW5lZCk7XG4gICAgdHJ5IHtcbiAgICAgIGxvZ2dlci5pbmZvKFwiZW5zdXJlLXN0YXJ0XCIsIHsgaW5kZXggfSk7XG4gICAgICBjb25zdCB7IGNsaWVudCB9ID0gZ2V0U2VhcmNoUnVudGltZSgpO1xuICAgICAgY29uc3QgaW5mbyA9IGF3YWl0IGNsaWVudC5wcm92aXNpb24oaW5kZXgsIHtcbiAgICAgICAgLi4uKHNwZWMuZW5kcG9pbnQgPyB7IGVuZHBvaW50OiBzcGVjLmVuZHBvaW50IH0gOiB7fSksXG4gICAgICAgIC4uLihzcGVjLnByaW1hcnlLZXkgPyB7IHByaW1hcnlLZXk6IHNwZWMucHJpbWFyeUtleSB9IDoge30pLFxuICAgICAgICAuLi4oc3BlYy50ZXh0Q29sdW1uID8geyBlbWJlZGRpbmdTb3VyY2VDb2x1bW46IHNwZWMudGV4dENvbHVtbiB9IDoge30pLFxuICAgICAgICAuLi4oc3BlYy5lbWJlZGRpbmdNb2RlbCA/IHsgZW1iZWRkaW5nTW9kZWw6IHNwZWMuZW1iZWRkaW5nTW9kZWwgfSA6IHt9KSxcbiAgICAgICAgLi4uKHNwZWMuc291cmNlVGFibGUgPyB7IHNvdXJjZVRhYmxlOiBzcGVjLnNvdXJjZVRhYmxlIH0gOiB7fSksXG4gICAgICAgIC4uLihzY2hlbWEgPyB7IHNjaGVtYSB9IDoge30pLFxuICAgICAgICAuLi4oc3BlYy50aW1lb3V0TXMgPyB7IHRpbWVvdXRNczogc3BlYy50aW1lb3V0TXMgfSA6IHt9KSxcbiAgICAgICAgLi4uKGRvY3VtZW50cy5sZW5ndGggPiAwID8geyBzZWVkOiBkb2N1bWVudHMgfSA6IHt9KSxcbiAgICAgIH0pO1xuICAgICAgbG9nZ2VyLmluZm8oXCJlbnN1cmUtcmVhZHlcIiwge1xuICAgICAgICBpbmRleDogaW5mby5uYW1lLFxuICAgICAgICByZWFkeTogaW5mby5yZWFkeSxcbiAgICAgICAgcm93Q291bnQ6IGluZm8ucm93Q291bnQgPz8gMCxcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGNhdXNlKSB7XG4gICAgICBsb2dnZXIud2FybihcImVuc3VyZS1mYWlsZWRcIiwgeyBpbmRleCwgbWVzc2FnZTogZXJyb3JVdGlsLmVycm9yTWVzc2FnZShjYXVzZSkgfSk7XG4gICAgfVxuICB9XG5cbiAgLyoqIEJ1aWxkIGEgVmVjdG9yIFNlYXJjaCBgc2NoZW1hX2pzb25gIG1hcCBmcm9tIGEgc2VlZCBkb2N1bWVudCdzIHZhbHVlIHR5cGVzLiAqL1xuICBwcml2YXRlIGluZmVyU2NoZW1hKFxuICAgIGRvYzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICAgcHJpbWFyeUtleTogc3RyaW5nLFxuICAgIHRleHRDb2x1bW46IHN0cmluZyxcbiAgKTogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB7XG4gICAgY29uc3Qgc2NoZW1hOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0geyBbcHJpbWFyeUtleV06IFwic3RyaW5nXCIsIFt0ZXh0Q29sdW1uXTogXCJzdHJpbmdcIiB9O1xuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGRvYykpIHtcbiAgICAgIGlmIChrZXkgaW4gc2NoZW1hKSBjb250aW51ZTtcbiAgICAgIHNjaGVtYVtrZXldID1cbiAgICAgICAgdHlwZW9mIHZhbHVlID09PSBcIm51bWJlclwiXG4gICAgICAgICAgPyBOdW1iZXIuaXNJbnRlZ2VyKHZhbHVlKVxuICAgICAgICAgICAgPyBcImludFwiXG4gICAgICAgICAgICA6IFwiZG91YmxlXCJcbiAgICAgICAgICA6IHR5cGVvZiB2YWx1ZSA9PT0gXCJib29sZWFuXCJcbiAgICAgICAgICAgID8gXCJib29sZWFuXCJcbiAgICAgICAgICAgIDogXCJzdHJpbmdcIjtcbiAgICB9XG4gICAgcmV0dXJuIHNjaGVtYTtcbiAgfVxuXG4gIC8qKiBEcm9wIHRoZSBzaGFyZWQgcnVudGltZSBzbyBhIHJlc3RhcnRlZCBhcHAgcmUtcmVzb2x2ZXMgY29uZmlnLiAqL1xuICBhc3luYyBzaHV0ZG93bigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXNldFNlYXJjaFJ1bnRpbWUoKTtcbiAgfVxuXG4gIG92ZXJyaWRlIGFib3J0QWN0aXZlT3BlcmF0aW9ucygpOiB2b2lkIHtcbiAgICBzdXBlci5hYm9ydEFjdGl2ZU9wZXJhdGlvbnMoKTtcbiAgICB2b2lkIHRoaXMuc2h1dGRvd24oKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNb3VudCB0aGUgc2VhcmNoIHJvdXRlcyB1bmRlciBgL2FwaS9zZWFyY2hgLiBFYWNoIGlzIHdyYXBwZWQgaW5cbiAgICogYGFzVXNlcihyZXEpYCBzbyB0aGUgcXVlcnkgcnVucyBhcyB0aGUgcmVxdWVzdGluZyB1c2VyIGFuZCB0aGUgaW5kZXgnc1xuICAgKiBVbml0eSBDYXRhbG9nIEFDTHMgYXBwbHkuIGBHRVQgL2luZGV4ZXNgIG5lZWRzIG5vIHVzZXIgc2NvcGUgLSBpdCBqdXN0XG4gICAqIGVjaG9lcyB0aGUgY29uZmlndXJlZCBjYXRhbG9ndWUuXG4gICAqL1xuICBvdmVycmlkZSBpbmplY3RSb3V0ZXMocm91dGVyOiBJQXBwUm91dGVyKTogdm9pZCB7XG4gICAgdGhpcy5yb3V0ZShyb3V0ZXIsIHtcbiAgICAgIG5hbWU6IFwic2VhcmNoXCIsXG4gICAgICBtZXRob2Q6IFwicG9zdFwiLFxuICAgICAgcGF0aDogU0VBUkNIX1JPVVRFLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcSwgcmVzKSA9PiB7XG4gICAgICAgIGF3YWl0IHRoaXMucmVzcG9uZChyZXMsIFwic2VhcmNoXCIsICgpID0+IHtcbiAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gc2VhcmNoQ29udHJhY3Quc2VhcmNoUmVxdWVzdFNjaGVtYS5wYXJzZShyZXEuYm9keSA/PyB7fSk7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuYXNVc2VyKHJlcSkucnVuU2VhcmNoKHJlcXVlc3QpO1xuICAgICAgICB9KTtcbiAgICAgIH0sXG4gICAgfSk7XG4gICAgdGhpcy5yb3V0ZShyb3V0ZXIsIHtcbiAgICAgIG5hbWU6IFwidW5pdmVyc2FsU2VhcmNoXCIsXG4gICAgICBtZXRob2Q6IFwicG9zdFwiLFxuICAgICAgcGF0aDogVU5JVkVSU0FMX1JPVVRFLFxuICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcSwgcmVzKSA9PiB7XG4gICAgICAgIGF3YWl0IHRoaXMucmVzcG9uZChyZXMsIFwidW5pdmVyc2FsU2VhcmNoXCIsICgpID0+IHtcbiAgICAgICAgICBjb25zdCByZXF1ZXN0ID0gc2VhcmNoQ29udHJhY3QudW5pdmVyc2FsU2VhcmNoUmVxdWVzdFNjaGVtYS5wYXJzZShyZXEuYm9keSA/PyB7fSk7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuYXNVc2VyKHJlcSkucnVuVW5pdmVyc2FsU2VhcmNoKHJlcXVlc3QpO1xuICAgICAgICB9KTtcbiAgICAgIH0sXG4gICAgfSk7XG4gICAgdGhpcy5yb3V0ZShyb3V0ZXIsIHtcbiAgICAgIG5hbWU6IFwiaW5kZXhlc1wiLFxuICAgICAgbWV0aG9kOiBcImdldFwiLFxuICAgICAgcGF0aDogSU5ERVhFU19ST1VURSxcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChfcmVxLCByZXMpID0+IHtcbiAgICAgICAgcmVzLmpzb24odGhpcy5jbGllbnRDb25maWcoKSk7XG4gICAgICB9LFxuICAgIH0pO1xuICAgIHRoaXMucm91dGUocm91dGVyLCB7XG4gICAgICBuYW1lOiBcImRvY3VtZW50c1wiLFxuICAgICAgbWV0aG9kOiBcInBvc3RcIixcbiAgICAgIHBhdGg6IERPQ1VNRU5UU19ST1VURSxcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChyZXEsIHJlcykgPT4ge1xuICAgICAgICBjb25zdCB7IGNvbmZpZyB9ID0gZ2V0U2VhcmNoUnVudGltZSgpO1xuICAgICAgICBpZiAoIWNvbmZpZy5hbGxvd1dyaXRlKSB7XG4gICAgICAgICAgcmVzLnN0YXR1cyg0MDMpLmpzb24oeyBlcnJvcjogXCJ0aGUgZG9jdW1lbnQgd3JpdGUgc3VyZmFjZSBpcyBkaXNhYmxlZFwiIH0pO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCB0aGlzLnJlc3BvbmQocmVzLCBcImFkZERvY3VtZW50c1wiLCAoKSA9PlxuICAgICAgICAgIHRoaXMuYXNVc2VyKHJlcSkucnVuQWRkRG9jdW1lbnRzKHJlcS5ib2R5ID8/IHt9KSxcbiAgICAgICAgKTtcbiAgICAgIH0sXG4gICAgfSk7XG4gICAgdGhpcy5yb3V0ZShyb3V0ZXIsIHtcbiAgICAgIG5hbWU6IFwiY3JlYXRlSW5kZXhcIixcbiAgICAgIG1ldGhvZDogXCJwb3N0XCIsXG4gICAgICBwYXRoOiBJTkRFWF9ST1VURSxcbiAgICAgIGhhbmRsZXI6IGFzeW5jIChyZXEsIHJlcykgPT4ge1xuICAgICAgICBjb25zdCB7IGNvbmZpZyB9ID0gZ2V0U2VhcmNoUnVudGltZSgpO1xuICAgICAgICBpZiAoIWNvbmZpZy5hbGxvd1dyaXRlKSB7XG4gICAgICAgICAgcmVzLnN0YXR1cyg0MDMpLmpzb24oeyBlcnJvcjogXCJ0aGUgaW5kZXggd3JpdGUgc3VyZmFjZSBpcyBkaXNhYmxlZFwiIH0pO1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCB0aGlzLnJlc3BvbmQocmVzLCBcImNyZWF0ZUluZGV4XCIsICgpID0+XG4gICAgICAgICAgdGhpcy5hc1VzZXIocmVxKS5ydW5DcmVhdGVJbmRleChyZXEuYm9keSA/PyB7fSksXG4gICAgICAgICk7XG4gICAgICB9LFxuICAgIH0pO1xuICAgIHRoaXMucm91dGUocm91dGVyLCB7XG4gICAgICBuYW1lOiBcInN5bmNJbmRleFwiLFxuICAgICAgbWV0aG9kOiBcInBvc3RcIixcbiAgICAgIHBhdGg6IElOREVYX1NZTkNfUk9VVEUsXG4gICAgICBoYW5kbGVyOiBhc3luYyAocmVxLCByZXMpID0+IHtcbiAgICAgICAgY29uc3QgeyBjb25maWcgfSA9IGdldFNlYXJjaFJ1bnRpbWUoKTtcbiAgICAgICAgaWYgKCFjb25maWcuYWxsb3dXcml0ZSkge1xuICAgICAgICAgIHJlcy5zdGF0dXMoNDAzKS5qc29uKHsgZXJyb3I6IFwidGhlIGluZGV4IHdyaXRlIHN1cmZhY2UgaXMgZGlzYWJsZWRcIiB9KTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgdGhpcy5yZXNwb25kKHJlcywgXCJzeW5jSW5kZXhcIiwgKCkgPT4gdGhpcy5hc1VzZXIocmVxKS5ydW5TeW5jSW5kZXgocmVxLmJvZHkgPz8ge30pKTtcbiAgICAgIH0sXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUnVuIGEgcm91dGUgYm9keSwgdHVybmluZyBhIGZhaWx1cmUgaW50byBhIEpTT04gZXJyb3IgcmVzcG9uc2UuXG4gICAqXG4gICAqIEFwcEtpdCdzIGByb3V0ZSgpYCByZWdpc3RlcnMgdGhlIGhhbmRsZXIgYXMtaXMsIHNvIGEgcmVqZWN0aW9uIGVzY2FwZXMgYXNcbiAgICogYW4gdW5oYW5kbGVkIHByb21pc2UgcmVqZWN0aW9uIGFuZCB0YWtlcyB0aGUgd2hvbGUgcHJvY2VzcyBkb3duIHJhdGhlciB0aGFuXG4gICAqIGZhaWxpbmcgdGhlIG9uZSByZXF1ZXN0LiBFdmVyeSByb3V0ZSBoZXJlIGdvZXMgdGhyb3VnaCB0aGlzLCBzbyBhIGJhZCBpbmRleFxuICAgKiBuYW1lIG9yIGEgVmVjdG9yIFNlYXJjaCBBUEkgZXJyb3IgcmV0dXJucyA1MDAgd2l0aCBhIG1lc3NhZ2UgaW5zdGVhZCBvZlxuICAgKiBraWxsaW5nIHRoZSBzZXJ2ZXIuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHJlc3BvbmQoXG4gICAgcmVzOiBQYXJhbWV0ZXJzPFBhcmFtZXRlcnM8SUFwcFJvdXRlcltcInBvc3RcIl0+WzFdPlsxXSxcbiAgICBvcGVyYXRpb246IHN0cmluZyxcbiAgICBydW46ICgpID0+IFByb21pc2U8dW5rbm93bj4sXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICByZXMuanNvbihhd2FpdCBydW4oKSk7XG4gICAgfSBjYXRjaCAoY2F1c2UpIHtcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPSBlcnJvclV0aWwuZXJyb3JNZXNzYWdlKGNhdXNlKTtcbiAgICAgIGxvZ2dlci53YXJuKFwicm91dGUtZmFpbGVkXCIsIHsgb3BlcmF0aW9uLCBtZXNzYWdlIH0pO1xuICAgICAgcmVzLnN0YXR1cyg1MDApLmpzb24oeyBlcnJvcjogbWVzc2FnZSB9KTtcbiAgICB9XG4gIH1cblxuICAvKiogU3VyZmFjZSB0aGUgaW5kZXggY2F0YWxvZ3VlICsgZGVmYXVsdHMgc28gYSBzZWFyY2ggYm94IG5lZWRzIG5vIHJvdW5kLXRyaXAuICovXG4gIG92ZXJyaWRlIGNsaWVudENvbmZpZygpOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gICAgY29uc3QgeyBjb25maWcgfSA9IGdldFNlYXJjaFJ1bnRpbWUoeyBjb25maWc6IHRoaXMuY29uZmlnIH0pO1xuICAgIGNvbnN0IHBheWxvYWQ6IFNlYXJjaENsaWVudENvbmZpZyA9IHtcbiAgICAgIGluZGV4ZXM6IGNvbmZpZy5pbmRleGVzLm1hcCgoaW5kZXgpID0+ICh7XG4gICAgICAgIG5hbWU6IGluZGV4Lm5hbWUsXG4gICAgICAgIGFsaWFzOiBpbmRleC5hbGlhcyxcbiAgICAgICAgLi4uKGluZGV4LnByaW1hcnlLZXkgPyB7IHByaW1hcnlLZXk6IGluZGV4LnByaW1hcnlLZXkgfSA6IHt9KSxcbiAgICAgICAgLi4uKGluZGV4LmNvbHVtbnMgPyB7IGNvbHVtbnM6IGluZGV4LmNvbHVtbnMgfSA6IHt9KSxcbiAgICAgICAgLi4uKGluZGV4Lm5hbWUgPT09IGNvbmZpZy5kZWZhdWx0SW5kZXggPyB7IGlzRGVmYXVsdDogdHJ1ZSB9IDoge30pLFxuICAgICAgfSkpLFxuICAgICAgLi4uKGNvbmZpZy5kZWZhdWx0SW5kZXggPyB7IGRlZmF1bHRJbmRleDogY29uZmlnLmRlZmF1bHRJbmRleCB9IDoge30pLFxuICAgICAgcGFnZVNpemU6IGNvbmZpZy5wYWdlU2l6ZSxcbiAgICAgIGJhc2VQYXRoOiB0aGlzLmJhc2VQYXRoLFxuICAgIH07XG4gICAgcmV0dXJuIHBheWxvYWQgYXMgdW5rbm93biBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgfVxuXG4gIC8qKiBBcHBLaXQgYFRvb2xQcm92aWRlcmA6IHRoZSB0b29sIGRlZmluaXRpb25zIG9mZmVyZWQgdG8gYW4gYWdlbnQuICovXG4gIGdldEFnZW50VG9vbHMoKTogQWdlbnRUb29sRGVmaW5pdGlvbltdIHtcbiAgICByZXR1cm4gdG9vbHNGcm9tUmVnaXN0cnkodGhpcy50b29scyk7XG4gIH1cblxuICAvKiogQXBwS2l0IGBUb29sUHJvdmlkZXJgOiBydW4gb25lIHRvb2wgY2FsbCwgdmFsaWRhdGluZyBpbnB1dCBhZ2FpbnN0IGl0cyBzY2hlbWEuICovXG4gIGFzeW5jIGV4ZWN1dGVBZ2VudFRvb2wobmFtZTogc3RyaW5nLCBhcmdzOiB1bmtub3duLCBzaWduYWw/OiBBYm9ydFNpZ25hbCk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIHJldHVybiBleGVjdXRlRnJvbVJlZ2lzdHJ5KHRoaXMudG9vbHMsIG5hbWUsIGFyZ3MsIHNpZ25hbCk7XG4gIH1cblxuICBvdmVycmlkZSBleHBvcnRzKCkge1xuICAgIHJldHVybiB7XG4gICAgICAvKiogU2VhcmNoIG9uZSBpbmRleCAoZGVmYXVsdCB3aGVuIG9taXR0ZWQpLiBSdW5zIGFzIHRoZSBjdXJyZW50IGNvbnRleHQgdXNlci4gKi9cbiAgICAgIHNlYXJjaDogKHJlcXVlc3Q6IHNlYXJjaENvbnRyYWN0LlNlYXJjaFJlcXVlc3QsIHNpZ25hbD86IEFib3J0U2lnbmFsKSA9PlxuICAgICAgICB0aGlzLnJ1blNlYXJjaChyZXF1ZXN0LCBzaWduYWwpLFxuICAgICAgLyoqIFNlYXJjaCBhY3Jvc3MgZXZlcnkgY29uZmlndXJlZCBpbmRleCBhbmQgbWVyZ2UgdGhlIGhpdHMuICovXG4gICAgICB1bml2ZXJzYWxTZWFyY2g6IChyZXF1ZXN0OiBzZWFyY2hDb250cmFjdC5Vbml2ZXJzYWxTZWFyY2hSZXF1ZXN0LCBzaWduYWw/OiBBYm9ydFNpZ25hbCkgPT5cbiAgICAgICAgdGhpcy5ydW5Vbml2ZXJzYWxTZWFyY2gocmVxdWVzdCwgc2lnbmFsKSxcbiAgICAgIC8qKiBBZGQgb3IgdXBkYXRlIGRvY3VtZW50cyBpbiBhIGRpcmVjdC1hY2Nlc3MgaW5kZXggKHRocm93cyB3aGVuIHdyaXRlcyBhcmUgZGlzYWJsZWQpLiAqL1xuICAgICAgYWRkRG9jdW1lbnRzOiAocmVxdWVzdDogeyBpbmRleD86IHN0cmluZzsgZG9jdW1lbnRzOiB1bmtub3duIH0sIHNpZ25hbD86IEFib3J0U2lnbmFsKSA9PlxuICAgICAgICB0aGlzLnJ1bkFkZERvY3VtZW50cyhyZXF1ZXN0LCBzaWduYWwpLFxuICAgICAgLyoqIENyZWF0ZSBhIFZlY3RvciBTZWFyY2ggaW5kZXggKHRocm93cyB3aGVuIHdyaXRlcyBhcmUgZGlzYWJsZWQpLiAqL1xuICAgICAgY3JlYXRlSW5kZXg6IChyZXF1ZXN0OiBzZWFyY2hDb250cmFjdC5DcmVhdGVJbmRleFJlcXVlc3QsIHNpZ25hbD86IEFib3J0U2lnbmFsKSA9PlxuICAgICAgICB0aGlzLnJ1bkNyZWF0ZUluZGV4KHJlcXVlc3QsIHNpZ25hbCksXG4gICAgICAvKiogU3luYyBhIERlbHRhIFN5bmMgaW5kZXggZnJvbSBpdHMgc291cmNlIHRhYmxlICh0aHJvd3Mgd2hlbiB3cml0ZXMgYXJlIGRpc2FibGVkKS4gKi9cbiAgICAgIHN5bmNJbmRleDogKHJlcXVlc3Q6IHNlYXJjaENvbnRyYWN0LlN5bmNJbmRleFJlcXVlc3QsIHNpZ25hbD86IEFib3J0U2lnbmFsKSA9PlxuICAgICAgICB0aGlzLnJ1blN5bmNJbmRleChyZXF1ZXN0LCBzaWduYWwpLFxuICAgIH07XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJ1blNlYXJjaChhcmdzOiB1bmtub3duLCBzaWduYWw/OiBBYm9ydFNpZ25hbCkge1xuICAgIGNvbnN0IHJlcXVlc3QgPSBzZWFyY2hDb250cmFjdC5zZWFyY2hSZXF1ZXN0U2NoZW1hLnBhcnNlKGFyZ3MpO1xuICAgIGNvbnN0IHsgY2xpZW50IH0gPSBnZXRTZWFyY2hSdW50aW1lKCk7XG4gICAgcmV0dXJuIGNsaWVudC5zZWFyY2gocmVxdWVzdC5xdWVyeSwge1xuICAgICAgLi4uKHJlcXVlc3QuaW5kZXggPyB7IGluZGV4OiByZXF1ZXN0LmluZGV4IH0gOiB7fSksXG4gICAgICAuLi4ocmVxdWVzdC5saW1pdCA/IHsgbGltaXQ6IHJlcXVlc3QubGltaXQgfSA6IHt9KSxcbiAgICAgIC4uLihyZXF1ZXN0Lm1vZGUgPyB7IG1vZGU6IHJlcXVlc3QubW9kZSB9IDoge30pLFxuICAgICAgLi4uKHJlcXVlc3QuY29sdW1ucyA/IHsgY29sdW1uczogcmVxdWVzdC5jb2x1bW5zIH0gOiB7fSksXG4gICAgICAuLi4ocmVxdWVzdC5maWx0ZXIgPyB7IGZpbHRlcjogcmVxdWVzdC5maWx0ZXIgfSA6IHt9KSxcbiAgICAgIC4uLihyZXF1ZXN0LnNjb3JlVGhyZXNob2xkICE9PSB1bmRlZmluZWQgPyB7IHNjb3JlVGhyZXNob2xkOiByZXF1ZXN0LnNjb3JlVGhyZXNob2xkIH0gOiB7fSksXG4gICAgICAuLi4oc2lnbmFsID8geyBzaWduYWwgfSA6IHt9KSxcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcnVuVW5pdmVyc2FsU2VhcmNoKGFyZ3M6IHVua25vd24sIHNpZ25hbD86IEFib3J0U2lnbmFsKSB7XG4gICAgY29uc3QgcmVxdWVzdCA9IHNlYXJjaENvbnRyYWN0LnVuaXZlcnNhbFNlYXJjaFJlcXVlc3RTY2hlbWEucGFyc2UoYXJncyk7XG4gICAgY29uc3QgeyBjbGllbnQgfSA9IGdldFNlYXJjaFJ1bnRpbWUoKTtcbiAgICByZXR1cm4gY2xpZW50LnVuaXZlcnNhbFNlYXJjaChyZXF1ZXN0LnF1ZXJ5LCB7XG4gICAgICAuLi4ocmVxdWVzdC5pbmRleGVzID8geyBpbmRleGVzOiByZXF1ZXN0LmluZGV4ZXMgfSA6IHt9KSxcbiAgICAgIC4uLihyZXF1ZXN0LmxpbWl0ID8geyBsaW1pdDogcmVxdWVzdC5saW1pdCB9IDoge30pLFxuICAgICAgLi4uKHJlcXVlc3QubW9kZSA/IHsgbW9kZTogcmVxdWVzdC5tb2RlIH0gOiB7fSksXG4gICAgICAuLi4oc2lnbmFsID8geyBzaWduYWwgfSA6IHt9KSxcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcnVuQWRkRG9jdW1lbnRzKGFyZ3M6IHVua25vd24sIHNpZ25hbD86IEFib3J0U2lnbmFsKSB7XG4gICAgY29uc3QgcmVjb3JkID0gKGFyZ3MgPz8ge30pIGFzIHsgaW5kZXg/OiBzdHJpbmc7IGRvY3VtZW50czogdW5rbm93biB9O1xuICAgIGNvbnN0IHsgY2xpZW50LCBjb25maWcgfSA9IGdldFNlYXJjaFJ1bnRpbWUoKTtcbiAgICBjb25zdCBkb2N1bWVudHMgPSB0b0RvY3VtZW50QXJyYXkocmVjb3JkLmRvY3VtZW50cyk7XG4gICAgY29uc3QgaW5kZXggPSByZWNvcmQuaW5kZXggPz8gY29uZmlnLmRlZmF1bHRJbmRleCA/PyBcIlwiO1xuICAgIHJldHVybiBjbGllbnQuYWRkRG9jdW1lbnRzKGluZGV4LCBkb2N1bWVudHMsIHNpZ25hbCk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHJ1bkNyZWF0ZUluZGV4KGFyZ3M6IHVua25vd24sIHNpZ25hbD86IEFib3J0U2lnbmFsKSB7XG4gICAgY29uc3QgcmVxdWVzdCA9IHNlYXJjaENvbnRyYWN0LmNyZWF0ZUluZGV4UmVxdWVzdFNjaGVtYS5wYXJzZShhcmdzKTtcbiAgICBjb25zdCB7IGNsaWVudCB9ID0gZ2V0U2VhcmNoUnVudGltZSgpO1xuICAgIHJldHVybiBjbGllbnQuY3JlYXRlSW5kZXgocmVxdWVzdC5uYW1lLCB0b0NyZWF0ZUluZGV4T3B0aW9ucyhyZXF1ZXN0LCBzaWduYWwpKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgcnVuU3luY0luZGV4KGFyZ3M6IHVua25vd24sIHNpZ25hbD86IEFib3J0U2lnbmFsKSB7XG4gICAgY29uc3QgcmVxdWVzdCA9IHNlYXJjaENvbnRyYWN0LnN5bmNJbmRleFJlcXVlc3RTY2hlbWEucGFyc2UoYXJncyk7XG4gICAgY29uc3QgeyBjbGllbnQsIGNvbmZpZyB9ID0gZ2V0U2VhcmNoUnVudGltZSgpO1xuICAgIGNvbnN0IGluZGV4ID0gcmVxdWVzdC5pbmRleCA/PyBjb25maWcuZGVmYXVsdEluZGV4ID8/IFwiXCI7XG4gICAgYXdhaXQgY2xpZW50LnN5bmNJbmRleChpbmRleCwgc2lnbmFsKTtcbiAgICByZXR1cm4geyBpbmRleCwgc3luY2VkOiB0cnVlIH07XG4gIH1cbn1cblxuLyoqXG4gKiBSZWdpc3RlciB0aGUgQUkgU2VhcmNoIHBsdWdpbiB3aXRoIEFwcEtpdC5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHNcbiAqIGltcG9ydCB7IGNyZWF0ZUFwcCwgc2VydmVyIH0gZnJvbSBcIkBkYXRhYnJpY2tzL2FwcGtpdFwiO1xuICogaW1wb3J0IHsgcGx1Z2luIGFzIHNlYXJjaFBsdWdpbiwgdG9vbCBhcyBzZWFyY2hUb29sTW9kdWxlIH0gZnJvbSBcIkBkYngtdG9vbHMvc2VhcmNoXCI7XG4gKiBpbXBvcnQgeyBhZ2VudHMsIHBsdWdpbiBhcyBtYXN0cmFQbHVnaW4gfSBmcm9tIFwiQGRieC10b29scy9hcHBraXQtbWFzdHJhXCI7XG4gKlxuICogY29uc3Qgc3VwcG9ydCA9IGFnZW50cy5jcmVhdGVBZ2VudCh7XG4gKiAgIGluc3RydWN0aW9uczogXCJBbnN3ZXIgZnJvbSB0aGUgZG9jczsgdXNlIGBzZWFyY2hgIHRvIGZpbmQgdGhlbS5cIixcbiAqICAgdG9vbHM6ICgpID0+ICh7IHNlYXJjaDogc2VhcmNoVG9vbE1vZHVsZS5zZWFyY2hUb29sKCkgfSksXG4gKiB9KTtcbiAqXG4gKiBhd2FpdCBjcmVhdGVBcHAoe1xuICogICBwbHVnaW5zOiBbXG4gKiAgICAgc2VydmVyKCksXG4gKiAgICAgc2VhcmNoUGx1Z2luLnNlYXJjaCh7IGluZGV4OiBcIm1haW4uc3VwcG9ydC5kb2NzXCIgfSksXG4gKiAgICAgbWFzdHJhUGx1Z2luLm1hc3RyYSh7IGFnZW50czogc3VwcG9ydCB9KSxcbiAqICAgXSxcbiAqIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBjb25zdCBzZWFyY2ggPSB0b1BsdWdpbihTZWFyY2hQbHVnaW4pO1xuIl19
|