@dbx-tools/search 0.9.5 → 0.9.6

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/src/plugin.ts CHANGED
@@ -21,22 +21,14 @@
21
21
  * @module
22
22
  */
23
23
 
24
- import { Plugin, toPlugin, type IAppRouter, type PluginManifest } from "@databricks/appkit";
25
- import {
26
- aiSearch,
27
- defineTool,
28
- executeFromRegistry,
29
- toolsFromRegistry,
30
- type AgentToolDefinition,
31
- type ToolProvider,
32
- type ToolRegistry,
33
- } from "@databricks/appkit/beta";
34
- import { plugin as appkitPlugin } from "@dbx-tools/appkit";
35
- import { error as sharedError, log, string } from "@dbx-tools/shared-core";
36
- import { search as sharedSearch, type SearchClientConfig } from "@dbx-tools/shared-search";
24
+ import { toPlugin, type IAppRouter, type PluginManifest } from "@databricks/appkit";
25
+ import { aiSearch, defineTool, type ToolRegistry } from "@databricks/appkit/beta";
26
+ import { pluginRegistry, ToolRegistryPlugin } from "@dbx-tools/appkit";
27
+ import { errorUtils, log, stringUtils } from "@dbx-tools/shared-core";
28
+ import { searchSchemas, type SearchClientConfig } from "@dbx-tools/shared-search";
37
29
  import { toSearchOptions, toUniversalSearchOptions } from "./_search-options.ts";
38
30
  import { SEARCH_CONFIG_SCHEMA, resolveSearchConfig, type SearchPluginConfig } from "./config.ts";
39
- import { toCreateIndexOptions } from "./index-tools.ts";
31
+ import { toCreateIndexOptions } from "./index-options.ts";
40
32
  import { nativeAiSearchBackend } from "./native.ts";
41
33
  import { toDocumentArray } from "./query.ts";
42
34
  import { createSearchRuntime, setSearchReadBackend, type SearchRuntime } from "./runtime.ts";
@@ -74,7 +66,7 @@ const INDEX_SYNC_ROUTE = "/index/sync";
74
66
  * ```ts
75
67
  * import { createApp, server } from "@databricks/appkit";
76
68
  * import { aiSearch } from "@databricks/appkit/beta";
77
- * import { plugin as searchPlugin } from "@dbx-tools/search";
69
+ * import { search } from "@dbx-tools/search";
78
70
  *
79
71
  * await createApp({
80
72
  * plugins: [
@@ -87,7 +79,7 @@ const INDEX_SYNC_ROUTE = "/index/sync";
87
79
  * },
88
80
  * },
89
81
  * }),
90
- * searchPlugin.search({
82
+ * search({
91
83
  * index: "main.support.docs",
92
84
  * indexes: [{ name: "main.support.docs", alias: "docs" }],
93
85
  * }),
@@ -95,7 +87,7 @@ const INDEX_SYNC_ROUTE = "/index/sync";
95
87
  * });
96
88
  * ```
97
89
  */
98
- export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProvider {
90
+ export class SearchPlugin extends ToolRegistryPlugin<SearchPluginConfig> {
99
91
  static manifest = {
100
92
  name: "search",
101
93
  displayName: "AI Search",
@@ -125,19 +117,19 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
125
117
  * autoInheritable: every tool runs under the caller's identity, so it must be
126
118
  * granted explicitly.
127
119
  */
128
- private get tools(): ToolRegistry {
120
+ protected get toolRegistry(): ToolRegistry {
129
121
  const { config, readBackend } = this.runtime;
130
122
  const registry: ToolRegistry = {
131
123
  search: defineTool({
132
124
  description: SEARCH_TOOL_DESCRIPTION,
133
- schema: sharedSearch.searchRequestSchema,
125
+ schema: searchSchemas.searchRequestSchema,
134
126
  annotations: { effect: "read", requiresUserContext: true },
135
127
  autoInheritable: false,
136
128
  execute: async (args, signal) => this.runSearch(args, signal),
137
129
  }),
138
130
  universal_search: defineTool({
139
131
  description: UNIVERSAL_SEARCH_TOOL_DESCRIPTION,
140
- schema: sharedSearch.universalSearchRequestSchema,
132
+ schema: searchSchemas.universalSearchRequestSchema,
141
133
  annotations: { effect: "read", requiresUserContext: true },
142
134
  autoInheritable: false,
143
135
  execute: async (args, signal) => this.runUniversalSearch(args, signal),
@@ -146,9 +138,9 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
146
138
  if (config.allowWrite) {
147
139
  registry.add_documents = defineTool({
148
140
  description: ADD_DOCUMENTS_TOOL_DESCRIPTION,
149
- schema: sharedSearch.searchRequestSchema
141
+ schema: searchSchemas.searchRequestSchema
150
142
  .pick({ index: true })
151
- .extend({ documents: sharedSearch.searchDocumentSchema.array() }),
143
+ .extend({ documents: searchSchemas.searchDocumentSchema.array() }),
152
144
  annotations: { effect: "write", requiresUserContext: true },
153
145
  autoInheritable: false,
154
146
  execute: async (args, signal) => this.runAddDocuments(args, signal),
@@ -156,14 +148,14 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
156
148
  if (readBackend?.supportsLifecycle) {
157
149
  registry.create_index = defineTool({
158
150
  description: CREATE_INDEX_TOOL_DESCRIPTION,
159
- schema: sharedSearch.createIndexRequestSchema,
151
+ schema: searchSchemas.createIndexRequestSchema,
160
152
  annotations: { effect: "write", requiresUserContext: true },
161
153
  autoInheritable: false,
162
154
  execute: async (args, signal) => this.runCreateIndex(args, signal),
163
155
  });
164
156
  registry.sync_index = defineTool({
165
157
  description: SYNC_INDEX_TOOL_DESCRIPTION,
166
- schema: sharedSearch.syncIndexRequestSchema,
158
+ schema: searchSchemas.syncIndexRequestSchema,
167
159
  annotations: { effect: "write", requiresUserContext: true },
168
160
  autoInheritable: false,
169
161
  execute: async (args, signal) => this.runSyncIndex(args, signal),
@@ -199,7 +191,7 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
199
191
 
200
192
  /** Resolve the registered AppKit-compatible AI Search provider. */
201
193
  private resolveProviderBackend(): ReturnType<typeof nativeAiSearchBackend> {
202
- const provider = appkitPlugin.require(this.context, aiSearch, this).exports();
194
+ const provider = pluginRegistry.require(this.context, aiSearch, this).exports();
203
195
  return nativeAiSearchBackend(provider, resolveSearchConfig(this.config));
204
196
  }
205
197
 
@@ -212,7 +204,7 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
212
204
  private async runEnsureOnSetup(config: SearchRuntime["config"]): Promise<void> {
213
205
  const spec = config.ensureOnSetup;
214
206
  if (!spec) return;
215
- const index = string.trimToNull(spec.index) ?? config.defaultIndex;
207
+ const index = stringUtils.trimToNull(spec.index) ?? config.defaultIndex;
216
208
  if (!index) {
217
209
  logger.warn("ensure-skipped", {
218
210
  reason: "no index name (set `index` or `ensureOnSetup.index`)",
@@ -245,7 +237,7 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
245
237
  rowCount: info.rowCount ?? 0,
246
238
  });
247
239
  } catch (cause) {
248
- logger.warn("ensure-failed", { index, message: sharedError.errorMessage(cause) });
240
+ logger.warn("ensure-failed", { index, message: errorUtils.errorMessage(cause) });
249
241
  }
250
242
  }
251
243
 
@@ -290,7 +282,7 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
290
282
  path: UNIVERSAL_ROUTE,
291
283
  handler: async (req, res) => {
292
284
  await this.respond(res, "universalSearch", () => {
293
- const request = sharedSearch.universalSearchRequestSchema.parse(req.body ?? {});
285
+ const request = searchSchemas.universalSearchRequestSchema.parse(req.body ?? {});
294
286
  return this.asUser(req).runUniversalSearch(request);
295
287
  });
296
288
  },
@@ -365,7 +357,7 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
365
357
  try {
366
358
  res.json(await run());
367
359
  } catch (cause) {
368
- const message = sharedError.errorMessage(cause);
360
+ const message = errorUtils.errorMessage(cause);
369
361
  logger.warn("route-failed", { operation, message });
370
362
  res.status(500).json({ error: message });
371
363
  }
@@ -389,44 +381,34 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
389
381
  return payload as unknown as Record<string, unknown>;
390
382
  }
391
383
 
392
- /** AppKit `ToolProvider`: the tool definitions offered to an agent. */
393
- getAgentTools(): AgentToolDefinition[] {
394
- return toolsFromRegistry(this.tools);
395
- }
396
-
397
- /** AppKit `ToolProvider`: run one tool call, validating input against its schema. */
398
- async executeAgentTool(name: string, args: unknown, signal?: AbortSignal): Promise<unknown> {
399
- return executeFromRegistry(this.tools, name, args, signal);
400
- }
401
-
402
384
  override exports() {
403
385
  return {
404
386
  /** Search one index (default when omitted). Runs as the current context user. */
405
- search: (request: sharedSearch.SearchRequest, signal?: AbortSignal) =>
387
+ search: (request: searchSchemas.SearchRequest, signal?: AbortSignal) =>
406
388
  this.runSearch(request, signal),
407
389
  /** Search across every configured index and merge the hits. */
408
- universalSearch: (request: sharedSearch.UniversalSearchRequest, signal?: AbortSignal) =>
390
+ universalSearch: (request: searchSchemas.UniversalSearchRequest, signal?: AbortSignal) =>
409
391
  this.runUniversalSearch(request, signal),
410
392
  /** Add or update documents in a direct-access index (throws when writes are disabled). */
411
393
  addDocuments: (request: { index?: string; documents: unknown }, signal?: AbortSignal) =>
412
394
  this.runAddDocuments(request, signal),
413
395
  /** Create a Vector Search index (throws when writes are disabled). */
414
- createIndex: (request: sharedSearch.CreateIndexRequest, signal?: AbortSignal) =>
396
+ createIndex: (request: searchSchemas.CreateIndexRequest, signal?: AbortSignal) =>
415
397
  this.runCreateIndex(request, signal),
416
398
  /** Sync a Delta Sync index from its source table (throws when writes are disabled). */
417
- syncIndex: (request: sharedSearch.SyncIndexRequest, signal?: AbortSignal) =>
399
+ syncIndex: (request: searchSchemas.SyncIndexRequest, signal?: AbortSignal) =>
418
400
  this.runSyncIndex(request, signal),
419
401
  };
420
402
  }
421
403
 
422
404
  private async runSearch(args: unknown, signal?: AbortSignal) {
423
- const request = sharedSearch.searchRequestSchema.parse(args);
405
+ const request = searchSchemas.searchRequestSchema.parse(args);
424
406
  const { client } = this.runtime;
425
407
  return client.search(request.query, toSearchOptions(request, signal));
426
408
  }
427
409
 
428
410
  private async runUniversalSearch(args: unknown, signal?: AbortSignal) {
429
- const request = sharedSearch.universalSearchRequestSchema.parse(args);
411
+ const request = searchSchemas.universalSearchRequestSchema.parse(args);
430
412
  const { client } = this.runtime;
431
413
  return client.universalSearch(request.query, toUniversalSearchOptions(request, signal));
432
414
  }
@@ -440,13 +422,13 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
440
422
  }
441
423
 
442
424
  private async runCreateIndex(args: unknown, signal?: AbortSignal) {
443
- const request = sharedSearch.createIndexRequestSchema.parse(args);
425
+ const request = searchSchemas.createIndexRequestSchema.parse(args);
444
426
  const { client } = this.runtime;
445
427
  return client.createIndex(request.name, toCreateIndexOptions(request, signal));
446
428
  }
447
429
 
448
430
  private async runSyncIndex(args: unknown, signal?: AbortSignal) {
449
- const request = sharedSearch.syncIndexRequestSchema.parse(args);
431
+ const request = searchSchemas.syncIndexRequestSchema.parse(args);
450
432
  const { client, config } = this.runtime;
451
433
  const index = request.index ?? config.defaultIndex ?? "";
452
434
  await client.syncIndex(index, signal);
@@ -460,8 +442,8 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
460
442
  * @example
461
443
  * ```ts
462
444
  * import { createApp, server } from "@databricks/appkit";
463
- * import { plugin as searchPlugin } from "@dbx-tools/search";
464
- * import { agents, plugin as mastraPlugin } from "@dbx-tools/appkit-mastra";
445
+ * import { search } from "@dbx-tools/search";
446
+ * import { agents, mastra } from "@dbx-tools/appkit-mastra";
465
447
  *
466
448
  * const support = agents.createAgent({
467
449
  * instructions: "Answer from the docs; use `search` to find them.",
@@ -473,8 +455,8 @@ export class SearchPlugin extends Plugin<SearchPluginConfig> implements ToolProv
473
455
  * await createApp({
474
456
  * plugins: [
475
457
  * server(),
476
- * searchPlugin.search({ index: "main.support.docs" }),
477
- * mastraPlugin.mastra({ agents: support }),
458
+ * search({ index: "main.support.docs" }),
459
+ * mastra({ agents: support }),
478
460
  * ],
479
461
  * });
480
462
  * ```
package/src/runtime.ts CHANGED
@@ -43,21 +43,3 @@ export function setSearchReadBackend(runtime: SearchRuntime, readBackend: Search
43
43
  runtime.readBackend = readBackend;
44
44
  runtime.client = createSearchClient(runtime.config, undefined, readBackend);
45
45
  }
46
-
47
- /**
48
- * Build an isolated standalone runtime.
49
- *
50
- * @deprecated Use {@link createSearchRuntime}. This compatibility helper
51
- * returns a new runtime on every call and never reads or updates plugin state.
52
- */
53
- export function getSearchRuntime(options?: SearchRuntimeOptions): SearchRuntime {
54
- return createSearchRuntime(options);
55
- }
56
-
57
- /**
58
- * @deprecated Plugin runtimes are instance-owned and need no global reset.
59
- * This compatibility helper is intentionally a no-op.
60
- */
61
- export function resetSearchRuntime(): void {
62
- return;
63
- }
package/src/schema.ts CHANGED
@@ -7,11 +7,11 @@
7
7
  * @module
8
8
  */
9
9
 
10
- import { string } from "@dbx-tools/shared-core";
11
- import { search } from "@dbx-tools/shared-search";
10
+ import { stringUtils } from "@dbx-tools/shared-core";
11
+ import { searchSchemas } from "@dbx-tools/shared-search";
12
12
 
13
13
  /** Description the model reads for the `search` tool. */
14
- export const SEARCH_TOOL_DESCRIPTION = string.toDescription(`
14
+ export const SEARCH_TOOL_DESCRIPTION = stringUtils.toDescription(`
15
15
  Search a Databricks AI Search (Vector Search) index for the documents most
16
16
  relevant to a query. Pass a natural-language question or keywords; hybrid
17
17
  matching combines semantic similarity with keyword ranking, so exact terms
@@ -22,7 +22,7 @@ export const SEARCH_TOOL_DESCRIPTION = string.toDescription(`
22
22
  `);
23
23
 
24
24
  /** Description the model reads for the `universal_search` tool. */
25
- export const UNIVERSAL_SEARCH_TOOL_DESCRIPTION = string.toDescription(`
25
+ export const UNIVERSAL_SEARCH_TOOL_DESCRIPTION = stringUtils.toDescription(`
26
26
  Search across every configured AI Search index at once and return the best
27
27
  matches from all of them, merged and ranked. Use it when the right index
28
28
  isn't known in advance or when an answer may live in any of several
@@ -30,7 +30,7 @@ export const UNIVERSAL_SEARCH_TOOL_DESCRIPTION = string.toDescription(`
30
30
  `);
31
31
 
32
32
  /** Description the model reads for the `add_documents` tool. */
33
- export const ADD_DOCUMENTS_TOOL_DESCRIPTION = string.toDescription(`
33
+ export const ADD_DOCUMENTS_TOOL_DESCRIPTION = stringUtils.toDescription(`
34
34
  Add or update documents in a direct-access AI Search index. Pass an array of
35
35
  documents as JSON objects; each MUST include the index's primary-key column.
36
36
  Only available when the app enables the write surface. Use it to index new
@@ -38,7 +38,7 @@ export const ADD_DOCUMENTS_TOOL_DESCRIPTION = string.toDescription(`
38
38
  `);
39
39
 
40
40
  /** Description the model reads for the `create_index` tool. */
41
- export const CREATE_INDEX_TOOL_DESCRIPTION = string.toDescription(`
41
+ export const CREATE_INDEX_TOOL_DESCRIPTION = stringUtils.toDescription(`
42
42
  Create a Databricks AI Search (Vector Search) index. For the common case pass
43
43
  a Delta source table (catalog.schema.table): Databricks computes embeddings
44
44
  from its text column and keeps the index synced. To create a direct-access
@@ -50,26 +50,26 @@ export const CREATE_INDEX_TOOL_DESCRIPTION = string.toDescription(`
50
50
  `);
51
51
 
52
52
  /** Description the model reads for the `sync_index` tool. */
53
- export const SYNC_INDEX_TOOL_DESCRIPTION = string.toDescription(`
53
+ export const SYNC_INDEX_TOOL_DESCRIPTION = stringUtils.toDescription(`
54
54
  Refresh a Delta Sync AI Search index from its source table so newly added or
55
55
  changed rows become searchable. Optionally name the index (defaults to the
56
56
  app's default index). Only available when the app enables the write surface.
57
57
  `);
58
58
 
59
59
  /** Schema for the `search` tool input (the shared request schema). */
60
- export const searchToolSchema = search.searchRequestSchema;
60
+ export const searchToolSchema = searchSchemas.searchRequestSchema;
61
61
 
62
62
  /** Schema for the `universal_search` tool input. */
63
- export const universalSearchToolSchema = search.universalSearchRequestSchema;
63
+ export const universalSearchToolSchema = searchSchemas.universalSearchRequestSchema;
64
64
 
65
65
  /** Schema for the `search` / `universal_search` tool output. */
66
- export const searchResultSchema = search.searchResultSchema;
66
+ export const searchResultSchema = searchSchemas.searchResultSchema;
67
67
 
68
68
  /** Schema for the `create_index` tool input. */
69
- export const createIndexToolSchema = search.createIndexRequestSchema;
69
+ export const createIndexToolSchema = searchSchemas.createIndexRequestSchema;
70
70
 
71
71
  /** Schema for the `create_index` tool output (a resolved index definition). */
72
- export const indexInfoSchema = search.indexInfoSchema;
72
+ export const indexInfoSchema = searchSchemas.indexInfoSchema;
73
73
 
74
74
  /** Schema for the `sync_index` tool input. */
75
- export const syncIndexToolSchema = search.syncIndexRequestSchema;
75
+ export const syncIndexToolSchema = searchSchemas.syncIndexRequestSchema;
package/src/tool.ts CHANGED
@@ -15,13 +15,13 @@
15
15
  * @module
16
16
  */
17
17
 
18
- import { search as sharedSearch, type UpsertResult } from "@dbx-tools/shared-search";
18
+ import { searchSchemas, type UpsertResult } from "@dbx-tools/shared-search";
19
19
  import { createTool } from "@mastra/core/tools";
20
20
  import { z } from "zod";
21
21
  import { toSearchOptions, toUniversalSearchOptions } from "./_search-options.ts";
22
22
  import type { SearchReadBackend } from "./client.ts";
23
23
  import type { SearchPluginConfig } from "./config.ts";
24
- import { toCreateIndexOptions } from "./index-tools.ts";
24
+ import { toCreateIndexOptions } from "./index-options.ts";
25
25
  import { toDocumentArray } from "./query.ts";
26
26
  import { createSearchRuntime, type SearchRuntime } from "./runtime.ts";
27
27
  import {
@@ -117,16 +117,16 @@ export function universalSearchTool(options: SearchToolOptions = {}) {
117
117
  */
118
118
  export function addDocumentsTool(options: SearchToolOptions = {}) {
119
119
  const runtime = toolRuntime(options);
120
- const inputSchema = sharedSearch.searchDocumentSchema
120
+ const inputSchema = searchSchemas.searchDocumentSchema
121
121
  .array()
122
122
  .describe("Documents to add or update. Each must include the index primary key.");
123
123
  return createTool({
124
124
  id: options.id ?? "add_documents",
125
125
  description: ADD_DOCUMENTS_TOOL_DESCRIPTION,
126
- inputSchema: sharedSearch.searchRequestSchema
126
+ inputSchema: searchSchemas.searchRequestSchema
127
127
  .pick({ index: true })
128
128
  .extend({ documents: inputSchema }),
129
- outputSchema: sharedSearch.upsertResultSchema,
129
+ outputSchema: searchSchemas.upsertResultSchema,
130
130
  execute: async (input, context): Promise<UpsertResult> => {
131
131
  const { client, config } = runtime;
132
132
  const record = input as { index?: string; documents: unknown };
@@ -1,34 +0,0 @@
1
- /**
2
- * Shared translation from the browser-safe {@link CreateIndexRequest} wire
3
- * shape onto the client's {@link CreateIndexOptions}. Lives in its own module
4
- * because both the Mastra `create_index` tool (`tool.ts`) and the AppKit tool
5
- * provider (`plugin.ts`) build the same options object, and the mapping (which
6
- * optional fields to spread, folding in the abort signal) is easy to let drift
7
- * between the two.
8
- *
9
- * @module
10
- */
11
- /**
12
- * Map a validated {@link CreateIndexRequest} onto {@link CreateIndexOptions},
13
- * spreading only the fields that were supplied so the client's own defaults
14
- * (endpoint, embedding model, primary key, text column) still apply, and
15
- * folding in the caller's abort signal.
16
- */
17
- export function toCreateIndexOptions(request, signal) {
18
- return {
19
- ...(request.sourceTable ? { sourceTable: request.sourceTable } : {}),
20
- ...(request.primaryKey ? { primaryKey: request.primaryKey } : {}),
21
- ...(request.embeddingSourceColumn
22
- ? { embeddingSourceColumn: request.embeddingSourceColumn }
23
- : {}),
24
- ...(request.embeddingModel ? { embeddingModel: request.embeddingModel } : {}),
25
- ...(request.endpoint ? { endpoint: request.endpoint } : {}),
26
- ...(request.embeddingDimension !== undefined
27
- ? { embeddingDimension: request.embeddingDimension }
28
- : {}),
29
- ...(request.pipelineType ? { pipelineType: request.pipelineType } : {}),
30
- ...(request.columnsToSync ? { columnsToSync: request.columnsToSync } : {}),
31
- ...(signal ? { signal } : {}),
32
- };
33
- }
34
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXgtdG9vbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgtdG9vbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7OztHQVNHO0FBS0g7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsb0JBQW9CLENBQ2xDLE9BQTJCLEVBQzNCLE1BQW9CO0lBRXBCLE9BQU87UUFDTCxHQUFHLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxXQUFXLEVBQUUsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDcEUsR0FBRyxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLEVBQUUsVUFBVSxFQUFFLE9BQU8sQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ2pFLEdBQUcsQ0FBQyxPQUFPLENBQUMscUJBQXFCO1lBQy9CLENBQUMsQ0FBQyxFQUFFLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxxQkFBcUIsRUFBRTtZQUMxRCxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ1AsR0FBRyxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLEVBQUUsY0FBYyxFQUFFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzdFLEdBQUcsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUMzRCxHQUFHLENBQUMsT0FBTyxDQUFDLGtCQUFrQixLQUFLLFNBQVM7WUFDMUMsQ0FBQyxDQUFDLEVBQUUsa0JBQWtCLEVBQUUsT0FBTyxDQUFDLGtCQUFrQixFQUFFO1lBQ3BELENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDUCxHQUFHLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsRUFBRSxZQUFZLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDdkUsR0FBRyxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLEVBQUUsYUFBYSxFQUFFLE9BQU8sQ0FBQyxhQUFhLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzFFLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztLQUM5QixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU2hhcmVkIHRyYW5zbGF0aW9uIGZyb20gdGhlIGJyb3dzZXItc2FmZSB7QGxpbmsgQ3JlYXRlSW5kZXhSZXF1ZXN0fSB3aXJlXG4gKiBzaGFwZSBvbnRvIHRoZSBjbGllbnQncyB7QGxpbmsgQ3JlYXRlSW5kZXhPcHRpb25zfS4gTGl2ZXMgaW4gaXRzIG93biBtb2R1bGVcbiAqIGJlY2F1c2UgYm90aCB0aGUgTWFzdHJhIGBjcmVhdGVfaW5kZXhgIHRvb2wgKGB0b29sLnRzYCkgYW5kIHRoZSBBcHBLaXQgdG9vbFxuICogcHJvdmlkZXIgKGBwbHVnaW4udHNgKSBidWlsZCB0aGUgc2FtZSBvcHRpb25zIG9iamVjdCwgYW5kIHRoZSBtYXBwaW5nICh3aGljaFxuICogb3B0aW9uYWwgZmllbGRzIHRvIHNwcmVhZCwgZm9sZGluZyBpbiB0aGUgYWJvcnQgc2lnbmFsKSBpcyBlYXN5IHRvIGxldCBkcmlmdFxuICogYmV0d2VlbiB0aGUgdHdvLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgdHlwZSB7IENyZWF0ZUluZGV4UmVxdWVzdCB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1zZWFyY2hcIjtcbmltcG9ydCB0eXBlIHsgQ3JlYXRlSW5kZXhPcHRpb25zIH0gZnJvbSBcIi4vY2xpZW50LnRzXCI7XG5cbi8qKlxuICogTWFwIGEgdmFsaWRhdGVkIHtAbGluayBDcmVhdGVJbmRleFJlcXVlc3R9IG9udG8ge0BsaW5rIENyZWF0ZUluZGV4T3B0aW9uc30sXG4gKiBzcHJlYWRpbmcgb25seSB0aGUgZmllbGRzIHRoYXQgd2VyZSBzdXBwbGllZCBzbyB0aGUgY2xpZW50J3Mgb3duIGRlZmF1bHRzXG4gKiAoZW5kcG9pbnQsIGVtYmVkZGluZyBtb2RlbCwgcHJpbWFyeSBrZXksIHRleHQgY29sdW1uKSBzdGlsbCBhcHBseSwgYW5kXG4gKiBmb2xkaW5nIGluIHRoZSBjYWxsZXIncyBhYm9ydCBzaWduYWwuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB0b0NyZWF0ZUluZGV4T3B0aW9ucyhcbiAgcmVxdWVzdDogQ3JlYXRlSW5kZXhSZXF1ZXN0LFxuICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbik6IENyZWF0ZUluZGV4T3B0aW9ucyB7XG4gIHJldHVybiB7XG4gICAgLi4uKHJlcXVlc3Quc291cmNlVGFibGUgPyB7IHNvdXJjZVRhYmxlOiByZXF1ZXN0LnNvdXJjZVRhYmxlIH0gOiB7fSksXG4gICAgLi4uKHJlcXVlc3QucHJpbWFyeUtleSA/IHsgcHJpbWFyeUtleTogcmVxdWVzdC5wcmltYXJ5S2V5IH0gOiB7fSksXG4gICAgLi4uKHJlcXVlc3QuZW1iZWRkaW5nU291cmNlQ29sdW1uXG4gICAgICA/IHsgZW1iZWRkaW5nU291cmNlQ29sdW1uOiByZXF1ZXN0LmVtYmVkZGluZ1NvdXJjZUNvbHVtbiB9XG4gICAgICA6IHt9KSxcbiAgICAuLi4ocmVxdWVzdC5lbWJlZGRpbmdNb2RlbCA/IHsgZW1iZWRkaW5nTW9kZWw6IHJlcXVlc3QuZW1iZWRkaW5nTW9kZWwgfSA6IHt9KSxcbiAgICAuLi4ocmVxdWVzdC5lbmRwb2ludCA/IHsgZW5kcG9pbnQ6IHJlcXVlc3QuZW5kcG9pbnQgfSA6IHt9KSxcbiAgICAuLi4ocmVxdWVzdC5lbWJlZGRpbmdEaW1lbnNpb24gIT09IHVuZGVmaW5lZFxuICAgICAgPyB7IGVtYmVkZGluZ0RpbWVuc2lvbjogcmVxdWVzdC5lbWJlZGRpbmdEaW1lbnNpb24gfVxuICAgICAgOiB7fSksXG4gICAgLi4uKHJlcXVlc3QucGlwZWxpbmVUeXBlID8geyBwaXBlbGluZVR5cGU6IHJlcXVlc3QucGlwZWxpbmVUeXBlIH0gOiB7fSksXG4gICAgLi4uKHJlcXVlc3QuY29sdW1uc1RvU3luYyA/IHsgY29sdW1uc1RvU3luYzogcmVxdWVzdC5jb2x1bW5zVG9TeW5jIH0gOiB7fSksXG4gICAgLi4uKHNpZ25hbCA/IHsgc2lnbmFsIH0gOiB7fSksXG4gIH07XG59XG4iXX0=
File without changes