@pipeline-builder/pipeline-data 4.1.24 → 4.1.26
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/api/alert-destinations.d.ts +17 -0
- package/lib/api/alert-destinations.js +36 -0
- package/lib/api/reporting/analytics-queries.d.ts +274 -0
- package/lib/api/reporting/analytics-queries.js +598 -0
- package/lib/api/reporting/daily-rollup.d.ts +41 -0
- package/lib/api/reporting/daily-rollup.js +173 -0
- package/lib/api/reporting/deploy-rollup.d.ts +78 -0
- package/lib/api/reporting/deploy-rollup.js +203 -0
- package/lib/api/reporting/dora-scoring.d.ts +14 -0
- package/lib/api/reporting/dora-scoring.js +25 -1
- package/lib/api/reporting/dora.js +30 -6
- package/lib/api/reporting/failure-classifier.d.ts +54 -0
- package/lib/api/reporting/failure-classifier.js +179 -0
- package/lib/api/reporting/ingest.d.ts +2 -2
- package/lib/api/reporting/ingest.js +129 -4
- package/lib/api/reporting/last-deploy.d.ts +31 -0
- package/lib/api/reporting/last-deploy.js +93 -0
- package/lib/api/reporting/needs-attention.d.ts +93 -0
- package/lib/api/reporting/needs-attention.js +152 -0
- package/lib/api/reporting/plugin-exposure.d.ts +129 -0
- package/lib/api/reporting/plugin-exposure.js +312 -0
- package/lib/api/reporting/retention-sweep.js +9 -1
- package/lib/api/reporting/sql-helpers.d.ts +35 -2
- package/lib/api/reporting/sql-helpers.js +39 -1
- package/lib/api/reporting/stakeholder/compose.d.ts +115 -0
- package/lib/api/reporting/stakeholder/compose.js +234 -0
- package/lib/api/reporting/stakeholder/index.d.ts +14 -0
- package/lib/api/reporting/stakeholder/index.js +17 -0
- package/lib/api/reporting/stakeholder/period.d.ts +102 -0
- package/lib/api/reporting/stakeholder/period.js +273 -0
- package/lib/api/reporting/stakeholder/report-html.d.ts +93 -0
- package/lib/api/reporting/stakeholder/report-html.js +208 -0
- package/lib/api/reporting/stakeholder/sections.d.ts +117 -0
- package/lib/api/reporting/stakeholder/sections.js +249 -0
- package/lib/api/reporting/stakeholder/store.d.ts +450 -0
- package/lib/api/reporting/stakeholder/store.js +981 -0
- package/lib/api/reporting/stakeholder/templates.d.ts +21 -0
- package/lib/api/reporting/stakeholder/templates.js +104 -0
- package/lib/api/reporting/types.d.ts +73 -0
- package/lib/api/reporting/types.js +1 -1
- package/lib/api/reporting-service.d.ts +94 -5
- package/lib/api/reporting-service.js +169 -10
- package/lib/api/soft-delete-sweep.d.ts +11 -2
- package/lib/api/soft-delete-sweep.js +17 -6
- package/lib/database/drizzle-schema.d.ts +2244 -385
- package/lib/database/drizzle-schema.js +18 -7
- package/lib/database/index.d.ts +1 -1
- package/lib/database/index.js +4 -2
- package/lib/database/schema/compliance.d.ts +0 -682
- package/lib/database/schema/compliance.js +6 -66
- package/lib/database/schema/ecosystem.d.ts +20 -157
- package/lib/database/schema/ecosystem.js +23 -16
- package/lib/database/schema/pipeline.d.ts +200 -0
- package/lib/database/schema/pipeline.js +100 -1
- package/lib/database/schema/plugin.d.ts +20 -1
- package/lib/database/schema/plugin.js +13 -1
- package/lib/database/schema/reporting-analytics.d.ts +1212 -0
- package/lib/database/schema/reporting-analytics.js +250 -0
- package/lib/database/schema/reporting-stakeholder.d.ts +1373 -0
- package/lib/database/schema/reporting-stakeholder.js +251 -0
- package/lib/index.d.ts +10 -1
- package/lib/index.js +15 -1
- package/lib/testing/exports.json +67 -4
- package/package.json +4 -3
|
@@ -0,0 +1,981 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Persistence for stakeholder reports: definitions, runs, share links and
|
|
5
|
+
* recipients.
|
|
6
|
+
*
|
|
7
|
+
* Hand-rolled rather than four `CrudService` subclasses because none of these is
|
|
8
|
+
* a generic listable entity with an `isDefault` slot and project-scoped access
|
|
9
|
+
* control. What they need instead is a small number of exact operations with
|
|
10
|
+
* invariants the generic base cannot express:
|
|
11
|
+
*
|
|
12
|
+
* - A RUN IS APPEND-ONLY once published. Regenerating a period inserts version
|
|
13
|
+
* N+1 and points the old row at it (`supersededBy`); nothing overwrites a
|
|
14
|
+
* snapshot a manager has already read.
|
|
15
|
+
* - A SHARE LINK IS RESOLVED WITHOUT A TENANT. The reader has no account, so the
|
|
16
|
+
* lookup by token hash is the one read here that deliberately runs as sysadmin
|
|
17
|
+
* — see {@link resolveShareLink} for why that is the narrowest safe shape.
|
|
18
|
+
* - A RECIPIENT IS UPSERTED, NEVER RE-CREATED. Re-adding an address must keep its
|
|
19
|
+
* verification and unsubscribe state, or an unsubscribe could be undone by
|
|
20
|
+
* deleting and re-adding the row.
|
|
21
|
+
*
|
|
22
|
+
* Everything else runs inside the caller's ambient tenant context (established by
|
|
23
|
+
* `withTenantContext()` at the request boundary), so RLS is the tenancy gate and
|
|
24
|
+
* an `orgId` argument is a redundant second check rather than the only one.
|
|
25
|
+
*/
|
|
26
|
+
import { randomUUID, createHash, randomBytes } from 'node:crypto';
|
|
27
|
+
import { NotFoundError, ConflictError, ErrorCode } from '@pipeline-builder/api-core';
|
|
28
|
+
import { and, asc, desc, eq, inArray, isNull, lte, sql } from 'drizzle-orm';
|
|
29
|
+
import { schema } from '../../../database/drizzle-schema.js';
|
|
30
|
+
import { runWithTenantContext, withTenantTx } from '../../../database/tenancy.js';
|
|
31
|
+
import { softDeleteRetentionMs } from '../../crud-service.js';
|
|
32
|
+
/** How long a freshly minted share link lives when the caller names no window. */
|
|
33
|
+
export const DEFAULT_SHARE_LINK_TTL_DAYS = 30;
|
|
34
|
+
/** The longest a share link may live. Beyond this it is a permanent URL. */
|
|
35
|
+
export const MAX_SHARE_LINK_TTL_DAYS = 180;
|
|
36
|
+
/** A recipient's verification link. Short: it is clicked within minutes or never. */
|
|
37
|
+
export const VERIFICATION_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
38
|
+
/**
|
|
39
|
+
* Bounces before an address is treated as undeliverable and skipped. Three, not
|
|
40
|
+
* one: a single bounce is often an out-of-office autoresponder or a full mailbox,
|
|
41
|
+
* and dropping a manager off the list for one of those is a silent failure.
|
|
42
|
+
*/
|
|
43
|
+
export const MAX_BOUNCES = 3;
|
|
44
|
+
/** SHA-256 hex of a token. The only form any token is ever stored in. */
|
|
45
|
+
export function hashToken(token) {
|
|
46
|
+
return createHash('sha256').update(token, 'utf8').digest('hex');
|
|
47
|
+
}
|
|
48
|
+
/** A fresh URL-safe token, and its hash. The raw value is returned exactly once. */
|
|
49
|
+
export function mintToken() {
|
|
50
|
+
const token = randomBytes(32).toString('base64url');
|
|
51
|
+
return { token, tokenHash: hashToken(token) };
|
|
52
|
+
}
|
|
53
|
+
/** The `purge_after` stamp a soft-delete carries, or `{}` when purging is off. */
|
|
54
|
+
function purgeStamp(now) {
|
|
55
|
+
const ms = softDeleteRetentionMs();
|
|
56
|
+
return ms > 0 ? { purgeAfter: new Date(now.getTime() + ms) } : {};
|
|
57
|
+
}
|
|
58
|
+
export class StakeholderReportStore {
|
|
59
|
+
// ── Definitions ────────────────────────────────────────────────────────────
|
|
60
|
+
async createDefinition(input) {
|
|
61
|
+
const id = randomUUID();
|
|
62
|
+
const rows = await withTenantTx((tx) => tx.insert(schema.reportDefinition).values({
|
|
63
|
+
id,
|
|
64
|
+
orgId: input.orgId,
|
|
65
|
+
ownerId: input.ownerId,
|
|
66
|
+
name: input.name,
|
|
67
|
+
template: input.template,
|
|
68
|
+
sections: input.sections,
|
|
69
|
+
cadence: input.cadence,
|
|
70
|
+
timezone: input.timezone,
|
|
71
|
+
weekStart: input.weekStart,
|
|
72
|
+
scope: input.scope,
|
|
73
|
+
recipients: input.recipients,
|
|
74
|
+
autoSend: input.autoSend,
|
|
75
|
+
nextRunAt: input.nextRunAt ?? null,
|
|
76
|
+
createdBy: input.createdBy,
|
|
77
|
+
}).returning());
|
|
78
|
+
return rows[0];
|
|
79
|
+
}
|
|
80
|
+
async listDefinitions(orgId) {
|
|
81
|
+
const rows = await withTenantTx((tx) => tx.select().from(schema.reportDefinition)
|
|
82
|
+
.where(and(eq(schema.reportDefinition.orgId, orgId), isNull(schema.reportDefinition.deletedAt)))
|
|
83
|
+
.orderBy(asc(schema.reportDefinition.name)));
|
|
84
|
+
return rows;
|
|
85
|
+
}
|
|
86
|
+
async getDefinition(orgId, id) {
|
|
87
|
+
const rows = await withTenantTx((tx) => tx.select().from(schema.reportDefinition)
|
|
88
|
+
.where(and(eq(schema.reportDefinition.id, id), eq(schema.reportDefinition.orgId, orgId), isNull(schema.reportDefinition.deletedAt)))
|
|
89
|
+
.limit(1));
|
|
90
|
+
return rows[0] ?? null;
|
|
91
|
+
}
|
|
92
|
+
/** Throws {@link NotFoundError} rather than returning null, for route bodies. */
|
|
93
|
+
async requireDefinition(orgId, id) {
|
|
94
|
+
const found = await this.getDefinition(orgId, id);
|
|
95
|
+
if (!found)
|
|
96
|
+
throw new NotFoundError('Report definition not found');
|
|
97
|
+
return found;
|
|
98
|
+
}
|
|
99
|
+
async updateDefinition(orgId, id, patch, updatedBy) {
|
|
100
|
+
const set = { updatedBy, updatedAt: new Date() };
|
|
101
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
102
|
+
if (value !== undefined)
|
|
103
|
+
set[key] = value;
|
|
104
|
+
}
|
|
105
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportDefinition).set(set)
|
|
106
|
+
.where(and(eq(schema.reportDefinition.id, id), eq(schema.reportDefinition.orgId, orgId), isNull(schema.reportDefinition.deletedAt)))
|
|
107
|
+
.returning());
|
|
108
|
+
const updated = rows[0];
|
|
109
|
+
if (!updated)
|
|
110
|
+
throw new NotFoundError('Report definition not found');
|
|
111
|
+
return updated;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Hand a definition to a new owner. The owner is who a scheduled run is
|
|
115
|
+
* authorized AS, so this is a privilege move, not a metadata edit — the route
|
|
116
|
+
* checks the new owner is a live member of the org before calling.
|
|
117
|
+
*/
|
|
118
|
+
async transferOwner(orgId, id, newOwnerId, actorId) {
|
|
119
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportDefinition)
|
|
120
|
+
.set({
|
|
121
|
+
ownerId: newOwnerId,
|
|
122
|
+
updatedBy: actorId,
|
|
123
|
+
updatedAt: new Date(),
|
|
124
|
+
// A definition paused because its previous owner lost access is live again
|
|
125
|
+
// under an owner who has it. The scheduler re-checks every run anyway, so
|
|
126
|
+
// the worst case is one more paused run, never a standing grant.
|
|
127
|
+
isActive: true,
|
|
128
|
+
pausedReason: null,
|
|
129
|
+
})
|
|
130
|
+
.where(and(eq(schema.reportDefinition.id, id), eq(schema.reportDefinition.orgId, orgId), isNull(schema.reportDefinition.deletedAt)))
|
|
131
|
+
.returning());
|
|
132
|
+
const updated = rows[0];
|
|
133
|
+
if (!updated)
|
|
134
|
+
throw new NotFoundError('Report definition not found');
|
|
135
|
+
return updated;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Pause every definition a user owns, with the reason recorded. Called when the
|
|
139
|
+
* owner is deactivated or loses `reports:author` — a report must stop when the
|
|
140
|
+
* person accountable for it can no longer see the data behind it.
|
|
141
|
+
*
|
|
142
|
+
* Returns the paused definitions so the caller can notify an org admin: a
|
|
143
|
+
* silently stopped weekly report is discovered by a manager not receiving it.
|
|
144
|
+
*/
|
|
145
|
+
async pauseDefinitionsForOwner(orgId, ownerId, reason) {
|
|
146
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportDefinition)
|
|
147
|
+
.set({ isActive: false, pausedReason: reason, updatedAt: new Date() })
|
|
148
|
+
.where(and(eq(schema.reportDefinition.orgId, orgId), eq(schema.reportDefinition.ownerId, ownerId), eq(schema.reportDefinition.isActive, true), isNull(schema.reportDefinition.deletedAt)))
|
|
149
|
+
.returning());
|
|
150
|
+
return rows;
|
|
151
|
+
}
|
|
152
|
+
async deleteDefinition(orgId, id, deletedBy) {
|
|
153
|
+
const now = new Date();
|
|
154
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportDefinition)
|
|
155
|
+
.set({ deletedAt: now, deletedBy, ...purgeStamp(now) })
|
|
156
|
+
.where(and(eq(schema.reportDefinition.id, id), eq(schema.reportDefinition.orgId, orgId), isNull(schema.reportDefinition.deletedAt)))
|
|
157
|
+
.returning({ id: schema.reportDefinition.id }));
|
|
158
|
+
if (rows.length === 0) {
|
|
159
|
+
throw new NotFoundError('Report definition not found');
|
|
160
|
+
}
|
|
161
|
+
// Links to this definition's runs are revoked in the same breath. Deleting a
|
|
162
|
+
// report while a public URL to last month's numbers keeps working is the kind
|
|
163
|
+
// of gap nobody notices until it matters.
|
|
164
|
+
await this.revokeLinksForDefinition(orgId, id, deletedBy);
|
|
165
|
+
}
|
|
166
|
+
// ── Runs ───────────────────────────────────────────────────────────────────
|
|
167
|
+
/**
|
|
168
|
+
* Insert a run for a period, or return the existing one.
|
|
169
|
+
*
|
|
170
|
+
* Idempotent on (definition, periodStart, version): a retry, a catch-up pass
|
|
171
|
+
* and a manual backfill can all ask for the same period, and without this each
|
|
172
|
+
* would insert a row and the lead would review three copies. `onConflictDoNothing`
|
|
173
|
+
* plus a re-read is deliberate — the unique index is the arbiter, so two pods
|
|
174
|
+
* racing produce one row whichever wins.
|
|
175
|
+
*/
|
|
176
|
+
async createRun(input) {
|
|
177
|
+
const version = input.version ?? 1;
|
|
178
|
+
const id = randomUUID();
|
|
179
|
+
const rows = await withTenantTx((tx) => tx.insert(schema.reportRun).values({
|
|
180
|
+
id,
|
|
181
|
+
orgId: input.orgId,
|
|
182
|
+
definitionId: input.definitionId,
|
|
183
|
+
periodStart: input.periodStart,
|
|
184
|
+
periodEnd: input.periodEnd,
|
|
185
|
+
periodLabel: input.periodLabel,
|
|
186
|
+
version,
|
|
187
|
+
status: input.status ?? 'drafting',
|
|
188
|
+
snapshot: input.snapshot ?? null,
|
|
189
|
+
failureReason: input.failureReason ?? null,
|
|
190
|
+
}).onConflictDoNothing({
|
|
191
|
+
target: [schema.reportRun.definitionId, schema.reportRun.periodStart, schema.reportRun.version],
|
|
192
|
+
}).returning());
|
|
193
|
+
const inserted = rows[0];
|
|
194
|
+
if (inserted)
|
|
195
|
+
return { run: inserted, created: true };
|
|
196
|
+
const existing = await withTenantTx((tx) => tx.select().from(schema.reportRun)
|
|
197
|
+
.where(and(eq(schema.reportRun.definitionId, input.definitionId), eq(schema.reportRun.periodStart, input.periodStart), eq(schema.reportRun.version, version)))
|
|
198
|
+
.limit(1));
|
|
199
|
+
const found = existing[0];
|
|
200
|
+
if (!found) {
|
|
201
|
+
// The insert was skipped by the conflict target but nothing is there to
|
|
202
|
+
// read: the only way is a concurrent delete between the two statements.
|
|
203
|
+
throw new ConflictError('Report run could not be created', ErrorCode.CONFLICT, {
|
|
204
|
+
definitionId: input.definitionId,
|
|
205
|
+
periodLabel: input.periodLabel,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return { run: found, created: false };
|
|
209
|
+
}
|
|
210
|
+
/** The next version number for a period: `max(version) + 1`. */
|
|
211
|
+
async nextVersion(definitionId, periodStart) {
|
|
212
|
+
const rows = await withTenantTx((tx) => tx.select({ version: schema.reportRun.version })
|
|
213
|
+
.from(schema.reportRun)
|
|
214
|
+
.where(and(eq(schema.reportRun.definitionId, definitionId), eq(schema.reportRun.periodStart, periodStart)))
|
|
215
|
+
.orderBy(desc(schema.reportRun.version))
|
|
216
|
+
.limit(1));
|
|
217
|
+
const top = rows[0];
|
|
218
|
+
return (top?.version ?? 0) + 1;
|
|
219
|
+
}
|
|
220
|
+
/** Point an older run at the version that replaced it. */
|
|
221
|
+
async supersede(orgId, oldRunId, newRunId) {
|
|
222
|
+
await withTenantTx((tx) => tx.update(schema.reportRun)
|
|
223
|
+
.set({ supersededBy: newRunId, updatedAt: new Date() })
|
|
224
|
+
.where(and(eq(schema.reportRun.id, oldRunId), eq(schema.reportRun.orgId, orgId))));
|
|
225
|
+
}
|
|
226
|
+
async listRuns(orgId, definitionId, limit = 50) {
|
|
227
|
+
const rows = await withTenantTx((tx) => tx.select().from(schema.reportRun)
|
|
228
|
+
.where(and(eq(schema.reportRun.orgId, orgId), eq(schema.reportRun.definitionId, definitionId), isNull(schema.reportRun.deletedAt)))
|
|
229
|
+
.orderBy(desc(schema.reportRun.periodStart), desc(schema.reportRun.version))
|
|
230
|
+
.limit(limit));
|
|
231
|
+
return rows;
|
|
232
|
+
}
|
|
233
|
+
async getRun(orgId, id) {
|
|
234
|
+
const rows = await withTenantTx((tx) => tx.select().from(schema.reportRun)
|
|
235
|
+
.where(and(eq(schema.reportRun.id, id), eq(schema.reportRun.orgId, orgId), isNull(schema.reportRun.deletedAt)))
|
|
236
|
+
.limit(1));
|
|
237
|
+
return rows[0] ?? null;
|
|
238
|
+
}
|
|
239
|
+
async requireRun(orgId, id) {
|
|
240
|
+
const found = await this.getRun(orgId, id);
|
|
241
|
+
if (!found)
|
|
242
|
+
throw new NotFoundError('Report run not found');
|
|
243
|
+
return found;
|
|
244
|
+
}
|
|
245
|
+
/** Store a computed snapshot and move the run to `ready_for_review`. */
|
|
246
|
+
async completeRun(orgId, id, snapshot, status = 'ready_for_review') {
|
|
247
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportRun)
|
|
248
|
+
.set({ snapshot, status, failureReason: null, updatedAt: new Date() })
|
|
249
|
+
.where(and(eq(schema.reportRun.id, id), eq(schema.reportRun.orgId, orgId), isNull(schema.reportRun.deletedAt)))
|
|
250
|
+
.returning());
|
|
251
|
+
const updated = rows[0];
|
|
252
|
+
if (!updated)
|
|
253
|
+
throw new NotFoundError('Report run not found');
|
|
254
|
+
return updated;
|
|
255
|
+
}
|
|
256
|
+
async failRun(orgId, id, failureReason) {
|
|
257
|
+
await withTenantTx((tx) => tx.update(schema.reportRun)
|
|
258
|
+
.set({ status: 'failed', failureReason, updatedAt: new Date() })
|
|
259
|
+
.where(and(eq(schema.reportRun.id, id), eq(schema.reportRun.orgId, orgId))));
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Edit the lead's own words on a run that has NOT been published.
|
|
263
|
+
*
|
|
264
|
+
* Refused after publish: the notes are part of what the manager read, and a
|
|
265
|
+
* report whose narrative can be edited after delivery is a report whose
|
|
266
|
+
* delivered version cannot be reconstructed. Regenerating produces version N+1,
|
|
267
|
+
* which is the supported way to correct a published report.
|
|
268
|
+
*/
|
|
269
|
+
async setRunNotes(orgId, id, patch) {
|
|
270
|
+
const run = await this.requireRun(orgId, id);
|
|
271
|
+
if (run.status === 'published') {
|
|
272
|
+
throw new ConflictError('This report has already been published. Regenerate it to produce a new version with your changes — '
|
|
273
|
+
+ 'the published version stays exactly as the recipients read it.', ErrorCode.CONFLICT, { runId: id, publishedAt: run.publishedAt?.toISOString() });
|
|
274
|
+
}
|
|
275
|
+
const set = { updatedAt: new Date() };
|
|
276
|
+
if (patch.leadNotes !== undefined)
|
|
277
|
+
set.leadNotes = patch.leadNotes;
|
|
278
|
+
if (patch.aiDraft !== undefined)
|
|
279
|
+
set.aiDraft = patch.aiDraft;
|
|
280
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportRun).set(set)
|
|
281
|
+
.where(and(eq(schema.reportRun.id, id), eq(schema.reportRun.orgId, orgId), isNull(schema.reportRun.deletedAt)))
|
|
282
|
+
.returning());
|
|
283
|
+
return rows[0];
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Record what happened when a run was delivered.
|
|
287
|
+
*
|
|
288
|
+
* Deliberately NOT part of {@link setRunNotes}, which refuses a published run: delivery
|
|
289
|
+
* happens AT publish, so a method that refused published runs could never record it.
|
|
290
|
+
* This one writes exactly one column and nothing else, which is why it is safe to allow
|
|
291
|
+
* after publish — it cannot change a number a recipient has read.
|
|
292
|
+
*/
|
|
293
|
+
async recordDelivery(orgId, id, delivery) {
|
|
294
|
+
await withTenantTx((tx) => tx.update(schema.reportRun)
|
|
295
|
+
.set({ delivery, updatedAt: new Date() })
|
|
296
|
+
.where(and(eq(schema.reportRun.id, id), eq(schema.reportRun.orgId, orgId), isNull(schema.reportRun.deletedAt))));
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Publish a run.
|
|
300
|
+
*
|
|
301
|
+
* Conditional on the row still being unpublished (`published_at IS NULL`), so
|
|
302
|
+
* two clicks — or two tabs — produce one publish and one set of deliveries
|
|
303
|
+
* rather than two. A run with no snapshot cannot be published: there would be
|
|
304
|
+
* nothing for a recipient to read.
|
|
305
|
+
*/
|
|
306
|
+
async publishRun(orgId, id, publishedBy) {
|
|
307
|
+
const run = await this.requireRun(orgId, id);
|
|
308
|
+
if (run.status === 'failed' || !run.snapshot) {
|
|
309
|
+
throw new ConflictError('This report has no computed snapshot, so there is nothing to publish. Regenerate it first.', ErrorCode.CONFLICT, { runId: id, status: run.status });
|
|
310
|
+
}
|
|
311
|
+
const now = new Date();
|
|
312
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportRun)
|
|
313
|
+
.set({ status: 'published', publishedBy, publishedAt: now, updatedAt: now })
|
|
314
|
+
.where(and(eq(schema.reportRun.id, id), eq(schema.reportRun.orgId, orgId), isNull(schema.reportRun.publishedAt), isNull(schema.reportRun.deletedAt)))
|
|
315
|
+
.returning());
|
|
316
|
+
const published = rows[0];
|
|
317
|
+
if (published)
|
|
318
|
+
return { run: published, alreadyPublished: false };
|
|
319
|
+
// The conditional update matched nothing: someone else published it first.
|
|
320
|
+
return { run: await this.requireRun(orgId, id), alreadyPublished: true };
|
|
321
|
+
}
|
|
322
|
+
// ── Share links ────────────────────────────────────────────────────────────
|
|
323
|
+
/**
|
|
324
|
+
* Mint a read-only link to a published run. The raw token is returned here and
|
|
325
|
+
* nowhere else — only its hash is stored, so a database copy cannot be turned
|
|
326
|
+
* back into working URLs.
|
|
327
|
+
*/
|
|
328
|
+
async createShareLink(input) {
|
|
329
|
+
const run = await this.requireRun(input.orgId, input.runId);
|
|
330
|
+
if (run.status !== 'published') {
|
|
331
|
+
throw new ConflictError('Only a published report can be shared. Publish it first — a link to a draft would show numbers the lead has not reviewed.', ErrorCode.CONFLICT, { runId: input.runId, status: run.status });
|
|
332
|
+
}
|
|
333
|
+
const days = Math.min(input.ttlDays ?? DEFAULT_SHARE_LINK_TTL_DAYS, MAX_SHARE_LINK_TTL_DAYS);
|
|
334
|
+
const now = input.now ?? new Date();
|
|
335
|
+
const { token, tokenHash } = mintToken();
|
|
336
|
+
const rows = await withTenantTx((tx) => tx.insert(schema.reportShareLink).values({
|
|
337
|
+
id: randomUUID(),
|
|
338
|
+
orgId: input.orgId,
|
|
339
|
+
runId: input.runId,
|
|
340
|
+
tokenHash,
|
|
341
|
+
expiresAt: new Date(now.getTime() + days * 24 * 60 * 60 * 1000),
|
|
342
|
+
redactNames: input.redactNames ?? false,
|
|
343
|
+
createdBy: input.createdBy,
|
|
344
|
+
}).returning());
|
|
345
|
+
return { link: rows[0], token };
|
|
346
|
+
}
|
|
347
|
+
async listShareLinks(orgId, runId) {
|
|
348
|
+
const rows = await withTenantTx((tx) => tx.select().from(schema.reportShareLink)
|
|
349
|
+
.where(and(eq(schema.reportShareLink.orgId, orgId), eq(schema.reportShareLink.runId, runId)))
|
|
350
|
+
.orderBy(desc(schema.reportShareLink.createdAt)));
|
|
351
|
+
return rows;
|
|
352
|
+
}
|
|
353
|
+
async revokeShareLink(orgId, id, revokedBy) {
|
|
354
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportShareLink)
|
|
355
|
+
.set({ revokedAt: new Date(), revokedBy })
|
|
356
|
+
.where(and(eq(schema.reportShareLink.id, id), eq(schema.reportShareLink.orgId, orgId), isNull(schema.reportShareLink.revokedAt)))
|
|
357
|
+
.returning());
|
|
358
|
+
const revoked = rows[0];
|
|
359
|
+
if (!revoked)
|
|
360
|
+
throw new NotFoundError('Share link not found or already revoked');
|
|
361
|
+
return revoked;
|
|
362
|
+
}
|
|
363
|
+
/** Revoke every live link to any run of a definition. Used on delete. */
|
|
364
|
+
async revokeLinksForDefinition(orgId, definitionId, revokedBy) {
|
|
365
|
+
const runs = await withTenantTx((tx) => tx.select({ id: schema.reportRun.id }).from(schema.reportRun)
|
|
366
|
+
.where(and(eq(schema.reportRun.orgId, orgId), eq(schema.reportRun.definitionId, definitionId))));
|
|
367
|
+
const runIds = runs.map((r) => r.id);
|
|
368
|
+
if (runIds.length === 0)
|
|
369
|
+
return 0;
|
|
370
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportShareLink)
|
|
371
|
+
.set({ revokedAt: new Date(), revokedBy })
|
|
372
|
+
.where(and(eq(schema.reportShareLink.orgId, orgId), inArray(schema.reportShareLink.runId, runIds), isNull(schema.reportShareLink.revokedAt)))
|
|
373
|
+
.returning({ id: schema.reportShareLink.id }));
|
|
374
|
+
return rows.length;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Resolve a share token to its link and run, for the UNAUTHENTICATED public
|
|
378
|
+
* route.
|
|
379
|
+
*
|
|
380
|
+
* This is the one read in this module that runs as sysadmin, and the reason is
|
|
381
|
+
* structural: the reader holds a token and nothing else — no account, no org,
|
|
382
|
+
* no JWT — so there is no tenant context for RLS to scope by, and the org can
|
|
383
|
+
* only be learned FROM the row. The escalation is kept as narrow as the problem:
|
|
384
|
+
*
|
|
385
|
+
* - The sysadmin scope covers the token-hash lookup ONLY. The run is read back
|
|
386
|
+
* under the link's OWN org, so a bug in the run query cannot reach another
|
|
387
|
+
* tenant's rows.
|
|
388
|
+
* - The lookup is by a 256-bit hash on a unique index. It is not a filter a
|
|
389
|
+
* caller can widen — there is no other predicate to influence.
|
|
390
|
+
* - Expired and revoked links resolve to null here, so the caller cannot
|
|
391
|
+
* accidentally serve one by forgetting a check.
|
|
392
|
+
*
|
|
393
|
+
* Returns null for every failure — unknown, revoked, expired, or pointing at a
|
|
394
|
+
* run that is no longer published — so the route answers one indistinguishable
|
|
395
|
+
* 404 and a holder of a dead link learns nothing about which of those it was.
|
|
396
|
+
*/
|
|
397
|
+
async resolveShareLink(token, now = new Date()) {
|
|
398
|
+
const tokenHash = hashToken(token);
|
|
399
|
+
const links = await runWithTenantContext({ isSuperAdmin: true }, () => withTenantTx((tx) => tx.select().from(schema.reportShareLink)
|
|
400
|
+
.where(eq(schema.reportShareLink.tokenHash, tokenHash))
|
|
401
|
+
.limit(1)));
|
|
402
|
+
const link = links[0];
|
|
403
|
+
// No constant-time comparison here on purpose: the lookup is an equality match
|
|
404
|
+
// on a 256-bit hash over a unique index, so there is no secret-dependent branch
|
|
405
|
+
// in this function to leak. A hand-rolled compare after the database already
|
|
406
|
+
// decided it would be theatre.
|
|
407
|
+
if (!link)
|
|
408
|
+
return null;
|
|
409
|
+
if (link.revokedAt)
|
|
410
|
+
return null;
|
|
411
|
+
if (link.expiresAt.getTime() <= now.getTime())
|
|
412
|
+
return null;
|
|
413
|
+
const runs = await runWithTenantContext({ orgId: link.orgId, isSuperAdmin: false }, () => withTenantTx((tx) => tx.select().from(schema.reportRun)
|
|
414
|
+
.where(and(eq(schema.reportRun.id, link.runId), eq(schema.reportRun.orgId, link.orgId), eq(schema.reportRun.status, 'published'), isNull(schema.reportRun.deletedAt)))
|
|
415
|
+
.limit(1)));
|
|
416
|
+
const run = runs[0];
|
|
417
|
+
if (!run)
|
|
418
|
+
return null;
|
|
419
|
+
return { link, run };
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Count one human view of a shared report.
|
|
423
|
+
*
|
|
424
|
+
* Separate from {@link resolveShareLink} so the route can serve a link preview,
|
|
425
|
+
* a HEAD probe or a mail scanner's fetch WITHOUT counting it — a view count that
|
|
426
|
+
* jumps the moment a link is pasted into Slack tells the lead their manager read
|
|
427
|
+
* the report when nobody has.
|
|
428
|
+
*/
|
|
429
|
+
async recordShareView(link, now = new Date()) {
|
|
430
|
+
await runWithTenantContext({ orgId: link.orgId, isSuperAdmin: false }, () => withTenantTx((tx) => tx.update(schema.reportShareLink)
|
|
431
|
+
.set({ viewCount: sql `${schema.reportShareLink.viewCount} + 1`, lastViewedAt: now })
|
|
432
|
+
.where(eq(schema.reportShareLink.id, link.id))));
|
|
433
|
+
}
|
|
434
|
+
// ── Recipients ─────────────────────────────────────────────────────────────
|
|
435
|
+
/**
|
|
436
|
+
* Add (or re-add) a delivery address.
|
|
437
|
+
*
|
|
438
|
+
* Upserts on (org, email) and deliberately does NOT reset `unsubscribed_at` or
|
|
439
|
+
* `verified_at`: a lead must not be able to undo someone's unsubscribe by
|
|
440
|
+
* removing and re-adding them, and an address already verified should not have
|
|
441
|
+
* to confirm twice. A verification token is minted only when the address is not
|
|
442
|
+
* yet verified.
|
|
443
|
+
*/
|
|
444
|
+
async upsertRecipient(input) {
|
|
445
|
+
const email = input.email.trim().toLowerCase();
|
|
446
|
+
const now = input.now ?? new Date();
|
|
447
|
+
const minted = input.preVerified ? undefined : mintToken();
|
|
448
|
+
// The unsubscribe token is minted for EVERY recipient, member or not, because every
|
|
449
|
+
// report email carries the link. Minted once and kept: a link in a report from March
|
|
450
|
+
// has to keep working.
|
|
451
|
+
const unsub = mintToken();
|
|
452
|
+
const rows = await withTenantTx((tx) => tx.insert(schema.reportRecipient).values({
|
|
453
|
+
id: randomUUID(),
|
|
454
|
+
orgId: input.orgId,
|
|
455
|
+
email,
|
|
456
|
+
displayName: input.displayName ?? null,
|
|
457
|
+
verifiedAt: input.preVerified ? now : null,
|
|
458
|
+
verificationTokenHash: minted?.tokenHash ?? null,
|
|
459
|
+
unsubscribeToken: unsub.token,
|
|
460
|
+
approvedBy: input.approvedBy ?? null,
|
|
461
|
+
createdBy: input.createdBy,
|
|
462
|
+
}).onConflictDoUpdate({
|
|
463
|
+
target: [schema.reportRecipient.orgId, schema.reportRecipient.email],
|
|
464
|
+
set: {
|
|
465
|
+
displayName: input.displayName ?? sql `${schema.reportRecipient.displayName}`,
|
|
466
|
+
// Re-adding a tombstoned recipient revives the row with its history.
|
|
467
|
+
deletedAt: null,
|
|
468
|
+
deletedBy: null,
|
|
469
|
+
purgeAfter: null,
|
|
470
|
+
approvedBy: input.approvedBy ?? sql `${schema.reportRecipient.approvedBy}`,
|
|
471
|
+
// Only mint over an UNVERIFIED row; a verified address keeps its state.
|
|
472
|
+
verificationTokenHash: sql `CASE WHEN ${schema.reportRecipient.verifiedAt} IS NULL
|
|
473
|
+
THEN ${minted?.tokenHash ?? null} ELSE ${schema.reportRecipient.verificationTokenHash} END`,
|
|
474
|
+
verifiedAt: input.preVerified
|
|
475
|
+
? sql `COALESCE(${schema.reportRecipient.verifiedAt}, ${now})`
|
|
476
|
+
: sql `${schema.reportRecipient.verifiedAt}`,
|
|
477
|
+
// COALESCE, never overwrite: re-adding an address must not break the unsubscribe
|
|
478
|
+
// link in every report already delivered to it.
|
|
479
|
+
unsubscribeToken: sql `COALESCE(${schema.reportRecipient.unsubscribeToken}, ${unsub.token})`,
|
|
480
|
+
},
|
|
481
|
+
}).returning());
|
|
482
|
+
const recipient = rows[0];
|
|
483
|
+
// The token is only useful if this row actually took it.
|
|
484
|
+
const tookToken = minted && recipient.verificationTokenHash === minted.tokenHash;
|
|
485
|
+
return { recipient, ...(tookToken ? { verificationToken: minted.token } : {}) };
|
|
486
|
+
}
|
|
487
|
+
async listRecipients(orgId) {
|
|
488
|
+
const rows = await withTenantTx((tx) => tx.select().from(schema.reportRecipient)
|
|
489
|
+
.where(and(eq(schema.reportRecipient.orgId, orgId), isNull(schema.reportRecipient.deletedAt)))
|
|
490
|
+
.orderBy(asc(schema.reportRecipient.email)));
|
|
491
|
+
return rows;
|
|
492
|
+
}
|
|
493
|
+
async getRecipients(orgId, ids) {
|
|
494
|
+
if (ids.length === 0)
|
|
495
|
+
return [];
|
|
496
|
+
const rows = await withTenantTx((tx) => tx.select().from(schema.reportRecipient)
|
|
497
|
+
.where(and(eq(schema.reportRecipient.orgId, orgId), inArray(schema.reportRecipient.id, ids), isNull(schema.reportRecipient.deletedAt))));
|
|
498
|
+
return rows;
|
|
499
|
+
}
|
|
500
|
+
/** Mint a fresh verification token for a recipient that has not confirmed yet. */
|
|
501
|
+
async resendVerification(orgId, id) {
|
|
502
|
+
const { token, tokenHash } = mintToken();
|
|
503
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportRecipient)
|
|
504
|
+
.set({ verificationTokenHash: tokenHash })
|
|
505
|
+
.where(and(eq(schema.reportRecipient.id, id), eq(schema.reportRecipient.orgId, orgId), isNull(schema.reportRecipient.verifiedAt), isNull(schema.reportRecipient.deletedAt)))
|
|
506
|
+
.returning());
|
|
507
|
+
const recipient = rows[0];
|
|
508
|
+
if (!recipient) {
|
|
509
|
+
throw new ConflictError('This recipient is already verified, or no longer exists.', ErrorCode.CONFLICT, { recipientId: id });
|
|
510
|
+
}
|
|
511
|
+
return { recipient, token };
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Confirm an address from the emailed token. Cross-org by necessity (the person
|
|
515
|
+
* clicking has no session), so it runs as sysadmin over a unique 256-bit hash —
|
|
516
|
+
* the same shape, and the same reasoning, as {@link resolveShareLink}.
|
|
517
|
+
*
|
|
518
|
+
* The token is CONSUMED: it is cleared on success, so a forwarded confirmation
|
|
519
|
+
* link cannot be replayed by whoever the mail was forwarded to.
|
|
520
|
+
*/
|
|
521
|
+
async verifyRecipientByToken(token, now = new Date()) {
|
|
522
|
+
const tokenHash = hashToken(token);
|
|
523
|
+
const rows = await runWithTenantContext({ isSuperAdmin: true }, () => withTenantTx((tx) => tx.update(schema.reportRecipient)
|
|
524
|
+
.set({ verifiedAt: now, verificationTokenHash: null })
|
|
525
|
+
.where(and(eq(schema.reportRecipient.verificationTokenHash, tokenHash), isNull(schema.reportRecipient.verifiedAt), isNull(schema.reportRecipient.deletedAt),
|
|
526
|
+
// A token older than the window is not accepted. `created_at` is the
|
|
527
|
+
// floor for the first token; a resend refreshes the hash, so this bounds
|
|
528
|
+
// the original mint and is deliberately generous.
|
|
529
|
+
sql `${schema.reportRecipient.createdAt} > ${new Date(now.getTime() - VERIFICATION_TTL_MS)}`))
|
|
530
|
+
.returning()));
|
|
531
|
+
return rows[0] ?? null;
|
|
532
|
+
}
|
|
533
|
+
/** Honour an unsubscribe across every definition in the org. */
|
|
534
|
+
async unsubscribeByEmail(orgId, email, now = new Date()) {
|
|
535
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportRecipient)
|
|
536
|
+
.set({ unsubscribedAt: now })
|
|
537
|
+
.where(and(eq(schema.reportRecipient.orgId, orgId), eq(schema.reportRecipient.email, email.trim().toLowerCase()), isNull(schema.reportRecipient.unsubscribedAt)))
|
|
538
|
+
.returning({ id: schema.reportRecipient.id }));
|
|
539
|
+
return rows.length > 0;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Honour an unsubscribe from the link in a report email.
|
|
543
|
+
*
|
|
544
|
+
* Looked up by TOKEN across orgs, as sysadmin, for the same reason the share-link
|
|
545
|
+
* resolve is: the person clicking has no account and no tenant, and the token is the
|
|
546
|
+
* only thing that identifies the row. It is the narrowest possible shape — one indexed
|
|
547
|
+
* lookup, one column written, nothing returned to the caller but whether a row
|
|
548
|
+
* matched.
|
|
549
|
+
*
|
|
550
|
+
* Idempotent. A mail client that sends the one-click POST twice, or a manager who
|
|
551
|
+
* clicks again months later, gets the same answer rather than an error page — the
|
|
552
|
+
* second call finds the row already unsubscribed and reports success, because from the
|
|
553
|
+
* clicker's point of view it is.
|
|
554
|
+
*/
|
|
555
|
+
async unsubscribeByToken(token, now = new Date()) {
|
|
556
|
+
if (token.length < 16)
|
|
557
|
+
return false;
|
|
558
|
+
return runWithTenantContext({ isSuperAdmin: true }, async () => {
|
|
559
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportRecipient)
|
|
560
|
+
.set({ unsubscribedAt: sql `COALESCE(${schema.reportRecipient.unsubscribedAt}, ${now})` })
|
|
561
|
+
.where(eq(schema.reportRecipient.unsubscribeToken, token))
|
|
562
|
+
.returning({ id: schema.reportRecipient.id }));
|
|
563
|
+
return rows.length > 0;
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
/** Record a delivery bounce. At {@link MAX_BOUNCES} the address is skipped. */
|
|
567
|
+
async recordBounce(orgId, email, now = new Date()) {
|
|
568
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportRecipient)
|
|
569
|
+
.set({ bounceCount: sql `${schema.reportRecipient.bounceCount} + 1`, lastBounceAt: now })
|
|
570
|
+
.where(and(eq(schema.reportRecipient.orgId, orgId), eq(schema.reportRecipient.email, email.trim().toLowerCase())))
|
|
571
|
+
.returning({ bounceCount: schema.reportRecipient.bounceCount }));
|
|
572
|
+
return rows[0]?.bounceCount ?? 0;
|
|
573
|
+
}
|
|
574
|
+
async deleteRecipient(orgId, id, deletedBy) {
|
|
575
|
+
const now = new Date();
|
|
576
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportRecipient)
|
|
577
|
+
.set({ deletedAt: now, deletedBy, ...purgeStamp(now) })
|
|
578
|
+
.where(and(eq(schema.reportRecipient.id, id), eq(schema.reportRecipient.orgId, orgId), isNull(schema.reportRecipient.deletedAt)))
|
|
579
|
+
.returning({ id: schema.reportRecipient.id }));
|
|
580
|
+
if (rows.length === 0) {
|
|
581
|
+
throw new NotFoundError('Recipient not found');
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Is this address deliverable right now? The single place the delivery rules
|
|
586
|
+
* live, so the scheduler and the UI agree about why a recipient is being
|
|
587
|
+
* skipped.
|
|
588
|
+
*/
|
|
589
|
+
deliverability(recipient) {
|
|
590
|
+
if (recipient.deletedAt)
|
|
591
|
+
return { deliverable: false, reason: 'removed' };
|
|
592
|
+
if (recipient.unsubscribedAt)
|
|
593
|
+
return { deliverable: false, reason: 'unsubscribed' };
|
|
594
|
+
if (!recipient.verifiedAt)
|
|
595
|
+
return { deliverable: false, reason: 'pending_verification' };
|
|
596
|
+
if (recipient.bounceCount >= MAX_BOUNCES)
|
|
597
|
+
return { deliverable: false, reason: 'bouncing' };
|
|
598
|
+
return { deliverable: true };
|
|
599
|
+
}
|
|
600
|
+
// ── Recipient policy ───────────────────────────────────────────────────────
|
|
601
|
+
/**
|
|
602
|
+
* The org's admin-owned report policy. Read through the reporting settings row,
|
|
603
|
+
* which may not exist — an org that has never configured anything gets the
|
|
604
|
+
* closed defaults (no external sharing, no external domains, approval required).
|
|
605
|
+
*/
|
|
606
|
+
async getReportPolicy(orgId) {
|
|
607
|
+
const rows = await withTenantTx((tx) => tx.select({
|
|
608
|
+
externalSharing: schema.doraSettings.reportExternalSharing,
|
|
609
|
+
recipientDomains: schema.doraSettings.reportRecipientDomains,
|
|
610
|
+
requireApproval: schema.doraSettings.reportRequireApproval,
|
|
611
|
+
defaultTimezone: schema.doraSettings.reportDefaultTimezone,
|
|
612
|
+
defaultWeekStart: schema.doraSettings.reportDefaultWeekStart,
|
|
613
|
+
}).from(schema.doraSettings).where(eq(schema.doraSettings.orgId, orgId)).limit(1));
|
|
614
|
+
const row = rows[0];
|
|
615
|
+
return {
|
|
616
|
+
externalSharing: row?.externalSharing ?? false,
|
|
617
|
+
recipientDomains: row?.recipientDomains ?? null,
|
|
618
|
+
requireApproval: row?.requireApproval ?? true,
|
|
619
|
+
// NULL rather than a guessed zone: the create form falls back to the BROWSER's zone,
|
|
620
|
+
// which is a better default than the server's and is visibly the lead's own.
|
|
621
|
+
defaultTimezone: row?.defaultTimezone ?? null,
|
|
622
|
+
defaultWeekStart: row?.defaultWeekStart ?? null,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
async setReportPolicy(orgId, patch) {
|
|
626
|
+
const set = { updatedAt: new Date() };
|
|
627
|
+
if (patch.externalSharing !== undefined)
|
|
628
|
+
set.reportExternalSharing = patch.externalSharing;
|
|
629
|
+
if (patch.recipientDomains !== undefined)
|
|
630
|
+
set.reportRecipientDomains = patch.recipientDomains;
|
|
631
|
+
if (patch.requireApproval !== undefined)
|
|
632
|
+
set.reportRequireApproval = patch.requireApproval;
|
|
633
|
+
if (patch.defaultTimezone !== undefined)
|
|
634
|
+
set.reportDefaultTimezone = patch.defaultTimezone;
|
|
635
|
+
if (patch.defaultWeekStart !== undefined)
|
|
636
|
+
set.reportDefaultWeekStart = patch.defaultWeekStart;
|
|
637
|
+
await withTenantTx((tx) => tx.insert(schema.doraSettings).values({
|
|
638
|
+
orgId,
|
|
639
|
+
reportExternalSharing: patch.externalSharing ?? false,
|
|
640
|
+
reportRecipientDomains: patch.recipientDomains ?? null,
|
|
641
|
+
reportRequireApproval: patch.requireApproval ?? true,
|
|
642
|
+
reportDefaultTimezone: patch.defaultTimezone ?? null,
|
|
643
|
+
reportDefaultWeekStart: patch.defaultWeekStart ?? null,
|
|
644
|
+
}).onConflictDoUpdate({ target: schema.doraSettings.orgId, set }));
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Apply a Stakeholder Reports entitlement change from billing: pause everything, or
|
|
648
|
+
* resume what the lapse paused.
|
|
649
|
+
*
|
|
650
|
+
* THE WATERMARK AND THE WORK ARE ONE TRANSACTION, and the watermark update is what
|
|
651
|
+
* serializes concurrent pushes. Billing's legs are best-effort and retried, so
|
|
652
|
+
* "lapsed at 10:00" and "renewed at 10:05" can arrive in either order; applying the
|
|
653
|
+
* older one last would leave a lapsed account producing reports, or a paying one
|
|
654
|
+
* paused. The conditional update takes a row lock on `dora_settings`, so a second
|
|
655
|
+
* push waits and then sees the newer watermark — no advisory lock needed.
|
|
656
|
+
*
|
|
657
|
+
* RESUME IS SCOPED TO `entitlement`. It must not un-pause a definition whose OWNER was
|
|
658
|
+
* deactivated or who lost the permission: those are different problems with different
|
|
659
|
+
* fixes, and resuming one by fixing the other is how a report starts running under
|
|
660
|
+
* somebody who left.
|
|
661
|
+
*
|
|
662
|
+
* `nextRunFor` is a callback because the period arithmetic needs each definition's
|
|
663
|
+
* cadence, timezone and week start, and that belongs with the period resolver rather
|
|
664
|
+
* than the store — while the write still has to happen inside this transaction.
|
|
665
|
+
*
|
|
666
|
+
* This leg makes the change IMMEDIATE; it is not what makes it safe. Every scheduled
|
|
667
|
+
* run re-checks the entitlement against platform anyway, so a lost push delays
|
|
668
|
+
* enforcement to the next run rather than defeating it.
|
|
669
|
+
*/
|
|
670
|
+
async syncReportEntitlement(orgId, entitled, opts = {}) {
|
|
671
|
+
const now = opts.now ?? new Date();
|
|
672
|
+
const occurredAt = opts.occurredAt ?? now;
|
|
673
|
+
return withTenantTx(async (tx) => {
|
|
674
|
+
// The claim. A push at or before the last applied change updates zero rows.
|
|
675
|
+
const claimed = await tx.insert(schema.doraSettings)
|
|
676
|
+
.values({ orgId, reportEntitlementSyncedAt: occurredAt })
|
|
677
|
+
.onConflictDoUpdate({
|
|
678
|
+
target: schema.doraSettings.orgId,
|
|
679
|
+
set: { reportEntitlementSyncedAt: occurredAt, updatedAt: now },
|
|
680
|
+
setWhere: sql `${schema.doraSettings.reportEntitlementSyncedAt} IS NULL
|
|
681
|
+
OR ${schema.doraSettings.reportEntitlementSyncedAt} < ${occurredAt}`,
|
|
682
|
+
})
|
|
683
|
+
.returning({ orgId: schema.doraSettings.orgId });
|
|
684
|
+
if (claimed.length === 0) {
|
|
685
|
+
return { skipped: true, paused: [], resumed: [] };
|
|
686
|
+
}
|
|
687
|
+
if (!entitled) {
|
|
688
|
+
const rows = await tx.update(schema.reportDefinition)
|
|
689
|
+
.set({ isActive: false, pausedReason: 'entitlement', nextRunAt: null, updatedAt: now })
|
|
690
|
+
.where(and(eq(schema.reportDefinition.orgId, orgId), eq(schema.reportDefinition.isActive, true), isNull(schema.reportDefinition.deletedAt)))
|
|
691
|
+
.returning();
|
|
692
|
+
return { skipped: false, paused: rows, resumed: [] };
|
|
693
|
+
}
|
|
694
|
+
const paused = await tx.select().from(schema.reportDefinition)
|
|
695
|
+
.where(and(eq(schema.reportDefinition.orgId, orgId), eq(schema.reportDefinition.isActive, false), eq(schema.reportDefinition.pausedReason, 'entitlement'), isNull(schema.reportDefinition.deletedAt)));
|
|
696
|
+
const resumed = [];
|
|
697
|
+
for (const definition of paused) {
|
|
698
|
+
const rows = await tx.update(schema.reportDefinition)
|
|
699
|
+
.set({
|
|
700
|
+
isActive: true,
|
|
701
|
+
pausedReason: null,
|
|
702
|
+
nextRunAt: opts.nextRunFor ? opts.nextRunFor(definition) : null,
|
|
703
|
+
updatedAt: now,
|
|
704
|
+
})
|
|
705
|
+
.where(eq(schema.reportDefinition.id, definition.id))
|
|
706
|
+
.returning();
|
|
707
|
+
const row = rows[0];
|
|
708
|
+
if (row)
|
|
709
|
+
resumed.push(row);
|
|
710
|
+
}
|
|
711
|
+
return { skipped: false, paused: [], resumed };
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
// ── The free preview ───────────────────────────────────────────────────────
|
|
715
|
+
/**
|
|
716
|
+
* Claim the org's ONE free preview, or report that it is already spent.
|
|
717
|
+
*
|
|
718
|
+
* A CONDITIONAL insert-or-update, not read-then-write: two tabs clicking "see a sample"
|
|
719
|
+
* at the same moment must produce one preview, and a check followed by a separate write
|
|
720
|
+
* has a window between them wide enough for both to pass. `WHERE report_preview_used_at
|
|
721
|
+
* IS NULL` on the update, and `onConflictDoUpdate` for the row that may not exist yet,
|
|
722
|
+
* make the claim itself the guard.
|
|
723
|
+
*
|
|
724
|
+
* Returns whether THIS caller won it. A loser is told the preview is spent, which is
|
|
725
|
+
* the truth and is also the upsell.
|
|
726
|
+
*/
|
|
727
|
+
async claimReportPreview(orgId, now = new Date()) {
|
|
728
|
+
const rows = await withTenantTx((tx) => tx.insert(schema.doraSettings)
|
|
729
|
+
.values({ orgId, reportPreviewUsedAt: now })
|
|
730
|
+
.onConflictDoUpdate({
|
|
731
|
+
target: schema.doraSettings.orgId,
|
|
732
|
+
set: { reportPreviewUsedAt: now, updatedAt: now },
|
|
733
|
+
// The predicate is what makes this once-ever: an org whose row already carries a
|
|
734
|
+
// timestamp updates zero rows and returns nothing.
|
|
735
|
+
setWhere: isNull(schema.doraSettings.reportPreviewUsedAt),
|
|
736
|
+
})
|
|
737
|
+
.returning({ orgId: schema.doraSettings.orgId }));
|
|
738
|
+
return rows.length > 0;
|
|
739
|
+
}
|
|
740
|
+
/** Whether the org has already spent its free preview. */
|
|
741
|
+
async reportPreviewUsed(orgId) {
|
|
742
|
+
const rows = await withTenantTx((tx) => tx.select({ at: schema.doraSettings.reportPreviewUsedAt })
|
|
743
|
+
.from(schema.doraSettings)
|
|
744
|
+
.where(eq(schema.doraSettings.orgId, orgId))
|
|
745
|
+
.limit(1));
|
|
746
|
+
return rows[0]?.at != null;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Is `email` admissible as a recipient for this org, and does it need an
|
|
750
|
+
* admin's approval first?
|
|
751
|
+
*
|
|
752
|
+
* `orgMemberEmails` is the set of addresses that already authenticate to this
|
|
753
|
+
* org — they are always admissible and never need verification, because the org
|
|
754
|
+
* has already established they belong to it. Everything else is measured against
|
|
755
|
+
* the allowed-domain list, and a NULL list means "no external addresses at all",
|
|
756
|
+
* not "any address": the closed reading is the one that cannot leak a report by
|
|
757
|
+
* an admin forgetting to configure something.
|
|
758
|
+
*/
|
|
759
|
+
admitRecipient(email, policy, orgMemberEmails) {
|
|
760
|
+
const normalized = email.trim().toLowerCase();
|
|
761
|
+
if (orgMemberEmails.has(normalized))
|
|
762
|
+
return { admitted: true, member: true, needsApproval: false };
|
|
763
|
+
const at = normalized.lastIndexOf('@');
|
|
764
|
+
const domain = at >= 0 ? normalized.slice(at + 1) : '';
|
|
765
|
+
if (!domain)
|
|
766
|
+
return { admitted: false, reason: 'Not a valid email address.' };
|
|
767
|
+
const allowed = policy.recipientDomains;
|
|
768
|
+
if (!allowed || allowed.length === 0) {
|
|
769
|
+
return {
|
|
770
|
+
admitted: false,
|
|
771
|
+
reason: 'This organization only delivers reports to its own members. '
|
|
772
|
+
+ 'An administrator can allow specific external domains in reporting settings.',
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
const ok = allowed.some((d) => {
|
|
776
|
+
const clean = d.trim().toLowerCase().replace(/^@/, '');
|
|
777
|
+
return clean.length > 0 && (domain === clean || domain.endsWith(`.${clean}`));
|
|
778
|
+
});
|
|
779
|
+
if (!ok) {
|
|
780
|
+
return {
|
|
781
|
+
admitted: false,
|
|
782
|
+
reason: `Reports can only be sent to ${allowed.join(', ')} addresses in this organization.`,
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
return { admitted: true, member: false, needsApproval: policy.requireApproval };
|
|
786
|
+
}
|
|
787
|
+
// ── Retention ──────────────────────────────────────────────────────────────
|
|
788
|
+
/**
|
|
789
|
+
* Hard-delete expired tombstones across every org, for the shared soft-delete
|
|
790
|
+
* sweep. Runs inside the sweep's own sysadmin scope.
|
|
791
|
+
*
|
|
792
|
+
* Runs go first: a purged definition with orphaned runs would leave snapshots
|
|
793
|
+
* nothing can reach, and share links are deleted with their runs so a purge
|
|
794
|
+
* cannot leave a live token pointing at a row that no longer exists.
|
|
795
|
+
*/
|
|
796
|
+
purgeableEntities() {
|
|
797
|
+
return [
|
|
798
|
+
{ name: 'report_run', purgeExpired: (now, limit) => this.purgeRuns(now, limit) },
|
|
799
|
+
{ name: 'report_definition', purgeExpired: (now, limit) => this.purgeDefinitions(now, limit) },
|
|
800
|
+
{ name: 'report_recipient', purgeExpired: (now, limit) => this.purgeRecipients(now, limit) },
|
|
801
|
+
];
|
|
802
|
+
}
|
|
803
|
+
async purgeRuns(now, limit = 500) {
|
|
804
|
+
return withTenantTx(async (tx) => {
|
|
805
|
+
const due = await tx.select({ id: schema.reportRun.id }).from(schema.reportRun)
|
|
806
|
+
.where(and(sql `${schema.reportRun.deletedAt} IS NOT NULL`, lte(schema.reportRun.purgeAfter, now)))
|
|
807
|
+
.limit(limit);
|
|
808
|
+
const ids = due.map((r) => r.id);
|
|
809
|
+
if (ids.length === 0)
|
|
810
|
+
return 0;
|
|
811
|
+
await tx.delete(schema.reportShareLink).where(inArray(schema.reportShareLink.runId, ids));
|
|
812
|
+
const gone = await tx.delete(schema.reportRun).where(inArray(schema.reportRun.id, ids))
|
|
813
|
+
.returning({ id: schema.reportRun.id });
|
|
814
|
+
return gone.length;
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
async purgeDefinitions(now, limit = 500) {
|
|
818
|
+
return withTenantTx(async (tx) => {
|
|
819
|
+
const due = await tx.select({ id: schema.reportDefinition.id }).from(schema.reportDefinition)
|
|
820
|
+
.where(and(sql `${schema.reportDefinition.deletedAt} IS NOT NULL`, lte(schema.reportDefinition.purgeAfter, now)))
|
|
821
|
+
.limit(limit);
|
|
822
|
+
const ids = due.map((r) => r.id);
|
|
823
|
+
if (ids.length === 0)
|
|
824
|
+
return 0;
|
|
825
|
+
// The definition's runs (and their links) go with it, tombstoned or not:
|
|
826
|
+
// a snapshot whose definition is gone can never be listed again.
|
|
827
|
+
const runs = await tx.select({ id: schema.reportRun.id }).from(schema.reportRun)
|
|
828
|
+
.where(inArray(schema.reportRun.definitionId, ids));
|
|
829
|
+
const runIds = runs.map((r) => r.id);
|
|
830
|
+
if (runIds.length > 0) {
|
|
831
|
+
await tx.delete(schema.reportShareLink).where(inArray(schema.reportShareLink.runId, runIds));
|
|
832
|
+
await tx.delete(schema.reportRun).where(inArray(schema.reportRun.id, runIds));
|
|
833
|
+
}
|
|
834
|
+
const gone = await tx.delete(schema.reportDefinition).where(inArray(schema.reportDefinition.id, ids))
|
|
835
|
+
.returning({ id: schema.reportDefinition.id });
|
|
836
|
+
return gone.length;
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
async purgeRecipients(now, limit = 500) {
|
|
840
|
+
return withTenantTx(async (tx) => {
|
|
841
|
+
const due = await tx.select({ id: schema.reportRecipient.id }).from(schema.reportRecipient)
|
|
842
|
+
.where(and(sql `${schema.reportRecipient.deletedAt} IS NOT NULL`, lte(schema.reportRecipient.purgeAfter, now)))
|
|
843
|
+
.limit(limit);
|
|
844
|
+
const ids = due.map((r) => r.id);
|
|
845
|
+
if (ids.length === 0)
|
|
846
|
+
return 0;
|
|
847
|
+
const gone = await tx.delete(schema.reportRecipient).where(inArray(schema.reportRecipient.id, ids))
|
|
848
|
+
.returning({ id: schema.reportRecipient.id });
|
|
849
|
+
return gone.length;
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
/** Every definition the owner must be re-checked for, for the pause sweep. */
|
|
853
|
+
async listOwnedDefinitions(orgId, ownerId) {
|
|
854
|
+
const rows = await withTenantTx((tx) => tx.select().from(schema.reportDefinition)
|
|
855
|
+
.where(and(eq(schema.reportDefinition.orgId, orgId), eq(schema.reportDefinition.ownerId, ownerId), isNull(schema.reportDefinition.deletedAt))));
|
|
856
|
+
return rows;
|
|
857
|
+
}
|
|
858
|
+
// ── The scheduler's reads and writes ───────────────────────────────────────
|
|
859
|
+
/**
|
|
860
|
+
* Definitions whose next run is due, across every org.
|
|
861
|
+
*
|
|
862
|
+
* Runs as SYSADMIN, because the scheduler has no tenant: it is looking for work
|
|
863
|
+
* in orgs it has not been told about. This is the one read here that crosses org
|
|
864
|
+
* boundaries, and it is kept to the narrowest possible shape — the rows needed to
|
|
865
|
+
* claim and execute, ordered oldest-due first so a backlog drains in order rather
|
|
866
|
+
* than starving whichever org sorts last by id.
|
|
867
|
+
*
|
|
868
|
+
* `limit` is the per-cycle ceiling: a cycle that tried to drain everything would
|
|
869
|
+
* hold the leader lock for as long as the slowest org's compose takes, times the
|
|
870
|
+
* number of definitions.
|
|
871
|
+
*/
|
|
872
|
+
async dueDefinitions(now, limit) {
|
|
873
|
+
return runWithTenantContext({ isSuperAdmin: true }, async () => {
|
|
874
|
+
const rows = await withTenantTx((tx) => tx.select().from(schema.reportDefinition)
|
|
875
|
+
.where(and(eq(schema.reportDefinition.isActive, true), isNull(schema.reportDefinition.deletedAt), lte(schema.reportDefinition.nextRunAt, now)))
|
|
876
|
+
.orderBy(asc(schema.reportDefinition.nextRunAt))
|
|
877
|
+
.limit(limit));
|
|
878
|
+
return rows;
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Claim a due definition by advancing its `nextRunAt`, but only if it still holds
|
|
883
|
+
* the value the scan saw.
|
|
884
|
+
*
|
|
885
|
+
* THE CONDITIONAL UPDATE IS THE CONCURRENCY GUARD, not the leader lock. The lock
|
|
886
|
+
* stops two replicas sweeping in the same window; it does not stop a sweep that
|
|
887
|
+
* outlives its lock TTL from overlapping the next leader's sweep, and both would
|
|
888
|
+
* then select the same still-due definition. Without the `next_run_at = <seen>`
|
|
889
|
+
* predicate each would advance the schedule and each would compose and DELIVER a
|
|
890
|
+
* report — the same manager gets the same report twice, from two pods, which is
|
|
891
|
+
* the failure everyone notices.
|
|
892
|
+
*
|
|
893
|
+
* Runs as sysadmin for the same reason as the scan, and returns whether the claim
|
|
894
|
+
* won so the caller can simply skip a lost race.
|
|
895
|
+
*/
|
|
896
|
+
async claimDefinition(id, seenNextRunAt, nextRunAt, now = new Date()) {
|
|
897
|
+
return runWithTenantContext({ isSuperAdmin: true }, async () => {
|
|
898
|
+
const claimed = await withTenantTx((tx) => tx.update(schema.reportDefinition)
|
|
899
|
+
.set({ lastRunAt: now, nextRunAt, updatedAt: now })
|
|
900
|
+
.where(and(eq(schema.reportDefinition.id, id), eq(schema.reportDefinition.nextRunAt, seenNextRunAt)))
|
|
901
|
+
.returning({ id: schema.reportDefinition.id }));
|
|
902
|
+
return claimed.length > 0;
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Set (or clear) a definition's `nextRunAt` without claiming it.
|
|
907
|
+
*
|
|
908
|
+
* Used when a definition is created, resumed, or has its cadence/timezone changed
|
|
909
|
+
* — the schedule has to be re-derived from the calendar rather than left at
|
|
910
|
+
* whatever the old cadence produced.
|
|
911
|
+
*/
|
|
912
|
+
async setNextRun(orgId, id, nextRunAt) {
|
|
913
|
+
await withTenantTx((tx) => tx.update(schema.reportDefinition)
|
|
914
|
+
.set({ nextRunAt, updatedAt: new Date() })
|
|
915
|
+
.where(and(eq(schema.reportDefinition.orgId, orgId), eq(schema.reportDefinition.id, id), isNull(schema.reportDefinition.deletedAt))));
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Pause ONE definition with the reason, from the scheduler.
|
|
919
|
+
*
|
|
920
|
+
* Separate from {@link pauseDefinitionsForOwner} because the trigger is different:
|
|
921
|
+
* that one is a membership change affecting every definition a person owns, this
|
|
922
|
+
* one is a per-run recheck that failed. Clears `nextRunAt` so a paused definition
|
|
923
|
+
* stops appearing in the due scan at all, rather than being re-claimed and
|
|
924
|
+
* re-rejected every cycle.
|
|
925
|
+
*
|
|
926
|
+
* Runs as sysadmin: the caller is the scheduler, which has no tenant, and the id
|
|
927
|
+
* came from its own cross-org scan.
|
|
928
|
+
*/
|
|
929
|
+
async pauseDefinition(id, reason) {
|
|
930
|
+
await runWithTenantContext({ isSuperAdmin: true }, async () => {
|
|
931
|
+
await withTenantTx((tx) => tx.update(schema.reportDefinition)
|
|
932
|
+
.set({ isActive: false, pausedReason: reason, nextRunAt: null, updatedAt: new Date() })
|
|
933
|
+
.where(and(eq(schema.reportDefinition.id, id), isNull(schema.reportDefinition.deletedAt))));
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Resume a paused definition, clearing the reason and re-deriving its schedule.
|
|
938
|
+
*
|
|
939
|
+
* `nextRunAt` is supplied by the caller rather than computed here: the period
|
|
940
|
+
* arithmetic needs the definition's timezone and week start, and that belongs with
|
|
941
|
+
* the period resolver, not the store.
|
|
942
|
+
*/
|
|
943
|
+
async resumeDefinition(orgId, id, nextRunAt, actorId) {
|
|
944
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportDefinition)
|
|
945
|
+
.set({ isActive: true, pausedReason: null, nextRunAt, updatedBy: actorId, updatedAt: new Date() })
|
|
946
|
+
.where(and(eq(schema.reportDefinition.orgId, orgId), eq(schema.reportDefinition.id, id), isNull(schema.reportDefinition.deletedAt)))
|
|
947
|
+
.returning());
|
|
948
|
+
const row = rows[0];
|
|
949
|
+
if (!row)
|
|
950
|
+
throw new NotFoundError('Report definition not found');
|
|
951
|
+
return row;
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Pause every ACTIVE definition in an org, with the reason.
|
|
955
|
+
*
|
|
956
|
+
* The entitlement lapse leg: billing tells reporting the add-on is gone, and every
|
|
957
|
+
* definition in the account stops. Returns the rows so the caller can notify.
|
|
958
|
+
*/
|
|
959
|
+
async pauseDefinitionsForOrg(orgId, reason) {
|
|
960
|
+
const rows = await withTenantTx((tx) => tx.update(schema.reportDefinition)
|
|
961
|
+
.set({ isActive: false, pausedReason: reason, nextRunAt: null, updatedAt: new Date() })
|
|
962
|
+
.where(and(eq(schema.reportDefinition.orgId, orgId), eq(schema.reportDefinition.isActive, true), isNull(schema.reportDefinition.deletedAt)))
|
|
963
|
+
.returning());
|
|
964
|
+
return rows;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Every definition in an org paused for `reason`, for the resume leg.
|
|
968
|
+
*
|
|
969
|
+
* Scoped to the reason on purpose: re-subscribing must not un-pause a definition
|
|
970
|
+
* whose owner was deactivated. Those are different problems with different fixes,
|
|
971
|
+
* and resuming one by fixing the other is how a report starts running under a
|
|
972
|
+
* person who left.
|
|
973
|
+
*/
|
|
974
|
+
async definitionsPausedFor(orgId, reason) {
|
|
975
|
+
const rows = await withTenantTx((tx) => tx.select().from(schema.reportDefinition)
|
|
976
|
+
.where(and(eq(schema.reportDefinition.orgId, orgId), eq(schema.reportDefinition.isActive, false), eq(schema.reportDefinition.pausedReason, reason), isNull(schema.reportDefinition.deletedAt))));
|
|
977
|
+
return rows;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
export const stakeholderReportStore = new StakeholderReportStore();
|
|
981
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXBpL3JlcG9ydGluZy9zdGFrZWhvbGRlci9zdG9yZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0JHO0FBRUgsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xFLE9BQU8sRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLFNBQVMsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3JGLE9BQU8sRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzVFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQVk3RCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsWUFBWSxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDbEYsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFOUQsa0ZBQWtGO0FBQ2xGLE1BQU0sQ0FBQyxNQUFNLDJCQUEyQixHQUFHLEVBQUUsQ0FBQztBQUM5Qyw0RUFBNEU7QUFDNUUsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsR0FBRyxDQUFDO0FBQzNDLHFGQUFxRjtBQUNyRixNQUFNLENBQUMsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDO0FBQzNEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsQ0FBQyxDQUFDO0FBRTdCLHlFQUF5RTtBQUN6RSxNQUFNLFVBQVUsU0FBUyxDQUFDLEtBQWE7SUFDckMsT0FBTyxVQUFVLENBQUMsUUFB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LEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFBlcnNpc3RlbmNlIGZvciBzdGFrZWhvbGRlciByZXBvcnRzOiBkZWZpbml0aW9ucywgcnVucywgc2hhcmUgbGlua3MgYW5kXG4gKiByZWNpcGllbnRzLlxuICpcbiAqIEhhbmQtcm9sbGVkIHJhdGhlciB0aGFuIGZvdXIgYENydWRTZXJ2aWNlYCBzdWJjbGFzc2VzIGJlY2F1c2Ugbm9uZSBvZiB0aGVzZSBpc1xuICogYSBnZW5lcmljIGxpc3RhYmxlIGVudGl0eSB3aXRoIGFuIGBpc0RlZmF1bHRgIHNsb3QgYW5kIHByb2plY3Qtc2NvcGVkIGFjY2Vzc1xuICogY29udHJvbC4gV2hhdCB0aGV5IG5lZWQgaW5zdGVhZCBpcyBhIHNtYWxsIG51bWJlciBvZiBleGFjdCBvcGVyYXRpb25zIHdpdGhcbiAqIGludmFyaWFudHMgdGhlIGdlbmVyaWMgYmFzZSBjYW5ub3QgZXhwcmVzczpcbiAqXG4gKiAgLSBBIFJVTiBJUyBBUFBFTkQtT05MWSBvbmNlIHB1Ymxpc2hlZC4gUmVnZW5lcmF0aW5nIGEgcGVyaW9kIGluc2VydHMgdmVyc2lvblxuICogICAgTisxIGFuZCBwb2ludHMgdGhlIG9sZCByb3cgYXQgaXQgKGBzdXBlcnNlZGVkQnlgKTsgbm90aGluZyBvdmVyd3JpdGVzIGFcbiAqICAgIHNuYXBzaG90IGEgbWFuYWdlciBoYXMgYWxyZWFkeSByZWFkLlxuICogIC0gQSBTSEFSRSBMSU5LIElTIFJFU09MVkVEIFdJVEhPVVQgQSBURU5BTlQuIFRoZSByZWFkZXIgaGFzIG5vIGFjY291bnQsIHNvIHRoZVxuICogICAgbG9va3VwIGJ5IHRva2VuIGhhc2ggaXMgdGhlIG9uZSByZWFkIGhlcmUgdGhhdCBkZWxpYmVyYXRlbHkgcnVucyBhcyBzeXNhZG1pblxuICogICAg4oCUIHNlZSB7QGxpbmsgcmVzb2x2ZVNoYXJlTGlua30gZm9yIHdoeSB0aGF0IGlzIHRoZSBuYXJyb3dlc3Qgc2FmZSBzaGFwZS5cbiAqICAtIEEgUkVDSVBJRU5UIElTIFVQU0VSVEVELCBORVZFUiBSRS1DUkVBVEVELiBSZS1hZGRpbmcgYW4gYWRkcmVzcyBtdXN0IGtlZXAgaXRzXG4gKiAgICB2ZXJpZmljYXRpb24gYW5kIHVuc3Vic2NyaWJlIHN0YXRlLCBvciBhbiB1bnN1YnNjcmliZSBjb3VsZCBiZSB1bmRvbmUgYnlcbiAqICAgIGRlbGV0aW5nIGFuZCByZS1hZGRpbmcgdGhlIHJvdy5cbiAqXG4gKiBFdmVyeXRoaW5nIGVsc2UgcnVucyBpbnNpZGUgdGhlIGNhbGxlcidzIGFtYmllbnQgdGVuYW50IGNvbnRleHQgKGVzdGFibGlzaGVkIGJ5XG4gKiBgd2l0aFRlbmFudENvbnRleHQoKWAgYXQgdGhlIHJlcXVlc3QgYm91bmRhcnkpLCBzbyBSTFMgaXMgdGhlIHRlbmFuY3kgZ2F0ZSBhbmRcbiAqIGFuIGBvcmdJZGAgYXJndW1lbnQgaXMgYSByZWR1bmRhbnQgc2Vjb25kIGNoZWNrIHJhdGhlciB0aGFuIHRoZSBvbmx5IG9uZS5cbiAqL1xuXG5pbXBvcnQgeyByYW5kb21VVUlELCBjcmVhdGVIYXNoLCByYW5kb21CeXRlcyB9IGZyb20gJ25vZGU6Y3J5cHRvJztcbmltcG9ydCB7IE5vdEZvdW5kRXJyb3IsIENvbmZsaWN0RXJyb3IsIEVycm9yQ29kZSB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbmltcG9ydCB7IGFuZCwgYXNjLCBkZXNjLCBlcSwgaW5BcnJheSwgaXNOdWxsLCBsdGUsIHNxbCB9IGZyb20gJ2RyaXp6bGUtb3JtJztcbmltcG9ydCB7IHNjaGVtYSB9IGZyb20gJy4uLy4uLy4uL2RhdGFiYXNlL2RyaXp6bGUtc2NoZW1hLmpzJztcbmltcG9ydCB0eXBlIHtcbiAgUmVwb3J0Q2FkZW5jZSxcbiAgUmVwb3J0RGVmaW5pdGlvbixcbiAgUmVwb3J0UGF1c2VSZWFzb24sXG4gIFJlcG9ydFJlY2lwaWVudCxcbiAgUmVwb3J0UnVuLFxuICBSZXBvcnRSdW5TdGF0dXMsXG4gIFJlcG9ydFNjb3BlLFxuICBSZXBvcnRTaGFyZUxpbmssXG4gIFJlcG9ydFRlbXBsYXRlLFxufSBmcm9tICcuLi8uLi8uLi9kYXRhYmFzZS9zY2hlbWEvcmVwb3J0aW5nLXN0YWtlaG9sZGVyLmpzJztcbmltcG9ydCB7IHJ1bldpdGhUZW5hbnRDb250ZXh0LCB3aXRoVGVuYW50VHggfSBmcm9tICcuLi8uLi8uLi9kYXRhYmFzZS90ZW5hbmN5LmpzJztcbmltcG9ydCB7IHNvZnREZWxldGVSZXRlbnRpb25NcyB9IGZyb20gJy4uLy4uL2NydWQtc2VydmljZS5qcyc7XG5cbi8qKiBIb3cgbG9uZyBhIGZyZXNobHkgbWludGVkIHNoYXJlIGxpbmsgbGl2ZXMgd2hlbiB0aGUgY2FsbGVyIG5hbWVzIG5vIHdpbmRvdy4gKi9cbmV4cG9ydCBjb25zdCBERUZBVUxUX1NIQVJFX0xJTktfVFRMX0RBWVMgPSAzMDtcbi8qKiBUaGUgbG9uZ2VzdCBhIHNoYXJlIGxpbmsgbWF5IGxpdmUuIEJleW9uZCB0aGlzIGl0IGlzIGEgcGVybWFuZW50IFVSTC4gKi9cbmV4cG9ydCBjb25zdCBNQVhfU0hBUkVfTElOS19UVExfREFZUyA9IDE4MDtcbi8qKiBBIHJlY2lwaWVudCdzIHZlcmlmaWNhdGlvbiBsaW5rLiBTaG9ydDogaXQgaXMgY2xpY2tlZCB3aXRoaW4gbWludXRlcyBvciBuZXZlci4gKi9cbmV4cG9ydCBjb25zdCBWRVJJRklDQVRJT05fVFRMX01TID0gNyAqIDI0ICogNjAgKiA2MCAqIDEwMDA7XG4vKipcbiAqIEJvdW5jZXMgYmVmb3JlIGFuIGFkZHJlc3MgaXMgdHJlYXRlZCBhcyB1bmRlbGl2ZXJhYmxlIGFuZCBza2lwcGVkLiBUaHJlZSwgbm90XG4gKiBvbmU6IGEgc2luZ2xlIGJvdW5jZSBpcyBvZnRlbiBhbiBvdXQtb2Ytb2ZmaWNlIGF1dG9yZXNwb25kZXIgb3IgYSBmdWxsIG1haWxib3gsXG4gKiBhbmQgZHJvcHBpbmcgYSBtYW5hZ2VyIG9mZiB0aGUgbGlzdCBmb3Igb25lIG9mIHRob3NlIGlzIGEgc2lsZW50IGZhaWx1cmUuXG4gKi9cbmV4cG9ydCBjb25zdCBNQVhfQk9VTkNFUyA9IDM7XG5cbi8qKiBTSEEtMjU2IGhleCBvZiBhIHRva2VuLiBUaGUgb25seSBmb3JtIGFueSB0b2tlbiBpcyBldmVyIHN0b3JlZCBpbi4gKi9cbmV4cG9ydCBmdW5jdGlvbiBoYXNoVG9rZW4odG9rZW46IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBjcmVhdGVIYXNoKCdzaGEyNTYnKS51cGRhdGUodG9rZW4sICd1dGY4JykuZGlnZXN0KCdoZXgnKTtcbn1cblxuLyoqIEEgZnJlc2ggVVJMLXNhZmUgdG9rZW4sIGFuZCBpdHMgaGFzaC4gVGhlIHJhdyB2YWx1ZSBpcyByZXR1cm5lZCBleGFjdGx5IG9uY2UuICovXG5leHBvcnQgZnVuY3Rpb24gbWludFRva2VuKCk6IHsgdG9rZW46IHN0cmluZzsgdG9rZW5IYXNoOiBzdHJpbmcgfSB7XG4gIGNvbnN0IHRva2VuID0gcmFuZG9tQnl0ZXMoMzIpLnRvU3RyaW5nKCdiYXNlNjR1cmwnKTtcbiAgcmV0dXJuIHsgdG9rZW4sIHRva2VuSGFzaDogaGFzaFRva2VuKHRva2VuKSB9O1xufVxuXG4vKiogV2hhdCBjcmVhdGluZyBhIGRlZmluaXRpb24gbmVlZHMuICovXG5leHBvcnQgaW50ZXJmYWNlIENyZWF0ZURlZmluaXRpb25JbnB1dCB7XG4gIG9yZ0lkOiBzdHJpbmc7XG4gIG5hbWU6IHN0cmluZztcbiAgdGVtcGxhdGU6IFJlcG9ydFRlbXBsYXRlO1xuICBzZWN0aW9uczogc3RyaW5nW107XG4gIGNhZGVuY2U6IFJlcG9ydENhZGVuY2U7XG4gIHRpbWV6b25lOiBzdHJpbmc7XG4gIHdlZWtTdGFydDogJ21vbmRheScgfCAnc3VuZGF5JztcbiAgc2NvcGU6IFJlcG9ydFNjb3BlO1xuICByZWNpcGllbnRzOiBzdHJpbmdbXTtcbiAgYXV0b1NlbmQ6IGJvb2xlYW47XG4gIG93bmVySWQ6IHN0cmluZztcbiAgY3JlYXRlZEJ5OiBzdHJpbmc7XG4gIG5leHRSdW5BdD86IERhdGUgfCBudWxsO1xufVxuXG4vKiogVGhlIG11dGFibGUgaGFsZiBvZiBhIGRlZmluaXRpb24uICovXG5leHBvcnQgaW50ZXJmYWNlIFVwZGF0ZURlZmluaXRpb25JbnB1dCB7XG4gIG5hbWU/OiBzdHJpbmc7XG4gIHRlbXBsYXRlPzogUmVwb3J0VGVtcGxhdGU7XG4gIHNlY3Rpb25zPzogc3RyaW5nW107XG4gIGNhZGVuY2U/OiBSZXBvcnRDYWRlbmNlO1xuICB0aW1lem9uZT86IHN0cmluZztcbiAgd2Vla1N0YXJ0PzogJ21vbmRheScgfCAnc3VuZGF5JztcbiAgc2NvcGU/OiBSZXBvcnRTY29wZTtcbiAgcmVjaXBpZW50cz86IHN0cmluZ1tdO1xuICBhdXRvU2VuZD86IGJvb2xlYW47XG4gIGlzQWN0aXZlPzogYm9vbGVhbjtcbiAgcGF1c2VkUmVhc29uPzogUmVwb3J0UGF1c2VSZWFzb24gfCBudWxsO1xuICBuZXh0UnVuQXQ/OiBEYXRlIHwgbnVsbDtcbn1cblxuLyoqIEEgc2hhcmUgbGluayBwbHVzIHRoZSBvbmUtdGltZSB0b2tlbiwgcmV0dXJuZWQgb25seSBmcm9tIHRoZSBtaW50IGNhbGwuICovXG5leHBvcnQgaW50ZXJmYWNlIE1pbnRlZFNoYXJlTGluayB7XG4gIGxpbms6IFJlcG9ydFNoYXJlTGluaztcbiAgLyoqIFNob3duIG9uY2UuIE5ldmVyIHN0b3JlZCwgbmV2ZXIgbG9nZ2VkLCBuZXZlciBpbiBhIHJlc3BvbnNlIGFnYWluLiAqL1xuICB0b2tlbjogc3RyaW5nO1xufVxuXG4vKiogV2hhdCB0aGUgcHVibGljIHJvdXRlIG5lZWRzOiB0aGUgbGluaywgaXRzIHJ1biwgYW5kIHRoZSBvd25pbmcgb3JnLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXNvbHZlZFNoYXJlTGluayB7XG4gIGxpbms6IFJlcG9ydFNoYXJlTGluaztcbiAgcnVuOiBSZXBvcnRSdW47XG59XG5cbi8qKiBUaGUgYHB1cmdlX2FmdGVyYCBzdGFtcCBhIHNvZnQtZGVsZXRlIGNhcnJpZXMsIG9yIGB7fWAgd2hlbiBwdXJnaW5nIGlzIG9mZi4gKi9cbmZ1bmN0aW9uIHB1cmdlU3RhbXAobm93OiBEYXRlKTogeyBwdXJnZUFmdGVyPzogRGF0ZSB9IHtcbiAgY29uc3QgbXMgPSBzb2Z0RGVsZXRlUmV0ZW50aW9uTXMoKTtcbiAgcmV0dXJuIG1zID4gMCA/IHsgcHVyZ2VBZnRlcjogbmV3IERhdGUobm93LmdldFRpbWUoKSArIG1zKSB9IDoge307XG59XG5cbmV4cG9ydCBjbGFzcyBTdGFrZWhvbGRlclJlcG9ydFN0b3JlIHtcbiAgLy8g4pSA4pSAIERlZmluaXRpb25zIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIGFzeW5jIGNyZWF0ZURlZmluaXRpb24oaW5wdXQ6IENyZWF0ZURlZmluaXRpb25JbnB1dCk6IFByb21pc2U8UmVwb3J0RGVmaW5pdGlvbj4ge1xuICAgIGNvbnN0IGlkID0gcmFuZG9tVVVJRCgpO1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5pbnNlcnQoc2NoZW1hLnJlcG9ydERlZmluaXRpb24pLnZhbHVlcyh7XG4gICAgICBpZCxcbiAgICAgIG9yZ0lkOiBpbnB1dC5vcmdJZCxcbiAgICAgIG93bmVySWQ6IGlucHV0Lm93bmVySWQsXG4gICAgICBuYW1lOiBpbnB1dC5uYW1lLFxuICAgICAgdGVtcGxhdGU6IGlucHV0LnRlbXBsYXRlLFxuICAgICAgc2VjdGlvbnM6IGlucHV0LnNlY3Rpb25zLFxuICAgICAgY2FkZW5jZTogaW5wdXQuY2FkZW5jZSxcbiAgICAgIHRpbWV6b25lOiBpbnB1dC50aW1lem9uZSxcbiAgICAgIHdlZWtTdGFydDogaW5wdXQud2Vla1N0YXJ0LFxuICAgICAgc2NvcGU6IGlucHV0LnNjb3BlLFxuICAgICAgcmVjaXBpZW50czogaW5wdXQucmVjaXBpZW50cyxcbiAgICAgIGF1dG9TZW5kOiBpbnB1dC5hdXRvU2VuZCxcbiAgICAgIG5leHRSdW5BdDogaW5wdXQubmV4dFJ1bkF0ID8/IG51bGwsXG4gICAgICBjcmVhdGVkQnk6IGlucHV0LmNyZWF0ZWRCeSxcbiAgICB9KS5yZXR1cm5pbmcoKSk7XG4gICAgcmV0dXJuIChyb3dzIGFzIFJlcG9ydERlZmluaXRpb25bXSlbMF07XG4gIH1cblxuICBhc3luYyBsaXN0RGVmaW5pdGlvbnMob3JnSWQ6IHN0cmluZyk6IFByb21pc2U8UmVwb3J0RGVmaW5pdGlvbltdPiB7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnNlbGVjdCgpLmZyb20oc2NoZW1hLnJlcG9ydERlZmluaXRpb24pXG4gICAgICAud2hlcmUoYW5kKGVxKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLm9yZ0lkLCBvcmdJZCksIGlzTnVsbChzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5kZWxldGVkQXQpKSlcbiAgICAgIC5vcmRlckJ5KGFzYyhzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5uYW1lKSkpO1xuICAgIHJldHVybiByb3dzIGFzIFJlcG9ydERlZmluaXRpb25bXTtcbiAgfVxuXG4gIGFzeW5jIGdldERlZmluaXRpb24ob3JnSWQ6IHN0cmluZywgaWQ6IHN0cmluZyk6IFByb21pc2U8UmVwb3J0RGVmaW5pdGlvbiB8IG51bGw+IHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguc2VsZWN0KCkuZnJvbShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbilcbiAgICAgIC53aGVyZShhbmQoXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLmlkLCBpZCksXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLm9yZ0lkLCBvcmdJZCksXG4gICAgICAgIGlzTnVsbChzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5kZWxldGVkQXQpLFxuICAgICAgKSlcbiAgICAgIC5saW1pdCgxKSk7XG4gICAgcmV0dXJuIChyb3dzIGFzIFJlcG9ydERlZmluaXRpb25bXSlbMF0gPz8gbnVsbDtcbiAgfVxuXG4gIC8qKiBUaHJvd3Mge0BsaW5rIE5vdEZvdW5kRXJyb3J9IHJhdGhlciB0aGFuIHJldHVybmluZyBudWxsLCBmb3Igcm91dGUgYm9kaWVzLiAqL1xuICBhc3luYyByZXF1aXJlRGVmaW5pdGlvbihvcmdJZDogc3RyaW5nLCBpZDogc3RyaW5nKTogUHJvbWlzZTxSZXBvcnREZWZpbml0aW9uPiB7XG4gICAgY29uc3QgZm91bmQgPSBhd2FpdCB0aGlzLmdldERlZmluaXRpb24ob3JnSWQsIGlkKTtcbiAgICBpZiAoIWZvdW5kKSB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcignUmVwb3J0IGRlZmluaXRpb24gbm90IGZvdW5kJyk7XG4gICAgcmV0dXJuIGZvdW5kO1xuICB9XG5cbiAgYXN5bmMgdXBkYXRlRGVmaW5pdGlvbihcbiAgICBvcmdJZDogc3RyaW5nLFxuICAgIGlkOiBzdHJpbmcsXG4gICAgcGF0Y2g6IFVwZGF0ZURlZmluaXRpb25JbnB1dCxcbiAgICB1cGRhdGVkQnk6IHN0cmluZyxcbiAgKTogUHJvbWlzZTxSZXBvcnREZWZpbml0aW9uPiB7XG4gICAgY29uc3Qgc2V0OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHsgdXBkYXRlZEJ5LCB1cGRhdGVkQXQ6IG5ldyBEYXRlKCkgfTtcbiAgICBmb3IgKGNvbnN0IFtrZXksIHZhbHVlXSBvZiBPYmplY3QuZW50cmllcyhwYXRjaCkpIHtcbiAgICAgIGlmICh2YWx1ZSAhPT0gdW5kZWZpbmVkKSBzZXRba2V5XSA9IHZhbHVlO1xuICAgIH1cbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHgudXBkYXRlKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uKS5zZXQoc2V0KVxuICAgICAgLndoZXJlKGFuZChcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uaWQsIGlkKSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24ub3JnSWQsIG9yZ0lkKSxcbiAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLmRlbGV0ZWRBdCksXG4gICAgICApKVxuICAgICAgLnJldHVybmluZygpKTtcbiAgICBjb25zdCB1cGRhdGVkID0gKHJvd3MgYXMgUmVwb3J0RGVmaW5pdGlvbltdKVswXTtcbiAgICBpZiAoIXVwZGF0ZWQpIHRocm93IG5ldyBOb3RGb3VuZEVycm9yKCdSZXBvcnQgZGVmaW5pdGlvbiBub3QgZm91bmQnKTtcbiAgICByZXR1cm4gdXBkYXRlZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBIYW5kIGEgZGVmaW5pdGlvbiB0byBhIG5ldyBvd25lci4gVGhlIG93bmVyIGlzIHdobyBhIHNjaGVkdWxlZCBydW4gaXNcbiAgICogYXV0aG9yaXplZCBBUywgc28gdGhpcyBpcyBhIHByaXZpbGVnZSBtb3ZlLCBub3QgYSBtZXRhZGF0YSBlZGl0IOKAlCB0aGUgcm91dGVcbiAgICogY2hlY2tzIHRoZSBuZXcgb3duZXIgaXMgYSBsaXZlIG1lbWJlciBvZiB0aGUgb3JnIGJlZm9yZSBjYWxsaW5nLlxuICAgKi9cbiAgYXN5bmMgdHJhbnNmZXJPd25lcihvcmdJZDogc3RyaW5nLCBpZDogc3RyaW5nLCBuZXdPd25lcklkOiBzdHJpbmcsIGFjdG9ySWQ6IHN0cmluZyk6IFByb21pc2U8UmVwb3J0RGVmaW5pdGlvbj4ge1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC51cGRhdGUoc2NoZW1hLnJlcG9ydERlZmluaXRpb24pXG4gICAgICAuc2V0KHtcbiAgICAgICAgb3duZXJJZDogbmV3T3duZXJJZCxcbiAgICAgICAgdXBkYXRlZEJ5OiBhY3RvcklkLFxuICAgICAgICB1cGRhdGVkQXQ6IG5ldyBEYXRlKCksXG4gICAgICAgIC8vIEEgZGVmaW5pdGlvbiBwYXVzZWQgYmVjYXVzZSBpdHMgcHJldmlvdXMgb3duZXIgbG9zdCBhY2Nlc3MgaXMgbGl2ZSBhZ2FpblxuICAgICAgICAvLyB1bmRlciBhbiBvd25lciB3aG8gaGFzIGl0LiBUaGUgc2NoZWR1bGVyIHJlLWNoZWNrcyBldmVyeSBydW4gYW55d2F5LCBzb1xuICAgICAgICAvLyB0aGUgd29yc3QgY2FzZSBpcyBvbmUgbW9yZSBwYXVzZWQgcnVuLCBuZXZlciBhIHN0YW5kaW5nIGdyYW50LlxuICAgICAgICBpc0FjdGl2ZTogdHJ1ZSxcbiAgICAgICAgcGF1c2VkUmVhc29uOiBudWxsLFxuICAgICAgfSlcbiAgICAgIC53aGVyZShhbmQoXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLmlkLCBpZCksXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLm9yZ0lkLCBvcmdJZCksXG4gICAgICAgIGlzTnVsbChzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5kZWxldGVkQXQpLFxuICAgICAgKSlcbiAgICAgIC5yZXR1cm5pbmcoKSk7XG4gICAgY29uc3QgdXBkYXRlZCA9IChyb3dzIGFzIFJlcG9ydERlZmluaXRpb25bXSlbMF07XG4gICAgaWYgKCF1cGRhdGVkKSB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcignUmVwb3J0IGRlZmluaXRpb24gbm90IGZvdW5kJyk7XG4gICAgcmV0dXJuIHVwZGF0ZWQ7XG4gIH1cblxuICAvKipcbiAgICogUGF1c2UgZXZlcnkgZGVmaW5pdGlvbiBhIHVzZXIgb3ducywgd2l0aCB0aGUgcmVhc29uIHJlY29yZGVkLiBDYWxsZWQgd2hlbiB0aGVcbiAgICogb3duZXIgaXMgZGVhY3RpdmF0ZWQgb3IgbG9zZXMgYHJlcG9ydHM6YXV0aG9yYCDigJQgYSByZXBvcnQgbXVzdCBzdG9wIHdoZW4gdGhlXG4gICAqIHBlcnNvbiBhY2NvdW50YWJsZSBmb3IgaXQgY2FuIG5vIGxvbmdlciBzZWUgdGhlIGRhdGEgYmVoaW5kIGl0LlxuICAgKlxuICAgKiBSZXR1cm5zIHRoZSBwYXVzZWQgZGVmaW5pdGlvbnMgc28gdGhlIGNhbGxlciBjYW4gbm90aWZ5IGFuIG9yZyBhZG1pbjogYVxuICAgKiBzaWxlbnRseSBzdG9wcGVkIHdlZWtseSByZXBvcnQgaXMgZGlzY292ZXJlZCBieSBhIG1hbmFnZXIgbm90IHJlY2VpdmluZyBpdC5cbiAgICovXG4gIGFzeW5jIHBhdXNlRGVmaW5pdGlvbnNGb3JPd25lcihcbiAgICBvcmdJZDogc3RyaW5nLFxuICAgIG93bmVySWQ6IHN0cmluZyxcbiAgICByZWFzb246IFJlcG9ydFBhdXNlUmVhc29uLFxuICApOiBQcm9taXNlPFJlcG9ydERlZmluaXRpb25bXT4ge1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC51cGRhdGUoc2NoZW1hLnJlcG9ydERlZmluaXRpb24pXG4gICAgICAuc2V0KHsgaXNBY3RpdmU6IGZhbHNlLCBwYXVzZWRSZWFzb246IHJlYXNvbiwgdXBkYXRlZEF0OiBuZXcgRGF0ZSgpIH0pXG4gICAgICAud2hlcmUoYW5kKFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5vcmdJZCwgb3JnSWQpLFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5vd25lcklkLCBvd25lcklkKSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uaXNBY3RpdmUsIHRydWUpLFxuICAgICAgICBpc051bGwoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uZGVsZXRlZEF0KSxcbiAgICAgICkpXG4gICAgICAucmV0dXJuaW5nKCkpO1xuICAgIHJldHVybiByb3dzIGFzIFJlcG9ydERlZmluaXRpb25bXTtcbiAgfVxuXG4gIGFzeW5jIGRlbGV0ZURlZmluaXRpb24ob3JnSWQ6IHN0cmluZywgaWQ6IHN0cmluZywgZGVsZXRlZEJ5OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpO1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC51cGRhdGUoc2NoZW1hLnJlcG9ydERlZmluaXRpb24pXG4gICAgICAuc2V0KHsgZGVsZXRlZEF0OiBub3csIGRlbGV0ZWRCeSwgLi4ucHVyZ2VTdGFtcChub3cpIH0pXG4gICAgICAud2hlcmUoYW5kKFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5pZCwgaWQpLFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5vcmdJZCwgb3JnSWQpLFxuICAgICAgICBpc051bGwoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uZGVsZXRlZEF0KSxcbiAgICAgICkpXG4gICAgICAucmV0dXJuaW5nKHsgaWQ6IHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLmlkIH0pKTtcbiAgICBpZiAoKHJvd3MgYXMgdW5rbm93bltdKS5sZW5ndGggPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBOb3RGb3VuZEVycm9yKCdSZXBvcnQgZGVmaW5pdGlvbiBub3QgZm91bmQnKTtcbiAgICB9XG4gICAgLy8gTGlua3MgdG8gdGhpcyBkZWZpbml0aW9uJ3MgcnVucyBhcmUgcmV2b2tlZCBpbiB0aGUgc2FtZSBicmVhdGguIERlbGV0aW5nIGFcbiAgICAvLyByZXBvcnQgd2hpbGUgYSBwdWJsaWMgVVJMIHRvIGxhc3QgbW9udGgncyBudW1iZXJzIGtlZXBzIHdvcmtpbmcgaXMgdGhlIGtpbmRcbiAgICAvLyBvZiBnYXAgbm9ib2R5IG5vdGljZXMgdW50aWwgaXQgbWF0dGVycy5cbiAgICBhd2FpdCB0aGlzLnJldm9rZUxpbmtzRm9yRGVmaW5pdGlvbihvcmdJZCwgaWQsIGRlbGV0ZWRCeSk7XG4gIH1cblxuICAvLyDilIDilIAgUnVucyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKipcbiAgICogSW5zZXJ0IGEgcnVuIGZvciBhIHBlcmlvZCwgb3IgcmV0dXJuIHRoZSBleGlzdGluZyBvbmUuXG4gICAqXG4gICAqIElkZW1wb3RlbnQgb24gKGRlZmluaXRpb24sIHBlcmlvZFN0YXJ0LCB2ZXJzaW9uKTogYSByZXRyeSwgYSBjYXRjaC11cCBwYXNzXG4gICAqIGFuZCBhIG1hbnVhbCBiYWNrZmlsbCBjYW4gYWxsIGFzayBmb3IgdGhlIHNhbWUgcGVyaW9kLCBhbmQgd2l0aG91dCB0aGlzIGVhY2hcbiAgICogd291bGQgaW5zZXJ0IGEgcm93IGFuZCB0aGUgbGVhZCB3b3VsZCByZXZpZXcgdGhyZWUgY29waWVzLiBgb25Db25mbGljdERvTm90aGluZ2BcbiAgICogcGx1cyBhIHJlLXJlYWQgaXMgZGVsaWJlcmF0ZSDigJQgdGhlIHVuaXF1ZSBpbmRleCBpcyB0aGUgYXJiaXRlciwgc28gdHdvIHBvZHNcbiAgICogcmFjaW5nIHByb2R1Y2Ugb25lIHJvdyB3aGljaGV2ZXIgd2lucy5cbiAgICovXG4gIGFzeW5jIGNyZWF0ZVJ1bihpbnB1dDoge1xuICAgIG9yZ0lkOiBzdHJpbmc7XG4gICAgZGVmaW5pdGlvbklkOiBzdHJpbmc7XG4gICAgcGVyaW9kU3RhcnQ6IERhdGU7XG4gICAgcGVyaW9kRW5kOiBEYXRlO1xuICAgIHBlcmlvZExhYmVsOiBzdHJpbmc7XG4gICAgdmVyc2lvbj86IG51bWJlcjtcbiAgICBzdGF0dXM/OiBSZXBvcnRSdW5TdGF0dXM7XG4gICAgc25hcHNob3Q/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IG51bGw7XG4gICAgZmFpbHVyZVJlYXNvbj86IHN0cmluZyB8IG51bGw7XG4gIH0pOiBQcm9taXNlPHsgcnVuOiBSZXBvcnRSdW47IGNyZWF0ZWQ6IGJvb2xlYW4gfT4ge1xuICAgIGNvbnN0IHZlcnNpb24gPSBpbnB1dC52ZXJzaW9uID8/IDE7XG4gICAgY29uc3QgaWQgPSByYW5kb21VVUlEKCk7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4Lmluc2VydChzY2hlbWEucmVwb3J0UnVuKS52YWx1ZXMoe1xuICAgICAgaWQsXG4gICAgICBvcmdJZDogaW5wdXQub3JnSWQsXG4gICAgICBkZWZpbml0aW9uSWQ6IGlucHV0LmRlZmluaXRpb25JZCxcbiAgICAgIHBlcmlvZFN0YXJ0OiBpbnB1dC5wZXJpb2RTdGFydCxcbiAgICAgIHBlcmlvZEVuZDogaW5wdXQucGVyaW9kRW5kLFxuICAgICAgcGVyaW9kTGFiZWw6IGlucHV0LnBlcmlvZExhYmVsLFxuICAgICAgdmVyc2lvbixcbiAgICAgIHN0YXR1czogaW5wdXQuc3RhdHVzID8/ICdkcmFmdGluZycsXG4gICAgICBzbmFwc2hvdDogaW5wdXQuc25hcHNob3QgPz8gbnVsbCxcbiAgICAgIGZhaWx1cmVSZWFzb246IGlucHV0LmZhaWx1cmVSZWFzb24gPz8gbnVsbCxcbiAgICB9KS5vbkNvbmZsaWN0RG9Ob3RoaW5nKHtcbiAgICAgIHRhcmdldDogW3NjaGVtYS5yZXBvcnRSdW4uZGVmaW5pdGlvbklkLCBzY2hlbWEucmVwb3J0UnVuLnBlcmlvZFN0YXJ0LCBzY2hlbWEucmVwb3J0UnVuLnZlcnNpb25dLFxuICAgIH0pLnJldHVybmluZygpKTtcbiAgICBjb25zdCBpbnNlcnRlZCA9IChyb3dzIGFzIFJlcG9ydFJ1bltdKVswXTtcbiAgICBpZiAoaW5zZXJ0ZWQpIHJldHVybiB7IHJ1bjogaW5zZXJ0ZWQsIGNyZWF0ZWQ6IHRydWUgfTtcblxuICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguc2VsZWN0KCkuZnJvbShzY2hlbWEucmVwb3J0UnVuKVxuICAgICAgLndoZXJlKGFuZChcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJ1bi5kZWZpbml0aW9uSWQsIGlucHV0LmRlZmluaXRpb25JZCksXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnRSdW4ucGVyaW9kU3RhcnQsIGlucHV0LnBlcmlvZFN0YXJ0KSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJ1bi52ZXJzaW9uLCB2ZXJzaW9uKSxcbiAgICAgICkpXG4gICAgICAubGltaXQoMSkpO1xuICAgIGNvbnN0IGZvdW5kID0gKGV4aXN0aW5nIGFzIFJlcG9ydFJ1bltdKVswXTtcbiAgICBpZiAoIWZvdW5kKSB7XG4gICAgICAvLyBUaGUgaW5zZXJ0IHdhcyBza2lwcGVkIGJ5IHRoZSBjb25mbGljdCB0YXJnZXQgYnV0IG5vdGhpbmcgaXMgdGhlcmUgdG9cbiAgICAgIC8vIHJlYWQ6IHRoZSBvbmx5IHdheSBpcyBhIGNvbmN1cnJlbnQgZGVsZXRlIGJldHdlZW4gdGhlIHR3byBzdGF0ZW1lbnRzLlxuICAgICAgdGhyb3cgbmV3IENvbmZsaWN0RXJyb3IoJ1JlcG9ydCBydW4gY291bGQgbm90IGJlIGNyZWF0ZWQnLCBFcnJvckNvZGUuQ09ORkxJQ1QsIHtcbiAgICAgICAgZGVmaW5pdGlvbklkOiBpbnB1dC5kZWZpbml0aW9uSWQsXG4gICAgICAgIHBlcmlvZExhYmVsOiBpbnB1dC5wZXJpb2RMYWJlbCxcbiAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4geyBydW46IGZvdW5kLCBjcmVhdGVkOiBmYWxzZSB9O1xuICB9XG5cbiAgLyoqIFRoZSBuZXh0IHZlcnNpb24gbnVtYmVyIGZvciBhIHBlcmlvZDogYG1heCh2ZXJzaW9uKSArIDFgLiAqL1xuICBhc3luYyBuZXh0VmVyc2lvbihkZWZpbml0aW9uSWQ6IHN0cmluZywgcGVyaW9kU3RhcnQ6IERhdGUpOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5zZWxlY3QoeyB2ZXJzaW9uOiBzY2hlbWEucmVwb3J0UnVuLnZlcnNpb24gfSlcbiAgICAgIC5mcm9tKHNjaGVtYS5yZXBvcnRSdW4pXG4gICAgICAud2hlcmUoYW5kKFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0UnVuLmRlZmluaXRpb25JZCwgZGVmaW5pdGlvbklkKSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJ1bi5wZXJpb2RTdGFydCwgcGVyaW9kU3RhcnQpLFxuICAgICAgKSlcbiAgICAgIC5vcmRlckJ5KGRlc2Moc2NoZW1hLnJlcG9ydFJ1bi52ZXJzaW9uKSlcbiAgICAgIC5saW1pdCgxKSk7XG4gICAgY29uc3QgdG9wID0gKHJvd3MgYXMgQXJyYXk8eyB2ZXJzaW9uOiBudW1iZXIgfT4pWzBdO1xuICAgIHJldHVybiAodG9wPy52ZXJzaW9uID8/IDApICsgMTtcbiAgfVxuXG4gIC8qKiBQb2ludCBhbiBvbGRlciBydW4gYXQgdGhlIHZlcnNpb24gdGhhdCByZXBsYWNlZCBpdC4gKi9cbiAgYXN5bmMgc3VwZXJzZWRlKG9yZ0lkOiBzdHJpbmcsIG9sZFJ1bklkOiBzdHJpbmcsIG5ld1J1bklkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC51cGRhdGUoc2NoZW1hLnJlcG9ydFJ1bilcbiAgICAgIC5zZXQoeyBzdXBlcnNlZGVkQnk6IG5ld1J1bklkLCB1cGRhdGVkQXQ6IG5ldyBEYXRlKCkgfSlcbiAgICAgIC53aGVyZShhbmQoZXEoc2NoZW1hLnJlcG9ydFJ1bi5pZCwgb2xkUnVuSWQpLCBlcShzY2hlbWEucmVwb3J0UnVuLm9yZ0lkLCBvcmdJZCkpKSk7XG4gIH1cblxuICBhc3luYyBsaXN0UnVucyhvcmdJZDogc3RyaW5nLCBkZWZpbml0aW9uSWQ6IHN0cmluZywgbGltaXQgPSA1MCk6IFByb21pc2U8UmVwb3J0UnVuW10+IHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguc2VsZWN0KCkuZnJvbShzY2hlbWEucmVwb3J0UnVuKVxuICAgICAgLndoZXJlKGFuZChcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJ1bi5vcmdJZCwgb3JnSWQpLFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0UnVuLmRlZmluaXRpb25JZCwgZGVmaW5pdGlvbklkKSxcbiAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnRSdW4uZGVsZXRlZEF0KSxcbiAgICAgICkpXG4gICAgICAub3JkZXJCeShkZXNjKHNjaGVtYS5yZXBvcnRSdW4ucGVyaW9kU3RhcnQpLCBkZXNjKHNjaGVtYS5yZXBvcnRSdW4udmVyc2lvbikpXG4gICAgICAubGltaXQobGltaXQpKTtcbiAgICByZXR1cm4gcm93cyBhcyBSZXBvcnRSdW5bXTtcbiAgfVxuXG4gIGFzeW5jIGdldFJ1bihvcmdJZDogc3RyaW5nLCBpZDogc3RyaW5nKTogUHJvbWlzZTxSZXBvcnRSdW4gfCBudWxsPiB7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnNlbGVjdCgpLmZyb20oc2NoZW1hLnJlcG9ydFJ1bilcbiAgICAgIC53aGVyZShhbmQoZXEoc2NoZW1hLnJlcG9ydFJ1bi5pZCwgaWQpLCBlcShzY2hlbWEucmVwb3J0UnVuLm9yZ0lkLCBvcmdJZCksIGlzTnVsbChzY2hlbWEucmVwb3J0UnVuLmRlbGV0ZWRBdCkpKVxuICAgICAgLmxpbWl0KDEpKTtcbiAgICByZXR1cm4gKHJvd3MgYXMgUmVwb3J0UnVuW10pWzBdID8/IG51bGw7XG4gIH1cblxuICBhc3luYyByZXF1aXJlUnVuKG9yZ0lkOiBzdHJpbmcsIGlkOiBzdHJpbmcpOiBQcm9taXNlPFJlcG9ydFJ1bj4ge1xuICAgIGNvbnN0IGZvdW5kID0gYXdhaXQgdGhpcy5nZXRSdW4ob3JnSWQsIGlkKTtcbiAgICBpZiAoIWZvdW5kKSB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcignUmVwb3J0IHJ1biBub3QgZm91bmQnKTtcbiAgICByZXR1cm4gZm91bmQ7XG4gIH1cblxuICAvKiogU3RvcmUgYSBjb21wdXRlZCBzbmFwc2hvdCBhbmQgbW92ZSB0aGUgcnVuIHRvIGByZWFkeV9mb3JfcmV2aWV3YC4gKi9cbiAgYXN5bmMgY29tcGxldGVSdW4oXG4gICAgb3JnSWQ6IHN0cmluZyxcbiAgICBpZDogc3RyaW5nLFxuICAgIHNuYXBzaG90OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICBzdGF0dXM6IFJlcG9ydFJ1blN0YXR1cyA9ICdyZWFkeV9mb3JfcmV2aWV3JyxcbiAgKTogUHJvbWlzZTxSZXBvcnRSdW4+IHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHgudXBkYXRlKHNjaGVtYS5yZXBvcnRSdW4pXG4gICAgICAuc2V0KHsgc25hcHNob3QsIHN0YXR1cywgZmFpbHVyZVJlYXNvbjogbnVsbCwgdXBkYXRlZEF0OiBuZXcgRGF0ZSgpIH0pXG4gICAgICAud2hlcmUoYW5kKGVxKHNjaGVtYS5yZXBvcnRSdW4uaWQsIGlkKSwgZXEoc2NoZW1hLnJlcG9ydFJ1bi5vcmdJZCwgb3JnSWQpLCBpc051bGwoc2NoZW1hLnJlcG9ydFJ1bi5kZWxldGVkQXQpKSlcbiAgICAgIC5yZXR1cm5pbmcoKSk7XG4gICAgY29uc3QgdXBkYXRlZCA9IChyb3dzIGFzIFJlcG9ydFJ1bltdKVswXTtcbiAgICBpZiAoIXVwZGF0ZWQpIHRocm93IG5ldyBOb3RGb3VuZEVycm9yKCdSZXBvcnQgcnVuIG5vdCBmb3VuZCcpO1xuICAgIHJldHVybiB1cGRhdGVkO1xuICB9XG5cbiAgYXN5bmMgZmFpbFJ1bihvcmdJZDogc3RyaW5nLCBpZDogc3RyaW5nLCBmYWlsdXJlUmVhc29uOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC51cGRhdGUoc2NoZW1hLnJlcG9ydFJ1bilcbiAgICAgIC5zZXQoeyBzdGF0dXM6ICdmYWlsZWQnLCBmYWlsdXJlUmVhc29uLCB1cGRhdGVkQXQ6IG5ldyBEYXRlKCkgfSlcbiAgICAgIC53aGVyZShhbmQoZXEoc2NoZW1hLnJlcG9ydFJ1bi5pZCwgaWQpLCBlcShzY2hlbWEucmVwb3J0UnVuLm9yZ0lkLCBvcmdJZCkpKSk7XG4gIH1cblxuICAvKipcbiAgICogRWRpdCB0aGUgbGVhZCdzIG93biB3b3JkcyBvbiBhIHJ1biB0aGF0IGhhcyBOT1QgYmVlbiBwdWJsaXNoZWQuXG4gICAqXG4gICAqIFJlZnVzZWQgYWZ0ZXIgcHVibGlzaDogdGhlIG5vdGVzIGFyZSBwYXJ0IG9mIHdoYXQgdGhlIG1hbmFnZXIgcmVhZCwgYW5kIGFcbiAgICogcmVwb3J0IHdob3NlIG5hcnJhdGl2ZSBjYW4gYmUgZWRpdGVkIGFmdGVyIGRlbGl2ZXJ5IGlzIGEgcmVwb3J0IHdob3NlXG4gICAqIGRlbGl2ZXJlZCB2ZXJzaW9uIGNhbm5vdCBiZSByZWNvbnN0cnVjdGVkLiBSZWdlbmVyYXRpbmcgcHJvZHVjZXMgdmVyc2lvbiBOKzEsXG4gICAqIHdoaWNoIGlzIHRoZSBzdXBwb3J0ZWQgd2F5IHRvIGNvcnJlY3QgYSBwdWJsaXNoZWQgcmVwb3J0LlxuICAgKi9cbiAgYXN5bmMgc2V0UnVuTm90ZXMoXG4gICAgb3JnSWQ6IHN0cmluZyxcbiAgICBpZDogc3RyaW5nLFxuICAgIHBhdGNoOiB7IGxlYWROb3Rlcz86IHN0cmluZzsgYWlEcmFmdD86IHN0cmluZyB9LFxuICApOiBQcm9taXNlPFJlcG9ydFJ1bj4ge1xuICAgIGNvbnN0IHJ1biA9IGF3YWl0IHRoaXMucmVxdWlyZVJ1bihvcmdJZCwgaWQpO1xuICAgIGlmIChydW4uc3RhdHVzID09PSAncHVibGlzaGVkJykge1xuICAgICAgdGhyb3cgbmV3IENvbmZsaWN0RXJyb3IoXG4gICAgICAgICdUaGlzIHJlcG9ydCBoYXMgYWxyZWFkeSBiZWVuIHB1Ymxpc2hlZC4gUmVnZW5lcmF0ZSBpdCB0byBwcm9kdWNlIGEgbmV3IHZlcnNpb24gd2l0aCB5b3VyIGNoYW5nZXMg4oCUICdcbiAgICAgICAgKyAndGhlIHB1Ymxpc2hlZCB2ZXJzaW9uIHN0YXlzIGV4YWN0bHkgYXMgdGhlIHJlY2lwaWVudHMgcmVhZCBpdC4nLFxuICAgICAgICBFcnJvckNvZGUuQ09ORkxJQ1QsXG4gICAgICAgIHsgcnVuSWQ6IGlkLCBwdWJsaXNoZWRBdDogcnVuLnB1Ymxpc2hlZEF0Py50b0lTT1N0cmluZygpIH0sXG4gICAgICApO1xuICAgIH1cbiAgICBjb25zdCBzZXQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0geyB1cGRhdGVkQXQ6IG5ldyBEYXRlKCkgfTtcbiAgICBpZiAocGF0Y2gubGVhZE5vdGVzICE9PSB1bmRlZmluZWQpIHNldC5sZWFkTm90ZXMgPSBwYXRjaC5sZWFkTm90ZXM7XG4gICAgaWYgKHBhdGNoLmFpRHJhZnQgIT09IHVuZGVmaW5lZCkgc2V0LmFpRHJhZnQgPSBwYXRjaC5haURyYWZ0O1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC51cGRhdGUoc2NoZW1hLnJlcG9ydFJ1bikuc2V0KHNldClcbiAgICAgIC53aGVyZShhbmQoZXEoc2NoZW1hLnJlcG9ydFJ1bi5pZCwgaWQpLCBlcShzY2hlbWEucmVwb3J0UnVuLm9yZ0lkLCBvcmdJZCksIGlzTnVsbChzY2hlbWEucmVwb3J0UnVuLmRlbGV0ZWRBdCkpKVxuICAgICAgLnJldHVybmluZygpKTtcbiAgICByZXR1cm4gKHJvd3MgYXMgUmVwb3J0UnVuW10pWzBdO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlY29yZCB3aGF0IGhhcHBlbmVkIHdoZW4gYSBydW4gd2FzIGRlbGl2ZXJlZC5cbiAgICpcbiAgICogRGVsaWJlcmF0ZWx5IE5PVCBwYXJ0IG9mIHtAbGluayBzZXRSdW5Ob3Rlc30sIHdoaWNoIHJlZnVzZXMgYSBwdWJsaXNoZWQgcnVuOiBkZWxpdmVyeVxuICAgKiBoYXBwZW5zIEFUIHB1Ymxpc2gsIHNvIGEgbWV0aG9kIHRoYXQgcmVmdXNlZCBwdWJsaXNoZWQgcnVucyBjb3VsZCBuZXZlciByZWNvcmQgaXQuXG4gICAqIFRoaXMgb25lIHdyaXRlcyBleGFjdGx5IG9uZSBjb2x1bW4gYW5kIG5vdGhpbmcgZWxzZSwgd2hpY2ggaXMgd2h5IGl0IGlzIHNhZmUgdG8gYWxsb3dcbiAgICogYWZ0ZXIgcHVibGlzaCDigJQgaXQgY2Fubm90IGNoYW5nZSBhIG51bWJlciBhIHJlY2lwaWVudCBoYXMgcmVhZC5cbiAgICovXG4gIGFzeW5jIHJlY29yZERlbGl2ZXJ5KG9yZ0lkOiBzdHJpbmcsIGlkOiBzdHJpbmcsIGRlbGl2ZXJ5OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnVwZGF0ZShzY2hlbWEucmVwb3J0UnVuKVxuICAgICAgLnNldCh7IGRlbGl2ZXJ5LCB1cGRhdGVkQXQ6IG5ldyBEYXRlKCkgfSlcbiAgICAgIC53aGVyZShhbmQoXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnRSdW4uaWQsIGlkKSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJ1bi5vcmdJZCwgb3JnSWQpLFxuICAgICAgICBpc051bGwoc2NoZW1hLnJlcG9ydFJ1bi5kZWxldGVkQXQpLFxuICAgICAgKSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFB1Ymxpc2ggYSBydW4uXG4gICAqXG4gICAqIENvbmRpdGlvbmFsIG9uIHRoZSByb3cgc3RpbGwgYmVpbmcgdW5wdWJsaXNoZWQgKGBwdWJsaXNoZWRfYXQgSVMgTlVMTGApLCBzb1xuICAgKiB0d28gY2xpY2tzIOKAlCBvciB0d28gdGFicyDigJQgcHJvZHVjZSBvbmUgcHVibGlzaCBhbmQgb25lIHNldCBvZiBkZWxpdmVyaWVzXG4gICAqIHJhdGhlciB0aGFuIHR3by4gQSBydW4gd2l0aCBubyBzbmFwc2hvdCBjYW5ub3QgYmUgcHVibGlzaGVkOiB0aGVyZSB3b3VsZCBiZVxuICAgKiBub3RoaW5nIGZvciBhIHJlY2lwaWVudCB0byByZWFkLlxuICAgKi9cbiAgYXN5bmMgcHVibGlzaFJ1bihvcmdJZDogc3RyaW5nLCBpZDogc3RyaW5nLCBwdWJsaXNoZWRCeTogc3RyaW5nKTogUHJvbWlzZTx7IHJ1bjogUmVwb3J0UnVuOyBhbHJlYWR5UHVibGlzaGVkOiBib29sZWFuIH0+IHtcbiAgICBjb25zdCBydW4gPSBhd2FpdCB0aGlzLnJlcXVpcmVSdW4ob3JnSWQsIGlkKTtcbiAgICBpZiAocnVuLnN0YXR1cyA9PT0gJ2ZhaWxlZCcgfHwgIXJ1bi5zbmFwc2hvdCkge1xuICAgICAgdGhyb3cgbmV3IENvbmZsaWN0RXJyb3IoXG4gICAgICAgICdUaGlzIHJlcG9ydCBoYXMgbm8gY29tcHV0ZWQgc25hcHNob3QsIHNvIHRoZXJlIGlzIG5vdGhpbmcgdG8gcHVibGlzaC4gUmVnZW5lcmF0ZSBpdCBmaXJzdC4nLFxuICAgICAgICBFcnJvckNvZGUuQ09ORkxJQ1QsXG4gICAgICAgIHsgcnVuSWQ6IGlkLCBzdGF0dXM6IHJ1bi5zdGF0dXMgfSxcbiAgICAgICk7XG4gICAgfVxuICAgIGNvbnN0IG5vdyA9IG5ldyBEYXRlKCk7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnVwZGF0ZShzY2hlbWEucmVwb3J0UnVuKVxuICAgICAgLnNldCh7IHN0YXR1czogJ3B1Ymxpc2hlZCcsIHB1Ymxpc2hlZEJ5LCBwdWJsaXNoZWRBdDogbm93LCB1cGRhdGVkQXQ6IG5vdyB9KVxuICAgICAgLndoZXJlKGFuZChcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJ1bi5pZCwgaWQpLFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0UnVuLm9yZ0lkLCBvcmdJZCksXG4gICAgICAgIGlzTnVsbChzY2hlbWEucmVwb3J0UnVuLnB1Ymxpc2hlZEF0KSxcbiAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnRSdW4uZGVsZXRlZEF0KSxcbiAgICAgICkpXG4gICAgICAucmV0dXJuaW5nKCkpO1xuICAgIGNvbnN0IHB1Ymxpc2hlZCA9IChyb3dzIGFzIFJlcG9ydFJ1bltdKVswXTtcbiAgICBpZiAocHVibGlzaGVkKSByZXR1cm4geyBydW46IHB1Ymxpc2hlZCwgYWxyZWFkeVB1Ymxpc2hlZDogZmFsc2UgfTtcbiAgICAvLyBUaGUgY29uZGl0aW9uYWwgdXBkYXRlIG1hdGNoZWQgbm90aGluZzogc29tZW9uZSBlbHNlIHB1Ymxpc2hlZCBpdCBmaXJzdC5cbiAgICByZXR1cm4geyBydW46IGF3YWl0IHRoaXMucmVxdWlyZVJ1bihvcmdJZCwgaWQpLCBhbHJlYWR5UHVibGlzaGVkOiB0cnVlIH07XG4gIH1cblxuICAvLyDilIDilIAgU2hhcmUgbGlua3Mg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqXG4gICAqIE1pbnQgYSByZWFkLW9ubHkgbGluayB0byBhIHB1Ymxpc2hlZCBydW4uIFRoZSByYXcgdG9rZW4gaXMgcmV0dXJuZWQgaGVyZSBhbmRcbiAgICogbm93aGVyZSBlbHNlIOKAlCBvbmx5IGl0cyBoYXNoIGlzIHN0b3JlZCwgc28gYSBkYXRhYmFzZSBjb3B5IGNhbm5vdCBiZSB0dXJuZWRcbiAgICogYmFjayBpbnRvIHdvcmtpbmcgVVJMcy5cbiAgICovXG4gIGFzeW5jIGNyZWF0ZVNoYXJlTGluayhpbnB1dDoge1xuICAgIG9yZ0lkOiBzdHJpbmc7XG4gICAgcnVuSWQ6IHN0cmluZztcbiAgICBjcmVhdGVkQnk6IHN0cmluZztcbiAgICB0dGxEYXlzPzogbnVtYmVyO1xuICAgIHJlZGFjdE5hbWVzPzogYm9vbGVhbjtcbiAgICBub3c/OiBEYXRlO1xuICB9KTogUHJvbWlzZTxNaW50ZWRTaGFyZUxpbms+IHtcbiAgICBjb25zdCBydW4gPSBhd2FpdCB0aGlzLnJlcXVpcmVSdW4oaW5wdXQub3JnSWQsIGlucHV0LnJ1bklkKTtcbiAgICBpZiAocnVuLnN0YXR1cyAhPT0gJ3B1Ymxpc2hlZCcpIHtcbiAgICAgIHRocm93IG5ldyBDb25mbGljdEVycm9yKFxuICAgICAgICAnT25seSBhIHB1Ymxpc2hlZCByZXBvcnQgY2FuIGJlIHNoYXJlZC4gUHVibGlzaCBpdCBmaXJzdCDigJQgYSBsaW5rIHRvIGEgZHJhZnQgd291bGQgc2hvdyBudW1iZXJzIHRoZSBsZWFkIGhhcyBub3QgcmV2aWV3ZWQuJyxcbiAgICAgICAgRXJyb3JDb2RlLkNPTkZMSUNULFxuICAgICAgICB7IHJ1bklkOiBpbnB1dC5ydW5JZCwgc3RhdHVzOiBydW4uc3RhdHVzIH0sXG4gICAgICApO1xuICAgIH1cbiAgICBjb25zdCBkYXlzID0gTWF0aC5taW4oaW5wdXQudHRsRGF5cyA/PyBERUZBVUxUX1NIQVJFX0xJTktfVFRMX0RBWVMsIE1BWF9TSEFSRV9MSU5LX1RUTF9EQVlTKTtcbiAgICBjb25zdCBub3cgPSBpbnB1dC5ub3cgPz8gbmV3IERhdGUoKTtcbiAgICBjb25zdCB7IHRva2VuLCB0b2tlbkhhc2ggfSA9IG1pbnRUb2tlbigpO1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5pbnNlcnQoc2NoZW1hLnJlcG9ydFNoYXJlTGluaykudmFsdWVzKHtcbiAgICAgIGlkOiByYW5kb21VVUlEKCksXG4gICAgICBvcmdJZDogaW5wdXQub3JnSWQsXG4gICAgICBydW5JZDogaW5wdXQucnVuSWQsXG4gICAgICB0b2tlbkhhc2gsXG4gICAgICBleHBpcmVzQXQ6IG5ldyBEYXRlKG5vdy5nZXRUaW1lKCkgKyBkYXlzICogMjQgKiA2MCAqIDYwICogMTAwMCksXG4gICAgICByZWRhY3ROYW1lczogaW5wdXQucmVkYWN0TmFtZXMgPz8gZmFsc2UsXG4gICAgICBjcmVhdGVkQnk6IGlucHV0LmNyZWF0ZWRCeSxcbiAgICB9KS5yZXR1cm5pbmcoKSk7XG4gICAgcmV0dXJuIHsgbGluazogKHJvd3MgYXMgUmVwb3J0U2hhcmVMaW5rW10pWzBdLCB0b2tlbiB9O1xuICB9XG5cbiAgYXN5bmMgbGlzdFNoYXJlTGlua3Mob3JnSWQ6IHN0cmluZywgcnVuSWQ6IHN0cmluZyk6IFByb21pc2U8UmVwb3J0U2hhcmVMaW5rW10+IHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguc2VsZWN0KCkuZnJvbShzY2hlbWEucmVwb3J0U2hhcmVMaW5rKVxuICAgICAgLndoZXJlKGFuZChlcShzY2hlbWEucmVwb3J0U2hhcmVMaW5rLm9yZ0lkLCBvcmdJZCksIGVxKHNjaGVtYS5yZXBvcnRTaGFyZUxpbmsucnVuSWQsIHJ1bklkKSkpXG4gICAgICAub3JkZXJCeShkZXNjKHNjaGVtYS5yZXBvcnRTaGFyZUxpbmsuY3JlYXRlZEF0KSkpO1xuICAgIHJldHVybiByb3dzIGFzIFJlcG9ydFNoYXJlTGlua1tdO1xuICB9XG5cbiAgYXN5bmMgcmV2b2tlU2hhcmVMaW5rKG9yZ0lkOiBzdHJpbmcsIGlkOiBzdHJpbmcsIHJldm9rZWRCeTogc3RyaW5nKTogUHJvbWlzZTxSZXBvcnRTaGFyZUxpbms+IHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHgudXBkYXRlKHNjaGVtYS5yZXBvcnRTaGFyZUxpbmspXG4gICAgICAuc2V0KHsgcmV2b2tlZEF0OiBuZXcgRGF0ZSgpLCByZXZva2VkQnkgfSlcbiAgICAgIC53aGVyZShhbmQoXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnRTaGFyZUxpbmsuaWQsIGlkKSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFNoYXJlTGluay5vcmdJZCwgb3JnSWQpLFxuICAgICAgICBpc051bGwoc2NoZW1hLnJlcG9ydFNoYXJlTGluay5yZXZva2VkQXQpLFxuICAgICAgKSlcbiAgICAgIC5yZXR1cm5pbmcoKSk7XG4gICAgY29uc3QgcmV2b2tlZCA9IChyb3dzIGFzIFJlcG9ydFNoYXJlTGlua1tdKVswXTtcbiAgICBpZiAoIXJldm9rZWQpIHRocm93IG5ldyBOb3RGb3VuZEVycm9yKCdTaGFyZSBsaW5rIG5vdCBmb3VuZCBvciBhbHJlYWR5IHJldm9rZWQnKTtcbiAgICByZXR1cm4gcmV2b2tlZDtcbiAgfVxuXG4gIC8qKiBSZXZva2UgZXZlcnkgbGl2ZSBsaW5rIHRvIGFueSBydW4gb2YgYSBkZWZpbml0aW9uLiBVc2VkIG9uIGRlbGV0ZS4gKi9cbiAgYXN5bmMgcmV2b2tlTGlua3NGb3JEZWZpbml0aW9uKG9yZ0lkOiBzdHJpbmcsIGRlZmluaXRpb25JZDogc3RyaW5nLCByZXZva2VkQnk6IHN0cmluZyk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgY29uc3QgcnVucyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnNlbGVjdCh7IGlkOiBzY2hlbWEucmVwb3J0UnVuLmlkIH0pLmZyb20oc2NoZW1hLnJlcG9ydFJ1bilcbiAgICAgIC53aGVyZShhbmQoZXEoc2NoZW1hLnJlcG9ydFJ1bi5vcmdJZCwgb3JnSWQpLCBlcShzY2hlbWEucmVwb3J0UnVuLmRlZmluaXRpb25JZCwgZGVmaW5pdGlvbklkKSkpKTtcbiAgICBjb25zdCBydW5JZHMgPSAocnVucyBhcyBBcnJheTx7IGlkOiBzdHJpbmcgfT4pLm1hcCgocikgPT4gci5pZCk7XG4gICAgaWYgKHJ1bklkcy5sZW5ndGggPT09IDApIHJldHVybiAwO1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC51cGRhdGUoc2NoZW1hLnJlcG9ydFNoYXJlTGluaylcbiAgICAgIC5zZXQoeyByZXZva2VkQXQ6IG5ldyBEYXRlKCksIHJldm9rZWRCeSB9KVxuICAgICAgLndoZXJlKGFuZChcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFNoYXJlTGluay5vcmdJZCwgb3JnSWQpLFxuICAgICAgICBpbkFycmF5KHNjaGVtYS5yZXBvcnRTaGFyZUxpbmsucnVuSWQsIHJ1bklkcyksXG4gICAgICAgIGlzTnVsbChzY2hlbWEucmVwb3J0U2hhcmVMaW5rLnJldm9rZWRBdCksXG4gICAgICApKVxuICAgICAgLnJldHVybmluZyh7IGlkOiBzY2hlbWEucmVwb3J0U2hhcmVMaW5rLmlkIH0pKTtcbiAgICByZXR1cm4gKHJvd3MgYXMgdW5rbm93bltdKS5sZW5ndGg7XG4gIH1cblxuICAvKipcbiAgICogUmVzb2x2ZSBhIHNoYXJlIHRva2VuIHRvIGl0cyBsaW5rIGFuZCBydW4sIGZvciB0aGUgVU5BVVRIRU5USUNBVEVEIHB1YmxpY1xuICAgKiByb3V0ZS5cbiAgICpcbiAgICogVGhpcyBpcyB0aGUgb25lIHJlYWQgaW4gdGhpcyBtb2R1bGUgdGhhdCBydW5zIGFzIHN5c2FkbWluLCBhbmQgdGhlIHJlYXNvbiBpc1xuICAgKiBzdHJ1Y3R1cmFsOiB0aGUgcmVhZGVyIGhvbGRzIGEgdG9rZW4gYW5kIG5vdGhpbmcgZWxzZSDigJQgbm8gYWNjb3VudCwgbm8gb3JnLFxuICAgKiBubyBKV1Qg4oCUIHNvIHRoZXJlIGlzIG5vIHRlbmFudCBjb250ZXh0IGZvciBSTFMgdG8gc2NvcGUgYnksIGFuZCB0aGUgb3JnIGNhblxuICAgKiBvbmx5IGJlIGxlYXJuZWQgRlJPTSB0aGUgcm93LiBUaGUgZXNjYWxhdGlvbiBpcyBrZXB0IGFzIG5hcnJvdyBhcyB0aGUgcHJvYmxlbTpcbiAgICpcbiAgICogIC0gVGhlIHN5c2FkbWluIHNjb3BlIGNvdmVycyB0aGUgdG9rZW4taGFzaCBsb29rdXAgT05MWS4gVGhlIHJ1biBpcyByZWFkIGJhY2tcbiAgICogICAgdW5kZXIgdGhlIGxpbmsncyBPV04gb3JnLCBzbyBhIGJ1ZyBpbiB0aGUgcnVuIHF1ZXJ5IGNhbm5vdCByZWFjaCBhbm90aGVyXG4gICAqICAgIHRlbmFudCdzIHJvd3MuXG4gICAqICAtIFRoZSBsb29rdXAgaXMgYnkgYSAyNTYtYml0IGhhc2ggb24gYSB1bmlxdWUgaW5kZXguIEl0IGlzIG5vdCBhIGZpbHRlciBhXG4gICAqICAgIGNhbGxlciBjYW4gd2lkZW4g4oCUIHRoZXJlIGlzIG5vIG90aGVyIHByZWRpY2F0ZSB0byBpbmZsdWVuY2UuXG4gICAqICAtIEV4cGlyZWQgYW5kIHJldm9rZWQgbGlua3MgcmVzb2x2ZSB0byBudWxsIGhlcmUsIHNvIHRoZSBjYWxsZXIgY2Fubm90XG4gICAqICAgIGFjY2lkZW50YWxseSBzZXJ2ZSBvbmUgYnkgZm9yZ2V0dGluZyBhIGNoZWNrLlxuICAgKlxuICAgKiBSZXR1cm5zIG51bGwgZm9yIGV2ZXJ5IGZhaWx1cmUg4oCUIHVua25vd24sIHJldm9rZWQsIGV4cGlyZWQsIG9yIHBvaW50aW5nIGF0IGFcbiAgICogcnVuIHRoYXQgaXMgbm8gbG9uZ2VyIHB1Ymxpc2hlZCDigJQgc28gdGhlIHJvdXRlIGFuc3dlcnMgb25lIGluZGlzdGluZ3Vpc2hhYmxlXG4gICAqIDQwNCBhbmQgYSBob2xkZXIgb2YgYSBkZWFkIGxpbmsgbGVhcm5zIG5vdGhpbmcgYWJvdXQgd2hpY2ggb2YgdGhvc2UgaXQgd2FzLlxuICAgKi9cbiAgYXN5bmMgcmVzb2x2ZVNoYXJlTGluayh0b2tlbjogc3RyaW5nLCBub3cgPSBuZXcgRGF0ZSgpKTogUHJvbWlzZTxSZXNvbHZlZFNoYXJlTGluayB8IG51bGw+IHtcbiAgICBjb25zdCB0b2tlbkhhc2ggPSBoYXNoVG9rZW4odG9rZW4pO1xuICAgIGNvbnN0IGxpbmtzID0gYXdhaXQgcnVuV2l0aFRlbmFudENvbnRleHQoeyBpc1N1cGVyQWRtaW46IHRydWUgfSwgKCkgPT5cbiAgICAgIHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnNlbGVjdCgpLmZyb20oc2NoZW1hLnJlcG9ydFNoYXJlTGluaylcbiAgICAgICAgLndoZXJlKGVxKHNjaGVtYS5yZXBvcnRTaGFyZUxpbmsudG9rZW5IYXNoLCB0b2tlbkhhc2gpKVxuICAgICAgICAubGltaXQoMSkpKTtcbiAgICBjb25zdCBsaW5rID0gKGxpbmtzIGFzIFJlcG9ydFNoYXJlTGlua1tdKVswXTtcbiAgICAvLyBObyBjb25zdGFudC10aW1lIGNvbXBhcmlzb24gaGVyZSBvbiBwdXJwb3NlOiB0aGUgbG9va3VwIGlzIGFuIGVxdWFsaXR5IG1hdGNoXG4gICAgLy8gb24gYSAyNTYtYml0IGhhc2ggb3ZlciBhIHVuaXF1ZSBpbmRleCwgc28gdGhlcmUgaXMgbm8gc2VjcmV0LWRlcGVuZGVudCBicmFuY2hcbiAgICAvLyBpbiB0aGlzIGZ1bmN0aW9uIHRvIGxlYWsuIEEgaGFuZC1yb2xsZWQgY29tcGFyZSBhZnRlciB0aGUgZGF0YWJhc2UgYWxyZWFkeVxuICAgIC8vIGRlY2lkZWQgaXQgd291bGQgYmUgdGhlYXRyZS5cbiAgICBpZiAoIWxpbmspIHJldHVybiBudWxsO1xuICAgIGlmIChsaW5rLnJldm9rZWRBdCkgcmV0dXJuIG51bGw7XG4gICAgaWYgKGxpbmsuZXhwaXJlc0F0LmdldFRpbWUoKSA8PSBub3cuZ2V0VGltZSgpKSByZXR1cm4gbnVsbDtcblxuICAgIGNvbnN0IHJ1bnMgPSBhd2FpdCBydW5XaXRoVGVuYW50Q29udGV4dCh7IG9yZ0lkOiBsaW5rLm9yZ0lkLCBpc1N1cGVyQWRtaW46IGZhbHNlIH0sICgpID0+XG4gICAgICB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5zZWxlY3QoKS5mcm9tKHNjaGVtYS5yZXBvcnRSdW4pXG4gICAgICAgIC53aGVyZShhbmQoXG4gICAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJ1bi5pZCwgbGluay5ydW5JZCksXG4gICAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJ1bi5vcmdJZCwgbGluay5vcmdJZCksXG4gICAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJ1bi5zdGF0dXMsICdwdWJsaXNoZWQnKSxcbiAgICAgICAgICBpc051bGwoc2NoZW1hLnJlcG9ydFJ1bi5kZWxldGVkQXQpLFxuICAgICAgICApKVxuICAgICAgICAubGltaXQoMSkpKTtcbiAgICBjb25zdCBydW4gPSAocnVucyBhcyBSZXBvcnRSdW5bXSlbMF07XG4gICAgaWYgKCFydW4pIHJldHVybiBudWxsO1xuICAgIHJldHVybiB7IGxpbmssIHJ1biB9O1xuICB9XG5cbiAgLyoqXG4gICAqIENvdW50IG9uZSBodW1hbiB2aWV3IG9mIGEgc2hhcmVkIHJlcG9ydC5cbiAgICpcbiAgICogU2VwYXJhdGUgZnJvbSB7QGxpbmsgcmVzb2x2ZVNoYXJlTGlua30gc28gdGhlIHJvdXRlIGNhbiBzZXJ2ZSBhIGxpbmsgcHJldmlldyxcbiAgICogYSBIRUFEIHByb2JlIG9yIGEgbWFpbCBzY2FubmVyJ3MgZmV0Y2ggV0lUSE9VVCBjb3VudGluZyBpdCDigJQgYSB2aWV3IGNvdW50IHRoYXRcbiAgICoganVtcHMgdGhlIG1vbWVudCBhIGxpbmsgaXMgcGFzdGVkIGludG8gU2xhY2sgdGVsbHMgdGhlIGxlYWQgdGhlaXIgbWFuYWdlciByZWFkXG4gICAqIHRoZSByZXBvcnQgd2hlbiBub2JvZHkgaGFzLlxuICAgKi9cbiAgYXN5bmMgcmVjb3JkU2hhcmVWaWV3KGxpbms6IFJlcG9ydFNoYXJlTGluaywgbm93ID0gbmV3IERhdGUoKSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHJ1bldpdGhUZW5hbnRDb250ZXh0KHsgb3JnSWQ6IGxpbmsub3JnSWQsIGlzU3VwZXJBZG1pbjogZmFsc2UgfSwgKCkgPT5cbiAgICAgIHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnVwZGF0ZShzY2hlbWEucmVwb3J0U2hhcmVMaW5rKVxuICAgICAgICAuc2V0KHsgdmlld0NvdW50OiBzcWxgJHtzY2hlbWEucmVwb3J0U2hhcmVMaW5rLnZpZXdDb3VudH0gKyAxYCwgbGFzdFZpZXdlZEF0OiBub3cgfSlcbiAgICAgICAgLndoZXJlKGVxKHNjaGVtYS5yZXBvcnRTaGFyZUxpbmsuaWQsIGxpbmsuaWQpKSkpO1xuICB9XG5cbiAgLy8g4pSA4pSAIFJlY2lwaWVudHMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqXG4gICAqIEFkZCAob3IgcmUtYWRkKSBhIGRlbGl2ZXJ5IGFkZHJlc3MuXG4gICAqXG4gICAqIFVwc2VydHMgb24gKG9yZywgZW1haWwpIGFuZCBkZWxpYmVyYXRlbHkgZG9lcyBOT1QgcmVzZXQgYHVuc3Vic2NyaWJlZF9hdGAgb3JcbiAgICogYHZlcmlmaWVkX2F0YDogYSBsZWFkIG11c3Qgbm90IGJlIGFibGUgdG8gdW5kbyBzb21lb25lJ3MgdW5zdWJzY3JpYmUgYnlcbiAgICogcmVtb3ZpbmcgYW5kIHJlLWFkZGluZyB0aGVtLCBhbmQgYW4gYWRkcmVzcyBhbHJlYWR5IHZlcmlmaWVkIHNob3VsZCBub3QgaGF2ZVxuICAgKiB0byBjb25maXJtIHR3aWNlLiBBIHZlcmlmaWNhdGlvbiB0b2tlbiBpcyBtaW50ZWQgb25seSB3aGVuIHRoZSBhZGRyZXNzIGlzIG5vdFxuICAgKiB5ZXQgdmVyaWZpZWQuXG4gICAqL1xuICBhc3luYyB1cHNlcnRSZWNpcGllbnQoaW5wdXQ6IHtcbiAgICBvcmdJZDogc3RyaW5nO1xuICAgIGVtYWlsOiBzdHJpbmc7XG4gICAgZGlzcGxheU5hbWU/OiBzdHJpbmc7XG4gICAgY3JlYXRlZEJ5OiBzdHJpbmc7XG4gICAgLyoqIFNldCB3aGVuIGFuIGFkbWluIHByZS1hcHByb3ZlZCBhbiBvdXQtb2YtZG9tYWluIGFkZHJlc3MuICovXG4gICAgYXBwcm92ZWRCeT86IHN0cmluZyB8IG51bGw7XG4gICAgLyoqIE9yZyBtZW1iZXJzIHNraXAgdmVyaWZpY2F0aW9uIOKAlCB0aGV5IGFscmVhZHkgYXV0aGVudGljYXRlZCB0byB0aGlzIG9yZy4gKi9cbiAgICBwcmVWZXJpZmllZD86IGJvb2xlYW47XG4gICAgbm93PzogRGF0ZTtcbiAgfSk6IFByb21pc2U8eyByZWNpcGllbnQ6IFJlcG9ydFJlY2lwaWVudDsgdmVyaWZpY2F0aW9uVG9rZW4/OiBzdHJpbmcgfT4ge1xuICAgIGNvbnN0IGVtYWlsID0gaW5wdXQuZW1haWwudHJpbSgpLnRvTG93ZXJDYXNlKCk7XG4gICAgY29uc3Qgbm93ID0gaW5wdXQubm93ID8/IG5ldyBEYXRlKCk7XG4gICAgY29uc3QgbWludGVkID0gaW5wdXQucHJlVmVyaWZpZWQgPyB1bmRlZmluZWQgOiBtaW50VG9rZW4oKTtcbiAgICAvLyBUaGUgdW5zdWJzY3JpYmUgdG9rZW4gaXMgbWludGVkIGZvciBFVkVSWSByZWNpcGllbnQsIG1lbWJlciBvciBub3QsIGJlY2F1c2UgZXZlcnlcbiAgICAvLyByZXBvcnQgZW1haWwgY2FycmllcyB0aGUgbGluay4gTWludGVkIG9uY2UgYW5kIGtlcHQ6IGEgbGluayBpbiBhIHJlcG9ydCBmcm9tIE1hcmNoXG4gICAgLy8gaGFzIHRvIGtlZXAgd29ya2luZy5cbiAgICBjb25zdCB1bnN1YiA9IG1pbnRUb2tlbigpO1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5pbnNlcnQoc2NoZW1hLnJlcG9ydFJlY2lwaWVudCkudmFsdWVzKHtcbiAgICAgIGlkOiByYW5kb21VVUlEKCksXG4gICAgICBvcmdJZDogaW5wdXQub3JnSWQsXG4gICAgICBlbWFpbCxcbiAgICAgIGRpc3BsYXlOYW1lOiBpbnB1dC5kaXNwbGF5TmFtZSA/PyBudWxsLFxuICAgICAgdmVyaWZpZWRBdDogaW5wdXQucHJlVmVyaWZpZWQgPyBub3cgOiBudWxsLFxuICAgICAgdmVyaWZpY2F0aW9uVG9rZW5IYXNoOiBtaW50ZWQ/LnRva2VuSGFzaCA/PyBudWxsLFxuICAgICAgdW5zdWJzY3JpYmVUb2tlbjogdW5zdWIudG9rZW4sXG4gICAgICBhcHByb3ZlZEJ5OiBpbnB1dC5hcHByb3ZlZEJ5ID8/IG51bGwsXG4gICAgICBjcmVhdGVkQnk6IGlucHV0LmNyZWF0ZWRCeSxcbiAgICB9KS5vbkNvbmZsaWN0RG9VcGRhdGUoe1xuICAgICAgdGFyZ2V0OiBbc2NoZW1hLnJlcG9ydFJlY2lwaWVudC5vcmdJZCwgc2NoZW1hLnJlcG9ydFJlY2lwaWVudC5lbWFpbF0sXG4gICAgICBzZXQ6IHtcbiAgICAgICAgZGlzcGxheU5hbWU6IGlucHV0LmRpc3BsYXlOYW1lID8/IHNxbGAke3NjaGVtYS5yZXBvcnRSZWNpcGllbnQuZGlzcGxheU5hbWV9YCxcbiAgICAgICAgLy8gUmUtYWRkaW5nIGEgdG9tYnN0b25lZCByZWNpcGllbnQgcmV2aXZlcyB0aGUgcm93IHdpdGggaXRzIGhpc3RvcnkuXG4gICAgICAgIGRlbGV0ZWRBdDogbnVsbCxcbiAgICAgICAgZGVsZXRlZEJ5OiBudWxsLFxuICAgICAgICBwdXJnZUFmdGVyOiBudWxsLFxuICAgICAgICBhcHByb3ZlZEJ5OiBpbnB1dC5hcHByb3ZlZEJ5ID8/IHNxbGAke3NjaGVtYS5yZXBvcnRSZWNpcGllbnQuYXBwcm92ZWRCeX1gLFxuICAgICAgICAvLyBPbmx5IG1pbnQgb3ZlciBhbiBVTlZFUklGSUVEIHJvdzsgYSB2ZXJpZmllZCBhZGRyZXNzIGtlZXBzIGl0cyBzdGF0ZS5cbiAgICAgICAgdmVyaWZpY2F0aW9uVG9rZW5IYXNoOiBzcWxgQ0FTRSBXSEVOICR7c2NoZW1hLnJlcG9ydFJlY2lwaWVudC52ZXJpZmllZEF0fSBJUyBOVUxMXG4gICAgICAgICAgVEhFTiAke21pbnRlZD8udG9rZW5IYXNoID8/IG51bGx9IEVMU0UgJHtzY2hlbWEucmVwb3J0UmVjaXBpZW50LnZlcmlmaWNhdGlvblRva2VuSGFzaH0gRU5EYCxcbiAgICAgICAgdmVyaWZpZWRBdDogaW5wdXQucHJlVmVyaWZpZWRcbiAgICAgICAgICA/IHNxbGBDT0FMRVNDRSgke3NjaGVtYS5yZXBvcnRSZWNpcGllbnQudmVyaWZpZWRBdH0sICR7bm93fSlgXG4gICAgICAgICAgOiBzcWxgJHtzY2hlbWEucmVwb3J0UmVjaXBpZW50LnZlcmlmaWVkQXR9YCxcbiAgICAgICAgLy8gQ09BTEVTQ0UsIG5ldmVyIG92ZXJ3cml0ZTogcmUtYWRkaW5nIGFuIGFkZHJlc3MgbXVzdCBub3QgYnJlYWsgdGhlIHVuc3Vic2NyaWJlXG4gICAgICAgIC8vIGxpbmsgaW4gZXZlcnkgcmVwb3J0IGFscmVhZHkgZGVsaXZlcmVkIHRvIGl0LlxuICAgICAgICB1bnN1YnNjcmliZVRva2VuOiBzcWxgQ09BTEVTQ0UoJHtzY2hlbWEucmVwb3J0UmVjaXBpZW50LnVuc3Vic2NyaWJlVG9rZW59LCAke3Vuc3ViLnRva2VufSlgLFxuICAgICAgfSxcbiAgICB9KS5yZXR1cm5pbmcoKSk7XG4gICAgY29uc3QgcmVjaXBpZW50ID0gKHJvd3MgYXMgUmVwb3J0UmVjaXBpZW50W10pWzBdO1xuICAgIC8vIFRoZSB0b2tlbiBpcyBvbmx5IHVzZWZ1bCBpZiB0aGlzIHJvdyBhY3R1YWxseSB0b29rIGl0LlxuICAgIGNvbnN0IHRvb2tUb2tlbiA9IG1pbnRlZCAmJiByZWNpcGllbnQudmVyaWZpY2F0aW9uVG9rZW5IYXNoID09PSBtaW50ZWQudG9rZW5IYXNoO1xuICAgIHJldHVybiB7IHJlY2lwaWVudCwgLi4uKHRvb2tUb2tlbiA/IHsgdmVyaWZpY2F0aW9uVG9rZW46IG1pbnRlZC50b2tlbiB9IDoge30pIH07XG4gIH1cblxuICBhc3luYyBsaXN0UmVjaXBpZW50cyhvcmdJZDogc3RyaW5nKTogUHJvbWlzZTxSZXBvcnRSZWNpcGllbnRbXT4ge1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5zZWxlY3QoKS5mcm9tKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQpXG4gICAgICAud2hlcmUoYW5kKGVxKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQub3JnSWQsIG9yZ0lkKSwgaXNOdWxsKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQuZGVsZXRlZEF0KSkpXG4gICAgICAub3JkZXJCeShhc2Moc2NoZW1hLnJlcG9ydFJlY2lwaWVudC5lbWFpbCkpKTtcbiAgICByZXR1cm4gcm93cyBhcyBSZXBvcnRSZWNpcGllbnRbXTtcbiAgfVxuXG4gIGFzeW5jIGdldFJlY2lwaWVudHMob3JnSWQ6IHN0cmluZywgaWRzOiBzdHJpbmdbXSk6IFByb21pc2U8UmVwb3J0UmVjaXBpZW50W10+IHtcbiAgICBpZiAoaWRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIFtdO1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5zZWxlY3QoKS5mcm9tKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQpXG4gICAgICAud2hlcmUoYW5kKFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0UmVjaXBpZW50Lm9yZ0lkLCBvcmdJZCksXG4gICAgICAgIGluQXJyYXkoc2NoZW1hLnJlcG9ydFJlY2lwaWVudC5pZCwgaWRzKSxcbiAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQuZGVsZXRlZEF0KSxcbiAgICAgICkpKTtcbiAgICByZXR1cm4gcm93cyBhcyBSZXBvcnRSZWNpcGllbnRbXTtcbiAgfVxuXG4gIC8qKiBNaW50IGEgZnJlc2ggdmVyaWZpY2F0aW9uIHRva2VuIGZvciBhIHJlY2lwaWVudCB0aGF0IGhhcyBub3QgY29uZmlybWVkIHlldC4gKi9cbiAgYXN5bmMgcmVzZW5kVmVyaWZpY2F0aW9uKG9yZ0lkOiBzdHJpbmcsIGlkOiBzdHJpbmcpOiBQcm9taXNlPHsgcmVjaXBpZW50OiBSZXBvcnRSZWNpcGllbnQ7IHRva2VuOiBzdHJpbmcgfT4ge1xuICAgIGNvbnN0IHsgdG9rZW4sIHRva2VuSGFzaCB9ID0gbWludFRva2VuKCk7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnVwZGF0ZShzY2hlbWEucmVwb3J0UmVjaXBpZW50KVxuICAgICAgLnNldCh7IHZlcmlmaWNhdGlvblRva2VuSGFzaDogdG9rZW5IYXNoIH0pXG4gICAgICAud2hlcmUoYW5kKFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0UmVjaXBpZW50LmlkLCBpZCksXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQub3JnSWQsIG9yZ0lkKSxcbiAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQudmVyaWZpZWRBdCksXG4gICAgICAgIGlzTnVsbChzY2hlbWEucmVwb3J0UmVjaXBpZW50LmRlbGV0ZWRBdCksXG4gICAgICApKVxuICAgICAgLnJldHVybmluZygpKTtcbiAgICBjb25zdCByZWNpcGllbnQgPSAocm93cyBhcyBSZXBvcnRSZWNpcGllbnRbXSlbMF07XG4gICAgaWYgKCFyZWNpcGllbnQpIHtcbiAgICAgIHRocm93IG5ldyBDb25mbGljdEVycm9yKFxuICAgICAgICAnVGhpcyByZWNpcGllbnQgaXMgYWxyZWFkeSB2ZXJpZmllZCwgb3Igbm8gbG9uZ2VyIGV4aXN0cy4nLFxuICAgICAgICBFcnJvckNvZGUuQ09ORkxJQ1QsXG4gICAgICAgIHsgcmVjaXBpZW50SWQ6IGlkIH0sXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4geyByZWNpcGllbnQsIHRva2VuIH07XG4gIH1cblxuICAvKipcbiAgICogQ29uZmlybSBhbiBhZGRyZXNzIGZyb20gdGhlIGVtYWlsZWQgdG9rZW4uIENyb3NzLW9yZyBieSBuZWNlc3NpdHkgKHRoZSBwZXJzb25cbiAgICogY2xpY2tpbmcgaGFzIG5vIHNlc3Npb24pLCBzbyBpdCBydW5zIGFzIHN5c2FkbWluIG92ZXIgYSB1bmlxdWUgMjU2LWJpdCBoYXNoIOKAlFxuICAgKiB0aGUgc2FtZSBzaGFwZSwgYW5kIHRoZSBzYW1lIHJlYXNvbmluZywgYXMge0BsaW5rIHJlc29sdmVTaGFyZUxpbmt9LlxuICAgKlxuICAgKiBUaGUgdG9rZW4gaXMgQ09OU1VNRUQ6IGl0IGlzIGNsZWFyZWQgb24gc3VjY2Vzcywgc28gYSBmb3J3YXJkZWQgY29uZmlybWF0aW9uXG4gICAqIGxpbmsgY2Fubm90IGJlIHJlcGxheWVkIGJ5IHdob2V2ZXIgdGhlIG1haWwgd2FzIGZvcndhcmRlZCB0by5cbiAgICovXG4gIGFzeW5jIHZlcmlmeVJlY2lwaWVudEJ5VG9rZW4odG9rZW46IHN0cmluZywgbm93ID0gbmV3IERhdGUoKSk6IFByb21pc2U8UmVwb3J0UmVjaXBpZW50IHwgbnVsbD4ge1xuICAgIGNvbnN0IHRva2VuSGFzaCA9IGhhc2hUb2tlbih0b2tlbik7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IHJ1bldpdGhUZW5hbnRDb250ZXh0KHsgaXNTdXBlckFkbWluOiB0cnVlIH0sICgpID0+XG4gICAgICB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC51cGRhdGUoc2NoZW1hLnJlcG9ydFJlY2lwaWVudClcbiAgICAgICAgLnNldCh7IHZlcmlmaWVkQXQ6IG5vdywgdmVyaWZpY2F0aW9uVG9rZW5IYXNoOiBudWxsIH0pXG4gICAgICAgIC53aGVyZShhbmQoXG4gICAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJlY2lwaWVudC52ZXJpZmljYXRpb25Ub2tlbkhhc2gsIHRva2VuSGFzaCksXG4gICAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQudmVyaWZpZWRBdCksXG4gICAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQuZGVsZXRlZEF0KSxcbiAgICAgICAgICAvLyBBIHRva2VuIG9sZGVyIHRoYW4gdGhlIHdpbmRvdyBpcyBub3QgYWNjZXB0ZWQuIGBjcmVhdGVkX2F0YCBpcyB0aGVcbiAgICAgICAgICAvLyBmbG9vciBmb3IgdGhlIGZpcnN0IHRva2VuOyBhIHJlc2VuZCByZWZyZXNoZXMgdGhlIGhhc2gsIHNvIHRoaXMgYm91bmRzXG4gICAgICAgICAgLy8gdGhlIG9yaWdpbmFsIG1pbnQgYW5kIGlzIGRlbGliZXJhdGVseSBnZW5lcm91cy5cbiAgICAgICAgICBzcWxgJHtzY2hlbWEucmVwb3J0UmVjaXBpZW50LmNyZWF0ZWRBdH0gPiAke25ldyBEYXRlKG5vdy5nZXRUaW1lKCkgLSBWRVJJRklDQVRJT05fVFRMX01TKX1gLFxuICAgICAgICApKVxuICAgICAgICAucmV0dXJuaW5nKCkpKTtcbiAgICByZXR1cm4gKHJvd3MgYXMgUmVwb3J0UmVjaXBpZW50W10pWzBdID8/IG51bGw7XG4gIH1cblxuICAvKiogSG9ub3VyIGFuIHVuc3Vic2NyaWJlIGFjcm9zcyBldmVyeSBkZWZpbml0aW9uIGluIHRoZSBvcmcuICovXG4gIGFzeW5jIHVuc3Vic2NyaWJlQnlFbWFpbChvcmdJZDogc3RyaW5nLCBlbWFpbDogc3RyaW5nLCBub3cgPSBuZXcgRGF0ZSgpKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnVwZGF0ZShzY2hlbWEucmVwb3J0UmVjaXBpZW50KVxuICAgICAgLnNldCh7IHVuc3Vic2NyaWJlZEF0OiBub3cgfSlcbiAgICAgIC53aGVyZShhbmQoXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQub3JnSWQsIG9yZ0lkKSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydFJlY2lwaWVudC5lbWFpbCwgZW1haWwudHJpbSgpLnRvTG93ZXJDYXNlKCkpLFxuICAgICAgICBpc051bGwoc2NoZW1hLnJlcG9ydFJlY2lwaWVudC51bnN1YnNjcmliZWRBdCksXG4gICAgICApKVxuICAgICAgLnJldHVybmluZyh7IGlkOiBzY2hlbWEucmVwb3J0UmVjaXBpZW50LmlkIH0pKTtcbiAgICByZXR1cm4gKHJvd3MgYXMgdW5rbm93bltdKS5sZW5ndGggPiAwO1xuICB9XG5cbiAgLyoqXG4gICAqIEhvbm91ciBhbiB1bnN1YnNjcmliZSBmcm9tIHRoZSBsaW5rIGluIGEgcmVwb3J0IGVtYWlsLlxuICAgKlxuICAgKiBMb29rZWQgdXAgYnkgVE9LRU4gYWNyb3NzIG9yZ3MsIGFzIHN5c2FkbWluLCBmb3IgdGhlIHNhbWUgcmVhc29uIHRoZSBzaGFyZS1saW5rXG4gICAqIHJlc29sdmUgaXM6IHRoZSBwZXJzb24gY2xpY2tpbmcgaGFzIG5vIGFjY291bnQgYW5kIG5vIHRlbmFudCwgYW5kIHRoZSB0b2tlbiBpcyB0aGVcbiAgICogb25seSB0aGluZyB0aGF0IGlkZW50aWZpZXMgdGhlIHJvdy4gSXQgaXMgdGhlIG5hcnJvd2VzdCBwb3NzaWJsZSBzaGFwZSDigJQgb25lIGluZGV4ZWRcbiAgICogbG9va3VwLCBvbmUgY29sdW1uIHdyaXR0ZW4sIG5vdGhpbmcgcmV0dXJuZWQgdG8gdGhlIGNhbGxlciBidXQgd2hldGhlciBhIHJvd1xuICAgKiBtYXRjaGVkLlxuICAgKlxuICAgKiBJZGVtcG90ZW50LiBBIG1haWwgY2xpZW50IHRoYXQgc2VuZHMgdGhlIG9uZS1jbGljayBQT1NUIHR3aWNlLCBvciBhIG1hbmFnZXIgd2hvXG4gICAqIGNsaWNrcyBhZ2FpbiBtb250aHMgbGF0ZXIsIGdldHMgdGhlIHNhbWUgYW5zd2VyIHJhdGhlciB0aGFuIGFuIGVycm9yIHBhZ2Ug4oCUIHRoZVxuICAgKiBzZWNvbmQgY2FsbCBmaW5kcyB0aGUgcm93IGFscmVhZHkgdW5zdWJzY3JpYmVkIGFuZCByZXBvcnRzIHN1Y2Nlc3MsIGJlY2F1c2UgZnJvbSB0aGVcbiAgICogY2xpY2tlcidzIHBvaW50IG9mIHZpZXcgaXQgaXMuXG4gICAqL1xuICBhc3luYyB1bnN1YnNjcmliZUJ5VG9rZW4odG9rZW46IHN0cmluZywgbm93ID0gbmV3IERhdGUoKSk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGlmICh0b2tlbi5sZW5ndGggPCAxNikgcmV0dXJuIGZhbHNlO1xuICAgIHJldHVybiBydW5XaXRoVGVuYW50Q29udGV4dCh7IGlzU3VwZXJBZG1pbjogdHJ1ZSB9LCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHgudXBkYXRlKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQpXG4gICAgICAgIC5zZXQoeyB1bnN1YnNjcmliZWRBdDogc3FsYENPQUxFU0NFKCR7c2NoZW1hLnJlcG9ydFJlY2lwaWVudC51bnN1YnNjcmliZWRBdH0sICR7bm93fSlgIH0pXG4gICAgICAgIC53aGVyZShlcShzY2hlbWEucmVwb3J0UmVjaXBpZW50LnVuc3Vic2NyaWJlVG9rZW4sIHRva2VuKSlcbiAgICAgICAgLnJldHVybmluZyh7IGlkOiBzY2hlbWEucmVwb3J0UmVjaXBpZW50LmlkIH0pKTtcbiAgICAgIHJldHVybiAocm93cyBhcyB1bmtub3duW10pLmxlbmd0aCA+IDA7XG4gICAgfSk7XG4gIH1cblxuICAvKiogUmVjb3JkIGEgZGVsaXZlcnkgYm91bmNlLiBBdCB7QGxpbmsgTUFYX0JPVU5DRVN9IHRoZSBhZGRyZXNzIGlzIHNraXBwZWQuICovXG4gIGFzeW5jIHJlY29yZEJvdW5jZShvcmdJZDogc3RyaW5nLCBlbWFpbDogc3RyaW5nLCBub3cgPSBuZXcgRGF0ZSgpKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHgudXBkYXRlKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQpXG4gICAgICAuc2V0KHsgYm91bmNlQ291bnQ6IHNxbGAke3NjaGVtYS5yZXBvcnRSZWNpcGllbnQuYm91bmNlQ291bnR9ICsgMWAsIGxhc3RCb3VuY2VBdDogbm93IH0pXG4gICAgICAud2hlcmUoYW5kKFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0UmVjaXBpZW50Lm9yZ0lkLCBvcmdJZCksXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQuZW1haWwsIGVtYWlsLnRyaW0oKS50b0xvd2VyQ2FzZSgpKSxcbiAgICAgICkpXG4gICAgICAucmV0dXJuaW5nKHsgYm91bmNlQ291bnQ6IHNjaGVtYS5yZXBvcnRSZWNpcGllbnQuYm91bmNlQ291bnQgfSkpO1xuICAgIHJldHVybiAocm93cyBhcyBBcnJheTx7IGJvdW5jZUNvdW50OiBudW1iZXIgfT4pWzBdPy5ib3VuY2VDb3VudCA/PyAwO1xuICB9XG5cbiAgYXN5bmMgZGVsZXRlUmVjaXBpZW50KG9yZ0lkOiBzdHJpbmcsIGlkOiBzdHJpbmcsIGRlbGV0ZWRCeTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKTtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHgudXBkYXRlKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQpXG4gICAgICAuc2V0KHsgZGVsZXRlZEF0OiBub3csIGRlbGV0ZWRCeSwgLi4ucHVyZ2VTdGFtcChub3cpIH0pXG4gICAgICAud2hlcmUoYW5kKFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0UmVjaXBpZW50LmlkLCBpZCksXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQub3JnSWQsIG9yZ0lkKSxcbiAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQuZGVsZXRlZEF0KSxcbiAgICAgICkpXG4gICAgICAucmV0dXJuaW5nKHsgaWQ6IHNjaGVtYS5yZXBvcnRSZWNpcGllbnQuaWQgfSkpO1xuICAgIGlmICgocm93cyBhcyB1bmtub3duW10pLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IE5vdEZvdW5kRXJyb3IoJ1JlY2lwaWVudCBub3QgZm91bmQnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSXMgdGhpcyBhZGRyZXNzIGRlbGl2ZXJhYmxlIHJpZ2h0IG5vdz8gVGhlIHNpbmdsZSBwbGFjZSB0aGUgZGVsaXZlcnkgcnVsZXNcbiAgICogbGl2ZSwgc28gdGhlIHNjaGVkdWxlciBhbmQgdGhlIFVJIGFncmVlIGFib3V0IHdoeSBhIHJlY2lwaWVudCBpcyBiZWluZ1xuICAgKiBza2lwcGVkLlxuICAgKi9cbiAgZGVsaXZlcmFiaWxpdHkocmVjaXBpZW50OiBSZXBvcnRSZWNpcGllbnQpOiB7IGRlbGl2ZXJhYmxlOiBib29sZWFuOyByZWFzb24/OiBzdHJpbmcgfSB7XG4gICAgaWYgKHJlY2lwaWVudC5kZWxldGVkQXQpIHJldHVybiB7IGRlbGl2ZXJhYmxlOiBmYWxzZSwgcmVhc29uOiAncmVtb3ZlZCcgfTtcbiAgICBpZiAocmVjaXBpZW50LnVuc3Vic2NyaWJlZEF0KSByZXR1cm4geyBkZWxpdmVyYWJsZTogZmFsc2UsIHJlYXNvbjogJ3Vuc3Vic2NyaWJlZCcgfTtcbiAgICBpZiAoIXJlY2lwaWVudC52ZXJpZmllZEF0KSByZXR1cm4geyBkZWxpdmVyYWJsZTogZmFsc2UsIHJlYXNvbjogJ3BlbmRpbmdfdmVyaWZpY2F0aW9uJyB9O1xuICAgIGlmIChyZWNpcGllbnQuYm91bmNlQ291bnQgPj0gTUFYX0JPVU5DRVMpIHJldHVybiB7IGRlbGl2ZXJhYmxlOiBmYWxzZSwgcmVhc29uOiAnYm91bmNpbmcnIH07XG4gICAgcmV0dXJuIHsgZGVsaXZlcmFibGU6IHRydWUgfTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBSZWNpcGllbnQgcG9saWN5IOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG4gIC8qKlxuICAgKiBUaGUgb3JnJ3MgYWRtaW4tb3duZWQgcmVwb3J0IHBvbGljeS4gUmVhZCB0aHJvdWdoIHRoZSByZXBvcnRpbmcgc2V0dGluZ3Mgcm93LFxuICAgKiB3aGljaCBtYXkgbm90IGV4aXN0IOKAlCBhbiBvcmcgdGhhdCBoYXMgbmV2ZXIgY29uZmlndXJlZCBhbnl0aGluZyBnZXRzIHRoZVxuICAgKiBjbG9zZWQgZGVmYXVsdHMgKG5vIGV4dGVybmFsIHNoYXJpbmcsIG5vIGV4dGVybmFsIGRvbWFpbnMsIGFwcHJvdmFsIHJlcXVpcmVkKS5cbiAgICovXG4gIGFzeW5jIGdldFJlcG9ydFBvbGljeShvcmdJZDogc3RyaW5nKTogUHJvbWlzZTx7XG4gICAgZXh0ZXJuYWxTaGFyaW5nOiBib29sZWFuO1xuICAgIHJlY2lwaWVudERvbWFpbnM6IHN0cmluZ1tdIHwgbnVsbDtcbiAgICByZXF1aXJlQXBwcm92YWw6IGJvb2xlYW47XG4gICAgZGVmYXVsdFRpbWV6b25lOiBzdHJpbmcgfCBudWxsO1xuICAgIGRlZmF1bHRXZWVrU3RhcnQ6IHN0cmluZyB8IG51bGw7XG4gIH0+IHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguc2VsZWN0KHtcbiAgICAgIGV4dGVybmFsU2hhcmluZzogc2NoZW1hLmRvcmFTZXR0aW5ncy5yZXBvcnRFeHRlcm5hbFNoYXJpbmcsXG4gICAgICByZWNpcGllbnREb21haW5zOiBzY2hlbWEuZG9yYVNldHRpbmdzLnJlcG9ydFJlY2lwaWVudERvbWFpbnMsXG4gICAgICByZXF1aXJlQXBwcm92YWw6IHNjaGVtYS5kb3JhU2V0dGluZ3MucmVwb3J0UmVxdWlyZUFwcHJvdmFsLFxuICAgICAgZGVmYXVsdFRpbWV6b25lOiBzY2hlbWEuZG9yYVNldHRpbmdzLnJlcG9ydERlZmF1bHRUaW1lem9uZSxcbiAgICAgIGRlZmF1bHRXZWVrU3RhcnQ6IHNjaGVtYS5kb3JhU2V0dGluZ3MucmVwb3J0RGVmYXVsdFdlZWtTdGFydCxcbiAgICB9KS5mcm9tKHNjaGVtYS5kb3JhU2V0dGluZ3MpLndoZXJlKGVxKHNjaGVtYS5kb3JhU2V0dGluZ3Mub3JnSWQsIG9yZ0lkKSkubGltaXQoMSkpO1xuICAgIGNvbnN0IHJvdyA9IChyb3dzIGFzIEFycmF5PHtcbiAgICAgIGV4dGVybmFsU2hhcmluZzogYm9vbGVhbjtcbiAgICAgIHJlY2lwaWVudERvbWFpbnM6IHN0cmluZ1tdIHwgbnVsbDtcbiAgICAgIHJlcXVpcmVBcHByb3ZhbDogYm9vbGVhbjtcbiAgICAgIGRlZmF1bHRUaW1lem9uZTogc3RyaW5nIHwgbnVsbDtcbiAgICAgIGRlZmF1bHRXZWVrU3RhcnQ6IHN0cmluZyB8IG51bGw7XG4gICAgfT4pWzBdO1xuICAgIHJldHVybiB7XG4gICAgICBleHRlcm5hbFNoYXJpbmc6IHJvdz8uZXh0ZXJuYWxTaGFyaW5nID8/IGZhbHNlLFxuICAgICAgcmVjaXBpZW50RG9tYWluczogcm93Py5yZWNpcGllbnREb21haW5zID8/IG51bGwsXG4gICAgICByZXF1aXJlQXBwcm92YWw6IHJvdz8ucmVxdWlyZUFwcHJvdmFsID8/IHRydWUsXG4gICAgICAvLyBOVUxMIHJhdGhlciB0aGFuIGEgZ3Vlc3NlZCB6b25lOiB0aGUgY3JlYXRlIGZvcm0gZmFsbHMgYmFjayB0byB0aGUgQlJPV1NFUidzIHpvbmUsXG4gICAgICAvLyB3aGljaCBpcyBhIGJldHRlciBkZWZhdWx0IHRoYW4gdGhlIHNlcnZlcidzIGFuZCBpcyB2aXNpYmx5IHRoZSBsZWFkJ3Mgb3duLlxuICAgICAgZGVmYXVsdFRpbWV6b25lOiByb3c/LmRlZmF1bHRUaW1lem9uZSA/PyBudWxsLFxuICAgICAgZGVmYXVsdFdlZWtTdGFydDogcm93Py5kZWZhdWx0V2Vla1N0YXJ0ID8/IG51bGwsXG4gICAgfTtcbiAgfVxuXG4gIGFzeW5jIHNldFJlcG9ydFBvbGljeShvcmdJZDogc3RyaW5nLCBwYXRjaDoge1xuICAgIGV4dGVybmFsU2hhcmluZz86IGJvb2xlYW47XG4gICAgcmVjaXBpZW50RG9tYWlucz86IHN0cmluZ1tdIHwgbnVsbDtcbiAgICByZXF1aXJlQXBwcm92YWw/OiBib29sZWFuO1xuICAgIGRlZmF1bHRUaW1lem9uZT86IHN0cmluZyB8IG51bGw7XG4gICAgZGVmYXVsdFdlZWtTdGFydD86IHN0cmluZyB8IG51bGw7XG4gIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBzZXQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0geyB1cGRhdGVkQXQ6IG5ldyBEYXRlKCkgfTtcbiAgICBpZiAocGF0Y2guZXh0ZXJuYWxTaGFyaW5nICE9PSB1bmRlZmluZWQpIHNldC5yZXBvcnRFeHRlcm5hbFNoYXJpbmcgPSBwYXRjaC5leHRlcm5hbFNoYXJpbmc7XG4gICAgaWYgKHBhdGNoLnJlY2lwaWVudERvbWFpbnMgIT09IHVuZGVmaW5lZCkgc2V0LnJlcG9ydFJlY2lwaWVudERvbWFpbnMgPSBwYXRjaC5yZWNpcGllbnREb21haW5zO1xuICAgIGlmIChwYXRjaC5yZXF1aXJlQXBwcm92YWwgIT09IHVuZGVmaW5lZCkgc2V0LnJlcG9ydFJlcXVpcmVBcHByb3ZhbCA9IHBhdGNoLnJlcXVpcmVBcHByb3ZhbDtcbiAgICBpZiAocGF0Y2guZGVmYXVsdFRpbWV6b25lICE9PSB1bmRlZmluZWQpIHNldC5yZXBvcnREZWZhdWx0VGltZXpvbmUgPSBwYXRjaC5kZWZhdWx0VGltZXpvbmU7XG4gICAgaWYgKHBhdGNoLmRlZmF1bHRXZWVrU3RhcnQgIT09IHVuZGVmaW5lZCkgc2V0LnJlcG9ydERlZmF1bHRXZWVrU3RhcnQgPSBwYXRjaC5kZWZhdWx0V2Vla1N0YXJ0O1xuICAgIGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4Lmluc2VydChzY2hlbWEuZG9yYVNldHRpbmdzKS52YWx1ZXMoe1xuICAgICAgb3JnSWQsXG4gICAgICByZXBvcnRFeHRlcm5hbFNoYXJpbmc6IHBhdGNoLmV4dGVybmFsU2hhcmluZyA/PyBmYWxzZSxcbiAgICAgIHJlcG9ydFJlY2lwaWVudERvbWFpbnM6IHBhdGNoLnJlY2lwaWVudERvbWFpbnMgPz8gbnVsbCxcbiAgICAgIHJlcG9ydFJlcXVpcmVBcHByb3ZhbDogcGF0Y2gucmVxdWlyZUFwcHJvdmFsID8/IHRydWUsXG4gICAgICByZXBvcnREZWZhdWx0VGltZXpvbmU6IHBhdGNoLmRlZmF1bHRUaW1lem9uZSA/PyBudWxsLFxuICAgICAgcmVwb3J0RGVmYXVsdFdlZWtTdGFydDogcGF0Y2guZGVmYXVsdFdlZWtTdGFydCA/PyBudWxsLFxuICAgIH0pLm9uQ29uZmxpY3REb1VwZGF0ZSh7IHRhcmdldDogc2NoZW1hLmRvcmFTZXR0aW5ncy5vcmdJZCwgc2V0IH0pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBcHBseSBhIFN0YWtlaG9sZGVyIFJlcG9ydHMgZW50aXRsZW1lbnQgY2hhbmdlIGZyb20gYmlsbGluZzogcGF1c2UgZXZlcnl0aGluZywgb3JcbiAgICogcmVzdW1lIHdoYXQgdGhlIGxhcHNlIHBhdXNlZC5cbiAgICpcbiAgICogVEhFIFdBVEVSTUFSSyBBTkQgVEhFIFdPUksgQVJFIE9ORSBUUkFOU0FDVElPTiwgYW5kIHRoZSB3YXRlcm1hcmsgdXBkYXRlIGlzIHdoYXRcbiAgICogc2VyaWFsaXplcyBjb25jdXJyZW50IHB1c2hlcy4gQmlsbGluZydzIGxlZ3MgYXJlIGJlc3QtZWZmb3J0IGFuZCByZXRyaWVkLCBzb1xuICAgKiBcImxhcHNlZCBhdCAxMDowMFwiIGFuZCBcInJlbmV3ZWQgYXQgMTA6MDVcIiBjYW4gYXJyaXZlIGluIGVpdGhlciBvcmRlcjsgYXBwbHlpbmcgdGhlXG4gICAqIG9sZGVyIG9uZSBsYXN0IHdvdWxkIGxlYXZlIGEgbGFwc2VkIGFjY291bnQgcHJvZHVjaW5nIHJlcG9ydHMsIG9yIGEgcGF5aW5nIG9uZVxuICAgKiBwYXVzZWQuIFRoZSBjb25kaXRpb25hbCB1cGRhdGUgdGFrZXMgYSByb3cgbG9jayBvbiBgZG9yYV9zZXR0aW5nc2AsIHNvIGEgc2Vjb25kXG4gICAqIHB1c2ggd2FpdHMgYW5kIHRoZW4gc2VlcyB0aGUgbmV3ZXIgd2F0ZXJtYXJrIOKAlCBubyBhZHZpc29yeSBsb2NrIG5lZWRlZC5cbiAgICpcbiAgICogUkVTVU1FIElTIFNDT1BFRCBUTyBgZW50aXRsZW1lbnRgLiBJdCBtdXN0IG5vdCB1bi1wYXVzZSBhIGRlZmluaXRpb24gd2hvc2UgT1dORVIgd2FzXG4gICAqIGRlYWN0aXZhdGVkIG9yIHdobyBsb3N0IHRoZSBwZXJtaXNzaW9uOiB0aG9zZSBhcmUgZGlmZmVyZW50IHByb2JsZW1zIHdpdGggZGlmZmVyZW50XG4gICAqIGZpeGVzLCBhbmQgcmVzdW1pbmcgb25lIGJ5IGZpeGluZyB0aGUgb3RoZXIgaXMgaG93IGEgcmVwb3J0IHN0YXJ0cyBydW5uaW5nIHVuZGVyXG4gICAqIHNvbWVib2R5IHdobyBsZWZ0LlxuICAgKlxuICAgKiBgbmV4dFJ1bkZvcmAgaXMgYSBjYWxsYmFjayBiZWNhdXNlIHRoZSBwZXJpb2QgYXJpdGhtZXRpYyBuZWVkcyBlYWNoIGRlZmluaXRpb24nc1xuICAgKiBjYWRlbmNlLCB0aW1lem9uZSBhbmQgd2VlayBzdGFydCwgYW5kIHRoYXQgYmVsb25ncyB3aXRoIHRoZSBwZXJpb2QgcmVzb2x2ZXIgcmF0aGVyXG4gICAqIHRoYW4gdGhlIHN0b3JlIOKAlCB3aGlsZSB0aGUgd3JpdGUgc3RpbGwgaGFzIHRvIGhhcHBlbiBpbnNpZGUgdGhpcyB0cmFuc2FjdGlvbi5cbiAgICpcbiAgICogVGhpcyBsZWcgbWFrZXMgdGhlIGNoYW5nZSBJTU1FRElBVEU7IGl0IGlzIG5vdCB3aGF0IG1ha2VzIGl0IHNhZmUuIEV2ZXJ5IHNjaGVkdWxlZFxuICAgKiBydW4gcmUtY2hlY2tzIHRoZSBlbnRpdGxlbWVudCBhZ2FpbnN0IHBsYXRmb3JtIGFueXdheSwgc28gYSBsb3N0IHB1c2ggZGVsYXlzXG4gICAqIGVuZm9yY2VtZW50IHRvIHRoZSBuZXh0IHJ1biByYXRoZXIgdGhhbiBkZWZlYXRpbmcgaXQuXG4gICAqL1xuICBhc3luYyBzeW5jUmVwb3J0RW50aXRsZW1lbnQoXG4gICAgb3JnSWQ6IHN0cmluZyxcbiAgICBlbnRpdGxlZDogYm9vbGVhbixcbiAgICBvcHRzOiB7IG9jY3VycmVkQXQ/OiBEYXRlOyBuZXh0UnVuRm9yPzogKGQ6IFJlcG9ydERlZmluaXRpb24pID0+IERhdGU7IG5vdz86IERhdGUgfSA9IHt9LFxuICApOiBQcm9taXNlPHsgc2tpcHBlZDogYm9vbGVhbjsgcGF1c2VkOiBSZXBvcnREZWZpbml0aW9uW107IHJlc3VtZWQ6IFJlcG9ydERlZmluaXRpb25bXSB9PiB7XG4gICAgY29uc3Qgbm93ID0gb3B0cy5ub3cgPz8gbmV3IERhdGUoKTtcbiAgICBjb25zdCBvY2N1cnJlZEF0ID0gb3B0cy5vY2N1cnJlZEF0ID8/IG5vdztcbiAgICByZXR1cm4gd2l0aFRlbmFudFR4KGFzeW5jICh0eCkgPT4ge1xuICAgICAgLy8gVGhlIGNsYWltLiBBIHB1c2ggYXQgb3IgYmVmb3JlIHRoZSBsYXN0IGFwcGxpZWQgY2hhbmdlIHVwZGF0ZXMgemVybyByb3dzLlxuICAgICAgY29uc3QgY2xhaW1lZCA9IGF3YWl0IHR4Lmluc2VydChzY2hlbWEuZG9yYVNldHRpbmdzKVxuICAgICAgICAudmFsdWVzKHsgb3JnSWQsIHJlcG9ydEVudGl0bGVtZW50U3luY2VkQXQ6IG9jY3VycmVkQXQgfSlcbiAgICAgICAgLm9uQ29uZmxpY3REb1VwZGF0ZSh7XG4gICAgICAgICAgdGFyZ2V0OiBzY2hlbWEuZG9yYVNldHRpbmdzLm9yZ0lkLFxuICAgICAgICAgIHNldDogeyByZXBvcnRFbnRpdGxlbWVudFN5bmNlZEF0OiBvY2N1cnJlZEF0LCB1cGRhdGVkQXQ6IG5vdyB9LFxuICAgICAgICAgIHNldFdoZXJlOiBzcWxgJHtzY2hlbWEuZG9yYVNldHRpbmdzLnJlcG9ydEVudGl0bGVtZW50U3luY2VkQXR9IElTIE5VTExcbiAgICAgICAgICAgIE9SICR7c2NoZW1hLmRvcmFTZXR0aW5ncy5yZXBvcnRFbnRpdGxlbWVudFN5bmNlZEF0fSA8ICR7b2NjdXJyZWRBdH1gLFxuICAgICAgICB9KVxuICAgICAgICAucmV0dXJuaW5nKHsgb3JnSWQ6IHNjaGVtYS5kb3JhU2V0dGluZ3Mub3JnSWQgfSk7XG4gICAgICBpZiAoKGNsYWltZWQgYXMgdW5rbm93bltdKS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIHsgc2tpcHBlZDogdHJ1ZSwgcGF1c2VkOiBbXSwgcmVzdW1lZDogW10gfTtcbiAgICAgIH1cblxuICAgICAgaWYgKCFlbnRpdGxlZCkge1xuICAgICAgICBjb25zdCByb3dzID0gYXdhaXQgdHgudXBkYXRlKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uKVxuICAgICAgICAgIC5zZXQoeyBpc0FjdGl2ZTogZmFsc2UsIHBhdXNlZFJlYXNvbjogJ2VudGl0bGVtZW50JywgbmV4dFJ1bkF0OiBudWxsLCB1cGRhdGVkQXQ6IG5vdyB9KVxuICAgICAgICAgIC53aGVyZShhbmQoXG4gICAgICAgICAgICBlcShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5vcmdJZCwgb3JnSWQpLFxuICAgICAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uaXNBY3RpdmUsIHRydWUpLFxuICAgICAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLmRlbGV0ZWRBdCksXG4gICAgICAgICAgKSlcbiAgICAgICAgICAucmV0dXJuaW5nKCk7XG4gICAgICAgIHJldHVybiB7IHNraXBwZWQ6IGZhbHNlLCBwYXVzZWQ6IHJvd3MgYXMgUmVwb3J0RGVmaW5pdGlvbltdLCByZXN1bWVkOiBbXSB9O1xuICAgICAgfVxuXG4gICAgICBjb25zdCBwYXVzZWQgPSBhd2FpdCB0eC5zZWxlY3QoKS5mcm9tKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uKVxuICAgICAgICAud2hlcmUoYW5kKFxuICAgICAgICAgIGVxKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLm9yZ0lkLCBvcmdJZCksXG4gICAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uaXNBY3RpdmUsIGZhbHNlKSxcbiAgICAgICAgICBlcShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5wYXVzZWRSZWFzb24sICdlbnRpdGxlbWVudCcpLFxuICAgICAgICAgIGlzTnVsbChzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5kZWxldGVkQXQpLFxuICAgICAgICApKSBhcyBSZXBvcnREZWZpbml0aW9uW107XG4gICAgICBjb25zdCByZXN1bWVkOiBSZXBvcnREZWZpbml0aW9uW10gPSBbXTtcbiAgICAgIGZvciAoY29uc3QgZGVmaW5pdGlvbiBvZiBwYXVzZWQpIHtcbiAgICAgICAgY29uc3Qgcm93cyA9IGF3YWl0IHR4LnVwZGF0ZShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbilcbiAgICAgICAgICAuc2V0KHtcbiAgICAgICAgICAgIGlzQWN0aXZlOiB0cnVlLFxuICAgICAgICAgICAgcGF1c2VkUmVhc29uOiBudWxsLFxuICAgICAgICAgICAgbmV4dFJ1bkF0OiBvcHRzLm5leHRSdW5Gb3IgPyBvcHRzLm5leHRSdW5Gb3IoZGVmaW5pdGlvbikgOiBudWxsLFxuICAgICAgICAgICAgdXBkYXRlZEF0OiBub3csXG4gICAgICAgICAgfSlcbiAgICAgICAgICAud2hlcmUoZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uaWQsIGRlZmluaXRpb24uaWQpKVxuICAgICAgICAgIC5yZXR1cm5pbmcoKTtcbiAgICAgICAgY29uc3Qgcm93ID0gKHJvd3MgYXMgUmVwb3J0RGVmaW5pdGlvbltdKVswXTtcbiAgICAgICAgaWYgKHJvdykgcmVzdW1lZC5wdXNoKHJvdyk7XG4gICAgICB9XG4gICAgICByZXR1cm4geyBza2lwcGVkOiBmYWxzZSwgcGF1c2VkOiBbXSwgcmVzdW1lZCB9O1xuICAgIH0pO1xuICB9XG5cbiAgLy8g4pSA4pSAIFRoZSBmcmVlIHByZXZpZXcg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqXG4gICAqIENsYWltIHRoZSBvcmcncyBPTkUgZnJlZSBwcmV2aWV3LCBvciByZXBvcnQgdGhhdCBpdCBpcyBhbHJlYWR5IHNwZW50LlxuICAgKlxuICAgKiBBIENPTkRJVElPTkFMIGluc2VydC1vci11cGRhdGUsIG5vdCByZWFkLXRoZW4td3JpdGU6IHR3byB0YWJzIGNsaWNraW5nIFwic2VlIGEgc2FtcGxlXCJcbiAgICogYXQgdGhlIHNhbWUgbW9tZW50IG11c3QgcHJvZHVjZSBvbmUgcHJldmlldywgYW5kIGEgY2hlY2sgZm9sbG93ZWQgYnkgYSBzZXBhcmF0ZSB3cml0ZVxuICAgKiBoYXMgYSB3aW5kb3cgYmV0d2VlbiB0aGVtIHdpZGUgZW5vdWdoIGZvciBib3RoIHRvIHBhc3MuIGBXSEVSRSByZXBvcnRfcHJldmlld191c2VkX2F0XG4gICAqIElTIE5VTExgIG9uIHRoZSB1cGRhdGUsIGFuZCBgb25Db25mbGljdERvVXBkYXRlYCBmb3IgdGhlIHJvdyB0aGF0IG1heSBub3QgZXhpc3QgeWV0LFxuICAgKiBtYWtlIHRoZSBjbGFpbSBpdHNlbGYgdGhlIGd1YXJkLlxuICAgKlxuICAgKiBSZXR1cm5zIHdoZXRoZXIgVEhJUyBjYWxsZXIgd29uIGl0LiBBIGxvc2VyIGlzIHRvbGQgdGhlIHByZXZpZXcgaXMgc3BlbnQsIHdoaWNoIGlzXG4gICAqIHRoZSB0cnV0aCBhbmQgaXMgYWxzbyB0aGUgdXBzZWxsLlxuICAgKi9cbiAgYXN5bmMgY2xhaW1SZXBvcnRQcmV2aWV3KG9yZ0lkOiBzdHJpbmcsIG5vdyA9IG5ldyBEYXRlKCkpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHguaW5zZXJ0KHNjaGVtYS5kb3JhU2V0dGluZ3MpXG4gICAgICAudmFsdWVzKHsgb3JnSWQsIHJlcG9ydFByZXZpZXdVc2VkQXQ6IG5vdyB9KVxuICAgICAgLm9uQ29uZmxpY3REb1VwZGF0ZSh7XG4gICAgICAgIHRhcmdldDogc2NoZW1hLmRvcmFTZXR0aW5ncy5vcmdJZCxcbiAgICAgICAgc2V0OiB7IHJlcG9ydFByZXZpZXdVc2VkQXQ6IG5vdywgdXBkYXRlZEF0OiBub3cgfSxcbiAgICAgICAgLy8gVGhlIHByZWRpY2F0ZSBpcyB3aGF0IG1ha2VzIHRoaXMgb25jZS1ldmVyOiBhbiBvcmcgd2hvc2Ugcm93IGFscmVhZHkgY2FycmllcyBhXG4gICAgICAgIC8vIHRpbWVzdGFtcCB1cGRhdGVzIHplcm8gcm93cyBhbmQgcmV0dXJucyBub3RoaW5nLlxuICAgICAgICBzZXRXaGVyZTogaXNOdWxsKHNjaGVtYS5kb3JhU2V0dGluZ3MucmVwb3J0UHJldmlld1VzZWRBdCksXG4gICAgICB9KVxuICAgICAgLnJldHVybmluZyh7IG9yZ0lkOiBzY2hlbWEuZG9yYVNldHRpbmdzLm9yZ0lkIH0pKTtcbiAgICByZXR1cm4gKHJvd3MgYXMgdW5rbm93bltdKS5sZW5ndGggPiAwO1xuICB9XG5cbiAgLyoqIFdoZXRoZXIgdGhlIG9yZyBoYXMgYWxyZWFkeSBzcGVudCBpdHMgZnJlZSBwcmV2aWV3LiAqL1xuICBhc3luYyByZXBvcnRQcmV2aWV3VXNlZChvcmdJZDogc3RyaW5nKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnNlbGVjdCh7IGF0OiBzY2hlbWEuZG9yYVNldHRpbmdzLnJlcG9ydFByZXZpZXdVc2VkQXQgfSlcbiAgICAgIC5mcm9tKHNjaGVtYS5kb3JhU2V0dGluZ3MpXG4gICAgICAud2hlcmUoZXEoc2NoZW1hLmRvcmFTZXR0aW5ncy5vcmdJZCwgb3JnSWQpKVxuICAgICAgLmxpbWl0KDEpKTtcbiAgICByZXR1cm4gKHJvd3MgYXMgQXJyYXk8eyBhdDogRGF0ZSB8IG51bGwgfT4pWzBdPy5hdCAhPSBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIElzIGBlbWFpbGAgYWRtaXNzaWJsZSBhcyBhIHJlY2lwaWVudCBmb3IgdGhpcyBvcmcsIGFuZCBkb2VzIGl0IG5lZWQgYW5cbiAgICogYWRtaW4ncyBhcHByb3ZhbCBmaXJzdD9cbiAgICpcbiAgICogYG9yZ01lbWJlckVtYWlsc2AgaXMgdGhlIHNldCBvZiBhZGRyZXNzZXMgdGhhdCBhbHJlYWR5IGF1dGhlbnRpY2F0ZSB0byB0aGlzXG4gICAqIG9yZyDigJQgdGhleSBhcmUgYWx3YXlzIGFkbWlzc2libGUgYW5kIG5ldmVyIG5lZWQgdmVyaWZpY2F0aW9uLCBiZWNhdXNlIHRoZSBvcmdcbiAgICogaGFzIGFscmVhZHkgZXN0YWJsaXNoZWQgdGhleSBiZWxvbmcgdG8gaXQuIEV2ZXJ5dGhpbmcgZWxzZSBpcyBtZWFzdXJlZCBhZ2FpbnN0XG4gICAqIHRoZSBhbGxvd2VkLWRvbWFpbiBsaXN0LCBhbmQgYSBOVUxMIGxpc3QgbWVhbnMgXCJubyBleHRlcm5hbCBhZGRyZXNzZXMgYXQgYWxsXCIsXG4gICAqIG5vdCBcImFueSBhZGRyZXNzXCI6IHRoZSBjbG9zZWQgcmVhZGluZyBpcyB0aGUgb25lIHRoYXQgY2Fubm90IGxlYWsgYSByZXBvcnQgYnlcbiAgICogYW4gYWRtaW4gZm9yZ2V0dGluZyB0byBjb25maWd1cmUgc29tZXRoaW5nLlxuICAgKi9cbiAgYWRtaXRSZWNpcGllbnQoXG4gICAgZW1haWw6IHN0cmluZyxcbiAgICBwb2xpY3k6IHsgcmVjaXBpZW50RG9tYWluczogc3RyaW5nW10gfCBudWxsOyByZXF1aXJlQXBwcm92YWw6IGJvb2xlYW4gfSxcbiAgICBvcmdNZW1iZXJFbWFpbHM6IFJlYWRvbmx5U2V0PHN0cmluZz4sXG4gICk6IHsgYWRtaXR0ZWQ6IHRydWU7IG1lbWJlcjogYm9vbGVhbjsgbmVlZHNBcHByb3ZhbDogYm9vbGVhbiB9IHwgeyBhZG1pdHRlZDogZmFsc2U7IHJlYXNvbjogc3RyaW5nIH0ge1xuICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBlbWFpbC50cmltKCkudG9Mb3dlckNhc2UoKTtcbiAgICBpZiAob3JnTWVtYmVyRW1haWxzLmhhcyhub3JtYWxpemVkKSkgcmV0dXJuIHsgYWRtaXR0ZWQ6IHRydWUsIG1lbWJlcjogdHJ1ZSwgbmVlZHNBcHByb3ZhbDogZmFsc2UgfTtcblxuICAgIGNvbnN0IGF0ID0gbm9ybWFsaXplZC5sYXN0SW5kZXhPZignQCcpO1xuICAgIGNvbnN0IGRvbWFpbiA9IGF0ID49IDAgPyBub3JtYWxpemVkLnNsaWNlKGF0ICsgMSkgOiAnJztcbiAgICBpZiAoIWRvbWFpbikgcmV0dXJuIHsgYWRtaXR0ZWQ6IGZhbHNlLCByZWFzb246ICdOb3QgYSB2YWxpZCBlbWFpbCBhZGRyZXNzLicgfTtcblxuICAgIGNvbnN0IGFsbG93ZWQgPSBwb2xpY3kucmVjaXBpZW50RG9tYWlucztcbiAgICBpZiAoIWFsbG93ZWQgfHwgYWxsb3dlZC5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGFkbWl0dGVkOiBmYWxzZSxcbiAgICAgICAgcmVhc29uOiAnVGhpcyBvcmdhbml6YXRpb24gb25seSBkZWxpdmVycyByZXBvcnRzIHRvIGl0cyBvd24gbWVtYmVycy4gJ1xuICAgICAgICAgICsgJ0FuIGFkbWluaXN0cmF0b3IgY2FuIGFsbG93IHNwZWNpZmljIGV4dGVybmFsIGRvbWFpbnMgaW4gcmVwb3J0aW5nIHNldHRpbmdzLicsXG4gICAgICB9O1xuICAgIH1cbiAgICBjb25zdCBvayA9IGFsbG93ZWQuc29tZSgoZCkgPT4ge1xuICAgICAgY29uc3QgY2xlYW4gPSBkLnRyaW0oKS50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL15ALywgJycpO1xuICAgICAgcmV0dXJuIGNsZWFuLmxlbmd0aCA+IDAgJiYgKGRvbWFpbiA9PT0gY2xlYW4gfHwgZG9tYWluLmVuZHNXaXRoKGAuJHtjbGVhbn1gKSk7XG4gICAgfSk7XG4gICAgaWYgKCFvaykge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgYWRtaXR0ZWQ6IGZhbHNlLFxuICAgICAgICByZWFzb246IGBSZXBvcnRzIGNhbiBvbmx5IGJlIHNlbnQgdG8gJHthbGxvd2VkLmpvaW4oJywgJyl9IGFkZHJlc3NlcyBpbiB0aGlzIG9yZ2FuaXphdGlvbi5gLFxuICAgICAgfTtcbiAgICB9XG4gICAgcmV0dXJuIHsgYWRtaXR0ZWQ6IHRydWUsIG1lbWJlcjogZmFsc2UsIG5lZWRzQXBwcm92YWw6IHBvbGljeS5yZXF1aXJlQXBwcm92YWwgfTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBSZXRlbnRpb24g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG5cbiAgLyoqXG4gICAqIEhhcmQtZGVsZXRlIGV4cGlyZWQgdG9tYnN0b25lcyBhY3Jvc3MgZXZlcnkgb3JnLCBmb3IgdGhlIHNoYXJlZCBzb2Z0LWRlbGV0ZVxuICAgKiBzd2VlcC4gUnVucyBpbnNpZGUgdGhlIHN3ZWVwJ3Mgb3duIHN5c2FkbWluIHNjb3BlLlxuICAgKlxuICAgKiBSdW5zIGdvIGZpcnN0OiBhIHB1cmdlZCBkZWZpbml0aW9uIHdpdGggb3JwaGFuZWQgcnVucyB3b3VsZCBsZWF2ZSBzbmFwc2hvdHNcbiAgICogbm90aGluZyBjYW4gcmVhY2gsIGFuZCBzaGFyZSBsaW5rcyBhcmUgZGVsZXRlZCB3aXRoIHRoZWlyIHJ1bnMgc28gYSBwdXJnZVxuICAgKiBjYW5ub3QgbGVhdmUgYSBsaXZlIHRva2VuIHBvaW50aW5nIGF0IGEgcm93IHRoYXQgbm8gbG9uZ2VyIGV4aXN0cy5cbiAgICovXG4gIHB1cmdlYWJsZUVudGl0aWVzKCk6IEFycmF5PHsgbmFtZTogc3RyaW5nOyBwdXJnZUV4cGlyZWQobm93OiBEYXRlLCBsaW1pdD86IG51bWJlcik6IFByb21pc2U8bnVtYmVyPiB9PiB7XG4gICAgcmV0dXJuIFtcbiAgICAgIHsgbmFtZTogJ3JlcG9ydF9ydW4nLCBwdXJnZUV4cGlyZWQ6IChub3csIGxpbWl0KSA9PiB0aGlzLnB1cmdlUnVucyhub3csIGxpbWl0KSB9LFxuICAgICAgeyBuYW1lOiAncmVwb3J0X2RlZmluaXRpb24nLCBwdXJnZUV4cGlyZWQ6IChub3csIGxpbWl0KSA9PiB0aGlzLnB1cmdlRGVmaW5pdGlvbnMobm93LCBsaW1pdCkgfSxcbiAgICAgIHsgbmFtZTogJ3JlcG9ydF9yZWNpcGllbnQnLCBwdXJnZUV4cGlyZWQ6IChub3csIGxpbWl0KSA9PiB0aGlzLnB1cmdlUmVjaXBpZW50cyhub3csIGxpbWl0KSB9LFxuICAgIF07XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHB1cmdlUnVucyhub3c6IERhdGUsIGxpbWl0ID0gNTAwKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICByZXR1cm4gd2l0aFRlbmFudFR4KGFzeW5jICh0eCkgPT4ge1xuICAgICAgY29uc3QgZHVlID0gYXdhaXQgdHguc2VsZWN0KHsgaWQ6IHNjaGVtYS5yZXBvcnRSdW4uaWQgfSkuZnJvbShzY2hlbWEucmVwb3J0UnVuKVxuICAgICAgICAud2hlcmUoYW5kKHNxbGAke3NjaGVtYS5yZXBvcnRSdW4uZGVsZXRlZEF0fSBJUyBOT1QgTlVMTGAsIGx0ZShzY2hlbWEucmVwb3J0UnVuLnB1cmdlQWZ0ZXIsIG5vdykpKVxuICAgICAgICAubGltaXQobGltaXQpO1xuICAgICAgY29uc3QgaWRzID0gKGR1ZSBhcyBBcnJheTx7IGlkOiBzdHJpbmcgfT4pLm1hcCgocikgPT4gci5pZCk7XG4gICAgICBpZiAoaWRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIDA7XG4gICAgICBhd2FpdCB0eC5kZWxldGUoc2NoZW1hLnJlcG9ydFNoYXJlTGluaykud2hlcmUoaW5BcnJheShzY2hlbWEucmVwb3J0U2hhcmVMaW5rLnJ1bklkLCBpZHMpKTtcbiAgICAgIGNvbnN0IGdvbmUgPSBhd2FpdCB0eC5kZWxldGUoc2NoZW1hLnJlcG9ydFJ1bikud2hlcmUoaW5BcnJheShzY2hlbWEucmVwb3J0UnVuLmlkLCBpZHMpKVxuICAgICAgICAucmV0dXJuaW5nKHsgaWQ6IHNjaGVtYS5yZXBvcnRSdW4uaWQgfSk7XG4gICAgICByZXR1cm4gKGdvbmUgYXMgdW5rbm93bltdKS5sZW5ndGg7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHB1cmdlRGVmaW5pdGlvbnMobm93OiBEYXRlLCBsaW1pdCA9IDUwMCk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgcmV0dXJuIHdpdGhUZW5hbnRUeChhc3luYyAodHgpID0+IHtcbiAgICAgIGNvbnN0IGR1ZSA9IGF3YWl0IHR4LnNlbGVjdCh7IGlkOiBzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5pZCB9KS5mcm9tKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uKVxuICAgICAgICAud2hlcmUoYW5kKFxuICAgICAgICAgIHNxbGAke3NjaGVtYS5yZXBvcnREZWZpbml0aW9uLmRlbGV0ZWRBdH0gSVMgTk9UIE5VTExgLFxuICAgICAgICAgIGx0ZShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5wdXJnZUFmdGVyLCBub3cpLFxuICAgICAgICApKVxuICAgICAgICAubGltaXQobGltaXQpO1xuICAgICAgY29uc3QgaWRzID0gKGR1ZSBhcyBBcnJheTx7IGlkOiBzdHJpbmcgfT4pLm1hcCgocikgPT4gci5pZCk7XG4gICAgICBpZiAoaWRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIDA7XG4gICAgICAvLyBUaGUgZGVmaW5pdGlvbidzIHJ1bnMgKGFuZCB0aGVpciBsaW5rcykgZ28gd2l0aCBpdCwgdG9tYnN0b25lZCBvciBub3Q6XG4gICAgICAvLyBhIHNuYXBzaG90IHdob3NlIGRlZmluaXRpb24gaXMgZ29uZSBjYW4gbmV2ZXIgYmUgbGlzdGVkIGFnYWluLlxuICAgICAgY29uc3QgcnVucyA9IGF3YWl0IHR4LnNlbGVjdCh7IGlkOiBzY2hlbWEucmVwb3J0UnVuLmlkIH0pLmZyb20oc2NoZW1hLnJlcG9ydFJ1bilcbiAgICAgICAgLndoZXJlKGluQXJyYXkoc2NoZW1hLnJlcG9ydFJ1bi5kZWZpbml0aW9uSWQsIGlkcykpO1xuICAgICAgY29uc3QgcnVuSWRzID0gKHJ1bnMgYXMgQXJyYXk8eyBpZDogc3RyaW5nIH0+KS5tYXAoKHIpID0+IHIuaWQpO1xuICAgICAgaWYgKHJ1bklkcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGF3YWl0IHR4LmRlbGV0ZShzY2hlbWEucmVwb3J0U2hhcmVMaW5rKS53aGVyZShpbkFycmF5KHNjaGVtYS5yZXBvcnRTaGFyZUxpbmsucnVuSWQsIHJ1bklkcykpO1xuICAgICAgICBhd2FpdCB0eC5kZWxldGUoc2NoZW1hLnJlcG9ydFJ1bikud2hlcmUoaW5BcnJheShzY2hlbWEucmVwb3J0UnVuLmlkLCBydW5JZHMpKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGdvbmUgPSBhd2FpdCB0eC5kZWxldGUoc2NoZW1hLnJlcG9ydERlZmluaXRpb24pLndoZXJlKGluQXJyYXkoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uaWQsIGlkcykpXG4gICAgICAgIC5yZXR1cm5pbmcoeyBpZDogc2NoZW1hLnJlcG9ydERlZmluaXRpb24uaWQgfSk7XG4gICAgICByZXR1cm4gKGdvbmUgYXMgdW5rbm93bltdKS5sZW5ndGg7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHB1cmdlUmVjaXBpZW50cyhub3c6IERhdGUsIGxpbWl0ID0gNTAwKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICByZXR1cm4gd2l0aFRlbmFudFR4KGFzeW5jICh0eCkgPT4ge1xuICAgICAgY29uc3QgZHVlID0gYXdhaXQgdHguc2VsZWN0KHsgaWQ6IHNjaGVtYS5yZXBvcnRSZWNpcGllbnQuaWQgfSkuZnJvbShzY2hlbWEucmVwb3J0UmVjaXBpZW50KVxuICAgICAgICAud2hlcmUoYW5kKFxuICAgICAgICAgIHNxbGAke3NjaGVtYS5yZXBvcnRSZWNpcGllbnQuZGVsZXRlZEF0fSBJUyBOT1QgTlVMTGAsXG4gICAgICAgICAgbHRlKHNjaGVtYS5yZXBvcnRSZWNpcGllbnQucHVyZ2VBZnRlciwgbm93KSxcbiAgICAgICAgKSlcbiAgICAgICAgLmxpbWl0KGxpbWl0KTtcbiAgICAgIGNvbnN0IGlkcyA9IChkdWUgYXMgQXJyYXk8eyBpZDogc3RyaW5nIH0+KS5tYXAoKHIpID0+IHIuaWQpO1xuICAgICAgaWYgKGlkcy5sZW5ndGggPT09IDApIHJldHVybiAwO1xuICAgICAgY29uc3QgZ29uZSA9IGF3YWl0IHR4LmRlbGV0ZShzY2hlbWEucmVwb3J0UmVjaXBpZW50KS53aGVyZShpbkFycmF5KHNjaGVtYS5yZXBvcnRSZWNpcGllbnQuaWQsIGlkcykpXG4gICAgICAgIC5yZXR1cm5pbmcoeyBpZDogc2NoZW1hLnJlcG9ydFJlY2lwaWVudC5pZCB9KTtcbiAgICAgIHJldHVybiAoZ29uZSBhcyB1bmtub3duW10pLmxlbmd0aDtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKiBFdmVyeSBkZWZpbml0aW9uIHRoZSBvd25lciBtdXN0IGJlIHJlLWNoZWNrZWQgZm9yLCBmb3IgdGhlIHBhdXNlIHN3ZWVwLiAqL1xuICBhc3luYyBsaXN0T3duZWREZWZpbml0aW9ucyhvcmdJZDogc3RyaW5nLCBvd25lcklkOiBzdHJpbmcpOiBQcm9taXNlPFJlcG9ydERlZmluaXRpb25bXT4ge1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5zZWxlY3QoKS5mcm9tKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uKVxuICAgICAgLndoZXJlKGFuZChcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24ub3JnSWQsIG9yZ0lkKSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24ub3duZXJJZCwgb3duZXJJZCksXG4gICAgICAgIGlzTnVsbChzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5kZWxldGVkQXQpLFxuICAgICAgKSkpO1xuICAgIHJldHVybiByb3dzIGFzIFJlcG9ydERlZmluaXRpb25bXTtcbiAgfVxuXG4gIC8vIOKUgOKUgCBUaGUgc2NoZWR1bGVyJ3MgcmVhZHMgYW5kIHdyaXRlcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxuICAvKipcbiAgICogRGVmaW5pdGlvbnMgd2hvc2UgbmV4dCBydW4gaXMgZHVlLCBhY3Jvc3MgZXZlcnkgb3JnLlxuICAgKlxuICAgKiBSdW5zIGFzIFNZU0FETUlOLCBiZWNhdXNlIHRoZSBzY2hlZHVsZXIgaGFzIG5vIHRlbmFudDogaXQgaXMgbG9va2luZyBmb3Igd29ya1xuICAgKiBpbiBvcmdzIGl0IGhhcyBub3QgYmVlbiB0b2xkIGFib3V0LiBUaGlzIGlzIHRoZSBvbmUgcmVhZCBoZXJlIHRoYXQgY3Jvc3NlcyBvcmdcbiAgICogYm91bmRhcmllcywgYW5kIGl0IGlzIGtlcHQgdG8gdGhlIG5hcnJvd2VzdCBwb3NzaWJsZSBzaGFwZSDigJQgdGhlIHJvd3MgbmVlZGVkIHRvXG4gICAqIGNsYWltIGFuZCBleGVjdXRlLCBvcmRlcmVkIG9sZGVzdC1kdWUgZmlyc3Qgc28gYSBiYWNrbG9nIGRyYWlucyBpbiBvcmRlciByYXRoZXJcbiAgICogdGhhbiBzdGFydmluZyB3aGljaGV2ZXIgb3JnIHNvcnRzIGxhc3QgYnkgaWQuXG4gICAqXG4gICAqIGBsaW1pdGAgaXMgdGhlIHBlci1jeWNsZSBjZWlsaW5nOiBhIGN5Y2xlIHRoYXQgdHJpZWQgdG8gZHJhaW4gZXZlcnl0aGluZyB3b3VsZFxuICAgKiBob2xkIHRoZSBsZWFkZXIgbG9jayBmb3IgYXMgbG9uZyBhcyB0aGUgc2xvd2VzdCBvcmcncyBjb21wb3NlIHRha2VzLCB0aW1lcyB0aGVcbiAgICogbnVtYmVyIG9mIGRlZmluaXRpb25zLlxuICAgKi9cbiAgYXN5bmMgZHVlRGVmaW5pdGlvbnMobm93OiBEYXRlLCBsaW1pdDogbnVtYmVyKTogUHJvbWlzZTxSZXBvcnREZWZpbml0aW9uW10+IHtcbiAgICByZXR1cm4gcnVuV2l0aFRlbmFudENvbnRleHQoeyBpc1N1cGVyQWRtaW46IHRydWUgfSwgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3Qgcm93cyA9IGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnNlbGVjdCgpLmZyb20oc2NoZW1hLnJlcG9ydERlZmluaXRpb24pXG4gICAgICAgIC53aGVyZShhbmQoXG4gICAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uaXNBY3RpdmUsIHRydWUpLFxuICAgICAgICAgIGlzTnVsbChzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5kZWxldGVkQXQpLFxuICAgICAgICAgIGx0ZShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5uZXh0UnVuQXQsIG5vdyksXG4gICAgICAgICkpXG4gICAgICAgIC5vcmRlckJ5KGFzYyhzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5uZXh0UnVuQXQpKVxuICAgICAgICAubGltaXQobGltaXQpKTtcbiAgICAgIHJldHVybiByb3dzIGFzIFJlcG9ydERlZmluaXRpb25bXTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGFpbSBhIGR1ZSBkZWZpbml0aW9uIGJ5IGFkdmFuY2luZyBpdHMgYG5leHRSdW5BdGAsIGJ1dCBvbmx5IGlmIGl0IHN0aWxsIGhvbGRzXG4gICAqIHRoZSB2YWx1ZSB0aGUgc2NhbiBzYXcuXG4gICAqXG4gICAqIFRIRSBDT05ESVRJT05BTCBVUERBVEUgSVMgVEhFIENPTkNVUlJFTkNZIEdVQVJELCBub3QgdGhlIGxlYWRlciBsb2NrLiBUaGUgbG9ja1xuICAgKiBzdG9wcyB0d28gcmVwbGljYXMgc3dlZXBpbmcgaW4gdGhlIHNhbWUgd2luZG93OyBpdCBkb2VzIG5vdCBzdG9wIGEgc3dlZXAgdGhhdFxuICAgKiBvdXRsaXZlcyBpdHMgbG9jayBUVEwgZnJvbSBvdmVybGFwcGluZyB0aGUgbmV4dCBsZWFkZXIncyBzd2VlcCwgYW5kIGJvdGggd291bGRcbiAgICogdGhlbiBzZWxlY3QgdGhlIHNhbWUgc3RpbGwtZHVlIGRlZmluaXRpb24uIFdpdGhvdXQgdGhlIGBuZXh0X3J1bl9hdCA9IDxzZWVuPmBcbiAgICogcHJlZGljYXRlIGVhY2ggd291bGQgYWR2YW5jZSB0aGUgc2NoZWR1bGUgYW5kIGVhY2ggd291bGQgY29tcG9zZSBhbmQgREVMSVZFUiBhXG4gICAqIHJlcG9ydCDigJQgdGhlIHNhbWUgbWFuYWdlciBnZXRzIHRoZSBzYW1lIHJlcG9ydCB0d2ljZSwgZnJvbSB0d28gcG9kcywgd2hpY2ggaXNcbiAgICogdGhlIGZhaWx1cmUgZXZlcnlvbmUgbm90aWNlcy5cbiAgICpcbiAgICogUnVucyBhcyBzeXNhZG1pbiBmb3IgdGhlIHNhbWUgcmVhc29uIGFzIHRoZSBzY2FuLCBhbmQgcmV0dXJucyB3aGV0aGVyIHRoZSBjbGFpbVxuICAgKiB3b24gc28gdGhlIGNhbGxlciBjYW4gc2ltcGx5IHNraXAgYSBsb3N0IHJhY2UuXG4gICAqL1xuICBhc3luYyBjbGFpbURlZmluaXRpb24oaWQ6IHN0cmluZywgc2Vlbk5leHRSdW5BdDogRGF0ZSwgbmV4dFJ1bkF0OiBEYXRlLCBub3cgPSBuZXcgRGF0ZSgpKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgcmV0dXJuIHJ1bldpdGhUZW5hbnRDb250ZXh0KHsgaXNTdXBlckFkbWluOiB0cnVlIH0sIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IGNsYWltZWQgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC51cGRhdGUoc2NoZW1hLnJlcG9ydERlZmluaXRpb24pXG4gICAgICAgIC5zZXQoeyBsYXN0UnVuQXQ6IG5vdywgbmV4dFJ1bkF0LCB1cGRhdGVkQXQ6IG5vdyB9KVxuICAgICAgICAud2hlcmUoYW5kKFxuICAgICAgICAgIGVxKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLmlkLCBpZCksXG4gICAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24ubmV4dFJ1bkF0LCBzZWVuTmV4dFJ1bkF0KSxcbiAgICAgICAgKSlcbiAgICAgICAgLnJldHVybmluZyh7IGlkOiBzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5pZCB9KSk7XG4gICAgICByZXR1cm4gKGNsYWltZWQgYXMgdW5rbm93bltdKS5sZW5ndGggPiAwO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCAob3IgY2xlYXIpIGEgZGVmaW5pdGlvbidzIGBuZXh0UnVuQXRgIHdpdGhvdXQgY2xhaW1pbmcgaXQuXG4gICAqXG4gICAqIFVzZWQgd2hlbiBhIGRlZmluaXRpb24gaXMgY3JlYXRlZCwgcmVzdW1lZCwgb3IgaGFzIGl0cyBjYWRlbmNlL3RpbWV6b25lIGNoYW5nZWRcbiAgICog4oCUIHRoZSBzY2hlZHVsZSBoYXMgdG8gYmUgcmUtZGVyaXZlZCBmcm9tIHRoZSBjYWxlbmRhciByYXRoZXIgdGhhbiBsZWZ0IGF0XG4gICAqIHdoYXRldmVyIHRoZSBvbGQgY2FkZW5jZSBwcm9kdWNlZC5cbiAgICovXG4gIGFzeW5jIHNldE5leHRSdW4ob3JnSWQ6IHN0cmluZywgaWQ6IHN0cmluZywgbmV4dFJ1bkF0OiBEYXRlIHwgbnVsbCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHdpdGhUZW5hbnRUeCgodHgpID0+IHR4LnVwZGF0ZShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbilcbiAgICAgIC5zZXQoeyBuZXh0UnVuQXQsIHVwZGF0ZWRBdDogbmV3IERhdGUoKSB9KVxuICAgICAgLndoZXJlKGFuZChcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24ub3JnSWQsIG9yZ0lkKSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uaWQsIGlkKSxcbiAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLmRlbGV0ZWRBdCksXG4gICAgICApKSk7XG4gIH1cblxuICAvKipcbiAgICogUGF1c2UgT05FIGRlZmluaXRpb24gd2l0aCB0aGUgcmVhc29uLCBmcm9tIHRoZSBzY2hlZHVsZXIuXG4gICAqXG4gICAqIFNlcGFyYXRlIGZyb20ge0BsaW5rIHBhdXNlRGVmaW5pdGlvbnNGb3JPd25lcn0gYmVjYXVzZSB0aGUgdHJpZ2dlciBpcyBkaWZmZXJlbnQ6XG4gICAqIHRoYXQgb25lIGlzIGEgbWVtYmVyc2hpcCBjaGFuZ2UgYWZmZWN0aW5nIGV2ZXJ5IGRlZmluaXRpb24gYSBwZXJzb24gb3ducywgdGhpc1xuICAgKiBvbmUgaXMgYSBwZXItcnVuIHJlY2hlY2sgdGhhdCBmYWlsZWQuIENsZWFycyBgbmV4dFJ1bkF0YCBzbyBhIHBhdXNlZCBkZWZpbml0aW9uXG4gICAqIHN0b3BzIGFwcGVhcmluZyBpbiB0aGUgZHVlIHNjYW4gYXQgYWxsLCByYXRoZXIgdGhhbiBiZWluZyByZS1jbGFpbWVkIGFuZFxuICAgKiByZS1yZWplY3RlZCBldmVyeSBjeWNsZS5cbiAgICpcbiAgICogUnVucyBhcyBzeXNhZG1pbjogdGhlIGNhbGxlciBpcyB0aGUgc2NoZWR1bGVyLCB3aGljaCBoYXMgbm8gdGVuYW50LCBhbmQgdGhlIGlkXG4gICAqIGNhbWUgZnJvbSBpdHMgb3duIGNyb3NzLW9yZyBzY2FuLlxuICAgKi9cbiAgYXN5bmMgcGF1c2VEZWZpbml0aW9uKGlkOiBzdHJpbmcsIHJlYXNvbjogUmVwb3J0UGF1c2VSZWFzb24pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCBydW5XaXRoVGVuYW50Q29udGV4dCh7IGlzU3VwZXJBZG1pbjogdHJ1ZSB9LCBhc3luYyAoKSA9PiB7XG4gICAgICBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC51cGRhdGUoc2NoZW1hLnJlcG9ydERlZmluaXRpb24pXG4gICAgICAgIC5zZXQoeyBpc0FjdGl2ZTogZmFsc2UsIHBhdXNlZFJlYXNvbjogcmVhc29uLCBuZXh0UnVuQXQ6IG51bGwsIHVwZGF0ZWRBdDogbmV3IERhdGUoKSB9KVxuICAgICAgICAud2hlcmUoYW5kKFxuICAgICAgICAgIGVxKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLmlkLCBpZCksXG4gICAgICAgICAgaXNOdWxsKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLmRlbGV0ZWRBdCksXG4gICAgICAgICkpKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXN1bWUgYSBwYXVzZWQgZGVmaW5pdGlvbiwgY2xlYXJpbmcgdGhlIHJlYXNvbiBhbmQgcmUtZGVyaXZpbmcgaXRzIHNjaGVkdWxlLlxuICAgKlxuICAgKiBgbmV4dFJ1bkF0YCBpcyBzdXBwbGllZCBieSB0aGUgY2FsbGVyIHJhdGhlciB0aGFuIGNvbXB1dGVkIGhlcmU6IHRoZSBwZXJpb2RcbiAgICogYXJpdGhtZXRpYyBuZWVkcyB0aGUgZGVmaW5pdGlvbidzIHRpbWV6b25lIGFuZCB3ZWVrIHN0YXJ0LCBhbmQgdGhhdCBiZWxvbmdzIHdpdGhcbiAgICogdGhlIHBlcmlvZCByZXNvbHZlciwgbm90IHRoZSBzdG9yZS5cbiAgICovXG4gIGFzeW5jIHJlc3VtZURlZmluaXRpb24ob3JnSWQ6IHN0cmluZywgaWQ6IHN0cmluZywgbmV4dFJ1bkF0OiBEYXRlLCBhY3RvcklkOiBzdHJpbmcpOiBQcm9taXNlPFJlcG9ydERlZmluaXRpb24+IHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHgudXBkYXRlKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uKVxuICAgICAgLnNldCh7IGlzQWN0aXZlOiB0cnVlLCBwYXVzZWRSZWFzb246IG51bGwsIG5leHRSdW5BdCwgdXBkYXRlZEJ5OiBhY3RvcklkLCB1cGRhdGVkQXQ6IG5ldyBEYXRlKCkgfSlcbiAgICAgIC53aGVyZShhbmQoXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLm9yZ0lkLCBvcmdJZCksXG4gICAgICAgIGVxKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uLmlkLCBpZCksXG4gICAgICAgIGlzTnVsbChzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5kZWxldGVkQXQpLFxuICAgICAgKSlcbiAgICAgIC5yZXR1cm5pbmcoKSk7XG4gICAgY29uc3Qgcm93ID0gKHJvd3MgYXMgUmVwb3J0RGVmaW5pdGlvbltdKVswXTtcbiAgICBpZiAoIXJvdykgdGhyb3cgbmV3IE5vdEZvdW5kRXJyb3IoJ1JlcG9ydCBkZWZpbml0aW9uIG5vdCBmb3VuZCcpO1xuICAgIHJldHVybiByb3c7XG4gIH1cblxuICAvKipcbiAgICogUGF1c2UgZXZlcnkgQUNUSVZFIGRlZmluaXRpb24gaW4gYW4gb3JnLCB3aXRoIHRoZSByZWFzb24uXG4gICAqXG4gICAqIFRoZSBlbnRpdGxlbWVudCBsYXBzZSBsZWc6IGJpbGxpbmcgdGVsbHMgcmVwb3J0aW5nIHRoZSBhZGQtb24gaXMgZ29uZSwgYW5kIGV2ZXJ5XG4gICAqIGRlZmluaXRpb24gaW4gdGhlIGFjY291bnQgc3RvcHMuIFJldHVybnMgdGhlIHJvd3Mgc28gdGhlIGNhbGxlciBjYW4gbm90aWZ5LlxuICAgKi9cbiAgYXN5bmMgcGF1c2VEZWZpbml0aW9uc0Zvck9yZyhvcmdJZDogc3RyaW5nLCByZWFzb246IFJlcG9ydFBhdXNlUmVhc29uKTogUHJvbWlzZTxSZXBvcnREZWZpbml0aW9uW10+IHtcbiAgICBjb25zdCByb3dzID0gYXdhaXQgd2l0aFRlbmFudFR4KCh0eCkgPT4gdHgudXBkYXRlKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uKVxuICAgICAgLnNldCh7IGlzQWN0aXZlOiBmYWxzZSwgcGF1c2VkUmVhc29uOiByZWFzb24sIG5leHRSdW5BdDogbnVsbCwgdXBkYXRlZEF0OiBuZXcgRGF0ZSgpIH0pXG4gICAgICAud2hlcmUoYW5kKFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5vcmdJZCwgb3JnSWQpLFxuICAgICAgICBlcShzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5pc0FjdGl2ZSwgdHJ1ZSksXG4gICAgICAgIGlzTnVsbChzY2hlbWEucmVwb3J0RGVmaW5pdGlvbi5kZWxldGVkQXQpLFxuICAgICAgKSlcbiAgICAgIC5yZXR1cm5pbmcoKSk7XG4gICAgcmV0dXJuIHJvd3MgYXMgUmVwb3J0RGVmaW5pdGlvbltdO1xuICB9XG5cbiAgLyoqXG4gICAqIEV2ZXJ5IGRlZmluaXRpb24gaW4gYW4gb3JnIHBhdXNlZCBmb3IgYHJlYXNvbmAsIGZvciB0aGUgcmVzdW1lIGxlZy5cbiAgICpcbiAgICogU2NvcGVkIHRvIHRoZSByZWFzb24gb24gcHVycG9zZTogcmUtc3Vic2NyaWJpbmcgbXVzdCBub3QgdW4tcGF1c2UgYSBkZWZpbml0aW9uXG4gICAqIHdob3NlIG93bmVyIHdhcyBkZWFjdGl2YXRlZC4gVGhvc2UgYXJlIGRpZmZlcmVudCBwcm9ibGVtcyB3aXRoIGRpZmZlcmVudCBmaXhlcyxcbiAgICogYW5kIHJlc3VtaW5nIG9uZSBieSBmaXhpbmcgdGhlIG90aGVyIGlzIGhvdyBhIHJlcG9ydCBzdGFydHMgcnVubmluZyB1bmRlciBhXG4gICAqIHBlcnNvbiB3aG8gbGVmdC5cbiAgICovXG4gIGFzeW5jIGRlZmluaXRpb25zUGF1c2VkRm9yKG9yZ0lkOiBzdHJpbmcsIHJlYXNvbjogUmVwb3J0UGF1c2VSZWFzb24pOiBQcm9taXNlPFJlcG9ydERlZmluaXRpb25bXT4ge1xuICAgIGNvbnN0IHJvd3MgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5zZWxlY3QoKS5mcm9tKHNjaGVtYS5yZXBvcnREZWZpbml0aW9uKVxuICAgICAgLndoZXJlKGFuZChcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24ub3JnSWQsIG9yZ0lkKSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uaXNBY3RpdmUsIGZhbHNlKSxcbiAgICAgICAgZXEoc2NoZW1hLnJlcG9ydERlZmluaXRpb24ucGF1c2VkUmVhc29uLCByZWFzb24pLFxuICAgICAgICBpc051bGwoc2NoZW1hLnJlcG9ydERlZmluaXRpb24uZGVsZXRlZEF0KSxcbiAgICAgICkpKTtcbiAgICByZXR1cm4gcm93cyBhcyBSZXBvcnREZWZpbml0aW9uW107XG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IHN0YWtlaG9sZGVyUmVwb3J0U3RvcmUgPSBuZXcgU3Rha2Vob2xkZXJSZXBvcnRTdG9yZSgpO1xuIl19
|