@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 ADDED
@@ -0,0 +1,297 @@
1
+ # @dbx-tools/search
2
+
3
+ A Meilisearch-style shortcut over Databricks AI Search (Vector Search): a search
4
+ client, agent tools, and an AppKit plugin.
5
+
6
+ Import this package when an AppKit or Mastra backend needs to search a
7
+ [Databricks AI Search](https://docs.databricks.com/aws/en/ai-search/ai-search)
8
+ index - for autocomplete, docs lookup, RAG retrieval, or a universal search box
9
+ across several indexes. It wraps the low-level SDK
10
+ (`vectorSearchIndexes.queryIndex({ index_name, columns, query_text, query_type,
11
+ num_results, filters_json })` and its columnar response) behind an ergonomic
12
+ client, and ships the agent tools, HTTP routes, and boot config a search UI
13
+ needs - so search is one plugin and, in the simple case, zero config.
14
+
15
+ **Key features:**
16
+
17
+ - A small, Meilisearch-shaped client: `client.index("catalog.schema.docs")`,
18
+ `index.search(query)`, `index.autocomplete(prefix)`, `index.addDocuments(...)`,
19
+ and `client.universalSearch(query)` to fan a query across many indexes and
20
+ merge the hits.
21
+ - Hybrid matching by default (semantic similarity fused with BM25 keyword
22
+ ranking), with `vector` and `keyword` modes when you want one or the other.
23
+ - Agent tools for both Mastra (`searchTool()` etc.) and AppKit agents (through
24
+ the plugin's `ToolProvider`): `search` and `universal_search` reads, plus the
25
+ opt-in write tools `add_documents`, `create_index`, and `sync_index`.
26
+ - HTTP routes under `/api/search` a browser search box calls directly:
27
+ `POST /` (search), `POST /universal` (federated), `GET /indexes` (catalogue),
28
+ and, when writes are enabled, `POST /documents` (upsert),
29
+ `POST /index` (create), and `POST /index/sync` (refresh).
30
+ - A `clientConfig()` payload so a UI knows the indexes, default, and page size at
31
+ boot with no round-trip (read it with `usePluginClientConfig("search")`).
32
+ - Sensible-default config that infers almost everything: name a default index
33
+ (or set `DATABRICKS_VECTOR_SEARCH_INDEX`) and the columns, page size, mode,
34
+ aliases, and route path all have defaults you can override when you need to go
35
+ deeper.
36
+ - OBO throughout: routes wrap in `asUser(req)` and the client resolves the
37
+ execution-context workspace client, so search runs as the requesting user and
38
+ Unity Catalog ACLs apply.
39
+ - Filters as plain `{ column: value }` (or `{ column: { ">=": n } }`) compiled to
40
+ the index `filters_json` for you; the columnar response is unpacked into
41
+ `{ id, score, fields }` hits.
42
+ - Embedding-model resolution for index creation reuses
43
+ [`@dbx-tools/model`](../model): a loose name fuzzy-matches the live catalogue,
44
+ or the best embedding endpoint is chosen automatically.
45
+ - Index lifecycle without the ceremony: `createIndex` / `ensureIndex` (Delta
46
+ Sync from a source table, self-managed direct-access with a dimension, or a
47
+ managed direct-access index that embeds a text column - no Delta table or
48
+ warehouse), `provision` (ensure + seed in one idempotent call), `syncIndex`,
49
+ `deleteIndex`, `listIndexes`, and `ensureEndpoint` - each inferring the
50
+ endpoint, embedding model, primary key, and columns from sensible defaults.
51
+ - Wire up a real index on boot with `ensureOnSetup`: the plugin provisions the
52
+ endpoint + index and seeds documents in the background using the app's SDK
53
+ auth (env or `DATABRICKS_CONFIG_PROFILE`), so a fresh deployment is searchable
54
+ with no manual setup.
55
+ - A **Lakebase full-text fallback**: when no Vector Search endpoint is
56
+ configured but the AppKit `lakebase` plugin is registered, search transparently
57
+ runs on a Postgres `tsvector` index instead - same `provision` / `search` /
58
+ `add_documents` calls, same `{ id, score, fields }` hits, so tools, routes, and
59
+ the UI can't tell which backend answered. No endpoint, no embeddings, no Delta
60
+ table - just a table the plugin creates and seeds on boot.
61
+
62
+ ## Why Use This Over Native AppKit
63
+
64
+ AppKit exposes Vector Search as a resource type and a low-level serving surface,
65
+ but nothing that makes a search box or an agent tool a one-liner. Use this
66
+ package when you want search to be a drop-in: the friendly client hides the
67
+ verbose `queryIndex` request and columnar response, the plugin gives agents a
68
+ `search` tool and a browser a `POST /api/search` route in one registration,
69
+ universal search fans across indexes, and the config infers everything from a
70
+ single index name. Reach for the raw SDK when you need index lifecycle
71
+ operations this package does not wrap.
72
+
73
+ ## Quick Start
74
+
75
+ ```ts
76
+ import { createApp, server } from "@databricks/appkit";
77
+ import { plugin as searchPlugin, tool as searchToolModule } from "@dbx-tools/search";
78
+ import { agents, plugin as mastraPlugin } from "@dbx-tools/appkit-mastra";
79
+
80
+ const support = agents.createAgent({
81
+ instructions: "Answer from the docs; use `search` to find them.",
82
+ tools: () => ({ search: searchToolModule.searchTool() }),
83
+ });
84
+
85
+ await createApp({
86
+ plugins: [
87
+ server(),
88
+ // zero-config: reads DATABRICKS_VECTOR_SEARCH_INDEX / SEARCH_INDEX
89
+ searchPlugin.search(),
90
+ mastraPlugin.mastra({ agents: support }),
91
+ ],
92
+ });
93
+ ```
94
+
95
+ Going deeper:
96
+
97
+ ```ts
98
+ searchPlugin.search({
99
+ index: "main.support.docs",
100
+ indexes: [
101
+ "main.support.docs",
102
+ { name: "main.catalog.products", alias: "products", columns: ["name", "sku", "price"] },
103
+ ],
104
+ columns: ["title", "url", "body"],
105
+ mode: "hybrid",
106
+ pageSize: 10,
107
+ allowWrite: false,
108
+ });
109
+ ```
110
+
111
+ ## Use The Client Directly
112
+
113
+ ```ts
114
+ import { createSearchClient } from "@dbx-tools/search";
115
+
116
+ const client = createSearchClient();
117
+ const docs = client.index("main.support.docs");
118
+
119
+ const { hits } = await docs.search("reset my password", { limit: 5 });
120
+ const suggestions = await docs.autocomplete("rese");
121
+ const everywhere = await client.universalSearch("invoice error 402");
122
+
123
+ await docs.addDocuments([{ id: "42", title: "Reset", body: "…" }]);
124
+ ```
125
+
126
+ ## Manage Indexes
127
+
128
+ Create and maintain indexes with the same infer-everything ergonomics. A Delta
129
+ Sync index computes embeddings from a source Delta table and stays synced; the
130
+ embedding model, endpoint, primary key (`id`), and text column
131
+ (`text`/`content`/`body`) are all inferred when omitted.
132
+
133
+ ```ts
134
+ const client = createSearchClient();
135
+
136
+ // Ensure the Vector Search endpoint exists (creates a STANDARD one if not).
137
+ await client.ensureEndpoint("my-vs-endpoint", { wait: true });
138
+
139
+ // Delta Sync index from a Delta table (embeddings computed by Databricks).
140
+ await client.ensureIndex("main.support.docs", {
141
+ endpoint: "my-vs-endpoint",
142
+ sourceTable: "main.support.docs_source",
143
+ // embeddingModel / primaryKey / embeddingSourceColumn inferred when omitted
144
+ });
145
+
146
+ // Trigger a sync, then later delete.
147
+ await client.syncIndex("main.support.docs");
148
+ await client.deleteIndex("main.support.docs");
149
+
150
+ // Managed direct-access index (the lightest REAL index): Databricks embeds a
151
+ // text column on write AND query, so no Delta table, no warehouse, no vectors.
152
+ await client.createIndex("main.support.docs", {
153
+ endpoint: "my-vs-endpoint",
154
+ // managed by default when no embeddingDimension is given
155
+ });
156
+ await client
157
+ .index("main.support.docs")
158
+ .addDocuments([{ id: "1", text: "AI Search finds the most relevant documents for a query." }]);
159
+
160
+ // Self-managed direct-access index you write vectors to yourself.
161
+ await client.createIndex("main.support.vectors", { embeddingDimension: 1024 });
162
+ await client.index("main.support.vectors").addDocuments([{ id: "1", embedding: [/* … */] }]);
163
+
164
+ // One call to make an index real AND seeded (idempotent - safe every boot).
165
+ await client.provision("main.support.docs", {
166
+ endpoint: "my-vs-endpoint",
167
+ seed: [{ id: "1", text: "Databricks AI Search overview", url: "https://…" }],
168
+ });
169
+ ```
170
+
171
+ The `SearchIndex` handle mirrors these: `index.ensure(opts)`, `index.sync()`,
172
+ `index.delete()`, `index.info()`.
173
+
174
+ When the plugin's write surface is on (`search({ allowWrite: true })` or
175
+ `SEARCH_WRITE=true`), agents get the same lifecycle as tools: `create_index`
176
+ provisions an index with everything inferred (pass a `sourceTable` for the
177
+ common Delta Sync case, or an `embeddingDimension` for a direct-access index),
178
+ and `sync_index` refreshes a Delta Sync index from its source table. They are
179
+ gated because they change infrastructure, so grant them only where a caller
180
+ should be able to set up or refresh indexes.
181
+
182
+ ### Provision a real index on boot
183
+
184
+ `ensureOnSetup` makes the plugin wire up a real index when the app starts, using
185
+ the boot-time SDK auth (env vars or a `DATABRICKS_CONFIG_PROFILE`). It ensures
186
+ the endpoint + index exist and seeds documents only when the index is empty, all
187
+ in the background so a slow first-time endpoint build never blocks the server.
188
+ The default is a managed direct-access index, so the seed rows are plain
189
+ objects and search-by-text works immediately - no Delta table, no warehouse.
190
+
191
+ ```ts
192
+ search({
193
+ index: "main.support.docs",
194
+ endpoint: "my-vs-endpoint",
195
+ ensureOnSetup: {
196
+ embeddingModel: "databricks-gte-large-en", // optional; best embedding endpoint otherwise
197
+ documents: [
198
+ { id: "1", title: "Overview", text: "AI Search finds relevant docs.", url: "https://…" },
199
+ { id: "2", title: "Indexes", text: "Delta Sync vs. direct access." },
200
+ ],
201
+ // schema inferred from the first row; primaryKey/textColumn default to id/text
202
+ },
203
+ });
204
+ ```
205
+
206
+ Idempotent: later boots see the endpoint, index, and rows already present and do
207
+ nothing. Point `ensureOnSetup.sourceTable` at a Delta table to provision a Delta
208
+ Sync index instead of a managed direct-access one.
209
+
210
+ ### Lakebase full-text fallback
211
+
212
+ When you have **no** Vector Search endpoint configured but the AppKit `lakebase`
213
+ plugin is registered, the plugin transparently falls back to a Postgres
214
+ full-text index. It provisions one table per index (a generated `tsvector`
215
+ column with a GIN index), seeds the same `ensureOnSetup.documents`, and answers
216
+ queries with `websearch_to_tsquery` + `ts_rank`. The pool is built from the
217
+ `lakebase` plugin's service-principal config exactly like
218
+ [`@dbx-tools/appkit-mastra`](../appkit-mastra) builds its memory pool - no auth
219
+ is re-implemented.
220
+
221
+ The point is parity: the client returns the identical `SearchResult` /
222
+ `SearchHit` (`{ id, score, fields }`) / `UpsertResult` shapes, so the agent
223
+ tools, the `/api/search` routes, and the React search box behave the same
224
+ whichever backend is active. Register `lakebase()`, omit `endpoint`, and search
225
+ works with no Vector Search infrastructure:
226
+
227
+ ```ts
228
+ import { createApp, lakebase } from "@databricks/appkit";
229
+ import { plugin as searchPlugin } from "@dbx-tools/search";
230
+
231
+ const { search } = searchPlugin;
232
+
233
+ createApp({
234
+ plugins: [
235
+ lakebase(), // register it and search uses Postgres full-text
236
+ search({
237
+ // no `endpoint` -> Lakebase full-text fallback
238
+ index: "docs",
239
+ ensureOnSetup: {
240
+ documents: [{ id: "1", title: "Overview", text: "Search over Postgres full-text." }],
241
+ },
242
+ }),
243
+ ],
244
+ });
245
+ ```
246
+
247
+ Selection is automatic and logged at boot (`backend: "vector-search"` vs
248
+ `"lakebase"`). Configure an `endpoint` and Vector Search wins; drop it and the
249
+ Lakebase fallback takes over.
250
+
251
+ ## Configuration
252
+
253
+ All fields are optional. Precedence is plugin config, then environment, then a
254
+ default.
255
+
256
+ | Config | Environment | Default | Purpose |
257
+ | ---------------- | ------------------------------------------------ | ----------------------- | ------------------------------------------------------------------------------------------ |
258
+ | `index` | `SEARCH_INDEX`, `DATABRICKS_VECTOR_SEARCH_INDEX` | – | Default index (name or alias). |
259
+ | `indexes` | – | `[index]` | Indexes known for aliases, universal search, and the UI. |
260
+ | `endpoint` | `SEARCH_ENDPOINT` | – | Vector Search endpoint (only needed to create an index). |
261
+ | `columns` | `SEARCH_COLUMNS` | index's columns | Default columns per hit. |
262
+ | `pageSize` | `SEARCH_PAGE_SIZE` | `10` | Default hits per search. |
263
+ | `mode` | `SEARCH_MODE` | `hybrid` | `hybrid` / `vector` / `keyword`. |
264
+ | `embeddingModel` | `SEARCH_EMBEDDING_MODEL` | best embedding endpoint | Embedding endpoint for index creation. |
265
+ | `timeoutMs` | `SEARCH_TIMEOUT_MS` | `30000` | Per-call timeout. |
266
+ | `allowWrite` | `SEARCH_WRITE` | `false` | Enable the write tools (`add_documents` / `create_index` / `sync_index`) and their routes. |
267
+ | `ensureOnSetup` | – | – | Provision the endpoint + index and seed documents at boot (background). |
268
+
269
+ ## Modules
270
+
271
+ - `client` - `SearchClient`, `SearchIndex`, `createSearchClient`, the search
272
+ methods, and the index lifecycle (`createIndex` / `ensureIndex` / `syncIndex`
273
+ / `deleteIndex` / `listIndexes` / `ensureEndpoint`), plus the `SearchOptions` /
274
+ `UniversalSearchOptions` / `IndexInfo` / `CreateIndexOptions` /
275
+ `EnsureEndpointOptions` types.
276
+ - `plugin` - `SearchPlugin` and the `search()` factory (`ToolProvider`,
277
+ routes, `clientConfig`, `exports`).
278
+ - `tool` - the `searchTool()`, `universalSearchTool()`, `addDocumentsTool()`,
279
+ `createIndexTool()`, and `syncIndexTool()` Mastra factories.
280
+ - `index-tools` - `toCreateIndexOptions`, the shared mapping from the
281
+ `create_index` wire request onto `SearchClient.createIndex` options (used by
282
+ both the Mastra tool and the plugin route).
283
+ - `config` - `resolveSearchConfig`, `resolveIndexName`, `SEARCH_CONFIG_SCHEMA`,
284
+ the config env constants, and the `SearchPluginConfig` /
285
+ `ResolvedSearchConfig` types.
286
+ - `query` - `toQueryType`, `compileFilter`, `toHits`, `toRequestColumns`,
287
+ `toDocumentArray` (contract ↔ serving-API translation).
288
+ - `lakebase` - `LakebaseSearchBackend`, the Postgres full-text FALLBACK used
289
+ when no Vector Search endpoint is configured (provision + seed + `tsvector`
290
+ search, same hit shape as Vector Search).
291
+ - `runtime` - `getSearchRuntime` / `resetSearchRuntime` (the shared client).
292
+ - `schema` - the tool descriptions and re-exported request schemas.
293
+
294
+ Browser-safe schemas live in
295
+ [`@dbx-tools/shared-search`](../../shared/search); the React search box
296
+ lives in [`@dbx-tools/ui-search`](../../ui/search). Model resolution reuses
297
+ [`@dbx-tools/model`](../model).
package/index.ts ADDED
@@ -0,0 +1,28 @@
1
+ // GENERATED by projen watch - DO NOT EDIT.
2
+ // Regenerated from the exporting modules in ./src.
3
+ // Hand edits are overwritten on the next watch; this file is read-only.
4
+
5
+ export * as client from "./src/client.ts";
6
+ export * as config from "./src/config.ts";
7
+ export * as indexTools from "./src/index-tools.ts";
8
+ export * as lakebase from "./src/lakebase.ts";
9
+ export * as plugin from "./src/plugin.ts";
10
+ export * as query from "./src/query.ts";
11
+ export * as runtime from "./src/runtime.ts";
12
+ export * as schema from "./src/schema.ts";
13
+ export * as tool from "./src/tool.ts";
14
+ export { SearchIndex, SearchClient, createSearchClient } from "./src/client.ts";
15
+ export type { SearchOptions, UniversalSearchOptions, IndexInfo, CreateIndexOptions, EnsureEndpointOptions, ProvisionOptions } from "./src/client.ts";
16
+ export { INDEX_ENV, DATABRICKS_INDEX_ENV, ENDPOINT_ENV, DEFAULT_MODE, DEFAULT_PAGE_SIZE, DEFAULT_TIMEOUT_MS, DEFAULT_BASE_PATH, SEARCH_CONFIG_SCHEMA, defaultAlias, resolveSearchConfig, resolveIndexName, indexConfigFor } from "./src/config.ts";
17
+ export type { SearchIndexConfig, SearchPluginConfig, EnsureOnSetupConfig, ResolvedIndexConfig, ResolvedSearchConfig } from "./src/config.ts";
18
+ export { toCreateIndexOptions } from "./src/index-tools.ts";
19
+ export { LakebaseSearchBackend } from "./src/lakebase.ts";
20
+ export type { LakebaseSearchOptions, LakebaseProvisionOptions } from "./src/lakebase.ts";
21
+ export { SearchPlugin, search } from "./src/plugin.ts";
22
+ export { toQueryType, compileFilter, toHits, toRequestColumns, toDocumentArray } from "./src/query.ts";
23
+ export type { QueryResponseLike } from "./src/query.ts";
24
+ export { getSearchRuntime, resetSearchRuntime } from "./src/runtime.ts";
25
+ export type { SearchRuntimeOptions, SearchRuntime } from "./src/runtime.ts";
26
+ export { SEARCH_TOOL_DESCRIPTION, UNIVERSAL_SEARCH_TOOL_DESCRIPTION, ADD_DOCUMENTS_TOOL_DESCRIPTION, CREATE_INDEX_TOOL_DESCRIPTION, SYNC_INDEX_TOOL_DESCRIPTION, searchToolSchema, universalSearchToolSchema, searchResultSchema, createIndexToolSchema, indexInfoSchema, syncIndexToolSchema } from "./src/schema.ts";
27
+ export { searchTool, universalSearchTool, addDocumentsTool, createIndexTool, syncIndexTool } from "./src/tool.ts";
28
+ export type { SearchToolOptions } from "./src/tool.ts";
package/lib/index.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ export * as client from "./src/client.ts";
2
+ export * as config from "./src/config.ts";
3
+ export * as indexTools from "./src/index-tools.ts";
4
+ export * as lakebase from "./src/lakebase.ts";
5
+ export * as plugin from "./src/plugin.ts";
6
+ export * as query from "./src/query.ts";
7
+ export * as runtime from "./src/runtime.ts";
8
+ export * as schema from "./src/schema.ts";
9
+ export * as tool from "./src/tool.ts";
10
+ export { SearchIndex, SearchClient, createSearchClient } from "./src/client.ts";
11
+ export type { SearchOptions, UniversalSearchOptions, IndexInfo, CreateIndexOptions, EnsureEndpointOptions, ProvisionOptions } from "./src/client.ts";
12
+ export { INDEX_ENV, DATABRICKS_INDEX_ENV, ENDPOINT_ENV, DEFAULT_MODE, DEFAULT_PAGE_SIZE, DEFAULT_TIMEOUT_MS, DEFAULT_BASE_PATH, SEARCH_CONFIG_SCHEMA, defaultAlias, resolveSearchConfig, resolveIndexName, indexConfigFor } from "./src/config.ts";
13
+ export type { SearchIndexConfig, SearchPluginConfig, EnsureOnSetupConfig, ResolvedIndexConfig, ResolvedSearchConfig } from "./src/config.ts";
14
+ export { toCreateIndexOptions } from "./src/index-tools.ts";
15
+ export { LakebaseSearchBackend } from "./src/lakebase.ts";
16
+ export type { LakebaseSearchOptions, LakebaseProvisionOptions } from "./src/lakebase.ts";
17
+ export { SearchPlugin, search } from "./src/plugin.ts";
18
+ export { toQueryType, compileFilter, toHits, toRequestColumns, toDocumentArray } from "./src/query.ts";
19
+ export type { QueryResponseLike } from "./src/query.ts";
20
+ export { getSearchRuntime, resetSearchRuntime } from "./src/runtime.ts";
21
+ export type { SearchRuntimeOptions, SearchRuntime } from "./src/runtime.ts";
22
+ export { SEARCH_TOOL_DESCRIPTION, UNIVERSAL_SEARCH_TOOL_DESCRIPTION, ADD_DOCUMENTS_TOOL_DESCRIPTION, CREATE_INDEX_TOOL_DESCRIPTION, SYNC_INDEX_TOOL_DESCRIPTION, searchToolSchema, universalSearchToolSchema, searchResultSchema, createIndexToolSchema, indexInfoSchema, syncIndexToolSchema } from "./src/schema.ts";
23
+ export { searchTool, universalSearchTool, addDocumentsTool, createIndexTool, syncIndexTool } from "./src/tool.ts";
24
+ export type { SearchToolOptions } from "./src/tool.ts";
package/lib/index.js ADDED
@@ -0,0 +1,22 @@
1
+ // GENERATED by projen watch - DO NOT EDIT.
2
+ // Regenerated from the exporting modules in ./src.
3
+ // Hand edits are overwritten on the next watch; this file is read-only.
4
+ export * as client from "./src/client.js";
5
+ export * as config from "./src/config.js";
6
+ export * as indexTools from "./src/index-tools.js";
7
+ export * as lakebase from "./src/lakebase.js";
8
+ export * as plugin from "./src/plugin.js";
9
+ export * as query from "./src/query.js";
10
+ export * as runtime from "./src/runtime.js";
11
+ export * as schema from "./src/schema.js";
12
+ export * as tool from "./src/tool.js";
13
+ export { SearchIndex, SearchClient, createSearchClient } from "./src/client.js";
14
+ export { INDEX_ENV, DATABRICKS_INDEX_ENV, ENDPOINT_ENV, DEFAULT_MODE, DEFAULT_PAGE_SIZE, DEFAULT_TIMEOUT_MS, DEFAULT_BASE_PATH, SEARCH_CONFIG_SCHEMA, defaultAlias, resolveSearchConfig, resolveIndexName, indexConfigFor } from "./src/config.js";
15
+ export { toCreateIndexOptions } from "./src/index-tools.js";
16
+ export { LakebaseSearchBackend } from "./src/lakebase.js";
17
+ export { SearchPlugin, search } from "./src/plugin.js";
18
+ export { toQueryType, compileFilter, toHits, toRequestColumns, toDocumentArray } from "./src/query.js";
19
+ export { getSearchRuntime, resetSearchRuntime } from "./src/runtime.js";
20
+ export { SEARCH_TOOL_DESCRIPTION, UNIVERSAL_SEARCH_TOOL_DESCRIPTION, ADD_DOCUMENTS_TOOL_DESCRIPTION, CREATE_INDEX_TOOL_DESCRIPTION, SYNC_INDEX_TOOL_DESCRIPTION, searchToolSchema, universalSearchToolSchema, searchResultSchema, createIndexToolSchema, indexInfoSchema, syncIndexToolSchema } from "./src/schema.js";
21
+ export { searchTool, universalSearchTool, addDocumentsTool, createIndexTool, syncIndexTool } from "./src/tool.js";
22
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxPQUFPLEtBQUssTUFBTSxNQUFNLGlCQUFpQixDQUFDO0FBQzFDLE9BQU8sS0FBSyxNQUFNLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxLQUFLLFVBQVUsTUFBTSxzQkFBc0IsQ0FBQztBQUNuRCxPQUFPLEtBQUssUUFBUSxNQUFNLG1CQUFtQixDQUFDO0FBQzlDLE9BQU8sS0FBSyxNQUFNLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxLQUFLLEtBQUssTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QyxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sS0FBSyxNQUFNLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxLQUFLLElBQUksTUFBTSxlQUFlLENBQUM7QUFDdEMsT0FBTyxFQUFFLFdBQVcsRUFBRSxZQUFZLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUVoRixPQUFPLEVBQUUsU0FBUyxFQUFFLG9CQUFvQixFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsaUJBQWlCLEVBQUUsa0JBQWtCLEVBQUUsaUJBQWlCLEVBQUUsb0JBQW9CLEVBQUUsWUFBWSxFQUFFLG1CQUFtQixFQUFFLGdCQUFnQixFQUFFLGNBQWMsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRW5QLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzVELE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRTFELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDdkQsT0FBTyxFQUFFLFdBQVcsRUFBRSxhQUFhLEVBQUUsTUFBTSxFQUFFLGdCQUFnQixFQUFFLGVBQWUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXZHLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRXhFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxpQ0FBaUMsRUFBRSw4QkFBOEIsRUFBRSw2QkFBNkIsRUFBRSwyQkFBMkIsRUFBRSxnQkFBZ0IsRUFBRSx5QkFBeUIsRUFBRSxrQkFBa0IsRUFBRSxxQkFBcUIsRUFBRSxlQUFlLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN2VCxPQUFPLEVBQUUsVUFBVSxFQUFFLG1CQUFtQixFQUFFLGdCQUFnQixFQUFFLGVBQWUsRUFBRSxhQUFhLEVBQUUsTUFBTSxlQUFlLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBHRU5FUkFURUQgYnkgcHJvamVuIHdhdGNoIC0gRE8gTk9UIEVESVQuXG4vLyBSZWdlbmVyYXRlZCBmcm9tIHRoZSBleHBvcnRpbmcgbW9kdWxlcyBpbiAuL3NyYy5cbi8vIEhhbmQgZWRpdHMgYXJlIG92ZXJ3cml0dGVuIG9uIHRoZSBuZXh0IHdhdGNoOyB0aGlzIGZpbGUgaXMgcmVhZC1vbmx5LlxuXG5leHBvcnQgKiBhcyBjbGllbnQgZnJvbSBcIi4vc3JjL2NsaWVudC50c1wiO1xuZXhwb3J0ICogYXMgY29uZmlnIGZyb20gXCIuL3NyYy9jb25maWcudHNcIjtcbmV4cG9ydCAqIGFzIGluZGV4VG9vbHMgZnJvbSBcIi4vc3JjL2luZGV4LXRvb2xzLnRzXCI7XG5leHBvcnQgKiBhcyBsYWtlYmFzZSBmcm9tIFwiLi9zcmMvbGFrZWJhc2UudHNcIjtcbmV4cG9ydCAqIGFzIHBsdWdpbiBmcm9tIFwiLi9zcmMvcGx1Z2luLnRzXCI7XG5leHBvcnQgKiBhcyBxdWVyeSBmcm9tIFwiLi9zcmMvcXVlcnkudHNcIjtcbmV4cG9ydCAqIGFzIHJ1bnRpbWUgZnJvbSBcIi4vc3JjL3J1bnRpbWUudHNcIjtcbmV4cG9ydCAqIGFzIHNjaGVtYSBmcm9tIFwiLi9zcmMvc2NoZW1hLnRzXCI7XG5leHBvcnQgKiBhcyB0b29sIGZyb20gXCIuL3NyYy90b29sLnRzXCI7XG5leHBvcnQgeyBTZWFyY2hJbmRleCwgU2VhcmNoQ2xpZW50LCBjcmVhdGVTZWFyY2hDbGllbnQgfSBmcm9tIFwiLi9zcmMvY2xpZW50LnRzXCI7XG5leHBvcnQgdHlwZSB7IFNlYXJjaE9wdGlvbnMsIFVuaXZlcnNhbFNlYXJjaE9wdGlvbnMsIEluZGV4SW5mbywgQ3JlYXRlSW5kZXhPcHRpb25zLCBFbnN1cmVFbmRwb2ludE9wdGlvbnMsIFByb3Zpc2lvbk9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvY2xpZW50LnRzXCI7XG5leHBvcnQgeyBJTkRFWF9FTlYsIERBVEFCUklDS1NfSU5ERVhfRU5WLCBFTkRQT0lOVF9FTlYsIERFRkFVTFRfTU9ERSwgREVGQVVMVF9QQUdFX1NJWkUsIERFRkFVTFRfVElNRU9VVF9NUywgREVGQVVMVF9CQVNFX1BBVEgsIFNFQVJDSF9DT05GSUdfU0NIRU1BLCBkZWZhdWx0QWxpYXMsIHJlc29sdmVTZWFyY2hDb25maWcsIHJlc29sdmVJbmRleE5hbWUsIGluZGV4Q29uZmlnRm9yIH0gZnJvbSBcIi4vc3JjL2NvbmZpZy50c1wiO1xuZXhwb3J0IHR5cGUgeyBTZWFyY2hJbmRleENvbmZpZywgU2VhcmNoUGx1Z2luQ29uZmlnLCBFbnN1cmVPblNldHVwQ29uZmlnLCBSZXNvbHZlZEluZGV4Q29uZmlnLCBSZXNvbHZlZFNlYXJjaENvbmZpZyB9IGZyb20gXCIuL3NyYy9jb25maWcudHNcIjtcbmV4cG9ydCB7IHRvQ3JlYXRlSW5kZXhPcHRpb25zIH0gZnJvbSBcIi4vc3JjL2luZGV4LXRvb2xzLnRzXCI7XG5leHBvcnQgeyBMYWtlYmFzZVNlYXJjaEJhY2tlbmQgfSBmcm9tIFwiLi9zcmMvbGFrZWJhc2UudHNcIjtcbmV4cG9ydCB0eXBlIHsgTGFrZWJhc2VTZWFyY2hPcHRpb25zLCBMYWtlYmFzZVByb3Zpc2lvbk9wdGlvbnMgfSBmcm9tIFwiLi9zcmMvbGFrZWJhc2UudHNcIjtcbmV4cG9ydCB7IFNlYXJjaFBsdWdpbiwgc2VhcmNoIH0gZnJvbSBcIi4vc3JjL3BsdWdpbi50c1wiO1xuZXhwb3J0IHsgdG9RdWVyeVR5cGUsIGNvbXBpbGVGaWx0ZXIsIHRvSGl0cywgdG9SZXF1ZXN0Q29sdW1ucywgdG9Eb2N1bWVudEFycmF5IH0gZnJvbSBcIi4vc3JjL3F1ZXJ5LnRzXCI7XG5leHBvcnQgdHlwZSB7IFF1ZXJ5UmVzcG9uc2VMaWtlIH0gZnJvbSBcIi4vc3JjL3F1ZXJ5LnRzXCI7XG5leHBvcnQgeyBnZXRTZWFyY2hSdW50aW1lLCByZXNldFNlYXJjaFJ1bnRpbWUgfSBmcm9tIFwiLi9zcmMvcnVudGltZS50c1wiO1xuZXhwb3J0IHR5cGUgeyBTZWFyY2hSdW50aW1lT3B0aW9ucywgU2VhcmNoUnVudGltZSB9IGZyb20gXCIuL3NyYy9ydW50aW1lLnRzXCI7XG5leHBvcnQgeyBTRUFSQ0hfVE9PTF9ERVNDUklQVElPTiwgVU5JVkVSU0FMX1NFQVJDSF9UT09MX0RFU0NSSVBUSU9OLCBBRERfRE9DVU1FTlRTX1RPT0xfREVTQ1JJUFRJT04sIENSRUFURV9JTkRFWF9UT09MX0RFU0NSSVBUSU9OLCBTWU5DX0lOREVYX1RPT0xfREVTQ1JJUFRJT04sIHNlYXJjaFRvb2xTY2hlbWEsIHVuaXZlcnNhbFNlYXJjaFRvb2xTY2hlbWEsIHNlYXJjaFJlc3VsdFNjaGVtYSwgY3JlYXRlSW5kZXhUb29sU2NoZW1hLCBpbmRleEluZm9TY2hlbWEsIHN5bmNJbmRleFRvb2xTY2hlbWEgfSBmcm9tIFwiLi9zcmMvc2NoZW1hLnRzXCI7XG5leHBvcnQgeyBzZWFyY2hUb29sLCB1bml2ZXJzYWxTZWFyY2hUb29sLCBhZGREb2N1bWVudHNUb29sLCBjcmVhdGVJbmRleFRvb2wsIHN5bmNJbmRleFRvb2wgfSBmcm9tIFwiLi9zcmMvdG9vbC50c1wiO1xuZXhwb3J0IHR5cGUgeyBTZWFyY2hUb29sT3B0aW9ucyB9IGZyb20gXCIuL3NyYy90b29sLnRzXCI7XG4iXX0=