@dbx-tools/appkit 0.3.44 → 0.4.0
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/lib/index.d.ts +23 -0
- package/lib/index.js +20 -0
- package/lib/src/appkit.d.ts +51 -0
- package/lib/src/appkit.js +57 -0
- package/lib/src/config.d.ts +89 -0
- package/lib/src/config.js +403 -0
- package/lib/src/create-app.d.ts +72 -0
- package/lib/src/create-app.js +120 -0
- package/lib/src/databricks.d.ts +60 -0
- package/lib/src/databricks.js +107 -0
- package/lib/src/lakebase-resolver.d.ts +122 -0
- package/lib/src/lakebase-resolver.js +679 -0
- package/lib/src/pgaddress.d.ts +104 -0
- package/lib/src/pgaddress.js +171 -0
- package/lib/src/plugin.d.ts +90 -0
- package/lib/src/plugin.js +97 -0
- package/lib/src/provision.d.ts +51 -0
- package/lib/src/provision.js +133 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +11 -7
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lakebase Postgres connection resolver.
|
|
3
|
+
*
|
|
4
|
+
* Reads the same env vars the `lakebase` plugin consumes (`PGHOST`,
|
|
5
|
+
* `PGDATABASE`, `PGPORT`, `PGSSLMODE`, `LAKEBASE_ENDPOINT`) and fills in
|
|
6
|
+
* whichever pieces are missing using the Lakebase Autoscaling REST API
|
|
7
|
+
* under `/api/2.0/postgres/` via the Databricks workspace client.
|
|
8
|
+
*
|
|
9
|
+
* `LAKEBASE_ENDPOINT` (and `config.endpoint`) accept anything
|
|
10
|
+
* {@link parseAddress} understands - canonical resource paths, Postgres
|
|
11
|
+
* URIs, bare hostnames, or bare project ids. The resolver layers
|
|
12
|
+
* whatever pieces fall out of parsing under explicit config / env
|
|
13
|
+
* values, then fills the remaining gaps via the API:
|
|
14
|
+
*
|
|
15
|
+
* 1. Reverse-lookup: when a host is known but no resource path is,
|
|
16
|
+
* scan projects -> branches -> endpoints for a matching
|
|
17
|
+
* `status.hosts.host` and recover the owning project/branch/endpoint.
|
|
18
|
+
* 2. Pick: when a project is known but child resources aren't, prefer
|
|
19
|
+
* the server-side default (`status.default`, `ENDPOINT_TYPE_READ_WRITE`,
|
|
20
|
+
* `databricks_postgres`) and fall back to "the only one" when a
|
|
21
|
+
* listing returns a single result.
|
|
22
|
+
* 3. Auto-create: when no projects exist at all, create one whose
|
|
23
|
+
* id defaults to `project.name()` slugified (override
|
|
24
|
+
* with `config.autoCreate: "my-id"` or disable with
|
|
25
|
+
* `config.autoCreate: false`). The create call is idempotent - an
|
|
26
|
+
* `ALREADY_EXISTS` response from a concurrent boot is treated as
|
|
27
|
+
* success. Then poll the default endpoint until it reports
|
|
28
|
+
* `current_state` `READY` or `IDLE`.
|
|
29
|
+
*
|
|
30
|
+
* {@link applyLakebaseToEnv} writes the resolved values back to
|
|
31
|
+
* `process.env` so the downstream `lakebase` plugin picks them up.
|
|
32
|
+
*
|
|
33
|
+
* @see https://docs.databricks.com/api/workspace/postgres
|
|
34
|
+
*
|
|
35
|
+
* @module
|
|
36
|
+
*/
|
|
37
|
+
import { ConfigurationError, ExecutionError, getWorkspaceClient, ValidationError, } from "@databricks/appkit";
|
|
38
|
+
import { project } from "@dbx-tools/core";
|
|
39
|
+
import { async, log, object, string } from "@dbx-tools/shared-core";
|
|
40
|
+
import { z } from "zod";
|
|
41
|
+
import { resolveConfigValue } from "./config.js";
|
|
42
|
+
import { MAX_TCP_PORT, toContext } from "./databricks.js";
|
|
43
|
+
import { parseAddress, parseResourcePath, SSL_MODES, } from "./pgaddress.js";
|
|
44
|
+
const logger = log.logger("lakebase-resolver");
|
|
45
|
+
const API_BASE = "/api/2.0/postgres";
|
|
46
|
+
const DEFAULT_PORT = 5432;
|
|
47
|
+
const DEFAULT_SSL_MODE = "require";
|
|
48
|
+
const DEFAULT_PG_VERSION = 17;
|
|
49
|
+
/** Lakebase project ids: `^[a-z][a-z0-9-]{0,61}[a-z0-9]$`. */
|
|
50
|
+
const PROJECT_ID_MAX_LEN = 63;
|
|
51
|
+
const OPERATION_TIMEOUT_MS = 5 * 60_000;
|
|
52
|
+
const OPERATION_POLL_MS = 2_000;
|
|
53
|
+
const ENDPOINT_READY_TIMEOUT_MS = 5 * 60_000;
|
|
54
|
+
const ENDPOINT_READY_POLL_MS = 2_000;
|
|
55
|
+
/** Ceiling on a single backoff step, so a long wait still polls regularly. */
|
|
56
|
+
const POLL_MAX_DELAY_MS = 15_000;
|
|
57
|
+
const POLL_BACKOFF_FACTOR = 1.5;
|
|
58
|
+
/** Fraction of each delay applied as +/- jitter, so concurrent boots desynchronize. */
|
|
59
|
+
const POLL_JITTER_RATIO = 0.2;
|
|
60
|
+
/** Endpoint `status.endpoint_type` for the writable primary. */
|
|
61
|
+
const READ_WRITE_ENDPOINT_TYPE = "ENDPOINT_TYPE_READ_WRITE";
|
|
62
|
+
/** Endpoint `status.current_state` values that will accept a connection. */
|
|
63
|
+
const CONNECTABLE_ENDPOINT_STATES = new Set(["READY", "IDLE"]);
|
|
64
|
+
/** Endpoint `status.current_state` before a hostname is meaningful. */
|
|
65
|
+
const INITIALIZING_ENDPOINT_STATE = "INITIALIZING";
|
|
66
|
+
/** Lakebase's own default Postgres database name. */
|
|
67
|
+
const DEFAULT_DATABASE = "databricks_postgres";
|
|
68
|
+
/**
|
|
69
|
+
* Every field of the Lakebase REST payloads is modelled as optional and
|
|
70
|
+
* validated at the boundary rather than asserted with a cast: the pickers below
|
|
71
|
+
* branch on `status.*`, and a shape change upstream should surface as one loud
|
|
72
|
+
* error here instead of an `undefined` three frames away. Enum-shaped fields
|
|
73
|
+
* stay `string` so a newly added state does not fail the whole response.
|
|
74
|
+
*/
|
|
75
|
+
const projectSchema = z.object({
|
|
76
|
+
// Full resource path: `projects/{p}`.
|
|
77
|
+
name: z.string().optional(),
|
|
78
|
+
});
|
|
79
|
+
const endpointSchema = z.object({
|
|
80
|
+
// Full resource path: `projects/{p}/branches/{b}/endpoints/{e}`.
|
|
81
|
+
name: z.string().optional(),
|
|
82
|
+
uid: z.string().optional(),
|
|
83
|
+
// Server-side state. All connection info lives here - the spec block
|
|
84
|
+
// only carries the desired configuration, not the runtime hostnames.
|
|
85
|
+
status: z
|
|
86
|
+
.object({
|
|
87
|
+
endpoint_type: z.string().optional(),
|
|
88
|
+
// Resolved hostnames; `hosts.host` is the writable primary.
|
|
89
|
+
hosts: z
|
|
90
|
+
.object({
|
|
91
|
+
host: z.string().optional(),
|
|
92
|
+
read_only_host: z.string().optional(),
|
|
93
|
+
})
|
|
94
|
+
.optional(),
|
|
95
|
+
// Compute state: `INITIALIZING`, `STARTING`, `READY`, `IDLE`, ...
|
|
96
|
+
current_state: z.string().optional(),
|
|
97
|
+
})
|
|
98
|
+
.optional(),
|
|
99
|
+
});
|
|
100
|
+
const branchSchema = z.object({
|
|
101
|
+
// Full resource path: `projects/{p}/branches/{b}`.
|
|
102
|
+
name: z.string().optional(),
|
|
103
|
+
status: z
|
|
104
|
+
.object({
|
|
105
|
+
// True for the project's default branch (e.g. `production`).
|
|
106
|
+
default: z.boolean().optional(),
|
|
107
|
+
current_state: z.string().optional(),
|
|
108
|
+
})
|
|
109
|
+
.optional(),
|
|
110
|
+
});
|
|
111
|
+
const databaseSchema = z.object({
|
|
112
|
+
// Full resource path: `projects/{p}/branches/{b}/databases/{d}`.
|
|
113
|
+
name: z.string().optional(),
|
|
114
|
+
status: z
|
|
115
|
+
.object({
|
|
116
|
+
// Actual Postgres database name (used as `PGDATABASE`). May differ
|
|
117
|
+
// from the resource id - e.g. resource `databricks-postgres`
|
|
118
|
+
// surfaces as Postgres database `databricks_postgres`.
|
|
119
|
+
postgres_database: z.string().optional(),
|
|
120
|
+
})
|
|
121
|
+
.optional(),
|
|
122
|
+
});
|
|
123
|
+
/**
|
|
124
|
+
* Lakebase REST list responses follow the Google AIP convention:
|
|
125
|
+
* `{ <plural-resource>: T[], next_page_token?: string }`. We only read
|
|
126
|
+
* the first page; for auto-config's "pick something sensible" semantics the
|
|
127
|
+
* cap is fine.
|
|
128
|
+
*/
|
|
129
|
+
const listResponseSchema = z.object({
|
|
130
|
+
next_page_token: z.string().optional(),
|
|
131
|
+
projects: z.array(projectSchema).optional(),
|
|
132
|
+
branches: z.array(branchSchema).optional(),
|
|
133
|
+
endpoints: z.array(endpointSchema).optional(),
|
|
134
|
+
databases: z.array(databaseSchema).optional(),
|
|
135
|
+
});
|
|
136
|
+
/**
|
|
137
|
+
* Long-running operation envelope returned by mutating REST calls.
|
|
138
|
+
* `done: true` means terminal; check `error` before reading `response`.
|
|
139
|
+
*/
|
|
140
|
+
const operationSchema = z.object({
|
|
141
|
+
name: z.string().optional(),
|
|
142
|
+
done: z.boolean().optional(),
|
|
143
|
+
error: z.unknown().optional(),
|
|
144
|
+
response: z.unknown().optional(),
|
|
145
|
+
});
|
|
146
|
+
/** `PGPORT` must land inside the TCP port range. */
|
|
147
|
+
const portSchema = z.coerce.number().int().min(1).max(MAX_TCP_PORT);
|
|
148
|
+
const sslModeSchema = z.enum(SSL_MODES);
|
|
149
|
+
/**
|
|
150
|
+
* Validate a `PGPORT`-shaped value. Returns `undefined` when unset, and throws
|
|
151
|
+
* a {@link ValidationError} naming `PGPORT` for anything that is not a TCP
|
|
152
|
+
* port, so a typo cannot reach the connection record as `NaN`.
|
|
153
|
+
*/
|
|
154
|
+
export function parsePort(value) {
|
|
155
|
+
if (value === undefined || value === "")
|
|
156
|
+
return undefined;
|
|
157
|
+
const parsed = portSchema.safeParse(value);
|
|
158
|
+
if (!parsed.success) {
|
|
159
|
+
throw ValidationError.invalidValue("PGPORT", value, `a TCP port between 1 and ${MAX_TCP_PORT}`);
|
|
160
|
+
}
|
|
161
|
+
return parsed.data;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Validate a `PGSSLMODE`-shaped value. Returns `undefined` when unset, and
|
|
165
|
+
* throws a {@link ValidationError} naming `PGSSLMODE` for an unsupported mode
|
|
166
|
+
* rather than handing `pg` a value it will reject at connect time.
|
|
167
|
+
*/
|
|
168
|
+
export function parseSslMode(value) {
|
|
169
|
+
if (value === undefined || value === "")
|
|
170
|
+
return undefined;
|
|
171
|
+
const parsed = sslModeSchema.safeParse(value.trim().toLowerCase());
|
|
172
|
+
if (!parsed.success) {
|
|
173
|
+
throw ValidationError.invalidValue("PGSSLMODE", value, SSL_MODES.join(", "));
|
|
174
|
+
}
|
|
175
|
+
return parsed.data;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Delay before the next poll attempt: exponential backoff capped at
|
|
179
|
+
* {@link POLL_MAX_DELAY_MS}, plus jitter so several apps booting against the
|
|
180
|
+
* same workspace do not retry in lockstep.
|
|
181
|
+
*/
|
|
182
|
+
export function nextPollDelay(attempt, baseMs) {
|
|
183
|
+
const backoff = Math.min(baseMs * POLL_BACKOFF_FACTOR ** attempt, POLL_MAX_DELAY_MS);
|
|
184
|
+
const jitter = backoff * POLL_JITTER_RATIO * (Math.random() * 2 - 1);
|
|
185
|
+
return Math.max(0, Math.round(backoff + jitter));
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Wait out {@link nextPollDelay} before the next attempt. Rejects with the
|
|
189
|
+
* signal's reason when the caller cancels mid-wait, so a poll loop unwinds
|
|
190
|
+
* instead of finishing its backoff first.
|
|
191
|
+
*/
|
|
192
|
+
export function pollDelay(attempt, baseMs, signal) {
|
|
193
|
+
return async.sleep(nextPollDelay(attempt, baseMs), signal);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Pull resolver inputs from `process.env`, parse the address blob, and
|
|
197
|
+
* layer explicit config on top with this precedence:
|
|
198
|
+
*
|
|
199
|
+
* `config.<field>` > `config.resolveConfigValue` (`env`, then bundle
|
|
200
|
+
* validate JSON) > whatever {@link parseAddress} recovered from the
|
|
201
|
+
* `endpoint` / `LAKEBASE_ENDPOINT` blob.
|
|
202
|
+
*
|
|
203
|
+
* Set `config.endpoint` (or `LAKEBASE_ENDPOINT`) to any input
|
|
204
|
+
* {@link parseAddress} understands: canonical resource paths, Postgres
|
|
205
|
+
* URIs, bare hostnames, or bare project ids.
|
|
206
|
+
*/
|
|
207
|
+
export async function readLakebaseInputs(config) {
|
|
208
|
+
const rawAddress = config?.endpoint ?? (await resolveConfigValue("LAKEBASE_ENDPOINT"));
|
|
209
|
+
const parsed = parseAddress(rawAddress);
|
|
210
|
+
const portEnv = parsePort(await resolveConfigValue("PGPORT"));
|
|
211
|
+
const sslModeEnv = parseSslMode(await resolveConfigValue("PGSSLMODE"));
|
|
212
|
+
return {
|
|
213
|
+
project: config?.project ?? parsed.project,
|
|
214
|
+
branch: config?.branch ?? parsed.branch,
|
|
215
|
+
// Only canonical endpoint resource paths survive here; URIs and
|
|
216
|
+
// bare hostnames set `host` instead and leave `endpoint` undefined
|
|
217
|
+
// until the REST resolver fills it in.
|
|
218
|
+
endpoint: parsed.endpoint,
|
|
219
|
+
database: config?.database ?? (await resolveConfigValue("PGDATABASE")) ?? parsed.database,
|
|
220
|
+
host: config?.host ?? (await resolveConfigValue("PGHOST")) ?? parsed.host,
|
|
221
|
+
port: config?.port ?? portEnv ?? parsed.port,
|
|
222
|
+
sslMode: config?.sslMode ?? sslModeEnv ?? parsed.sslMode,
|
|
223
|
+
autoCreate: config?.autoCreate,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Resolve a fully-populated Postgres connection record from config + env.
|
|
228
|
+
*
|
|
229
|
+
* Returns immediately without network traffic when env already supplies
|
|
230
|
+
* `endpoint`, `host`, and `database`. Otherwise issues REST calls and
|
|
231
|
+
* may auto-create a project (see module docstring).
|
|
232
|
+
*
|
|
233
|
+
* `signal` cancels every REST call and inter-poll sleep.
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* import { lakebaseResolver } from "@dbx-tools/appkit";
|
|
237
|
+
*
|
|
238
|
+
* const resolved = await lakebaseResolver.resolveLakebaseConnection(
|
|
239
|
+
* { autoCreate: false },
|
|
240
|
+
* AbortSignal.timeout(60_000),
|
|
241
|
+
* );
|
|
242
|
+
* lakebaseResolver.applyLakebaseToEnv(resolved);
|
|
243
|
+
*/
|
|
244
|
+
export async function resolveLakebaseConnection(config, signal) {
|
|
245
|
+
const inputs = await readLakebaseInputs(config);
|
|
246
|
+
let { project, branch, endpoint, database, host } = inputs;
|
|
247
|
+
const port = inputs.port ?? DEFAULT_PORT;
|
|
248
|
+
const sslMode = inputs.sslMode ?? DEFAULT_SSL_MODE;
|
|
249
|
+
// Resource paths may carry redundant info; harvest project/branch
|
|
250
|
+
// from any canonical path that snuck in via PGDATABASE or similar.
|
|
251
|
+
if (endpoint && (!project || !branch)) {
|
|
252
|
+
const parsedEndpoint = parseAddress(endpoint);
|
|
253
|
+
project ??= parsedEndpoint.project;
|
|
254
|
+
branch ??= parsedEndpoint.branch;
|
|
255
|
+
}
|
|
256
|
+
if (database && (!project || !branch)) {
|
|
257
|
+
const parsedDatabase = parseAddress(database);
|
|
258
|
+
project ??= parsedDatabase.project;
|
|
259
|
+
branch ??= parsedDatabase.branch;
|
|
260
|
+
if (parsedDatabase.databaseResourceId) {
|
|
261
|
+
database = parsedDatabase.databaseResourceId;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// Already complete: skip every REST call.
|
|
265
|
+
if (endpoint && host && database) {
|
|
266
|
+
return { project, branch, endpoint, database, host, port, sslMode };
|
|
267
|
+
}
|
|
268
|
+
const ws = getWorkspaceClient({});
|
|
269
|
+
// Host known but no resource path: scan the workspace to find which
|
|
270
|
+
// endpoint owns this host so we can populate LAKEBASE_ENDPOINT.
|
|
271
|
+
if (!project && host) {
|
|
272
|
+
const found = await findEndpointByHost(ws, host, signal);
|
|
273
|
+
if (found) {
|
|
274
|
+
project = found.project;
|
|
275
|
+
branch = found.branch;
|
|
276
|
+
endpoint ??= found.endpoint;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// No project anywhere in config/env/address: list, pick, or create.
|
|
280
|
+
if (!project) {
|
|
281
|
+
project = await pickOrCreateProject(ws, config?.autoCreate, signal);
|
|
282
|
+
}
|
|
283
|
+
if (!branch) {
|
|
284
|
+
branch = await pickBranch(ws, project, signal);
|
|
285
|
+
}
|
|
286
|
+
if (!endpoint) {
|
|
287
|
+
const ep = await pickEndpoint(ws, project, branch, signal);
|
|
288
|
+
endpoint = ep.name;
|
|
289
|
+
host ??= ep.host;
|
|
290
|
+
}
|
|
291
|
+
if (!host && endpoint) {
|
|
292
|
+
const parsedEndpoint = parseAddress(endpoint);
|
|
293
|
+
if (parsedEndpoint.project && parsedEndpoint.branch && parsedEndpoint.endpointId) {
|
|
294
|
+
const ep = await waitEndpointReady(ws, parsedEndpoint.project, parsedEndpoint.branch, parsedEndpoint.endpointId, signal);
|
|
295
|
+
host = ep.status?.hosts?.host;
|
|
296
|
+
logger.debug("autopg: resolved host from endpoint", { host });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (!database) {
|
|
300
|
+
database = await pickDatabase(ws, project, branch, signal);
|
|
301
|
+
}
|
|
302
|
+
return { project, branch, endpoint, database, host, port, sslMode };
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Write resolved values back to `process.env` so the `lakebase` plugin
|
|
306
|
+
* (which reads env directly) picks them up during its own `setup()`.
|
|
307
|
+
* Existing env values are preserved; only missing keys are filled in,
|
|
308
|
+
* which keeps explicit overrides authoritative.
|
|
309
|
+
*/
|
|
310
|
+
export function applyLakebaseToEnv(resolved) {
|
|
311
|
+
if (resolved.endpoint)
|
|
312
|
+
process.env.LAKEBASE_ENDPOINT ??= resolved.endpoint;
|
|
313
|
+
if (resolved.host)
|
|
314
|
+
process.env.PGHOST ??= resolved.host;
|
|
315
|
+
if (resolved.database)
|
|
316
|
+
process.env.PGDATABASE ??= resolved.database;
|
|
317
|
+
process.env.PGPORT ??= String(resolved.port);
|
|
318
|
+
process.env.PGSSLMODE ??= resolved.sslMode;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Adapt an {@link AbortSignal} into the api client's cancellation context.
|
|
322
|
+
* AppKit resolves a different copy of `@databricks/sdk-experimental` than this
|
|
323
|
+
* package does, and `Context` carries a private field, so the two declarations
|
|
324
|
+
* are nominally incompatible even though the api client only reads
|
|
325
|
+
* `cancellationToken` and `logger` off the object.
|
|
326
|
+
*/
|
|
327
|
+
function requestContext(signal) {
|
|
328
|
+
return signal ? toContext(signal) : undefined;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Validate a Lakebase REST body. The full payload is logged rather than thrown
|
|
332
|
+
* so a client never receives upstream detail.
|
|
333
|
+
*/
|
|
334
|
+
function parseResponse(schema, path, body) {
|
|
335
|
+
const parsed = schema.safeParse(body);
|
|
336
|
+
if (parsed.success)
|
|
337
|
+
return parsed.data;
|
|
338
|
+
logger.error("autopg: unexpected Lakebase API response", {
|
|
339
|
+
path,
|
|
340
|
+
issues: parsed.error.issues,
|
|
341
|
+
});
|
|
342
|
+
throw new ExecutionError("Lakebase API returned an unexpected response", { context: { path } });
|
|
343
|
+
}
|
|
344
|
+
/** GET helper that validates the JSON body and forwards cancellation. */
|
|
345
|
+
async function getJson(ws, path, schema, signal) {
|
|
346
|
+
const res = await ws.apiClient.request({
|
|
347
|
+
path,
|
|
348
|
+
method: "GET",
|
|
349
|
+
headers: new Headers({ Accept: "application/json" }),
|
|
350
|
+
raw: false,
|
|
351
|
+
}, requestContext(signal));
|
|
352
|
+
return parseResponse(schema, path, res);
|
|
353
|
+
}
|
|
354
|
+
/** POST helper for create / mutate calls; validates the JSON body. */
|
|
355
|
+
async function postJson(ws, path, schema, body, query, signal) {
|
|
356
|
+
const res = await ws.apiClient.request({
|
|
357
|
+
path,
|
|
358
|
+
method: "POST",
|
|
359
|
+
query,
|
|
360
|
+
headers: new Headers({
|
|
361
|
+
Accept: "application/json",
|
|
362
|
+
"Content-Type": "application/json",
|
|
363
|
+
}),
|
|
364
|
+
raw: false,
|
|
365
|
+
payload: body,
|
|
366
|
+
}, requestContext(signal));
|
|
367
|
+
return parseResponse(schema, path, res);
|
|
368
|
+
}
|
|
369
|
+
async function listProjects(ws, signal) {
|
|
370
|
+
const res = await getJson(ws, `${API_BASE}/projects`, listResponseSchema, signal);
|
|
371
|
+
return res.projects ?? [];
|
|
372
|
+
}
|
|
373
|
+
async function listBranches(ws, project, signal) {
|
|
374
|
+
const res = await getJson(ws, `${API_BASE}/projects/${project}/branches`, listResponseSchema, signal);
|
|
375
|
+
return res.branches ?? [];
|
|
376
|
+
}
|
|
377
|
+
async function listEndpoints(ws, project, branch, signal) {
|
|
378
|
+
const res = await getJson(ws, `${API_BASE}/projects/${project}/branches/${branch}/endpoints`, listResponseSchema, signal);
|
|
379
|
+
return res.endpoints ?? [];
|
|
380
|
+
}
|
|
381
|
+
async function listDatabases(ws, project, branch, signal) {
|
|
382
|
+
const res = await getJson(ws, `${API_BASE}/projects/${project}/branches/${branch}/databases`, listResponseSchema, signal);
|
|
383
|
+
return res.databases ?? [];
|
|
384
|
+
}
|
|
385
|
+
async function getEndpoint(ws, project, branch, endpointId, signal) {
|
|
386
|
+
return getJson(ws, `${API_BASE}/projects/${project}/branches/${branch}/endpoints/${endpointId}`, endpointSchema, signal);
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Scan the workspace for an endpoint whose `status.hosts.host` matches
|
|
390
|
+
* the provided hostname. Used to recover the owning project/branch/
|
|
391
|
+
* endpoint resource path when the caller only supplied a Postgres URI.
|
|
392
|
+
*
|
|
393
|
+
* O(projects * branches * endpoints) - fine for typical workspaces
|
|
394
|
+
* (single digits per tier); pagination is intentionally not followed
|
|
395
|
+
* since this is a best-effort fallback.
|
|
396
|
+
*/
|
|
397
|
+
async function findEndpointByHost(ws, host, signal) {
|
|
398
|
+
const projects = await listProjects(ws, signal);
|
|
399
|
+
for (const p of projects) {
|
|
400
|
+
const projectId = parseResourcePath(p.name).project;
|
|
401
|
+
if (!projectId)
|
|
402
|
+
continue;
|
|
403
|
+
const branches = await listBranches(ws, projectId, signal);
|
|
404
|
+
for (const b of branches) {
|
|
405
|
+
const branchId = parseResourcePath(b.name).branch;
|
|
406
|
+
if (!branchId)
|
|
407
|
+
continue;
|
|
408
|
+
const endpoints = await listEndpoints(ws, projectId, branchId, signal);
|
|
409
|
+
const match = endpoints.find((e) => e.status?.hosts?.host === host);
|
|
410
|
+
if (match?.name) {
|
|
411
|
+
logger.debug("autopg: matched endpoint by host", {
|
|
412
|
+
host,
|
|
413
|
+
endpoint: match.name,
|
|
414
|
+
});
|
|
415
|
+
return {
|
|
416
|
+
project: projectId,
|
|
417
|
+
branch: branchId,
|
|
418
|
+
endpoint: match.name,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
logger.debug("autopg: no endpoint matched host", { host });
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Pick the project to use, or create one when the workspace is empty.
|
|
428
|
+
*
|
|
429
|
+
* Selection order:
|
|
430
|
+
* 1. Exactly one project listed -> use it.
|
|
431
|
+
* 2. Zero projects AND `autoCreate !== false` -> ensure a project with
|
|
432
|
+
* the resolved id exists, then return its id.
|
|
433
|
+
* 3. Zero projects AND `autoCreate === false` -> throw.
|
|
434
|
+
* 4. Multiple projects -> throw with the candidate list (set
|
|
435
|
+
* `config.project` or pin a project id in `LAKEBASE_ENDPOINT`).
|
|
436
|
+
*/
|
|
437
|
+
async function pickOrCreateProject(ws, autoCreate, signal) {
|
|
438
|
+
const projects = await listProjects(ws, signal);
|
|
439
|
+
if (projects.length === 1) {
|
|
440
|
+
const id = parseResourcePath(projects[0].name).project;
|
|
441
|
+
if (id) {
|
|
442
|
+
logger.debug("autopg: using only project", { project: id });
|
|
443
|
+
return id;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (projects.length === 0) {
|
|
447
|
+
if (autoCreate === false) {
|
|
448
|
+
throw ConfigurationError.resourceNotFound("Lakebase project", "autoCreate is false; create a project or set config.project / LAKEBASE_ENDPOINT.");
|
|
449
|
+
}
|
|
450
|
+
const id = autoCreate ?? (await defaultProjectId());
|
|
451
|
+
return ensureProject(ws, id, signal);
|
|
452
|
+
}
|
|
453
|
+
const candidates = projects
|
|
454
|
+
.map((p) => parseResourcePath(p.name).project)
|
|
455
|
+
.filter((id) => Boolean(id))
|
|
456
|
+
.join(", ");
|
|
457
|
+
throw ConfigurationError.invalidConnection("Lakebase", `Multiple projects found; set config.project or pin a project id in LAKEBASE_ENDPOINT. Candidates: ${candidates}`);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Derive a Lakebase project id from the host repo's `package.json`
|
|
461
|
+
* name (via {@link project.name}) slugified to satisfy the
|
|
462
|
+
* Lakebase id constraint (`^[a-z][a-z0-9-]{0,61}[a-z0-9]$`).
|
|
463
|
+
*
|
|
464
|
+
* Throws when the slug ends up empty or starts with a digit, since the
|
|
465
|
+
* server would reject it anyway - callers should pass an explicit
|
|
466
|
+
* `autoCreate` id in that case.
|
|
467
|
+
*/
|
|
468
|
+
async function defaultProjectId() {
|
|
469
|
+
const name = project.name();
|
|
470
|
+
const slug = string.toSlugWithOptions({ maxLength: PROJECT_ID_MAX_LEN }, name);
|
|
471
|
+
if (!slug || !/^[a-z]/.test(slug)) {
|
|
472
|
+
logger.warn("autopg: project name does not slugify to a Lakebase project id", { name });
|
|
473
|
+
throw ConfigurationError.invalidConnection("Lakebase", "Could not derive a project id from the package name; pass autoCreate explicitly.");
|
|
474
|
+
}
|
|
475
|
+
return slug;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Ensure a Lakebase project with `projectId` exists. Creates it and
|
|
479
|
+
* waits for the create operation to complete. An `ALREADY_EXISTS`
|
|
480
|
+
* response is treated as success - someone else (a concurrent boot,
|
|
481
|
+
* a sibling process) won the race and the project we wanted is now
|
|
482
|
+
* sitting there ready for downstream pickers.
|
|
483
|
+
*
|
|
484
|
+
* Project creation typically provisions a default `production` branch
|
|
485
|
+
* alongside; downstream pickers handle the rest.
|
|
486
|
+
*/
|
|
487
|
+
async function ensureProject(ws, projectId, signal) {
|
|
488
|
+
logger.warn("autopg: no projects found; creating", { project: projectId });
|
|
489
|
+
try {
|
|
490
|
+
const op = await postJson(ws, `${API_BASE}/projects`, operationSchema, { spec: { pg_version: DEFAULT_PG_VERSION } }, { project_id: projectId }, signal);
|
|
491
|
+
await waitForOperation(ws, op, signal);
|
|
492
|
+
logger.info("autopg: created project", { project: projectId });
|
|
493
|
+
}
|
|
494
|
+
catch (err) {
|
|
495
|
+
if (!isAlreadyExistsError(err))
|
|
496
|
+
throw err;
|
|
497
|
+
logger.info("autopg: project already exists (race); proceeding", {
|
|
498
|
+
project: projectId,
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
return projectId;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Recognize the Databricks SDK's `ALREADY_EXISTS` failure modes so a
|
|
505
|
+
* lost race during `ensureProject` becomes a no-op instead of an error.
|
|
506
|
+
*
|
|
507
|
+
* The SDK throws `ApiError { errorCode, statusCode }` for structured
|
|
508
|
+
* server errors and `HttpError { code }` for transport-layer 4xx/5xx.
|
|
509
|
+
* Both surface a human message that often carries "already exists" so
|
|
510
|
+
* we use that as a final fallback for forward compatibility.
|
|
511
|
+
*/
|
|
512
|
+
function isAlreadyExistsError(error) {
|
|
513
|
+
if (!error || typeof error !== "object")
|
|
514
|
+
return false;
|
|
515
|
+
const e = error;
|
|
516
|
+
if (e.statusCode === 409 || e.code === 409)
|
|
517
|
+
return true;
|
|
518
|
+
if (e.errorCode && /already.?exists/i.test(e.errorCode))
|
|
519
|
+
return true;
|
|
520
|
+
if (e.message && /already.?exists/i.test(e.message))
|
|
521
|
+
return true;
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Reduce a Lakebase operation failure to an error safe to propagate: the
|
|
526
|
+
* operation name plus whatever code the payload carries. The payload itself can
|
|
527
|
+
* echo request context, so it goes to the log instead.
|
|
528
|
+
*/
|
|
529
|
+
function operationFailed(opName, opError) {
|
|
530
|
+
logger.error("autopg: operation failed", { operation: opName, error: opError });
|
|
531
|
+
const code = object.isRecord(opError) ? (opError.code ?? opError.reason) : undefined;
|
|
532
|
+
return new ExecutionError(`Lakebase operation failed: ${opName}`, {
|
|
533
|
+
context: {
|
|
534
|
+
operation: opName,
|
|
535
|
+
errorCode: typeof code === "string" || typeof code === "number" ? String(code) : "unknown",
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Poll a Lakebase long-running operation until `done: true`. Returns
|
|
541
|
+
* the final operation envelope (which may carry `response` or `error`).
|
|
542
|
+
*
|
|
543
|
+
* Throws when:
|
|
544
|
+
* - the response carries an `error` field;
|
|
545
|
+
* - `op.name` is missing (nothing to poll);
|
|
546
|
+
* - the timeout elapses before `done: true`;
|
|
547
|
+
* - `signal` aborts mid-wait.
|
|
548
|
+
*/
|
|
549
|
+
async function waitForOperation(ws, op, signal) {
|
|
550
|
+
if (op.done) {
|
|
551
|
+
if (op.error) {
|
|
552
|
+
throw operationFailed(op.name ?? "unknown", op.error);
|
|
553
|
+
}
|
|
554
|
+
return op;
|
|
555
|
+
}
|
|
556
|
+
const opName = op.name;
|
|
557
|
+
if (!opName) {
|
|
558
|
+
throw ExecutionError.missingData("operation name");
|
|
559
|
+
}
|
|
560
|
+
const start = Date.now();
|
|
561
|
+
for (let attempt = 0; Date.now() - start < OPERATION_TIMEOUT_MS; attempt++) {
|
|
562
|
+
await pollDelay(attempt, OPERATION_POLL_MS, signal);
|
|
563
|
+
const current = await getJson(ws, `${API_BASE}/${opName}`, operationSchema, signal);
|
|
564
|
+
logger.debug("autopg: operation status", { op: opName, done: current.done });
|
|
565
|
+
if (current.done) {
|
|
566
|
+
if (current.error) {
|
|
567
|
+
throw operationFailed(opName, current.error);
|
|
568
|
+
}
|
|
569
|
+
return current;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
throw new ExecutionError(`Lakebase operation did not complete: ${opName}`, {
|
|
573
|
+
context: { operation: opName, timeoutMs: OPERATION_TIMEOUT_MS },
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Poll `getEndpoint` until the compute reports a usable
|
|
578
|
+
* `status.current_state`. `READY` and `IDLE` are both acceptable -
|
|
579
|
+
* `IDLE` just means the compute has scaled to zero but a connection
|
|
580
|
+
* will wake it. Returns the final endpoint payload (with `hosts.host`).
|
|
581
|
+
*/
|
|
582
|
+
async function waitEndpointReady(ws, project, branch, endpointId, signal) {
|
|
583
|
+
const start = Date.now();
|
|
584
|
+
let last = null;
|
|
585
|
+
for (let attempt = 0; Date.now() - start < ENDPOINT_READY_TIMEOUT_MS; attempt++) {
|
|
586
|
+
last = await getEndpoint(ws, project, branch, endpointId, signal);
|
|
587
|
+
const state = last.status?.current_state;
|
|
588
|
+
if (state && CONNECTABLE_ENDPOINT_STATES.has(state))
|
|
589
|
+
return last;
|
|
590
|
+
if (last.status?.hosts?.host && state !== INITIALIZING_ENDPOINT_STATE) {
|
|
591
|
+
// Compute is in some other state (STARTING, etc.) but hostname is
|
|
592
|
+
// already published - good enough to connect; lakebase's OAuth
|
|
593
|
+
// token request will wake it.
|
|
594
|
+
return last;
|
|
595
|
+
}
|
|
596
|
+
logger.debug("autopg: waiting for endpoint", { endpointId, state });
|
|
597
|
+
await pollDelay(attempt, ENDPOINT_READY_POLL_MS, signal);
|
|
598
|
+
}
|
|
599
|
+
throw new ExecutionError(`Lakebase endpoint did not become ready: ${endpointId}`, {
|
|
600
|
+
context: {
|
|
601
|
+
project,
|
|
602
|
+
branch,
|
|
603
|
+
endpointId,
|
|
604
|
+
timeoutMs: ENDPOINT_READY_TIMEOUT_MS,
|
|
605
|
+
lastState: last?.status?.current_state ?? "unknown",
|
|
606
|
+
},
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Pick the default branch for a project. Prefers the branch flagged
|
|
611
|
+
* `status.default: true` (server-side default, typically `production`
|
|
612
|
+
* unless the project owner changed it). Falls back to the only branch
|
|
613
|
+
* when there's exactly one. Otherwise throws with the candidate list.
|
|
614
|
+
*/
|
|
615
|
+
async function pickBranch(ws, project, signal) {
|
|
616
|
+
const branches = await listBranches(ws, project, signal);
|
|
617
|
+
if (branches.length === 0) {
|
|
618
|
+
throw ConfigurationError.resourceNotFound(`Lakebase branch in project '${project}'`, "The project has no branches; create one or set config.branch.");
|
|
619
|
+
}
|
|
620
|
+
const flagged = branches.find((b) => b.status?.default === true);
|
|
621
|
+
const choice = parseResourcePath(flagged?.name).branch ??
|
|
622
|
+
(branches.length === 1 ? parseResourcePath(branches[0].name).branch : undefined);
|
|
623
|
+
if (!choice) {
|
|
624
|
+
const candidates = branches
|
|
625
|
+
.map((b) => parseResourcePath(b.name).branch)
|
|
626
|
+
.filter((id) => Boolean(id))
|
|
627
|
+
.join(", ");
|
|
628
|
+
throw ConfigurationError.invalidConnection("Lakebase", `Project '${project}' has multiple branches and none marked default; set config.branch or include the branch in LAKEBASE_ENDPOINT. Candidates: ${candidates}`);
|
|
629
|
+
}
|
|
630
|
+
logger.debug("autopg: resolved branch", { project, branch: choice });
|
|
631
|
+
return choice;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Pick the primary endpoint for a (project, branch). Prefers
|
|
635
|
+
* `status.endpoint_type === ENDPOINT_TYPE_READ_WRITE`; falls back to
|
|
636
|
+
* the only endpoint when there's exactly one. Returns `{ name, host }`
|
|
637
|
+
* so the caller can populate both `LAKEBASE_ENDPOINT` and `PGHOST`
|
|
638
|
+
* from a single call.
|
|
639
|
+
*/
|
|
640
|
+
async function pickEndpoint(ws, project, branch, signal) {
|
|
641
|
+
const endpoints = await listEndpoints(ws, project, branch, signal);
|
|
642
|
+
if (endpoints.length === 0) {
|
|
643
|
+
throw ConfigurationError.resourceNotFound(`Lakebase endpoint in 'projects/${project}/branches/${branch}'`, "The branch has no endpoints; create one or set LAKEBASE_ENDPOINT.");
|
|
644
|
+
}
|
|
645
|
+
const primary = endpoints.find((e) => e.status?.endpoint_type === READ_WRITE_ENDPOINT_TYPE) ??
|
|
646
|
+
(endpoints.length === 1 ? endpoints[0] : undefined);
|
|
647
|
+
if (!primary?.name) {
|
|
648
|
+
const names = endpoints.map((e) => e.name).filter(Boolean);
|
|
649
|
+
throw ConfigurationError.invalidConnection("Lakebase", `Branch has no primary read-write endpoint; set LAKEBASE_ENDPOINT or config.endpoint. Candidates: ${names.join(", ")}`);
|
|
650
|
+
}
|
|
651
|
+
const host = primary.status?.hosts?.host;
|
|
652
|
+
logger.debug("autopg: resolved endpoint", { endpoint: primary.name, host });
|
|
653
|
+
return { name: primary.name, host };
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Pick the default postgres database for a (project, branch). The
|
|
657
|
+
* Postgres database NAME (`status.postgres_database`) is what
|
|
658
|
+
* `PGDATABASE` needs - this differs from the resource id, which can
|
|
659
|
+
* use a different separator (e.g. resource `databricks-postgres`
|
|
660
|
+
* surfaces as database `databricks_postgres`). Prefers
|
|
661
|
+
* `databricks_postgres` (the Lakebase default), otherwise the only
|
|
662
|
+
* database.
|
|
663
|
+
*/
|
|
664
|
+
async function pickDatabase(ws, project, branch, signal) {
|
|
665
|
+
const databases = await listDatabases(ws, project, branch, signal);
|
|
666
|
+
if (databases.length === 0) {
|
|
667
|
+
throw ConfigurationError.resourceNotFound(`Lakebase database in 'projects/${project}/branches/${branch}'`, "The branch has no databases; create one or set PGDATABASE.");
|
|
668
|
+
}
|
|
669
|
+
const names = databases
|
|
670
|
+
.map((d) => d.status?.postgres_database)
|
|
671
|
+
.filter((n) => Boolean(n));
|
|
672
|
+
const choice = names.find((n) => n === DEFAULT_DATABASE) ?? (names.length === 1 ? names[0] : undefined);
|
|
673
|
+
if (!choice) {
|
|
674
|
+
throw ConfigurationError.invalidConnection("Lakebase", `Multiple databases and no '${DEFAULT_DATABASE}'; set PGDATABASE or config.database. Candidates: ${names.join(", ")}`);
|
|
675
|
+
}
|
|
676
|
+
logger.debug("autopg: resolved database", { database: choice });
|
|
677
|
+
return choice;
|
|
678
|
+
}
|
|
679
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGFrZWJhc2UtcmVzb2x2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbGFrZWJhc2UtcmVzb2x2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBbUNHO0FBRUgsT0FBTyxFQUNMLGtCQUFrQixFQUNsQixjQUFjLEVBQ2Qsa0JBQWtCLEVBQ2xCLGVBQWUsR0FDaEIsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFDeEIsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sVUFBVSxDQUFDO0FBRTlDLE9BQU8sRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ3ZELE9BQU8sRUFDTCxZQUFZLEVBQ1osaUJBQWlCLEVBQ2pCLFNBQVMsR0FHVixNQUFNLGFBQWEsQ0FBQztBQUVyQixNQUFNLE1BQU0sR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLG1CQUFtQixDQUFDLENBQUM7QUFDL0MsTUFBTSxRQUFRLEdBQUcsbUJBQW1CLENBQUM7QUFDckMsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDO0FBQzFCLE1BQU0sZ0JBQWdCLEdBQVksU0FBUyxDQUFDO0FBQzVDLE1BQU0sa0JBQWtCLEdBQUcsRUFBRSxDQUFDO0FBQzlCLDhEQUE4RDtBQUM5RCxNQUFNLGtCQUFrQixHQUFHLEVBQUUsQ0FBQztBQUM5QixNQUFNLG9CQUFvQixHQUFHLENBQUMsR0FBRyxNQUFNLENBQUM7QUFDeEMsTUFBTSxpQkFBaUIsR0FBRyxLQUFLLENBQUM7QUFDaEMsTUFBTSx5QkFBeUIsR0FBRyxD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b25uZWN0aW9uIHJlc29sdmVyLlxuICpcbiAqIFJlYWRzIHRoZSBzYW1lIGVudiB2YXJzIHRoZSBgbGFrZWJhc2VgIHBsdWdpbiBjb25zdW1lcyAoYFBHSE9TVGAsXG4gKiBgUEdEQVRBQkFTRWAsIGBQR1BPUlRgLCBgUEdTU0xNT0RFYCwgYExBS0VCQVNFX0VORFBPSU5UYCkgYW5kIGZpbGxzIGluXG4gKiB3aGljaGV2ZXIgcGllY2VzIGFyZSBtaXNzaW5nIHVzaW5nIHRoZSBMYWtlYmFzZSBBdXRvc2NhbGluZyBSRVNUIEFQSVxuICogdW5kZXIgYC9hcGkvMi4wL3Bvc3RncmVzL2AgdmlhIHRoZSBEYXRhYnJpY2tzIHdvcmtzcGFjZSBjbGllbnQuXG4gKlxuICogYExBS0VCQVNFX0VORFBPSU5UYCAoYW5kIGBjb25maWcuZW5kcG9pbnRgKSBhY2NlcHQgYW55dGhpbmdcbiAqIHtAbGluayBwYXJzZUFkZHJlc3N9IHVuZGVyc3RhbmRzIC0gY2Fub25pY2FsIHJlc291cmNlIHBhdGhzLCBQb3N0Z3Jlc1xuICogVVJJcywgYmFyZSBob3N0bmFtZXMsIG9yIGJhcmUgcHJvamVjdCBpZHMuIFRoZSByZXNvbHZlciBsYXllcnNcbiAqIHdoYXRldmVyIHBpZWNlcyBmYWxsIG91dCBvZiBwYXJzaW5nIHVuZGVyIGV4cGxpY2l0IGNvbmZpZyAvIGVudlxuICogdmFsdWVzLCB0aGVuIGZpbGxzIHRoZSByZW1haW5pbmcgZ2FwcyB2aWEgdGhlIEFQSTpcbiAqXG4gKiAgIDEuIFJldmVyc2UtbG9va3VwOiB3aGVuIGEgaG9zdCBpcyBrbm93biBidXQgbm8gcmVzb3VyY2UgcGF0aCBpcyxcbiAqICAgICAgc2NhbiBwcm9qZWN0cyAtPiBicmFuY2hlcyAtPiBlbmRwb2ludHMgZm9yIGEgbWF0Y2hpbmdcbiAqICAgICAgYHN0YXR1cy5ob3N0cy5ob3N0YCBhbmQgcmVjb3ZlciB0aGUgb3duaW5nIHByb2plY3QvYnJhbmNoL2VuZHBvaW50LlxuICogICAyLiBQaWNrOiB3aGVuIGEgcHJvamVjdCBpcyBrbm93biBidXQgY2hpbGQgcmVzb3VyY2VzIGFyZW4ndCwgcHJlZmVyXG4gKiAgICAgIHRoZSBzZXJ2ZXItc2lkZSBkZWZhdWx0IChgc3RhdHVzLmRlZmF1bHRgLCBgRU5EUE9JTlRfVFlQRV9SRUFEX1dSSVRFYCxcbiAqICAgICAgYGRhdGFicmlja3NfcG9zdGdyZXNgKSBhbmQgZmFsbCBiYWNrIHRvIFwidGhlIG9ubHkgb25lXCIgd2hlbiBhXG4gKiAgICAgIGxpc3RpbmcgcmV0dXJucyBhIHNpbmdsZSByZXN1bHQuXG4gKiAgIDMuIEF1dG8tY3JlYXRlOiB3aGVuIG5vIHByb2plY3RzIGV4aXN0IGF0IGFsbCwgY3JlYXRlIG9uZSB3aG9zZVxuICogICAgICBpZCBkZWZhdWx0cyB0byBgcHJvamVjdC5uYW1lKClgIHNsdWdpZmllZCAob3ZlcnJpZGVcbiAqICAgICAgd2l0aCBgY29uZmlnLmF1dG9DcmVhdGU6IFwibXktaWRcImAgb3IgZGlzYWJsZSB3aXRoXG4gKiAgICAgIGBjb25maWcuYXV0b0NyZWF0ZTogZmFsc2VgKS4gVGhlIGNyZWF0ZSBjYWxsIGlzIGlkZW1wb3RlbnQgLSBhblxuICogICAgICBgQUxSRUFEWV9FWElTVFNgIHJlc3BvbnNlIGZyb20gYSBjb25jdXJyZW50IGJvb3QgaXMgdHJlYXRlZCBhc1xuICogICAgICBzdWNjZXNzLiBUaGVuIHBvbGwgdGhlIGRlZmF1bHQgZW5kcG9pbnQgdW50aWwgaXQgcmVwb3J0c1xuICogICAgICBgY3VycmVudF9zdGF0ZWAgYFJFQURZYCBvciBgSURMRWAuXG4gKlxuICoge0BsaW5rIGFwcGx5TGFrZWJhc2VUb0Vudn0gd3JpdGVzIHRoZSByZXNvbHZlZCB2YWx1ZXMgYmFjayB0b1xuICogYHByb2Nlc3MuZW52YCBzbyB0aGUgZG93bnN0cmVhbSBgbGFrZWJhc2VgIHBsdWdpbiBwaWNrcyB0aGVtIHVwLlxuICpcbiAqIEBzZWUgaHR0cHM6Ly9kb2NzLmRhdGFicmlja3MuY29tL2FwaS93b3Jrc3BhY2UvcG9zdGdyZXNcbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHtcbiAgQ29uZmlndXJhdGlvbkVycm9yLFxuICBFeGVjdXRpb25FcnJvcixcbiAgZ2V0V29ya3NwYWNlQ2xpZW50LFxuICBWYWxpZGF0aW9uRXJyb3IsXG59IGZyb20gXCJAZGF0YWJyaWNrcy9hcHBraXRcIjtcbmltcG9ydCB7IHByb2plY3QgfSBmcm9tIFwiQGRieC10b29scy9jb3JlXCI7XG5pbXBvcnQgeyBhc3luYywgbG9nLCBvYmplY3QsIHN0cmluZyB9IGZyb20gXCJAZGJ4LXRvb2xzL3NoYXJlZC1jb3JlXCI7XG5pbXBvcnQgeyB6IH0gZnJvbSBcInpvZFwiO1xuaW1wb3J0IHsgcmVzb2x2ZUNvbmZpZ1ZhbHVlIH0gZnJvbSBcIi4vY29uZmlnXCI7XG5cbmltcG9ydCB7IE1BWF9UQ1BfUE9SVCwgdG9Db250ZXh0IH0gZnJvbSBcIi4vZGF0YWJyaWNrc1wiO1xuaW1wb3J0IHtcbiAgcGFyc2VBZGRyZXNzLFxuICBwYXJzZVJlc291cmNlUGF0aCxcbiAgU1NMX01PREVTLFxuICB0eXBlIExha2ViYXNlQ29ubmVjdGlvbklucHV0cyxcbiAgdHlwZSBTc2xNb2RlLFxufSBmcm9tIFwiLi9wZ2FkZHJlc3NcIjtcblxuY29uc3QgbG9nZ2VyID0gbG9nLmxvZ2dlcihcImxha2ViYXNlLXJlc29sdmVyXCIpO1xuY29uc3QgQVBJX0JBU0UgPSBcIi9hcGkvMi4wL3Bvc3RncmVzXCI7XG5jb25zdCBERUZBVUxUX1BPUlQgPSA1NDMyO1xuY29uc3QgREVGQVVMVF9TU0xfTU9ERTogU3NsTW9kZSA9IFwicmVxdWlyZVwiO1xuY29uc3QgREVGQVVMVF9QR19WRVJTSU9OID0gMTc7XG4vKiogTGFrZWJhc2UgcHJvamVjdCBpZHM6IGBeW2Etel1bYS16MC05LV17MCw2MX1bYS16MC05XSRgLiAqL1xuY29uc3QgUFJPSkVDVF9JRF9NQVhfTEVOID0gNjM7XG5jb25zdCBPUEVSQVRJT05fVElNRU9VVF9NUyA9IDUgKiA2MF8wMDA7XG5jb25zdCBPUEVSQVRJT05fUE9MTF9NUyA9IDJfMDAwO1xuY29uc3QgRU5EUE9JTlRfUkVBRFlfVElNRU9VVF9NUyA9IDUgKiA2MF8wMDA7XG5jb25zdCBFTkRQT0lOVF9SRUFEWV9QT0xMX01TID0gMl8wMDA7XG4vKiogQ2VpbGluZyBvbiBhIHNpbmdsZSBiYWNrb2ZmIHN0ZXAsIHNvIGEgbG9uZyB3YWl0IHN0aWxsIHBvbGxzIHJlZ3VsYXJseS4gKi9cbmNvbnN0IFBPTExfTUFYX0RFTEFZX01TID0gMTVfMDAwO1xuY29uc3QgUE9MTF9CQUNLT0ZGX0ZBQ1RPUiA9IDEuNTtcbi8qKiBGcmFjdGlvbiBvZiBlYWNoIGRlbGF5IGFwcGxpZWQgYXMgKy8tIGppdHRlciwgc28gY29uY3VycmVudCBib290cyBkZXN5bmNocm9uaXplLiAqL1xuY29uc3QgUE9MTF9KSVRURVJfUkFUSU8gPSAwLjI7XG4vKiogRW5kcG9pbnQgYHN0YXR1cy5lbmRwb2ludF90eXBlYCBmb3IgdGhlIHdyaXRhYmxlIHByaW1hcnkuICovXG5jb25zdCBSRUFEX1dSSVRFX0VORFBPSU5UX1RZUEUgPSBcIkVORFBPSU5UX1RZUEVfUkVBRF9XUklURVwiO1xuLyoqIEVuZHBvaW50IGBzdGF0dXMuY3VycmVudF9zdGF0ZWAgdmFsdWVzIHRoYXQgd2lsbCBhY2NlcHQgYSBjb25uZWN0aW9uLiAqL1xuY29uc3QgQ09OTkVDVEFCTEVfRU5EUE9JTlRfU1RBVEVTID0gbmV3IFNldChbXCJSRUFEWVwiLCBcIklETEVcIl0pO1xuLyoqIEVuZHBvaW50IGBzdGF0dXMuY3VycmVudF9zdGF0ZWAgYmVmb3JlIGEgaG9zdG5hbWUgaXMgbWVhbmluZ2Z1bC4gKi9cbmNvbnN0IElOSVRJQUxJWklOR19FTkRQT0lOVF9TVEFURSA9IFwiSU5JVElBTElaSU5HXCI7XG4vKiogTGFrZWJhc2UncyBvd24gZGVmYXVsdCBQb3N0Z3JlcyBkYXRhYmFzZSBuYW1lLiAqL1xuY29uc3QgREVGQVVMVF9EQVRBQkFTRSA9IFwiZGF0YWJyaWNrc19wb3N0Z3Jlc1wiO1xuXG4vKipcbiAqIFVzZXItc3VwcGxpZWQgTGFrZWJhc2UgaW5wdXRzIChjb25maWcgb3IgZW52KSBiZWZvcmUgYW55IEFQSSByZXNvbHV0aW9uLlxuICogRXh0ZW5kcyB7QGxpbmsgTGFrZWJhc2VDb25uZWN0aW9uSW5wdXRzfSB3aXRoIHJlc29sdmVyLW9ubHkgb3B0aW9ucy5cbiAqIHtAbGluayByZXNvbHZlTGFrZWJhc2VDb25uZWN0aW9ufSBmaWxscyBnYXBzIGZyb20gdGhlIExha2ViYXNlIEFQSSB3aGVuXG4gKiBpdCBoYXMgZW5vdWdoIGNvbnRleHQgKHR5cGljYWxseSBhIGBwcm9qZWN0YCkuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTGFrZWJhc2VSZXNvbHZlcklucHV0cyBleHRlbmRzIExha2ViYXNlQ29ubmVjdGlvbklucHV0cyB7XG4gIC8qKlxuICAgKiBXaGF0IHRvIGRvIHdoZW4gbm8gcHJvamVjdCBleGlzdHMgaW4gdGhlIHdvcmtzcGFjZSBhdCBhbGwuXG4gICAqIC0gYHVuZGVmaW5lZGAgKGRlZmF1bHQpOiBkZXJpdmUgYSBwcm9qZWN0IGlkIGZyb21cbiAgICogICB7QGxpbmsgcHJvamVjdC5uYW1lfSAodGhlIGhvc3QgcmVwbydzIGBwYWNrYWdlLmpzb25gXG4gICAqICAgbmFtZSkgc2x1Z2lmaWVkIHRvIExha2ViYXNlIGlkIGNvbnN0cmFpbnRzLCB0aGVuIGNyZWF0ZSBpdC5cbiAgICogLSBgc3RyaW5nYDogY3JlYXRlIGEgbmV3IHByb2plY3Qgd2l0aCB0aGlzIGV4YWN0IGlkLlxuICAgKiAtIGBmYWxzZWA6IHNraXAgY3JlYXRpb24gYW5kIHRocm93IHdpdGggYSBjbGVhciBlcnJvciBtZXNzYWdlLlxuICAgKi9cbiAgYXV0b0NyZWF0ZT86IHN0cmluZyB8IGZhbHNlO1xufVxuXG4vKiogRnVsbHktcmVzb2x2ZWQgTGFrZWJhc2UgUG9zdGdyZXMgY29ubmVjdGlvbi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTGFrZWJhc2VDb25uZWN0aW9uIGV4dGVuZHMgTGFrZWJhc2VDb25uZWN0aW9uSW5wdXRzIHtcbiAgcG9ydDogbnVtYmVyO1xuICBzc2xNb2RlOiBTc2xNb2RlO1xufVxuXG4vKipcbiAqIEV2ZXJ5IGZpZWxkIG9mIHRoZSBMYWtlYmFzZSBSRVNUIHBheWxvYWRzIGlzIG1vZGVsbGVkIGFzIG9wdGlvbmFsIGFuZFxuICogdmFsaWRhdGVkIGF0IHRoZSBib3VuZGFyeSByYXRoZXIgdGhhbiBhc3NlcnRlZCB3aXRoIGEgY2FzdDogdGhlIHBpY2tlcnMgYmVsb3dcbiAqIGJyYW5jaCBvbiBgc3RhdHVzLipgLCBhbmQgYSBzaGFwZSBjaGFuZ2UgdXBzdHJlYW0gc2hvdWxkIHN1cmZhY2UgYXMgb25lIGxvdWRcbiAqIGVycm9yIGhlcmUgaW5zdGVhZCBvZiBhbiBgdW5kZWZpbmVkYCB0aHJlZSBmcmFtZXMgYXdheS4gRW51bS1zaGFwZWQgZmllbGRzXG4gKiBzdGF5IGBzdHJpbmdgIHNvIGEgbmV3bHkgYWRkZWQgc3RhdGUgZG9lcyBub3QgZmFpbCB0aGUgd2hvbGUgcmVzcG9uc2UuXG4gKi9cbmNvbnN0IHByb2plY3RTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIC8vIEZ1bGwgcmVzb3VyY2UgcGF0aDogYHByb2plY3RzL3twfWAuXG4gIG5hbWU6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbn0pO1xudHlwZSBQcm9qZWN0ID0gei5pbmZlcjx0eXBlb2YgcHJvamVjdFNjaGVtYT47XG5cbmNvbnN0IGVuZHBvaW50U2NoZW1hID0gei5vYmplY3Qoe1xuICAvLyBGdWxsIHJlc291cmNlIHBhdGg6IGBwcm9qZWN0cy97cH0vYnJhbmNoZXMve2J9L2VuZHBvaW50cy97ZX1gLlxuICBuYW1lOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gIHVpZDogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICAvLyBTZXJ2ZXItc2lkZSBzdGF0ZS4gQWxsIGNvbm5lY3Rpb24gaW5mbyBsaXZlcyBoZXJlIC0gdGhlIHNwZWMgYmxvY2tcbiAgLy8gb25seSBjYXJyaWVzIHRoZSBkZXNpcmVkIGNvbmZpZ3VyYXRpb24sIG5vdCB0aGUgcnVudGltZSBob3N0bmFtZXMuXG4gIHN0YXR1czogelxuICAgIC5vYmplY3Qoe1xuICAgICAgZW5kcG9pbnRfdHlwZTogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICAgICAgLy8gUmVzb2x2ZWQgaG9zdG5hbWVzOyBgaG9zdHMuaG9zdGAgaXMgdGhlIHdyaXRhYmxlIHByaW1hcnkuXG4gICAgICBob3N0czogelxuICAgICAgICAub2JqZWN0KHtcbiAgICAgICAgICBob3N0OiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gICAgICAgICAgcmVhZF9vbmx5X2hvc3Q6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgICAgICAgfSlcbiAgICAgICAgLm9wdGlvbmFsKCksXG4gICAgICAvLyBDb21wdXRlIHN0YXRlOiBgSU5JVElBTElaSU5HYCwgYFNUQVJUSU5HYCwgYFJFQURZYCwgYElETEVgLCAuLi5cbiAgICAgIGN1cnJlbnRfc3RhdGU6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgICB9KVxuICAgIC5vcHRpb25hbCgpLFxufSk7XG50eXBlIEVuZHBvaW50ID0gei5pbmZlcjx0eXBlb2YgZW5kcG9pbnRTY2hlbWE+O1xuXG5jb25zdCBicmFuY2hTY2hlbWEgPSB6Lm9iamVjdCh7XG4gIC8vIEZ1bGwgcmVzb3VyY2UgcGF0aDogYHByb2plY3RzL3twfS9icmFuY2hlcy97Yn1gLlxuICBuYW1lOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gIHN0YXR1czogelxuICAgIC5vYmplY3Qoe1xuICAgICAgLy8gVHJ1ZSBmb3IgdGhlIHByb2plY3QncyBkZWZhdWx0IGJyYW5jaCAoZS5nLiBgcHJvZHVjdGlvbmApLlxuICAgICAgZGVmYXVsdDogei5ib29sZWFuKCkub3B0aW9uYWwoKSxcbiAgICAgIGN1cnJlbnRfc3RhdGU6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgICB9KVxuICAgIC5vcHRpb25hbCgpLFxufSk7XG50eXBlIEJyYW5jaCA9IHouaW5mZXI8dHlwZW9mIGJyYW5jaFNjaGVtYT47XG5cbmNvbnN0IGRhdGFiYXNlU2NoZW1hID0gei5vYmplY3Qoe1xuICAvLyBGdWxsIHJlc291cmNlIHBhdGg6IGBwcm9qZWN0cy97cH0vYnJhbmNoZXMve2J9L2RhdGFiYXNlcy97ZH1gLlxuICBuYW1lOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gIHN0YXR1czogelxuICAgIC5vYmplY3Qoe1xuICAgICAgLy8gQWN0dWFsIFBvc3RncmVzIGRhdGFiYXNlIG5hbWUgKHVzZWQgYXMgYFBHREFUQUJBU0VgKS4gTWF5IGRpZmZlclxuICAgICAgLy8gZnJvbSB0aGUgcmVzb3VyY2UgaWQgLSBlLmcuIHJlc291cmNlIGBkYXRhYnJpY2tzLXBvc3RncmVzYFxuICAgICAgLy8gc3VyZmFjZXMgYXMgUG9zdGdyZXMgZGF0YWJhc2UgYGRhdGFicmlja3NfcG9zdGdyZXNgLlxuICAgICAgcG9zdGdyZXNfZGF0YWJhc2U6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgICB9KVxuICAgIC5vcHRpb25hbCgpLFxufSk7XG50eXBlIERhdGFiYXNlID0gei5pbmZlcjx0eXBlb2YgZGF0YWJhc2VTY2hlbWE+O1xuXG4vKipcbiAqIExha2ViYXNlIFJFU1QgbGlzdCByZXNwb25zZXMgZm9sbG93IHRoZSBHb29nbGUgQUlQIGNvbnZlbnRpb246XG4gKiBgeyA8cGx1cmFsLXJlc291cmNlPjogVFtdLCBuZXh0X3BhZ2VfdG9rZW4/OiBzdHJpbmcgfWAuIFdlIG9ubHkgcmVhZFxuICogdGhlIGZpcnN0IHBhZ2U7IGZvciBhdXRvLWNvbmZpZydzIFwicGljayBzb21ldGhpbmcgc2Vuc2libGVcIiBzZW1hbnRpY3MgdGhlXG4gKiBjYXAgaXMgZmluZS5cbiAqL1xuY29uc3QgbGlzdFJlc3BvbnNlU2NoZW1hID0gei5vYmplY3Qoe1xuICBuZXh0X3BhZ2VfdG9rZW46IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgcHJvamVjdHM6IHouYXJyYXkocHJvamVjdFNjaGVtYSkub3B0aW9uYWwoKSxcbiAgYnJhbmNoZXM6IHouYXJyYXkoYnJhbmNoU2NoZW1hKS5vcHRpb25hbCgpLFxuICBlbmRwb2ludHM6IHouYXJyYXkoZW5kcG9pbnRTY2hlbWEpLm9wdGlvbmFsKCksXG4gIGRhdGFiYXNlczogei5hcnJheShkYXRhYmFzZVNjaGVtYSkub3B0aW9uYWwoKSxcbn0pO1xuXG4vKipcbiAqIExvbmctcnVubmluZyBvcGVyYXRpb24gZW52ZWxvcGUgcmV0dXJuZWQgYnkgbXV0YXRpbmcgUkVTVCBjYWxscy5cbiAqIGBkb25lOiB0cnVlYCBtZWFucyB0ZXJtaW5hbDsgY2hlY2sgYGVycm9yYCBiZWZvcmUgcmVhZGluZyBgcmVzcG9uc2VgLlxuICovXG5jb25zdCBvcGVyYXRpb25TY2hlbWEgPSB6Lm9iamVjdCh7XG4gIG5hbWU6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgZG9uZTogei5ib29sZWFuKCkub3B0aW9uYWwoKSxcbiAgZXJyb3I6IHoudW5rbm93bigpLm9wdGlvbmFsKCksXG4gIHJlc3BvbnNlOiB6LnVua25vd24oKS5vcHRpb25hbCgpLFxufSk7XG50eXBlIE9wZXJhdGlvbiA9IHouaW5mZXI8dHlwZW9mIG9wZXJhdGlvblNjaGVtYT47XG5cbi8qKiBgUEdQT1JUYCBtdXN0IGxhbmQgaW5zaWRlIHRoZSBUQ1AgcG9ydCByYW5nZS4gKi9cbmNvbnN0IHBvcnRTY2hlbWEgPSB6LmNvZXJjZS5udW1iZXIoKS5pbnQoKS5taW4oMSkubWF4KE1BWF9UQ1BfUE9SVCk7XG5cbmNvbnN0IHNzbE1vZGVTY2hlbWEgPSB6LmVudW0oU1NMX01PREVTKTtcblxuLyoqXG4gKiBWYWxpZGF0ZSBhIGBQR1BPUlRgLXNoYXBlZCB2YWx1ZS4gUmV0dXJucyBgdW5kZWZpbmVkYCB3aGVuIHVuc2V0LCBhbmQgdGhyb3dzXG4gKiBhIHtAbGluayBWYWxpZGF0aW9uRXJyb3J9IG5hbWluZyBgUEdQT1JUYCBmb3IgYW55dGhpbmcgdGhhdCBpcyBub3QgYSBUQ1BcbiAqIHBvcnQsIHNvIGEgdHlwbyBjYW5ub3QgcmVhY2ggdGhlIGNvbm5lY3Rpb24gcmVjb3JkIGFzIGBOYU5gLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VQb3J0KHZhbHVlOiBzdHJpbmcgfCBudW1iZXIgfCB1bmRlZmluZWQpOiBudW1iZXIgfCB1bmRlZmluZWQge1xuICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCB8fCB2YWx1ZSA9PT0gXCJcIikgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3QgcGFyc2VkID0gcG9ydFNjaGVtYS5zYWZlUGFyc2UodmFsdWUpO1xuICBpZiAoIXBhcnNlZC5zdWNjZXNzKSB7XG4gICAgdGhyb3cgVmFsaWRhdGlvbkVycm9yLmludmFsaWRWYWx1ZShcIlBHUE9SVFwiLCB2YWx1ZSwgYGEgVENQIHBvcnQgYmV0d2VlbiAxIGFuZCAke01BWF9UQ1BfUE9SVH1gKTtcbiAgfVxuICByZXR1cm4gcGFyc2VkLmRhdGE7XG59XG5cbi8qKlxuICogVmFsaWRhdGUgYSBgUEdTU0xNT0RFYC1zaGFwZWQgdmFsdWUuIFJldHVybnMgYHVuZGVmaW5lZGAgd2hlbiB1bnNldCwgYW5kXG4gKiB0aHJvd3MgYSB7QGxpbmsgVmFsaWRhdGlvbkVycm9yfSBuYW1pbmcgYFBHU1NMTU9ERWAgZm9yIGFuIHVuc3VwcG9ydGVkIG1vZGVcbiAqIHJhdGhlciB0aGFuIGhhbmRpbmcgYHBnYCBhIHZhbHVlIGl0IHdpbGwgcmVqZWN0IGF0IGNvbm5lY3QgdGltZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlU3NsTW9kZSh2YWx1ZTogc3RyaW5nIHwgdW5kZWZpbmVkKTogU3NsTW9kZSB8IHVuZGVmaW5lZCB7XG4gIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IHZhbHVlID09PSBcIlwiKSByZXR1cm4gdW5kZWZpbmVkO1xuICBjb25zdCBwYXJzZWQgPSBzc2xNb2RlU2NoZW1hLnNhZmVQYXJzZSh2YWx1ZS50cmltKCkudG9Mb3dlckNhc2UoKSk7XG4gIGlmICghcGFyc2VkLnN1Y2Nlc3MpIHtcbiAgICB0aHJvdyBWYWxpZGF0aW9uRXJyb3IuaW52YWxpZFZhbHVlKFwiUEdTU0xNT0RFXCIsIHZhbHVlLCBTU0xfTU9ERVMuam9pbihcIiwgXCIpKTtcbiAgfVxuICByZXR1cm4gcGFyc2VkLmRhdGE7XG59XG5cbi8qKlxuICogRGVsYXkgYmVmb3JlIHRoZSBuZXh0IHBvbGwgYXR0ZW1wdDogZXhwb25lbnRpYWwgYmFja29mZiBjYXBwZWQgYXRcbiAqIHtAbGluayBQT0xMX01BWF9ERUxBWV9NU30sIHBsdXMgaml0dGVyIHNvIHNldmVyYWwgYXBwcyBib290aW5nIGFnYWluc3QgdGhlXG4gKiBzYW1lIHdvcmtzcGFjZSBkbyBub3QgcmV0cnkgaW4gbG9ja3N0ZXAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBuZXh0UG9sbERlbGF5KGF0dGVtcHQ6IG51bWJlciwgYmFzZU1zOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBiYWNrb2ZmID0gTWF0aC5taW4oYmFzZU1zICogUE9MTF9CQUNLT0ZGX0ZBQ1RPUiAqKiBhdHRlbXB0LCBQT0xMX01BWF9ERUxBWV9NUyk7XG4gIGNvbnN0IGppdHRlciA9IGJhY2tvZmYgKiBQT0xMX0pJVFRFUl9SQVRJTyAqIChNYXRoLnJhbmRvbSgpICogMiAtIDEpO1xuICByZXR1cm4gTWF0aC5tYXgoMCwgTWF0aC5yb3VuZChiYWNrb2ZmICsgaml0dGVyKSk7XG59XG5cbi8qKlxuICogV2FpdCBvdXQge0BsaW5rIG5leHRQb2xsRGVsYXl9IGJlZm9yZSB0aGUgbmV4dCBhdHRlbXB0LiBSZWplY3RzIHdpdGggdGhlXG4gKiBzaWduYWwncyByZWFzb24gd2hlbiB0aGUgY2FsbGVyIGNhbmNlbHMgbWlkLXdhaXQsIHNvIGEgcG9sbCBsb29wIHVud2luZHNcbiAqIGluc3RlYWQgb2YgZmluaXNoaW5nIGl0cyBiYWNrb2ZmIGZpcnN0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gcG9sbERlbGF5KGF0dGVtcHQ6IG51bWJlciwgYmFzZU1zOiBudW1iZXIsIHNpZ25hbD86IEFib3J0U2lnbmFsKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBhc3luYy5zbGVlcChuZXh0UG9sbERlbGF5KGF0dGVtcHQsIGJhc2VNcyksIHNpZ25hbCk7XG59XG5cbi8qKlxuICogUHVsbCByZXNvbHZlciBpbnB1dHMgZnJvbSBgcHJvY2Vzcy5lbnZgLCBwYXJzZSB0aGUgYWRkcmVzcyBibG9iLCBhbmRcbiAqIGxheWVyIGV4cGxpY2l0IGNvbmZpZyBvbiB0b3Agd2l0aCB0aGlzIHByZWNlZGVuY2U6XG4gKlxuICogICBgY29uZmlnLjxmaWVsZD5gID4gYGNvbmZpZy5yZXNvbHZlQ29uZmlnVmFsdWVgIChgZW52YCwgdGhlbiBidW5kbGVcbiAqICAgdmFsaWRhdGUgSlNPTikgPiB3aGF0ZXZlciB7QGxpbmsgcGFyc2VBZGRyZXNzfSByZWNvdmVyZWQgZnJvbSB0aGVcbiAqICAgYGVuZHBvaW50YCAvIGBMQUtFQkFTRV9FTkRQT0lOVGAgYmxvYi5cbiAqXG4gKiBTZXQgYGNvbmZpZy5lbmRwb2ludGAgKG9yIGBMQUtFQkFTRV9FTkRQT0lOVGApIHRvIGFueSBpbnB1dFxuICoge0BsaW5rIHBhcnNlQWRkcmVzc30gdW5kZXJzdGFuZHM6IGNhbm9uaWNhbCByZXNvdXJjZSBwYXRocywgUG9zdGdyZXNcbiAqIFVSSXMsIGJhcmUgaG9zdG5hbWVzLCBvciBiYXJlIHByb2plY3QgaWRzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVhZExha2ViYXNlSW5wdXRzKFxuICBjb25maWc/OiBMYWtlYmFzZVJlc29sdmVySW5wdXRzLFxuKTogUHJvbWlzZTxMYWtlYmFzZVJlc29sdmVySW5wdXRzPiB7XG4gIGNvbnN0IHJhd0FkZHJlc3MgPSBjb25maWc/LmVuZHBvaW50ID8/IChhd2FpdCByZXNvbHZlQ29uZmlnVmFsdWUoXCJMQUtFQkFTRV9FTkRQT0lOVFwiKSk7XG4gIGNvbnN0IHBhcnNlZCA9IHBhcnNlQWRkcmVzcyhyYXdBZGRyZXNzKTtcbiAgY29uc3QgcG9ydEVudiA9IHBhcnNlUG9ydChhd2FpdCByZXNvbHZlQ29uZmlnVmFsdWUoXCJQR1BPUlRcIikpO1xuICBjb25zdCBzc2xNb2RlRW52ID0gcGFyc2VTc2xNb2RlKGF3YWl0IHJlc29sdmVDb25maWdWYWx1ZShcIlBHU1NMTU9ERVwiKSk7XG4gIHJldHVybiB7XG4gICAgcHJvamVjdDogY29uZmlnPy5wcm9qZWN0ID8/IHBhcnNlZC5wcm9qZWN0LFxuICAgIGJyYW5jaDogY29uZmlnPy5icmFuY2ggPz8gcGFyc2VkLmJyYW5jaCxcbiAgICAvLyBPbmx5IGNhbm9uaWNhbCBlbmRwb2ludCByZXNvdXJjZSBwYXRocyBzdXJ2aXZlIGhlcmU7IFVSSXMgYW5kXG4gICAgLy8gYmFyZSBob3N0bmFtZXMgc2V0IGBob3N0YCBpbnN0ZWFkIGFuZCBsZWF2ZSBgZW5kcG9pbnRgIHVuZGVmaW5lZFxuICAgIC8vIHVudGlsIHRoZSBSRVNUIHJlc29sdmVyIGZpbGxzIGl0IGluLlxuICAgIGVuZHBvaW50OiBwYXJzZWQuZW5kcG9pbnQsXG4gICAgZGF0YWJhc2U6IGNvbmZpZz8uZGF0YWJhc2UgPz8gKGF3YWl0IHJlc29sdmVDb25maWdWYWx1ZShcIlBHREFUQUJBU0VcIikpID8/IHBhcnNlZC5kYXRhYmFzZSxcbiAgICBob3N0OiBjb25maWc/Lmhvc3QgPz8gKGF3YWl0IHJlc29sdmVDb25maWdWYWx1ZShcIlBHSE9TVFwiKSkgPz8gcGFyc2VkLmhvc3QsXG4gICAgcG9ydDogY29uZmlnPy5wb3J0ID8/IHBvcnRFbnYgPz8gcGFyc2VkLnBvcnQsXG4gICAgc3NsTW9kZTogY29uZmlnPy5zc2xNb2RlID8/IHNzbE1vZGVFbnYgPz8gcGFyc2VkLnNzbE1vZGUsXG4gICAgYXV0b0NyZWF0ZTogY29uZmlnPy5hdXRvQ3JlYXRlLFxuICB9O1xufVxuXG4vKipcbiAqIFJlc29sdmUgYSBmdWxseS1wb3B1bGF0ZWQgUG9zdGdyZXMgY29ubmVjdGlvbiByZWNvcmQgZnJvbSBjb25maWcgKyBlbnYuXG4gKlxuICogUmV0dXJucyBpbW1lZGlhdGVseSB3aXRob3V0IG5ldHdvcmsgdHJhZmZpYyB3aGVuIGVudiBhbHJlYWR5IHN1cHBsaWVzXG4gKiBgZW5kcG9pbnRgLCBgaG9zdGAsIGFuZCBgZGF0YWJhc2VgLiBPdGhlcndpc2UgaXNzdWVzIFJFU1QgY2FsbHMgYW5kXG4gKiBtYXkgYXV0by1jcmVhdGUgYSBwcm9qZWN0IChzZWUgbW9kdWxlIGRvY3N0cmluZykuXG4gKlxuICogYHNpZ25hbGAgY2FuY2VscyBldmVyeSBSRVNUIGNhbGwgYW5kIGludGVyLXBvbGwgc2xlZXAuXG4gKlxuICogQGV4YW1wbGVcbiAqIGltcG9ydCB7IGxha2ViYXNlUmVzb2x2ZXIgfSBmcm9tIFwiQGRieC10b29scy9hcHBraXRcIjtcbiAqXG4gKiBjb25zdCByZXNvbHZlZCA9IGF3YWl0IGxha2ViYXNlUmVzb2x2ZXIucmVzb2x2ZUxha2ViYXNlQ29ubmVjdGlvbihcbiAqICAgeyBhdXRvQ3JlYXRlOiBmYWxzZSB9LFxuICogICBBYm9ydFNpZ25hbC50aW1lb3V0KDYwXzAwMCksXG4gKiApO1xuICogbGFrZWJhc2VSZXNvbHZlci5hcHBseUxha2ViYXNlVG9FbnYocmVzb2x2ZWQpO1xuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZUxha2ViYXNlQ29ubmVjdGlvbihcbiAgY29uZmlnPzogTGFrZWJhc2VSZXNvbHZlcklucHV0cyxcbiAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPExha2ViYXNlQ29ubmVjdGlvbj4ge1xuICBjb25zdCBpbnB1dHMgPSBhd2FpdCByZWFkTGFrZWJhc2VJbnB1dHMoY29uZmlnKTtcbiAgbGV0IHsgcHJvamVjdCwgYnJhbmNoLCBlbmRwb2ludCwgZGF0YWJhc2UsIGhvc3QgfSA9IGlucHV0cztcbiAgY29uc3QgcG9ydCA9IGlucHV0cy5wb3J0ID8/IERFRkFVTFRfUE9SVDtcbiAgY29uc3Qgc3NsTW9kZSA9IGlucHV0cy5zc2xNb2RlID8/IERFRkFVTFRfU1NMX01PREU7XG5cbiAgLy8gUmVzb3VyY2UgcGF0aHMgbWF5IGNhcnJ5IHJlZHVuZGFudCBpbmZvOyBoYXJ2ZXN0IHByb2plY3QvYnJhbmNoXG4gIC8vIGZyb20gYW55IGNhbm9uaWNhbCBwYXRoIHRoYXQgc251Y2sgaW4gdmlhIFBHREFUQUJBU0Ugb3Igc2ltaWxhci5cbiAgaWYgKGVuZHBvaW50ICYmICghcHJvamVjdCB8fCAhYnJhbmNoKSkge1xuICAgIGNvbnN0IHBhcnNlZEVuZHBvaW50ID0gcGFyc2VBZGRyZXNzKGVuZHBvaW50KTtcbiAgICBwcm9qZWN0ID8/PSBwYXJzZWRFbmRwb2ludC5wcm9qZWN0O1xuICAgIGJyYW5jaCA/Pz0gcGFyc2VkRW5kcG9pbnQuYnJhbmNoO1xuICB9XG4gIGlmIChkYXRhYmFzZSAmJiAoIXByb2plY3QgfHwgIWJyYW5jaCkpIHtcbiAgICBjb25zdCBwYXJzZWREYXRhYmFzZSA9IHBhcnNlQWRkcmVzcyhkYXRhYmFzZSk7XG4gICAgcHJvamVjdCA/Pz0gcGFyc2VkRGF0YWJhc2UucHJvamVjdDtcbiAgICBicmFuY2ggPz89IHBhcnNlZERhdGFiYXNlLmJyYW5jaDtcbiAgICBpZiAocGFyc2VkRGF0YWJhc2UuZGF0YWJhc2VSZXNvdXJjZUlkKSB7XG4gICAgICBkYXRhYmFzZSA9IHBhcnNlZERhdGFiYXNlLmRhdGFiYXNlUmVzb3VyY2VJZDtcbiAgICB9XG4gIH1cblxuICAvLyBBbHJlYWR5IGNvbXBsZXRlOiBza2lwIGV2ZXJ5IFJFU1QgY2FsbC5cbiAgaWYgKGVuZHBvaW50ICYmIGhvc3QgJiYgZGF0YWJhc2UpIHtcbiAgICByZXR1cm4geyBwcm9qZWN0LCBicmFuY2gsIGVuZHBvaW50LCBkYXRhYmFzZSwgaG9zdCwgcG9ydCwgc3NsTW9kZSB9O1xuICB9XG5cbiAgY29uc3Qgd3MgPSBnZXRXb3Jrc3BhY2VDbGllbnQoe30pO1xuXG4gIC8vIEhvc3Qga25vd24gYnV0IG5vIHJlc291cmNlIHBhdGg6IHNjYW4gdGhlIHdvcmtzcGFjZSB0byBmaW5kIHdoaWNoXG4gIC8vIGVuZHBvaW50IG93bnMgdGhpcyBob3N0IHNvIHdlIGNhbiBwb3B1bGF0ZSBMQUtFQkFTRV9FTkRQT0lOVC5cbiAgaWYgKCFwcm9qZWN0ICYmIGhvc3QpIHtcbiAgICBjb25zdCBmb3VuZCA9IGF3YWl0IGZpbmRFbmRwb2ludEJ5SG9zdCh3cywgaG9zdCwgc2lnbmFsKTtcbiAgICBpZiAoZm91bmQpIHtcbiAgICAgIHByb2plY3QgPSBmb3VuZC5wcm9qZWN0O1xuICAgICAgYnJhbmNoID0gZm91bmQuYnJhbmNoO1xuICAgICAgZW5kcG9pbnQgPz89IGZvdW5kLmVuZHBvaW50O1xuICAgIH1cbiAgfVxuXG4gIC8vIE5vIHByb2plY3QgYW55d2hlcmUgaW4gY29uZmlnL2Vudi9hZGRyZXNzOiBsaXN0LCBwaWNrLCBvciBjcmVhdGUuXG4gIGlmICghcHJvamVjdCkge1xuICAgIHByb2plY3QgPSBhd2FpdCBwaWNrT3JDcmVhdGVQcm9qZWN0KHdzLCBjb25maWc/LmF1dG9DcmVhdGUsIHNpZ25hbCk7XG4gIH1cblxuICBpZiAoIWJyYW5jaCkge1xuICAgIGJyYW5jaCA9IGF3YWl0IHBpY2tCcmFuY2god3MsIHByb2plY3QsIHNpZ25hbCk7XG4gIH1cblxuICBpZiAoIWVuZHBvaW50KSB7XG4gICAgY29uc3QgZXAgPSBhd2FpdCBwaWNrRW5kcG9pbnQod3MsIHByb2plY3QsIGJyYW5jaCwgc2lnbmFsKTtcbiAgICBlbmRwb2ludCA9IGVwLm5hbWU7XG4gICAgaG9zdCA/Pz0gZXAuaG9zdDtcbiAgfVxuXG4gIGlmICghaG9zdCAmJiBlbmRwb2ludCkge1xuICAgIGNvbnN0IHBhcnNlZEVuZHBvaW50ID0gcGFyc2VBZGRyZXNzKGVuZHBvaW50KTtcbiAgICBpZiAocGFyc2VkRW5kcG9pbnQucHJvamVjdCAmJiBwYXJzZWRFbmRwb2ludC5icmFuY2ggJiYgcGFyc2VkRW5kcG9pbnQuZW5kcG9pbnRJZCkge1xuICAgICAgY29uc3QgZXAgPSBhd2FpdCB3YWl0RW5kcG9pbnRSZWFkeShcbiAgICAgICAgd3MsXG4gICAgICAgIHBhcnNlZEVuZHBvaW50LnByb2plY3QsXG4gICAgICAgIHBhcnNlZEVuZHBvaW50LmJyYW5jaCxcbiAgICAgICAgcGFyc2VkRW5kcG9pbnQuZW5kcG9pbnRJZCxcbiAgICAgICAgc2lnbmFsLFxuICAgICAgKTtcbiAgICAgIGhvc3QgPSBlcC5zdGF0dXM/Lmhvc3RzPy5ob3N0O1xuICAgICAgbG9nZ2VyLmRlYnVnKFwiYXV0b3BnOiByZXNvbHZlZCBob3N0IGZyb20gZW5kcG9pbnRcIiwgeyBob3N0IH0pO1xuICAgIH1cbiAgfVxuXG4gIGlmICghZGF0YWJhc2UpIHtcbiAgICBkYXRhYmFzZSA9IGF3YWl0IHBpY2tEYXRhYmFzZSh3cywgcHJvamVjdCwgYnJhbmNoLCBzaWduYWwpO1xuICB9XG5cbiAgcmV0dXJuIHsgcHJvamVjdCwgYnJhbmNoLCBlbmRwb2ludCwgZGF0YWJhc2UsIGhvc3QsIHBvcnQsIHNzbE1vZGUgfTtcbn1cblxuLyoqXG4gKiBXcml0ZSByZXNvbHZlZCB2YWx1ZXMgYmFjayB0byBgcHJvY2Vzcy5lbnZgIHNvIHRoZSBgbGFrZWJhc2VgIHBsdWdpblxuICogKHdoaWNoIHJlYWRzIGVudiBkaXJlY3RseSkgcGlja3MgdGhlbSB1cCBkdXJpbmcgaXRzIG93biBgc2V0dXAoKWAuXG4gKiBFeGlzdGluZyBlbnYgdmFsdWVzIGFyZSBwcmVzZXJ2ZWQ7IG9ubHkgbWlzc2luZyBrZXlzIGFyZSBmaWxsZWQgaW4sXG4gKiB3aGljaCBrZWVwcyBleHBsaWNpdCBvdmVycmlkZXMgYXV0aG9yaXRhdGl2ZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5TGFrZWJhc2VUb0VudihyZXNvbHZlZDogTGFrZWJhc2VDb25uZWN0aW9uKTogdm9pZCB7XG4gIGlmIChyZXNvbHZlZC5lbmRwb2ludCkgcHJvY2Vzcy5lbnYuTEFLRUJBU0VfRU5EUE9JTlQgPz89IHJlc29sdmVkLmVuZHBvaW50O1xuICBpZiAocmVzb2x2ZWQuaG9zdCkgcHJvY2Vzcy5lbnYuUEdIT1NUID8/PSByZXNvbHZlZC5ob3N0O1xuICBpZiAocmVzb2x2ZWQuZGF0YWJhc2UpIHByb2Nlc3MuZW52LlBHREFUQUJBU0UgPz89IHJlc29sdmVkLmRhdGFiYXNlO1xuICBwcm9jZXNzLmVudi5QR1BPUlQgPz89IFN0cmluZyhyZXNvbHZlZC5wb3J0KTtcbiAgcHJvY2Vzcy5lbnYuUEdTU0xNT0RFID8/PSByZXNvbHZlZC5zc2xNb2RlO1xufVxuXG50eXBlIFdvcmtzcGFjZUNsaWVudCA9IFJldHVyblR5cGU8dHlwZW9mIGdldFdvcmtzcGFjZUNsaWVudD47XG5cbi8qKiBUaGUgU0RLIGBDb250ZXh0YCBhY2NlcHRlZCBieSB0aGUgd29ya3NwYWNlIGNsaWVudCdzIG93biBjb3B5IG9mIHRoZSBTREsuICovXG50eXBlIEFwaVJlcXVlc3RDb250ZXh0ID0gTm9uTnVsbGFibGU8UGFyYW1ldGVyczxXb3Jrc3BhY2VDbGllbnRbXCJhcGlDbGllbnRcIl1bXCJyZXF1ZXN0XCJdPlsxXT47XG5cbi8qKlxuICogQWRhcHQgYW4ge0BsaW5rIEFib3J0U2lnbmFsfSBpbnRvIHRoZSBhcGkgY2xpZW50J3MgY2FuY2VsbGF0aW9uIGNvbnRleHQuXG4gKiBBcHBLaXQgcmVzb2x2ZXMgYSBkaWZmZXJlbnQgY29weSBvZiBgQGRhdGFicmlja3Mvc2RrLWV4cGVyaW1lbnRhbGAgdGhhbiB0aGlzXG4gKiBwYWNrYWdlIGRvZXMsIGFuZCBgQ29udGV4dGAgY2FycmllcyBhIHByaXZhdGUgZmllbGQsIHNvIHRoZSB0d28gZGVjbGFyYXRpb25zXG4gKiBhcmUgbm9taW5hbGx5IGluY29tcGF0aWJsZSBldmVuIHRob3VnaCB0aGUgYXBpIGNsaWVudCBvbmx5IHJlYWRzXG4gKiBgY2FuY2VsbGF0aW9uVG9rZW5gIGFuZCBgbG9nZ2VyYCBvZmYgdGhlIG9iamVjdC5cbiAqL1xuZnVuY3Rpb24gcmVxdWVzdENvbnRleHQoc2lnbmFsOiBBYm9ydFNpZ25hbCB8IHVuZGVmaW5lZCk6IEFwaVJlcXVlc3RDb250ZXh0IHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHNpZ25hbCA/ICh0b0NvbnRleHQoc2lnbmFsKSBhcyB1bmtub3duIGFzIEFwaVJlcXVlc3RDb250ZXh0KSA6IHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBWYWxpZGF0ZSBhIExha2ViYXNlIFJFU1QgYm9keS4gVGhlIGZ1bGwgcGF5bG9hZCBpcyBsb2dnZWQgcmF0aGVyIHRoYW4gdGhyb3duXG4gKiBzbyBhIGNsaWVudCBuZXZlciByZWNlaXZlcyB1cHN0cmVhbSBkZXRhaWwuXG4gKi9cbmZ1bmN0aW9uIHBhcnNlUmVzcG9uc2U8VD4oc2NoZW1hOiB6LlpvZFR5cGU8VD4sIHBhdGg6IHN0cmluZywgYm9keTogdW5rbm93bik6IFQge1xuICBjb25zdCBwYXJzZWQgPSBzY2hlbWEuc2FmZVBhcnNlKGJvZHkpO1xuICBpZiAocGFyc2VkLnN1Y2Nlc3MpIHJldHVybiBwYXJzZWQuZGF0YTtcbiAgbG9nZ2VyLmVycm9yKFwiYXV0b3BnOiB1bmV4cGVjdGVkIExha2ViYXNlIEFQSSByZXNwb25zZVwiLCB7XG4gICAgcGF0aCxcbiAgICBpc3N1ZXM6IHBhcnNlZC5lcnJvci5pc3N1ZXMsXG4gIH0pO1xuICB0aHJvdyBuZXcgRXhlY3V0aW9uRXJyb3IoXCJMYWtlYmFzZSBBUEkgcmV0dXJuZWQgYW4gdW5leHBlY3RlZCByZXNwb25zZVwiLCB7IGNvbnRleHQ6IHsgcGF0aCB9IH0pO1xufVxuXG4vKiogR0VUIGhlbHBlciB0aGF0IHZhbGlkYXRlcyB0aGUgSlNPTiBib2R5IGFuZCBmb3J3YXJkcyBjYW5jZWxsYXRpb24uICovXG5hc3luYyBmdW5jdGlvbiBnZXRKc29uPFQ+KFxuICB3czogV29ya3NwYWNlQ2xpZW50LFxuICBwYXRoOiBzdHJpbmcsXG4gIHNjaGVtYTogei5ab2RUeXBlPFQ+LFxuICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbik6IFByb21pc2U8VD4ge1xuICBjb25zdCByZXMgPSBhd2FpdCB3cy5hcGlDbGllbnQucmVxdWVzdChcbiAgICB7XG4gICAgICBwYXRoLFxuICAgICAgbWV0aG9kOiBcIkdFVFwiLFxuICAgICAgaGVhZGVyczogbmV3IEhlYWRlcnMoeyBBY2NlcHQ6IFwiYXBwbGljYXRpb24vanNvblwiIH0pLFxuICAgICAgcmF3OiBmYWxzZSxcbiAgICB9LFxuICAgIHJlcXVlc3RDb250ZXh0KHNpZ25hbCksXG4gICk7XG4gIHJldHVybiBwYXJzZVJlc3BvbnNlKHNjaGVtYSwgcGF0aCwgcmVzKTtcbn1cblxuLyoqIFBPU1QgaGVscGVyIGZvciBjcmVhdGUgLyBtdXRhdGUgY2FsbHM7IHZhbGlkYXRlcyB0aGUgSlNPTiBib2R5LiAqL1xuYXN5bmMgZnVuY3Rpb24gcG9zdEpzb248VD4oXG4gIHdzOiBXb3Jrc3BhY2VDbGllbnQsXG4gIHBhdGg6IHN0cmluZyxcbiAgc2NoZW1hOiB6LlpvZFR5cGU8VD4sXG4gIGJvZHk6IHVua25vd24sXG4gIHF1ZXJ5PzogUmVjb3JkPHN0cmluZywgc3RyaW5nPixcbiAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPFQ+IHtcbiAgY29uc3QgcmVzID0gYXdhaXQgd3MuYXBpQ2xpZW50LnJlcXVlc3QoXG4gICAge1xuICAgICAgcGF0aCxcbiAgICAgIG1ldGhvZDogXCJQT1NUXCIsXG4gICAgICBxdWVyeSxcbiAgICAgIGhlYWRlcnM6IG5ldyBIZWFkZXJzKHtcbiAgICAgICAgQWNjZXB0OiBcImFwcGxpY2F0aW9uL2pzb25cIixcbiAgICAgICAgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uXCIsXG4gICAgICB9KSxcbiAgICAgIHJhdzogZmFsc2UsXG4gICAgICBwYXlsb2FkOiBib2R5LFxuICAgIH0sXG4gICAgcmVxdWVzdENvbnRleHQoc2lnbmFsKSxcbiAgKTtcbiAgcmV0dXJuIHBhcnNlUmVzcG9uc2Uoc2NoZW1hLCBwYXRoLCByZXMpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBsaXN0UHJvamVjdHMod3M6IFdvcmtzcGFjZUNsaWVudCwgc2lnbmFsPzogQWJvcnRTaWduYWwpOiBQcm9taXNlPFByb2plY3RbXT4ge1xuICBjb25zdCByZXMgPSBhd2FpdCBnZXRKc29uKHdzLCBgJHtBUElfQkFTRX0vcHJvamVjdHNgLCBsaXN0UmVzcG9uc2VTY2hlbWEsIHNpZ25hbCk7XG4gIHJldHVybiByZXMucHJvamVjdHMgPz8gW107XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGxpc3RCcmFuY2hlcyhcbiAgd3M6IFdvcmtzcGFjZUNsaWVudCxcbiAgcHJvamVjdDogc3RyaW5nLFxuICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbik6IFByb21pc2U8QnJhbmNoW10+IHtcbiAgY29uc3QgcmVzID0gYXdhaXQgZ2V0SnNvbihcbiAgICB3cyxcbiAgICBgJHtBUElfQkFTRX0vcHJvamVjdHMvJHtwcm9qZWN0fS9icmFuY2hlc2AsXG4gICAgbGlzdFJlc3BvbnNlU2NoZW1hLFxuICAgIHNpZ25hbCxcbiAgKTtcbiAgcmV0dXJuIHJlcy5icmFuY2hlcyA/PyBbXTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gbGlzdEVuZHBvaW50cyhcbiAgd3M6IFdvcmtzcGFjZUNsaWVudCxcbiAgcHJvamVjdDogc3RyaW5nLFxuICBicmFuY2g6IHN0cmluZyxcbiAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPEVuZHBvaW50W10+IHtcbiAgY29uc3QgcmVzID0gYXdhaXQgZ2V0SnNvbihcbiAgICB3cyxcbiAgICBgJHtBUElfQkFTRX0vcHJvamVjdHMvJHtwcm9qZWN0fS9icmFuY2hlcy8ke2JyYW5jaH0vZW5kcG9pbnRzYCxcbiAgICBsaXN0UmVzcG9uc2VTY2hlbWEsXG4gICAgc2lnbmFsLFxuICApO1xuICByZXR1cm4gcmVzLmVuZHBvaW50cyA/PyBbXTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gbGlzdERhdGFiYXNlcyhcbiAgd3M6IFdvcmtzcGFjZUNsaWVudCxcbiAgcHJvamVjdDogc3RyaW5nLFxuICBicmFuY2g6IHN0cmluZyxcbiAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPERhdGFiYXNlW10+IHtcbiAgY29uc3QgcmVzID0gYXdhaXQgZ2V0SnNvbihcbiAgICB3cyxcbiAgICBgJHtBUElfQkFTRX0vcHJvamVjdHMvJHtwcm9qZWN0fS9icmFuY2hlcy8ke2JyYW5jaH0vZGF0YWJhc2VzYCxcbiAgICBsaXN0UmVzcG9uc2VTY2hlbWEsXG4gICAgc2lnbmFsLFxuICApO1xuICByZXR1cm4gcmVzLmRhdGFiYXNlcyA/PyBbXTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZ2V0RW5kcG9pbnQoXG4gIHdzOiBXb3Jrc3BhY2VDbGllbnQsXG4gIHByb2plY3Q6IHN0cmluZyxcbiAgYnJhbmNoOiBzdHJpbmcsXG4gIGVuZHBvaW50SWQ6IHN0cmluZyxcbiAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPEVuZHBvaW50PiB7XG4gIHJldHVybiBnZXRKc29uKFxuICAgIHdzLFxuICAgIGAke0FQSV9CQVNFfS9wcm9qZWN0cy8ke3Byb2plY3R9L2JyYW5jaGVzLyR7YnJhbmNofS9lbmRwb2ludHMvJHtlbmRwb2ludElkfWAsXG4gICAgZW5kcG9pbnRTY2hlbWEsXG4gICAgc2lnbmFsLFxuICApO1xufVxuXG4vKipcbiAqIFNjYW4gdGhlIHdvcmtzcGFjZSBmb3IgYW4gZW5kcG9pbnQgd2hvc2UgYHN0YXR1cy5ob3N0cy5ob3N0YCBtYXRjaGVzXG4gKiB0aGUgcHJvdmlkZWQgaG9zdG5hbWUuIFVzZWQgdG8gcmVjb3ZlciB0aGUgb3duaW5nIHByb2plY3QvYnJhbmNoL1xuICogZW5kcG9pbnQgcmVzb3VyY2UgcGF0aCB3aGVuIHRoZSBjYWxsZXIgb25seSBzdXBwbGllZCBhIFBvc3RncmVzIFVSSS5cbiAqXG4gKiBPKHByb2plY3RzICogYnJhbmNoZXMgKiBlbmRwb2ludHMpIC0gZmluZSBmb3IgdHlwaWNhbCB3b3Jrc3BhY2VzXG4gKiAoc2luZ2xlIGRpZ2l0cyBwZXIgdGllcik7IHBhZ2luYXRpb24gaXMgaW50ZW50aW9uYWxseSBub3QgZm9sbG93ZWRcbiAqIHNpbmNlIHRoaXMgaXMgYSBiZXN0LWVmZm9ydCBmYWxsYmFjay5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gZmluZEVuZHBvaW50QnlIb3N0KFxuICB3czogV29ya3NwYWNlQ2xpZW50LFxuICBob3N0OiBzdHJpbmcsXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuKTogUHJvbWlzZTx7IHByb2plY3Q6IHN0cmluZzsgYnJhbmNoOiBzdHJpbmc7IGVuZHBvaW50OiBzdHJpbmcgfSB8IG51bGw+IHtcbiAgY29uc3QgcHJvamVjdHMgPSBhd2FpdCBsaXN0UHJvamVjdHMod3MsIHNpZ25hbCk7XG4gIGZvciAoY29uc3QgcCBvZiBwcm9qZWN0cykge1xuICAgIGNvbnN0IHByb2plY3RJZCA9IHBhcnNlUmVzb3VyY2VQYXRoKHAubmFtZSkucHJvamVjdDtcbiAgICBpZiAoIXByb2plY3RJZCkgY29udGludWU7XG4gICAgY29uc3QgYnJhbmNoZXMgPSBhd2FpdCBsaXN0QnJhbmNoZXMod3MsIHByb2plY3RJZCwgc2lnbmFsKTtcbiAgICBmb3IgKGNvbnN0IGIgb2YgYnJhbmNoZXMpIHtcbiAgICAgIGNvbnN0IGJyYW5jaElkID0gcGFyc2VSZXNvdXJjZVBhdGgoYi5uYW1lKS5icmFuY2g7XG4gICAgICBpZiAoIWJyYW5jaElkKSBjb250aW51ZTtcbiAgICAgIGNvbnN0IGVuZHBvaW50cyA9IGF3YWl0IGxpc3RFbmRwb2ludHMod3MsIHByb2plY3RJZCwgYnJhbmNoSWQsIHNpZ25hbCk7XG4gICAgICBjb25zdCBtYXRjaCA9IGVuZHBvaW50cy5maW5kKChlKSA9PiBlLnN0YXR1cz8uaG9zdHM/Lmhvc3QgPT09IGhvc3QpO1xuICAgICAgaWYgKG1hdGNoPy5uYW1lKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhcImF1dG9wZzogbWF0Y2hlZCBlbmRwb2ludCBieSBob3N0XCIsIHtcbiAgICAgICAgICBob3N0LFxuICAgICAgICAgIGVuZHBvaW50OiBtYXRjaC5uYW1lLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBwcm9qZWN0OiBwcm9qZWN0SWQsXG4gICAgICAgICAgYnJhbmNoOiBicmFuY2hJZCxcbiAgICAgICAgICBlbmRwb2ludDogbWF0Y2gubmFtZSxcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgbG9nZ2VyLmRlYnVnKFwiYXV0b3BnOiBubyBlbmRwb2ludCBtYXRjaGVkIGhvc3RcIiwgeyBob3N0IH0pO1xuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqXG4gKiBQaWNrIHRoZSBwcm9qZWN0IHRvIHVzZSwgb3IgY3JlYXRlIG9uZSB3aGVuIHRoZSB3b3Jrc3BhY2UgaXMgZW1wdHkuXG4gKlxuICogU2VsZWN0aW9uIG9yZGVyOlxuICogMS4gRXhhY3RseSBvbmUgcHJvamVjdCBsaXN0ZWQgLT4gdXNlIGl0LlxuICogMi4gWmVybyBwcm9qZWN0cyBBTkQgYGF1dG9DcmVhdGUgIT09IGZhbHNlYCAtPiBlbnN1cmUgYSBwcm9qZWN0IHdpdGhcbiAqICAgIHRoZSByZXNvbHZlZCBpZCBleGlzdHMsIHRoZW4gcmV0dXJuIGl0cyBpZC5cbiAqIDMuIFplcm8gcHJvamVjdHMgQU5EIGBhdXRvQ3JlYXRlID09PSBmYWxzZWAgLT4gdGhyb3cuXG4gKiA0LiBNdWx0aXBsZSBwcm9qZWN0cyAtPiB0aHJvdyB3aXRoIHRoZSBjYW5kaWRhdGUgbGlzdCAoc2V0XG4gKiAgICBgY29uZmlnLnByb2plY3RgIG9yIHBpbiBhIHByb2plY3QgaWQgaW4gYExBS0VCQVNFX0VORFBPSU5UYCkuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHBpY2tPckNyZWF0ZVByb2plY3QoXG4gIHdzOiBXb3Jrc3BhY2VDbGllbnQsXG4gIGF1dG9DcmVhdGU6IHN0cmluZyB8IGZhbHNlIHwgdW5kZWZpbmVkLFxuICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbik6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHByb2plY3RzID0gYXdhaXQgbGlzdFByb2plY3RzKHdzLCBzaWduYWwpO1xuICBpZiAocHJvamVjdHMubGVuZ3RoID09PSAxKSB7XG4gICAgY29uc3QgaWQgPSBwYXJzZVJlc291cmNlUGF0aChwcm9qZWN0c1swXSEubmFtZSkucHJvamVjdDtcbiAgICBpZiAoaWQpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhcImF1dG9wZzogdXNpbmcgb25seSBwcm9qZWN0XCIsIHsgcHJvamVjdDogaWQgfSk7XG4gICAgICByZXR1cm4gaWQ7XG4gICAgfVxuICB9XG4gIGlmIChwcm9qZWN0cy5sZW5ndGggPT09IDApIHtcbiAgICBpZiAoYXV0b0NyZWF0ZSA9PT0gZmFsc2UpIHtcbiAgICAgIHRocm93IENvbmZpZ3VyYXRpb25FcnJvci5yZXNvdXJjZU5vdEZvdW5kKFxuICAgICAgICBcIkxha2ViYXNlIHByb2plY3RcIixcbiAgICAgICAgXCJhdXRvQ3JlYXRlIGlzIGZhbHNlOyBjcmVhdGUgYSBwcm9qZWN0IG9yIHNldCBjb25maWcucHJvamVjdCAvIExBS0VCQVNFX0VORFBPSU5ULlwiLFxuICAgICAgKTtcbiAgICB9XG4gICAgY29uc3QgaWQgPSBhdXRvQ3JlYXRlID8/IChhd2FpdCBkZWZhdWx0UHJvamVjdElkKCkpO1xuICAgIHJldHVybiBlbnN1cmVQcm9qZWN0KHdzLCBpZCwgc2lnbmFsKTtcbiAgfVxuICBjb25zdCBjYW5kaWRhdGVzID0gcHJvamVjdHNcbiAgICAubWFwKChwKSA9PiBwYXJzZVJlc291cmNlUGF0aChwLm5hbWUpLnByb2plY3QpXG4gICAgLmZpbHRlcigoaWQpOiBpZCBpcyBzdHJpbmcgPT4gQm9vbGVhbihpZCkpXG4gICAgLmpvaW4oXCIsIFwiKTtcbiAgdGhyb3cgQ29uZmlndXJhdGlvbkVycm9yLmludmFsaWRDb25uZWN0aW9uKFxuICAgIFwiTGFrZWJhc2VcIixcbiAgICBgTXVsdGlwbGUgcHJvamVjdHMgZm91bmQ7IHNldCBjb25maWcucHJvamVjdCBvciBwaW4gYSBwcm9qZWN0IGlkIGluIExBS0VCQVNFX0VORFBPSU5ULiBDYW5kaWRhdGVzOiAke2NhbmRpZGF0ZXN9YCxcbiAgKTtcbn1cblxuLyoqXG4gKiBEZXJpdmUgYSBMYWtlYmFzZSBwcm9qZWN0IGlkIGZyb20gdGhlIGhvc3QgcmVwbydzIGBwYWNrYWdlLmpzb25gXG4gKiBuYW1lICh2aWEge0BsaW5rIHByb2plY3QubmFtZX0pIHNsdWdpZmllZCB0byBzYXRpc2Z5IHRoZVxuICogTGFrZWJhc2UgaWQgY29uc3RyYWludCAoYF5bYS16XVthLXowLTktXXswLDYxfVthLXowLTldJGApLlxuICpcbiAqIFRocm93cyB3aGVuIHRoZSBzbHVnIGVuZHMgdXAgZW1wdHkgb3Igc3RhcnRzIHdpdGggYSBkaWdpdCwgc2luY2UgdGhlXG4gKiBzZXJ2ZXIgd291bGQgcmVqZWN0IGl0IGFueXdheSAtIGNhbGxlcnMgc2hvdWxkIHBhc3MgYW4gZXhwbGljaXRcbiAqIGBhdXRvQ3JlYXRlYCBpZCBpbiB0aGF0IGNhc2UuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGRlZmF1bHRQcm9qZWN0SWQoKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgbmFtZSA9IHByb2plY3QubmFtZSgpO1xuICBjb25zdCBzbHVnID0gc3RyaW5nLnRvU2x1Z1dpdGhPcHRpb25zKHsgbWF4TGVuZ3RoOiBQUk9KRUNUX0lEX01BWF9MRU4gfSwgbmFtZSk7XG4gIGlmICghc2x1ZyB8fCAhL15bYS16XS8udGVzdChzbHVnKSkge1xuICAgIGxvZ2dlci53YXJuKFwiYXV0b3BnOiBwcm9qZWN0IG5hbWUgZG9lcyBub3Qgc2x1Z2lmeSB0byBhIExha2ViYXNlIHByb2plY3QgaWRcIiwgeyBuYW1lIH0pO1xuICAgIHRocm93IENvbmZpZ3VyYXRpb25FcnJvci5pbnZhbGlkQ29ubmVjdGlvbihcbiAgICAgIFwiTGFrZWJhc2VcIixcbiAgICAgIFwiQ291bGQgbm90IGRlcml2ZSBhIHByb2plY3QgaWQgZnJvbSB0aGUgcGFja2FnZSBuYW1lOyBwYXNzIGF1dG9DcmVhdGUgZXhwbGljaXRseS5cIixcbiAgICApO1xuICB9XG4gIHJldHVybiBzbHVnO1xufVxuXG4vKipcbiAqIEVuc3VyZSBhIExha2ViYXNlIHByb2plY3Qgd2l0aCBgcHJvamVjdElkYCBleGlzdHMuIENyZWF0ZXMgaXQgYW5kXG4gKiB3YWl0cyBmb3IgdGhlIGNyZWF0ZSBvcGVyYXRpb24gdG8gY29tcGxldGUuIEFuIGBBTFJFQURZX0VYSVNUU2BcbiAqIHJlc3BvbnNlIGlzIHRyZWF0ZWQgYXMgc3VjY2VzcyAtIHNvbWVvbmUgZWxzZSAoYSBjb25jdXJyZW50IGJvb3QsXG4gKiBhIHNpYmxpbmcgcHJvY2Vzcykgd29uIHRoZSByYWNlIGFuZCB0aGUgcHJvamVjdCB3ZSB3YW50ZWQgaXMgbm93XG4gKiBzaXR0aW5nIHRoZXJlIHJlYWR5IGZvciBkb3duc3RyZWFtIHBpY2tlcnMuXG4gKlxuICogUHJvamVjdCBjcmVhdGlvbiB0eXBpY2FsbHkgcHJvdmlzaW9ucyBhIGRlZmF1bHQgYHByb2R1Y3Rpb25gIGJyYW5jaFxuICogYWxvbmdzaWRlOyBkb3duc3RyZWFtIHBpY2tlcnMgaGFuZGxlIHRoZSByZXN0LlxuICovXG5hc3luYyBmdW5jdGlvbiBlbnN1cmVQcm9qZWN0KFxuICB3czogV29ya3NwYWNlQ2xpZW50LFxuICBwcm9qZWN0SWQ6IHN0cmluZyxcbiAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPHN0cmluZz4ge1xuICBsb2dnZXIud2FybihcImF1dG9wZzogbm8gcHJvamVjdHMgZm91bmQ7IGNyZWF0aW5nXCIsIHsgcHJvamVjdDogcHJvamVjdElkIH0pO1xuICB0cnkge1xuICAgIGNvbnN0IG9wID0gYXdhaXQgcG9zdEpzb24oXG4gICAgICB3cyxcbiAgICAgIGAke0FQSV9CQVNFfS9wcm9qZWN0c2AsXG4gICAgICBvcGVyYXRpb25TY2hlbWEsXG4gICAgICB7IHNwZWM6IHsgcGdfdmVyc2lvbjogREVGQVVMVF9QR19WRVJTSU9OIH0gfSxcbiAgICAgIHsgcHJvamVjdF9pZDogcHJvamVjdElkIH0sXG4gICAgICBzaWduYWwsXG4gICAgKTtcbiAgICBhd2FpdCB3YWl0Rm9yT3BlcmF0aW9uKHdzLCBvcCwgc2lnbmFsKTtcbiAgICBsb2dnZXIuaW5mbyhcImF1dG9wZzogY3JlYXRlZCBwcm9qZWN0XCIsIHsgcHJvamVjdDogcHJvamVjdElkIH0pO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBpZiAoIWlzQWxyZWFkeUV4aXN0c0Vycm9yKGVycikpIHRocm93IGVycjtcbiAgICBsb2dnZXIuaW5mbyhcImF1dG9wZzogcHJvamVjdCBhbHJlYWR5IGV4aXN0cyAocmFjZSk7IHByb2NlZWRpbmdcIiwge1xuICAgICAgcHJvamVjdDogcHJvamVjdElkLFxuICAgIH0pO1xuICB9XG4gIHJldHVybiBwcm9qZWN0SWQ7XG59XG5cbi8qKlxuICogUmVjb2duaXplIHRoZSBEYXRhYnJpY2tzIFNESydzIGBBTFJFQURZX0VYSVNUU2AgZmFpbHVyZSBtb2RlcyBzbyBhXG4gKiBsb3N0IHJhY2UgZHVyaW5nIGBlbnN1cmVQcm9qZWN0YCBiZWNvbWVzIGEgbm8tb3AgaW5zdGVhZCBvZiBhbiBlcnJvci5cbiAqXG4gKiBUaGUgU0RLIHRocm93cyBgQXBpRXJyb3IgeyBlcnJvckNvZGUsIHN0YXR1c0NvZGUgfWAgZm9yIHN0cnVjdHVyZWRcbiAqIHNlcnZlciBlcnJvcnMgYW5kIGBIdHRwRXJyb3IgeyBjb2RlIH1gIGZvciB0cmFuc3BvcnQtbGF5ZXIgNHh4LzV4eC5cbiAqIEJvdGggc3VyZmFjZSBhIGh1bWFuIG1lc3NhZ2UgdGhhdCBvZnRlbiBjYXJyaWVzIFwiYWxyZWFkeSBleGlzdHNcIiBzb1xuICogd2UgdXNlIHRoYXQgYXMgYSBmaW5hbCBmYWxsYmFjayBmb3IgZm9yd2FyZCBjb21wYXRpYmlsaXR5LlxuICovXG5mdW5jdGlvbiBpc0FscmVhZHlFeGlzdHNFcnJvcihlcnJvcjogdW5rbm93bik6IGJvb2xlYW4ge1xuICBpZiAoIWVycm9yIHx8IHR5cGVvZiBlcnJvciAhPT0gXCJvYmplY3RcIikgcmV0dXJuIGZhbHNlO1xuICBjb25zdCBlID0gZXJyb3IgYXMge1xuICAgIHN0YXR1c0NvZGU/OiBudW1iZXI7XG4gICAgY29kZT86IG51bWJlcjtcbiAgICBlcnJvckNvZGU/OiBzdHJpbmc7XG4gICAgbWVzc2FnZT86IHN0cmluZztcbiAgfTtcbiAgaWYgKGUuc3RhdHVzQ29kZSA9PT0gNDA5IHx8IGUuY29kZSA9PT0gNDA5KSByZXR1cm4gdHJ1ZTtcbiAgaWYgKGUuZXJyb3JDb2RlICYmIC9hbHJlYWR5Lj9leGlzdHMvaS50ZXN0KGUuZXJyb3JDb2RlKSkgcmV0dXJuIHRydWU7XG4gIGlmIChlLm1lc3NhZ2UgJiYgL2FscmVhZHkuP2V4aXN0cy9pLnRlc3QoZS5tZXNzYWdlKSkgcmV0dXJuIHRydWU7XG4gIHJldHVybiBmYWxzZTtcbn1cblxuLyoqXG4gKiBSZWR1Y2UgYSBMYWtlYmFzZSBvcGVyYXRpb24gZmFpbHVyZSB0byBhbiBlcnJvciBzYWZlIHRvIHByb3BhZ2F0ZTogdGhlXG4gKiBvcGVyYXRpb24gbmFtZSBwbHVzIHdoYXRldmVyIGNvZGUgdGhlIHBheWxvYWQgY2Fycmllcy4gVGhlIHBheWxvYWQgaXRzZWxmIGNhblxuICogZWNobyByZXF1ZXN0IGNvbnRleHQsIHNvIGl0IGdvZXMgdG8gdGhlIGxvZyBpbnN0ZWFkLlxuICovXG5mdW5jdGlvbiBvcGVyYXRpb25GYWlsZWQob3BOYW1lOiBzdHJpbmcsIG9wRXJyb3I6IHVua25vd24pOiBFeGVjdXRpb25FcnJvciB7XG4gIGxvZ2dlci5lcnJvcihcImF1dG9wZzogb3BlcmF0aW9uIGZhaWxlZFwiLCB7IG9wZXJhdGlvbjogb3BOYW1lLCBlcnJvcjogb3BFcnJvciB9KTtcbiAgY29uc3QgY29kZSA9IG9iamVjdC5pc1JlY29yZChvcEVycm9yKSA/IChvcEVycm9yLmNvZGUgPz8gb3BFcnJvci5yZWFzb24pIDogdW5kZWZpbmVkO1xuICByZXR1cm4gbmV3IEV4ZWN1dGlvbkVycm9yKGBMYWtlYmFzZSBvcGVyYXRpb24gZmFpbGVkOiAke29wTmFtZX1gLCB7XG4gICAgY29udGV4dDoge1xuICAgICAgb3BlcmF0aW9uOiBvcE5hbWUsXG4gICAgICBlcnJvckNvZGU6IHR5cGVvZiBjb2RlID09PSBcInN0cmluZ1wiIHx8IHR5cGVvZiBjb2RlID09PSBcIm51bWJlclwiID8gU3RyaW5nKGNvZGUpIDogXCJ1bmtub3duXCIsXG4gICAgfSxcbiAgfSk7XG59XG5cbi8qKlxuICogUG9sbCBhIExha2ViYXNlIGxvbmctcnVubmluZyBvcGVyYXRpb24gdW50aWwgYGRvbmU6IHRydWVgLiBSZXR1cm5zXG4gKiB0aGUgZmluYWwgb3BlcmF0aW9uIGVudmVsb3BlICh3aGljaCBtYXkgY2FycnkgYHJlc3BvbnNlYCBvciBgZXJyb3JgKS5cbiAqXG4gKiBUaHJvd3Mgd2hlbjpcbiAqICAgLSB0aGUgcmVzcG9uc2UgY2FycmllcyBhbiBgZXJyb3JgIGZpZWxkO1xuICogICAtIGBvcC5uYW1lYCBpcyBtaXNzaW5nIChub3RoaW5nIHRvIHBvbGwpO1xuICogICAtIHRoZSB0aW1lb3V0IGVsYXBzZXMgYmVmb3JlIGBkb25lOiB0cnVlYDtcbiAqICAgLSBgc2lnbmFsYCBhYm9ydHMgbWlkLXdhaXQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHdhaXRGb3JPcGVyYXRpb24oXG4gIHdzOiBXb3Jrc3BhY2VDbGllbnQsXG4gIG9wOiBPcGVyYXRpb24sXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuKTogUHJvbWlzZTxPcGVyYXRpb24+IHtcbiAgaWYgKG9wLmRvbmUpIHtcbiAgICBpZiAob3AuZXJyb3IpIHtcbiAgICAgIHRocm93IG9wZXJhdGlvbkZhaWxlZChvcC5uYW1lID8/IFwidW5rbm93blwiLCBvcC5lcnJvcik7XG4gICAgfVxuICAgIHJldHVybiBvcDtcbiAgfVxuICBjb25zdCBvcE5hbWUgPSBvcC5uYW1lO1xuICBpZiAoIW9wTmFtZSkge1xuICAgIHRocm93IEV4ZWN1dGlvbkVycm9yLm1pc3NpbmdEYXRhKFwib3BlcmF0aW9uIG5hbWVcIik7XG4gIH1cbiAgY29uc3Qgc3RhcnQgPSBEYXRlLm5vdygpO1xuICBmb3IgKGxldCBhdHRlbXB0ID0gMDsgRGF0ZS5ub3coKSAtIHN0YXJ0IDwgT1BFUkFUSU9OX1RJTUVPVVRfTVM7IGF0dGVtcHQrKykge1xuICAgIGF3YWl0IHBvbGxEZWxheShhdHRlbXB0LCBPUEVSQVRJT05fUE9MTF9NUywgc2lnbmFsKTtcbiAgICBjb25zdCBjdXJyZW50ID0gYXdhaXQgZ2V0SnNvbih3cywgYCR7QVBJX0JBU0V9LyR7b3BOYW1lfWAsIG9wZXJhdGlvblNjaGVtYSwgc2lnbmFsKTtcbiAgICBsb2dnZXIuZGVidWcoXCJhdXRvcGc6IG9wZXJhdGlvbiBzdGF0dXNcIiwgeyBvcDogb3BOYW1lLCBkb25lOiBjdXJyZW50LmRvbmUgfSk7XG4gICAgaWYgKGN1cnJlbnQuZG9uZSkge1xuICAgICAgaWYgKGN1cnJlbnQuZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgb3BlcmF0aW9uRmFpbGVkKG9wTmFtZSwgY3VycmVudC5lcnJvcik7XG4gICAgICB9XG4gICAgICByZXR1cm4gY3VycmVudDtcbiAgICB9XG4gIH1cbiAgdGhyb3cgbmV3IEV4ZWN1dGlvbkVycm9yKGBMYWtlYmFzZSBvcGVyYXRpb24gZGlkIG5vdCBjb21wbGV0ZTogJHtvcE5hbWV9YCwge1xuICAgIGNvbnRleHQ6IHsgb3BlcmF0aW9uOiBvcE5hbWUsIHRpbWVvdXRNczogT1BFUkFUSU9OX1RJTUVPVVRfTVMgfSxcbiAgfSk7XG59XG5cbi8qKlxuICogUG9sbCBgZ2V0RW5kcG9pbnRgIHVudGlsIHRoZSBjb21wdXRlIHJlcG9ydHMgYSB1c2FibGVcbiAqIGBzdGF0dXMuY3VycmVudF9zdGF0ZWAuIGBSRUFEWWAgYW5kIGBJRExFYCBhcmUgYm90aCBhY2NlcHRhYmxlIC1cbiAqIGBJRExFYCBqdXN0IG1lYW5zIHRoZSBjb21wdXRlIGhhcyBzY2FsZWQgdG8gemVybyBidXQgYSBjb25uZWN0aW9uXG4gKiB3aWxsIHdha2UgaXQuIFJldHVybnMgdGhlIGZpbmFsIGVuZHBvaW50IHBheWxvYWQgKHdpdGggYGhvc3RzLmhvc3RgKS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gd2FpdEVuZHBvaW50UmVhZHkoXG4gIHdzOiBXb3Jrc3BhY2VDbGllbnQsXG4gIHByb2plY3Q6IHN0cmluZyxcbiAgYnJhbmNoOiBzdHJpbmcsXG4gIGVuZHBvaW50SWQ6IHN0cmluZyxcbiAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPEVuZHBvaW50PiB7XG4gIGNvbnN0IHN0YXJ0ID0gRGF0ZS5ub3coKTtcbiAgbGV0IGxhc3Q6IEVuZHBvaW50IHwgbnVsbCA9IG51bGw7XG4gIGZvciAobGV0IGF0dGVtcHQgPSAwOyBEYXRlLm5vdygpIC0gc3RhcnQgPCBFTkRQT0lOVF9SRUFEWV9USU1FT1VUX01TOyBhdHRlbXB0KyspIHtcbiAgICBsYXN0ID0gYXdhaXQgZ2V0RW5kcG9pbnQod3MsIHByb2plY3QsIGJyYW5jaCwgZW5kcG9pbnRJZCwgc2lnbmFsKTtcbiAgICBjb25zdCBzdGF0ZSA9IGxhc3Quc3RhdHVzPy5jdXJyZW50X3N0YXRlO1xuICAgIGlmIChzdGF0ZSAmJiBDT05ORUNUQUJMRV9FTkRQT0lOVF9TVEFURVMuaGFzKHN0YXRlKSkgcmV0dXJuIGxhc3Q7XG4gICAgaWYgKGxhc3Quc3RhdHVzPy5ob3N0cz8uaG9zdCAmJiBzdGF0ZSAhPT0gSU5JVElBTElaSU5HX0VORFBPSU5UX1NUQVRFKSB7XG4gICAgICAvLyBDb21wdXRlIGlzIGluIHNvbWUgb3RoZXIgc3RhdGUgKFNUQVJUSU5HLCBldGMuKSBidXQgaG9zdG5hbWUgaXNcbiAgICAgIC8vIGFscmVhZHkgcHVibGlzaGVkIC0gZ29vZCBlbm91Z2ggdG8gY29ubmVjdDsgbGFrZWJhc2UncyBPQXV0aFxuICAgICAgLy8gdG9rZW4gcmVxdWVzdCB3aWxsIHdha2UgaXQuXG4gICAgICByZXR1cm4gbGFzdDtcbiAgICB9XG4gICAgbG9nZ2VyLmRlYnVnKFwiYXV0b3BnOiB3YWl0aW5nIGZvciBlbmRwb2ludFwiLCB7IGVuZHBvaW50SWQsIHN0YXRlIH0pO1xuICAgIGF3YWl0IHBvbGxEZWxheShhdHRlbXB0LCBFTkRQT0lOVF9SRUFEWV9QT0xMX01TLCBzaWduYWwpO1xuICB9XG4gIHRocm93IG5ldyBFeGVjdXRpb25FcnJvcihgTGFrZWJhc2UgZW5kcG9pbnQgZGlkIG5vdCBiZWNvbWUgcmVhZHk6ICR7ZW5kcG9pbnRJZH1gLCB7XG4gICAgY29udGV4dDoge1xuICAgICAgcHJvamVjdCxcbiAgICAgIGJyYW5jaCxcbiAgICAgIGVuZHBvaW50SWQsXG4gICAgICB0aW1lb3V0TXM6IEVORFBPSU5UX1JFQURZX1RJTUVPVVRfTVMsXG4gICAgICBsYXN0U3RhdGU6IGxhc3Q/LnN0YXR1cz8uY3VycmVudF9zdGF0ZSA/PyBcInVua25vd25cIixcbiAgICB9LFxuICB9KTtcbn1cblxuLyoqXG4gKiBQaWNrIHRoZSBkZWZhdWx0IGJyYW5jaCBmb3IgYSBwcm9qZWN0LiBQcmVmZXJzIHRoZSBicmFuY2ggZmxhZ2dlZFxuICogYHN0YXR1cy5kZWZhdWx0OiB0cnVlYCAoc2VydmVyLXNpZGUgZGVmYXVsdCwgdHlwaWNhbGx5IGBwcm9kdWN0aW9uYFxuICogdW5sZXNzIHRoZSBwcm9qZWN0IG93bmVyIGNoYW5nZWQgaXQpLiBGYWxscyBiYWNrIHRvIHRoZSBvbmx5IGJyYW5jaFxuICogd2hlbiB0aGVyZSdzIGV4YWN0bHkgb25lLiBPdGhlcndpc2UgdGhyb3dzIHdpdGggdGhlIGNhbmRpZGF0ZSBsaXN0LlxuICovXG5hc3luYyBmdW5jdGlvbiBwaWNrQnJhbmNoKFxuICB3czogV29ya3NwYWNlQ2xpZW50LFxuICBwcm9qZWN0OiBzdHJpbmcsXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgYnJhbmNoZXMgPSBhd2FpdCBsaXN0QnJhbmNoZXMod3MsIHByb2plY3QsIHNpZ25hbCk7XG4gIGlmIChicmFuY2hlcy5sZW5ndGggPT09IDApIHtcbiAgICB0aHJvdyBDb25maWd1cmF0aW9uRXJyb3IucmVzb3VyY2VOb3RGb3VuZChcbiAgICAgIGBMYWtlYmFzZSBicmFuY2ggaW4gcHJvamVjdCAnJHtwcm9qZWN0fSdgLFxuICAgICAgXCJUaGUgcHJvamVjdCBoYXMgbm8gYnJhbmNoZXM7IGNyZWF0ZSBvbmUgb3Igc2V0IGNvbmZpZy5icmFuY2guXCIsXG4gICAgKTtcbiAgfVxuICBjb25zdCBmbGFnZ2VkID0gYnJhbmNoZXMuZmluZCgoYikgPT4gYi5zdGF0dXM/LmRlZmF1bHQgPT09IHRydWUpO1xuICBjb25zdCBjaG9pY2UgPVxuICAgIHBhcnNlUmVzb3VyY2VQYXRoKGZsYWdnZWQ/Lm5hbWUpLmJyYW5jaCA/P1xuICAgIChicmFuY2hlcy5sZW5ndGggPT09IDEgPyBwYXJzZVJlc291cmNlUGF0aChicmFuY2hlc1swXSEubmFtZSkuYnJhbmNoIDogdW5kZWZpbmVkKTtcbiAgaWYgKCFjaG9pY2UpIHtcbiAgICBjb25zdCBjYW5kaWRhdGVzID0gYnJhbmNoZXNcbiAgICAgIC5tYXAoKGIpID0+IHBhcnNlUmVzb3VyY2VQYXRoKGIubmFtZSkuYnJhbmNoKVxuICAgICAgLmZpbHRlcigoaWQpOiBpZCBpcyBzdHJpbmcgPT4gQm9vbGVhbihpZCkpXG4gICAgICAuam9pbihcIiwgXCIpO1xuICAgIHRocm93IENvbmZpZ3VyYXRpb25FcnJvci5pbnZhbGlkQ29ubmVjdGlvbihcbiAgICAgIFwiTGFrZWJhc2VcIixcbiAgICAgIGBQcm9qZWN0ICcke3Byb2plY3R9JyBoYXMgbXVsdGlwbGUgYnJhbmNoZXMgYW5kIG5vbmUgbWFya2VkIGRlZmF1bHQ7IHNldCBjb25maWcuYnJhbmNoIG9yIGluY2x1ZGUgdGhlIGJyYW5jaCBpbiBMQUtFQkFTRV9FTkRQT0lOVC4gQ2FuZGlkYXRlczogJHtjYW5kaWRhdGVzfWAsXG4gICAgKTtcbiAgfVxuICBsb2dnZXIuZGVidWcoXCJhdXRvcGc6IHJlc29sdmVkIGJyYW5jaFwiLCB7IHByb2plY3QsIGJyYW5jaDogY2hvaWNlIH0pO1xuICByZXR1cm4gY2hvaWNlO1xufVxuXG4vKipcbiAqIFBpY2sgdGhlIHByaW1hcnkgZW5kcG9pbnQgZm9yIGEgKHByb2plY3QsIGJyYW5jaCkuIFByZWZlcnNcbiAqIGBzdGF0dXMuZW5kcG9pbnRfdHlwZSA9PT0gRU5EUE9JTlRfVFlQRV9SRUFEX1dSSVRFYDsgZmFsbHMgYmFjayB0b1xuICogdGhlIG9ubHkgZW5kcG9pbnQgd2hlbiB0aGVyZSdzIGV4YWN0bHkgb25lLiBSZXR1cm5zIGB7IG5hbWUsIGhvc3QgfWBcbiAqIHNvIHRoZSBjYWxsZXIgY2FuIHBvcHVsYXRlIGJvdGggYExBS0VCQVNFX0VORFBPSU5UYCBhbmQgYFBHSE9TVGBcbiAqIGZyb20gYSBzaW5nbGUgY2FsbC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gcGlja0VuZHBvaW50KFxuICB3czogV29ya3NwYWNlQ2xpZW50LFxuICBwcm9qZWN0OiBzdHJpbmcsXG4gIGJyYW5jaDogc3RyaW5nLFxuICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbik6IFByb21pc2U8eyBuYW1lOiBzdHJpbmc7IGhvc3Q/OiBzdHJpbmcgfT4ge1xuICBjb25zdCBlbmRwb2ludHMgPSBhd2FpdCBsaXN0RW5kcG9pbnRzKHdzLCBwcm9qZWN0LCBicmFuY2gsIHNpZ25hbCk7XG4gIGlmIChlbmRwb2ludHMubGVuZ3RoID09PSAwKSB7XG4gICAgdGhyb3cgQ29uZmlndXJhdGlvbkVycm9yLnJlc291cmNlTm90Rm91bmQoXG4gICAgICBgTGFrZWJhc2UgZW5kcG9pbnQgaW4gJ3Byb2plY3RzLyR7cHJvamVjdH0vYnJhbmNoZXMvJHticmFuY2h9J2AsXG4gICAgICBcIlRoZSBicmFuY2ggaGFzIG5vIGVuZHBvaW50czsgY3JlYXRlIG9uZSBvciBzZXQgTEFLRUJBU0VfRU5EUE9JTlQuXCIsXG4gICAgKTtcbiAgfVxuICBjb25zdCBwcmltYXJ5ID1cbiAgICBlbmRwb2ludHMuZmluZCgoZSkgPT4gZS5zdGF0dXM/LmVuZHBvaW50X3R5cGUgPT09IFJFQURfV1JJVEVfRU5EUE9JTlRfVFlQRSkgPz9cbiAgICAoZW5kcG9pbnRzLmxlbmd0aCA9PT0gMSA/IGVuZHBvaW50c1swXSA6IHVuZGVmaW5lZCk7XG4gIGlmICghcHJpbWFyeT8ubmFtZSkge1xuICAgIGNvbnN0IG5hbWVzID0gZW5kcG9pbnRzLm1hcCgoZSkgPT4gZS5uYW1lKS5maWx0ZXIoQm9vbGVhbik7XG4gICAgdGhyb3cgQ29uZmlndXJhdGlvbkVycm9yLmludmFsaWRDb25uZWN0aW9uKFxuICAgICAgXCJMYWtlYmFzZVwiLFxuICAgICAgYEJyYW5jaCBoYXMgbm8gcHJpbWFyeSByZWFkLXdyaXRlIGVuZHBvaW50OyBzZXQgTEFLRUJBU0VfRU5EUE9JTlQgb3IgY29uZmlnLmVuZHBvaW50LiBDYW5kaWRhdGVzOiAke25hbWVzLmpvaW4oXCIsIFwiKX1gLFxuICAgICk7XG4gIH1cbiAgY29uc3QgaG9zdCA9IHByaW1hcnkuc3RhdHVzPy5ob3N0cz8uaG9zdDtcbiAgbG9nZ2VyLmRlYnVnKFwiYXV0b3BnOiByZXNvbHZlZCBlbmRwb2ludFwiLCB7IGVuZHBvaW50OiBwcmltYXJ5Lm5hbWUsIGhvc3QgfSk7XG4gIHJldHVybiB7IG5hbWU6IHByaW1hcnkubmFtZSwgaG9zdCB9O1xufVxuXG4vKipcbiAqIFBpY2sgdGhlIGRlZmF1bHQgcG9zdGdyZXMgZGF0YWJhc2UgZm9yIGEgKHByb2plY3QsIGJyYW5jaCkuIFRoZVxuICogUG9zdGdyZXMgZGF0YWJhc2UgTkFNRSAoYHN0YXR1cy5wb3N0Z3Jlc19kYXRhYmFzZWApIGlzIHdoYXRcbiAqIGBQR0RBVEFCQVNFYCBuZWVkcyAtIHRoaXMgZGlmZmVycyBmcm9tIHRoZSByZXNvdXJjZSBpZCwgd2hpY2ggY2FuXG4gKiB1c2UgYSBkaWZmZXJlbnQgc2VwYXJhdG9yIChlLmcuIHJlc291cmNlIGBkYXRhYnJpY2tzLXBvc3RncmVzYFxuICogc3VyZmFjZXMgYXMgZGF0YWJhc2UgYGRhdGFicmlja3NfcG9zdGdyZXNgKS4gUHJlZmVyc1xuICogYGRhdGFicmlja3NfcG9zdGdyZXNgICh0aGUgTGFrZWJhc2UgZGVmYXVsdCksIG90aGVyd2lzZSB0aGUgb25seVxuICogZGF0YWJhc2UuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHBpY2tEYXRhYmFzZShcbiAgd3M6IFdvcmtzcGFjZUNsaWVudCxcbiAgcHJvamVjdDogc3RyaW5nLFxuICBicmFuY2g6IHN0cmluZyxcbiAgc2lnbmFsPzogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBkYXRhYmFzZXMgPSBhd2FpdCBsaXN0RGF0YWJhc2VzKHdzLCBwcm9qZWN0LCBicmFuY2gsIHNpZ25hbCk7XG4gIGlmIChkYXRhYmFzZXMubGVuZ3RoID09PSAwKSB7XG4gICAgdGhyb3cgQ29uZmlndXJhdGlvbkVycm9yLnJlc291cmNlTm90Rm91bmQoXG4gICAgICBgTGFrZWJhc2UgZGF0YWJhc2UgaW4gJ3Byb2plY3RzLyR7cHJvamVjdH0vYnJhbmNoZXMvJHticmFuY2h9J2AsXG4gICAgICBcIlRoZSBicmFuY2ggaGFzIG5vIGRhdGFiYXNlczsgY3JlYXRlIG9uZSBvciBzZXQgUEdEQVRBQkFTRS5cIixcbiAgICApO1xuICB9XG4gIGNvbnN0IG5hbWVzID0gZGF0YWJhc2VzXG4gICAgLm1hcCgoZCkgPT4gZC5zdGF0dXM/LnBvc3RncmVzX2RhdGFiYXNlKVxuICAgIC5maWx0ZXIoKG4pOiBuIGlzIHN0cmluZyA9PiBCb29sZWFuKG4pKTtcbiAgY29uc3QgY2hvaWNlID1cbiAgICBuYW1lcy5maW5kKChuKSA9PiBuID09PSBERUZBVUxUX0RBVEFCQVNFKSA/PyAobmFtZXMubGVuZ3RoID09PSAxID8gbmFtZXNbMF0gOiB1bmRlZmluZWQpO1xuICBpZiAoIWNob2ljZSkge1xuICAgIHRocm93IENvbmZpZ3VyYXRpb25FcnJvci5pbnZhbGlkQ29ubmVjdGlvbihcbiAgICAgIFwiTGFrZWJhc2VcIixcbiAgICAgIGBNdWx0aXBsZSBkYXRhYmFzZXMgYW5kIG5vICcke0RFRkFVTFRfREFUQUJBU0V9Jzsgc2V0IFBHREFUQUJBU0Ugb3IgY29uZmlnLmRhdGFiYXNlLiBDYW5kaWRhdGVzOiAke25hbWVzLmpvaW4oXCIsIFwiKX1gLFxuICAgICk7XG4gIH1cbiAgbG9nZ2VyLmRlYnVnKFwiYXV0b3BnOiByZXNvbHZlZCBkYXRhYmFzZVwiLCB7IGRhdGFiYXNlOiBjaG9pY2UgfSk7XG4gIHJldHVybiBjaG9pY2U7XG59XG4iXX0=
|