velocious 1.0.521 → 1.0.522
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 +1 -1
- package/build/database/drivers/base.js +13 -0
- package/build/database/drivers/mysql/index.js +1 -0
- package/build/src/database/drivers/base.d.ts +12 -0
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +13 -1
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +2 -1
- package/build/src/tenants/tenant-aggregator.d.ts +180 -0
- package/build/src/tenants/tenant-aggregator.d.ts.map +1 -0
- package/build/src/tenants/tenant-aggregator.js +285 -0
- package/build/src/tenants/tenant.d.ts +13 -0
- package/build/src/tenants/tenant.d.ts.map +1 -1
- package/build/src/tenants/tenant.js +17 -1
- package/build/tenants/tenant-aggregator.js +351 -0
- package/build/tenants/tenant.js +17 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/database/drivers/base.js +13 -0
- package/src/database/drivers/mysql/index.js +1 -0
- package/src/tenants/tenant-aggregator.js +351 -0
- package/src/tenants/tenant.js +17 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import Current from "../current.js";
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {"SUM" | "COUNT" | "MAX" | "MIN"} AggregateOperation
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {AggregateOperation | {op: AggregateOperation, column: string}} AggregateSpec
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {object} SubqueryContext
|
|
11
|
+
* @property {(tableName: string) => string} table - Quotes a table for this tenant. In a cross-database `UNION ALL` it returns a `database`.`table` qualified identifier; when each tenant runs on its own connection it returns the plain quoted table name. Use it for every table the subquery reads so the same subquery works on both execution paths.
|
|
12
|
+
* @property {((value: ?) => string) & {list: (values: Array<?>) => string}} quote - Quotes a value for the active connection. `quote.list(values)` quotes and comma-joins an array for `IN (...)` clauses.
|
|
13
|
+
* @property {object} tenant - The tenant descriptor this subquery is being built for.
|
|
14
|
+
* @property {import("../database/drivers/base.js").default} connection - The driver the query will run on.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {object} TenantAggregateOptions
|
|
18
|
+
* @property {string} identifier - Database identifier whose tenants are aggregated (for example `"projectTenant"`).
|
|
19
|
+
* @property {object[]} [tenants] - Explicit tenant descriptors to aggregate. Defaults to every tenant the identifier's provider `listTenants` returns.
|
|
20
|
+
* @property {(tenant: ?) => boolean} [filter] - Optional filter applied to the resolved tenant list.
|
|
21
|
+
* @property {string[]} [keyColumns] - Columns the aggregate is grouped by (for example `["docker_server_id"]`). Empty means a single grand-total row.
|
|
22
|
+
* @property {Record<string, AggregateSpec>} aggregates - Output column name to aggregate. `"SUM"` is shorthand for `{op: "SUM", column: <output name>}`; use `{op: "COUNT", column: "*"}` for `COUNT(*)`.
|
|
23
|
+
* @property {(context: SubqueryContext) => string} subquery - Builds one tenant's inner `SELECT`, which must select every `keyColumns` entry plus every aggregate source column.
|
|
24
|
+
* @property {import("../configuration.js").default} [configuration] - Configuration to run against. Defaults to the current configuration.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {{tenant: object, database: string | undefined, serverKey: string}} ResolvedTenant
|
|
28
|
+
*/
|
|
29
|
+
const AGGREGATE_OPERATIONS = new Set(["SUM", "COUNT", "MAX", "MIN"]);
|
|
30
|
+
const DERIVED_TABLE_ALIAS = "velocious_tenant_aggregate";
|
|
31
|
+
/**
|
|
32
|
+
* Runs one aggregate query across many tenant databases and merges the result. Tenant databases may
|
|
33
|
+
* be co-located on the default server or spread across other servers, and they can appear or
|
|
34
|
+
* disappear at runtime; this resolves the live tenant list, groups tenants by the server they live
|
|
35
|
+
* on, and — per server — emits a single cross-database `UNION ALL` when the driver supports qualified
|
|
36
|
+
* cross-database references (MySQL/MSSQL) or falls back to one query per tenant otherwise
|
|
37
|
+
* (PostgreSQL/SQLite). Results from every server are merged with the aggregate's own operation, so
|
|
38
|
+
* callers get one combined result set regardless of how the tenants are distributed. Reached through
|
|
39
|
+
* `Tenant.aggregateAcross`.
|
|
40
|
+
*/
|
|
41
|
+
export default class TenantAggregator {
|
|
42
|
+
/**
|
|
43
|
+
* Prepares an aggregate run, normalizing the aggregate specs up front.
|
|
44
|
+
* @param {TenantAggregateOptions} options - Aggregate configuration.
|
|
45
|
+
*/
|
|
46
|
+
constructor(options) {
|
|
47
|
+
this.options = options;
|
|
48
|
+
this.configuration = options.configuration ?? Current.configuration();
|
|
49
|
+
this.keyColumns = options.keyColumns ?? [];
|
|
50
|
+
this.aggregates = this._normalizeAggregates(options.aggregates);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolves the tenant list, runs the aggregate per server, and merges everything.
|
|
54
|
+
* @returns {Promise<Array<Record<string, ?>>>} - One merged row per distinct key-column combination.
|
|
55
|
+
*/
|
|
56
|
+
async run() {
|
|
57
|
+
const resolvedTenants = await this._resolveTenants();
|
|
58
|
+
if (resolvedTenants.length === 0)
|
|
59
|
+
return [];
|
|
60
|
+
/** @type {Array<Record<string, ?>>} */
|
|
61
|
+
const rows = [];
|
|
62
|
+
for (const group of this._groupByServer(resolvedTenants)) {
|
|
63
|
+
const groupRows = await this._runForServer(group);
|
|
64
|
+
rows.push(...groupRows);
|
|
65
|
+
}
|
|
66
|
+
return this._mergeRows(rows);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolves the tenant list (explicit or from the provider), then maps each to its server and
|
|
70
|
+
* database via the tenant database resolver.
|
|
71
|
+
* @returns {Promise<ResolvedTenant[]>} - Tenants to aggregate, each resolved to its server + database.
|
|
72
|
+
*/
|
|
73
|
+
async _resolveTenants() {
|
|
74
|
+
const tenants = this.options.tenants ?? await this._listProviderTenants();
|
|
75
|
+
const filtered = this.options.filter ? tenants.filter(this.options.filter) : tenants;
|
|
76
|
+
return filtered.map((tenant) => {
|
|
77
|
+
const databaseConfiguration = this.configuration.resolveDatabaseConfiguration(this.options.identifier, tenant);
|
|
78
|
+
// The server key groups tenants that share a connection endpoint so co-located tenants can be
|
|
79
|
+
// UNION-ed on one connection. It only needs to be exact for the cross-database `UNION ALL`
|
|
80
|
+
// path, which is MySQL-only, where host/port/username/type fully identify the server. Every
|
|
81
|
+
// other driver takes the fan-out path (one connection per tenant), so a coarse key there just
|
|
82
|
+
// groups tenants that are then queried individually anyway — it can never route a query to the
|
|
83
|
+
// wrong server.
|
|
84
|
+
return {
|
|
85
|
+
database: databaseConfiguration.database,
|
|
86
|
+
serverKey: JSON.stringify([
|
|
87
|
+
databaseConfiguration.type ?? null,
|
|
88
|
+
databaseConfiguration.host ?? null,
|
|
89
|
+
databaseConfiguration.port ?? null,
|
|
90
|
+
databaseConfiguration.username ?? null
|
|
91
|
+
]),
|
|
92
|
+
tenant
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Lists every tenant for the identifier through its provider's `listTenants` hook.
|
|
98
|
+
* @returns {Promise<object[]>} - Every tenant the provider lists for the identifier.
|
|
99
|
+
*/
|
|
100
|
+
async _listProviderTenants() {
|
|
101
|
+
const provider = this.configuration.getTenantDatabaseProvider(this.options.identifier);
|
|
102
|
+
const listedTenants = await this.configuration.ensureConnections({ name: `Tenant.aggregateAcross: ${this.options.identifier}` }, async () => await provider.listTenants({ configuration: this.configuration, identifier: this.options.identifier }));
|
|
103
|
+
if (!Array.isArray(listedTenants)) {
|
|
104
|
+
throw new Error(`Tenant database provider for ${this.options.identifier} must return an array from listTenants`);
|
|
105
|
+
}
|
|
106
|
+
return listedTenants;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Groups tenants by the server they live on so co-located tenants can share one query.
|
|
110
|
+
* @param {ResolvedTenant[]} resolvedTenants - Tenants resolved to their server.
|
|
111
|
+
* @returns {ResolvedTenant[][]} - Tenants grouped by the server they live on.
|
|
112
|
+
*/
|
|
113
|
+
_groupByServer(resolvedTenants) {
|
|
114
|
+
/** @type {Map<string, ResolvedTenant[]>} */
|
|
115
|
+
const groups = new Map();
|
|
116
|
+
for (const resolvedTenant of resolvedTenants) {
|
|
117
|
+
const group = groups.get(resolvedTenant.serverKey);
|
|
118
|
+
if (group) {
|
|
119
|
+
group.push(resolvedTenant);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
groups.set(resolvedTenant.serverKey, [resolvedTenant]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return Array.from(groups.values());
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Runs the aggregate for one server's tenants, using a single cross-database `UNION ALL` when the
|
|
129
|
+
* driver supports it or one query per tenant otherwise. The driver capability is probed in its own
|
|
130
|
+
* connection scope that is released before the fan-out runs, so a `max: 1` tenant pool is never
|
|
131
|
+
* asked for a second connection while the first is still held (which would deadlock).
|
|
132
|
+
* @param {ResolvedTenant[]} group - Tenants sharing one server.
|
|
133
|
+
* @returns {Promise<Array<Record<string, ?>>>} - Rows produced for this server.
|
|
134
|
+
*/
|
|
135
|
+
async _runForServer(group) {
|
|
136
|
+
const [firstTenant] = group;
|
|
137
|
+
const supportsCrossDatabaseReferences = await this._withTenant(firstTenant.tenant, async (connections) => connections[this.options.identifier].supportsCrossDatabaseReferences());
|
|
138
|
+
if (supportsCrossDatabaseReferences) {
|
|
139
|
+
return await this._withTenant(firstTenant.tenant, async (connections) => {
|
|
140
|
+
const connection = connections[this.options.identifier];
|
|
141
|
+
return await connection.query(this.buildAggregateSql({ connection, entries: group, qualified: true }));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
/** @type {Array<Record<string, ?>>} */
|
|
145
|
+
const rows = [];
|
|
146
|
+
for (const resolvedTenant of group) {
|
|
147
|
+
const tenantRows = await this._withTenant(resolvedTenant.tenant, async (connections) => {
|
|
148
|
+
const connection = connections[this.options.identifier];
|
|
149
|
+
return await connection.query(this.buildAggregateSql({ connection, entries: [resolvedTenant], qualified: false }));
|
|
150
|
+
});
|
|
151
|
+
rows.push(...tenantRows);
|
|
152
|
+
}
|
|
153
|
+
return rows;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Runs `callback` inside a tenant's context with its connections established, mirroring
|
|
157
|
+
* `Tenant.with` without importing it (which would create an import cycle).
|
|
158
|
+
* @template T
|
|
159
|
+
* @param {object} tenant - Tenant descriptor to switch into.
|
|
160
|
+
* @param {(connections: Record<string, import("../database/drivers/base.js").default>) => Promise<T>} callback - Operation to run with the tenant's active connections.
|
|
161
|
+
* @returns {Promise<T>} - Callback result from within the tenant context.
|
|
162
|
+
*/
|
|
163
|
+
async _withTenant(tenant, callback) {
|
|
164
|
+
return await this.configuration.runWithTenant(tenant, async () => await this.configuration.ensureConnections(callback));
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Builds the aggregate SQL: an outer `GROUP BY` over a `UNION ALL` of each entry's subquery.
|
|
168
|
+
* @param {{connection: import("../database/drivers/base.js").default, entries: ResolvedTenant[], qualified: boolean}} args - Connection, tenants, and whether to qualify tables with their database name.
|
|
169
|
+
* @returns {string} - Executable aggregate SQL.
|
|
170
|
+
*/
|
|
171
|
+
buildAggregateSql({ connection, entries, qualified }) {
|
|
172
|
+
const options = connection.options();
|
|
173
|
+
const quote = this._buildQuote(connection);
|
|
174
|
+
const subqueries = entries.map((entry) => {
|
|
175
|
+
if (qualified && !entry.database) {
|
|
176
|
+
throw new Error(`Cannot build a cross-database query for a tenant without a resolved database name (identifier: ${this.options.identifier}).`);
|
|
177
|
+
}
|
|
178
|
+
const table = (/** @type {string} */ tableName) => qualified
|
|
179
|
+
? `${options.quoteDatabaseName(/** @type {string} */ (entry.database))}.${options.quoteTableName(tableName)}`
|
|
180
|
+
: options.quoteTableName(tableName);
|
|
181
|
+
return this.options.subquery({ connection, quote, table, tenant: entry.tenant });
|
|
182
|
+
});
|
|
183
|
+
const selectParts = this.keyColumns.map((keyColumn) => options.quoteColumnName(keyColumn));
|
|
184
|
+
for (const [name, spec] of Object.entries(this.aggregates)) {
|
|
185
|
+
const aggregateArgument = spec.column === "*" ? "*" : options.quoteColumnName(spec.column);
|
|
186
|
+
selectParts.push(`${spec.op}(${aggregateArgument}) AS ${options.quoteColumnName(name)}`);
|
|
187
|
+
}
|
|
188
|
+
const unionSql = subqueries.map((subquery) => `SELECT * FROM (${subquery}) ${options.quoteTableName(`${DERIVED_TABLE_ALIAS}_source`)}`).join("\nUNION ALL\n");
|
|
189
|
+
const groupBySql = this.keyColumns.length > 0
|
|
190
|
+
? `\nGROUP BY ${this.keyColumns.map((keyColumn) => options.quoteColumnName(keyColumn)).join(", ")}`
|
|
191
|
+
: "";
|
|
192
|
+
return `SELECT ${selectParts.join(", ")}\nFROM (\n${unionSql}\n) ${options.quoteTableName(DERIVED_TABLE_ALIAS)}${groupBySql}`;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Merges rows from every server by combining each aggregate with its own operation. A `NULL`
|
|
196
|
+
* aggregate value (an empty tenant's `SUM`/`MAX`/`MIN` returns `NULL` on the fan-out path) is
|
|
197
|
+
* treated as "no contribution" and skipped, not coerced to `0` — otherwise an empty tenant would
|
|
198
|
+
* drag a `MAX` of negatives or a `MIN` of positives to `0`. A key whose every tenant contributed
|
|
199
|
+
* `NULL` stays `NULL`, matching SQL aggregate semantics over no rows.
|
|
200
|
+
* @param {Array<Record<string, ?>>} rows - Rows collected from all servers/tenants.
|
|
201
|
+
* @returns {Array<Record<string, ?>>} - One merged row per distinct key-column combination.
|
|
202
|
+
*/
|
|
203
|
+
_mergeRows(rows) {
|
|
204
|
+
/** @type {Map<string, Record<string, ?>>} */
|
|
205
|
+
const merged = new Map();
|
|
206
|
+
for (const row of rows) {
|
|
207
|
+
const mapKey = JSON.stringify(this.keyColumns.map((keyColumn) => row[keyColumn]));
|
|
208
|
+
let accumulator = merged.get(mapKey);
|
|
209
|
+
if (!accumulator) {
|
|
210
|
+
accumulator = {};
|
|
211
|
+
for (const keyColumn of this.keyColumns) {
|
|
212
|
+
accumulator[keyColumn] = row[keyColumn];
|
|
213
|
+
}
|
|
214
|
+
for (const name of Object.keys(this.aggregates)) {
|
|
215
|
+
accumulator[name] = null;
|
|
216
|
+
}
|
|
217
|
+
merged.set(mapKey, accumulator);
|
|
218
|
+
}
|
|
219
|
+
for (const [name, spec] of Object.entries(this.aggregates)) {
|
|
220
|
+
const rawValue = row[name];
|
|
221
|
+
if (rawValue === null || rawValue === undefined)
|
|
222
|
+
continue;
|
|
223
|
+
const value = this._toExactNumber(rawValue);
|
|
224
|
+
accumulator[name] = accumulator[name] === null ? value : this._combine(spec.op, accumulator[name], value);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return Array.from(merged.values());
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Combines two non-null per-server aggregate values with the aggregate's own operation.
|
|
231
|
+
* @param {AggregateOperation} op - Aggregate operation.
|
|
232
|
+
* @param {number} current - Accumulated value.
|
|
233
|
+
* @param {number} value - Incoming per-server value.
|
|
234
|
+
* @returns {number} - Combined value.
|
|
235
|
+
*/
|
|
236
|
+
_combine(op, current, value) {
|
|
237
|
+
if (op === "MAX")
|
|
238
|
+
return Math.max(current, value);
|
|
239
|
+
if (op === "MIN")
|
|
240
|
+
return Math.min(current, value);
|
|
241
|
+
return current + value;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Converts a driver-returned aggregate value to a number, failing loudly rather than silently
|
|
245
|
+
* losing precision. Drivers return exact integer aggregates (MySQL `SUM`/`COUNT`, PostgreSQL
|
|
246
|
+
* `bigint`) as strings; an integer beyond `Number.MAX_SAFE_INTEGER` cannot be represented exactly
|
|
247
|
+
* as a JS number, so cross-server merging would corrupt the result — throw instead. (Fractional
|
|
248
|
+
* `DECIMAL`/`NUMERIC` values are still subject to normal floating-point representation.)
|
|
249
|
+
* @param {?} rawValue - Value returned by the driver for an aggregate column.
|
|
250
|
+
* @returns {number} - The value as a number.
|
|
251
|
+
*/
|
|
252
|
+
_toExactNumber(rawValue) {
|
|
253
|
+
const value = Number(rawValue);
|
|
254
|
+
if (typeof rawValue === "string" && /^-?\d+$/.test(rawValue.trim()) && !Number.isSafeInteger(value)) {
|
|
255
|
+
throw new Error(`Aggregate value ${rawValue} exceeds the safe-integer range and cannot be merged without losing precision.`);
|
|
256
|
+
}
|
|
257
|
+
return value;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Builds the value quoter passed to subqueries, with a `.list` helper for `IN (...)` clauses.
|
|
261
|
+
* @param {import("../database/drivers/base.js").default} connection - Driver whose quoting is used.
|
|
262
|
+
* @returns {((value: ?) => string) & {list: (values: Array<?>) => string}} - Value quoter with a `.list` helper.
|
|
263
|
+
*/
|
|
264
|
+
_buildQuote(connection) {
|
|
265
|
+
return Object.assign((/** @type {?} */ value) => String(connection.quote(value)), { list: (/** @type {Array<?>} */ values) => values.map((value) => String(connection.quote(value))).join(", ") });
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Normalizes the aggregate specs (string shorthand or object) and validates each operation.
|
|
269
|
+
* @param {Record<string, AggregateSpec>} aggregates - Raw aggregate specs.
|
|
270
|
+
* @returns {Record<string, {op: AggregateOperation, column: string}>} - Normalized aggregate specs.
|
|
271
|
+
*/
|
|
272
|
+
_normalizeAggregates(aggregates) {
|
|
273
|
+
/** @type {Record<string, {op: AggregateOperation, column: string}>} */
|
|
274
|
+
const normalized = {};
|
|
275
|
+
for (const [name, spec] of Object.entries(aggregates)) {
|
|
276
|
+
const op = /** @type {AggregateOperation} */ ((typeof spec === "string" ? spec : spec.op).toUpperCase());
|
|
277
|
+
if (!AGGREGATE_OPERATIONS.has(op)) {
|
|
278
|
+
throw new Error(`Unsupported aggregate operation for ${name}: ${op}. Supported: ${Array.from(AGGREGATE_OPERATIONS).join(", ")}.`);
|
|
279
|
+
}
|
|
280
|
+
normalized[name] = { column: typeof spec === "string" ? name : spec.column, op };
|
|
281
|
+
}
|
|
282
|
+
return normalized;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuYW50LWFnZ3JlZ2F0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvdGVuYW50cy90ZW5hbnQtYWdncmVnYXRvci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosT0FBTyxPQUFPLE1BQU0sZUFBZSxDQUFBO0FBRW5DOztHQUVHO0FBRUg7O0dBRUc7QUFFSDs7Ozs7O0dBTUc7QUFFSDs7Ozs7Ozs7O0dBU0c7QUFFSDs7R0FFRztBQUVILE1BQU0sb0JBQW9CLEdBQUcsSUFBSSxHQUFHLENBQUMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFBO0FBQ3BFLE1BQU0sbUJBQW1CLEdBQUcsNEJBQTRCLENBQUE7QUFFeEQ7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sT0FBTyxnQkFBZ0I7SUFDbkM7OztPQUdHO0lBQ0gsWUFBWSxPQUFPO1FBQ2pCLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFBO1FBQ3RCLElBQUksQ0FBQyxhQUFhLEdBQUcsT0FBTyxDQUFDLGFBQWEsSUFBSSxPQUFPLENBQUMsYUFBYSxFQUFFLENBQUE7UUFDckUsSUFBSSxDQUFDLFVBQVUsR0FBRyxPQUFPLENBQUMsVUFBVSxJQUFJLEVBQUUsQ0FBQTtRQUMxQyxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUE7SUFDakUsQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxHQUFHO1FBQ1AsTUFBTSxlQUFlLEdBQUcsTUFBTSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUE7UUFFcEQsSUFBSSxlQUFlLENBQUMsTUFBTSxLQUFLLENBQUM7WUFBRSxPQUFPLEVBQUUsQ0FBQTtRQUUzQyx1Q0FBdUM7UUFDdkMsTUFBTSxJQUFJLEdBQUcsRUFBRSxDQUFBO1FBRWYsS0FBSyxNQUFNLEtBQUss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QUFJLEVBQUUsSUFBSSxDQUFDLElBQUksTUFBTSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQ3RELE1BQU0sRUFBRSxHQUFHLGlDQUFpQyxDQUFDLENBQUMsQ0FBQyxPQUFPLElBQUksS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDLENBQUE7WUFFeEcsSUFBSSxDQUFDLG9CQUFvQixDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDO2dCQUNsQyxNQUFNLElBQUksS0FBSyxDQUFDLHVDQUF1QyxJQUFJLEtBQUssRUFBRSxnQkFBZ0IsS0FBSyxDQUFDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUE7WUFDbkksQ0FBQztZQUVELFVBQVUsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFDLE1BQU0sRUFBRSxPQUFPLElBQUksS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxFQUFFLEVBQUMsQ0FBQTtRQUNoRixDQUFDO1FBRUQsT0FBTyxVQUFVLENBQUE7SUFDbkIsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmltcG9ydCBDdXJyZW50IGZyb20gXCIuLi9jdXJyZW50LmpzXCJcblxuLyoqXG4gKiBAdHlwZWRlZiB7XCJTVU1cIiB8IFwiQ09VTlRcIiB8IFwiTUFYXCIgfCBcIk1JTlwifSBBZ2dyZWdhdGVPcGVyYXRpb25cbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIHtBZ2dyZWdhdGVPcGVyYXRpb24gfCB7b3A6IEFnZ3JlZ2F0ZU9wZXJhdGlvbiwgY29sdW1uOiBzdHJpbmd9fSBBZ2dyZWdhdGVTcGVjXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiB7b2JqZWN0fSBTdWJxdWVyeUNvbnRleHRcbiAqIEBwcm9wZXJ0eSB7KHRhYmxlTmFtZTogc3RyaW5nKSA9PiBzdHJpbmd9IHRhYmxlIC0gUXVvdGVzIGEgdGFibGUgZm9yIHRoaXMgdGVuYW50LiBJbiBhIGNyb3NzLWRhdGFiYXNlIGBVTklPTiBBTExgIGl0IHJldHVybnMgYSBgZGF0YWJhc2VgLmB0YWJsZWAgcXVhbGlmaWVkIGlkZW50aWZpZXI7IHdoZW4gZWFjaCB0ZW5hbnQgcnVucyBvbiBpdHMgb3duIGNvbm5lY3Rpb24gaXQgcmV0dXJucyB0aGUgcGxhaW4gcXVvdGVkIHRhYmxlIG5hbWUuIFVzZSBpdCBmb3IgZXZlcnkgdGFibGUgdGhlIHN1YnF1ZXJ5IHJlYWRzIHNvIHRoZSBzYW1lIHN1YnF1ZXJ5IHdvcmtzIG9uIGJvdGggZXhlY3V0aW9uIHBhdGhzLlxuICogQHByb3BlcnR5IHsoKHZhbHVlOiA/KSA9PiBzdHJpbmcpICYge2xpc3Q6ICh2YWx1ZXM6IEFycmF5PD8+KSA9PiBzdHJpbmd9fSBxdW90ZSAtIFF1b3RlcyBhIHZhbHVlIGZvciB0aGUgYWN0aXZlIGNvbm5lY3Rpb24uIGBxdW90ZS5saXN0KHZhbHVlcylgIHF1b3RlcyBhbmQgY29tbWEtam9pbnMgYW4gYXJyYXkgZm9yIGBJTiAoLi4uKWAgY2xhdXNlcy5cbiAqIEBwcm9wZXJ0eSB7b2JqZWN0fSB0ZW5hbnQgLSBUaGUgdGVuYW50IGRlc2NyaXB0b3IgdGhpcyBzdWJxdWVyeSBpcyBiZWluZyBidWlsdCBmb3IuXG4gKiBAcHJvcGVydHkge2ltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0fSBjb25uZWN0aW9uIC0gVGhlIGRyaXZlciB0aGUgcXVlcnkgd2lsbCBydW4gb24uXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiB7b2JqZWN0fSBUZW5hbnRBZ2dyZWdhdGVPcHRpb25zXG4gKiBAcHJvcGVydHkge3N0cmluZ30gaWRlbnRpZmllciAtIERhdGFiYXNlIGlkZW50aWZpZXIgd2hvc2UgdGVuYW50cyBhcmUgYWdncmVnYXRlZCAoZm9yIGV4YW1wbGUgYFwicHJvamVjdFRlbmFudFwiYCkuXG4gKiBAcHJvcGVydHkge29iamVjdFtdfSBbdGVuYW50c10gLSBFeHBsaWNpdCB0ZW5hbnQgZGVzY3JpcHRvcnMgdG8gYWdncmVnYXRlLiBEZWZhdWx0cyB0byBldmVyeSB0ZW5hbnQgdGhlIGlkZW50aWZpZXIncyBwcm92aWRlciBgbGlzdFRlbmFudHNgIHJldHVybnMuXG4gKiBAcHJvcGVydHkgeyh0ZW5hbnQ6ID8pID0+IGJvb2xlYW59IFtmaWx0ZXJdIC0gT3B0aW9uYWwgZmlsdGVyIGFwcGxpZWQgdG8gdGhlIHJlc29sdmVkIHRlbmFudCBsaXN0LlxuICogQHByb3BlcnR5IHtzdHJpbmdbXX0gW2tleUNvbHVtbnNdIC0gQ29sdW1ucyB0aGUgYWdncmVnYXRlIGlzIGdyb3VwZWQgYnkgKGZvciBleGFtcGxlIGBbXCJkb2NrZXJfc2VydmVyX2lkXCJdYCkuIEVtcHR5IG1lYW5zIGEgc2luZ2xlIGdyYW5kLXRvdGFsIHJvdy5cbiAqIEBwcm9wZXJ0eSB7UmVjb3JkPHN0cmluZywgQWdncmVnYXRlU3BlYz59IGFnZ3JlZ2F0ZXMgLSBPdXRwdXQgY29sdW1uIG5hbWUgdG8gYWdncmVnYXRlLiBgXCJTVU1cImAgaXMgc2hvcnRoYW5kIGZvciBge29wOiBcIlNVTVwiLCBjb2x1bW46IDxvdXRwdXQgbmFtZT59YDsgdXNlIGB7b3A6IFwiQ09VTlRcIiwgY29sdW1uOiBcIipcIn1gIGZvciBgQ09VTlQoKilgLlxuICogQHByb3BlcnR5IHsoY29udGV4dDogU3VicXVlcnlDb250ZXh0KSA9PiBzdHJpbmd9IHN1YnF1ZXJ5IC0gQnVpbGRzIG9uZSB0ZW5hbnQncyBpbm5lciBgU0VMRUNUYCwgd2hpY2ggbXVzdCBzZWxlY3QgZXZlcnkgYGtleUNvbHVtbnNgIGVudHJ5IHBsdXMgZXZlcnkgYWdncmVnYXRlIHNvdXJjZSBjb2x1bW4uXG4gKiBAcHJvcGVydHkge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0gW2NvbmZpZ3VyYXRpb25dIC0gQ29uZmlndXJhdGlvbiB0byBydW4gYWdhaW5zdC4gRGVmYXVsdHMgdG8gdGhlIGN1cnJlbnQgY29uZmlndXJhdGlvbi5cbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIHt7dGVuYW50OiBvYmplY3QsIGRhdGFiYXNlOiBzdHJpbmcgfCB1bmRlZmluZWQsIHNlcnZlcktleTogc3RyaW5nfX0gUmVzb2x2ZWRUZW5hbnRcbiAqL1xuXG5jb25zdCBBR0dSRUdBVEVfT1BFUkFUSU9OUyA9IG5ldyBTZXQoW1wiU1VNXCIsIFwiQ09VTlRcIiwgXCJNQVhcIiwgXCJNSU5cIl0pXG5jb25zdCBERVJJVkVEX1RBQkxFX0FMSUFTID0gXCJ2ZWxvY2lvdXNfdGVuYW50X2FnZ3JlZ2F0ZVwiXG5cbi8qKlxuICogUnVucyBvbmUgYWdncmVnYXRlIHF1ZXJ5IGFjcm9zcyBtYW55IHRlbmFudCBkYXRhYmFzZXMgYW5kIG1lcmdlcyB0aGUgcmVzdWx0LiBUZW5hbnQgZGF0YWJhc2VzIG1heVxuICogYmUgY28tbG9jYXRlZCBvbiB0aGUgZGVmYXVsdCBzZXJ2ZXIgb3Igc3ByZWFkIGFjcm9zcyBvdGhlciBzZXJ2ZXJzLCBhbmQgdGhleSBjYW4gYXBwZWFyIG9yXG4gKiBkaXNhcHBlYXIgYXQgcnVudGltZTsgdGhpcyByZXNvbHZlcyB0aGUgbGl2ZSB0ZW5hbnQgbGlzdCwgZ3JvdXBzIHRlbmFudHMgYnkgdGhlIHNlcnZlciB0aGV5IGxpdmVcbiAqIG9uLCBhbmQg4oCUIHBlciBzZXJ2ZXIg4oCUIGVtaXRzIGEgc2luZ2xlIGNyb3NzLWRhdGFiYXNlIGBVTklPTiBBTExgIHdoZW4gdGhlIGRyaXZlciBzdXBwb3J0cyBxdWFsaWZpZWRcbiAqIGNyb3NzLWRhdGFiYXNlIHJlZmVyZW5jZXMgKE15U1FML01TU1FMKSBvciBmYWxscyBiYWNrIHRvIG9uZSBxdWVyeSBwZXIgdGVuYW50IG90aGVyd2lzZVxuICogKFBvc3RncmVTUUwvU1FMaXRlKS4gUmVzdWx0cyBmcm9tIGV2ZXJ5IHNlcnZlciBhcmUgbWVyZ2VkIHdpdGggdGhlIGFnZ3JlZ2F0ZSdzIG93biBvcGVyYXRpb24sIHNvXG4gKiBjYWxsZXJzIGdldCBvbmUgY29tYmluZWQgcmVzdWx0IHNldCByZWdhcmRsZXNzIG9mIGhvdyB0aGUgdGVuYW50cyBhcmUgZGlzdHJpYnV0ZWQuIFJlYWNoZWQgdGhyb3VnaFxuICogYFRlbmFudC5hZ2dyZWdhdGVBY3Jvc3NgLlxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBUZW5hbnRBZ2dyZWdhdG9yIHtcbiAgLyoqXG4gICAqIFByZXBhcmVzIGFuIGFnZ3JlZ2F0ZSBydW4sIG5vcm1hbGl6aW5nIHRoZSBhZ2dyZWdhdGUgc3BlY3MgdXAgZnJvbnQuXG4gICAqIEBwYXJhbSB7VGVuYW50QWdncmVnYXRlT3B0aW9uc30gb3B0aW9ucyAtIEFnZ3JlZ2F0ZSBjb25maWd1cmF0aW9uLlxuICAgKi9cbiAgY29uc3RydWN0b3Iob3B0aW9ucykge1xuICAgIHRoaXMub3B0aW9ucyA9IG9wdGlvbnNcbiAgICB0aGlzLmNvbmZpZ3VyYXRpb24gPSBvcHRpb25zLmNvbmZpZ3VyYXRpb24gPz8gQ3VycmVudC5jb25maWd1cmF0aW9uKClcbiAgICB0aGlzLmtleUNvbHVtbnMgPSBvcHRpb25zLmtleUNvbHVtbnMgPz8gW11cbiAgICB0aGlzLmFnZ3JlZ2F0ZXMgPSB0aGlzLl9ub3JtYWxpemVBZ2dyZWdhdGVzKG9wdGlvbnMuYWdncmVnYXRlcylcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNvbHZlcyB0aGUgdGVuYW50IGxpc3QsIHJ1bnMgdGhlIGFnZ3JlZ2F0ZSBwZXIgc2VydmVyLCBhbmQgbWVyZ2VzIGV2ZXJ5dGhpbmcuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPEFycmF5PFJlY29yZDxzdHJpbmcsID8+Pj59IC0gT25lIG1lcmdlZCByb3cgcGVyIGRpc3RpbmN0IGtleS1jb2x1bW4gY29tYmluYXRpb24uXG4gICAqL1xuICBhc3luYyBydW4oKSB7XG4gICAgY29uc3QgcmVzb2x2ZWRUZW5hbnRzID0gYXdhaXQgdGhpcy5fcmVzb2x2ZVRlbmFudHMoKVxuXG4gICAgaWYgKHJlc29sdmVkVGVuYW50cy5sZW5ndGggPT09IDApIHJldHVybiBbXVxuXG4gICAgLyoqIEB0eXBlIHtBcnJheTxSZWNvcmQ8c3RyaW5nLCA/Pj59ICovXG4gICAgY29uc3Qgcm93cyA9IFtdXG5cbiAgICBmb3IgKGNvbnN0IGdyb3VwIG9mIHRoaXMuX2dyb3VwQnlTZXJ2ZXIocmVzb2x2ZWRUZW5hbnRzKSkge1xuICAgICAgY29uc3QgZ3JvdXBSb3dzID0gYXdhaXQgdGhpcy5fcnVuRm9yU2VydmVyKGdyb3VwKVxuXG4gICAgICByb3dzLnB1c2goLi4uZ3JvdXBSb3dzKVxuICAgIH1cblxuICAgIHJldHVybiB0aGlzLl9tZXJnZVJvd3Mocm93cylcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNvbHZlcyB0aGUgdGVuYW50IGxpc3QgKGV4cGxpY2l0IG9yIGZyb20gdGhlIHByb3ZpZGVyKSwgdGhlbiBtYXBzIGVhY2ggdG8gaXRzIHNlcnZlciBhbmRcbiAgICogZGF0YWJhc2UgdmlhIHRoZSB0ZW5hbnQgZGF0YWJhc2UgcmVzb2x2ZXIuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFJlc29sdmVkVGVuYW50W10+fSAtIFRlbmFudHMgdG8gYWdncmVnYXRlLCBlYWNoIHJlc29sdmVkIHRvIGl0cyBzZXJ2ZXIgKyBkYXRhYmFzZS5cbiAgICovXG4gIGFzeW5jIF9yZXNvbHZlVGVuYW50cygpIHtcbiAgICBjb25zdCB0ZW5hbnRzID0gdGhpcy5vcHRpb25zLnRlbmFudHMgPz8gYXdhaXQgdGhpcy5fbGlzdFByb3ZpZGVyVGVuYW50cygpXG4gICAgY29uc3QgZmlsdGVyZWQgPSB0aGlzLm9wdGlvbnMuZmlsdGVyID8gdGVuYW50cy5maWx0ZXIodGhpcy5vcHRpb25zLmZpbHRlcikgOiB0ZW5hbnRzXG5cbiAgICByZXR1cm4gZmlsdGVyZWQubWFwKCh0ZW5hbnQpID0+IHtcbiAgICAgIGNvbnN0IGRhdGFiYXNlQ29uZmlndXJhdGlvbiA9IHRoaXMuY29uZmlndXJhdGlvbi5yZXNvbHZlRGF0YWJhc2VDb25maWd1cmF0aW9uKHRoaXMub3B0aW9ucy5pZGVudGlmaWVyLCB0ZW5hbnQpXG5cbiAgICAgIC8vIFRoZSBzZXJ2ZXIga2V5IGdyb3VwcyB0ZW5hbnRzIHRoYXQgc2hhcmUgYSBjb25uZWN0aW9uIGVuZHBvaW50IHNvIGNvLWxvY2F0ZWQgdGVuYW50cyBjYW4gYmVcbiAgICAgIC8vIFVOSU9OLWVkIG9uIG9uZSBjb25uZWN0aW9uLiBJdCBvbmx5IG5lZWRzIHRvIGJlIGV4YWN0IGZvciB0aGUgY3Jvc3MtZGF0YWJhc2UgYFVOSU9OIEFMTGBcbiAgICAgIC8vIHBhdGgsIHdoaWNoIGlzIE15U1FMLW9ubHksIHdoZXJlIGhvc3QvcG9ydC91c2VybmFtZS90eXBlIGZ1bGx5IGlkZW50aWZ5IHRoZSBzZXJ2ZXIuIEV2ZXJ5XG4gICAgICAvLyBvdGhlciBkcml2ZXIgdGFrZXMgdGhlIGZhbi1vdXQgcGF0aCAob25lIGNvbm5lY3Rpb24gcGVyIHRlbmFudCksIHNvIGEgY29hcnNlIGtleSB0aGVyZSBqdXN0XG4gICAgICAvLyBncm91cHMgdGVuYW50cyB0aGF0IGFyZSB0aGVuIHF1ZXJpZWQgaW5kaXZpZHVhbGx5IGFueXdheSDigJQgaXQgY2FuIG5ldmVyIHJvdXRlIGEgcXVlcnkgdG8gdGhlXG4gICAgICAvLyB3cm9uZyBzZXJ2ZXIuXG4gICAgICByZXR1cm4ge1xuICAgICAgICBkYXRhYmFzZTogZGF0YWJhc2VDb25maWd1cmF0aW9uLmRhdGFiYXNlLFxuICAgICAgICBzZXJ2ZXJLZXk6IEpTT04uc3RyaW5naWZ5KFtcbiAgICAgICAgICBkYXRhYmFzZUNvbmZpZ3VyYXRpb24udHlwZSA/PyBudWxsLFxuICAgICAgICAgIGRhdGFiYXNlQ29uZmlndXJhdGlvbi5ob3N0ID8/IG51bGwsXG4gICAgICAgICAgZGF0YWJhc2VDb25maWd1cmF0aW9uLnBvcnQgPz8gbnVsbCxcbiAgICAgICAgICBkYXRhYmFzZUNvbmZpZ3VyYXRpb24udXNlcm5hbWUgPz8gbnVsbFxuICAgICAgICBdKSxcbiAgICAgICAgdGVuYW50XG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBMaXN0cyBldmVyeSB0ZW5hbnQgZm9yIHRoZSBpZGVudGlmaWVyIHRocm91Z2ggaXRzIHByb3ZpZGVyJ3MgYGxpc3RUZW5hbnRzYCBob29rLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxvYmplY3RbXT59IC0gRXZlcnkgdGVuYW50IHRoZSBwcm92aWRlciBsaXN0cyBmb3IgdGhlIGlkZW50aWZpZXIuXG4gICAqL1xuICBhc3luYyBfbGlzdFByb3ZpZGVyVGVuYW50cygpIHtcbiAgICBjb25zdCBwcm92aWRlciA9IHRoaXMuY29uZmlndXJhdGlvbi5nZXRUZW5hbnREYXRhYmFzZVByb3ZpZGVyKHRoaXMub3B0aW9ucy5pZGVudGlmaWVyKVxuICAgIGNvbnN0IGxpc3RlZFRlbmFudHMgPSBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24uZW5zdXJlQ29ubmVjdGlvbnMoXG4gICAgICB7bmFtZTogYFRlbmFudC5hZ2dyZWdhdGVBY3Jvc3M6ICR7dGhpcy5vcHRpb25zLmlkZW50aWZpZXJ9YH0sXG4gICAgICBhc3luYyAoKSA9PiBhd2FpdCBwcm92aWRlci5saXN0VGVuYW50cyh7Y29uZmlndXJhdGlvbjogdGhpcy5jb25maWd1cmF0aW9uLCBpZGVudGlmaWVyOiB0aGlzLm9wdGlvbnMuaWRlbnRpZmllcn0pXG4gICAgKVxuXG4gICAgaWYgKCFBcnJheS5pc0FycmF5KGxpc3RlZFRlbmFudHMpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFRlbmFudCBkYXRhYmFzZSBwcm92aWRlciBmb3IgJHt0aGlzLm9wdGlvbnMuaWRlbnRpZmllcn0gbXVzdCByZXR1cm4gYW4gYXJyYXkgZnJvbSBsaXN0VGVuYW50c2ApXG4gICAgfVxuXG4gICAgcmV0dXJuIGxpc3RlZFRlbmFudHNcbiAgfVxuXG4gIC8qKlxuICAgKiBHcm91cHMgdGVuYW50cyBieSB0aGUgc2VydmVyIHRoZXkgbGl2ZSBvbiBzbyBjby1sb2NhdGVkIHRlbmFudHMgY2FuIHNoYXJlIG9uZSBxdWVyeS5cbiAgICogQHBhcmFtIHtSZXNvbHZlZFRlbmFudFtdfSByZXNvbHZlZFRlbmFudHMgLSBUZW5hbnRzIHJlc29sdmVkIHRvIHRoZWlyIHNlcnZlci5cbiAgICogQHJldHVybnMge1Jlc29sdmVkVGVuYW50W11bXX0gLSBUZW5hbnRzIGdyb3VwZWQgYnkgdGhlIHNlcnZlciB0aGV5IGxpdmUgb24uXG4gICAqL1xuICBfZ3JvdXBCeVNlcnZlcihyZXNvbHZlZFRlbmFudHMpIHtcbiAgICAvKiogQHR5cGUge01hcDxzdHJpbmcsIFJlc29sdmVkVGVuYW50W10+fSAqL1xuICAgIGNvbnN0IGdyb3VwcyA9IG5ldyBNYXAoKVxuXG4gICAgZm9yIChjb25zdCByZXNvbHZlZFRlbmFudCBvZiByZXNvbHZlZFRlbmFudHMpIHtcbiAgICAgIGNvbnN0IGdyb3VwID0gZ3JvdXBzLmdldChyZXNvbHZlZFRlbmFudC5zZXJ2ZXJLZXkpXG5cbiAgICAgIGlmIChncm91cCkge1xuICAgICAgICBncm91cC5wdXNoKHJlc29sdmVkVGVuYW50KVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZ3JvdXBzLnNldChyZXNvbHZlZFRlbmFudC5zZXJ2ZXJLZXksIFtyZXNvbHZlZFRlbmFudF0pXG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIEFycmF5LmZyb20oZ3JvdXBzLnZhbHVlcygpKVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgdGhlIGFnZ3JlZ2F0ZSBmb3Igb25lIHNlcnZlcidzIHRlbmFudHMsIHVzaW5nIGEgc2luZ2xlIGNyb3NzLWRhdGFiYXNlIGBVTklPTiBBTExgIHdoZW4gdGhlXG4gICAqIGRyaXZlciBzdXBwb3J0cyBpdCBvciBvbmUgcXVlcnkgcGVyIHRlbmFudCBvdGhlcndpc2UuIFRoZSBkcml2ZXIgY2FwYWJpbGl0eSBpcyBwcm9iZWQgaW4gaXRzIG93blxuICAgKiBjb25uZWN0aW9uIHNjb3BlIHRoYXQgaXMgcmVsZWFzZWQgYmVmb3JlIHRoZSBmYW4tb3V0IHJ1bnMsIHNvIGEgYG1heDogMWAgdGVuYW50IHBvb2wgaXMgbmV2ZXJcbiAgICogYXNrZWQgZm9yIGEgc2Vjb25kIGNvbm5lY3Rpb24gd2hpbGUgdGhlIGZpcnN0IGlzIHN0aWxsIGhlbGQgKHdoaWNoIHdvdWxkIGRlYWRsb2NrKS5cbiAgICogQHBhcmFtIHtSZXNvbHZlZFRlbmFudFtdfSBncm91cCAtIFRlbmFudHMgc2hhcmluZyBvbmUgc2VydmVyLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxBcnJheTxSZWNvcmQ8c3RyaW5nLCA/Pj4+fSAtIFJvd3MgcHJvZHVjZWQgZm9yIHRoaXMgc2VydmVyLlxuICAgKi9cbiAgYXN5bmMgX3J1bkZvclNlcnZlcihncm91cCkge1xuICAgIGNvbnN0IFtmaXJzdFRlbmFudF0gPSBncm91cFxuICAgIGNvbnN0IHN1cHBvcnRzQ3Jvc3NEYXRhYmFzZVJlZmVyZW5jZXMgPSBhd2FpdCB0aGlzLl93aXRoVGVuYW50KFxuICAgICAgZmlyc3RUZW5hbnQudGVuYW50LFxuICAgICAgYXN5bmMgKGNvbm5lY3Rpb25zKSA9PiBjb25uZWN0aW9uc1t0aGlzLm9wdGlvbnMuaWRlbnRpZmllcl0uc3VwcG9ydHNDcm9zc0RhdGFiYXNlUmVmZXJlbmNlcygpXG4gICAgKVxuXG4gICAgaWYgKHN1cHBvcnRzQ3Jvc3NEYXRhYmFzZVJlZmVyZW5jZXMpIHtcbiAgICAgIHJldHVybiBhd2FpdCB0aGlzLl93aXRoVGVuYW50KGZpcnN0VGVuYW50LnRlbmFudCwgYXN5bmMgKGNvbm5lY3Rpb25zKSA9PiB7XG4gICAgICAgIGNvbnN0IGNvbm5lY3Rpb24gPSBjb25uZWN0aW9uc1t0aGlzLm9wdGlvbnMuaWRlbnRpZmllcl1cblxuICAgICAgICByZXR1cm4gYXdhaXQgY29ubmVjdGlvbi5xdWVyeSh0aGlzLmJ1aWxkQWdncmVnYXRlU3FsKHtjb25uZWN0aW9uLCBlbnRyaWVzOiBncm91cCwgcXVhbGlmaWVkOiB0cnVlfSkpXG4gICAgICB9KVxuICAgIH1cblxuICAgIC8qKiBAdHlwZSB7QXJyYXk8UmVjb3JkPHN0cmluZywgPz4+fSAqL1xuICAgIGNvbnN0IHJvd3MgPSBbXVxuXG4gICAgZm9yIChjb25zdCByZXNvbHZlZFRlbmFudCBvZiBncm91cCkge1xuICAgICAgY29uc3QgdGVuYW50Um93cyA9IGF3YWl0IHRoaXMuX3dpdGhUZW5hbnQocmVzb2x2ZWRUZW5hbnQudGVuYW50LCBhc3luYyAoY29ubmVjdGlvbnMpID0+IHtcbiAgICAgICAgY29uc3QgY29ubmVjdGlvbiA9IGNvbm5lY3Rpb25zW3RoaXMub3B0aW9ucy5pZGVudGlmaWVyXVxuXG4gICAgICAgIHJldHVybiBhd2FpdCBjb25uZWN0aW9uLnF1ZXJ5KHRoaXMuYnVpbGRBZ2dyZWdhdGVTcWwoe2Nvbm5lY3Rpb24sIGVudHJpZXM6IFtyZXNvbHZlZFRlbmFudF0sIHF1YWxpZmllZDogZmFsc2V9KSlcbiAgICAgIH0pXG5cbiAgICAgIHJvd3MucHVzaCguLi50ZW5hbnRSb3dzKVxuICAgIH1cblxuICAgIHJldHVybiByb3dzXG4gIH1cblxuICAvKipcbiAgICogUnVucyBgY2FsbGJhY2tgIGluc2lkZSBhIHRlbmFudCdzIGNvbnRleHQgd2l0aCBpdHMgY29ubmVjdGlvbnMgZXN0YWJsaXNoZWQsIG1pcnJvcmluZ1xuICAgKiBgVGVuYW50LndpdGhgIHdpdGhvdXQgaW1wb3J0aW5nIGl0ICh3aGljaCB3b3VsZCBjcmVhdGUgYW4gaW1wb3J0IGN5Y2xlKS5cbiAgICogQHRlbXBsYXRlIFRcbiAgICogQHBhcmFtIHtvYmplY3R9IHRlbmFudCAtIFRlbmFudCBkZXNjcmlwdG9yIHRvIHN3aXRjaCBpbnRvLlxuICAgKiBAcGFyYW0geyhjb25uZWN0aW9uczogUmVjb3JkPHN0cmluZywgaW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHQ+KSA9PiBQcm9taXNlPFQ+fSBjYWxsYmFjayAtIE9wZXJhdGlvbiB0byBydW4gd2l0aCB0aGUgdGVuYW50J3MgYWN0aXZlIGNvbm5lY3Rpb25zLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxUPn0gLSBDYWxsYmFjayByZXN1bHQgZnJvbSB3aXRoaW4gdGhlIHRlbmFudCBjb250ZXh0LlxuICAgKi9cbiAgYXN5bmMgX3dpdGhUZW5hbnQodGVuYW50LCBjYWxsYmFjaykge1xuICAgIHJldHVybiBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24ucnVuV2l0aFRlbmFudCh0ZW5hbnQsIGFzeW5jICgpID0+IGF3YWl0IHRoaXMuY29uZmlndXJhdGlvbi5lbnN1cmVDb25uZWN0aW9ucyhjYWxsYmFjaykpXG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIHRoZSBhZ2dyZWdhdGUgU1FMOiBhbiBvdXRlciBgR1JPVVAgQllgIG92ZXIgYSBgVU5JT04gQUxMYCBvZiBlYWNoIGVudHJ5J3Mgc3VicXVlcnkuXG4gICAqIEBwYXJhbSB7e2Nvbm5lY3Rpb246IGltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0LCBlbnRyaWVzOiBSZXNvbHZlZFRlbmFudFtdLCBxdWFsaWZpZWQ6IGJvb2xlYW59fSBhcmdzIC0gQ29ubmVjdGlvbiwgdGVuYW50cywgYW5kIHdoZXRoZXIgdG8gcXVhbGlmeSB0YWJsZXMgd2l0aCB0aGVpciBkYXRhYmFzZSBuYW1lLlxuICAgKiBAcmV0dXJucyB7c3RyaW5nfSAtIEV4ZWN1dGFibGUgYWdncmVnYXRlIFNRTC5cbiAgICovXG4gIGJ1aWxkQWdncmVnYXRlU3FsKHtjb25uZWN0aW9uLCBlbnRyaWVzLCBxdWFsaWZpZWR9KSB7XG4gICAgY29uc3Qgb3B0aW9ucyA9IGNvbm5lY3Rpb24ub3B0aW9ucygpXG4gICAgY29uc3QgcXVvdGUgPSB0aGlzLl9idWlsZFF1b3RlKGNvbm5lY3Rpb24pXG4gICAgY29uc3Qgc3VicXVlcmllcyA9IGVudHJpZXMubWFwKChlbnRyeSkgPT4ge1xuICAgICAgaWYgKHF1YWxpZmllZCAmJiAhZW50cnkuZGF0YWJhc2UpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBDYW5ub3QgYnVpbGQgYSBjcm9zcy1kYXRhYmFzZSBxdWVyeSBmb3IgYSB0ZW5hbnQgd2l0aG91dCBhIHJlc29sdmVkIGRhdGFiYXNlIG5hbWUgKGlkZW50aWZpZXI6ICR7dGhpcy5vcHRpb25zLmlkZW50aWZpZXJ9KS5gKVxuICAgICAgfVxuXG4gICAgICBjb25zdCB0YWJsZSA9ICgvKiogQHR5cGUge3N0cmluZ30gKi8gdGFibGVOYW1lKSA9PiBxdWFsaWZpZWRcbiAgICAgICAgPyBgJHtvcHRpb25zLnF1b3RlRGF0YWJhc2VOYW1lKC8qKiBAdHlwZSB7c3RyaW5nfSAqLyAoZW50cnkuZGF0YWJhc2UpKX0uJHtvcHRpb25zLnF1b3RlVGFibGVOYW1lKHRhYmxlTmFtZSl9YFxuICAgICAgICA6IG9wdGlvbnMucXVvdGVUYWJsZU5hbWUodGFibGVOYW1lKVxuXG4gICAgICByZXR1cm4gdGhpcy5vcHRpb25zLnN1YnF1ZXJ5KHtjb25uZWN0aW9uLCBxdW90ZSwgdGFibGUsIHRlbmFudDogZW50cnkudGVuYW50fSlcbiAgICB9KVxuXG4gICAgY29uc3Qgc2VsZWN0UGFydHMgPSB0aGlzLmtleUNvbHVtbnMubWFwKChrZXlDb2x1bW4pID0+IG9wdGlvbnMucXVvdGVDb2x1bW5OYW1lKGtleUNvbHVtbikpXG5cbiAgICBmb3IgKGNvbnN0IFtuYW1lLCBzcGVjXSBvZiBPYmplY3QuZW50cmllcyh0aGlzLmFnZ3JlZ2F0ZXMpKSB7XG4gICAgICBjb25zdCBhZ2dyZWdhdGVBcmd1bWVudCA9IHNwZWMuY29sdW1uID09PSBcIipcIiA/IFwiKlwiIDogb3B0aW9ucy5xdW90ZUNvbHVtbk5hbWUoc3BlYy5jb2x1bW4pXG5cbiAgICAgIHNlbGVjdFBhcnRzLnB1c2goYCR7c3BlYy5vcH0oJHthZ2dyZWdhdGVBcmd1bWVudH0pIEFTICR7b3B0aW9ucy5xdW90ZUNvbHVtbk5hbWUobmFtZSl9YClcbiAgICB9XG5cbiAgICBjb25zdCB1bmlvblNxbCA9IHN1YnF1ZXJpZXMubWFwKChzdWJxdWVyeSkgPT4gYFNFTEVDVCAqIEZST00gKCR7c3VicXVlcnl9KSAke29wdGlvbnMucXVvdGVUYWJsZU5hbWUoYCR7REVSSVZFRF9UQUJMRV9BTElBU31fc291cmNlYCl9YCkuam9pbihcIlxcblVOSU9OIEFMTFxcblwiKVxuICAgIGNvbnN0IGdyb3VwQnlTcWwgPSB0aGlzLmtleUNvbHVtbnMubGVuZ3RoID4gMFxuICAgICAgPyBgXFxuR1JPVVAgQlkgJHt0aGlzLmtleUNvbHVtbnMubWFwKChrZXlDb2x1bW4pID0+IG9wdGlvbnMucXVvdGVDb2x1bW5OYW1lKGtleUNvbHVtbikpLmpvaW4oXCIsIFwiKX1gXG4gICAgICA6IFwiXCJcblxuICAgIHJldHVybiBgU0VMRUNUICR7c2VsZWN0UGFydHMuam9pbihcIiwgXCIpfVxcbkZST00gKFxcbiR7dW5pb25TcWx9XFxuKSAke29wdGlvbnMucXVvdGVUYWJsZU5hbWUoREVSSVZFRF9UQUJMRV9BTElBUyl9JHtncm91cEJ5U3FsfWBcbiAgfVxuXG4gIC8qKlxuICAgKiBNZXJnZXMgcm93cyBmcm9tIGV2ZXJ5IHNlcnZlciBieSBjb21iaW5pbmcgZWFjaCBhZ2dyZWdhdGUgd2l0aCBpdHMgb3duIG9wZXJhdGlvbi4gQSBgTlVMTGBcbiAgICogYWdncmVnYXRlIHZhbHVlIChhbiBlbXB0eSB0ZW5hbnQncyBgU1VNYC9gTUFYYC9gTUlOYCByZXR1cm5zIGBOVUxMYCBvbiB0aGUgZmFuLW91dCBwYXRoKSBpc1xuICAgKiB0cmVhdGVkIGFzIFwibm8gY29udHJpYnV0aW9uXCIgYW5kIHNraXBwZWQsIG5vdCBjb2VyY2VkIHRvIGAwYCDigJQgb3RoZXJ3aXNlIGFuIGVtcHR5IHRlbmFudCB3b3VsZFxuICAgKiBkcmFnIGEgYE1BWGAgb2YgbmVnYXRpdmVzIG9yIGEgYE1JTmAgb2YgcG9zaXRpdmVzIHRvIGAwYC4gQSBrZXkgd2hvc2UgZXZlcnkgdGVuYW50IGNvbnRyaWJ1dGVkXG4gICAqIGBOVUxMYCBzdGF5cyBgTlVMTGAsIG1hdGNoaW5nIFNRTCBhZ2dyZWdhdGUgc2VtYW50aWNzIG92ZXIgbm8gcm93cy5cbiAgICogQHBhcmFtIHtBcnJheTxSZWNvcmQ8c3RyaW5nLCA/Pj59IHJvd3MgLSBSb3dzIGNvbGxlY3RlZCBmcm9tIGFsbCBzZXJ2ZXJzL3RlbmFudHMuXG4gICAqIEByZXR1cm5zIHtBcnJheTxSZWNvcmQ8c3RyaW5nLCA/Pj59IC0gT25lIG1lcmdlZCByb3cgcGVyIGRpc3RpbmN0IGtleS1jb2x1bW4gY29tYmluYXRpb24uXG4gICAqL1xuICBfbWVyZ2VSb3dzKHJvd3MpIHtcbiAgICAvKiogQHR5cGUge01hcDxzdHJpbmcsIFJlY29yZDxzdHJpbmcsID8+Pn0gKi9cbiAgICBjb25zdCBtZXJnZWQgPSBuZXcgTWFwKClcblxuICAgIGZvciAoY29uc3Qgcm93IG9mIHJvd3MpIHtcbiAgICAgIGNvbnN0IG1hcEtleSA9IEpTT04uc3RyaW5naWZ5KHRoaXMua2V5Q29sdW1ucy5tYXAoKGtleUNvbHVtbikgPT4gcm93W2tleUNvbHVtbl0pKVxuICAgICAgbGV0IGFjY3VtdWxhdG9yID0gbWVyZ2VkLmdldChtYXBLZXkpXG5cbiAgICAgIGlmICghYWNjdW11bGF0b3IpIHtcbiAgICAgICAgYWNjdW11bGF0b3IgPSB7fVxuXG4gICAgICAgIGZvciAoY29uc3Qga2V5Q29sdW1uIG9mIHRoaXMua2V5Q29sdW1ucykge1xuICAgICAgICAgIGFjY3VtdWxhdG9yW2tleUNvbHVtbl0gPSByb3dba2V5Q29sdW1uXVxuICAgICAgICB9XG5cbiAgICAgICAgZm9yIChjb25zdCBuYW1lIG9mIE9iamVjdC5rZXlzKHRoaXMuYWdncmVnYXRlcykpIHtcbiAgICAgICAgICBhY2N1bXVsYXRvcltuYW1lXSA9IG51bGxcbiAgICAgICAgfVxuXG4gICAgICAgIG1lcmdlZC5zZXQobWFwS2V5LCBhY2N1bXVsYXRvcilcbiAgICAgIH1cblxuICAgICAgZm9yIChjb25zdCBbbmFtZSwgc3BlY10gb2YgT2JqZWN0LmVudHJpZXModGhpcy5hZ2dyZWdhdGVzKSkge1xuICAgICAgICBjb25zdCByYXdWYWx1ZSA9IHJvd1tuYW1lXVxuXG4gICAgICAgIGlmIChyYXdWYWx1ZSA9PT0gbnVsbCB8fCByYXdWYWx1ZSA9PT0gdW5kZWZpbmVkKSBjb250aW51ZVxuXG4gICAgICAgIGNvbnN0IHZhbHVlID0gdGhpcy5fdG9FeGFjdE51bWJlcihyYXdWYWx1ZSlcblxuICAgICAgICBhY2N1bXVsYXRvcltuYW1lXSA9IGFjY3VtdWxhdG9yW25hbWVdID09PSBudWxsID8gdmFsdWUgOiB0aGlzLl9jb21iaW5lKHNwZWMub3AsIGFjY3VtdWxhdG9yW25hbWVdLCB2YWx1ZSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gQXJyYXkuZnJvbShtZXJnZWQudmFsdWVzKCkpXG4gIH1cblxuICAvKipcbiAgICogQ29tYmluZXMgdHdvIG5vbi1udWxsIHBlci1zZXJ2ZXIgYWdncmVnYXRlIHZhbHVlcyB3aXRoIHRoZSBhZ2dyZWdhdGUncyBvd24gb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0ge0FnZ3JlZ2F0ZU9wZXJhdGlvbn0gb3AgLSBBZ2dyZWdhdGUgb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0ge251bWJlcn0gY3VycmVudCAtIEFjY3VtdWxhdGVkIHZhbHVlLlxuICAgKiBAcGFyYW0ge251bWJlcn0gdmFsdWUgLSBJbmNvbWluZyBwZXItc2VydmVyIHZhbHVlLlxuICAgKiBAcmV0dXJucyB7bnVtYmVyfSAtIENvbWJpbmVkIHZhbHVlLlxuICAgKi9cbiAgX2NvbWJpbmUob3AsIGN1cnJlbnQsIHZhbHVlKSB7XG4gICAgaWYgKG9wID09PSBcIk1BWFwiKSByZXR1cm4gTWF0aC5tYXgoY3VycmVudCwgdmFsdWUpXG4gICAgaWYgKG9wID09PSBcIk1JTlwiKSByZXR1cm4gTWF0aC5taW4oY3VycmVudCwgdmFsdWUpXG5cbiAgICByZXR1cm4gY3VycmVudCArIHZhbHVlXG4gIH1cblxuICAvKipcbiAgICogQ29udmVydHMgYSBkcml2ZXItcmV0dXJuZWQgYWdncmVnYXRlIHZhbHVlIHRvIGEgbnVtYmVyLCBmYWlsaW5nIGxvdWRseSByYXRoZXIgdGhhbiBzaWxlbnRseVxuICAgKiBsb3NpbmcgcHJlY2lzaW9uLiBEcml2ZXJzIHJldHVybiBleGFjdCBpbnRlZ2VyIGFnZ3JlZ2F0ZXMgKE15U1FMIGBTVU1gL2BDT1VOVGAsIFBvc3RncmVTUUxcbiAgICogYGJpZ2ludGApIGFzIHN0cmluZ3M7IGFuIGludGVnZXIgYmV5b25kIGBOdW1iZXIuTUFYX1NBRkVfSU5URUdFUmAgY2Fubm90IGJlIHJlcHJlc2VudGVkIGV4YWN0bHlcbiAgICogYXMgYSBKUyBudW1iZXIsIHNvIGNyb3NzLXNlcnZlciBtZXJnaW5nIHdvdWxkIGNvcnJ1cHQgdGhlIHJlc3VsdCDigJQgdGhyb3cgaW5zdGVhZC4gKEZyYWN0aW9uYWxcbiAgICogYERFQ0lNQUxgL2BOVU1FUklDYCB2YWx1ZXMgYXJlIHN0aWxsIHN1YmplY3QgdG8gbm9ybWFsIGZsb2F0aW5nLXBvaW50IHJlcHJlc2VudGF0aW9uLilcbiAgICogQHBhcmFtIHs/fSByYXdWYWx1ZSAtIFZhbHVlIHJldHVybmVkIGJ5IHRoZSBkcml2ZXIgZm9yIGFuIGFnZ3JlZ2F0ZSBjb2x1bW4uXG4gICAqIEByZXR1cm5zIHtudW1iZXJ9IC0gVGhlIHZhbHVlIGFzIGEgbnVtYmVyLlxuICAgKi9cbiAgX3RvRXhhY3ROdW1iZXIocmF3VmFsdWUpIHtcbiAgICBjb25zdCB2YWx1ZSA9IE51bWJlcihyYXdWYWx1ZSlcblxuICAgIGlmICh0eXBlb2YgcmF3VmFsdWUgPT09IFwic3RyaW5nXCIgJiYgL14tP1xcZCskLy50ZXN0KHJhd1ZhbHVlLnRyaW0oKSkgJiYgIU51bWJlci5pc1NhZmVJbnRlZ2VyKHZhbHVlKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBBZ2dyZWdhdGUgdmFsdWUgJHtyYXdWYWx1ZX0gZXhjZWVkcyB0aGUgc2FmZS1pbnRlZ2VyIHJhbmdlIGFuZCBjYW5ub3QgYmUgbWVyZ2VkIHdpdGhvdXQgbG9zaW5nIHByZWNpc2lvbi5gKVxuICAgIH1cblxuICAgIHJldHVybiB2YWx1ZVxuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkcyB0aGUgdmFsdWUgcXVvdGVyIHBhc3NlZCB0byBzdWJxdWVyaWVzLCB3aXRoIGEgYC5saXN0YCBoZWxwZXIgZm9yIGBJTiAoLi4uKWAgY2xhdXNlcy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9kcml2ZXJzL2Jhc2UuanNcIikuZGVmYXVsdH0gY29ubmVjdGlvbiAtIERyaXZlciB3aG9zZSBxdW90aW5nIGlzIHVzZWQuXG4gICAqIEByZXR1cm5zIHsoKHZhbHVlOiA/KSA9PiBzdHJpbmcpICYge2xpc3Q6ICh2YWx1ZXM6IEFycmF5PD8+KSA9PiBzdHJpbmd9fSAtIFZhbHVlIHF1b3RlciB3aXRoIGEgYC5saXN0YCBoZWxwZXIuXG4gICAqL1xuICBfYnVpbGRRdW90ZShjb25uZWN0aW9uKSB7XG4gICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oXG4gICAgICAoLyoqIEB0eXBlIHs/fSAqLyB2YWx1ZSkgPT4gU3RyaW5nKGNvbm5lY3Rpb24ucXVvdGUodmFsdWUpKSxcbiAgICAgIHtsaXN0OiAoLyoqIEB0eXBlIHtBcnJheTw/Pn0gKi8gdmFsdWVzKSA9PiB2YWx1ZXMubWFwKCh2YWx1ZSkgPT4gU3RyaW5nKGNvbm5lY3Rpb24ucXVvdGUodmFsdWUpKSkuam9pbihcIiwgXCIpfVxuICAgIClcbiAgfVxuXG4gIC8qKlxuICAgKiBOb3JtYWxpemVzIHRoZSBhZ2dyZWdhdGUgc3BlY3MgKHN0cmluZyBzaG9ydGhhbmQgb3Igb2JqZWN0KSBhbmQgdmFsaWRhdGVzIGVhY2ggb3BlcmF0aW9uLlxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIEFnZ3JlZ2F0ZVNwZWM+fSBhZ2dyZWdhdGVzIC0gUmF3IGFnZ3JlZ2F0ZSBzcGVjcy5cbiAgICogQHJldHVybnMge1JlY29yZDxzdHJpbmcsIHtvcDogQWdncmVnYXRlT3BlcmF0aW9uLCBjb2x1bW46IHN0cmluZ30+fSAtIE5vcm1hbGl6ZWQgYWdncmVnYXRlIHNwZWNzLlxuICAgKi9cbiAgX25vcm1hbGl6ZUFnZ3JlZ2F0ZXMoYWdncmVnYXRlcykge1xuICAgIC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZywge29wOiBBZ2dyZWdhdGVPcGVyYXRpb24sIGNvbHVtbjogc3RyaW5nfT59ICovXG4gICAgY29uc3Qgbm9ybWFsaXplZCA9IHt9XG5cbiAgICBmb3IgKGNvbnN0IFtuYW1lLCBzcGVjXSBvZiBPYmplY3QuZW50cmllcyhhZ2dyZWdhdGVzKSkge1xuICAgICAgY29uc3Qgb3AgPSAvKiogQHR5cGUge0FnZ3JlZ2F0ZU9wZXJhdGlvbn0gKi8gKCh0eXBlb2Ygc3BlYyA9PT0gXCJzdHJpbmdcIiA/IHNwZWMgOiBzcGVjLm9wKS50b1VwcGVyQ2FzZSgpKVxuXG4gICAgICBpZiAoIUFHR1JFR0FURV9PUEVSQVRJT05TLmhhcyhvcCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbnN1cHBvcnRlZCBhZ2dyZWdhdGUgb3BlcmF0aW9uIGZvciAke25hbWV9OiAke29wfS4gU3VwcG9ydGVkOiAke0FycmF5LmZyb20oQUdHUkVHQVRFX09QRVJBVElPTlMpLmpvaW4oXCIsIFwiKX0uYClcbiAgICAgIH1cblxuICAgICAgbm9ybWFsaXplZFtuYW1lXSA9IHtjb2x1bW46IHR5cGVvZiBzcGVjID09PSBcInN0cmluZ1wiID8gbmFtZSA6IHNwZWMuY29sdW1uLCBvcH1cbiAgICB9XG5cbiAgICByZXR1cm4gbm9ybWFsaXplZFxuICB9XG59XG4iXX0=
|
|
@@ -48,6 +48,19 @@ export default class Tenant {
|
|
|
48
48
|
filter?: (tenant: unknown) => boolean;
|
|
49
49
|
configuration?: import("../configuration.js").default;
|
|
50
50
|
}): Promise<number>;
|
|
51
|
+
/**
|
|
52
|
+
* Runs one aggregate query across many tenant databases and returns the merged result. The
|
|
53
|
+
* tenants may be co-located on the default server or spread across other servers, and can be
|
|
54
|
+
* created or dropped at runtime; the live tenant list is resolved (from `tenants` or the
|
|
55
|
+
* provider's `listTenants`), grouped by server, aggregated with a single cross-database
|
|
56
|
+
* `UNION ALL` where the driver supports it (MySQL/MSSQL) or one query per tenant otherwise
|
|
57
|
+
* (PostgreSQL/SQLite), and merged with each aggregate's own operation. The caller writes only one
|
|
58
|
+
* per-tenant subquery and declares the key columns and aggregates; see
|
|
59
|
+
* {@link import("./tenant-aggregator.js").TenantAggregateOptions}.
|
|
60
|
+
* @param {import("./tenant-aggregator.js").TenantAggregateOptions} options - Aggregate configuration.
|
|
61
|
+
* @returns {Promise<Array<Record<string, ?>>>} - One merged row per distinct key-column combination.
|
|
62
|
+
*/
|
|
63
|
+
static aggregateAcross(options: import("./tenant-aggregator.js").TenantAggregateOptions): Promise<Array<Record<string, unknown>>>;
|
|
51
64
|
/**
|
|
52
65
|
* Drops one tenant's database/schema through the provider's `dropDatabase` hook.
|
|
53
66
|
* @param {{identifier: string, tenant: object, configuration?: import("../configuration.js").default}} args - Tenant descriptor and database identifier to drop.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../../src/tenants/tenant.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tenant.d.ts","sourceRoot":"","sources":["../../../src/tenants/tenant.js"],"names":[],"mappings":"AAOA;;;;;;;;;;GAUG;AACH;IACE;;;;;;;;;;;;OAYG;IACH,YALa,CAAC,UACH,MAAM,YACN,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,6BAA6B,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACxF,OAAO,CAAC,CAAC,CAAC,CAMtB;IAED;;;OAGG;IACH,kBAFa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAI/C;IAED;;;;;;;;OAQG;IACH,uEAHW;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,CAAS,IAAiG,EAAjG;YAAC,qBAAqB,EAAE,OAAO,2BAA2B,EAAE,yBAAyB,CAAC;YAAC,MAAM,EAAE,OAAC,CAAA;SAAC,KAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,OAAC,KAAK,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAA;KAAC,GACpQ,OAAO,CAAC,MAAM,CAAC,CAsB3B;IAED;;;;;;;;;;;OAWG;IACH,gCAHW,OAAO,wBAAwB,EAAE,sBAAsB,GACrD,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAC,CAAC,CAAC,CAAC,CAI7C;IAED;;;;OAIG;IACH,mDAHW;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAA;KAAC,GACzF,OAAO,CAAC,IAAI,CAAC,CAwBzB;CACF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import Current from "../current.js";
|
|
3
3
|
import { dropTenantDatabase } from "./default-tenant-database-provisioning.js";
|
|
4
|
+
import TenantAggregator from "./tenant-aggregator.js";
|
|
4
5
|
import TenantIterator from "./tenant-iterator.js";
|
|
5
6
|
/**
|
|
6
7
|
* Apartment-style runtime façade for multi-tenant apps. A "tenant" is whatever descriptor
|
|
@@ -65,6 +66,21 @@ export default class Tenant {
|
|
|
65
66
|
await configuration.ensureConnections({ name: `Tenant.each: ${identifier}` }, async () => await callback(callbackArgs));
|
|
66
67
|
});
|
|
67
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Runs one aggregate query across many tenant databases and returns the merged result. The
|
|
71
|
+
* tenants may be co-located on the default server or spread across other servers, and can be
|
|
72
|
+
* created or dropped at runtime; the live tenant list is resolved (from `tenants` or the
|
|
73
|
+
* provider's `listTenants`), grouped by server, aggregated with a single cross-database
|
|
74
|
+
* `UNION ALL` where the driver supports it (MySQL/MSSQL) or one query per tenant otherwise
|
|
75
|
+
* (PostgreSQL/SQLite), and merged with each aggregate's own operation. The caller writes only one
|
|
76
|
+
* per-tenant subquery and declares the key columns and aggregates; see
|
|
77
|
+
* {@link import("./tenant-aggregator.js").TenantAggregateOptions}.
|
|
78
|
+
* @param {import("./tenant-aggregator.js").TenantAggregateOptions} options - Aggregate configuration.
|
|
79
|
+
* @returns {Promise<Array<Record<string, ?>>>} - One merged row per distinct key-column combination.
|
|
80
|
+
*/
|
|
81
|
+
static async aggregateAcross(options) {
|
|
82
|
+
return await new TenantAggregator(options).run();
|
|
83
|
+
}
|
|
68
84
|
/**
|
|
69
85
|
* Drops one tenant's database/schema through the provider's `dropDatabase` hook.
|
|
70
86
|
* @param {{identifier: string, tenant: object, configuration?: import("../configuration.js").default}} args - Tenant descriptor and database identifier to drop.
|
|
@@ -92,4 +108,4 @@ export default class Tenant {
|
|
|
92
108
|
});
|
|
93
109
|
}
|
|
94
110
|
}
|
|
95
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuYW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3RlbmFudHMvdGVuYW50LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLE9BQU8sTUFBTSxlQUFlLENBQUE7QUFDbkMsT0FBTyxFQUFDLGtCQUFrQixFQUFDLE1BQU0sMkNBQTJDLENBQUE7QUFDNUUsT0FBTyxjQUFjLE1BQU0sc0JBQXNCLENBQUE7QUFFakQ7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sQ0FBQyxPQUFPLE9BQU8sTUFBTTtJQUN6Qjs7Ozs7Ozs7Ozs7O09BWUc7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsUUFBUTtRQUNoQyxNQUFNLGFBQWEsR0FBRyxPQUFPLENBQUMsYUFBYSxFQUFFLENBQUE7UUFFN0MsT0FBTyxNQUFNLE9BQU8sQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUMsTUFBTSxhQUFhLENBQUMsaUJBQWlCLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQTtJQUN0RyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLE9BQU87UUFDWixPQUFPLE9BQU8sQ0FBQyxNQUFNLEVBQUUsQ0FBQTtJQUN6QixDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFDLFVBQVUsRUFBRSxRQUFRLEVBQUUsUUFBUSxHQUFHLENBQUMsRUFBRSxNQUFNLEVBQUUsYUFBYSxHQUFHLE9BQU8sQ0FBQyxhQUFhLEVBQUUsRUFBQztRQUNyRyxNQUFNLFFBQVEsR0FBRyxhQUFhLENBQUMseUJBQXlCLENBQUMsVUFBVSxDQUFDLENBQUE7UUFDcEUsTUFBTSxhQUFhLEdBQUcsTUFBTSxhQUFhLENBQUMsaUJBQWlCLENBQUMsRUFBQyxJQUFJLEVBQUUsZ0JBQWdCLFVBQVUsRUFBRSxFQUFDLEVBQUUsS0FBSyxJQUFJLEVBQUU7WUFDM0csT0FBTyxNQUFNLFFBQVEsQ0FBQyxXQUFXLENBQUMsRUFBQyxhQUFhLEVBQUUsVUFBVSxFQUFDLENBQUMsQ0FBQTtRQUNoRSxDQUFDLENBQUMsQ0FBQTtRQUVGLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxFQUFFLENBQUM7WUFDbEMsTUFBTSxJQUFJLEtBQUssQ0FBQyxnQ0FBZ0MsVUFBVSx3Q0FBd0MsQ0FBQyxDQUFBO1FBQ3JHLENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQTtRQUNyRSxNQUFNLFFBQVEsR0FBRyxJQUFJLGNBQWMsQ0FBQyxFQUFDLGFBQWEsRUFBRSxVQUFVLEVBQUUsYUFBYSxFQUFFLFFBQVEsRUFBQyxDQUFDLENBQUE7UUFFekYsNEVBQTRFO1FBQzVFLHVGQUF1RjtRQUN2RixvRkFBb0Y7UUFDcEYsMEZBQTBGO1FBQzFGLE9BQU8sTUFBTSxRQUFRLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLEVBQUU7WUFDeEQsTUFBTSxhQUFhLENBQUMsaUJBQWlCLENBQUMsRUFBQyxJQUFJLEVBQUUsZ0JBQWdCLFVBQVUsRUFBRSxFQUFDLEVBQUUsS0FBSyxJQUFJLEVBQUUsQ0FBQyxNQUFNLFFBQVEsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFBO1FBQ3ZILENBQUMsQ0FBQyxDQUFBO0lBQ0osQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsYUFBYSxHQUFHLE9BQU8sQ0FBQyxhQUFhLEVBQUUsRUFBQztRQUM3RSxNQUFNLFFBQVEsR0FBRyxhQUFhLENBQUMseUJBQXlCLENBQUMsVUFBVSxDQUFDLENBQUE7UUFDcEUsTUFBTSxZQUFZLEdBQUcsT0FBTyxRQUFRLENBQUMsWUFBWSxLQUFLLFVBQVU7WUFDOUQsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQztZQUN0QyxDQUFDLENBQUMsa0JBQWtCLENBQUE7UUFFdEIsTUFBTSxhQUFhLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxLQUFLLElBQUksRUFBRTtZQUNuRCxxRkFBcUY7WUFDckYscUZBQXFGO1lBQ3JGLHdGQUF3RjtZQUN4Rix3RUFBd0U7WUFDeEUsSUFBSSxDQUFDLGFBQWEsQ0FBQywwQkFBMEIsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO2dCQUMxRCxNQUFNLElBQUksS0FBSyxDQUFDLDhCQUE4QixVQUFVLDRCQUE0QixjQUFjLENBQUMsV0FBVyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQTtZQUMzSCxDQUFDO1lBRUQsTUFBTSxZQUFZLENBQUM7Z0JBQ2pCLGFBQWE7Z0JBQ2IscUJBQXFCLEVBQUUsYUFBYSxDQUFDLDRCQUE0QixDQUFDLFVBQVUsQ0FBQztnQkFDN0UsVUFBVTtnQkFDVixNQUFNO2FBQ1AsQ0FBQyxDQUFBO1FBQ0osQ0FBQyxDQUFDLENBQUE7SUFDSixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IEN1cnJlbnQgZnJvbSBcIi4uL2N1cnJlbnQuanNcIlxuaW1wb3J0IHtkcm9wVGVuYW50RGF0YWJhc2V9IGZyb20gXCIuL2RlZmF1bHQtdGVuYW50LWRhdGFiYXNlLXByb3Zpc2lvbmluZy5qc1wiXG5pbXBvcnQgVGVuYW50SXRlcmF0b3IgZnJvbSBcIi4vdGVuYW50LWl0ZXJhdG9yLmpzXCJcblxuLyoqXG4gKiBBcGFydG1lbnQtc3R5bGUgcnVudGltZSBmYcOnYWRlIGZvciBtdWx0aS10ZW5hbnQgYXBwcy4gQSBcInRlbmFudFwiIGlzIHdoYXRldmVyIGRlc2NyaXB0b3JcbiAqIG9iamVjdCB0aGUgYXBwJ3MgYHRlbmFudERhdGFiYXNlUmVzb2x2ZXJgIHVuZGVyc3RhbmRzIChhbiBhY2NvdW50LCBhIHByb2plY3QsIOKApik7IHRoaXNcbiAqIGNsYXNzIGlzIHRoZSBzaW5nbGUgZGlzY292ZXJhYmxlIGhvbWUgZm9yIHN3aXRjaGluZyBpbnRvIGEgdGVuYW50J3MgY29udGV4dCwgcmVhZGluZyB0aGVcbiAqIGN1cnJlbnQgb25lLCBpdGVyYXRpbmcgZXZlcnkgdGVuYW50IG9mIGEgZGF0YWJhc2UgaWRlbnRpZmllciwgYW5kIGRyb3BwaW5nIGEgdGVuYW50J3NcbiAqIGRhdGFiYXNlLiBTd2l0Y2hpbmcgZGVsZWdhdGVzIHRvIHtAbGluayBDdXJyZW50fSAod2hpY2ggb3ducyB0aGUgYXN5bmMtY29udGV4dCB0ZW5hbnRcbiAqIHN0YXRlKSBhbmQgYWRkaXRpb25hbGx5IHJ1bnMgdGhlIGNhbGxiYWNrIGluc2lkZSBgZW5zdXJlQ29ubmVjdGlvbnNgLCBzbyBlbnRlcmluZyBhIHRlbmFudFxuICogbWFrZXMgaXRzIGRhdGFiYXNlIGltbWVkaWF0ZWx5IHF1ZXJ5YWJsZSDigJQgdGhlIGFwYXJ0bWVudC1zdHlsZSBcInN3aXRjaFwiIHNlbWFudGljcyDigJQgd2l0aG91dFxuICogdGhlIGNhbGxlciBlc3RhYmxpc2hpbmcgY29ubmVjdGlvbnMgaXRzZWxmOyBpdGVyYXRpb24gYW5kIGRyb3AgZHJpdmUgdGhlIGFwcCdzIHRlbmFudFxuICogZGF0YWJhc2UgcHJvdmlkZXIgaG9va3MuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRlbmFudCB7XG4gIC8qKlxuICAgKiBSdW5zIGBjYWxsYmFja2Agd2l0aCBgdGVuYW50YCBhcyB0aGUgY3VycmVudCB0ZW5hbnQsIHJlc3RvcmluZyB0aGUgcHJldmlvdXMgdGVuYW50IGFmdGVyLlxuICAgKiBUaGUgY2FsbGJhY2sgcnVucyBpbnNpZGUgYGVuc3VyZUNvbm5lY3Rpb25zYCwgc28gZXZlcnkgZGF0YWJhc2UgaWRlbnRpZmllciB0aGUgdGVuYW50XG4gICAqIGFjdGl2YXRlcyAodGhlIGdsb2JhbCBkYXRhYmFzZSBwbHVzIHRoZSB0ZW5hbnQncyBkYXRhYmFzZSkgaGFzIGEgY2hlY2tlZC1vdXQgY29ubmVjdGlvblxuICAgKiBhdmFpbGFibGUgZm9yIHRoZSBjYWxsYmFjaydzIGR1cmF0aW9uOiBzd2l0Y2hpbmcgaW50byBhIHRlbmFudCBtYWtlcyBpdCBxdWVyeWFibGUgd2l0aG91dFxuICAgKiB0aGUgY2FsbGVyIHdpcmluZyB1cCBjb25uZWN0aW9ucy4gQWxyZWFkeS1jaGVja2VkLW91dCBjb25uZWN0aW9ucyBhcmUgcmV1c2VkLCBzbyBuZXN0aW5nXG4gICAqIGBUZW5hbnQud2l0aGAgY2FsbHMgZG9lcyBub3Qgb3BlbiByZWR1bmRhbnQgY29ubmVjdGlvbnMuIFRoZSBjYWxsYmFjayByZWNlaXZlcyB0aGUgYWN0aXZlXG4gICAqIGNvbm5lY3Rpb25zIGtleWVkIGJ5IGlkZW50aWZpZXIsIHRoZSBzYW1lIGFzIGBlbnN1cmVDb25uZWN0aW9uc2AuXG4gICAqIEB0ZW1wbGF0ZSBUXG4gICAqIEBwYXJhbSB7b2JqZWN0fSB0ZW5hbnQgRGVzY3JpcHRvciB1bmRlcnN0b29kIGJ5IHRoZSBhcHAncyB0ZW5hbnREYXRhYmFzZVJlc29sdmVyLlxuICAgKiBAcGFyYW0geyhjb25uZWN0aW9uczogUmVjb3JkPHN0cmluZywgaW1wb3J0KFwiLi4vZGF0YWJhc2UvZHJpdmVycy9iYXNlLmpzXCIpLmRlZmF1bHQ+KSA9PiBQcm9taXNlPFQ+fSBjYWxsYmFjayAtIE9wZXJhdGlvbiB0byBydW4gd2l0aCB0aGUgdGVuYW50J3MgYWN0aXZlIGNvbm5lY3Rpb25zLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxUPn0gLSBDYWxsYmFjayByZXN1bHQgZnJvbSB3aXRoaW4gdGhlIHRlbmFudCBjb250ZXh0LlxuICAgKi9cbiAgc3RhdGljIGFzeW5jIHdpdGgodGVuYW50LCBjYWxsYmFjaykge1xuICAgIGNvbnN0IGNvbmZpZ3VyYXRpb24gPSBDdXJyZW50LmNvbmZpZ3VyYXRpb24oKVxuXG4gICAgcmV0dXJuIGF3YWl0IEN1cnJlbnQud2l0aFRlbmFudCh0ZW5hbnQsIGFzeW5jICgpID0+IGF3YWl0IGNvbmZpZ3VyYXRpb24uZW5zdXJlQ29ubmVjdGlvbnMoY2FsbGJhY2spKVxuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBjdXJyZW50IHRlbmFudCBkZXNjcmlwdG9yLCBvciB1bmRlZmluZWQgd2hlbiBydW5uaW5nIG91dHNpZGUgYW55IHRlbmFudCBjb250ZXh0LlxuICAgKiBAcmV0dXJucyB7UmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCB1bmRlZmluZWR9IC0gQ3VycmVudCBhc3luYy1jb250ZXh0IHRlbmFudCBkZXNjcmlwdG9yLCBpZiBhbnkuXG4gICAqL1xuICBzdGF0aWMgY3VycmVudCgpIHtcbiAgICByZXR1cm4gQ3VycmVudC50ZW5hbnQoKVxuICB9XG5cbiAgLyoqXG4gICAqIExpc3RzIHRoZSB0ZW5hbnRzIGZvciBhIGRhdGFiYXNlIGlkZW50aWZpZXIgdGhyb3VnaCB0aGUgcHJvdmlkZXIgYW5kIHJ1bnMgYGNhbGxiYWNrYFxuICAgKiB3aXRoaW4gZWFjaCB0ZW5hbnQncyBjb250ZXh0LCBvcHRpb25hbGx5IGZpbHRlcmVkIGFuZCBzZXZlcmFsIGF0IGEgdGltZS4gTGlrZVxuICAgKiB7QGxpbmsgVGVuYW50LndpdGh9LCB0aGUgY2FsbGJhY2sgcnVucyBpbnNpZGUgYGVuc3VyZUNvbm5lY3Rpb25zYCBzbyBlYWNoIHRlbmFudCdzXG4gICAqIGRhdGFiYXNlIGlzIHF1ZXJ5YWJsZSB3aXRob3V0IHRoZSBjYWxsZXIgd2lyaW5nIHVwIGNvbm5lY3Rpb25zLiBSZXR1cm5zIGhvdyBtYW55IHRlbmFudHNcbiAgICogdGhlIGNhbGxiYWNrIHJhbiBmb3IgKGFmdGVyIGZpbHRlcmluZykuXG4gICAqIEBwYXJhbSB7e2lkZW50aWZpZXI6IHN0cmluZywgY2FsbGJhY2s6IGZ1bmN0aW9uKHtkYXRhYmFzZUNvbmZpZ3VyYXRpb246IGltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuRGF0YWJhc2VDb25maWd1cmF0aW9uVHlwZSwgdGVuYW50OiA/fSkgOiBQcm9taXNlPHZvaWQ+LCBwYXJhbGxlbD86IG51bWJlciwgZmlsdGVyPzogKHRlbmFudDogPykgPT4gYm9vbGVhbiwgY29uZmlndXJhdGlvbj86IGltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH19IGFyZ3MgLSBUZW5hbnQgZGF0YWJhc2UgaWRlbnRpZmllciwgcGVyLXRlbmFudCBvcGVyYXRpb24sIGZpbHRlcmluZywgYW5kIGNvbmN1cnJlbmN5IHNldHRpbmdzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxudW1iZXI+fSAtIE51bWJlciBvZiBwcm9jZXNzZWQgdGVuYW50cy5cbiAgICovXG4gIHN0YXRpYyBhc3luYyBlYWNoKHtpZGVudGlmaWVyLCBjYWxsYmFjaywgcGFyYWxsZWwgPSAxLCBmaWx0ZXIsIGNvbmZpZ3VyYXRpb24gPSBDdXJyZW50LmNvbmZpZ3VyYXRpb24oKX0pIHtcbiAgICBjb25zdCBwcm92aWRlciA9IGNvbmZpZ3VyYXRpb24uZ2V0VGVuYW50RGF0YWJhc2VQcm92aWRlcihpZGVudGlmaWVyKVxuICAgIGNvbnN0IGxpc3RlZFRlbmFudHMgPSBhd2FpdCBjb25maWd1cmF0aW9uLmVuc3VyZUNvbm5lY3Rpb25zKHtuYW1lOiBgVGVuYW50LmVhY2g6ICR7aWRlbnRpZmllcn1gfSwgYXN5bmMgKCkgPT4ge1xuICAgICAgcmV0dXJuIGF3YWl0IHByb3ZpZGVyLmxpc3RUZW5hbnRzKHtjb25maWd1cmF0aW9uLCBpZGVudGlmaWVyfSlcbiAgICB9KVxuXG4gICAgaWYgKCFBcnJheS5pc0FycmF5KGxpc3RlZFRlbmFudHMpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFRlbmFudCBkYXRhYmFzZSBwcm92aWRlciBmb3IgJHtpZGVudGlmaWVyfSBtdXN0IHJldHVybiBhbiBhcnJheSBmcm9tIGxpc3RUZW5hbnRzYClcbiAgICB9XG5cbiAgICBjb25zdCB0ZW5hbnRzID0gZmlsdGVyID8gbGlzdGVkVGVuYW50cy5maWx0ZXIoZmlsdGVyKSA6IGxpc3RlZFRlbmFudHNcbiAgICBjb25zdCBpdGVyYXRvciA9IG5ldyBUZW5hbnRJdGVyYXRvcih7Y29uZmlndXJhdGlvbiwgaWRlbnRpZmllciwgcGFyYWxsZWxDb3VudDogcGFyYWxsZWx9KVxuXG4gICAgLy8gUnVuIGVhY2ggdGVuYW50J3MgY2FsbGJhY2sgaW5zaWRlIGVuc3VyZUNvbm5lY3Rpb25zIHNvIHRoZSBpdGVyYXRvciBzdGF5c1xuICAgIC8vIGNvbm5lY3Rpb24tYWdub3N0aWMgKHRoZSBkYjp0ZW5hbnRzOiogQ0xJIGNvbW1hbmRzIHNoYXJlIFRlbmFudEl0ZXJhdG9yIGFuZCBtdXN0IHJ1blxuICAgIC8vIHRoZWlyIGNhbGxiYWNrcywgc3VjaCBhcyBjcmVhdGUsIGJlZm9yZSB0aGUgdGVuYW50IGRhdGFiYXNlIGV4aXN0cykgd2hpbGUgcnVudGltZVxuICAgIC8vIGl0ZXJhdGlvbiBoZXJlIGdldHMgdGhlIHRlbmFudCdzIGNvbm5lY3Rpb25zIGVzdGFibGlzaGVkIHRoZSBzYW1lIHdheSBUZW5hbnQud2l0aCBkb2VzLlxuICAgIHJldHVybiBhd2FpdCBpdGVyYXRvci5ydW4odGVuYW50cywgYXN5bmMgKGNhbGxiYWNrQXJncykgPT4ge1xuICAgICAgYXdhaXQgY29uZmlndXJhdGlvbi5lbnN1cmVDb25uZWN0aW9ucyh7bmFtZTogYFRlbmFudC5lYWNoOiAke2lkZW50aWZpZXJ9YH0sIGFzeW5jICgpID0+IGF3YWl0IGNhbGxiYWNrKGNhbGxiYWNrQXJncykpXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBEcm9wcyBvbmUgdGVuYW50J3MgZGF0YWJhc2Uvc2NoZW1hIHRocm91Z2ggdGhlIHByb3ZpZGVyJ3MgYGRyb3BEYXRhYmFzZWAgaG9vay5cbiAgICogQHBhcmFtIHt7aWRlbnRpZmllcjogc3RyaW5nLCB0ZW5hbnQ6IG9iamVjdCwgY29uZmlndXJhdGlvbj86IGltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH19IGFyZ3MgLSBUZW5hbnQgZGVzY3JpcHRvciBhbmQgZGF0YWJhc2UgaWRlbnRpZmllciB0byBkcm9wLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIHN0YXRpYyBhc3luYyBkcm9wKHtpZGVudGlmaWVyLCB0ZW5hbnQsIGNvbmZpZ3VyYXRpb24gPSBDdXJyZW50LmNvbmZpZ3VyYXRpb24oKX0pIHtcbiAgICBjb25zdCBwcm92aWRlciA9IGNvbmZpZ3VyYXRpb24uZ2V0VGVuYW50RGF0YWJhc2VQcm92aWRlcihpZGVudGlmaWVyKVxuICAgIGNvbnN0IGRyb3BEYXRhYmFzZSA9IHR5cGVvZiBwcm92aWRlci5kcm9wRGF0YWJhc2UgPT09IFwiZnVuY3Rpb25cIlxuICAgICAgPyBwcm92aWRlci5kcm9wRGF0YWJhc2UuYmluZChwcm92aWRlcilcbiAgICAgIDogZHJvcFRlbmFudERhdGFiYXNlXG5cbiAgICBhd2FpdCBjb25maWd1cmF0aW9uLnJ1bldpdGhUZW5hbnQodGVuYW50LCBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBHdWFyZCBhZ2FpbnN0IGFuIHVucmVzb2x2ZWQgdGVuYW50LiByZXNvbHZlRGF0YWJhc2VDb25maWd1cmF0aW9uIGZhbGxzIGJhY2sgdG8gdGhlXG4gICAgICAvLyBiYXNlICh0ZW1wbGF0ZS9kZWZhdWx0KSB0ZW5hbnQgZGF0YWJhc2Ugd2hlbiB0aGUgcmVzb2x2ZXIgcmV0dXJucyBub3RoaW5nIGZvciB0aGlzXG4gICAgICAvLyBkZXNjcmlwdG9yLCBzbyB3aXRob3V0IHRoaXMgY2hlY2sgYSBwcm92aWRlciB0aGF0IGRyb3BzIGJ5IGRhdGFiYXNlQ29uZmlndXJhdGlvbi5uYW1lXG4gICAgICAvLyB3b3VsZCBkcm9wIHRoZSB0ZW1wbGF0ZSBkYXRhYmFzZSBpbnN0ZWFkIG9mIHJlamVjdGluZyB0aGUgYmFkIHRlbmFudC5cbiAgICAgIGlmICghY29uZmlndXJhdGlvbi5pc0RhdGFiYXNlSWRlbnRpZmllckFjdGl2ZShpZGVudGlmaWVyKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFRlbmFudCBkYXRhYmFzZSBpZGVudGlmaWVyICR7aWRlbnRpZmllcn0gaXMgaW5hY3RpdmUgZm9yIHRlbmFudDogJHtUZW5hbnRJdGVyYXRvci50ZW5hbnRMYWJlbCh0ZW5hbnQpfWApXG4gICAgICB9XG5cbiAgICAgIGF3YWl0IGRyb3BEYXRhYmFzZSh7XG4gICAgICAgIGNvbmZpZ3VyYXRpb24sXG4gICAgICAgIGRhdGFiYXNlQ29uZmlndXJhdGlvbjogY29uZmlndXJhdGlvbi5yZXNvbHZlRGF0YWJhc2VDb25maWd1cmF0aW9uKGlkZW50aWZpZXIpLFxuICAgICAgICBpZGVudGlmaWVyLFxuICAgICAgICB0ZW5hbnRcbiAgICAgIH0pXG4gICAgfSlcbiAgfVxufVxuIl19
|
|
111
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVuYW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3RlbmFudHMvdGVuYW50LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLE9BQU8sTUFBTSxlQUFlLENBQUE7QUFDbkMsT0FBTyxFQUFDLGtCQUFrQixFQUFDLE1BQU0sMkNBQTJDLENBQUE7QUFDNUUsT0FBTyxnQkFBZ0IsTUFBTSx3QkFBd0IsQ0FBQTtBQUNyRCxPQUFPLGNBQWMsTUFBTSxzQkFBc0IsQ0FBQTtBQUVqRDs7Ozs7Ozs7OztHQVVHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sT0FBTyxNQUFNO0lBQ3pCOzs7Ozs7Ozs7Ozs7T0FZRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxRQUFRO1FBQ2hDLE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FBQyxhQUFhLEVBQUUsQ0FBQTtRQUU3QyxPQUFPLE1BQU0sT0FBTyxDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsS0FBSyxJQUFJLEVBQUUsQ0FBQyxNQUFNLGFBQWEsQ0FBQyxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFBO0lBQ3RHLENBQUM7SUFFRDs7O09BR0c7SUFDSCxNQUFNLENBQUMsT0FBTztRQUNaLE9BQU8sT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFBO0lBQ3pCLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUMsVUFBVSxFQUFFLFFBQVEsRUFBRSxRQUFRLEdBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxhQUFhLEdBQUcsT0FBTyxDQUFDLGFBQWEsRUFBRSxFQUFDO1FBQ3JHLE1BQU0sUUFBUSxHQUFHLGFBQWEsQ0FBQyx5QkFBeUIsQ0FBQyxVQUFVLENBQUMsQ0FBQTtRQUNwRSxNQUFNLGFBQWEsR0FBRyxNQUFNLGFBQWEsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFDLElBQUksRUFBRSxnQkFBZ0IsVUFBVSxFQUFFLEVBQUMsRUFBRSxLQUFLLElBQUksRUFBRTtZQUMzRyxPQUFPLE1BQU0sUUFBUSxDQUFDLFdBQVcsQ0FBQyxFQUFDLGFBQWEsRUFBRSxVQUFVLEVBQUMsQ0FBQyxDQUFBO1FBQ2hFLENBQUMsQ0FBQyxDQUFBO1FBRUYsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLEVBQUUsQ0FBQztZQUNsQyxNQUFNLElBQUksS0FBSyxDQUFDLGdDQUFnQyxVQUFVLHdDQUF3QyxDQUFDLENBQUE7UUFDckcsQ0FBQztRQUVELE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFBO1FBQ3JFLE1BQU0sUUFBUSxHQUFHLElBQUksY0FBYyxDQUFDLEVBQUMsYUFBYSxFQUFFLFVBQVUsRUFBRSxhQUFhLEVBQUUsUUFBUSxFQUFDLENBQUMsQ0FBQTtRQUV6Riw0RUFBNEU7UUFDNUUsdUZBQXVGO1FBQ3ZGLG9GQUFvRjtRQUNwRiwwRkFBMEY7UUFDMUYsT0FBTyxNQUFNLFFBQVEsQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLEtBQUssRUFBRSxZQUFZLEVBQUUsRUFBRTtZQUN4RCxNQUFNLGFBQWEsQ0FBQyxpQkFBaUIsQ0FBQyxFQUFDLElBQUksRUFBRSxnQkFBZ0IsVUFBVSxFQUFFLEVBQUMsRUFBRSxLQUFLLElBQUksRUFBRSxDQUFDLE1BQU0sUUFBUSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUE7UUFDdkgsQ0FBQyxDQUFDLENBQUE7SUFDSixDQUFDO0lBRUQ7Ozs7Ozs7Ozs7O09BV0c7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLGVBQWUsQ0FBQyxPQUFPO1FBQ2xDLE9BQU8sTUFBTSxJQUFJLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFBO0lBQ2xELENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBQyxVQUFVLEVBQUUsTUFBTSxFQUFFLGFBQWEsR0FBRyxPQUFPLENBQUMsYUFBYSxFQUFFLEVBQUM7UUFDN0UsTUFBTSxRQUFRLEdBQUcsYUFBYSxDQUFDLHlCQUF5QixDQUFDLFVBQVUsQ0FBQyxDQUFBO1FBQ3BFLE1BQU0sWUFBWSxHQUFHLE9BQU8sUUFBUSxDQUFDLFlBQVksS0FBSyxVQUFVO1lBQzlELENBQUMsQ0FBQyxRQUFRLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUM7WUFDdEMsQ0FBQyxDQUFDLGtCQUFrQixDQUFBO1FBRXRCLE1BQU0sYUFBYSxDQUFDLGFBQWEsQ0FBQyxNQUFNLEVBQUUsS0FBSyxJQUFJLEVBQUU7WUFDbkQscUZBQXFGO1lBQ3JGLHFGQUFxRjtZQUNyRix3RkFBd0Y7WUFDeEYsd0VBQXdFO1lBQ3hFLElBQUksQ0FBQyxhQUFhLENBQUMsMEJBQTBCLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztnQkFDMUQsTUFBTSxJQUFJLEtBQUssQ0FBQyw4QkFBOEIsVUFBVSw0QkFBNEIsY0FBYyxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUE7WUFDM0gsQ0FBQztZQUVELE1BQU0sWUFBWSxDQUFDO2dCQUNqQixhQUFhO2dCQUNiLHFCQUFxQixFQUFFLGFBQWEsQ0FBQyw0QkFBNEIsQ0FBQyxVQUFVLENBQUM7Z0JBQzdFLFVBQVU7Z0JBQ1YsTUFBTTthQUNQLENBQUMsQ0FBQTtRQUNKLENBQUMsQ0FBQyxDQUFBO0lBQ0osQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmltcG9ydCBDdXJyZW50IGZyb20gXCIuLi9jdXJyZW50LmpzXCJcbmltcG9ydCB7ZHJvcFRlbmFudERhdGFiYXNlfSBmcm9tIFwiLi9kZWZhdWx0LXRlbmFudC1kYXRhYmFzZS1wcm92aXNpb25pbmcuanNcIlxuaW1wb3J0IFRlbmFudEFnZ3JlZ2F0b3IgZnJvbSBcIi4vdGVuYW50LWFnZ3JlZ2F0b3IuanNcIlxuaW1wb3J0IFRlbmFudEl0ZXJhdG9yIGZyb20gXCIuL3RlbmFudC1pdGVyYXRvci5qc1wiXG5cbi8qKlxuICogQXBhcnRtZW50LXN0eWxlIHJ1bnRpbWUgZmHDp2FkZSBmb3IgbXVsdGktdGVuYW50IGFwcHMuIEEgXCJ0ZW5hbnRcIiBpcyB3aGF0ZXZlciBkZXNjcmlwdG9yXG4gKiBvYmplY3QgdGhlIGFwcCdzIGB0ZW5hbnREYXRhYmFzZVJlc29sdmVyYCB1bmRlcnN0YW5kcyAoYW4gYWNjb3VudCwgYSBwcm9qZWN0LCDigKYpOyB0aGlzXG4gKiBjbGFzcyBpcyB0aGUgc2luZ2xlIGRpc2NvdmVyYWJsZSBob21lIGZvciBzd2l0Y2hpbmcgaW50byBhIHRlbmFudCdzIGNvbnRleHQsIHJlYWRpbmcgdGhlXG4gKiBjdXJyZW50IG9uZSwgaXRlcmF0aW5nIGV2ZXJ5IHRlbmFudCBvZiBhIGRhdGFiYXNlIGlkZW50aWZpZXIsIGFuZCBkcm9wcGluZyBhIHRlbmFudCdzXG4gKiBkYXRhYmFzZS4gU3dpdGNoaW5nIGRlbGVnYXRlcyB0byB7QGxpbmsgQ3VycmVudH0gKHdoaWNoIG93bnMgdGhlIGFzeW5jLWNvbnRleHQgdGVuYW50XG4gKiBzdGF0ZSkgYW5kIGFkZGl0aW9uYWxseSBydW5zIHRoZSBjYWxsYmFjayBpbnNpZGUgYGVuc3VyZUNvbm5lY3Rpb25zYCwgc28gZW50ZXJpbmcgYSB0ZW5hbnRcbiAqIG1ha2VzIGl0cyBkYXRhYmFzZSBpbW1lZGlhdGVseSBxdWVyeWFibGUg4oCUIHRoZSBhcGFydG1lbnQtc3R5bGUgXCJzd2l0Y2hcIiBzZW1hbnRpY3Mg4oCUIHdpdGhvdXRcbiAqIHRoZSBjYWxsZXIgZXN0YWJsaXNoaW5nIGNvbm5lY3Rpb25zIGl0c2VsZjsgaXRlcmF0aW9uIGFuZCBkcm9wIGRyaXZlIHRoZSBhcHAncyB0ZW5hbnRcbiAqIGRhdGFiYXNlIHByb3ZpZGVyIGhvb2tzLlxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBUZW5hbnQge1xuICAvKipcbiAgICogUnVucyBgY2FsbGJhY2tgIHdpdGggYHRlbmFudGAgYXMgdGhlIGN1cnJlbnQgdGVuYW50LCByZXN0b3JpbmcgdGhlIHByZXZpb3VzIHRlbmFudCBhZnRlci5cbiAgICogVGhlIGNhbGxiYWNrIHJ1bnMgaW5zaWRlIGBlbnN1cmVDb25uZWN0aW9uc2AsIHNvIGV2ZXJ5IGRhdGFiYXNlIGlkZW50aWZpZXIgdGhlIHRlbmFudFxuICAgKiBhY3RpdmF0ZXMgKHRoZSBnbG9iYWwgZGF0YWJhc2UgcGx1cyB0aGUgdGVuYW50J3MgZGF0YWJhc2UpIGhhcyBhIGNoZWNrZWQtb3V0IGNvbm5lY3Rpb25cbiAgICogYXZhaWxhYmxlIGZvciB0aGUgY2FsbGJhY2sncyBkdXJhdGlvbjogc3dpdGNoaW5nIGludG8gYSB0ZW5hbnQgbWFrZXMgaXQgcXVlcnlhYmxlIHdpdGhvdXRcbiAgICogdGhlIGNhbGxlciB3aXJpbmcgdXAgY29ubmVjdGlvbnMuIEFscmVhZHktY2hlY2tlZC1vdXQgY29ubmVjdGlvbnMgYXJlIHJldXNlZCwgc28gbmVzdGluZ1xuICAgKiBgVGVuYW50LndpdGhgIGNhbGxzIGRvZXMgbm90IG9wZW4gcmVkdW5kYW50IGNvbm5lY3Rpb25zLiBUaGUgY2FsbGJhY2sgcmVjZWl2ZXMgdGhlIGFjdGl2ZVxuICAgKiBjb25uZWN0aW9ucyBrZXllZCBieSBpZGVudGlmaWVyLCB0aGUgc2FtZSBhcyBgZW5zdXJlQ29ubmVjdGlvbnNgLlxuICAgKiBAdGVtcGxhdGUgVFxuICAgKiBAcGFyYW0ge29iamVjdH0gdGVuYW50IERlc2NyaXB0b3IgdW5kZXJzdG9vZCBieSB0aGUgYXBwJ3MgdGVuYW50RGF0YWJhc2VSZXNvbHZlci5cbiAgICogQHBhcmFtIHsoY29ubmVjdGlvbnM6IFJlY29yZDxzdHJpbmcsIGltcG9ydChcIi4uL2RhdGFiYXNlL2RyaXZlcnMvYmFzZS5qc1wiKS5kZWZhdWx0PikgPT4gUHJvbWlzZTxUPn0gY2FsbGJhY2sgLSBPcGVyYXRpb24gdG8gcnVuIHdpdGggdGhlIHRlbmFudCdzIGFjdGl2ZSBjb25uZWN0aW9ucy5cbiAgICogQHJldHVybnMge1Byb21pc2U8VD59IC0gQ2FsbGJhY2sgcmVzdWx0IGZyb20gd2l0aGluIHRoZSB0ZW5hbnQgY29udGV4dC5cbiAgICovXG4gIHN0YXRpYyBhc3luYyB3aXRoKHRlbmFudCwgY2FsbGJhY2spIHtcbiAgICBjb25zdCBjb25maWd1cmF0aW9uID0gQ3VycmVudC5jb25maWd1cmF0aW9uKClcblxuICAgIHJldHVybiBhd2FpdCBDdXJyZW50LndpdGhUZW5hbnQodGVuYW50LCBhc3luYyAoKSA9PiBhd2FpdCBjb25maWd1cmF0aW9uLmVuc3VyZUNvbm5lY3Rpb25zKGNhbGxiYWNrKSlcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgY3VycmVudCB0ZW5hbnQgZGVzY3JpcHRvciwgb3IgdW5kZWZpbmVkIHdoZW4gcnVubmluZyBvdXRzaWRlIGFueSB0ZW5hbnQgY29udGV4dC5cbiAgICogQHJldHVybnMge1JlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5kZWZpbmVkfSAtIEN1cnJlbnQgYXN5bmMtY29udGV4dCB0ZW5hbnQgZGVzY3JpcHRvciwgaWYgYW55LlxuICAgKi9cbiAgc3RhdGljIGN1cnJlbnQoKSB7XG4gICAgcmV0dXJuIEN1cnJlbnQudGVuYW50KClcbiAgfVxuXG4gIC8qKlxuICAgKiBMaXN0cyB0aGUgdGVuYW50cyBmb3IgYSBkYXRhYmFzZSBpZGVudGlmaWVyIHRocm91Z2ggdGhlIHByb3ZpZGVyIGFuZCBydW5zIGBjYWxsYmFja2BcbiAgICogd2l0aGluIGVhY2ggdGVuYW50J3MgY29udGV4dCwgb3B0aW9uYWxseSBmaWx0ZXJlZCBhbmQgc2V2ZXJhbCBhdCBhIHRpbWUuIExpa2VcbiAgICoge0BsaW5rIFRlbmFudC53aXRofSwgdGhlIGNhbGxiYWNrIHJ1bnMgaW5zaWRlIGBlbnN1cmVDb25uZWN0aW9uc2Agc28gZWFjaCB0ZW5hbnQnc1xuICAgKiBkYXRhYmFzZSBpcyBxdWVyeWFibGUgd2l0aG91dCB0aGUgY2FsbGVyIHdpcmluZyB1cCBjb25uZWN0aW9ucy4gUmV0dXJucyBob3cgbWFueSB0ZW5hbnRzXG4gICAqIHRoZSBjYWxsYmFjayByYW4gZm9yIChhZnRlciBmaWx0ZXJpbmcpLlxuICAgKiBAcGFyYW0ge3tpZGVudGlmaWVyOiBzdHJpbmcsIGNhbGxiYWNrOiBmdW5jdGlvbih7ZGF0YWJhc2VDb25maWd1cmF0aW9uOiBpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLkRhdGFiYXNlQ29uZmlndXJhdGlvblR5cGUsIHRlbmFudDogP30pIDogUHJvbWlzZTx2b2lkPiwgcGFyYWxsZWw/OiBudW1iZXIsIGZpbHRlcj86ICh0ZW5hbnQ6ID8pID0+IGJvb2xlYW4sIGNvbmZpZ3VyYXRpb24/OiBpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLmpzXCIpLmRlZmF1bHR9fSBhcmdzIC0gVGVuYW50IGRhdGFiYXNlIGlkZW50aWZpZXIsIHBlci10ZW5hbnQgb3BlcmF0aW9uLCBmaWx0ZXJpbmcsIGFuZCBjb25jdXJyZW5jeSBzZXR0aW5ncy5cbiAgICogQHJldHVybnMge1Byb21pc2U8bnVtYmVyPn0gLSBOdW1iZXIgb2YgcHJvY2Vzc2VkIHRlbmFudHMuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgZWFjaCh7aWRlbnRpZmllciwgY2FsbGJhY2ssIHBhcmFsbGVsID0gMSwgZmlsdGVyLCBjb25maWd1cmF0aW9uID0gQ3VycmVudC5jb25maWd1cmF0aW9uKCl9KSB7XG4gICAgY29uc3QgcHJvdmlkZXIgPSBjb25maWd1cmF0aW9uLmdldFRlbmFudERhdGFiYXNlUHJvdmlkZXIoaWRlbnRpZmllcilcbiAgICBjb25zdCBsaXN0ZWRUZW5hbnRzID0gYXdhaXQgY29uZmlndXJhdGlvbi5lbnN1cmVDb25uZWN0aW9ucyh7bmFtZTogYFRlbmFudC5lYWNoOiAke2lkZW50aWZpZXJ9YH0sIGFzeW5jICgpID0+IHtcbiAgICAgIHJldHVybiBhd2FpdCBwcm92aWRlci5saXN0VGVuYW50cyh7Y29uZmlndXJhdGlvbiwgaWRlbnRpZmllcn0pXG4gICAgfSlcblxuICAgIGlmICghQXJyYXkuaXNBcnJheShsaXN0ZWRUZW5hbnRzKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBUZW5hbnQgZGF0YWJhc2UgcHJvdmlkZXIgZm9yICR7aWRlbnRpZmllcn0gbXVzdCByZXR1cm4gYW4gYXJyYXkgZnJvbSBsaXN0VGVuYW50c2ApXG4gICAgfVxuXG4gICAgY29uc3QgdGVuYW50cyA9IGZpbHRlciA/IGxpc3RlZFRlbmFudHMuZmlsdGVyKGZpbHRlcikgOiBsaXN0ZWRUZW5hbnRzXG4gICAgY29uc3QgaXRlcmF0b3IgPSBuZXcgVGVuYW50SXRlcmF0b3Ioe2NvbmZpZ3VyYXRpb24sIGlkZW50aWZpZXIsIHBhcmFsbGVsQ291bnQ6IHBhcmFsbGVsfSlcblxuICAgIC8vIFJ1biBlYWNoIHRlbmFudCdzIGNhbGxiYWNrIGluc2lkZSBlbnN1cmVDb25uZWN0aW9ucyBzbyB0aGUgaXRlcmF0b3Igc3RheXNcbiAgICAvLyBjb25uZWN0aW9uLWFnbm9zdGljICh0aGUgZGI6dGVuYW50czoqIENMSSBjb21tYW5kcyBzaGFyZSBUZW5hbnRJdGVyYXRvciBhbmQgbXVzdCBydW5cbiAgICAvLyB0aGVpciBjYWxsYmFja3MsIHN1Y2ggYXMgY3JlYXRlLCBiZWZvcmUgdGhlIHRlbmFudCBkYXRhYmFzZSBleGlzdHMpIHdoaWxlIHJ1bnRpbWVcbiAgICAvLyBpdGVyYXRpb24gaGVyZSBnZXRzIHRoZSB0ZW5hbnQncyBjb25uZWN0aW9ucyBlc3RhYmxpc2hlZCB0aGUgc2FtZSB3YXkgVGVuYW50LndpdGggZG9lcy5cbiAgICByZXR1cm4gYXdhaXQgaXRlcmF0b3IucnVuKHRlbmFudHMsIGFzeW5jIChjYWxsYmFja0FyZ3MpID0+IHtcbiAgICAgIGF3YWl0IGNvbmZpZ3VyYXRpb24uZW5zdXJlQ29ubmVjdGlvbnMoe25hbWU6IGBUZW5hbnQuZWFjaDogJHtpZGVudGlmaWVyfWB9LCBhc3luYyAoKSA9PiBhd2FpdCBjYWxsYmFjayhjYWxsYmFja0FyZ3MpKVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogUnVucyBvbmUgYWdncmVnYXRlIHF1ZXJ5IGFjcm9zcyBtYW55IHRlbmFudCBkYXRhYmFzZXMgYW5kIHJldHVybnMgdGhlIG1lcmdlZCByZXN1bHQuIFRoZVxuICAgKiB0ZW5hbnRzIG1heSBiZSBjby1sb2NhdGVkIG9uIHRoZSBkZWZhdWx0IHNlcnZlciBvciBzcHJlYWQgYWNyb3NzIG90aGVyIHNlcnZlcnMsIGFuZCBjYW4gYmVcbiAgICogY3JlYXRlZCBvciBkcm9wcGVkIGF0IHJ1bnRpbWU7IHRoZSBsaXZlIHRlbmFudCBsaXN0IGlzIHJlc29sdmVkIChmcm9tIGB0ZW5hbnRzYCBvciB0aGVcbiAgICogcHJvdmlkZXIncyBgbGlzdFRlbmFudHNgKSwgZ3JvdXBlZCBieSBzZXJ2ZXIsIGFnZ3JlZ2F0ZWQgd2l0aCBhIHNpbmdsZSBjcm9zcy1kYXRhYmFzZVxuICAgKiBgVU5JT04gQUxMYCB3aGVyZSB0aGUgZHJpdmVyIHN1cHBvcnRzIGl0IChNeVNRTC9NU1NRTCkgb3Igb25lIHF1ZXJ5IHBlciB0ZW5hbnQgb3RoZXJ3aXNlXG4gICAqIChQb3N0Z3JlU1FML1NRTGl0ZSksIGFuZCBtZXJnZWQgd2l0aCBlYWNoIGFnZ3JlZ2F0ZSdzIG93biBvcGVyYXRpb24uIFRoZSBjYWxsZXIgd3JpdGVzIG9ubHkgb25lXG4gICAqIHBlci10ZW5hbnQgc3VicXVlcnkgYW5kIGRlY2xhcmVzIHRoZSBrZXkgY29sdW1ucyBhbmQgYWdncmVnYXRlczsgc2VlXG4gICAqIHtAbGluayBpbXBvcnQoXCIuL3RlbmFudC1hZ2dyZWdhdG9yLmpzXCIpLlRlbmFudEFnZ3JlZ2F0ZU9wdGlvbnN9LlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4vdGVuYW50LWFnZ3JlZ2F0b3IuanNcIikuVGVuYW50QWdncmVnYXRlT3B0aW9uc30gb3B0aW9ucyAtIEFnZ3JlZ2F0ZSBjb25maWd1cmF0aW9uLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxBcnJheTxSZWNvcmQ8c3RyaW5nLCA/Pj4+fSAtIE9uZSBtZXJnZWQgcm93IHBlciBkaXN0aW5jdCBrZXktY29sdW1uIGNvbWJpbmF0aW9uLlxuICAgKi9cbiAgc3RhdGljIGFzeW5jIGFnZ3JlZ2F0ZUFjcm9zcyhvcHRpb25zKSB7XG4gICAgcmV0dXJuIGF3YWl0IG5ldyBUZW5hbnRBZ2dyZWdhdG9yKG9wdGlvbnMpLnJ1bigpXG4gIH1cblxuICAvKipcbiAgICogRHJvcHMgb25lIHRlbmFudCdzIGRhdGFiYXNlL3NjaGVtYSB0aHJvdWdoIHRoZSBwcm92aWRlcidzIGBkcm9wRGF0YWJhc2VgIGhvb2suXG4gICAqIEBwYXJhbSB7e2lkZW50aWZpZXI6IHN0cmluZywgdGVuYW50OiBvYmplY3QsIGNvbmZpZ3VyYXRpb24/OiBpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLmpzXCIpLmRlZmF1bHR9fSBhcmdzIC0gVGVuYW50IGRlc2NyaXB0b3IgYW5kIGRhdGFiYXNlIGlkZW50aWZpZXIgdG8gZHJvcC5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBzdGF0aWMgYXN5bmMgZHJvcCh7aWRlbnRpZmllciwgdGVuYW50LCBjb25maWd1cmF0aW9uID0gQ3VycmVudC5jb25maWd1cmF0aW9uKCl9KSB7XG4gICAgY29uc3QgcHJvdmlkZXIgPSBjb25maWd1cmF0aW9uLmdldFRlbmFudERhdGFiYXNlUHJvdmlkZXIoaWRlbnRpZmllcilcbiAgICBjb25zdCBkcm9wRGF0YWJhc2UgPSB0eXBlb2YgcHJvdmlkZXIuZHJvcERhdGFiYXNlID09PSBcImZ1bmN0aW9uXCJcbiAgICAgID8gcHJvdmlkZXIuZHJvcERhdGFiYXNlLmJpbmQocHJvdmlkZXIpXG4gICAgICA6IGRyb3BUZW5hbnREYXRhYmFzZVxuXG4gICAgYXdhaXQgY29uZmlndXJhdGlvbi5ydW5XaXRoVGVuYW50KHRlbmFudCwgYXN5bmMgKCkgPT4ge1xuICAgICAgLy8gR3VhcmQgYWdhaW5zdCBhbiB1bnJlc29sdmVkIHRlbmFudC4gcmVzb2x2ZURhdGFiYXNlQ29uZmlndXJhdGlvbiBmYWxscyBiYWNrIHRvIHRoZVxuICAgICAgLy8gYmFzZSAodGVtcGxhdGUvZGVmYXVsdCkgdGVuYW50IGRhdGFiYXNlIHdoZW4gdGhlIHJlc29sdmVyIHJldHVybnMgbm90aGluZyBmb3IgdGhpc1xuICAgICAgLy8gZGVzY3JpcHRvciwgc28gd2l0aG91dCB0aGlzIGNoZWNrIGEgcHJvdmlkZXIgdGhhdCBkcm9wcyBieSBkYXRhYmFzZUNvbmZpZ3VyYXRpb24ubmFtZVxuICAgICAgLy8gd291bGQgZHJvcCB0aGUgdGVtcGxhdGUgZGF0YWJhc2UgaW5zdGVhZCBvZiByZWplY3RpbmcgdGhlIGJhZCB0ZW5hbnQuXG4gICAgICBpZiAoIWNvbmZpZ3VyYXRpb24uaXNEYXRhYmFzZUlkZW50aWZpZXJBY3RpdmUoaWRlbnRpZmllcikpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBUZW5hbnQgZGF0YWJhc2UgaWRlbnRpZmllciAke2lkZW50aWZpZXJ9IGlzIGluYWN0aXZlIGZvciB0ZW5hbnQ6ICR7VGVuYW50SXRlcmF0b3IudGVuYW50TGFiZWwodGVuYW50KX1gKVxuICAgICAgfVxuXG4gICAgICBhd2FpdCBkcm9wRGF0YWJhc2Uoe1xuICAgICAgICBjb25maWd1cmF0aW9uLFxuICAgICAgICBkYXRhYmFzZUNvbmZpZ3VyYXRpb246IGNvbmZpZ3VyYXRpb24ucmVzb2x2ZURhdGFiYXNlQ29uZmlndXJhdGlvbihpZGVudGlmaWVyKSxcbiAgICAgICAgaWRlbnRpZmllcixcbiAgICAgICAgdGVuYW50XG4gICAgICB9KVxuICAgIH0pXG4gIH1cbn1cbiJdfQ==
|