@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.
@@ -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=
@@ -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;