@dynatrace/rum-javascript-sdk 1.329.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.
Files changed (115) hide show
  1. package/LICENSE +174 -0
  2. package/README.md +402 -0
  3. package/dist/api/constants.d.ts +1 -0
  4. package/dist/api/constants.js +2 -0
  5. package/dist/api/dynatrace-error.d.ts +3 -0
  6. package/dist/api/dynatrace-error.js +7 -0
  7. package/dist/api/global-adapter.d.ts +1 -0
  8. package/dist/api/global-adapter.js +4 -0
  9. package/dist/api/index.d.ts +65 -0
  10. package/dist/api/index.js +110 -0
  11. package/dist/api/logging.d.ts +2 -0
  12. package/dist/api/logging.js +8 -0
  13. package/dist/api/promises/index.d.ts +85 -0
  14. package/dist/api/promises/index.js +104 -0
  15. package/dist/api/promises/user-actions.d.ts +35 -0
  16. package/dist/api/promises/user-actions.js +48 -0
  17. package/dist/api/promises/wait-for-agent.d.ts +2 -0
  18. package/dist/api/promises/wait-for-agent.js +77 -0
  19. package/dist/api/promises/wait-for-send-exception-event.d.ts +9 -0
  20. package/dist/api/promises/wait-for-send-exception-event.js +18 -0
  21. package/dist/api/promises/wait-for-user-actions.d.ts +9 -0
  22. package/dist/api/promises/wait-for-user-actions.js +18 -0
  23. package/dist/api/type-guards/is-valid-agent.d.ts +2 -0
  24. package/dist/api/type-guards/is-valid-agent.js +4 -0
  25. package/dist/api/user-actions.d.ts +62 -0
  26. package/dist/api/user-actions.js +99 -0
  27. package/dist/testing/install.d.ts +15 -0
  28. package/dist/testing/install.js +19 -0
  29. package/dist/testing/test.d.ts +111 -0
  30. package/dist/testing/test.js +229 -0
  31. package/dist/types/api/dynatrace-api-types.d.ts +319 -0
  32. package/dist/types/api/dynatrace-api-types.js +7 -0
  33. package/dist/types/api/index.d.ts +1 -0
  34. package/dist/types/api/index.js +2 -0
  35. package/dist/types/index-typedoc.d.ts +31 -0
  36. package/dist/types/index-typedoc.js +32 -0
  37. package/dist/types/index.d.ts +11 -0
  38. package/dist/types/index.js +5 -0
  39. package/dist/types/internal/beacon-query.d.ts +29 -0
  40. package/dist/types/internal/beacon-query.js +32 -0
  41. package/dist/types/internal/data-dt-config-scope.d.ts +10 -0
  42. package/dist/types/internal/data-dt-config-scope.js +12 -0
  43. package/dist/types/internal/feature-hashes.d.ts +19 -0
  44. package/dist/types/internal/feature-hashes.js +22 -0
  45. package/dist/types/internal/index.d.ts +3 -0
  46. package/dist/types/internal/index.js +4 -0
  47. package/dist/types/rum-events/event-context/event-context.d.ts +364 -0
  48. package/dist/types/rum-events/event-context/event-context.js +106 -0
  49. package/dist/types/rum-events/event-context/index.d.ts +1 -0
  50. package/dist/types/rum-events/event-context/index.js +2 -0
  51. package/dist/types/rum-events/event-updates.d.ts +4 -0
  52. package/dist/types/rum-events/event-updates.js +2 -0
  53. package/dist/types/rum-events/index.d.ts +19 -0
  54. package/dist/types/rum-events/index.js +20 -0
  55. package/dist/types/rum-events/json-event.d.ts +84 -0
  56. package/dist/types/rum-events/json-event.js +2 -0
  57. package/dist/types/rum-events/rum-biz-event.d.ts +10 -0
  58. package/dist/types/rum-events/rum-biz-event.js +15 -0
  59. package/dist/types/rum-events/rum-event.d.ts +13 -0
  60. package/dist/types/rum-events/rum-event.js +3 -0
  61. package/dist/types/rum-events/rum-internal-selfmonitoring-event.d.ts +114 -0
  62. package/dist/types/rum-events/rum-internal-selfmonitoring-event.js +99 -0
  63. package/dist/types/rum-events/rum-long-task-event.d.ts +18 -0
  64. package/dist/types/rum-events/rum-long-task-event.js +10 -0
  65. package/dist/types/rum-events/rum-page-summary-event.d.ts +370 -0
  66. package/dist/types/rum-events/rum-page-summary-event.js +184 -0
  67. package/dist/types/rum-events/rum-selfmonitoring-event.d.ts +39 -0
  68. package/dist/types/rum-events/rum-selfmonitoring-event.js +32 -0
  69. package/dist/types/rum-events/rum-session-properties-event.d.ts +9 -0
  70. package/dist/types/rum-events/rum-session-properties-event.js +3 -0
  71. package/dist/types/rum-events/rum-standalone-csp-rule-violation-event.d.ts +16 -0
  72. package/dist/types/rum-events/rum-standalone-csp-rule-violation-event.js +3 -0
  73. package/dist/types/rum-events/rum-standalone-exception-event.d.ts +16 -0
  74. package/dist/types/rum-events/rum-standalone-exception-event.js +2 -0
  75. package/dist/types/rum-events/rum-standalone-navigation-event.d.ts +23 -0
  76. package/dist/types/rum-events/rum-standalone-navigation-event.js +6 -0
  77. package/dist/types/rum-events/rum-user-action-event.d.ts +187 -0
  78. package/dist/types/rum-events/rum-user-action-event.js +78 -0
  79. package/dist/types/rum-events/rum-user-interaction-event.d.ts +302 -0
  80. package/dist/types/rum-events/rum-user-interaction-event.js +131 -0
  81. package/dist/types/rum-events/rum-visibility-change-event.d.ts +15 -0
  82. package/dist/types/rum-events/rum-visibility-change-event.js +6 -0
  83. package/dist/types/rum-events/rum-web-request-event.d.ts +269 -0
  84. package/dist/types/rum-events/rum-web-request-event.js +143 -0
  85. package/dist/types/rum-events/schema-versions.d.ts +4 -0
  86. package/dist/types/rum-events/schema-versions.js +5 -0
  87. package/dist/types/rum-events/shared-namespaces-and-fields/csp-fields.d.ts +38 -0
  88. package/dist/types/rum-events/shared-namespaces-and-fields/csp-fields.js +23 -0
  89. package/dist/types/rum-events/shared-namespaces-and-fields/exception-fields.d.ts +26 -0
  90. package/dist/types/rum-events/shared-namespaces-and-fields/exception-fields.js +19 -0
  91. package/dist/types/rum-events/shared-namespaces-and-fields/general-rum-error-fields.d.ts +66 -0
  92. package/dist/types/rum-events/shared-namespaces-and-fields/general-rum-error-fields.js +66 -0
  93. package/dist/types/rum-events/shared-namespaces-and-fields/general-rum-event-fields.d.ts +142 -0
  94. package/dist/types/rum-events/shared-namespaces-and-fields/general-rum-event-fields.js +98 -0
  95. package/dist/types/rum-events/shared-namespaces-and-fields/http-namespace.d.ts +16 -0
  96. package/dist/types/rum-events/shared-namespaces-and-fields/http-namespace.js +11 -0
  97. package/dist/types/rum-events/shared-namespaces-and-fields/index.d.ts +9 -0
  98. package/dist/types/rum-events/shared-namespaces-and-fields/index.js +10 -0
  99. package/dist/types/rum-events/shared-namespaces-and-fields/navigation-fields.d.ts +48 -0
  100. package/dist/types/rum-events/shared-namespaces-and-fields/navigation-fields.js +47 -0
  101. package/dist/types/rum-events/shared-namespaces-and-fields/page-source-fields.d.ts +9 -0
  102. package/dist/types/rum-events/shared-namespaces-and-fields/page-source-fields.js +5 -0
  103. package/dist/types/rum-events/shared-namespaces-and-fields/request-fields.d.ts +14 -0
  104. package/dist/types/rum-events/shared-namespaces-and-fields/request-fields.js +10 -0
  105. package/dist/types/rum-events/shared-namespaces-and-fields/view-source-fields.d.ts +14 -0
  106. package/dist/types/rum-events/shared-namespaces-and-fields/view-source-fields.js +9 -0
  107. package/dist/types/user-actions/index.d.ts +3 -0
  108. package/dist/types/user-actions/index.js +4 -0
  109. package/dist/types/user-actions/user-action-end-event.d.ts +28 -0
  110. package/dist/types/user-actions/user-action-end-event.js +2 -0
  111. package/dist/types/user-actions/user-action-start-options.d.ts +23 -0
  112. package/dist/types/user-actions/user-action-start-options.js +2 -0
  113. package/dist/types/user-actions/user-action-tracker.d.ts +72 -0
  114. package/dist/types/user-actions/user-action-tracker.js +2 -0
  115. package/package.json +115 -0
@@ -0,0 +1,4 @@
1
+ export function isValidAgent(agent) {
2
+ return typeof agent === "object" && !!agent && "sendEvent" in agent;
3
+ }
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXMtdmFsaWQtYWdlbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zb3VyY2UvYXBpL3R5cGUtZ3VhcmRzL2lzLXZhbGlkLWFnZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE1BQU0sVUFBVSxZQUFZLENBQUMsS0FBYztJQUN2QyxPQUFPLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxDQUFDLENBQUMsS0FBSyxJQUFJLFdBQVcsSUFBSSxLQUFLLENBQUM7QUFDeEUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgZHluYXRyYWNlIGFzIER0QXBpIH0gZnJvbSBcIi4uLy4uL3R5cGVzL2FwaS9keW5hdHJhY2UtYXBpLXR5cGVzLmpzXCI7XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1ZhbGlkQWdlbnQoYWdlbnQ6IHVua25vd24pOiBhZ2VudCBpcyB0eXBlb2YgRHRBcGkge1xuICAgIHJldHVybiB0eXBlb2YgYWdlbnQgPT09IFwib2JqZWN0XCIgJiYgISFhZ2VudCAmJiBcInNlbmRFdmVudFwiIGluIGFnZW50O1xufVxuIl19
@@ -0,0 +1,62 @@
1
+ import type { UserActionStartOptions, UserActionTracker } from "../types/index.js";
2
+ /**
3
+ * Safe wrapper for {@link dynatrace.userActions.create} which reverts to a noop if RUM JavaScript or the user actions module is not available.
4
+ *
5
+ * @param options An optional options object to configure the user action.
6
+ * @returns A `UserActionTracker` object that can be started and finished, or `undefined` if the user actions module is not enabled.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import * as userActions from "@dynatrace/rum-javascript-sdk/api/user-actions";
11
+ * const userAction = userActions.create({ autoClose: false });
12
+ * // perform work
13
+ * userAction?.finish();
14
+ * ```
15
+ */
16
+ export declare function create(options?: UserActionStartOptions): UserActionTracker | undefined;
17
+ /**
18
+ * Safe wrapper for {@link dynatrace.userActions.subscribe} which reverts to a noop if RUM JavaScript or the user actions module is not available.
19
+ *
20
+ * @param subscriber A callback function that is called whenever Dynatrace creates a new user action.
21
+ * @returns An unsubscriber function to remove the subscription, or undefined if the user actions module is not enabled.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import * as userActions from "@dynatrace/rum-javascript-sdk/api/user-actions";
26
+ * const unsubscribe = userActions.subscribe((userAction) => {
27
+ * console.log('New user action created');
28
+ * userAction.autoClose = false;
29
+ * });
30
+ * // later
31
+ * unsubscribe?.();
32
+ * ```
33
+ */
34
+ export declare function subscribe(subscriber: (userAction: UserActionTracker) => void): (() => void) | undefined;
35
+ /**
36
+ * Safe wrapper for {@link dynatrace.userActions.setAutomaticDetection} which reverts to a noop if RUM JavaScript or the user actions module is not available.
37
+ *
38
+ * @param automaticDetection If false, disables automatic user action detection, otherwise enables it.
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * import * as userActions from "@dynatrace/rum-javascript-sdk/api/user-actions";
43
+ * userActions.setAutomaticDetection(false);
44
+ * // manual user action handling
45
+ * ```
46
+ */
47
+ export declare function setAutomaticDetection(automaticDetection: boolean): void;
48
+ /**
49
+ * Safe wrapper for {@link dynatrace.userActions.current} which returns undefined if RUM JavaScript or the user actions module is not available.
50
+ *
51
+ * @returns The current user action, or `undefined` if no user action is in progress or the user actions module is not enabled.
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * import * as userActions from "@dynatrace/rum-javascript-sdk/api/user-actions";
56
+ * const currentAction = userActions.getCurrent();
57
+ * if (currentAction) {
58
+ * currentAction.autoClose = false;
59
+ * }
60
+ * ```
61
+ */
62
+ export declare function getCurrent(): UserActionTracker | undefined;
@@ -0,0 +1,99 @@
1
+ import { getGlobal } from "./global-adapter.js";
2
+ import { isValidAgent } from "./type-guards/is-valid-agent.js";
3
+ import { log } from "./logging.js";
4
+ /**
5
+ * Safe wrapper for {@link dynatrace.userActions.create} which reverts to a noop if RUM JavaScript or the user actions module is not available.
6
+ *
7
+ * @param options An optional options object to configure the user action.
8
+ * @returns A `UserActionTracker` object that can be started and finished, or `undefined` if the user actions module is not enabled.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import * as userActions from "@dynatrace/rum-javascript-sdk/api/user-actions";
13
+ * const userAction = userActions.create({ autoClose: false });
14
+ * // perform work
15
+ * userAction?.finish();
16
+ * ```
17
+ */
18
+ export function create(options) {
19
+ return guardUserActions((userActions) => {
20
+ return userActions.create(options);
21
+ });
22
+ }
23
+ /**
24
+ * Safe wrapper for {@link dynatrace.userActions.subscribe} which reverts to a noop if RUM JavaScript or the user actions module is not available.
25
+ *
26
+ * @param subscriber A callback function that is called whenever Dynatrace creates a new user action.
27
+ * @returns An unsubscriber function to remove the subscription, or undefined if the user actions module is not enabled.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * import * as userActions from "@dynatrace/rum-javascript-sdk/api/user-actions";
32
+ * const unsubscribe = userActions.subscribe((userAction) => {
33
+ * console.log('New user action created');
34
+ * userAction.autoClose = false;
35
+ * });
36
+ * // later
37
+ * unsubscribe?.();
38
+ * ```
39
+ */
40
+ export function subscribe(subscriber) {
41
+ return guardUserActions((userActions) => {
42
+ return userActions.subscribe(subscriber);
43
+ });
44
+ }
45
+ /**
46
+ * Safe wrapper for {@link dynatrace.userActions.setAutomaticDetection} which reverts to a noop if RUM JavaScript or the user actions module is not available.
47
+ *
48
+ * @param automaticDetection If false, disables automatic user action detection, otherwise enables it.
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * import * as userActions from "@dynatrace/rum-javascript-sdk/api/user-actions";
53
+ * userActions.setAutomaticDetection(false);
54
+ * // manual user action handling
55
+ * ```
56
+ */
57
+ export function setAutomaticDetection(automaticDetection) {
58
+ guardUserActions((userActions) => {
59
+ userActions.setAutomaticDetection(automaticDetection);
60
+ });
61
+ }
62
+ /**
63
+ * Safe wrapper for {@link dynatrace.userActions.current} which returns undefined if RUM JavaScript or the user actions module is not available.
64
+ *
65
+ * @returns The current user action, or `undefined` if no user action is in progress or the user actions module is not enabled.
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * import * as userActions from "@dynatrace/rum-javascript-sdk/api/user-actions";
70
+ * const currentAction = userActions.getCurrent();
71
+ * if (currentAction) {
72
+ * currentAction.autoClose = false;
73
+ * }
74
+ * ```
75
+ */
76
+ export function getCurrent() {
77
+ return guardUserActions((userActions) => {
78
+ return userActions.current;
79
+ });
80
+ }
81
+ /**
82
+ * Safe wrapper for various user actions API functions which reverts to a noop if RUM JavaScript or the user actions module is not available.
83
+ *
84
+ * @param fn The function to be executed if the user actions module is available.
85
+ * @returns The result of the provided function call if the user actions module is available, otherwise undefined.
86
+ */
87
+ function guardUserActions(fn) {
88
+ const globalDt = getGlobal("dynatrace");
89
+ if (!isValidAgent(globalDt)) {
90
+ log("dynatrace is not available");
91
+ return void 0;
92
+ }
93
+ if (!globalDt.userActions) {
94
+ log("dynatrace.userActions module is not available");
95
+ return void 0;
96
+ }
97
+ return fn(globalDt.userActions);
98
+ }
99
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlci1hY3Rpb25zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc291cmNlL2FwaS91c2VyLWFjdGlvbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBS0EsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUMvRCxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBRW5DOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFNLFVBQVUsTUFBTSxDQUFDLE9BQWdDO0lBQ25ELE9BQU8sZ0JBQWdCLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRTtRQUNwQyxPQUFPLFdBQVcsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdkMsQ0FBQyxDQUFDLENBQUM7QUFDUCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxNQUFNLFVBQVUsU0FBUyxDQUFDLFVBQW1EO0lBQ3pFLE9BQU8sZ0JBQWdCLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRTtRQUNwQyxPQUFPLFdBQVcsQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLENBQUM7SUFDN0MsQ0FBQyxDQUFDLENBQUM7QUFDUCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLFVBQVUscUJBQXFCLENBQUMsa0JBQTJCO0lBQzdELGdCQUFnQixDQUFDLENBQUMsV0FBVyxFQUFFLEVBQUU7UUFDN0IsV0FBVyxDQUFDLHFCQUFxQixDQUFDLGtCQUFrQixDQUFDLENBQUM7SUFDMUQsQ0FBQyxDQUFDLENBQUM7QUFDUCxDQUFDO0FBRUQ7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQU0sVUFBVSxVQUFVO0lBQ3RCLE9BQU8sZ0JBQWdCLENBQUMsQ0FBQyxXQUFXLEVBQUUsRUFBRTtRQUNwQyxPQUFPLFdBQVcsQ0FBQyxPQUFPLENBQUM7SUFDL0IsQ0FBQyxDQUFDLENBQUM7QUFDUCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLGdCQUFnQixDQUE4QyxFQUFLO0lBQ3hFLE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUN4QyxJQUFJLENBQUMsWUFBWSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUM7UUFDMUIsR0FBRyxDQUFDLDRCQUE0QixDQUFDLENBQUM7UUFDbEMsT0FBTyxLQUFLLENBQUMsQ0FBQztJQUNsQixDQUFDO0lBRUQsSUFBSSxDQUFDLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUN4QixHQUFHLENBQUMsK0NBQStDLENBQUMsQ0FBQztRQUNyRCxPQUFPLEtBQUssQ0FBQyxDQUFDO0lBQ2xCLENBQUM7SUFFRCxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUM7QUFDcEMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHtcbiAgICBVc2VyQWN0aW9ucyxcbiAgICBVc2VyQWN0aW9uU3RhcnRPcHRpb25zLFxuICAgIFVzZXJBY3Rpb25UcmFja2VyXG59IGZyb20gXCIuLi90eXBlcy9pbmRleC5qc1wiO1xuaW1wb3J0IHsgZ2V0R2xvYmFsIH0gZnJvbSBcIi4vZ2xvYmFsLWFkYXB0ZXIuanNcIjtcbmltcG9ydCB7IGlzVmFsaWRBZ2VudCB9IGZyb20gXCIuL3R5cGUtZ3VhcmRzL2lzLXZhbGlkLWFnZW50LmpzXCI7XG5pbXBvcnQgeyBsb2cgfSBmcm9tIFwiLi9sb2dnaW5nLmpzXCI7XG5cbi8qKlxuICogU2FmZSB3cmFwcGVyIGZvciB7QGxpbmsgZHluYXRyYWNlLnVzZXJBY3Rpb25zLmNyZWF0ZX0gd2hpY2ggcmV2ZXJ0cyB0byBhIG5vb3AgaWYgUlVNIEphdmFTY3JpcHQgb3IgdGhlIHVzZXIgYWN0aW9ucyBtb2R1bGUgaXMgbm90IGF2YWlsYWJsZS5cbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyBBbiBvcHRpb25hbCBvcHRpb25zIG9iamVjdCB0byBjb25maWd1cmUgdGhlIHVzZXIgYWN0aW9uLlxuICogQHJldHVybnMgICAgICAgQSBgVXNlckFjdGlvblRyYWNrZXJgIG9iamVjdCB0aGF0IGNhbiBiZSBzdGFydGVkIGFuZCBmaW5pc2hlZCwgb3IgYHVuZGVmaW5lZGAgaWYgdGhlIHVzZXIgYWN0aW9ucyBtb2R1bGUgaXMgbm90IGVuYWJsZWQuXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGltcG9ydCAqIGFzIHVzZXJBY3Rpb25zIGZyb20gXCJAZHluYXRyYWNlL3J1bS1qYXZhc2NyaXB0LXNkay9hcGkvdXNlci1hY3Rpb25zXCI7XG4gKiBjb25zdCB1c2VyQWN0aW9uID0gdXNlckFjdGlvbnMuY3JlYXRlKHsgYXV0b0Nsb3NlOiBmYWxzZSB9KTtcbiAqIC8vIHBlcmZvcm0gd29ya1xuICogdXNlckFjdGlvbj8uZmluaXNoKCk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZShvcHRpb25zPzogVXNlckFjdGlvblN0YXJ0T3B0aW9ucyk6IFVzZXJBY3Rpb25UcmFja2VyIHwgdW5kZWZpbmVkIHtcbiAgICByZXR1cm4gZ3VhcmRVc2VyQWN0aW9ucygodXNlckFjdGlvbnMpID0+IHtcbiAgICAgICAgcmV0dXJuIHVzZXJBY3Rpb25zLmNyZWF0ZShvcHRpb25zKTtcbiAgICB9KTtcbn1cblxuLyoqXG4gKiBTYWZlIHdyYXBwZXIgZm9yIHtAbGluayBkeW5hdHJhY2UudXNlckFjdGlvbnMuc3Vic2NyaWJlfSB3aGljaCByZXZlcnRzIHRvIGEgbm9vcCBpZiBSVU0gSmF2YVNjcmlwdCBvciB0aGUgdXNlciBhY3Rpb25zIG1vZHVsZSBpcyBub3QgYXZhaWxhYmxlLlxuICpcbiAqIEBwYXJhbSBzdWJzY3JpYmVyIEEgY2FsbGJhY2sgZnVuY3Rpb24gdGhhdCBpcyBjYWxsZWQgd2hlbmV2ZXIgRHluYXRyYWNlIGNyZWF0ZXMgYSBuZXcgdXNlciBhY3Rpb24uXG4gKiBAcmV0dXJucyAgICAgICAgICBBbiB1bnN1YnNjcmliZXIgZnVuY3Rpb24gdG8gcmVtb3ZlIHRoZSBzdWJzY3JpcHRpb24sIG9yIHVuZGVmaW5lZCBpZiB0aGUgdXNlciBhY3Rpb25zIG1vZHVsZSBpcyBub3QgZW5hYmxlZC5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0ICogYXMgdXNlckFjdGlvbnMgZnJvbSBcIkBkeW5hdHJhY2UvcnVtLWphdmFzY3JpcHQtc2RrL2FwaS91c2VyLWFjdGlvbnNcIjtcbiAqIGNvbnN0IHVuc3Vic2NyaWJlID0gdXNlckFjdGlvbnMuc3Vic2NyaWJlKCh1c2VyQWN0aW9uKSA9PiB7XG4gKiAgICAgY29uc29sZS5sb2coJ05ldyB1c2VyIGFjdGlvbiBjcmVhdGVkJyk7XG4gKiAgICAgdXNlckFjdGlvbi5hdXRvQ2xvc2UgPSBmYWxzZTtcbiAqIH0pO1xuICogLy8gbGF0ZXJcbiAqIHVuc3Vic2NyaWJlPy4oKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gc3Vic2NyaWJlKHN1YnNjcmliZXI6ICh1c2VyQWN0aW9uOiBVc2VyQWN0aW9uVHJhY2tlcikgPT4gdm9pZCk6ICgoKSA9PiB2b2lkKSB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIGd1YXJkVXNlckFjdGlvbnMoKHVzZXJBY3Rpb25zKSA9PiB7XG4gICAgICAgIHJldHVybiB1c2VyQWN0aW9ucy5zdWJzY3JpYmUoc3Vic2NyaWJlcik7XG4gICAgfSk7XG59XG5cbi8qKlxuICogU2FmZSB3cmFwcGVyIGZvciB7QGxpbmsgZHluYXRyYWNlLnVzZXJBY3Rpb25zLnNldEF1dG9tYXRpY0RldGVjdGlvbn0gd2hpY2ggcmV2ZXJ0cyB0byBhIG5vb3AgaWYgUlVNIEphdmFTY3JpcHQgb3IgdGhlIHVzZXIgYWN0aW9ucyBtb2R1bGUgaXMgbm90IGF2YWlsYWJsZS5cbiAqXG4gKiBAcGFyYW0gYXV0b21hdGljRGV0ZWN0aW9uIElmIGZhbHNlLCBkaXNhYmxlcyBhdXRvbWF0aWMgdXNlciBhY3Rpb24gZGV0ZWN0aW9uLCBvdGhlcndpc2UgZW5hYmxlcyBpdC5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0ICogYXMgdXNlckFjdGlvbnMgZnJvbSBcIkBkeW5hdHJhY2UvcnVtLWphdmFzY3JpcHQtc2RrL2FwaS91c2VyLWFjdGlvbnNcIjtcbiAqIHVzZXJBY3Rpb25zLnNldEF1dG9tYXRpY0RldGVjdGlvbihmYWxzZSk7XG4gKiAvLyBtYW51YWwgdXNlciBhY3Rpb24gaGFuZGxpbmdcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gc2V0QXV0b21hdGljRGV0ZWN0aW9uKGF1dG9tYXRpY0RldGVjdGlvbjogYm9vbGVhbik6IHZvaWQge1xuICAgIGd1YXJkVXNlckFjdGlvbnMoKHVzZXJBY3Rpb25zKSA9PiB7XG4gICAgICAgIHVzZXJBY3Rpb25zLnNldEF1dG9tYXRpY0RldGVjdGlvbihhdXRvbWF0aWNEZXRlY3Rpb24pO1xuICAgIH0pO1xufVxuXG4vKipcbiAqIFNhZmUgd3JhcHBlciBmb3Ige0BsaW5rIGR5bmF0cmFjZS51c2VyQWN0aW9ucy5jdXJyZW50fSB3aGljaCByZXR1cm5zIHVuZGVmaW5lZCBpZiBSVU0gSmF2YVNjcmlwdCBvciB0aGUgdXNlciBhY3Rpb25zIG1vZHVsZSBpcyBub3QgYXZhaWxhYmxlLlxuICpcbiAqIEByZXR1cm5zIFRoZSBjdXJyZW50IHVzZXIgYWN0aW9uLCBvciBgdW5kZWZpbmVkYCBpZiBubyB1c2VyIGFjdGlvbiBpcyBpbiBwcm9ncmVzcyBvciB0aGUgdXNlciBhY3Rpb25zIG1vZHVsZSBpcyBub3QgZW5hYmxlZC5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0ICogYXMgdXNlckFjdGlvbnMgZnJvbSBcIkBkeW5hdHJhY2UvcnVtLWphdmFzY3JpcHQtc2RrL2FwaS91c2VyLWFjdGlvbnNcIjtcbiAqIGNvbnN0IGN1cnJlbnRBY3Rpb24gPSB1c2VyQWN0aW9ucy5nZXRDdXJyZW50KCk7XG4gKiBpZiAoY3VycmVudEFjdGlvbikge1xuICogICAgIGN1cnJlbnRBY3Rpb24uYXV0b0Nsb3NlID0gZmFsc2U7XG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEN1cnJlbnQoKTogVXNlckFjdGlvblRyYWNrZXIgfCB1bmRlZmluZWQge1xuICAgIHJldHVybiBndWFyZFVzZXJBY3Rpb25zKCh1c2VyQWN0aW9ucykgPT4ge1xuICAgICAgICByZXR1cm4gdXNlckFjdGlvbnMuY3VycmVudDtcbiAgICB9KTtcbn1cblxuLyoqXG4gKiBTYWZlIHdyYXBwZXIgZm9yIHZhcmlvdXMgdXNlciBhY3Rpb25zIEFQSSBmdW5jdGlvbnMgd2hpY2ggcmV2ZXJ0cyB0byBhIG5vb3AgaWYgUlVNIEphdmFTY3JpcHQgb3IgdGhlIHVzZXIgYWN0aW9ucyBtb2R1bGUgaXMgbm90IGF2YWlsYWJsZS5cbiAqXG4gKiBAcGFyYW0gZm4gVGhlIGZ1bmN0aW9uIHRvIGJlIGV4ZWN1dGVkIGlmIHRoZSB1c2VyIGFjdGlvbnMgbW9kdWxlIGlzIGF2YWlsYWJsZS5cbiAqIEByZXR1cm5zICBUaGUgcmVzdWx0IG9mIHRoZSBwcm92aWRlZCBmdW5jdGlvbiBjYWxsIGlmIHRoZSB1c2VyIGFjdGlvbnMgbW9kdWxlIGlzIGF2YWlsYWJsZSwgb3RoZXJ3aXNlIHVuZGVmaW5lZC5cbiAqL1xuZnVuY3Rpb24gZ3VhcmRVc2VyQWN0aW9uczxUIGV4dGVuZHMgKHVzZXJBY3Rpb25zOiBVc2VyQWN0aW9ucykgPT4gYW55PihmbjogVCk6IFJldHVyblR5cGU8VD4gfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IGdsb2JhbER0ID0gZ2V0R2xvYmFsKFwiZHluYXRyYWNlXCIpO1xuICAgIGlmICghaXNWYWxpZEFnZW50KGdsb2JhbER0KSkge1xuICAgICAgICBsb2coXCJkeW5hdHJhY2UgaXMgbm90IGF2YWlsYWJsZVwiKTtcbiAgICAgICAgcmV0dXJuIHZvaWQgMDtcbiAgICB9XG5cbiAgICBpZiAoIWdsb2JhbER0LnVzZXJBY3Rpb25zKSB7XG4gICAgICAgIGxvZyhcImR5bmF0cmFjZS51c2VyQWN0aW9ucyBtb2R1bGUgaXMgbm90IGF2YWlsYWJsZVwiKTtcbiAgICAgICAgcmV0dXJuIHZvaWQgMDtcbiAgICB9XG5cbiAgICByZXR1cm4gZm4oZ2xvYmFsRHQudXNlckFjdGlvbnMpO1xufVxuIl19
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Fetches the RUM JavaScript from the given environment.
3
+ *
4
+ * @param endpointUrl The location to fetch the script from, e.g. "https://{your-environment-id}.live.dynatrace.com" or "https://{your-activegate-domain}:9999/e/{your-environment-id}".
5
+ * @param appId The RUM application ID to fetch the configuration.
6
+ * @param token The authentication token to access the API.
7
+ * @returns A promise that resolves with an HTML string containing the RUM JavaScript for insertion into a page.
8
+ *
9
+ * @example
10
+ * const rumJavaScriptHtml = await fetchRumJavaScript("https://myenv.live.dynatrace.com","APPLICATION-EA7C4B59F27D43EB", "dt0c01.abcd.efghijk");
11
+ * // use the result to write your javascript, e.g. with cheerio:
12
+ * const $ = cheerio.load(myHtml);
13
+ * $("head").append(rumJavaScriptHtml);
14
+ */
15
+ export declare function fetchRumJavaScript(endpointUrl: string, appId: string, token: string): Promise<string>;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Fetches the RUM JavaScript from the given environment.
3
+ *
4
+ * @param endpointUrl The location to fetch the script from, e.g. "https://{your-environment-id}.live.dynatrace.com" or "https://{your-activegate-domain}:9999/e/{your-environment-id}".
5
+ * @param appId The RUM application ID to fetch the configuration.
6
+ * @param token The authentication token to access the API.
7
+ * @returns A promise that resolves with an HTML string containing the RUM JavaScript for insertion into a page.
8
+ *
9
+ * @example
10
+ * const rumJavaScriptHtml = await fetchRumJavaScript("https://myenv.live.dynatrace.com","APPLICATION-EA7C4B59F27D43EB", "dt0c01.abcd.efghijk");
11
+ * // use the result to write your javascript, e.g. with cheerio:
12
+ * const $ = cheerio.load(myHtml);
13
+ * $("head").append(rumJavaScriptHtml);
14
+ */
15
+ export async function fetchRumJavaScript(endpointUrl, appId, token) {
16
+ const response = await fetch(`${endpointUrl}/api/v2/rum/javaScriptTag/${appId}`, { headers: { Authorization: `Api-Token ${token}` } });
17
+ return response.text();
18
+ }
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdGFsbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS90ZXN0aW5nL2luc3RhbGwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsa0JBQWtCLENBQUMsV0FBbUIsRUFBRSxLQUFhLEVBQUUsS0FBYTtJQUN0RixNQUFNLFFBQVEsR0FBRyxNQUFNLEtBQUssQ0FDeEIsR0FBRyxXQUFXLDZCQUE2QixLQUFLLEVBQUUsRUFDbEQsRUFBRSxPQUFPLEVBQUUsRUFBRSxhQUFhLEVBQUUsYUFBYSxLQUFLLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUMxRCxPQUFPLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUMzQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBGZXRjaGVzIHRoZSBSVU0gSmF2YVNjcmlwdCBmcm9tIHRoZSBnaXZlbiBlbnZpcm9ubWVudC5cbiAqXG4gKiBAcGFyYW0gZW5kcG9pbnRVcmwgVGhlIGxvY2F0aW9uIHRvIGZldGNoIHRoZSBzY3JpcHQgZnJvbSwgZS5nLiBcImh0dHBzOi8ve3lvdXItZW52aXJvbm1lbnQtaWR9LmxpdmUuZHluYXRyYWNlLmNvbVwiIG9yIFwiaHR0cHM6Ly97eW91ci1hY3RpdmVnYXRlLWRvbWFpbn06OTk5OS9lL3t5b3VyLWVudmlyb25tZW50LWlkfVwiLlxuICogQHBhcmFtIGFwcElkICAgICAgIFRoZSBSVU0gYXBwbGljYXRpb24gSUQgdG8gZmV0Y2ggdGhlIGNvbmZpZ3VyYXRpb24uXG4gKiBAcGFyYW0gdG9rZW4gICAgICAgVGhlIGF1dGhlbnRpY2F0aW9uIHRva2VuIHRvIGFjY2VzcyB0aGUgQVBJLlxuICogQHJldHVybnMgICAgICAgICAgIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdpdGggYW4gSFRNTCBzdHJpbmcgY29udGFpbmluZyB0aGUgUlVNIEphdmFTY3JpcHQgZm9yIGluc2VydGlvbiBpbnRvIGEgcGFnZS5cbiAqXG4gKiBAZXhhbXBsZVxuICogY29uc3QgcnVtSmF2YVNjcmlwdEh0bWwgPSBhd2FpdCBmZXRjaFJ1bUphdmFTY3JpcHQoXCJodHRwczovL215ZW52LmxpdmUuZHluYXRyYWNlLmNvbVwiLFwiQVBQTElDQVRJT04tRUE3QzRCNTlGMjdENDNFQlwiLCBcImR0MGMwMS5hYmNkLmVmZ2hpamtcIik7XG4gKiAvLyB1c2UgdGhlIHJlc3VsdCB0byB3cml0ZSB5b3VyIGphdmFzY3JpcHQsIGUuZy4gd2l0aCBjaGVlcmlvOlxuICogY29uc3QgJCA9IGNoZWVyaW8ubG9hZChteUh0bWwpO1xuICogJChcImhlYWRcIikuYXBwZW5kKHJ1bUphdmFTY3JpcHRIdG1sKTtcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGZldGNoUnVtSmF2YVNjcmlwdChlbmRwb2ludFVybDogc3RyaW5nLCBhcHBJZDogc3RyaW5nLCB0b2tlbjogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKFxuICAgICAgICBgJHtlbmRwb2ludFVybH0vYXBpL3YyL3J1bS9qYXZhU2NyaXB0VGFnLyR7YXBwSWR9YCxcbiAgICAgICAgeyBoZWFkZXJzOiB7IEF1dGhvcml6YXRpb246IGBBcGktVG9rZW4gJHt0b2tlbn1gIH0gfSk7XG4gICAgcmV0dXJuIHJlc3BvbnNlLnRleHQoKTtcbn1cbiJdfQ==
@@ -0,0 +1,111 @@
1
+ export interface WaitForBeaconsOptions {
2
+ /**
3
+ * The minimum number of beacon requests to wait for
4
+ */
5
+ minCount?: number;
6
+ /**
7
+ * The maximum time to wait for beacon requests, in milliseconds - Default: 5_000
8
+ */
9
+ timeout?: number;
10
+ }
11
+ export interface ExpectOptions {
12
+ /**
13
+ * The maximum time to wait for the event to be sent, in milliseconds - Default: 5_000
14
+ */
15
+ timeout?: number;
16
+ }
17
+ export interface DynatraceTesting {
18
+ /**
19
+ * Waits for a specified number of beacon requests or until a timeout occurs.
20
+ *
21
+ * @param options Configuration options for waiting for beacons
22
+ * @returns A promise that resolves with an array of beacon requests
23
+ */
24
+ waitForBeacons(options?: WaitForBeaconsOptions): Promise<BeaconRequest[]>;
25
+ /**
26
+ * Verifies that a specific event has been sent, optionally within a timeout period.
27
+ *
28
+ * @param event The event to check, represented as a key-value pair object. This uses Playwrights expect(event).toMatchObject.
29
+ * @param options Configuration options for the verification
30
+ * @returns A promise that resolves when the event is confirmed to have been sent
31
+ */
32
+ expectToHaveSentEvent(event: Record<string, unknown>, options?: ExpectOptions): Promise<void>;
33
+ /**
34
+ * Verifies that a specific event has been sent a specified number of times, optionally within a timeout period.
35
+ *
36
+ * @param event The event to check, represented as a key-value pair object. This uses Playwrights expect(event).toMatchObject.
37
+ * @param times The exact number of times the event is expected to have been sent
38
+ * @param options Configuration options for the verification
39
+ * @returns A promise that resolves when the event is confirmed to have been sent the specified number of times
40
+ */
41
+ expectToHaveSentEventTimes(event: Record<string, unknown>, times: number, options?: ExpectOptions): Promise<void>;
42
+ /**
43
+ * Clears all events and beacons, allowing for subsequent expectations to ignore events that have been sent
44
+ * in the past. Example:
45
+ * ```
46
+ * sendFooEvent();
47
+ * await dynatraceTesting.expectToHaveSentEventTimes({ foo: "bar" }, 1);
48
+ * dynatraceTesting.clearEvents();
49
+ * await dynatraceTesting.expectToHaveSentEventTimes({ foo: "bar" }, 1);
50
+ * ```
51
+ */
52
+ clearEvents(): void;
53
+ }
54
+ export interface BeaconRequest {
55
+ url: string;
56
+ body: Record<string, unknown>;
57
+ }
58
+ export interface DynatraceTestingFixture {
59
+ /**
60
+ * The testing API providing expect methods.
61
+ */
62
+ dynatraceTesting: DynatraceTesting;
63
+ /**
64
+ * Configuration to enable Dynatrace testing with a given environment.
65
+ */
66
+ dynatraceConfig: DynatraceConfig;
67
+ }
68
+ export interface DynatraceConfig {
69
+ /**
70
+ * The URL of the Dynatrace API endpoint to connect to, see
71
+ * [RUM manual insertion tags API](https://docs.dynatrace.com/docs/discover-dynatrace/references/dynatrace-api/environment-api/rum/rum-manual-insertion-tags)
72
+ * for details. Example: https://{your-environment-id}.live.dynatrace.com or
73
+ * https://{your-activegate-domain}/e/{your-environment-id}
74
+ */
75
+ endpointUrl: string;
76
+ /**
77
+ * The application ID to identify the correct RUM JavaScript to fetch. You can find
78
+ * this in the URL if you open the Experience Vitals app and select the frontend you
79
+ * want to test. Example: APPLICATION-ABCDEF0123456789
80
+ */
81
+ appId: string;
82
+ /**
83
+ * The authentication token for the installation. See
84
+ * [Tokens and authentication](https://docs.dynatrace.com/docs/discover-dynatrace/references/dynatrace-api/basics/dynatrace-api-authentication).
85
+ */
86
+ token: string;
87
+ /**
88
+ * Accepts an array of regular expressions to match against Dynatrace-related warnings. By default, these warnings
89
+ * trigger test fails. If you anticipate warnings and don't want your tests to fail, use this setting to ignore them
90
+ * by message. Example:
91
+ * ["invalid property my_app_data\.\\w+[0-9_]*"]
92
+ */
93
+ ignoreWarnings?: string[];
94
+ /**
95
+ * Dump the array of Dynatrace events into the console in case an assertion fails.
96
+ */
97
+ dumpEventsOnFail?: boolean;
98
+ }
99
+ /**
100
+ * Worker-scoped fixtures for caching RUM JavaScript across tests in the same worker.
101
+ * This improves performance by avoiding redundant network requests.
102
+ */
103
+ export interface DynatraceTestingWorkerFixture {
104
+ /**
105
+ * Cache for RUM JavaScript snippets, scoped to the worker.
106
+ *
107
+ * @internal
108
+ */
109
+ rumJavaScriptCache: Map<string, string>;
110
+ }
111
+ export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & DynatraceTestingFixture, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions & DynatraceTestingWorkerFixture>;
@@ -0,0 +1,229 @@
1
+ import { expect, test as base } from "@playwright/test";
2
+ import { fetchRumJavaScript } from "./install.js";
3
+ import { uncompress } from "snappyjs";
4
+ const DEFAULT_TIMEOUT = 5_000;
5
+ const decoder = new TextDecoder();
6
+ export const test = base.extend({
7
+ dynatraceConfig: [{
8
+ appId: "",
9
+ endpointUrl: "",
10
+ token: ""
11
+ }, { option: true }],
12
+ // eslint-disable-next-line no-empty-pattern -- Playwright requires object destructuring even when no dependencies are needed
13
+ rumJavaScriptCache: [async ({}, use) => {
14
+ const cache = new Map();
15
+ await use(cache);
16
+ }, { scope: "worker", auto: true, box: true }],
17
+ dynatraceTesting: async ({ page, dynatraceConfig, rumJavaScriptCache }, use) => {
18
+ const { endpointUrl, appId, token, ignoreWarnings = [], dumpEventsOnFail = false } = dynatraceConfig;
19
+ const beacons = [];
20
+ const events = [];
21
+ const warnings = [];
22
+ const ignoreRegexes = ignoreWarnings.map(x => new RegExp(x, "i"));
23
+ page.on("console", (msg) => {
24
+ if (msg.text().includes("ynatrace") && !ignoreRegexes.some(regex => regex.test(msg.text()))) {
25
+ warnings.push(msg.text());
26
+ }
27
+ });
28
+ await setupRumJavaScript();
29
+ await use({
30
+ async waitForBeacons(options = {}) {
31
+ const { minCount = 0, timeout = DEFAULT_TIMEOUT } = options;
32
+ const start = Date.now();
33
+ while (beacons.length < minCount && (Date.now() - start) <= timeout) {
34
+ if (warnings.length > 0) {
35
+ throwWithEventDump("Unexpected Dynatrace API warnings: " + warnings.join("\n"));
36
+ }
37
+ await wait(100);
38
+ }
39
+ if (beacons.length < minCount) {
40
+ throwWithEventDump(`Found only ${beacons.length} Dynatrace beacons after timeout of ${timeout}ms, but expected at least ${minCount}.`);
41
+ }
42
+ return beacons;
43
+ },
44
+ async expectToHaveSentEvent(expectedEvent, options = {}) {
45
+ const { timeout = DEFAULT_TIMEOUT } = options;
46
+ const start = Date.now();
47
+ let lastNonMatch = void 0;
48
+ let nextCheckIndex = 0;
49
+ while ((Date.now() - start) <= timeout) {
50
+ for (let i = nextCheckIndex; i < events.length; i++) {
51
+ const beaconEvent = events[i];
52
+ try {
53
+ expect(beaconEvent).toMatchObject(expectedEvent);
54
+ return;
55
+ }
56
+ catch {
57
+ lastNonMatch = beaconEvent;
58
+ }
59
+ nextCheckIndex = i + 1;
60
+ }
61
+ await wait(100);
62
+ }
63
+ if (!lastNonMatch) {
64
+ throwWithEventDump("Dynatrace didn't send any events.");
65
+ }
66
+ console.log(`[Dynatrace Testing] Didn't find matching events.`);
67
+ expect(lastNonMatch).toMatchObject(expectedEvent);
68
+ },
69
+ async expectToHaveSentEventTimes(expectedEvent, times, options = {}) {
70
+ const { timeout = DEFAULT_TIMEOUT } = options;
71
+ const start = Date.now();
72
+ let lastNonMatch = void 0;
73
+ let nextCheckIndex = 0;
74
+ let foundTimes = 0;
75
+ while ((Date.now() - start) <= timeout) {
76
+ for (let i = nextCheckIndex; i < events.length; i++) {
77
+ const beaconEvent = events[i];
78
+ try {
79
+ expect(beaconEvent).toMatchObject(expectedEvent);
80
+ foundTimes++;
81
+ }
82
+ catch {
83
+ lastNonMatch = beaconEvent;
84
+ }
85
+ nextCheckIndex = i + 1;
86
+ }
87
+ if (foundTimes === times) {
88
+ return;
89
+ }
90
+ if (foundTimes > times) {
91
+ throwWithEventDump(`Expected ${times} event occurrences, found ${foundTimes}.`);
92
+ }
93
+ await wait(100);
94
+ }
95
+ if (!lastNonMatch) {
96
+ throwWithEventDump("Dynatrace didn't send any events");
97
+ }
98
+ if (foundTimes < times) {
99
+ throwWithEventDump(`Didn't find the expected amount of ${times} matching events, found ${foundTimes}.`);
100
+ }
101
+ expect(lastNonMatch).toMatchObject(expectedEvent);
102
+ },
103
+ clearEvents() {
104
+ events.length = 0;
105
+ beacons.length = 0;
106
+ }
107
+ });
108
+ /**
109
+ * Throws an error with a custom message. If event dumping is enabled, logs the received Dynatrace events before
110
+ * throwing the error.
111
+ *
112
+ * @param message The error message to be thrown
113
+ */
114
+ function throwWithEventDump(message) {
115
+ if (dumpEventsOnFail) {
116
+ console.log("Received Dynatrace events:", events);
117
+ }
118
+ throw new Error(message);
119
+ }
120
+ async function setupRumJavaScript() {
121
+ // Fetch RUM JavaScript once and cache it
122
+ const cacheKey = `${endpointUrl}:${appId}`;
123
+ let rum = rumJavaScriptCache.get(cacheKey);
124
+ let beaconUri;
125
+ if (!rum) {
126
+ rum = await fetchRumJavaScriptContent(endpointUrl, appId, token);
127
+ beaconUri = extractBeaconUri(rum);
128
+ rumJavaScriptCache.set(cacheKey, rum);
129
+ rumJavaScriptCache.set(`${cacheKey}:beaconUri`, beaconUri);
130
+ }
131
+ else {
132
+ beaconUri = rumJavaScriptCache.get(`${cacheKey}:beaconUri`);
133
+ if (!beaconUri) {
134
+ throw new Error("Failed to retrieve beaconUri from cache.");
135
+ }
136
+ }
137
+ // Listen to network requests to capture beacon data
138
+ // This approach doesn't interfere with user-defined routes
139
+ page.on("request", (request) => {
140
+ const url = request.url();
141
+ // Check if this is a beacon request matching the extracted beaconUri
142
+ if (!(beaconUri && url.startsWith(beaconUri) && url.includes("ty=js"))) {
143
+ return;
144
+ }
145
+ const beaconBodyString = extractBeaconBody(request, url);
146
+ if (!beaconBodyString) {
147
+ return;
148
+ }
149
+ try {
150
+ const beaconBody = JSON.parse(beaconBodyString);
151
+ beacons.push({
152
+ url: url,
153
+ body: beaconBody
154
+ });
155
+ events.push(...beaconBody.data.events);
156
+ }
157
+ catch (error) {
158
+ console.error("[Dynatrace Testing] Failed to parse beacon body:", error);
159
+ }
160
+ });
161
+ // Use addInitScript to inject RUM script tag before any page scripts execute
162
+ await page.addInitScript(rum);
163
+ }
164
+ }
165
+ });
166
+ /**
167
+ * Fetches the RUM JavaScript content from the Dynatrace API endpoint.
168
+ *
169
+ * @param endpointUrl The URL of the Dynatrace API endpoint
170
+ * @param appId The application ID to identify the correct RUM JavaScript
171
+ * @param token The authentication token for the installation
172
+ * @returns A promise that resolves to the RUM JavaScript content
173
+ */
174
+ async function fetchRumJavaScriptContent(endpointUrl, appId, token) {
175
+ const rumApiResult = await fetchRumJavaScript(endpointUrl, appId, token);
176
+ if (!rumApiResult.includes("_complete.js")) {
177
+ throw new Error("Dynatrace received unexpected RUM JavaScript when requesting the JavaScript Tag via API.");
178
+ }
179
+ // Extract the src URL from the script tag
180
+ const srcMatch = rumApiResult.match(/src="([^"]+)"/);
181
+ if (!srcMatch) {
182
+ throw new Error("Failed to extract src URL from RUM JavaScript tag.");
183
+ }
184
+ const scriptUrl = srcMatch[1];
185
+ // Fetch the actual JavaScript content
186
+ const scriptResponse = await fetch(scriptUrl);
187
+ return scriptResponse.text();
188
+ }
189
+ /**
190
+ * Extracts the beacon URI from the RUM JavaScript content.
191
+ *
192
+ * @param rum The RUM JavaScript content to extract the beacon URI from
193
+ * @returns The extracted beacon URI
194
+ */
195
+ function extractBeaconUri(rum) {
196
+ // Extract the beaconUri from the JavaScript content
197
+ const beaconUriMatch = rum.match(/"beaconUri":"([^"]+)"/);
198
+ if (!beaconUriMatch) {
199
+ throw new Error("Failed to extract beaconUri from RUM JavaScript content.");
200
+ }
201
+ return beaconUriMatch[1];
202
+ }
203
+ /**
204
+ * Waits for a given time and resolves upon timeout.
205
+ *
206
+ * @param time The time to wait in milliseconds
207
+ * @returns A resolved promise when the time ran out
208
+ */
209
+ function wait(time) {
210
+ return new Promise((resolve) => {
211
+ setTimeout(resolve, time);
212
+ });
213
+ }
214
+ /**
215
+ * Extracts the body of a beacon request, optionally decompressing it if the URL indicates compressed data.
216
+ *
217
+ * @param request The request object containing the beacon data
218
+ * @param url The URL of the request to check for specific compression indicators
219
+ * @returns The extracted body of the beacon request, or null if no request body is present
220
+ */
221
+ function extractBeaconBody(request, url) {
222
+ const buffer = request.postDataBuffer();
223
+ if (url.includes("co=snappy") && buffer) {
224
+ const uint8Data = uncompress(buffer);
225
+ return decoder.decode(uint8Data);
226
+ }
227
+ return request.postData();
228
+ }
229
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS90ZXN0aW5nL3Rlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUNILE1BQU0sRUFDTixJQUFJLElBQUksSUFBSSxFQUNmLE1BQU0sa0JBQWtCLENBQUM7QUFDMUIsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ2xELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFrRXRDLE1BQU0sZUFBZSxHQUFHLEtBQUssQ0FBQztBQUU5QixNQUFNLE9BQU8sR0FBRyxJQUFJLFdBQVcsRUFBRSxDQUFDO0FBK0RsQyxNQUFNLENBQUMsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBeUQ7SUFDcEYsZUFBZSxFQUFFLENBQUM7WUFDZCxLQUFLLEVBQUUsRUFBRTtZQUNULFdBQVcsRUFBRSxFQUFFO1lBQ2YsS0FBSyxFQUFFLEVBQUU7U0FDWixFQUFFLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxDQUFDO0lBRXBCLDZIQUE2SDtJQUM3SCxrQkFBa0IsRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFHLEVBQUUsR0FBRyxFQUFFLEVBQUU7WUFDcEMsTUFBTSxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7WUFDeEMsTUFBTSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDckIsQ0FBQyxFQUFFLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsQ0FBQztJQUU5QyxnQkFBZ0IsRUFBRSxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsZUFBZSxFQUFFLGtCQUFrQixFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUU7UUFDM0UsTUFBTSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLGNBQWMsR0FBRyxFQUFFLEVBQUUsZ0JBQWdCLEdB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QUFNLGNBQWMsR0FBRyxNQUFNLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUM5QyxPQUFPLGNBQWMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUNqQyxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLGdCQUFnQixDQUFDLEdBQVc7SUFDakMsb0RBQW9EO0lBQ3BELE1BQU0sY0FBYyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsdUJBQXVCLENBQUMsQ0FBQztJQUMxRCxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDbEIsTUFBTSxJQUFJLEtBQUssQ0FBQywwREFBMEQsQ0FBQyxDQUFDO0lBQ2hGLENBQUM7SUFDRCxPQUFPLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUM3QixDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLElBQUksQ0FBQyxJQUFZO0lBQ3RCLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtRQUMzQixVQUFVLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDO0lBQzlCLENBQUMsQ0FBQyxDQUFDO0FBQ1AsQ0FBQztBQUVEOzs7Ozs7R0FNRztBQUNILFNBQVMsaUJBQWlCLENBQUMsT0FBZ0IsRUFBRSxHQUFXO0lBQ3BELE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxjQUFjLEVBQUUsQ0FBQztJQUN4QyxJQUFJLEdBQUcsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLElBQUksTUFBTSxFQUFFLENBQUM7UUFDdEMsTUFBTSxTQUFTLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ3JDLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUNyQyxDQUFDO0lBQ0QsT0FBTyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7QUFDOUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgUmVxdWVzdCB9IGZyb20gXCJAcGxheXdyaWdodC90ZXN0XCI7XG5pbXBvcnQge1xuICAgIGV4cGVjdCxcbiAgICB0ZXN0IGFzIGJhc2Vcbn0gZnJvbSBcIkBwbGF5d3JpZ2h0L3Rlc3RcIjtcbmltcG9ydCB7IGZldGNoUnVtSmF2YVNjcmlwdCB9IGZyb20gXCIuL2luc3RhbGwuanNcIjtcbmltcG9ydCB7IHVuY29tcHJlc3MgfSBmcm9tIFwic25hcHB5anNcIjtcblxuZXhwb3J0IGludGVyZmFjZSBXYWl0Rm9yQmVhY29uc09wdGlvbnMge1xuICAgIC8qKlxuICAgICAqIFRoZSBtaW5pbXVtIG51bWJlciBvZiBiZWFjb24gcmVxdWVzdHMgdG8gd2FpdCBmb3JcbiAgICAgKi9cbiAgICBtaW5Db3VudD86IG51bWJlcjtcbiAgICAvKipcbiAgICAgKiBUaGUgbWF4aW11bSB0aW1lIHRvIHdhaXQgZm9yIGJlYWNvbiByZXF1ZXN0cywgaW4gbWlsbGlzZWNvbmRzIC0gRGVmYXVsdDogNV8wMDBcbiAgICAgKi9cbiAgICB0aW1lb3V0PzogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEV4cGVjdE9wdGlvbnMge1xuICAgIC8qKlxuICAgICAqIFRoZSBtYXhpbXVtIHRpbWUgdG8gd2FpdCBmb3IgdGhlIGV2ZW50IHRvIGJlIHNlbnQsIGluIG1pbGxpc2Vjb25kcyAtIERlZmF1bHQ6IDVfMDAwXG4gICAgICovXG4gICAgdGltZW91dD86IG51bWJlcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBEeW5hdHJhY2VUZXN0aW5nIHtcbiAgICAvKipcbiAgICAgKiBXYWl0cyBmb3IgYSBzcGVjaWZpZWQgbnVtYmVyIG9mIGJlYWNvbiByZXF1ZXN0cyBvciB1bnRpbCBhIHRpbWVvdXQgb2NjdXJzLlxuICAgICAqXG4gICAgICogQHBhcmFtIG9wdGlvbnMgQ29uZmlndXJhdGlvbiBvcHRpb25zIGZvciB3YWl0aW5nIGZvciBiZWFjb25zXG4gICAgICogQHJldHVybnMgICAgICAgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2l0aCBhbiBhcnJheSBvZiBiZWFjb24gcmVxdWVzdHNcbiAgICAgKi9cbiAgICB3YWl0Rm9yQmVhY29ucyhvcHRpb25zPzogV2FpdEZvckJlYWNvbnNPcHRpb25zKTogUHJvbWlzZTxCZWFjb25SZXF1ZXN0W10+O1xuXG4gICAgLyoqXG4gICAgICogVmVyaWZpZXMgdGhhdCBhIHNwZWNpZmljIGV2ZW50IGhhcyBiZWVuIHNlbnQsIG9wdGlvbmFsbHkgd2l0aGluIGEgdGltZW91dCBwZXJpb2QuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gZXZlbnQgICBUaGUgZXZlbnQgdG8gY2hlY2ssIHJlcHJlc2VudGVkIGFzIGEga2V5LXZhbHVlIHBhaXIgb2JqZWN0LiBUaGlzIHVzZXMgUGxheXdyaWdodHMgZXhwZWN0KGV2ZW50KS50b01hdGNoT2JqZWN0LlxuICAgICAqIEBwYXJhbSBvcHRpb25zIENvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgdGhlIHZlcmlmaWNhdGlvblxuICAgICAqIEByZXR1cm5zICAgICAgIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIGV2ZW50IGlzIGNvbmZpcm1lZCB0byBoYXZlIGJlZW4gc2VudFxuICAgICAqL1xuICAgIGV4cGVjdFRvSGF2ZVNlbnRFdmVudChldmVudDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sIG9wdGlvbnM/OiBFeHBlY3RPcHRpb25zKTogUHJvbWlzZTx2b2lkPjtcblxuICAgIC8qKlxuICAgICAqIFZlcmlmaWVzIHRoYXQgYSBzcGVjaWZpYyBldmVudCBoYXMgYmVlbiBzZW50IGEgc3BlY2lmaWVkIG51bWJlciBvZiB0aW1lcywgb3B0aW9uYWxseSB3aXRoaW4gYSB0aW1lb3V0IHBlcmlvZC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBldmVudCAgIFRoZSBldmVudCB0byBjaGVjaywgcmVwcmVzZW50ZWQgYXMgYSBrZXktdmFsdWUgcGFpciBvYmplY3QuIFRoaXMgdXNlcyBQbGF5d3JpZ2h0cyBleHBlY3QoZXZlbnQpLnRvTWF0Y2hPYmplY3QuXG4gICAgICogQHBhcmFtIHRpbWVzICAgVGhlIGV4YWN0IG51bWJlciBvZiB0aW1lcyB0aGUgZXZlbnQgaXMgZXhwZWN0ZWQgdG8gaGF2ZSBiZWVuIHNlbnRcbiAgICAgKiBAcGFyYW0gb3B0aW9ucyBDb25maWd1cmF0aW9uIG9wdGlvbnMgZm9yIHRoZSB2ZXJpZmljYXRpb25cbiAgICAgKiBAcmV0dXJucyAgICAgICBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBldmVudCBpcyBjb25maXJtZWQgdG8gaGF2ZSBiZWVuIHNlbnQgdGhlIHNwZWNpZmllZCBudW1iZXIgb2YgdGltZXNcbiAgICAgKi9cbiAgICBleHBlY3RUb0hhdmVTZW50RXZlbnRUaW1lcyhldmVudDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sIHRpbWVzOiBudW1iZXIsIG9wdGlvbnM/OiBFeHBlY3RPcHRpb25zKTogUHJvbWlzZTx2b2lkPjtcblxuICAgIC8qKlxuICAgICAqIENsZWFycyBhbGwgZXZlbnRzIGFuZCBiZWFjb25zLCBhbGxvd2luZyBmb3Igc3Vic2VxdWVudCBleHBlY3RhdGlvbnMgdG8gaWdub3JlIGV2ZW50cyB0aGF0IGhhdmUgYmVlbiBzZW50XG4gICAgICogaW4gdGhlIHBhc3QuIEV4YW1wbGU6XG4gICAgICogYGBgXG4gICAgICogc2VuZEZvb0V2ZW50KCk7XG4gICAgICogYXdhaXQgZHluYXRyYWNlVGVzdGluZy5leHBlY3RUb0hhdmVTZW50RXZlbnRUaW1lcyh7IGZvbzogXCJiYXJcIiB9LCAxKTtcbiAgICAgKiBkeW5hdHJhY2VUZXN0aW5nLmNsZWFyRXZlbnRzKCk7XG4gICAgICogYXdhaXQgZHluYXRyYWNlVGVzdGluZy5leHBlY3RUb0hhdmVTZW50RXZlbnRUaW1lcyh7IGZvbzogXCJiYXJcIiB9LCAxKTtcbiAgICAgKiBgYGBcbiAgICAgKi9cbiAgICBjbGVhckV2ZW50cygpOiB2b2lkO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEJlYWNvblJlcXVlc3Qge1xuICAgIHVybDogc3RyaW5nO1xuICAgIGJvZHk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xufVxuXG5jb25zdCBERUZBVUxUX1RJTUVPVVQgPSA1XzAwMDtcblxuY29uc3QgZGVjb2RlciA9IG5ldyBUZXh0RGVjb2RlcigpO1xuXG5leHBvcnQgaW50ZXJmYWNlIER5bmF0cmFjZVRlc3RpbmdGaXh0dXJlIHtcbiAgICAvKipcbiAgICAgKiBUaGUgdGVzdGluZyBBUEkgcHJvdmlkaW5nIGV4cGVjdCBtZXRob2RzLlxuICAgICAqL1xuICAgIGR5bmF0cmFjZVRlc3Rpbmc6IER5bmF0cmFjZVRlc3Rpbmc7XG5cbiAgICAvKipcbiAgICAgKiBDb25maWd1cmF0aW9uIHRvIGVuYWJsZSBEeW5hdHJhY2UgdGVzdGluZyB3aXRoIGEgZ2l2ZW4gZW52aXJvbm1lbnQuXG4gICAgICovXG4gICAgZHluYXRyYWNlQ29uZmlnOiBEeW5hdHJhY2VDb25maWc7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRHluYXRyYWNlQ29uZmlnIHtcbiAgICAvKipcbiAgICAgKiBUaGUgVVJMIG9mIHRoZSBEeW5hdHJhY2UgQVBJIGVuZHBvaW50IHRvIGNvbm5lY3QgdG8sIHNlZVxuICAgICAqIFtSVU0gbWFudWFsIGluc2VydGlvbiB0YWdzIEFQSV0oaHR0cHM6Ly9kb2NzLmR5bmF0cmFjZS5jb20vZG9jcy9kaXNjb3Zlci1keW5hdHJhY2UvcmVmZXJlbmNlcy9keW5hdHJhY2UtYXBpL2Vudmlyb25tZW50LWFwaS9ydW0vcnVtLW1hbnVhbC1pbnNlcnRpb24tdGFncylcbiAgICAgKiBmb3IgZGV0YWlscy4gRXhhbXBsZTogaHR0cHM6Ly97eW91ci1lbnZpcm9ubWVudC1pZH0ubGl2ZS5keW5hdHJhY2UuY29tIG9yXG4gICAgICogaHR0cHM6Ly97eW91ci1hY3RpdmVnYXRlLWRvbWFpbn0vZS97eW91ci1lbnZpcm9ubWVudC1pZH1cbiAgICAgKi9cbiAgICBlbmRwb2ludFVybDogc3RyaW5nO1xuXG4gICAgLyoqXG4gICAgICogVGhlIGFwcGxpY2F0aW9uIElEIHRvIGlkZW50aWZ5IHRoZSBjb3JyZWN0IFJVTSBKYXZhU2NyaXB0IHRvIGZldGNoLiBZb3UgY2FuIGZpbmRcbiAgICAgKiB0aGlzIGluIHRoZSBVUkwgaWYgeW91IG9wZW4gdGhlIEV4cGVyaWVuY2UgVml0YWxzIGFwcCBhbmQgc2VsZWN0IHRoZSBmcm9udGVuZCB5b3VcbiAgICAgKiB3YW50IHRvIHRlc3QuIEV4YW1wbGU6IEFQUExJQ0FUSU9OLUFCQ0RFRjAxMjM0NTY3ODlcbiAgICAgKi9cbiAgICBhcHBJZDogc3RyaW5nO1xuXG4gICAgLyoqXG4gICAgICogVGhlIGF1dGhlbnRpY2F0aW9uIHRva2VuIGZvciB0aGUgaW5zdGFsbGF0aW9uLiBTZWVcbiAgICAgKiBbVG9rZW5zIGFuZCBhdXRoZW50aWNhdGlvbl0oaHR0cHM6Ly9kb2NzLmR5bmF0cmFjZS5jb20vZG9jcy9kaXNjb3Zlci1keW5hdHJhY2UvcmVmZXJlbmNlcy9keW5hdHJhY2UtYXBpL2Jhc2ljcy9keW5hdHJhY2UtYXBpLWF1dGhlbnRpY2F0aW9uKS5cbiAgICAgKi9cbiAgICB0b2tlbjogc3RyaW5nO1xuXG4gICAgLyoqXG4gICAgICogQWNjZXB0cyBhbiBhcnJheSBvZiByZWd1bGFyIGV4cHJlc3Npb25zIHRvIG1hdGNoIGFnYWluc3QgRHluYXRyYWNlLXJlbGF0ZWQgd2FybmluZ3MuIEJ5IGRlZmF1bHQsIHRoZXNlIHdhcm5pbmdzXG4gICAgICogdHJpZ2dlciB0ZXN0IGZhaWxzLiBJZiB5b3UgYW50aWNpcGF0ZSB3YXJuaW5ncyBhbmQgZG9uJ3Qgd2FudCB5b3VyIHRlc3RzIHRvIGZhaWwsIHVzZSB0aGlzIHNldHRpbmcgdG8gaWdub3JlIHRoZW1cbiAgICAgKiBieSBtZXNzYWdlLiBFeGFtcGxlOlxuICAgICAqIFtcImludmFsaWQgcHJvcGVydHkgbXlfYXBwX2RhdGFcXC5cXFxcdytbMC05X10qXCJdXG4gICAgICovXG4gICAgaWdub3JlV2FybmluZ3M/OiBzdHJpbmdbXTtcblxuICAgIC8qKlxuICAgICAqIER1bXAgdGhlIGFycmF5IG9mIER5bmF0cmFjZSBldmVudHMgaW50byB0aGUgY29uc29sZSBpbiBjYXNlIGFuIGFzc2VydGlvbiBmYWlscy5cbiAgICAgKi9cbiAgICBkdW1wRXZlbnRzT25GYWlsPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBXb3JrZXItc2NvcGVkIGZpeHR1cmVzIGZvciBjYWNoaW5nIFJVTSBKYXZhU2NyaXB0IGFjcm9zcyB0ZXN0cyBpbiB0aGUgc2FtZSB3b3JrZXIuXG4gKiBUaGlzIGltcHJvdmVzIHBlcmZvcm1hbmNlIGJ5IGF2b2lkaW5nIHJlZHVuZGFudCBuZXR3b3JrIHJlcXVlc3RzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIER5bmF0cmFjZVRlc3RpbmdXb3JrZXJGaXh0dXJlIHtcbiAgICAvKipcbiAgICAgKiBDYWNoZSBmb3IgUlVNIEphdmFTY3JpcHQgc25pcHBldHMsIHNjb3BlZCB0byB0aGUgd29ya2VyLlxuICAgICAqXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcnVtSmF2YVNjcmlwdENhY2hlOiBNYXA8c3RyaW5nLCBzdHJpbmc+O1xufVxuXG5leHBvcnQgY29uc3QgdGVzdCA9IGJhc2UuZXh0ZW5kPER5bmF0cmFjZVRlc3RpbmdGaXh0dXJlLCBEeW5hdHJhY2VUZXN0aW5nV29ya2VyRml4dHVyZT4oe1xuICAgIGR5bmF0cmFjZUNvbmZpZzogW3tcbiAgICAgICAgYXBwSWQ6IFwiXCIsXG4gICAgICAgIGVuZHBvaW50VXJsOiBcIlwiLFxuICAgICAgICB0b2tlbjogXCJcIlxuICAgIH0sIHsgb3B0aW9uOiB0cnVlIH1dLFxuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWVtcHR5LXBhdHRlcm4gLS0gUGxheXdyaWdodCByZXF1aXJlcyBvYmplY3QgZGVzdHJ1Y3R1cmluZyBldmVuIHdoZW4gbm8gZGVwZW5kZW5jaWVzIGFyZSBuZWVkZWRcbiAgICBydW1KYXZhU2NyaXB0Q2FjaGU6IFthc3luYyAoeyB9LCB1c2UpID0+IHsgLy8gTk9TT05BUlxuICAgICAgICBjb25zdCBjYWNoZSA9IG5ldyBNYXA8c3RyaW5nLCBzdHJpbmc+KCk7XG4gICAgICAgIGF3YWl0IHVzZShjYWNoZSk7XG4gICAgfSwgeyBzY29wZTogXCJ3b3JrZXJcIiwgYXV0bzogdHJ1ZSwgYm94OiB0cnVlIH1dLFxuXG4gICAgZHluYXRyYWNlVGVzdGluZzogYXN5bmMgKHsgcGFnZSwgZHluYXRyYWNlQ29uZmlnLCBydW1KYXZhU2NyaXB0Q2FjaGUgfSwgdXNlKSA9PiB7XG4gICAgICAgIGNvbnN0IHsgZW5kcG9pbnRVcmwsIGFwcElkLCB0b2tlbiwgaWdub3JlV2FybmluZ3MgPSBbXSwgZHVtcEV2ZW50c09uRmFpbCA9IGZhbHNlIH0gPSBkeW5hdHJhY2VDb25maWc7XG4gICAgICAgIGNvbnN0IGJlYWNvbnM6IHsgdXJsOiBzdHJpbmcsIGJvZHk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IH1bXSA9IFtdO1xuICAgICAgICBjb25zdCBldmVudHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+W10gPSBbXTtcbiAgICAgICAgY29uc3Qgd2FybmluZ3M6IHN0cmluZ1tdID0gW107XG4gICAgICAgIGNvbnN0IGlnbm9yZVJlZ2V4ZXMgPSBpZ25vcmVXYXJuaW5ncy5tYXAoeCA9PiBuZXcgUmVnRXhwKHgsIFwiaVwiKSk7XG5cbiAgICAgICAgcGFnZS5vbihcImNvbnNvbGVcIiwgKG1zZykgPT4ge1xuICAgICAgICAgICAgaWYgKG1zZy50ZXh0KCkuaW5jbHVkZXMoXCJ5bmF0cmFjZVwiKSAmJiAhaWdub3JlUmVnZXhlcy5zb21lKHJlZ2V4ID0+IHJlZ2V4LnRlc3QobXNnLnRleHQoKSkpKSB7XG4gICAgICAgICAgICAgICAgd2FybmluZ3MucHVzaChtc2cudGV4dCgpKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgYXdhaXQgc2V0dXBSdW1KYXZhU2NyaXB0KCk7XG5cbiAgICAgICAgYXdhaXQgdXNlKHtcbiAgICAgICAgICAgIGFzeW5jIHdhaXRGb3JCZWFjb25zKG9wdGlvbnM6IFdhaXRGb3JCZWFjb25zT3B0aW9ucyA9IHt9KTogUHJvbWlzZTxCZWFjb25SZXF1ZXN0W10+IHtcbiAgICAgICAgICAgICAgICBjb25zdCB7IG1pbkNvdW50ID0gMCwgdGltZW91dCA9IERFRkFVTFRfVElNRU9VVCB9ID0gb3B0aW9ucztcbiAgICAgICAgICAgICAgICBjb25zdCBzdGFydCA9IERhdGUubm93KCk7XG5cbiAgICAgICAgICAgICAgICB3aGlsZSAoYmVhY29ucy5sZW5ndGggPCBtaW5Db3VudCAmJiAoRGF0ZS5ub3coKSAtIHN0YXJ0KSA8PSB0aW1lb3V0KSB7XG4gICAgICAgICAgICAgICAgICAgIGlmICh3YXJuaW5ncy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvd1dpdGhFdmVudER1bXAoXCJVbmV4cGVjdGVkIER5bmF0cmFjZSBBUEkgd2FybmluZ3M6IFwiICsgd2FybmluZ3Muam9pbihcIlxcblwiKSk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgd2FpdCgxMDApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChiZWFjb25zLmxlbmd0aCA8IG1pbkNvdW50KSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93V2l0aEV2ZW50RHVtcChgRm91bmQgb25seSAke2JlYWNvbnMubGVuZ3RofSBEeW5hdHJhY2UgYmVhY29ucyBhZnRlciB0aW1lb3V0IG9mICR7dGltZW91dH1tcywgYnV0IGV4cGVjdGVkIGF0IGxlYXN0ICR7bWluQ291bnR9LmApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJldHVybiBiZWFjb25zO1xuICAgICAgICAgICAgfSxcblxuICAgICAgICAgICAgYXN5bmMgZXhwZWN0VG9IYXZlU2VudEV2ZW50KFxuICAgICAgICAgICAgICAgIGV4cGVjdGVkRXZlbnQ6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICAgICAgICAgICAgICAgIG9wdGlvbnM6IEV4cGVjdE9wdGlvbnMgPSB7fVxuICAgICAgICAgICAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgICAgICAgICAgY29uc3QgeyB0aW1lb3V0ID0gREVGQVVMVF9USU1FT1VUIH0gPSBvcHRpb25zO1xuICAgICAgICAgICAgICAgIGNvbnN0IHN0YXJ0ID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgICAgICBsZXQgbGFzdE5vbk1hdGNoOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCA9IHZvaWQgMDtcbiAgICAgICAgICAgICAgICBsZXQgbmV4dENoZWNrSW5kZXggPSAwO1xuXG4gICAgICAgICAgICAgICAgd2hpbGUgKChEYXRlLm5vdygpIC0gc3RhcnQpIDw9IHRpbWVvdXQpIHtcbiAgICAgICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IG5leHRDaGVja0luZGV4OyBpIDwgZXZlbnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBiZWFjb25FdmVudCA9IGV2ZW50c1tpXTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwZWN0KGJlYWNvbkV2ZW50KS50b01hdGNoT2JqZWN0KGV4cGVjdGVkRXZlbnQpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3ROb25NYXRjaCA9IGJlYWNvbkV2ZW50O1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgbmV4dENoZWNrSW5kZXggPSBpICsgMTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBhd2FpdCB3YWl0KDEwMCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKCFsYXN0Tm9uTWF0Y2gpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3dXaXRoRXZlbnREdW1wKFwiRHluYXRyYWNlIGRpZG4ndCBzZW5kIGFueSBldmVudHMuXCIpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKGBbRHluYXRyYWNlIFRlc3RpbmddIERpZG4ndCBmaW5kIG1hdGNoaW5nIGV2ZW50cy5gKTtcbiAgICAgICAgICAgICAgICBleHBlY3QobGFzdE5vbk1hdGNoKS50b01hdGNoT2JqZWN0KGV4cGVjdGVkRXZlbnQpO1xuICAgICAgICAgICAgfSxcblxuICAgICAgICAgICAgYXN5bmMgZXhwZWN0VG9IYXZlU2VudEV2ZW50VGltZXMoXG4gICAgICAgICAgICAgICAgZXhwZWN0ZWRFdmVudDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICAgICAgICAgICAgICAgdGltZXM6IG51bWJlcixcbiAgICAgICAgICAgICAgICBvcHRpb25zOiBFeHBlY3RPcHRpb25zID0ge31cbiAgICAgICAgICAgICk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IHsgdGltZW91dCA9IERFRkFVTFRfVElNRU9VVCB9ID0gb3B0aW9ucztcbiAgICAgICAgICAgICAgICBjb25zdCBzdGFydCA9IERhdGUubm93KCk7XG4gICAgICAgICAgICAgICAgbGV0IGxhc3ROb25NYXRjaDogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gfCB1bmRlZmluZWQgPSB2b2lkIDA7XG4gICAgICAgICAgICAgICAgbGV0IG5leHRDaGVja0luZGV4ID0gMDtcbiAgICAgICAgICAgICAgICBsZXQgZm91bmRUaW1lcyA9IDA7XG5cbiAgICAgICAgICAgICAgICB3aGlsZSAoKERhdGUubm93KCkgLSBzdGFydCkgPD0gdGltZW91dCkge1xuICAgICAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gbmV4dENoZWNrSW5kZXg7IGkgPCBldmVudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGJlYWNvbkV2ZW50ID0gZXZlbnRzW2ldO1xuICAgICAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBlY3QoYmVhY29uRXZlbnQpLnRvTWF0Y2hPYmplY3QoZXhwZWN0ZWRFdmVudCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm91bmRUaW1lcysrO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdE5vbk1hdGNoID0gYmVhY29uRXZlbnQ7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBuZXh0Q2hlY2tJbmRleCA9IGkgKyAxO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmIChmb3VuZFRpbWVzID09PSB0aW1lcykge1xuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmIChmb3VuZFRpbWVzID4gdGltZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRocm93V2l0aEV2ZW50RHVtcChgRXhwZWN0ZWQgJHt0aW1lc30gZXZlbnQgb2NjdXJyZW5jZXMsIGZvdW5kICR7Zm91bmRUaW1lc30uYCk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgd2FpdCgxMDApO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmICghbGFzdE5vbk1hdGNoKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93V2l0aEV2ZW50RHVtcChcIkR5bmF0cmFjZSBkaWRuJ3Qgc2VuZCBhbnkgZXZlbnRzXCIpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChmb3VuZFRpbWVzIDwgdGltZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhyb3dXaXRoRXZlbnREdW1wKGBEaWRuJ3QgZmluZCB0aGUgZXhwZWN0ZWQgYW1vdW50IG9mICR7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aW1lc1xuICAgICAgICAgICAgICAgICAgICB9IG1hdGNoaW5nIGV2ZW50cywgZm91bmQgJHtmb3VuZFRpbWVzfS5gKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBleHBlY3QobGFzdE5vbk1hdGNoKS50b01hdGNoT2JqZWN0KGV4cGVjdGVkRXZlbnQpO1xuICAgICAgICAgICAgfSxcblxuICAgICAgICAgICAgY2xlYXJFdmVudHMoKTogdm9pZCB7XG4gICAgICAgICAgICAgICAgZXZlbnRzLmxlbmd0aCA9IDA7XG4gICAgICAgICAgICAgICAgYmVhY29ucy5sZW5ndGggPSAwO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICAvKipcbiAgICAgICAgICogVGhyb3dzIGFuIGVycm9yIHdpdGggYSBjdXN0b20gbWVzc2FnZS4gSWYgZXZlbnQgZHVtcGluZyBpcyBlbmFibGVkLCBsb2dzIHRoZSByZWNlaXZlZCBEeW5hdHJhY2UgZXZlbnRzIGJlZm9yZVxuICAgICAgICAgKiB0aHJvd2luZyB0aGUgZXJyb3IuXG4gICAgICAgICAqXG4gICAgICAgICAqIEBwYXJhbSBtZXNzYWdlIFRoZSBlcnJvciBtZXNzYWdlIHRvIGJlIHRocm93blxuICAgICAgICAgKi9cbiAgICAgICAgZnVuY3Rpb24gdGhyb3dXaXRoRXZlbnREdW1wKG1lc3NhZ2U6IHN0cmluZyk6IG5ldmVyIHtcbiAgICAgICAgICAgIGlmIChkdW1wRXZlbnRzT25GYWlsKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJSZWNlaXZlZCBEeW5hdHJhY2UgZXZlbnRzOlwiLCBldmVudHMpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKG1lc3NhZ2UpO1xuICAgICAgICB9XG5cbiAgICAgICAgYXN5bmMgZnVuY3Rpb24gc2V0dXBSdW1KYXZhU2NyaXB0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICAgICAgLy8gRmV0Y2ggUlVNIEphdmFTY3JpcHQgb25jZSBhbmQgY2FjaGUgaXRcbiAgICAgICAgICAgIGNvbnN0IGNhY2hlS2V5ID0gYCR7ZW5kcG9pbnRVcmx9OiR7YXBwSWR9YDtcbiAgICAgICAgICAgIGxldCBydW0gPSBydW1KYXZhU2NyaXB0Q2FjaGUuZ2V0KGNhY2hlS2V5KTtcbiAgICAgICAgICAgIGxldCBiZWFjb25Vcmk6IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIGlmICghcnVtKSB7XG4gICAgICAgICAgICAgICAgcnVtID0gYXdhaXQgZmV0Y2hSdW1KYXZhU2NyaXB0Q29udGVudChlbmRwb2ludFVybCwgYXBwSWQsIHRva2VuKTtcbiAgICAgICAgICAgICAgICBiZWFjb25VcmkgPSBleHRyYWN0QmVhY29uVXJpKHJ1bSk7XG5cbiAgICAgICAgICAgICAgICBydW1KYXZhU2NyaXB0Q2FjaGUuc2V0KGNhY2hlS2V5LCBydW0pO1xuICAgICAgICAgICAgICAgIHJ1bUphdmFTY3JpcHRDYWNoZS5zZXQoYCR7Y2FjaGVLZXl9OmJlYWNvblVyaWAsIGJlYWNvblVyaSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGJlYWNvblVyaSA9IHJ1bUphdmFTY3JpcHRDYWNoZS5nZXQoYCR7Y2FjaGVLZXl9OmJlYWNvblVyaWApO1xuICAgICAgICAgICAgICAgIGlmICghYmVhY29uVXJpKSB7XG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIkZhaWxlZCB0byByZXRyaWV2ZSBiZWFjb25VcmkgZnJvbSBjYWNoZS5cIik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBMaXN0ZW4gdG8gbmV0d29yayByZXF1ZXN0cyB0byBjYXB0dXJlIGJlYWNvbiBkYXRhXG4gICAgICAgICAgICAvLyBUaGlzIGFwcHJvYWNoIGRvZXNuJ3QgaW50ZXJmZXJlIHdpdGggdXNlci1kZWZpbmVkIHJvdXRlc1xuICAgICAgICAgICAgcGFnZS5vbihcInJlcXVlc3RcIiwgKHJlcXVlc3QpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCB1cmwgPSByZXF1ZXN0LnVybCgpO1xuICAgICAgICAgICAgICAgIC8vIENoZWNrIGlmIHRoaXMgaXMgYSBiZWFjb24gcmVxdWVzdCBtYXRjaGluZyB0aGUgZXh0cmFjdGVkIGJlYWNvblVyaVxuICAgICAgICAgICAgICAgIGlmICghKGJlYWNvblVyaSAmJiB1cmwuc3RhcnRzV2l0aChiZWFjb25VcmkpICYmIHVybC5pbmNsdWRlcyhcInR5PWpzXCIpKSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNvbnN0IGJlYWNvbkJvZHlTdHJpbmcgPSBleHRyYWN0QmVhY29uQm9keShyZXF1ZXN0LCB1cmwpO1xuICAgICAgICAgICAgICAgIGlmICghYmVhY29uQm9keVN0cmluZykge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGJlYWNvbkJvZHk6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBldmVudHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+W107XG4gICAgICAgICAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICAgICAgICB9ID0gSlNPTi5wYXJzZShiZWFjb25Cb2R5U3RyaW5nKTtcbiAgICAgICAgICAgICAgICAgICAgYmVhY29ucy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHVybDogdXJsLFxuICAgICAgICAgICAgICAgICAgICAgICAgYm9keTogYmVhY29uQm9keVxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICAgICAgZXZlbnRzLnB1c2goLi4uYmVhY29uQm9keS5kYXRhLmV2ZW50cyk7XG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIltEeW5hdHJhY2UgVGVzdGluZ10gRmFpbGVkIHRvIHBhcnNlIGJlYWNvbiBib2R5OlwiLCBlcnJvcik7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIC8vIFVzZSBhZGRJbml0U2NyaXB0IHRvIGluamVjdCBSVU0gc2NyaXB0IHRhZyBiZWZvcmUgYW55IHBhZ2Ugc2NyaXB0cyBleGVjdXRlXG4gICAgICAgICAgICBhd2FpdCBwYWdlLmFkZEluaXRTY3JpcHQocnVtKTtcbiAgICAgICAgfVxuICAgIH1cbn0pO1xuXG4vKipcbiAqIEZldGNoZXMgdGhlIFJVTSBKYXZhU2NyaXB0IGNvbnRlbnQgZnJvbSB0aGUgRHluYXRyYWNlIEFQSSBlbmRwb2ludC5cbiAqXG4gKiBAcGFyYW0gZW5kcG9pbnRVcmwgVGhlIFVSTCBvZiB0aGUgRHluYXRyYWNlIEFQSSBlbmRwb2ludFxuICogQHBhcmFtIGFwcElkICAgICAgIFRoZSBhcHBsaWNhdGlvbiBJRCB0byBpZGVudGlmeSB0aGUgY29ycmVjdCBSVU0gSmF2YVNjcmlwdFxuICogQHBhcmFtIHRva2VuICAgICAgIFRoZSBhdXRoZW50aWNhdGlvbiB0b2tlbiBmb3IgdGhlIGluc3RhbGxhdGlvblxuICogQHJldHVybnMgICAgICAgICAgIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBSVU0gSmF2YVNjcmlwdCBjb250ZW50XG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGZldGNoUnVtSmF2YVNjcmlwdENvbnRlbnQoZW5kcG9pbnRVcmw6IHN0cmluZywgYXBwSWQ6IHN0cmluZywgdG9rZW46IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc3QgcnVtQXBpUmVzdWx0ID0gYXdhaXQgZmV0Y2hSdW1KYXZhU2NyaXB0KGVuZHBvaW50VXJsLCBhcHBJZCwgdG9rZW4pO1xuICAgIGlmICghcnVtQXBpUmVzdWx0LmluY2x1ZGVzKFwiX2NvbXBsZXRlLmpzXCIpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkR5bmF0cmFjZSByZWNlaXZlZCB1bmV4cGVjdGVkIFJVTSBKYXZhU2NyaXB0IHdoZW4gcmVxdWVzdGluZyB0aGUgSmF2YVNjcmlwdCBUYWcgdmlhIEFQSS5cIik7XG4gICAgfVxuXG4gICAgLy8gRXh0cmFjdCB0aGUgc3JjIFVSTCBmcm9tIHRoZSBzY3JpcHQgdGFnXG4gICAgY29uc3Qgc3JjTWF0Y2ggPSBydW1BcGlSZXN1bHQubWF0Y2goL3NyYz1cIihbXlwiXSspXCIvKTtcbiAgICBpZiAoIXNyY01hdGNoKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkZhaWxlZCB0byBleHRyYWN0IHNyYyBVUkwgZnJvbSBSVU0gSmF2YVNjcmlwdCB0YWcuXCIpO1xuICAgIH1cbiAgICBjb25zdCBzY3JpcHRVcmwgPSBzcmNNYXRjaFsxXTtcblxuICAgIC8vIEZldGNoIHRoZSBhY3R1YWwgSmF2YVNjcmlwdCBjb250ZW50XG4gICAgY29uc3Qgc2NyaXB0UmVzcG9uc2UgPSBhd2FpdCBmZXRjaChzY3JpcHRVcmwpO1xuICAgIHJldHVybiBzY3JpcHRSZXNwb25zZS50ZXh0KCk7XG59XG5cbi8qKlxuICogRXh0cmFjdHMgdGhlIGJlYWNvbiBVUkkgZnJvbSB0aGUgUlVNIEphdmFTY3JpcHQgY29udGVudC5cbiAqXG4gKiBAcGFyYW0gcnVtIFRoZSBSVU0gSmF2YVNjcmlwdCBjb250ZW50IHRvIGV4dHJhY3QgdGhlIGJlYWNvbiBVUkkgZnJvbVxuICogQHJldHVybnMgICBUaGUgZXh0cmFjdGVkIGJlYWNvbiBVUklcbiAqL1xuZnVuY3Rpb24gZXh0cmFjdEJlYWNvblVyaShydW06IHN0cmluZyk6IHN0cmluZyB7XG4gICAgLy8gRXh0cmFjdCB0aGUgYmVhY29uVXJpIGZyb20gdGhlIEphdmFTY3JpcHQgY29udGVudFxuICAgIGNvbnN0IGJlYWNvblVyaU1hdGNoID0gcnVtLm1hdGNoKC9cImJlYWNvblVyaVwiOlwiKFteXCJdKylcIi8pO1xuICAgIGlmICghYmVhY29uVXJpTWF0Y2gpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRmFpbGVkIHRvIGV4dHJhY3QgYmVhY29uVXJpIGZyb20gUlVNIEphdmFTY3JpcHQgY29udGVudC5cIik7XG4gICAgfVxuICAgIHJldHVybiBiZWFjb25VcmlNYXRjaFsxXTtcbn1cblxuLyoqXG4gKiBXYWl0cyBmb3IgYSBnaXZlbiB0aW1lIGFuZCByZXNvbHZlcyB1cG9uIHRpbWVvdXQuXG4gKlxuICogQHBhcmFtIHRpbWUgVGhlIHRpbWUgdG8gd2FpdCBpbiBtaWxsaXNlY29uZHNcbiAqIEByZXR1cm5zICAgIEEgcmVzb2x2ZWQgcHJvbWlzZSB3aGVuIHRoZSB0aW1lIHJhbiBvdXRcbiAqL1xuZnVuY3Rpb24gd2FpdCh0aW1lOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgc2V0VGltZW91dChyZXNvbHZlLCB0aW1lKTtcbiAgICB9KTtcbn1cblxuLyoqXG4gKiBFeHRyYWN0cyB0aGUgYm9keSBvZiBhIGJlYWNvbiByZXF1ZXN0LCBvcHRpb25hbGx5IGRlY29tcHJlc3NpbmcgaXQgaWYgdGhlIFVSTCBpbmRpY2F0ZXMgY29tcHJlc3NlZCBkYXRhLlxuICpcbiAqIEBwYXJhbSByZXF1ZXN0IFRoZSByZXF1ZXN0IG9iamVjdCBjb250YWluaW5nIHRoZSBiZWFjb24gZGF0YVxuICogQHBhcmFtIHVybCAgICAgVGhlIFVSTCBvZiB0aGUgcmVxdWVzdCB0byBjaGVjayBmb3Igc3BlY2lmaWMgY29tcHJlc3Npb24gaW5kaWNhdG9yc1xuICogQHJldHVybnMgICAgICAgVGhlIGV4dHJhY3RlZCBib2R5IG9mIHRoZSBiZWFjb24gcmVxdWVzdCwgb3IgbnVsbCBpZiBubyByZXF1ZXN0IGJvZHkgaXMgcHJlc2VudFxuICovXG5mdW5jdGlvbiBleHRyYWN0QmVhY29uQm9keShyZXF1ZXN0OiBSZXF1ZXN0LCB1cmw6IHN0cmluZyk6IHN0cmluZyB8IG51bGwge1xuICAgIGNvbnN0IGJ1ZmZlciA9IHJlcXVlc3QucG9zdERhdGFCdWZmZXIoKTtcbiAgICBpZiAodXJsLmluY2x1ZGVzKFwiY289c25hcHB5XCIpICYmIGJ1ZmZlcikge1xuICAgICAgICBjb25zdCB1aW50OERhdGEgPSB1bmNvbXByZXNzKGJ1ZmZlcik7XG4gICAgICAgIHJldHVybiBkZWNvZGVyLmRlY29kZSh1aW50OERhdGEpO1xuICAgIH1cbiAgICByZXR1cm4gcmVxdWVzdC5wb3N0RGF0YSgpO1xufVxuIl19