@dynatrace/rum-javascript-sdk 1.331.18 → 1.333.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -390
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +3 -0
- package/dist/testing/snapshot.d.ts +129 -0
- package/dist/testing/snapshot.js +385 -0
- package/dist/testing/test.d.ts +9 -0
- package/dist/testing/test.js +237 -160
- package/dist/types/index-typedoc.d.ts +3 -0
- package/dist/types/index-typedoc.js +4 -1
- package/dist/types/rum-events/rum-event-keys.d.ts +9 -0
- package/dist/types/rum-events/rum-event-keys.js +11 -1
- package/dist/types/rum-events/rum-internal-selfmonitoring-event.d.ts +5 -1
- package/dist/types/rum-events/rum-internal-selfmonitoring-event.js +5 -1
- package/dist/types/rum-events/rum-web-request-event.d.ts +2 -1
- package/dist/types/rum-events/rum-web-request-event.js +2 -1
- package/dist/types/rum-events/shared-namespaces-and-fields/general-rum-event-fields.d.ts +1 -0
- package/dist/types/rum-events/shared-namespaces-and-fields/general-rum-event-fields.js +1 -1
- package/docs/1-overview.md +405 -0
- package/docs/2-testing.md +424 -0
- package/docs/{types.md → 3-types.md} +8 -2
- package/docs/{USERACTIONS.md → 4-useractions.md} +10 -8
- package/package.json +46 -9
- package/docs/testing.md +0 -215
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot testing utilities for Dynatrace RUM event verification.
|
|
3
|
+
* Provides mechanisms to compare captured events against stored snapshots
|
|
4
|
+
* with support for masking volatile fields.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { dirname } from "node:path";
|
|
8
|
+
/**
|
|
9
|
+
* Default properties that are ignored (replaced with "[IGNORED]") in snapshots to prevent flaky tests.
|
|
10
|
+
* These fields typically contain volatile values like timestamps, IDs, and durations
|
|
11
|
+
* that change between test runs.
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_IGNORED_FIELDS = [
|
|
14
|
+
// Base timing fields
|
|
15
|
+
"start_time",
|
|
16
|
+
"duration",
|
|
17
|
+
"timestamp",
|
|
18
|
+
// Session/Instance identity fields
|
|
19
|
+
"dt.rum.sid",
|
|
20
|
+
"dt.rum.browser.sid",
|
|
21
|
+
"dt.rum.instance.id",
|
|
22
|
+
"user_session.id",
|
|
23
|
+
"user.anonymous_id",
|
|
24
|
+
// View/Page
|
|
25
|
+
"view.foreground_time",
|
|
26
|
+
"view.instance_id",
|
|
27
|
+
"page.instance_id",
|
|
28
|
+
// User action
|
|
29
|
+
"user_action.instance_id",
|
|
30
|
+
"user_action.interrupted_by_instance_id",
|
|
31
|
+
"user_action.interrupted_instance_id",
|
|
32
|
+
"positions",
|
|
33
|
+
// Browser/Frame instance fields
|
|
34
|
+
"browser.frame.instance_id",
|
|
35
|
+
"browser.frame.parent_instance_id",
|
|
36
|
+
"browser.tab.instance_id",
|
|
37
|
+
// Distributed tracing fields
|
|
38
|
+
"trace.id",
|
|
39
|
+
"span.id",
|
|
40
|
+
// Self-monitoring
|
|
41
|
+
"dt.rum.sfm_events",
|
|
42
|
+
"dt.rum.agent.version",
|
|
43
|
+
"dt.support.last_user_input"
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* Default properties that are removed entirely from snapshots to prevent flaky tests.
|
|
47
|
+
* These fields vary in count or presence between test runs.
|
|
48
|
+
*/
|
|
49
|
+
export const DEFAULT_REMOVED_FIELDS = [
|
|
50
|
+
// Performance timing fields (W3C Navigation/Resource Timing)
|
|
51
|
+
"performance.*",
|
|
52
|
+
// Web Vitals timing fields
|
|
53
|
+
"fcp.*",
|
|
54
|
+
"fp.*",
|
|
55
|
+
"ttfb.*",
|
|
56
|
+
"lcp.*",
|
|
57
|
+
"fid.*",
|
|
58
|
+
"inp.*",
|
|
59
|
+
"cls.*",
|
|
60
|
+
// Long tasks
|
|
61
|
+
"long_task.*",
|
|
62
|
+
// Viewport/screen dimensions (vary by test environment)
|
|
63
|
+
"browser.window.*",
|
|
64
|
+
"device.screen.*",
|
|
65
|
+
"device.battery.level"
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* Default event patterns that are ignored during snapshot comparison.
|
|
69
|
+
* These events are volatile and may or may not be recorded depending on browser performance.
|
|
70
|
+
*/
|
|
71
|
+
export const DEFAULT_IGNORED_EVENTS = [
|
|
72
|
+
{ "characteristics.has_long_task": true },
|
|
73
|
+
{ "characteristics.is_self_monitoring": true }
|
|
74
|
+
];
|
|
75
|
+
/**
|
|
76
|
+
* Placeholder value used to replace ignored property values.
|
|
77
|
+
*/
|
|
78
|
+
export const IGNORED_PLACEHOLDER = "[IGNORED]";
|
|
79
|
+
/**
|
|
80
|
+
* Processes events by ignoring and removing specified properties.
|
|
81
|
+
* - Ignored properties have their values replaced with "[IGNORED]" placeholder
|
|
82
|
+
* - Removed properties are deleted entirely from the event
|
|
83
|
+
*
|
|
84
|
+
* @param events Array of event objects to process
|
|
85
|
+
* @param ignoredFields Array of field paths to ignore (values replaced with placeholder)
|
|
86
|
+
* @param removedFields Array of field paths to remove entirely
|
|
87
|
+
* @returns Deep copy of events with specified fields processed
|
|
88
|
+
*/
|
|
89
|
+
export function processEventProperties(events, ignoredFields = DEFAULT_IGNORED_FIELDS, removedFields = DEFAULT_REMOVED_FIELDS) {
|
|
90
|
+
return events.map(event => processSingleEvent(event, ignoredFields, removedFields));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Filters out events that match any of the ignore patterns.
|
|
94
|
+
* An event matches a pattern if all properties in the pattern exist in the event with the same values.
|
|
95
|
+
*
|
|
96
|
+
* @param events Array of events to filter
|
|
97
|
+
* @param ignorePatterns Array of patterns to match against
|
|
98
|
+
* @returns Filtered array excluding events that match any pattern
|
|
99
|
+
*/
|
|
100
|
+
export function filterIgnoredEvents(events, ignorePatterns = DEFAULT_IGNORED_EVENTS) {
|
|
101
|
+
if (ignorePatterns.length === 0) {
|
|
102
|
+
return events;
|
|
103
|
+
}
|
|
104
|
+
return events.filter(event => !matchesAnyPattern(event, ignorePatterns));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Checks if an event matches any of the provided patterns.
|
|
108
|
+
*
|
|
109
|
+
* @param event Event to check
|
|
110
|
+
* @param patterns Patterns to match against
|
|
111
|
+
* @returns True if the event matches any pattern
|
|
112
|
+
*/
|
|
113
|
+
function matchesAnyPattern(event, patterns) {
|
|
114
|
+
return patterns.some(pattern => matchesPattern(event, pattern));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Checks if an event matches a single pattern.
|
|
118
|
+
* A match occurs when all properties in the pattern exist in the event with equal values.
|
|
119
|
+
*
|
|
120
|
+
* @param event Event to check
|
|
121
|
+
* @param pattern Pattern to match against
|
|
122
|
+
* @returns True if all pattern properties match
|
|
123
|
+
*/
|
|
124
|
+
function matchesPattern(event, pattern) {
|
|
125
|
+
return Object.keys(pattern).every(key => key in event && event[key] === pattern[key]);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Processes a single event by ignoring and removing specified properties.
|
|
129
|
+
* - Ignored properties: values replaced with "[IGNORED]" placeholder
|
|
130
|
+
* - Removed properties: fields deleted entirely
|
|
131
|
+
* Both support exact field names and wildcard patterns (`prefix.*`).
|
|
132
|
+
*
|
|
133
|
+
* @param event Event object to process
|
|
134
|
+
* @param ignoredFields Array of field names/patterns to ignore (replace values)
|
|
135
|
+
* @param removedFields Array of field names/patterns to remove entirely
|
|
136
|
+
* @returns Deep copy of event with fields processed
|
|
137
|
+
*/
|
|
138
|
+
function processSingleEvent(event, ignoredFields, removedFields) {
|
|
139
|
+
const result = deepClone(event);
|
|
140
|
+
// Process ignored properties (replace values with placeholder)
|
|
141
|
+
for (const fieldPath of ignoredFields) {
|
|
142
|
+
if (fieldPath.endsWith(".*")) {
|
|
143
|
+
const prefix = fieldPath.slice(0, -1); // Remove "*", keep the dot
|
|
144
|
+
ignoreFieldsWithPrefix(result, prefix);
|
|
145
|
+
}
|
|
146
|
+
else if (fieldPath in result) {
|
|
147
|
+
result[fieldPath] = IGNORED_PLACEHOLDER;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Process removed properties (delete entirely)
|
|
151
|
+
for (const fieldPath of removedFields) {
|
|
152
|
+
if (fieldPath.endsWith(".*")) {
|
|
153
|
+
const prefix = fieldPath.slice(0, -1); // Remove "*", keep the dot
|
|
154
|
+
removeFieldsWithPrefix(result, prefix);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
delete result[fieldPath];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Replaces values of all fields in an object that start with the given prefix with the ignored placeholder.
|
|
164
|
+
* Used for wildcard patterns like `inp.*` to ignore `inp.start_time`, `inp.duration`, etc.
|
|
165
|
+
*
|
|
166
|
+
* @param obj Object to modify
|
|
167
|
+
* @param prefix Prefix to match (including trailing dot, e.g., "inp.")
|
|
168
|
+
*/
|
|
169
|
+
function ignoreFieldsWithPrefix(obj, prefix) {
|
|
170
|
+
for (const key of Object.keys(obj)) {
|
|
171
|
+
if (key.startsWith(prefix)) {
|
|
172
|
+
obj[key] = IGNORED_PLACEHOLDER;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Removes all fields in an object that start with the given prefix.
|
|
178
|
+
* Used for wildcard patterns like `inp.*` to remove `inp.start_time`, `inp.duration`, etc.
|
|
179
|
+
*
|
|
180
|
+
* @param obj Object to modify
|
|
181
|
+
* @param prefix Prefix to match (including trailing dot, e.g., "inp.")
|
|
182
|
+
*/
|
|
183
|
+
function removeFieldsWithPrefix(obj, prefix) {
|
|
184
|
+
for (const key of Object.keys(obj)) {
|
|
185
|
+
if (key.startsWith(prefix)) {
|
|
186
|
+
delete obj[key];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Type guard to check if a value is a Record<string, unknown>.
|
|
192
|
+
*
|
|
193
|
+
* @param value Value to check
|
|
194
|
+
* @returns True if value is a non-null object
|
|
195
|
+
*/
|
|
196
|
+
function isRecord(value) {
|
|
197
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Type guard to check if a value is an array of Record<string, unknown>.
|
|
201
|
+
*
|
|
202
|
+
* @param value Value to check
|
|
203
|
+
* @returns True if value is an array where all elements are records
|
|
204
|
+
*/
|
|
205
|
+
function isRecordArray(value) {
|
|
206
|
+
return Array.isArray(value) && value.every(isRecord);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Creates a deep clone of an object.
|
|
210
|
+
*
|
|
211
|
+
* @param obj Object to clone
|
|
212
|
+
* @returns Deep clone of the object
|
|
213
|
+
*/
|
|
214
|
+
function deepClone(obj) {
|
|
215
|
+
return JSON.parse(JSON.stringify(obj));
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Reads a snapshot file from disk.
|
|
219
|
+
* Returns an object indicating whether the file exists and its parsed contents.
|
|
220
|
+
*
|
|
221
|
+
* @param snapshotPath Absolute path to the snapshot file
|
|
222
|
+
* @returns Object with exists flag and parsed data
|
|
223
|
+
* @throws Error if the file contains invalid JSON or is not an array of records
|
|
224
|
+
*/
|
|
225
|
+
export function readSnapshot(snapshotPath) {
|
|
226
|
+
if (!existsSync(snapshotPath)) {
|
|
227
|
+
return { exists: false };
|
|
228
|
+
}
|
|
229
|
+
const content = readFileSync(snapshotPath, "utf8");
|
|
230
|
+
const parsed = safeParseJson(content, snapshotPath);
|
|
231
|
+
if (!isRecordArray(parsed)) {
|
|
232
|
+
throw new Error(`Invalid snapshot format in "${snapshotPath}": expected an array of objects`);
|
|
233
|
+
}
|
|
234
|
+
return { exists: true, data: parsed };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Safely parses a JSON string, wrapping errors with context.
|
|
238
|
+
*
|
|
239
|
+
* @param content JSON string to parse
|
|
240
|
+
* @param filePath Path to the file (for error messages)
|
|
241
|
+
* @returns Parsed value
|
|
242
|
+
* @throws Error with context if parsing fails
|
|
243
|
+
*/
|
|
244
|
+
function safeParseJson(content, filePath) {
|
|
245
|
+
try {
|
|
246
|
+
return JSON.parse(content);
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
250
|
+
throw new Error(`Failed to parse snapshot file "${filePath}": ${message}`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Writes snapshot data to disk.
|
|
255
|
+
* Creates parent directories if they don't exist.
|
|
256
|
+
* Events are sorted by characteristics for consistent ordering.
|
|
257
|
+
* Uses deterministic JSON serialization with sorted keys for consistent diffs.
|
|
258
|
+
*
|
|
259
|
+
* @param snapshotPath Absolute path to the snapshot file
|
|
260
|
+
* @param data Array of events to write
|
|
261
|
+
*/
|
|
262
|
+
export function writeSnapshot(snapshotPath, data) {
|
|
263
|
+
const dir = dirname(snapshotPath);
|
|
264
|
+
if (!existsSync(dir)) {
|
|
265
|
+
mkdirSync(dir, { recursive: true });
|
|
266
|
+
}
|
|
267
|
+
const sortedData = sortEventsByCharacteristics(data);
|
|
268
|
+
const serialized = serializeSnapshot(sortedData);
|
|
269
|
+
writeFileSync(snapshotPath, serialized, "utf8");
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Serializes snapshot data to a deterministic JSON string.
|
|
273
|
+
* Sorts object keys to ensure consistent output across different Node.js versions.
|
|
274
|
+
*
|
|
275
|
+
* @param data Array of events to serialize
|
|
276
|
+
* @returns Pretty-printed JSON string with sorted keys
|
|
277
|
+
*/
|
|
278
|
+
function serializeSnapshot(data) {
|
|
279
|
+
return JSON.stringify(data, sortedReplacer, 2) + "\n";
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* JSON replacer function that sorts object keys for deterministic serialization.
|
|
283
|
+
*
|
|
284
|
+
* @param key Current key (unused)
|
|
285
|
+
* @param value Current value
|
|
286
|
+
* @returns Value with sorted keys if it's an object
|
|
287
|
+
*/
|
|
288
|
+
function sortedReplacer(key, value) {
|
|
289
|
+
if (!isRecord(value)) {
|
|
290
|
+
return value;
|
|
291
|
+
}
|
|
292
|
+
const sortedKeys = Object.keys(value).sort();
|
|
293
|
+
const sorted = {};
|
|
294
|
+
for (const sortedKey of sortedKeys) {
|
|
295
|
+
sorted[sortedKey] = value[sortedKey];
|
|
296
|
+
}
|
|
297
|
+
return sorted;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Sorts events by their characteristics signature for order-independent comparison.
|
|
301
|
+
* Events with the same characteristics are further sorted by other stable properties.
|
|
302
|
+
* This ensures consistent ordering regardless of event arrival order.
|
|
303
|
+
*
|
|
304
|
+
* @param events Array of events to sort
|
|
305
|
+
* @returns New array with events sorted by characteristics
|
|
306
|
+
*/
|
|
307
|
+
export function sortEventsByCharacteristics(events) {
|
|
308
|
+
return [...events].sort((a, b) => {
|
|
309
|
+
const sigA = getCharacteristicsSignature(a);
|
|
310
|
+
const sigB = getCharacteristicsSignature(b);
|
|
311
|
+
if (sigA !== sigB) {
|
|
312
|
+
return sigA.localeCompare(sigB);
|
|
313
|
+
}
|
|
314
|
+
// If characteristics are the same, sort by other stable properties
|
|
315
|
+
return getSecondarySignature(a).localeCompare(getSecondarySignature(b));
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Generates a signature string from an event's characteristics fields.
|
|
320
|
+
* Characteristics fields are boolean flags like "characteristics.has_navigation".
|
|
321
|
+
*
|
|
322
|
+
* @param event Event object
|
|
323
|
+
* @returns Signature string representing the event's characteristics
|
|
324
|
+
*/
|
|
325
|
+
function getCharacteristicsSignature(event) {
|
|
326
|
+
const characteristicsKeys = Object.keys(event)
|
|
327
|
+
.filter(key => key.startsWith("characteristics.") && event[key] === true)
|
|
328
|
+
.sort();
|
|
329
|
+
return characteristicsKeys.join(",");
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Generates a secondary signature for events with the same characteristics.
|
|
333
|
+
* Uses stable identifying properties like name, type, url, etc.
|
|
334
|
+
*
|
|
335
|
+
* @param event Event object
|
|
336
|
+
* @returns Secondary signature string
|
|
337
|
+
*/
|
|
338
|
+
function getSecondarySignature(event) {
|
|
339
|
+
// Keys must match RumEventKeys enum values for consistency
|
|
340
|
+
const stableKeys = [
|
|
341
|
+
"url.full",
|
|
342
|
+
"view.sequence.number",
|
|
343
|
+
"view.url.full",
|
|
344
|
+
"page.url.full",
|
|
345
|
+
"user_action.type",
|
|
346
|
+
"interaction.name",
|
|
347
|
+
"navigation.type",
|
|
348
|
+
"exception.type",
|
|
349
|
+
"exception.message"
|
|
350
|
+
];
|
|
351
|
+
return stableKeys
|
|
352
|
+
.map((key) => {
|
|
353
|
+
const value = getValueByKey(event, key);
|
|
354
|
+
if (value !== void 0) {
|
|
355
|
+
return `${key}:${String(value)}`;
|
|
356
|
+
}
|
|
357
|
+
return void 0;
|
|
358
|
+
})
|
|
359
|
+
.filter(Boolean)
|
|
360
|
+
.join("|");
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Gets a value from an object, trying both literal key and nested path.
|
|
364
|
+
*
|
|
365
|
+
* @param obj Object to get value from
|
|
366
|
+
* @param key Key (may be literal or dot-notation path)
|
|
367
|
+
* @returns Value or undefined
|
|
368
|
+
*/
|
|
369
|
+
function getValueByKey(obj, key) {
|
|
370
|
+
// First try literal key (e.g., "request.url" as a single key)
|
|
371
|
+
if (key in obj) {
|
|
372
|
+
return obj[key];
|
|
373
|
+
}
|
|
374
|
+
// Then try nested path (e.g., obj.request.url)
|
|
375
|
+
const parts = key.split(".");
|
|
376
|
+
let current = obj;
|
|
377
|
+
for (const part of parts) {
|
|
378
|
+
if (!isRecord(current) || !(part in current)) {
|
|
379
|
+
return void 0;
|
|
380
|
+
}
|
|
381
|
+
current = current[part];
|
|
382
|
+
}
|
|
383
|
+
return current;
|
|
384
|
+
}
|
|
385
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic25hcHNob3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvdGVzdGluZy9zbmFwc2hvdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBRUgsT0FBTyxFQUNILFVBQVUsRUFDVixTQUFTLEVBQ1QsWUFBWSxFQUNaLGFBQWEsRUFDaEIsTUFBTSxTQUFTLENBQUM7QUFDakIsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQWdFcEM7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLHNCQUFzQixHQUFzQjtJQUNyRCxxQkFBcUI7SUFDckIsWUFBWTtJQUNaLFVBQVU7SUFDVixXQUFXO0lBRVgsbUNBQW1DO0lBQ25DLFlBQVk7SUFDWixvQkFBb0I7SUFDcEIsb0JBQW9CO0lBQ3BCLGlCQUFpQjtJQUNqQixtQkFBbUI7SUFFbkIsWUFBWTtJQUNaLHNCQUFzQjtJQUN0QixrQkFBa0I7SUFDbEIsa0JBQWtCO0lBRWxCLGNBQWM7SUFDZCx5QkFBeUI7SUFDekIsd0NBQXdDO0lBQ3hDLHFDQUFxQztJQUNyQyxXQUFXO0lBRVgsZ0NBQWdDO0lBQ2hDLDJCQUEyQjtJQUMzQixrQ0FBa0M7SUFDbEMseUJBQXlCO0lBRXpCLDZCQUE2QjtJQUM3QixVQUFVO0lBQ1YsU0FBUztJQUVULGtCQUFrQjtJQUNsQixtQkFBbUI7SUFDbkIsc0JBQXNCO0lBQ3RCLDRCQUE0QjtDQUN0QixDQUFDO0FBRVg7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQXNCO0lBQ3JELDZEQUE2RDtJQUM3RCxlQUFlO0lBRWYsMkJBQTJCO0lBQzNCLE9BQU87SUFDUCxNQUFNO0lBQ04sUUFBUTtJQUNSLE9BQU87SUFDUCxPQUFPO0lBQ1AsT0FBTztJQUNQLE9BQU87SUFFUCxhQUFhO0lBQ2IsYUFBYTtJQUViLHdEQUF3RDtJQUN4RCxrQkFBa0I7SUFDbEIsaUJBQWlCO0lBQ2pCLHNCQUFzQjtDQUNoQixDQUFD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RSxDQUFDO1lBQ25CLE9BQU8sR0FBRyxHQUFHLElBQUksTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDckMsQ0FBQztRQUNELE9BQU8sS0FBSyxDQUFDLENBQUM7SUFDbEIsQ0FBQyxDQUFDO1NBQ0QsTUFBTSxDQUFDLE9BQU8sQ0FBQztTQUNmLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUNuQixDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsU0FBUyxhQUFhLENBQUMsR0FBNEIsRUFBRSxHQUFXO0lBQzVELDhEQUE4RDtJQUM5RCxJQUFJLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUNiLE9BQU8sR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3BCLENBQUM7SUFFRCwrQ0FBK0M7SUFDL0MsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM3QixJQUFJLE9BQU8sR0FBWSxHQUFHLENBQUM7SUFFM0IsS0FBSyxNQUFNLElBQUksSUFBSSxLQUFLLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxJQUFJLElBQUksT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUMzQyxPQUFPLEtBQUssQ0FBQyxDQUFDO1FBQ2xCLENBQUM7UUFDRCxPQUFPLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzVCLENBQUM7SUFFRCxPQUFPLE9BQU8sQ0FBQztBQUNuQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBTbmFwc2hvdCB0ZXN0aW5nIHV0aWxpdGllcyBmb3IgRHluYXRyYWNlIFJVTSBldmVudCB2ZXJpZmljYXRpb24uXG4gKiBQcm92aWRlcyBtZWNoYW5pc21zIHRvIGNvbXBhcmUgY2FwdHVyZWQgZXZlbnRzIGFnYWluc3Qgc3RvcmVkIHNuYXBzaG90c1xuICogd2l0aCBzdXBwb3J0IGZvciBtYXNraW5nIHZvbGF0aWxlIGZpZWxkcy5cbiAqL1xuXG5pbXBvcnQge1xuICAgIGV4aXN0c1N5bmMsXG4gICAgbWtkaXJTeW5jLFxuICAgIHJlYWRGaWxlU3luYyxcbiAgICB3cml0ZUZpbGVTeW5jXG59IGZyb20gXCJub2RlOmZzXCI7XG5pbXBvcnQgeyBkaXJuYW1lIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuXG4vKipcbiAqIFJlc3VsdCBvZiByZWFkaW5nIGEgc25hcHNob3QgZmlsZS5cbiAqL1xuaW50ZXJmYWNlIFNuYXBzaG90UmVhZFJlc3VsdCB7XG4gICAgLyoqXG4gICAgICogV2hldGhlciB0aGUgc25hcHNob3QgZmlsZSBleGlzdHMuXG4gICAgICovXG4gICAgZXhpc3RzOiBib29sZWFuO1xuXG4gICAgLyoqXG4gICAgICogVGhlIHBhcnNlZCBzbmFwc2hvdCBkYXRhLCBvciB1bmRlZmluZWQgaWYgdGhlIGZpbGUgZG9lc24ndCBleGlzdC5cbiAgICAgKi9cbiAgICBkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXTtcbn1cblxuLyoqXG4gKiBQYXR0ZXJuIHRvIG1hdGNoIGV2ZW50cyB0aGF0IHNob3VsZCBiZSBjb21wbGV0ZWx5IGlnbm9yZWQgZHVyaW5nIHNuYXBzaG90IGNvbXBhcmlzb24uXG4gKiBFdmVudHMgbWF0Y2hpbmcgYW55IG9mIHRoZXNlIHBhdHRlcm5zIHdpbGwgYmUgZmlsdGVyZWQgb3V0IGJlZm9yZSBjb21wYXJpc29uLlxuICovXG5leHBvcnQgdHlwZSBFdmVudElnbm9yZVBhdHRlcm4gPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcblxuLyoqXG4gKiBPcHRpb25zIGZvciBjb25maWd1cmluZyBzbmFwc2hvdCBjb21wYXJpc29uIGJlaGF2aW9yLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFNuYXBzaG90T3B0aW9ucyB7XG4gICAgLyoqXG4gICAgICogUHJvcGVydGllcyB0byBpZ25vcmUgYmVmb3JlIGNvbXBhcmlzb24uIFZhbHVlcyBhcmUgcmVwbGFjZWQgd2l0aCBcIltJR05PUkVEXVwiIHBsYWNlaG9sZGVyLlxuICAgICAqIFN1cHBvcnRzIGV4YWN0IGZpZWxkIG5hbWVzIGFuZCB3aWxkY2FyZCBwYXR0ZXJucyAoYHByZWZpeC4qYCkuXG4gICAgICpcbiAgICAgKiBFeGFtcGxlczpcbiAgICAgKiAtIGBcInN0YXJ0X3RpbWVcImAg4oaSIHZhbHVlIHJlcGxhY2VkIHdpdGggXCJbSUdOT1JFRF1cIlxuICAgICAqIC0gYFwiaW5wLipcImAg4oaSIGFsbCBmaWVsZHMgc3RhcnRpbmcgd2l0aCBgaW5wLmAgaGF2ZSB2YWx1ZXMgcmVwbGFjZWQgd2l0aCBcIltJR05PUkVEXVwiXG4gICAgICpcbiAgICAgKiBEZWZhdWx0OiBERUZBVUxUX0lHTk9SRURfRklFTERTXG4gICAgICovXG4gICAgaWdub3JlZEZpZWxkcz86IHN0cmluZ1tdO1xuXG4gICAgLyoqXG4gICAgICogUHJvcGVydGllcyB0byByZW1vdmUgZW50aXJlbHkgYmVmb3JlIGNvbXBhcmlzb24uXG4gICAgICogU3VwcG9ydHMgZXhhY3QgZmllbGQgbmFtZXMgYW5kIHdpbGRjYXJkIHBhdHRlcm5zIChgcHJlZml4LipgKS5cbiAgICAgKlxuICAgICAqIEV4YW1wbGVzOlxuICAgICAqIC0gYFwic3RhcnRfdGltZVwiYCDihpIgZmllbGQgaXMgcmVtb3ZlZCBmcm9tIHRoZSBldmVudFxuICAgICAqIC0gYFwiaW5wLipcImAg4oaSIGFsbCBmaWVsZHMgc3RhcnRpbmcgd2l0aCBgaW5wLmAgYXJlIHJlbW92ZWRcbiAgICAgKlxuICAgICAqIERlZmF1bHQ6IERFRkFVTFRfUkVNT1ZFRF9GSUVMRFNcbiAgICAgKi9cbiAgICByZW1vdmVkRmllbGRzPzogc3RyaW5nW107XG5cbiAgICAvKipcbiAgICAgKiBFdmVudCBwYXR0ZXJucyB0byBjb21wbGV0ZWx5IGlnbm9yZSBkdXJpbmcgc25hcHNob3QgY29tcGFyaXNvbi5cbiAgICAgKiBFdmVudHMgbWF0Y2hpbmcgYW55IG9mIHRoZXNlIHBhdHRlcm5zIHdpbGwgYmUgZmlsdGVyZWQgb3V0LlxuICAgICAqIERlZmF1bHQ6IERFRkFVTFRfSUdOT1JFRF9FVkVOVFNcbiAgICAgKi9cbiAgICBpZ25vcmVFdmVudHM/OiBFdmVudElnbm9yZVBhdHRlcm5bXTtcblxuICAgIC8qKlxuICAgICAqIEN1c3RvbSBzbmFwc2hvdCBuYW1lLiBJZiBub3QgcHJvdmlkZWQsIGF1dG8tZ2VuZXJhdGVkIGZyb20gdGVzdCBuYW1lLlxuICAgICAqL1xuICAgIG5hbWU/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogRGVmYXVsdCBwcm9wZXJ0aWVzIHRoYXQgYXJlIGlnbm9yZWQgKHJlcGxhY2VkIHdpdGggXCJbSUdOT1JFRF1cIikgaW4gc25hcHNob3RzIHRvIHByZXZlbnQgZmxha3kgdGVzdHMuXG4gKiBUaGVzZSBmaWVsZHMgdHlwaWNhbGx5IGNvbnRhaW4gdm9sYXRpbGUgdmFsdWVzIGxpa2UgdGltZXN0YW1wcywgSURzLCBhbmQgZHVyYXRpb25zXG4gKiB0aGF0IGNoYW5nZSBiZXR3ZWVuIHRlc3QgcnVucy5cbiAqL1xuZXhwb3J0IGNvbnN0IERFRkFVTFRfSUdOT1JFRF9GSUVMRFM6IHJlYWRvbmx5IHN0cmluZ1tdID0gW1xuICAgIC8vIEJhc2UgdGltaW5nIGZpZWxkc1xuICAgIFwic3RhcnRfdGltZVwiLFxuICAgIFwiZHVyYXRpb25cIixcbiAgICBcInRpbWVzdGFtcFwiLFxuXG4gICAgLy8gU2Vzc2lvbi9JbnN0YW5jZSBpZGVudGl0eSBmaWVsZHNcbiAgICBcImR0LnJ1bS5zaWRcIixcbiAgICBcImR0LnJ1bS5icm93c2VyLnNpZFwiLFxuICAgIFwiZHQucnVtLmluc3RhbmNlLmlkXCIsXG4gICAgXCJ1c2VyX3Nlc3Npb24uaWRcIixcbiAgICBcInVzZXIuYW5vbnltb3VzX2lkXCIsXG5cbiAgICAvLyBWaWV3L1BhZ2VcbiAgICBcInZpZXcuZm9yZWdyb3VuZF90aW1lXCIsXG4gICAgXCJ2aWV3Lmluc3RhbmNlX2lkXCIsXG4gICAgXCJwYWdlLmluc3RhbmNlX2lkXCIsXG5cbiAgICAvLyBVc2VyIGFjdGlvblxuICAgIFwidXNlcl9hY3Rpb24uaW5zdGFuY2VfaWRcIixcbiAgICBcInVzZXJfYWN0aW9uLmludGVycnVwdGVkX2J5X2luc3RhbmNlX2lkXCIsXG4gICAgXCJ1c2VyX2FjdGlvbi5pbnRlcnJ1cHRlZF9pbnN0YW5jZV9pZFwiLFxuICAgIFwicG9zaXRpb25zXCIsXG5cbiAgICAvLyBCcm93c2VyL0ZyYW1lIGluc3RhbmNlIGZpZWxkc1xuICAgIFwiYnJvd3Nlci5mcmFtZS5pbnN0YW5jZV9pZFwiLFxuICAgIFwiYnJvd3Nlci5mcmFtZS5wYXJlbnRfaW5zdGFuY2VfaWRcIixcbiAgICBcImJyb3dzZXIudGFiLmluc3RhbmNlX2lkXCIsXG5cbiAgICAvLyBEaXN0cmlidXRlZCB0cmFjaW5nIGZpZWxkc1xuICAgIFwidHJhY2UuaWRcIixcbiAgICBcInNwYW4uaWRcIixcblxuICAgIC8vIFNlbGYtbW9uaXRvcmluZ1xuICAgIFwiZHQucnVtLnNmbV9ldmVudHNcIixcbiAgICBcImR0LnJ1bS5hZ2VudC52ZXJzaW9uXCIsXG4gICAgXCJkdC5zdXBwb3J0Lmxhc3RfdXNlcl9pbnB1dFwiXG5dIGFzIGNvbnN0O1xuXG4vKipcbiAqIERlZmF1bHQgcHJvcGVydGllcyB0aGF0IGFyZSByZW1vdmVkIGVudGlyZWx5IGZyb20gc25hcHNob3RzIHRvIHByZXZlbnQgZmxha3kgdGVzdHMuXG4gKiBUaGVzZSBmaWVsZHMgdmFyeSBpbiBjb3VudCBvciBwcmVzZW5jZSBiZXR3ZWVuIHRlc3QgcnVucy5cbiAqL1xuZXhwb3J0IGNvbnN0IERFRkFVTFRfUkVNT1ZFRF9GSUVMRFM6IHJlYWRvbmx5IHN0cmluZ1tdID0gW1xuICAgIC8vIFBlcmZvcm1hbmNlIHRpbWluZyBmaWVsZHMgKFczQyBOYXZpZ2F0aW9uL1Jlc291cmNlIFRpbWluZylcbiAgICBcInBlcmZvcm1hbmNlLipcIixcblxuICAgIC8vIFdlYiBWaXRhbHMgdGltaW5nIGZpZWxkc1xuICAgIFwiZmNwLipcIixcbiAgICBcImZwLipcIixcbiAgICBcInR0ZmIuKlwiLFxuICAgIFwibGNwLipcIixcbiAgICBcImZpZC4qXCIsXG4gICAgXCJpbnAuKlwiLFxuICAgIFwiY2xzLipcIixcblxuICAgIC8vIExvbmcgdGFza3NcbiAgICBcImxvbmdfdGFzay4qXCIsXG5cbiAgICAvLyBWaWV3cG9ydC9zY3JlZW4gZGltZW5zaW9ucyAodmFyeSBieSB0ZXN0IGVudmlyb25tZW50KVxuICAgIFwiYnJvd3Nlci53aW5kb3cuKlwiLFxuICAgIFwiZGV2aWNlLnNjcmVlbi4qXCIsXG4gICAgXCJkZXZpY2UuYmF0dGVyeS5sZXZlbFwiXG5dIGFzIGNvbnN0O1xuXG4vKipcbiAqIERlZmF1bHQgZXZlbnQgcGF0dGVybnMgdGhhdCBhcmUgaWdub3JlZCBkdXJpbmcgc25hcHNob3QgY29tcGFyaXNvbi5cbiAqIFRoZXNlIGV2ZW50cyBhcmUgdm9sYXRpbGUgYW5kIG1heSBvciBtYXkgbm90IGJlIHJlY29yZGVkIGRlcGVuZGluZyBvbiBicm93c2VyIHBlcmZvcm1hbmNlLlxuICovXG5leHBvcnQgY29uc3QgREVGQVVMVF9JR05PUkVEX0VWRU5UUzogcmVhZG9ubHkgRXZlbnRJZ25vcmVQYXR0ZXJuW10gPSBbXG4gICAgeyBcImNoYXJhY3RlcmlzdGljcy5oYXNfbG9uZ190YXNrXCI6IHRydWUgfSxcbiAgICB7IFwiY2hhcmFjdGVyaXN0aWNzLmlzX3NlbGZfbW9uaXRvcmluZ1wiOiB0cnVlIH1cbl07XG5cbi8qKlxuICogUGxhY2Vob2xkZXIgdmFsdWUgdXNlZCB0byByZXBsYWNlIGlnbm9yZWQgcHJvcGVydHkgdmFsdWVzLlxuICovXG5leHBvcnQgY29uc3QgSUdOT1JFRF9QTEFDRUhPTERFUiA9IFwiW0lHTk9SRURdXCI7XG5cbi8qKlxuICogUHJvY2Vzc2VzIGV2ZW50cyBieSBpZ25vcmluZyBhbmQgcmVtb3Zpbmcgc3BlY2lmaWVkIHByb3BlcnRpZXMuXG4gKiAtIElnbm9yZWQgcHJvcGVydGllcyBoYXZlIHRoZWlyIHZhbHVlcyByZXBsYWNlZCB3aXRoIFwiW0lHTk9SRURdXCIgcGxhY2Vob2xkZXJcbiAqIC0gUmVtb3ZlZCBwcm9wZXJ0aWVzIGFyZSBkZWxldGVkIGVudGlyZWx5IGZyb20gdGhlIGV2ZW50XG4gKlxuICogQHBhcmFtIGV2ZW50cyAgICAgICAgQXJyYXkgb2YgZXZlbnQgb2JqZWN0cyB0byBwcm9jZXNzXG4gKiBAcGFyYW0gaWdub3JlZEZpZWxkcyBBcnJheSBvZiBmaWVsZCBwYXRocyB0byBpZ25vcmUgKHZhbHVlcyByZXBsYWNlZCB3aXRoIHBsYWNlaG9sZGVyKVxuICogQHBhcmFtIHJlbW92ZWRGaWVsZHMgQXJyYXkgb2YgZmllbGQgcGF0aHMgdG8gcmVtb3ZlIGVudGlyZWx5XG4gKiBAcmV0dXJucyAgICAgICAgICAgICBEZWVwIGNvcHkgb2YgZXZlbnRzIHdpdGggc3BlY2lmaWVkIGZpZWxkcyBwcm9jZXNzZWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByb2Nlc3NFdmVudFByb3BlcnRpZXMoXG4gICAgZXZlbnRzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPltdLFxuICAgIGlnbm9yZWRGaWVsZHM6IHJlYWRvbmx5IHN0cmluZ1tdID0gREVGQVVMVF9JR05PUkVEX0ZJRUxEUyxcbiAgICByZW1vdmVkRmllbGRzOiByZWFkb25seSBzdHJpbmdbXSA9IERFRkFVTFRfUkVNT1ZFRF9GSUVMRFNcbik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+W10ge1xuICAgIHJldHVybiBldmVudHMubWFwKGV2ZW50ID0+IHByb2Nlc3NTaW5nbGVFdmVudChldmVudCwgaWdub3JlZEZpZWxkcywgcmVtb3ZlZEZpZWxkcykpO1xufVxuXG4vKipcbiAqIEZpbHRlcnMgb3V0IGV2ZW50cyB0aGF0IG1hdGNoIGFueSBvZiB0aGUgaWdub3JlIHBhdHRlcm5zLlxuICogQW4gZXZlbnQgbWF0Y2hlcyBhIHBhdHRlcm4gaWYgYWxsIHByb3BlcnRpZXMgaW4gdGhlIHBhdHRlcm4gZXhpc3QgaW4gdGhlIGV2ZW50IHdpdGggdGhlIHNhbWUgdmFsdWVzLlxuICpcbiAqIEBwYXJhbSBldmVudHMgICAgICAgICBBcnJheSBvZiBldmVudHMgdG8gZmlsdGVyXG4gKiBAcGFyYW0gaWdub3JlUGF0dGVybnMgQXJyYXkgb2YgcGF0dGVybnMgdG8gbWF0Y2ggYWdhaW5zdFxuICogQHJldHVybnMgICAgICAgICAgICAgIEZpbHRlcmVkIGFycmF5IGV4Y2x1ZGluZyBldmVudHMgdGhhdCBtYXRjaCBhbnkgcGF0dGVyblxuICovXG5leHBvcnQgZnVuY3Rpb24gZmlsdGVySWdub3JlZEV2ZW50cyhcbiAgICBldmVudHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+W10sXG4gICAgaWdub3JlUGF0dGVybnM6IHJlYWRvbmx5IEV2ZW50SWdub3JlUGF0dGVybltdID0gREVGQVVMVF9JR05PUkVEX0VWRU5UU1xuKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSB7XG4gICAgaWYgKGlnbm9yZVBhdHRlcm5zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICByZXR1cm4gZXZlbnRzO1xuICAgIH1cblxuICAgIHJldHVybiBldmVudHMuZmlsdGVyKGV2ZW50ID0+ICFtYXRjaGVzQW55UGF0dGVybihldmVudCwgaWdub3JlUGF0dGVybnMpKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYW4gZXZlbnQgbWF0Y2hlcyBhbnkgb2YgdGhlIHByb3ZpZGVkIHBhdHRlcm5zLlxuICpcbiAqIEBwYXJhbSBldmVudCAgICBFdmVudCB0byBjaGVja1xuICogQHBhcmFtIHBhdHRlcm5zIFBhdHRlcm5zIHRvIG1hdGNoIGFnYWluc3RcbiAqIEByZXR1cm5zICAgICAgICBUcnVlIGlmIHRoZSBldmVudCBtYXRjaGVzIGFueSBwYXR0ZXJuXG4gKi9cbmZ1bmN0aW9uIG1hdGNoZXNBbnlQYXR0ZXJuKFxuICAgIGV2ZW50OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICBwYXR0ZXJuczogcmVhZG9ubHkgRXZlbnRJZ25vcmVQYXR0ZXJuW11cbik6IGJvb2xlYW4ge1xuICAgIHJldHVybiBwYXR0ZXJucy5zb21lKHBhdHRlcm4gPT4gbWF0Y2hlc1BhdHRlcm4oZXZlbnQsIHBhdHRlcm4pKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYW4gZXZlbnQgbWF0Y2hlcyBhIHNpbmdsZSBwYXR0ZXJuLlxuICogQSBtYXRjaCBvY2N1cnMgd2hlbiBhbGwgcHJvcGVydGllcyBpbiB0aGUgcGF0dGVybiBleGlzdCBpbiB0aGUgZXZlbnQgd2l0aCBlcXVhbCB2YWx1ZXMuXG4gKlxuICogQHBhcmFtIGV2ZW50ICAgRXZlbnQgdG8gY2hlY2tcbiAqIEBwYXJhbSBwYXR0ZXJuIFBhdHRlcm4gdG8gbWF0Y2ggYWdhaW5zdFxuICogQHJldHVybnMgICAgICAgVHJ1ZSBpZiBhbGwgcGF0dGVybiBwcm9wZXJ0aWVzIG1hdGNoXG4gKi9cbmZ1bmN0aW9uIG1hdGNoZXNQYXR0ZXJuKFxuICAgIGV2ZW50OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgICBwYXR0ZXJuOiBFdmVudElnbm9yZVBhdHRlcm5cbik6IGJvb2xlYW4ge1xuICAgIHJldHVybiBPYmplY3Qua2V5cyhwYXR0ZXJuKS5ldmVyeShrZXkgPT4ga2V5IGluIGV2ZW50ICYmIGV2ZW50W2tleV0gPT09IHBhdHRlcm5ba2V5XSk7XG59XG5cbi8qKlxuICogUHJvY2Vzc2VzIGEgc2luZ2xlIGV2ZW50IGJ5IGlnbm9yaW5nIGFuZCByZW1vdmluZyBzcGVjaWZpZWQgcHJvcGVydGllcy5cbiAqIC0gSWdub3JlZCBwcm9wZXJ0aWVzOiB2YWx1ZXMgcmVwbGFjZWQgd2l0aCBcIltJR05PUkVEXVwiIHBsYWNlaG9sZGVyXG4gKiAtIFJlbW92ZWQgcHJvcGVydGllczogZmllbGRzIGRlbGV0ZWQgZW50aXJlbHlcbiAqIEJvdGggc3VwcG9ydCBleGFjdCBmaWVsZCBuYW1lcyBhbmQgd2lsZGNhcmQgcGF0dGVybnMgKGBwcmVmaXguKmApLlxuICpcbiAqIEBwYXJhbSBldmVudCAgICAgICAgIEV2ZW50IG9iamVjdCB0byBwcm9jZXNzXG4gKiBAcGFyYW0gaWdub3JlZEZpZWxkcyBBcnJheSBvZiBmaWVsZCBuYW1lcy9wYXR0ZXJucyB0byBpZ25vcmUgKHJlcGxhY2UgdmFsdWVzKVxuICogQHBhcmFtIHJlbW92ZWRGaWVsZHMgQXJyYXkgb2YgZmllbGQgbmFtZXMvcGF0dGVybnMgdG8gcmVtb3ZlIGVudGlyZWx5XG4gKiBAcmV0dXJucyAgICAgICAgICAgICBEZWVwIGNvcHkgb2YgZXZlbnQgd2l0aCBmaWVsZHMgcHJvY2Vzc2VkXG4gKi9cbmZ1bmN0aW9uIHByb2Nlc3NTaW5nbGVFdmVudChcbiAgICBldmVudDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICAgaWdub3JlZEZpZWxkczogcmVhZG9ubHkgc3RyaW5nW10sXG4gICAgcmVtb3ZlZEZpZWxkczogcmVhZG9ubHkgc3RyaW5nW11cbik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgICBjb25zdCByZXN1bHQgPSBkZWVwQ2xvbmUoZXZlbnQpO1xuXG4gICAgLy8gUHJvY2VzcyBpZ25vcmVkIHByb3BlcnRpZXMgKHJlcGxhY2UgdmFsdWVzIHdpdGggcGxhY2Vob2xkZXIpXG4gICAgZm9yIChjb25zdCBmaWVsZFBhdGggb2YgaWdub3JlZEZpZWxkcykge1xuICAgICAgICBpZiAoZmllbGRQYXRoLmVuZHNXaXRoKFwiLipcIikpIHtcbiAgICAgICAgICAgIGNvbnN0IHByZWZpeCA9IGZpZWxkUGF0aC5zbGljZSgwLCAtMSk7IC8vIFJlbW92ZSBcIipcIiwga2VlcCB0aGUgZG90XG4gICAgICAgICAgICBpZ25vcmVGaWVsZHNXaXRoUHJlZml4KHJlc3VsdCwgcHJlZml4KTtcbiAgICAgICAgfSBlbHNlIGlmIChmaWVsZFBhdGggaW4gcmVzdWx0KSB7XG4gICAgICAgICAgICByZXN1bHRbZmllbGRQYXRoXSA9IElHTk9SRURfUExBQ0VIT0xERVI7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBQcm9jZXNzIHJlbW92ZWQgcHJvcGVydGllcyAoZGVsZXRlIGVudGlyZWx5KVxuICAgIGZvciAoY29uc3QgZmllbGRQYXRoIG9mIHJlbW92ZWRGaWVsZHMpIHtcbiAgICAgICAgaWYgKGZpZWxkUGF0aC5lbmRzV2l0aChcIi4qXCIpKSB7XG4gICAgICAgICAgICBjb25zdCBwcmVmaXggPSBmaWVsZFBhdGguc2xpY2UoMCwgLTEpOyAvLyBSZW1vdmUgXCIqXCIsIGtlZXAgdGhlIGRvdFxuICAgICAgICAgICAgcmVtb3ZlRmllbGRzV2l0aFByZWZpeChyZXN1bHQsIHByZWZpeCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBkZWxldGUgcmVzdWx0W2ZpZWxkUGF0aF07XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gcmVzdWx0O1xufVxuXG4vKipcbiAqIFJlcGxhY2VzIHZhbHVlcyBvZiBhbGwgZmllbGRzIGluIGFuIG9iamVjdCB0aGF0IHN0YXJ0IHdpdGggdGhlIGdpdmVuIHByZWZpeCB3aXRoIHRoZSBpZ25vcmVkIHBsYWNlaG9sZGVyLlxuICogVXNlZCBmb3Igd2lsZGNhcmQgcGF0dGVybnMgbGlrZSBgaW5wLipgIHRvIGlnbm9yZSBgaW5wLnN0YXJ0X3RpbWVgLCBgaW5wLmR1cmF0aW9uYCwgZXRjLlxuICpcbiAqIEBwYXJhbSBvYmogICAgT2JqZWN0IHRvIG1vZGlmeVxuICogQHBhcmFtIHByZWZpeCBQcmVmaXggdG8gbWF0Y2ggKGluY2x1ZGluZyB0cmFpbGluZyBkb3QsIGUuZy4sIFwiaW5wLlwiKVxuICovXG5mdW5jdGlvbiBpZ25vcmVGaWVsZHNXaXRoUHJlZml4KG9iajogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sIHByZWZpeDogc3RyaW5nKTogdm9pZCB7XG4gICAgZm9yIChjb25zdCBrZXkgb2YgT2JqZWN0LmtleXMob2JqKSkge1xuICAgICAgICBpZiAoa2V5LnN0YXJ0c1dpdGgocHJlZml4KSkge1xuICAgICAgICAgICAgb2JqW2tleV0gPSBJR05PUkVEX1BMQUNFSE9MREVSO1xuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIFJlbW92ZXMgYWxsIGZpZWxkcyBpbiBhbiBvYmplY3QgdGhhdCBzdGFydCB3aXRoIHRoZSBnaXZlbiBwcmVmaXguXG4gKiBVc2VkIGZvciB3aWxkY2FyZCBwYXR0ZXJucyBsaWtlIGBpbnAuKmAgdG8gcmVtb3ZlIGBpbnAuc3RhcnRfdGltZWAsIGBpbnAuZHVyYXRpb25gLCBldGMuXG4gKlxuICogQHBhcmFtIG9iaiAgICBPYmplY3QgdG8gbW9kaWZ5XG4gKiBAcGFyYW0gcHJlZml4IFByZWZpeCB0byBtYXRjaCAoaW5jbHVkaW5nIHRyYWlsaW5nIGRvdCwgZS5nLiwgXCJpbnAuXCIpXG4gKi9cbmZ1bmN0aW9uIHJlbW92ZUZpZWxkc1dpdGhQcmVmaXgob2JqOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiwgcHJlZml4OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBmb3IgKGNvbnN0IGtleSBvZiBPYmplY3Qua2V5cyhvYmopKSB7XG4gICAgICAgIGlmIChrZXkuc3RhcnRzV2l0aChwcmVmaXgpKSB7XG4gICAgICAgICAgICBkZWxldGUgb2JqW2tleV07XG4gICAgICAgIH1cbiAgICB9XG59XG5cbi8qKlxuICogVHlwZSBndWFyZCB0byBjaGVjayBpZiBhIHZhbHVlIGlzIGEgUmVjb3JkPHN0cmluZywgdW5rbm93bj4uXG4gKlxuICogQHBhcmFtIHZhbHVlIFZhbHVlIHRvIGNoZWNrXG4gKiBAcmV0dXJucyAgICAgVHJ1ZSBpZiB2YWx1ZSBpcyBhIG5vbi1udWxsIG9iamVjdFxuICovXG5mdW5jdGlvbiBpc1JlY29yZCh2YWx1ZTogdW5rbm93bik6IHZhbHVlIGlzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSBcIm9iamVjdFwiICYmIHZhbHVlICE9PSBudWxsICYmICFBcnJheS5pc0FycmF5KHZhbHVlKTtcbn1cblxuLyoqXG4gKiBUeXBlIGd1YXJkIHRvIGNoZWNrIGlmIGEgdmFsdWUgaXMgYW4gYXJyYXkgb2YgUmVjb3JkPHN0cmluZywgdW5rbm93bj4uXG4gKlxuICogQHBhcmFtIHZhbHVlIFZhbHVlIHRvIGNoZWNrXG4gKiBAcmV0dXJucyAgICAgVHJ1ZSBpZiB2YWx1ZSBpcyBhbiBhcnJheSB3aGVyZSBhbGwgZWxlbWVudHMgYXJlIHJlY29yZHNcbiAqL1xuZnVuY3Rpb24gaXNSZWNvcmRBcnJheSh2YWx1ZTogdW5rbm93bik6IHZhbHVlIGlzIFJlY29yZDxzdHJpbmcsIHVua25vd24+W10ge1xuICAgIHJldHVybiBBcnJheS5pc0FycmF5KHZhbHVlKSAmJiB2YWx1ZS5ldmVyeShpc1JlY29yZCk7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIGRlZXAgY2xvbmUgb2YgYW4gb2JqZWN0LlxuICpcbiAqIEBwYXJhbSBvYmogT2JqZWN0IHRvIGNsb25lXG4gKiBAcmV0dXJucyAgIERlZXAgY2xvbmUgb2YgdGhlIG9iamVjdFxuICovXG5mdW5jdGlvbiBkZWVwQ2xvbmU8VD4ob2JqOiBUKTogVCB7XG4gICAgcmV0dXJuIEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkob2JqKSk7XG59XG5cbi8qKlxuICogUmVhZHMgYSBzbmFwc2hvdCBmaWxlIGZyb20gZGlzay5cbiAqIFJldHVybnMgYW4gb2JqZWN0IGluZGljYXRpbmcgd2hldGhlciB0aGUgZmlsZSBleGlzdHMgYW5kIGl0cyBwYXJzZWQgY29udGVudHMuXG4gKlxuICogQHBhcmFtIHNuYXBzaG90UGF0aCBBYnNvbHV0ZSBwYXRoIHRvIHRoZSBzbmFwc2hvdCBmaWxlXG4gKiBAcmV0dXJucyAgICAgICAgICAgIE9iamVjdCB3aXRoIGV4aXN0cyBmbGFnIGFuZCBwYXJzZWQgZGF0YVxuICogQHRocm93cyAgICAgICAgICAgICBFcnJvciBpZiB0aGUgZmlsZSBjb250YWlucyBpbnZhbGlkIEpTT04gb3IgaXMgbm90IGFuIGFycmF5IG9mIHJlY29yZHNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlYWRTbmFwc2hvdChzbmFwc2hvdFBhdGg6IHN0cmluZyk6IFNuYXBzaG90UmVhZFJlc3VsdCB7XG4gICAgaWYgKCFleGlzdHNTeW5jKHNuYXBzaG90UGF0aCkpIHtcbiAgICAgICAgcmV0dXJuIHsgZXhpc3RzOiBmYWxzZSB9O1xuICAgIH1cblxuICAgIGNvbnN0IGNvbnRlbnQgPSByZWFkRmlsZVN5bmMoc25hcHNob3RQYXRoLCBcInV0ZjhcIik7XG4gICAgY29uc3QgcGFyc2VkID0gc2FmZVBhcnNlSnNvbihjb250ZW50LCBzbmFwc2hvdFBhdGgpO1xuXG4gICAgaWYgKCFpc1JlY29yZEFycmF5KHBhcnNlZCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgICAgYEludmFsaWQgc25hcHNob3QgZm9ybWF0IGluIFwiJHtzbmFwc2hvdFBhdGh9XCI6IGV4cGVjdGVkIGFuIGFycmF5IG9mIG9iamVjdHNgXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHsgZXhpc3RzOiB0cnVlLCBkYXRhOiBwYXJzZWQgfTtcbn1cblxuLyoqXG4gKiBTYWZlbHkgcGFyc2VzIGEgSlNPTiBzdHJpbmcsIHdyYXBwaW5nIGVycm9ycyB3aXRoIGNvbnRleHQuXG4gKlxuICogQHBhcmFtIGNvbnRlbnQgIEpTT04gc3RyaW5nIHRvIHBhcnNlXG4gKiBAcGFyYW0gZmlsZVBhdGggUGF0aCB0byB0aGUgZmlsZSAoZm9yIGVycm9yIG1lc3NhZ2VzKVxuICogQHJldHVybnMgICAgICAgIFBhcnNlZCB2YWx1ZVxuICogQHRocm93cyAgICAgICAgIEVycm9yIHdpdGggY29udGV4dCBpZiBwYXJzaW5nIGZhaWxzXG4gKi9cbmZ1bmN0aW9uIHNhZmVQYXJzZUpzb24oY29udGVudDogc3RyaW5nLCBmaWxlUGF0aDogc3RyaW5nKTogdW5rbm93biB7XG4gICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIEpTT04ucGFyc2UoY29udGVudCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgY29uc3QgbWVzc2FnZSA9IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKTtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gcGFyc2Ugc25hcHNob3QgZmlsZSBcIiR7ZmlsZVBhdGh9XCI6ICR7bWVzc2FnZX1gKTtcbiAgICB9XG59XG5cbi8qKlxuICogV3JpdGVzIHNuYXBzaG90IGRhdGEgdG8gZGlzay5cbiAqIENyZWF0ZXMgcGFyZW50IGRpcmVjdG9yaWVzIGlmIHRoZXkgZG9uJ3QgZXhpc3QuXG4gKiBFdmVudHMgYXJlIHNvcnRlZCBieSBjaGFyYWN0ZXJpc3RpY3MgZm9yIGNvbnNpc3RlbnQgb3JkZXJpbmcuXG4gKiBVc2VzIGRldGVybWluaXN0aWMgSlNPTiBzZXJpYWxpemF0aW9uIHdpdGggc29ydGVkIGtleXMgZm9yIGNvbnNpc3RlbnQgZGlmZnMuXG4gKlxuICogQHBhcmFtIHNuYXBzaG90UGF0aCBBYnNvbHV0ZSBwYXRoIHRvIHRoZSBzbmFwc2hvdCBmaWxlXG4gKiBAcGFyYW0gZGF0YSAgICAgICAgIEFycmF5IG9mIGV2ZW50cyB0byB3cml0ZVxuICovXG5leHBvcnQgZnVuY3Rpb24gd3JpdGVTbmFwc2hvdChzbmFwc2hvdFBhdGg6IHN0cmluZywgZGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSk6IHZvaWQge1xuICAgIGNvbnN0IGRpciA9IGRpcm5hbWUoc25hcHNob3RQYXRoKTtcbiAgICBpZiAoIWV4aXN0c1N5bmMoZGlyKSkge1xuICAgICAgICBta2RpclN5bmMoZGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICB9XG5cbiAgICBjb25zdCBzb3J0ZWREYXRhID0gc29ydEV2ZW50c0J5Q2hhcmFjdGVyaXN0aWNzKGRhdGEpO1xuICAgIGNvbnN0IHNlcmlhbGl6ZWQgPSBzZXJpYWxpemVTbmFwc2hvdChzb3J0ZWREYXRhKTtcbiAgICB3cml0ZUZpbGVTeW5jKHNuYXBzaG90UGF0aCwgc2VyaWFsaXplZCwgXCJ1dGY4XCIpO1xufVxuXG4vKipcbiAqIFNlcmlhbGl6ZXMgc25hcHNob3QgZGF0YSB0byBhIGRldGVybWluaXN0aWMgSlNPTiBzdHJpbmcuXG4gKiBTb3J0cyBvYmplY3Qga2V5cyB0byBlbnN1cmUgY29uc2lzdGVudCBvdXRwdXQgYWNyb3NzIGRpZmZlcmVudCBOb2RlLmpzIHZlcnNpb25zLlxuICpcbiAqIEBwYXJhbSBkYXRhIEFycmF5IG9mIGV2ZW50cyB0byBzZXJpYWxpemVcbiAqIEByZXR1cm5zICAgIFByZXR0eS1wcmludGVkIEpTT04gc3RyaW5nIHdpdGggc29ydGVkIGtleXNcbiAqL1xuZnVuY3Rpb24gc2VyaWFsaXplU25hcHNob3QoZGF0YTogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSk6IHN0cmluZyB7XG4gICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KGRhdGEsIHNvcnRlZFJlcGxhY2VyLCAyKSArIFwiXFxuXCI7XG59XG5cbi8qKlxuICogSlNPTiByZXBsYWNlciBmdW5jdGlvbiB0aGF0IHNvcnRzIG9iamVjdCBrZXlzIGZvciBkZXRlcm1pbmlzdGljIHNlcmlhbGl6YXRpb24uXG4gKlxuICogQHBhcmFtIGtleSAgIEN1cnJlbnQga2V5ICh1bnVzZWQpXG4gKiBAcGFyYW0gdmFsdWUgQ3VycmVudCB2YWx1ZVxuICogQHJldHVybnMgICAgIFZhbHVlIHdpdGggc29ydGVkIGtleXMgaWYgaXQncyBhbiBvYmplY3RcbiAqL1xuZnVuY3Rpb24gc29ydGVkUmVwbGFjZXIoa2V5OiBzdHJpbmcsIHZhbHVlOiB1bmtub3duKTogdW5rbm93biB7XG4gICAgaWYgKCFpc1JlY29yZCh2YWx1ZSkpIHtcbiAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIH1cblxuICAgIGNvbnN0IHNvcnRlZEtleXMgPSBPYmplY3Qua2V5cyh2YWx1ZSkuc29ydCgpO1xuICAgIGNvbnN0IHNvcnRlZDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcbiAgICBmb3IgKGNvbnN0IHNvcnRlZEtleSBvZiBzb3J0ZWRLZXlzKSB7XG4gICAgICAgIHNvcnRlZFtzb3J0ZWRLZXldID0gdmFsdWVbc29ydGVkS2V5XTtcbiAgICB9XG4gICAgcmV0dXJuIHNvcnRlZDtcbn1cblxuLyoqXG4gKiBTb3J0cyBldmVudHMgYnkgdGhlaXIgY2hhcmFjdGVyaXN0aWNzIHNpZ25hdHVyZSBmb3Igb3JkZXItaW5kZXBlbmRlbnQgY29tcGFyaXNvbi5cbiAqIEV2ZW50cyB3aXRoIHRoZSBzYW1lIGNoYXJhY3RlcmlzdGljcyBhcmUgZnVydGhlciBzb3J0ZWQgYnkgb3RoZXIgc3RhYmxlIHByb3BlcnRpZXMuXG4gKiBUaGlzIGVuc3VyZXMgY29uc2lzdGVudCBvcmRlcmluZyByZWdhcmRsZXNzIG9mIGV2ZW50IGFycml2YWwgb3JkZXIuXG4gKlxuICogQHBhcmFtIGV2ZW50cyBBcnJheSBvZiBldmVudHMgdG8gc29ydFxuICogQHJldHVybnMgICAgICBOZXcgYXJyYXkgd2l0aCBldmVudHMgc29ydGVkIGJ5IGNoYXJhY3RlcmlzdGljc1xuICovXG5leHBvcnQgZnVuY3Rpb24gc29ydEV2ZW50c0J5Q2hhcmFjdGVyaXN0aWNzKGV2ZW50czogUmVjb3JkPHN0cmluZywgdW5rbm93bj5bXSk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+W10ge1xuICAgIHJldHVybiBbLi4uZXZlbnRzXS5zb3J0KChhLCBiKSA9PiB7XG4gICAgICAgIGNvbnN0IHNpZ0EgPSBnZXRDaGFyYWN0ZXJpc3RpY3NTaWduYXR1cmUoYSk7XG4gICAgICAgIGNvbnN0IHNpZ0IgPSBnZXRDaGFyYWN0ZXJpc3RpY3NTaWduYXR1cmUoYik7XG5cbiAgICAgICAgaWYgKHNpZ0EgIT09IHNpZ0IpIHtcbiAgICAgICAgICAgIHJldHVybiBzaWdBLmxvY2FsZUNvbXBhcmUoc2lnQik7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBJZiBjaGFyYWN0ZXJpc3RpY3MgYXJlIHRoZSBzYW1lLCBzb3J0IGJ5IG90aGVyIHN0YWJsZSBwcm9wZXJ0aWVzXG4gICAgICAgIHJldHVybiBnZXRTZWNvbmRhcnlTaWduYXR1cmUoYSkubG9jYWxlQ29tcGFyZShnZXRTZWNvbmRhcnlTaWduYXR1cmUoYikpO1xuICAgIH0pO1xufVxuXG4vKipcbiAqIEdlbmVyYXRlcyBhIHNpZ25hdHVyZSBzdHJpbmcgZnJvbSBhbiBldmVudCdzIGNoYXJhY3RlcmlzdGljcyBmaWVsZHMuXG4gKiBDaGFyYWN0ZXJpc3RpY3MgZmllbGRzIGFyZSBib29sZWFuIGZsYWdzIGxpa2UgXCJjaGFyYWN0ZXJpc3RpY3MuaGFzX25hdmlnYXRpb25cIi5cbiAqXG4gKiBAcGFyYW0gZXZlbnQgRXZlbnQgb2JqZWN0XG4gKiBAcmV0dXJucyAgICAgU2lnbmF0dXJlIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIGV2ZW50J3MgY2hhcmFjdGVyaXN0aWNzXG4gKi9cbmZ1bmN0aW9uIGdldENoYXJhY3RlcmlzdGljc1NpZ25hdHVyZShldmVudDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBzdHJpbmcge1xuICAgIGNvbnN0IGNoYXJhY3RlcmlzdGljc0tleXMgPSBPYmplY3Qua2V5cyhldmVudClcbiAgICAgICAgLmZpbHRlcihrZXkgPT4ga2V5LnN0YXJ0c1dpdGgoXCJjaGFyYWN0ZXJpc3RpY3MuXCIpICYmIGV2ZW50W2tleV0gPT09IHRydWUpXG4gICAgICAgIC5zb3J0KCk7XG5cbiAgICByZXR1cm4gY2hhcmFjdGVyaXN0aWNzS2V5cy5qb2luKFwiLFwiKTtcbn1cblxuLyoqXG4gKiBHZW5lcmF0ZXMgYSBzZWNvbmRhcnkgc2lnbmF0dXJlIGZvciBldmVudHMgd2l0aCB0aGUgc2FtZSBjaGFyYWN0ZXJpc3RpY3MuXG4gKiBVc2VzIHN0YWJsZSBpZGVudGlmeWluZyBwcm9wZXJ0aWVzIGxpa2UgbmFtZSwgdHlwZSwgdXJsLCBldGMuXG4gKlxuICogQHBhcmFtIGV2ZW50IEV2ZW50IG9iamVjdFxuICogQHJldHVybnMgICAgIFNlY29uZGFyeSBzaWduYXR1cmUgc3RyaW5nXG4gKi9cbmZ1bmN0aW9uIGdldFNlY29uZGFyeVNpZ25hdHVyZShldmVudDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBzdHJpbmcge1xuICAgIC8vIEtleXMgbXVzdCBtYXRjaCBSdW1FdmVudEtleXMgZW51bSB2YWx1ZXMgZm9yIGNvbnNpc3RlbmN5XG4gICAgY29uc3Qgc3RhYmxlS2V5cyA9IFtcbiAgICAgICAgXCJ1cmwuZnVsbFwiLFxuICAgICAgICBcInZpZXcuc2VxdWVuY2UubnVtYmVyXCIsXG4gICAgICAgIFwidmlldy51cmwuZnVsbFwiLFxuICAgICAgICBcInBhZ2UudXJsLmZ1bGxcIixcbiAgICAgICAgXCJ1c2VyX2FjdGlvbi50eXBlXCIsXG4gICAgICAgIFwiaW50ZXJhY3Rpb24ubmFtZVwiLFxuICAgICAgICBcIm5hdmlnYXRpb24udHlwZVwiLFxuICAgICAgICBcImV4Y2VwdGlvbi50eXBlXCIsXG4gICAgICAgIFwiZXhjZXB0aW9uLm1lc3NhZ2VcIlxuICAgIF07XG5cbiAgICByZXR1cm4gc3RhYmxlS2V5c1xuICAgICAgICAubWFwKChrZXkpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHZhbHVlID0gZ2V0VmFsdWVCeUtleShldmVudCwga2V5KTtcbiAgICAgICAgICAgIGlmICh2YWx1ZSAhPT0gdm9pZCAwKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGAke2tleX06JHtTdHJpbmcodmFsdWUpfWA7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gdm9pZCAwO1xuICAgICAgICB9KVxuICAgICAgICAuZmlsdGVyKEJvb2xlYW4pXG4gICAgICAgIC5qb2luKFwifFwiKTtcbn1cblxuLyoqXG4gKiBHZXRzIGEgdmFsdWUgZnJvbSBhbiBvYmplY3QsIHRyeWluZyBib3RoIGxpdGVyYWwga2V5IGFuZCBuZXN0ZWQgcGF0aC5cbiAqXG4gKiBAcGFyYW0gb2JqIE9iamVjdCB0byBnZXQgdmFsdWUgZnJvbVxuICogQHBhcmFtIGtleSBLZXkgKG1heSBiZSBsaXRlcmFsIG9yIGRvdC1ub3RhdGlvbiBwYXRoKVxuICogQHJldHVybnMgICBWYWx1ZSBvciB1bmRlZmluZWRcbiAqL1xuZnVuY3Rpb24gZ2V0VmFsdWVCeUtleShvYmo6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCBrZXk6IHN0cmluZyk6IHVua25vd24ge1xuICAgIC8vIEZpcnN0IHRyeSBsaXRlcmFsIGtleSAoZS5nLiwgXCJyZXF1ZXN0LnVybFwiIGFzIGEgc2luZ2xlIGtleSlcbiAgICBpZiAoa2V5IGluIG9iaikge1xuICAgICAgICByZXR1cm4gb2JqW2tleV07XG4gICAgfVxuXG4gICAgLy8gVGhlbiB0cnkgbmVzdGVkIHBhdGggKGUuZy4sIG9iai5yZXF1ZXN0LnVybClcbiAgICBjb25zdCBwYXJ0cyA9IGtleS5zcGxpdChcIi5cIik7XG4gICAgbGV0IGN1cnJlbnQ6IHVua25vd24gPSBvYmo7XG5cbiAgICBmb3IgKGNvbnN0IHBhcnQgb2YgcGFydHMpIHtcbiAgICAgICAgaWYgKCFpc1JlY29yZChjdXJyZW50KSB8fCAhKHBhcnQgaW4gY3VycmVudCkpIHtcbiAgICAgICAgICAgIHJldHVybiB2b2lkIDA7XG4gICAgICAgIH1cbiAgICAgICAgY3VycmVudCA9IGN1cnJlbnRbcGFydF07XG4gICAgfVxuXG4gICAgcmV0dXJuIGN1cnJlbnQ7XG59XG4iXX0=
|
package/dist/testing/test.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SnapshotOptions } from "./snapshot.js";
|
|
1
2
|
export interface WaitForBeaconsOptions {
|
|
2
3
|
/**
|
|
3
4
|
* The minimum number of beacon requests to wait for
|
|
@@ -50,6 +51,14 @@ export interface DynatraceTesting {
|
|
|
50
51
|
* ```
|
|
51
52
|
*/
|
|
52
53
|
clearEvents(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Compares captured events against a stored snapshot file.
|
|
56
|
+
* On first run, creates the snapshot. On subsequent runs, compares and fails if different.
|
|
57
|
+
* Volatile fields (timestamps, IDs, etc.) are masked by default to prevent flaky tests.
|
|
58
|
+
*
|
|
59
|
+
* @param options Configuration for snapshot comparison
|
|
60
|
+
*/
|
|
61
|
+
toMatchEventSnapshot(options?: SnapshotOptions): Promise<void>;
|
|
53
62
|
}
|
|
54
63
|
export interface BeaconRequest {
|
|
55
64
|
url: string;
|