@envive-ai/react-hooks 0.3.56 → 0.3.58

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 (95) hide show
  1. package/dist/application/commerce-api.cjs +11 -12
  2. package/dist/application/commerce-api.js +11 -12
  3. package/dist/application/logging/index.cjs +13 -0
  4. package/dist/application/logging/index.d.cts +3 -0
  5. package/dist/application/logging/index.d.ts +3 -0
  6. package/dist/application/logging/index.js +4 -0
  7. package/dist/application/logging/logLevel.cjs +123 -0
  8. package/dist/application/logging/logLevel.d.cts +47 -0
  9. package/dist/application/logging/logLevel.d.ts +47 -0
  10. package/dist/application/logging/logLevel.js +115 -0
  11. package/dist/application/logging/logger.cjs +6 -1
  12. package/dist/application/logging/logger.d.cts +13 -0
  13. package/dist/application/logging/logger.d.ts +13 -0
  14. package/dist/application/logging/logger.js +7 -1
  15. package/dist/atoms/app/index.d.cts +7 -7
  16. package/dist/atoms/app/index.d.ts +6 -6
  17. package/dist/atoms/app/variant.d.ts +6 -6
  18. package/dist/atoms/chat/chatState.d.cts +18 -18
  19. package/dist/atoms/chat/chatState.d.ts +19 -19
  20. package/dist/atoms/chat/form.d.cts +2 -2
  21. package/dist/atoms/chat/form.d.ts +2 -2
  22. package/dist/atoms/chat/index.d.ts +2 -2
  23. package/dist/atoms/chat/lastMessage.d.cts +2 -2
  24. package/dist/atoms/chat/lastMessage.d.ts +2 -2
  25. package/dist/atoms/chat/messageQueue.d.cts +6 -6
  26. package/dist/atoms/chat/messageQueue.d.ts +6 -6
  27. package/dist/atoms/chat/performanceMetrics.d.cts +6 -6
  28. package/dist/atoms/chat/performanceMetrics.d.ts +6 -6
  29. package/dist/atoms/chat/renderedWidgetRefs.d.cts +2 -2
  30. package/dist/atoms/chat/renderedWidgetRefs.d.ts +2 -2
  31. package/dist/atoms/chat/replies.d.cts +1 -1
  32. package/dist/atoms/chat/replies.d.ts +3 -3
  33. package/dist/atoms/chat/suggestions.d.cts +2 -2
  34. package/dist/atoms/chat/suggestions.d.ts +2 -2
  35. package/dist/atoms/envive/enviveConfig.d.ts +14 -14
  36. package/dist/atoms/globalSearch/globalSearch.d.cts +5 -5
  37. package/dist/atoms/globalSearch/globalSearch.d.ts +5 -5
  38. package/dist/atoms/org/customerService.d.cts +6 -6
  39. package/dist/atoms/org/customerService.d.ts +6 -6
  40. package/dist/atoms/org/graphqlConfig.d.cts +4 -4
  41. package/dist/atoms/org/graphqlConfig.d.ts +5 -5
  42. package/dist/atoms/org/newOrgConfigAtom.d.cts +2 -2
  43. package/dist/atoms/org/newOrgConfigAtom.d.ts +3 -3
  44. package/dist/atoms/org/orgAnalyticsConfig.d.cts +4 -4
  45. package/dist/atoms/org/orgAnalyticsConfig.d.ts +5 -5
  46. package/dist/atoms/search/chatSearch.d.cts +17 -17
  47. package/dist/atoms/search/chatSearch.d.ts +17 -17
  48. package/dist/atoms/search/searchAPI.d.cts +13 -13
  49. package/dist/atoms/search/searchAPI.d.ts +13 -13
  50. package/dist/atoms/search/types.d.cts +1 -1
  51. package/dist/atoms/search/types.d.ts +1 -1
  52. package/dist/atoms/widget/chatPreviewLoading.d.ts +2 -2
  53. package/dist/contexts/salesAgentContext/salesAgentService.cjs +2 -2
  54. package/dist/contexts/salesAgentContext/salesAgentService.js +2 -2
  55. package/dist/contexts/systemSettingsContext/systemSettingsContext.d.cts +2 -2
  56. package/dist/contexts/types.d.cts +1 -1
  57. package/dist/contexts/types.d.ts +1 -1
  58. package/dist/contexts/typesV3.d.cts +1 -1
  59. package/dist/contexts/typesV3.d.ts +1 -1
  60. package/dist/hooks/ChatToggle/useChatToggle.cjs +10 -8
  61. package/dist/hooks/ChatToggle/useChatToggle.d.cts +4 -3
  62. package/dist/hooks/ChatToggle/useChatToggle.d.ts +4 -3
  63. package/dist/hooks/ChatToggle/useChatToggle.js +11 -9
  64. package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.cts +2 -2
  65. package/dist/hooks/Intersection/useIntersection.cjs +7 -5
  66. package/dist/hooks/Intersection/useIntersection.js +7 -5
  67. package/dist/hooks/Search/useSearchInput.cjs +1 -1
  68. package/dist/hooks/Search/useSearchInput.js +1 -1
  69. package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.cjs +8 -6
  70. package/dist/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.js +8 -6
  71. package/dist/hooks/WidgetInteraction/types.cjs +40 -1
  72. package/dist/hooks/WidgetInteraction/types.d.cts +16 -2
  73. package/dist/hooks/WidgetInteraction/types.d.ts +16 -2
  74. package/dist/hooks/WidgetInteraction/types.js +41 -2
  75. package/dist/hooks/utils.d.cts +1 -1
  76. package/dist/hooks/utils.d.ts +1 -1
  77. package/dist/services/amplitudeService/amplitudeService.cjs +14 -5
  78. package/dist/services/amplitudeService/amplitudeService.js +14 -5
  79. package/package.json +5 -1
  80. package/src/application/__tests__/commerceApiStreaming.test.ts +171 -0
  81. package/src/application/commerce-api.ts +21 -21
  82. package/src/application/logging/__tests__/logger.test.ts +159 -0
  83. package/src/application/logging/index.ts +12 -0
  84. package/src/application/logging/logLevel.ts +162 -0
  85. package/src/application/logging/logger.ts +13 -0
  86. package/src/contexts/salesAgentContext/salesAgentService.ts +1 -1
  87. package/src/contexts/userIdentityContext/__tests__/userIdentityContext.test.tsx +4 -5
  88. package/src/hooks/ChatToggle/__tests__/useChatToggle.test.ts +273 -0
  89. package/src/hooks/ChatToggle/useChatToggle.ts +39 -12
  90. package/src/hooks/Intersection/useIntersection.ts +7 -4
  91. package/src/hooks/TrackComponentVisibleEvent/useTrackComponentVisibleEvent.ts +8 -5
  92. package/src/hooks/WidgetInteraction/__tests__/chatTriggerWidget.test.ts +51 -0
  93. package/src/hooks/WidgetInteraction/types.ts +53 -0
  94. package/src/services/amplitudeService/__tests__/amplitudeService.test.ts +76 -0
  95. package/src/services/amplitudeService/amplitudeService.ts +37 -5
@@ -10,6 +10,14 @@ import { projectToGA4 } from "../ga4ProjectionService/ga4ProjectionService.js";
10
10
 
11
11
  //#region src/services/amplitudeService/amplitudeService.ts
12
12
  const logger = new logger_default("amplitudeService");
13
+ const UTF8_TRANSPORT_PREFIX = "__spiffy_utf8__:";
14
+ const containsSupplementaryUnicode = (value) => Array.from(value).some((character) => (character.codePointAt(0) ?? 0) > 65535);
15
+ const encodeSupplementaryUnicode = (value) => {
16
+ if (typeof value === "string") return containsSupplementaryUnicode(value) ? `${UTF8_TRANSPORT_PREFIX}${encodeURIComponent(value)}` : value;
17
+ if (Array.isArray(value)) return value.map(encodeSupplementaryUnicode);
18
+ if (value !== null && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, nestedValue]) => [key, encodeSupplementaryUnicode(nestedValue)]));
19
+ return value;
20
+ };
13
21
  const EXPERIMENT_NAMESPACES = ["ml", "merchant"];
14
22
  const buildExperimentProps = (assignments) => {
15
23
  const byNamespace = /* @__PURE__ */ new Map();
@@ -257,9 +265,10 @@ var AmplitudeService = class AmplitudeService {
257
265
  eventName,
258
266
  eventProps
259
267
  });
268
+ const amplitudeEventProps = eventName === EnviveMetricsEventName.WidgetTextResponse || eventName === EnviveMetricsEventName.WidgetTextClicked ? encodeSupplementaryUnicode(mappedEventProps) : mappedEventProps;
260
269
  const eventData = JSON.stringify({
261
270
  eventName,
262
- eventProps: mappedEventProps,
271
+ eventProps: amplitudeEventProps,
263
272
  created_at: (/* @__PURE__ */ new Date()).toISOString()
264
273
  });
265
274
  const data = new TextEncoder().encode(eventData);
@@ -267,12 +276,12 @@ var AmplitudeService = class AmplitudeService {
267
276
  const currentInsertId = Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
268
277
  logger.logDebug(`amplitude tracking ${decoratedEventName}`, null, {
269
278
  event_name: decoratedEventName,
270
- props: mappedEventProps
279
+ props: amplitudeEventProps
271
280
  });
272
281
  this.amplitudeClient.track(decoratedEventName, {
273
282
  ...this.getDefaultTrackingProps(eventName),
274
- ...mappedEventProps,
275
- ...mappedEventProps ? this.eventPropsToPrefixedEventProps(eventName, mappedEventProps) : {}
283
+ ...amplitudeEventProps,
284
+ ...amplitudeEventProps ? this.eventPropsToPrefixedEventProps(eventName, amplitudeEventProps) : {}
276
285
  }, {
277
286
  ...eventGroups,
278
287
  insert_id: currentInsertId
@@ -303,4 +312,4 @@ var AmplitudeService = class AmplitudeService {
303
312
 
304
313
  //#endregion
305
314
  export { AmplitudeService };
306
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW1wbGl0dWRlU2VydmljZS5qcyIsIm5hbWVzIjpbIkxvZ2dlciIsIkVYUEVSSU1FTlRfTkFNRVNQQUNFUzogRXhwZXJpbWVudE5hbWVzcGFjZVtdIiwiZW5yaWNobWVudDogRW5yaWNobWVudFBsdWdpbiIsImVucmljaGVkRXZlbnQ6IEV2ZW50IiwiZ2xvYmFsUHJvcGVydGllczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiIsImN1cnJlbnRBbXBsaXR1ZGVJbnN0YW5jZTogQnJvd3NlckNsaWVudCJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9hbXBsaXR1ZGVTZXJ2aWNlL2FtcGxpdHVkZVNlcnZpY2UudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRXZlbnRzRGlzcGF0Y2hlciwgU3BpZmZ5RXZlbnQgfSBmcm9tICdzcmMvZXZlbnRzJztcbmltcG9ydCB7IExvY2FsU3RvcmFnZUtleXMgfSBmcm9tICdzcmMvY29udGV4dHMvbG9jYWxTdG9yYWdlQ29udGV4dCc7XG5pbXBvcnQgTG9nZ2VyIGZyb20gJ3NyYy9hcHBsaWNhdGlvbi9sb2dnaW5nL2xvZ2dlcic7XG5pbXBvcnQgdHlwZSB7XG4gIEJyb3dzZXJDbGllbnQsXG4gIEVucmljaG1lbnRQbHVnaW4sXG4gIEV2ZW50LFxuICBTZXJ2ZXJab25lVHlwZSxcbn0gZnJvbSAnQGFtcGxpdHVkZS9hbmFseXRpY3MtdHlwZXMnO1xuaW1wb3J0IHsgRmVhdHVyZUZsYWdTZXJ2aWNlIH0gZnJvbSAnc3JjL3NlcnZpY2VzL2ZlYXR1cmVGbGFnU2VydmljZSc7XG5pbXBvcnQgeyBXaWRnZXRUeXBlVjMgfSBmcm9tICdzcmMvY29udGV4dHMvdHlwZXNWMyc7XG5pbXBvcnQgeyBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzIH0gZnJvbSAnc3JjL2FwcGxpY2F0aW9uL21vZGVscy9jaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzJztcbmltcG9ydCB7IEV4cGVyaW1lbnROYW1lc3BhY2UsIE9yZ0NvbmZpZ0V4cGVyaW1lbnRBc3NpZ25tZW50IH0gZnJvbSAnc3JjL2FwcGxpY2F0aW9uL21vZGVscyc7XG5pbXBvcnQgeyBwcm9qZWN0VG9HQTQgfSBmcm9tICcuLi9nYTRQcm9qZWN0aW9uU2VydmljZS9nYTRQcm9qZWN0aW9uU2VydmljZSc7XG5pbXBvcnQgeyBFbnZpdmVNZXRyaWNzRXZlbnROYW1lLCBTcGlmZnlNZXRyaWNzRXZlbnROYW1lIH0gZnJvbSAnLi9ldmVudE5hbWVzJztcblxuLy8gUmUtZXhwb3J0IGV2ZW50IG5hbWVzIGZvciBjb252ZW5pZW5jZVxuZXhwb3J0IHsgRW52aXZlTWV0cmljc0V2ZW50TmFtZSwgU3BpZmZ5TWV0cmljc0V2ZW50TmFtZSB9O1xuXG5jb25zdCBsb2dnZXIgPSBuZXcgTG9nZ2VyKCdhbXBsaXR1ZGVTZXJ2aWNlJyk7XG5cbmV4cG9ydCBpbnRlcmZhY2UgVHJhY2tFdmVudFBhcmFtcyB7XG4gIGV2ZW50TmFtZTogU3BpZmZ5TWV0cmljc0V2ZW50TmFtZSB8IEVudml2ZU1ldHJpY3NFdmVudE5hbWU7XG4gIGV2ZW50UHJvcHM/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgZXZlbnRHcm91cHM/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgYWxzb1NlbmRUb0dvb2dsZUFuYWx5dGljcz86IGJvb2xlYW47XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQW1wbGl0dWRlU2VydmljZUNvbmZpZyB7XG4gIHVzZXJJZDogc3RyaW5nO1xuICBhbXBsaXR1ZGVBcGlLZXk6IHN0cmluZztcbiAgZGF0YVJlc2lkZW5jeTogc3RyaW5nO1xuICBlbnY6IHN0cmluZztcbiAgY29udGV4dFNvdXJjZTogc3RyaW5nO1xuICBvcmdTaG9ydE5hbWU6IHN0cmluZztcbiAgb3JnSWQ6IHN0cmluZztcbiAgZmVhdHVyZUZsYWdTZXJ2aWNlOiBGZWF0dXJlRmxhZ1NlcnZpY2U7XG4gIHNob3c6IGJvb2xlYW47XG4gIGVudml2ZU9uOiBib29sZWFuO1xuICBlbmFibGVkRmVhdHVyZXM/OiBSZWNvcmQ8c3RyaW5nLCBib29sZWFuPjtcbiAgZ2V0TG9jYWxTdG9yYWdlSXRlbTogbnVsbCB8ICgoa2V5OiBzdHJpbmcpID0+IHN0cmluZyB8IG51bGwpO1xuICAvLyBDYXB0dXJlZCBhdCBjb25zdHJ1Y3Rpb24uIERyaXZlcyBgZXhwZXJpbWVudC57bnN9LipgIGV2ZW50LXByb3BlcnR5XG4gIC8vIGVucmljaG1lbnQuIFNvdXJjZWQgZnJvbSAvdjEvb3JnL2NvbmZpZyB3aGVuIHRoZSBgdXNlX3VuaWZpZWRfY29uZmlnYFxuICAvLyBnYXRlIGlzIG9uOyBvdGhlcndpc2Ugb21pdHRlZCAocHJvcHMgZW1pdCBhcyBudWxsKS5cbiAgZXhwZXJpbWVudEFzc2lnbm1lbnRzPzogT3JnQ29uZmlnRXhwZXJpbWVudEFzc2lnbm1lbnRbXTtcbn1cblxuLy8gTmFtZXNwYWNlcyBlbWl0dGVkIG9uIGV2ZXJ5IGV2ZW50IHdpdGggY29uc3RhbnQga2V5cywgc28gdGhlIEJpZ1F1ZXJ5XG4vLyBzY2hlbWEgaXMgc3RhYmxlIHJlZ2FyZGxlc3Mgb2Ygd2hpY2ggKGlmIGFueSkgZXhwZXJpbWVudCBpcyBydW5uaW5nLlxuLy8gQ3VycmVudGx5IGBtbGAgKyBgbWVyY2hhbnRgOyBtYXRjaGVzIGBFeHBlcmltZW50TmFtZXNwYWNlYC5cbmNvbnN0IEVYUEVSSU1FTlRfTkFNRVNQQUNFUzogRXhwZXJpbWVudE5hbWVzcGFjZVtdID0gWydtbCcsICdtZXJjaGFudCddO1xuXG5jb25zdCBidWlsZEV4cGVyaW1lbnRQcm9wcyA9IChcbiAgYXNzaWdubWVudHM6IE9yZ0NvbmZpZ0V4cGVyaW1lbnRBc3NpZ25tZW50W10gfCB1bmRlZmluZWQsXG4pOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9PiB7XG4gIGNvbnN0IGJ5TmFtZXNwYWNlID0gbmV3IE1hcDxFeHBlcmltZW50TmFtZXNwYWNlLCBPcmdDb25maWdFeHBlcmltZW50QXNzaWdubWVudD4oKTtcbiAgKGFzc2lnbm1lbnRzID8/IFtdKS5mb3JFYWNoKGEgPT4ge1xuICAgIGlmIChhLmxheWVyX25hbWUgIT0gbnVsbCAmJiBhLm5hbWVzcGFjZSAhPSBudWxsKSB7XG4gICAgICBieU5hbWVzcGFjZS5zZXQoYS5uYW1lc3BhY2UsIGEpO1xuICAgIH1cbiAgfSk7XG4gIHJldHVybiBFWFBFUklNRU5UX05BTUVTUEFDRVMucmVkdWNlPFJlY29yZDxzdHJpbmcsIHVua25vd24+PigoYWNjLCBucykgPT4ge1xuICAgIGNvbnN0IGEgPSBieU5hbWVzcGFjZS5nZXQobnMpO1xuICAgIGFjY1tgZXhwZXJpbWVudC4ke25zfS5sYXllcl9uYW1lYF0gPSBhPy5sYXllcl9uYW1lID8/IG51bGw7XG4gICAgYWNjW2BleHBlcmltZW50LiR7bnN9LmV4cGVyaW1lbnRfbmFtZWBdID0gYT8uYWxsb2NhdGVkX2V4cGVyaW1lbnRfbmFtZSA/PyBudWxsO1xuICAgIGFjY1tgZXhwZXJpbWVudC4ke25zfS5ncm91cF9uYW1lYF0gPSBhPy5ncm91cF9uYW1lID8/IG51bGw7XG4gICAgLy8gYHJ1bGVfaWRgIHJlc2VydmVkIOKAlCBiYWNrZW5kIGRvZXNuJ3QgY3VycmVudGx5IHN1cmZhY2UgaXQuXG4gICAgYWNjW2BleHBlcmltZW50LiR7bnN9LnJ1bGVfaWRgXSA9IG51bGw7XG4gICAgcmV0dXJuIGFjYztcbiAgfSwge30pO1xufTtcblxuZXhwb3J0IGNsYXNzIEFtcGxpdHVkZVNlcnZpY2Uge1xuICBwcml2YXRlIGFtcGxpdHVkZUNsaWVudDogQnJvd3NlckNsaWVudCB8IHVuZGVmaW5lZDtcblxuICBwcml2YXRlIGludGVybmFsRXZlbnRUcmFja2luZ0VucmljaG1lbnQ6IEVucmljaG1lbnRQbHVnaW4gfCB1bmRlZmluZWQ7XG5cbiAgcHJpdmF0ZSBzdXBwbGVtZW50YWxEZWZhdWx0UHJvcHM6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0ge307XG5cbiAgcHJpdmF0ZSBjb25maWc6IEFtcGxpdHVkZVNlcnZpY2VDb25maWc7XG5cbiAgcHJpdmF0ZSBpbml0UHJvbWlzZTogUHJvbWlzZTx2b2lkPjtcblxuICBjb25zdHJ1Y3Rvcihjb25maWc6IEFtcGxpdHVkZVNlcnZpY2VDb25maWcpIHtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICB0aGlzLmluaXRQcm9taXNlID0gdGhpcy5pbml0aWFsaXplKCk7XG4gIH1cblxuICBnZXQgaXNNb2NrQXBpS2V5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmNvbmZpZy5hbXBsaXR1ZGVBcGlLZXkgPT09ICdtb2NrLWFtcGxpdHVkZS1rZXknO1xuICB9XG5cbiAgLy8gTXVzdCBub3QgZGVwZW5kIG9uIGBhbXBsaXR1ZGVDbGllbnRgOiB0aGUgU0RLIG5vdyBsb2FkcyB2aWEgZHluYW1pY1xuICAvLyBpbXBvcnQsIGFuZCBBbXBsaXR1ZGVQcm92aWRlciB1bm1vdW50cyBpdHMgZW50aXJlIHN1YnRyZWUgd2hpbGVcbiAgLy8gaXNSZWFkeSBpcyBmYWxzZSDigJQgZ2F0aW5nIG9uIHRoZSBjbGllbnQgd291bGQgYmxvY2sgd2lkZ2V0IHJlbmRlcmluZ1xuICAvLyBvbiB0aGUgYW5hbHl0aWNzIGNodW5rIGRvd25sb2FkLiBFdmVudHMgYXdhaXRlZCBpbiB0cmFja0V2ZW50IHF1ZXVlXG4gIC8vIGJlaGluZCBgaW5pdFByb21pc2VgIGluc3RlYWQuXG4gIGdldCBpc1JlYWR5KCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBCb29sZWFuKFxuICAgICAgdGhpcy5jb25maWcudXNlcklkICYmXG4gICAgICB0aGlzLmNvbmZpZy5mZWF0dXJlRmxhZ1NlcnZpY2UgJiZcbiAgICAgIHRoaXMuY29uZmlnLmFtcGxpdHVkZUFwaUtleSAmJlxuICAgICAgIXRoaXMuaXNNb2NrQXBpS2V5LFxuICAgICk7XG4gIH1cblxuICAvKiogUmVzb2x2ZXMgb25jZSBTREsgaW5pdGlhbGl6YXRpb24gaGFzIHNldHRsZWQgKG5ldmVyIHJlamVjdHMpLiAqL1xuICBpbml0aWFsaXplZCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gdGhpcy5pbml0UHJvbWlzZTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0TG9jYWxTdG9yYWdlSXRlbShrZXk6IHN0cmluZyk6IHN0cmluZyB8IG51bGwge1xuICAgIGlmICh0aGlzLmNvbmZpZy5nZXRMb2NhbFN0b3JhZ2VJdGVtKSB7XG4gICAgICByZXR1cm4gdGhpcy5jb25maWcuZ2V0TG9jYWxTdG9yYWdlSXRlbShrZXkpO1xuICAgIH1cbiAgICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93LmxvY2FsU3RvcmFnZSkge1xuICAgICAgcmV0dXJuIHdpbmRvdy5sb2NhbFN0b3JhZ2UuZ2V0SXRlbShrZXkpO1xuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0RGVmYXVsdFRyYWNraW5nUHJvcHMoXG4gICAgZXZlbnROYW1lOiBTcGlmZnlNZXRyaWNzRXZlbnROYW1lIHwgRW52aXZlTWV0cmljc0V2ZW50TmFtZSxcbiAgKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICAgIGNvbnN0IGZlYXR1cmVHYXRlcyA9IE9iamVjdC5lbnRyaWVzKHRoaXMuY29uZmlnLmZlYXR1cmVGbGFnU2VydmljZS5nZXRGZWF0dXJlRmxhZ3MoKSk7XG4gICAgY29uc3QgZ2F0ZXNQcm9wcyA9XG4gICAgICBmZWF0dXJlR2F0ZXMubGVuZ3RoID4gMFxuICAgICAgICA/IGZlYXR1cmVHYXRlcy5yZWR1Y2U8UmVjb3JkPHN0cmluZywgYm9vbGVhbj4+KChhY2MsIFtuYW1lLCB2YWx1ZV0pID0+IHtcbiAgICAgICAgICAgIGlmIChuYW1lICYmIHZhbHVlICE9IG51bGwpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHsgLi4uYWNjLCBbYGZlYXR1cmVfZ2F0ZS4ke25hbWV9YF06IHZhbHVlIH07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgICAgIH0sIHt9KVxuICAgICAgICA6IHt9O1xuICAgIGNvbnN0IGV4cGVyaW1lbnRQcm9wcyA9IGJ1aWxkRXhwZXJpbWVudFByb3BzKHRoaXMuY29uZmlnLmV4cGVyaW1lbnRBc3NpZ25tZW50cyk7XG5cbiAgICBjb25zdCBlbnZpcm9ubWVudFByb3BzID0ge1xuICAgICAgJ2Vudmlyb25tZW50LmV4ZWN1dGlvbl9jb250ZXh0JzogJ2J1bmRsZScsXG4gICAgICAnZW52aXJvbm1lbnQucGFnZV91cmwnOiB3aW5kb3cubG9jYXRpb24uaHJlZixcbiAgICAgICdlbnZpcm9ubWVudC5lbnZpdmVfdXNlcl9pZCc6IHRoaXMuY29uZmlnLnVzZXJJZCxcbiAgICAgICdlbnZpcm9ubWVudC5leGVjdXRpb25fZW52aXJvbm1lbnQnOiB0aGlzLmNvbmZpZy5lbnYgfHwgJ3Vua25vd24nLFxuICAgICAgJ2Vudmlyb25tZW50LmNvbnRleHRfc291cmNlJzogdGhpcy5jb25maWcuY29udGV4dFNvdXJjZSxcbiAgICAgICdlbnZpcm9ubWVudC5zYWxlc19hZ2VudF9lbmFibGVkJzogdGhpcy5jb25maWcuZW5hYmxlZEZlYXR1cmVzPy5zYWxlc0FnZW50ID8/IGZhbHNlLFxuICAgICAgJ2Vudmlyb25tZW50LnNlYXJjaF9lbmFibGVkJzogdGhpcy5jb25maWcuZW5hYmxlZEZlYXR1cmVzPy5zZWFyY2hBZ2VudCA/PyBmYWxzZSxcbiAgICAgICdlbnZpcm9ubWVudC5lbnZpdmVfb25fcXVlcnlfcGFyYW0nOiB0aGlzLmNvbmZpZy5lbnZpdmVPbixcbiAgICAgICdlbnZpcm9ubWVudC53aW5kb3dfc2hvdyc6IHRoaXMuY29uZmlnLnNob3csXG4gICAgICAnZW52aXJvbm1lbnQuZW52aXZlX2VuYWJsZWQnOiB0aGlzLmNvbmZpZy5lbmFibGVkRmVhdHVyZXM/LmVudml2ZSxcbiAgICB9O1xuXG4gICAgY29uc3Qgb3JnTGV2ZWxBbXBsaXR1ZGVUcmFja2luZ1Byb3BzID0gQW1wbGl0dWRlU2VydmljZS5pc1YzRW52aXZlRXZlbnQoZXZlbnROYW1lKVxuICAgICAgPyB7XG4gICAgICAgICAgLi4udGhpcy5jb25maWcuZmVhdHVyZUZsYWdTZXJ2aWNlLmdldEZ1bGxGbGFnVmFsdWVzKCksXG4gICAgICAgICAgLi4uZXhwZXJpbWVudFByb3BzLFxuICAgICAgICAgIC4uLmVudmlyb25tZW50UHJvcHMsXG4gICAgICAgIH1cbiAgICAgIDoge1xuICAgICAgICAgIC4uLmdhdGVzUHJvcHMsXG4gICAgICAgICAgLi4uZXhwZXJpbWVudFByb3BzLFxuICAgICAgICB9O1xuICAgIHJldHVybiB7XG4gICAgICAuLi5vcmdMZXZlbEFtcGxpdHVkZVRyYWNraW5nUHJvcHMsXG4gICAgICAuLi50aGlzLnN1cHBsZW1lbnRhbERlZmF1bHRQcm9wcyxcbiAgICAgIGFwcF9pZDogJ2NvbW1lcmNlLWNoYXQtcmVhY3QtY29tcG9uZW50JyxcbiAgICAgIGNoYXRfaWQ6IHRoaXMuZ2V0TG9jYWxTdG9yYWdlSXRlbShMb2NhbFN0b3JhZ2VLZXlzLkNoYXRJZCksXG4gICAgICBlbnY6IHRoaXMuY29uZmlnLmVudiB8fCAndW5rbm93bicsXG4gICAgICBhcHBfc291cmNlOiB0aGlzLmNvbmZpZy5jb250ZXh0U291cmNlLFxuICAgICAgb3JnX2lkOiB0aGlzLmNvbmZpZy5vcmdJZCxcbiAgICAgICdvcmcuc2hvcnRfbmFtZSc6IHRoaXMuY29uZmlnLm9yZ1Nob3J0TmFtZSxcbiAgICAgICd1c2VyLmlkJzogdGhpcy5jb25maWcudXNlcklkLFxuICAgICAgJ2NkcC51c2VyX2lkJzogbnVsbCxcbiAgICAgICdjZHAucHJvdmlkZXInOiBudWxsLFxuICAgICAgJ2V2ZW50LnNvdXJjZSc6ICd3ZWItYnJvd3NlcicsXG4gICAgICAnZXZlbnQudHlwZSc6ICd1c2VyLWFjdGl2aXR5JyxcbiAgICAgICdldmVudC5pZCc6IG51bGwsXG4gICAgICAnZXZlbnQuY2hhbm5lbCc6ICd3ZWInLFxuICAgICAgJ2V2ZW50LnRpbWVzdGFtcCc6IG51bGwsXG4gICAgfTtcbiAgfVxuXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjbGFzcy1tZXRob2RzLXVzZS10aGlzXG4gIHByaXZhdGUgZXZlbnRQcm9wc1RvUHJlZml4ZWRFdmVudFByb3BzKFxuICAgIGV2ZW50TmFtZTogU3BpZmZ5TWV0cmljc0V2ZW50TmFtZSB8IEVudml2ZU1ldHJpY3NFdmVudE5hbWUsXG4gICAgZXZlbnRQcm9wczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgICBjb25zdCBwcmVmaXggPSBldmVudE5hbWUudG9Mb3dlckNhc2UoKS5yZXBsYWNlKC9cXHMrL2csICdfJyk7XG4gICAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKGV2ZW50UHJvcHMpLnJlZHVjZShcbiAgICAgIChhY2MsIFtrZXksIHZhbHVlXSkgPT4ge1xuICAgICAgICBhY2NbYCR7cHJlZml4fS4ke2tleX1gXSA9IHZhbHVlO1xuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSxcbiAgICAgIHt9IGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+LFxuICAgICk7XG4gIH1cblxuICBwcml2YXRlIGdldEV2ZW50VHJhY2tpbmdFbnJpY2htZW50KCk6IEVucmljaG1lbnRQbHVnaW4ge1xuICAgIGlmICh0aGlzLmludGVybmFsRXZlbnRUcmFja2luZ0VucmljaG1lbnQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIHRoaXMuaW50ZXJuYWxFdmVudFRyYWNraW5nRW5yaWNobWVudDtcbiAgICB9XG5cbiAgICBjb25zdCBlbnJpY2htZW50OiBFbnJpY2htZW50UGx1Z2luID0ge1xuICAgICAgbmFtZTogJ3BhZ2Utdmlldy10cmFja2luZy1lbnJpY2htZW50JyxcbiAgICAgIHR5cGU6ICdlbnJpY2htZW50JyxcbiAgICAgIHNldHVwOiBhc3luYyAoKSA9PiB1bmRlZmluZWQsXG4gICAgICBleGVjdXRlOiBhc3luYyAoZXZlbnQ6IEV2ZW50KTogUHJvbWlzZTxFdmVudD4gPT4ge1xuICAgICAgICBsZXQgZW5yaWNoZWRFdmVudDogRXZlbnQ7XG5cbiAgICAgICAgY29uc3QgZXZlbnRzVG9FbnJpY2ggPSBbXG4gICAgICAgICAgJ1tBbXBsaXR1ZGVdIFBhZ2UgVmlld2VkJyxcbiAgICAgICAgICBgW1NwaWZmeV0gJHtTcGlmZnlNZXRyaWNzRXZlbnROYW1lLkJ1bmRsZUxvYWRlZH1gLFxuICAgICAgICAgIGBbRW52aXZlXSAke0Vudml2ZU1ldHJpY3NFdmVudE5hbWUuUGFnZVZpZXdlZH1gLFxuICAgICAgICBdO1xuXG4gICAgICAgIGlmIChldmVudHNUb0VucmljaC5pbmNsdWRlcyhldmVudC5ldmVudF90eXBlKSkge1xuICAgICAgICAgIGNvbnN0IGdsb2JhbFByb3BlcnRpZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fTtcblxuICAgICAgICAgIGlmICh0aGlzLmNvbmZpZy5zaG93ICE9IG51bGwpIHtcbiAgICAgICAgICAgIGdsb2JhbFByb3BlcnRpZXNbJ2dsb2JhbFByb3BlcnRpZXMuc2hvdyddID0gU3RyaW5nKHRoaXMuY29uZmlnLnNob3cpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBnbG9iYWxQcm9wZXJ0aWVzWydnbG9iYWxQcm9wZXJ0aWVzLmVudiddID0gU3RyaW5nKHRoaXMuY29uZmlnLmVudik7XG5cbiAgICAgICAgICBjb25zdCBlbmFibGVkRmVhdHVyZXNQcm9wZXJ0aWVzID0gT2JqZWN0LmVudHJpZXMoXG4gICAgICAgICAgICB0aGlzLmNvbmZpZz8uZW5hYmxlZEZlYXR1cmVzID8/ICh7fSBhcyBSZWNvcmQ8c3RyaW5nLCBib29sZWFuPiksXG4gICAgICAgICAgKS5yZWR1Y2U8UmVjb3JkPHN0cmluZywgc3RyaW5nPj4oXG4gICAgICAgICAgICAoYWNjLCBba2V5LCB2YWx1ZV0pID0+ICh7XG4gICAgICAgICAgICAgIC4uLmFjYyxcbiAgICAgICAgICAgICAgW2BlbmFibGVkRmVhdHVyZXMuJHtrZXl9YF06IGAke3ZhbHVlfWAsXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIHt9LFxuICAgICAgICAgICk7XG5cbiAgICAgICAgICBjb25zdCB0aW1pbmdQcm9wZXJ0aWVzID0ge1xuICAgICAgICAgICAgJ3RpbWluZy5lbnJpY2hlZF9hdF9tcyc6XG4gICAgICAgICAgICAgIHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnID8gd2luZG93LnBlcmZvcm1hbmNlPy5ub3coKSA6IHVuZGVmaW5lZCxcbiAgICAgICAgICB9O1xuXG4gICAgICAgICAgZW5yaWNoZWRFdmVudCA9IHtcbiAgICAgICAgICAgIC4uLmV2ZW50LFxuICAgICAgICAgICAgZXZlbnRfcHJvcGVydGllczoge1xuICAgICAgICAgICAgICAuLi5ldmVudC5ldmVudF9wcm9wZXJ0aWVzLFxuICAgICAgICAgICAgICAuLi50aGlzLmdldERlZmF1bHRUcmFja2luZ1Byb3BzKFxuICAgICAgICAgICAgICAgIGV2ZW50LmV2ZW50X3R5cGUgYXMgU3BpZmZ5TWV0cmljc0V2ZW50TmFtZSB8IEVudml2ZU1ldHJpY3NFdmVudE5hbWUsXG4gICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgIC4uLmdsb2JhbFByb3BlcnRpZXMsXG4gICAgICAgICAgICAgIC4uLmVuYWJsZWRGZWF0dXJlc1Byb3BlcnRpZXMsXG4gICAgICAgICAgICAgIC4uLnRpbWluZ1Byb3BlcnRpZXMsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH07XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZW5yaWNoZWRFdmVudCA9IGV2ZW50O1xuICAgICAgICB9XG5cbiAgICAgICAgRXZlbnRzRGlzcGF0Y2hlci5kaXNwYXRjaChTcGlmZnlFdmVudC5BTVBMSVRVREVfRVZFTlQsIGVucmljaGVkRXZlbnQpO1xuXG4gICAgICAgIHJldHVybiBlbnJpY2hlZEV2ZW50O1xuICAgICAgfSxcbiAgICB9O1xuICAgIHRoaXMuaW50ZXJuYWxFdmVudFRyYWNraW5nRW5yaWNobWVudCA9IGVucmljaG1lbnQ7XG4gICAgcmV0dXJuIGVucmljaG1lbnQ7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGluaXRpYWxpemUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgaXNSZWFkeSA9IEJvb2xlYW4oXG4gICAgICB0aGlzLmNvbmZpZy51c2VySWQgJiYgdGhpcy5jb25maWcuZmVhdHVyZUZsYWdTZXJ2aWNlICYmIHRoaXMuY29uZmlnLmFtcGxpdHVkZUFwaUtleSxcbiAgICApO1xuXG4gICAgaWYgKCFpc1JlYWR5KSB7XG4gICAgICBsb2dnZXIubG9nRGVidWcoJ0FtcGxpdHVkZVNlcnZpY2UgaXMgbm90IHJlYWR5Jywge1xuICAgICAgICBpc1JlYWR5LFxuICAgICAgICB1c2VySWQ6IHRoaXMuY29uZmlnLnVzZXJJZCxcbiAgICAgICAgaGFzRmVhdHVyZUZsYWdTZXJ2aWNlOiAhIXRoaXMuY29uZmlnLmZlYXR1cmVGbGFnU2VydmljZSxcbiAgICAgICAgaGFzQW1wbGl0dWRlQXBpS2V5OiAhIXRoaXMuY29uZmlnLmFtcGxpdHVkZUFwaUtleSxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmICh0aGlzLmlzTW9ja0FwaUtleSkge1xuICAgICAgbG9nZ2VyLmxvZ1dhcm4oXG4gICAgICAgICdNb2NrIEFQSSBrZXkgZGV0ZWN0ZWQg4oCUIHJ1bm5pbmcgaW4gbW9jayBtb2RlLCBubyBldmVudHMgd2lsbCBiZSBzZW50IHRvIEFtcGxpdHVkZS4nLFxuICAgICAgICB0aGlzLmNvbmZpZy5hbXBsaXR1ZGVBcGlLZXksXG4gICAgICApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5hbXBsaXR1ZGVDbGllbnQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIER5bmFtaWMgaW1wb3J0IGtlZXBzIHRoZSBBbXBsaXR1ZGUgU0RLIG91dCBvZiB0aGUgZWFnZXJcbiAgICAgICAgLy8gaW5qZWN0aW9uLWJ1bmRsZSBjaHVuazsgdHJhY2tFdmVudCBxdWV1ZXMgYmVoaW5kIGluaXRQcm9taXNlLlxuICAgICAgICBjb25zdCB7IGNyZWF0ZUluc3RhbmNlIH0gPSBhd2FpdCBpbXBvcnQoJ0BhbXBsaXR1ZGUvYW5hbHl0aWNzLWJyb3dzZXInKTtcbiAgICAgICAgY29uc3QgY3VycmVudEFtcGxpdHVkZUluc3RhbmNlOiBCcm93c2VyQ2xpZW50ID0gY3JlYXRlSW5zdGFuY2UoKTtcblxuICAgICAgICBjdXJyZW50QW1wbGl0dWRlSW5zdGFuY2UuYWRkKHRoaXMuZ2V0RXZlbnRUcmFja2luZ0VucmljaG1lbnQoKSk7XG4gICAgICAgIGN1cnJlbnRBbXBsaXR1ZGVJbnN0YW5jZS5pbml0KHRoaXMuY29uZmlnLmFtcGxpdHVkZUFwaUtleSwgdGhpcy5jb25maWcudXNlcklkLCB7XG4gICAgICAgICAgc2VydmVyWm9uZTogdGhpcy5jb25maWcuZGF0YVJlc2lkZW5jeSBhcyBTZXJ2ZXJab25lVHlwZSxcbiAgICAgICAgICB0cmFja2luZ09wdGlvbnM6IHtcbiAgICAgICAgICAgIGlwQWRkcmVzczogdHJ1ZSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIGF1dG9jYXB0dXJlOiB7XG4gICAgICAgICAgICBhdHRyaWJ1dGlvbjogdHJ1ZSxcbiAgICAgICAgICAgIHBhZ2VWaWV3czoge1xuICAgICAgICAgICAgICB0cmFja0hpc3RvcnlDaGFuZ2VzOiAncGF0aE9ubHknLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHNlc3Npb25zOiBmYWxzZSxcbiAgICAgICAgICAgIGZvcm1JbnRlcmFjdGlvbnM6IGZhbHNlLFxuICAgICAgICAgICAgZmlsZURvd25sb2FkczogZmFsc2UsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuYW1wbGl0dWRlQ2xpZW50ID0gY3VycmVudEFtcGxpdHVkZUluc3RhbmNlO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGxvZ2dlci5sb2dFcnJvcignRmFpbGVkIHRvIGxvYWQgb3IgaW5pdGlhbGl6ZSB0aGUgQW1wbGl0dWRlIFNESycsIGVycik7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgc3RhdGljIG1hcFdpZGdldFR5cGVUb0NoYXRDb21wb25lbnQod2lkZ2V0VHlwZTogdW5rbm93bik6IHN0cmluZyB7XG4gICAgc3dpdGNoICh3aWRnZXRUeXBlKSB7XG4gICAgICBjYXNlIFdpZGdldFR5cGVWMy5GbG9hdGluZ0J1dHRvblYzOlxuICAgICAgICByZXR1cm4gJ2Zsb2F0aW5nX2J1dHRvbic7XG4gICAgICBjYXNlIFdpZGdldFR5cGVWMy5UeXBpbmdBbmltYXRpb25WMzpcbiAgICAgIGNhc2UgV2lkZ2V0VHlwZVYzLkNoYXRQcmV2aWV3VjM6XG4gICAgICBjYXNlIFdpZGdldFR5cGVWMy5Tb2NpYWxQcm9vZlYzOlxuICAgICAgICByZXR1cm4gJ2VtYmVkZGVkX3dpZGdldCc7XG4gICAgICBjYXNlIFdpZGdldFR5cGVWMy5UaXRsZWRQcm9tcHRDYXJvdXNlbFYzOlxuICAgICAgY2FzZSBXaWRnZXRUeXBlVjMuUHJvbXB0Q2Fyb3VzZWxWMzpcbiAgICAgICAgcmV0dXJuICdzdWdnZXN0aW9uX2Jhcic7XG4gICAgICBjYXNlIFdpZGdldFR5cGVWMy5Qcm9kdWN0Q2FyZFYzOlxuICAgICAgICByZXR1cm4gJ2ltYWdlX3Byb21wdF9jYXJkJztcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiAndW5rbm93bic7XG4gICAgfVxuICB9XG5cbiAgc3RhdGljIG1hcFRyaWdnZXJMb2NhdGlvblRvQ2hhdENvbXBvbmVudCh0cmlnZ2VyTG9jYXRpb246IHVua25vd24pOiBzdHJpbmcge1xuICAgIHN3aXRjaCAodHJpZ2dlckxvY2F0aW9uKSB7XG4gICAgICBjYXNlIENoYXRFbGVtZW50RGlzcGxheUxvY2F0aW9uVjMuUFJPRFVDVF9DQVJEX1BST01QVF9CVVRUT046XG4gICAgICBjYXNlIENoYXRFbGVtZW50RGlzcGxheUxvY2F0aW9uVjMuUFJPRFVDVF9DQVJEX1RFWFRfRklFTEQ6XG4gICAgICAgIHJldHVybiAncHJvbXB0X2NhcmQnO1xuICAgICAgY2FzZSBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzLlBST01QVF9DQVJPVVNFTDpcbiAgICAgIGNhc2UgQ2hhdEVsZW1lbnREaXNwbGF5TG9jYXRpb25WMy5USVRMRURfUFJPTVBUX0NBUk9VU0VMOlxuICAgICAgICByZXR1cm4gJ3RvcF9yZXZpZXdzX3NuaXBwZXQnO1xuICAgICAgY2FzZSBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzLlRZUElOR19BTklNQVRJT046XG4gICAgICBjYXNlIENoYXRFbGVtZW50RGlzcGxheUxvY2F0aW9uVjMuU09DSUFMX1BST09GX1BSSU1BUllfQlVUVE9OOlxuICAgICAgY2FzZSBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzLlNPQ0lBTF9QUk9PRl9TRUNPTkRBUllfQlVUVE9OOlxuICAgICAgY2FzZSBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzLlNPQ0lBTF9QUk9PRl9URVhUX0ZJRUxEOlxuICAgICAgY2FzZSBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzLkNIQVRfUFJFVklFV19DT01QQVJJU09OX1BST01QVF9CVVRUT046XG4gICAgICBjYXNlIENoYXRFbGVtZW50RGlzcGxheUxvY2F0aW9uVjMuQ0hBVF9QUkVWSUVXX0NPTVBBUklTT05fVEVYVF9GSUVMRDpcbiAgICAgIGNhc2UgQ2hhdEVsZW1lbnREaXNwbGF5TG9jYXRpb25WMy5DSEFUX1BSRVZJRVdfUFJPTVBUX0JVVFRPTjpcbiAgICAgIGNhc2UgQ2hhdEVsZW1lbnREaXNwbGF5TG9jYXRpb25WMy5DSEFUX1BSRVZJRVdfVEVYVF9GSUVMRDpcbiAgICAgICAgcmV0dXJuICdjaGF0X3ByZXZpZXcnO1xuICAgICAgY2FzZSBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzLkZMT0FUSU5HX0NIQVRfT1ZFUkxBWTpcbiAgICAgIGNhc2UgQ2hhdEVsZW1lbnREaXNwbGF5TG9jYXRpb25WMy5GTE9BVElOR19DSEFUX0NMT1NFX0JVVFRPTjpcbiAgICAgICAgcmV0dXJuICdpbl9jaGF0JztcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiB0cmlnZ2VyTG9jYXRpb24gYXMgc3RyaW5nO1xuICAgIH1cbiAgfVxuXG4gIC8vIFRoaXMgYWxsb3dzIHVzIHRvIG1hcCB0aGUgZXZlbnQgcHJvcHMgdG8gdGhlIGNvcnJlY3QgZm9ybWF0IGZvciB0aGUgbGVnYWN5IGFtcGxpdHVkZSBldmVudHNcbiAgc3RhdGljIG1hcEV2ZW50UHJvcHMoeyBldmVudE5hbWUsIGV2ZW50UHJvcHMgfTogVHJhY2tFdmVudFBhcmFtcyk6IFJlY29yZDxzdHJpbmcsIHVua25vd24+IHtcbiAgICBjb25zdCBtZXNzYWdlTWV0YWRhdGEgPSBldmVudFByb3BzPy5tZXNzYWdlX21ldGFkYXRhID8/IHt9O1xuICAgIGNvbnN0IHRyaWdnZXJMb2NhdGlvbiA9XG4gICAgICBtZXNzYWdlTWV0YWRhdGEgIT09IG51bGwgJiZcbiAgICAgIHR5cGVvZiBtZXNzYWdlTWV0YWRhdGEgPT09ICdvYmplY3QnICYmXG4gICAgICAndHJpZ2dlcl9sb2NhdGlvbicgaW4gbWVzc2FnZU1ldGFkYXRhXG4gICAgICAgID8gbWVzc2FnZU1ldGFkYXRhLnRyaWdnZXJfbG9jYXRpb25cbiAgICAgICAgOiB1bmRlZmluZWQ7XG5cbiAgICBpZiAoZXZlbnROYW1lID09PSBTcGlmZnlNZXRyaWNzRXZlbnROYW1lLkNoYXRDb21wb25lbnRFeHBhbmRlZCkge1xuICAgICAgY29uc3QgbWFwcGVkVHJpZ2dlckxvY2NhdGlvbiA9XG4gICAgICAgIEFtcGxpdHVkZVNlcnZpY2UubWFwVHJpZ2dlckxvY2F0aW9uVG9DaGF0Q29tcG9uZW50KHRyaWdnZXJMb2NhdGlvbik7XG4gICAgICByZXR1cm4ge1xuICAgICAgICAuLi5ldmVudFByb3BzLFxuICAgICAgICBtZXNzYWdlX21ldGFkYXRhOiB7XG4gICAgICAgICAgLi4ubWVzc2FnZU1ldGFkYXRhLFxuICAgICAgICAgIHRyaWdnZXJfbG9jYXRpb246IG1hcHBlZFRyaWdnZXJMb2NjYXRpb24sXG4gICAgICAgICAgZnVsbF90cmlnZ2VyX2xvY2F0aW9uOiB0cmlnZ2VyTG9jYXRpb24sXG4gICAgICAgIH0sXG4gICAgICB9O1xuICAgIH1cblxuICAgIGlmIChldmVudE5hbWUgPT09IFNwaWZmeU1ldHJpY3NFdmVudE5hbWUuQ2hhdENvbXBvbmVudFZpc2libGUpIHtcbiAgICAgIGNvbnN0IG1hcHBlZFdpZGdldFR5cGUgPSBBbXBsaXR1ZGVTZXJ2aWNlLm1hcFdpZGdldFR5cGVUb0NoYXRDb21wb25lbnQoXG4gICAgICAgIGV2ZW50UHJvcHM/LndpZGdldF90eXBlLFxuICAgICAgKTtcbiAgICAgIGNvbnN0IG1hcHBlZFRyaWdnZXJMb2NjYXRpb24gPVxuICAgICAgICBBbXBsaXR1ZGVTZXJ2aWNlLm1hcFRyaWdnZXJMb2NhdGlvblRvQ2hhdENvbXBvbmVudCh0cmlnZ2VyTG9jYXRpb24pO1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4uZXZlbnRQcm9wcyxcbiAgICAgICAgbWVzc2FnZV9tZXRhZGF0YToge1xuICAgICAgICAgIC4uLm1lc3NhZ2VNZXRhZGF0YSxcbiAgICAgICAgICB0cmlnZ2VyX2xvY2F0aW9uOiBtYXBwZWRUcmlnZ2VyTG9jY2F0aW9uLFxuICAgICAgICAgIGZ1bGxfdHJpZ2dlcl9sb2NhdGlvbjogdHJpZ2dlckxvY2F0aW9uLFxuICAgICAgICB9LFxuICAgICAgICBjaGF0X2NvbXBvbmVudDogbWFwcGVkV2lkZ2V0VHlwZSxcbiAgICAgICAgJ2NoYXQuY29tcG9uZW50X3Zpc2libGUnOiB0cnVlLFxuICAgICAgfTtcbiAgICB9XG4gICAgcmV0dXJuIGV2ZW50UHJvcHMgPz8ge307XG4gIH1cblxuICBzdGF0aWMgaXNWM0Vudml2ZUV2ZW50KGV2ZW50TmFtZTogU3BpZmZ5TWV0cmljc0V2ZW50TmFtZSB8IEVudml2ZU1ldHJpY3NFdmVudE5hbWUpOiBib29sZWFuIHtcbiAgICByZXR1cm4gT2JqZWN0LnZhbHVlcyhFbnZpdmVNZXRyaWNzRXZlbnROYW1lKS5pbmNsdWRlcyhldmVudE5hbWUgYXMgRW52aXZlTWV0cmljc0V2ZW50TmFtZSk7XG4gIH1cblxuICBzdGF0aWMgZGVjb3JhdGVFdmVudE5hbWUoZXZlbnROYW1lOiBTcGlmZnlNZXRyaWNzRXZlbnROYW1lIHwgRW52aXZlTWV0cmljc0V2ZW50TmFtZSk6IHN0cmluZyB7XG4gICAgaWYgKEFtcGxpdHVkZVNlcnZpY2UuaXNWM0Vudml2ZUV2ZW50KGV2ZW50TmFtZSkpIHtcbiAgICAgIHJldHVybiBgW0Vudml2ZV0gJHtldmVudE5hbWV9YDtcbiAgICB9XG4gICAgcmV0dXJuIGBbU3BpZmZ5XSAke2V2ZW50TmFtZX1gO1xuICB9XG5cbiAgYXN5bmMgdHJhY2tFdmVudCh7XG4gICAgZXZlbnROYW1lLFxuICAgIGV2ZW50UHJvcHMsXG4gICAgZXZlbnRHcm91cHMsXG4gICAgYWxzb1NlbmRUb0dvb2dsZUFuYWx5dGljcyA9IGZhbHNlLFxuICB9OiBUcmFja0V2ZW50UGFyYW1zKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuaXNNb2NrQXBpS2V5KSByZXR1cm47XG4gICAgYXdhaXQgdGhpcy5pbml0UHJvbWlzZTtcbiAgICBsb2dnZXIubG9nRGVidWcoJ1N1Ym1pdHRpbmcgZXZlbnQnLCBldmVudE5hbWUpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBkZWNvcmF0ZWRFdmVudE5hbWUgPSBBbXBsaXR1ZGVTZXJ2aWNlLmRlY29yYXRlRXZlbnROYW1lKGV2ZW50TmFtZSk7XG5cbiAgICAgIGlmICghdGhpcy5hbXBsaXR1ZGVDbGllbnQpIHtcbiAgICAgICAgbG9nZ2VyLmxvZ1dhcm4oJ2FtcGxpdHVkZSBjbGllbnQgdW5kZWZpbmVkJywgdW5kZWZpbmVkLCB7XG4gICAgICAgICAgZXZlbnRfbmFtZTogZGVjb3JhdGVkRXZlbnROYW1lLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBtYXBwZWRFdmVudFByb3BzID0gQW1wbGl0dWRlU2VydmljZS5tYXBFdmVudFByb3BzKHsgZXZlbnROYW1lLCBldmVudFByb3BzIH0pO1xuXG4gICAgICBjb25zdCBldmVudERhdGEgPSBKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgIGV2ZW50TmFtZSxcbiAgICAgICAgZXZlbnRQcm9wczogbWFwcGVkRXZlbnRQcm9wcyxcbiAgICAgICAgY3JlYXRlZF9hdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgfSk7XG4gICAgICBjb25zdCBlbmNvZGVyID0gbmV3IFRleHRFbmNvZGVyKCk7XG4gICAgICBjb25zdCBkYXRhID0gZW5jb2Rlci5lbmNvZGUoZXZlbnREYXRhKTtcbiAgICAgIC8vIGNhbGN1bGF0ZSBhIGhhc2ggb2YgdGhlIGV2ZW50IHByb3BlcnRpZXMgdG8gdXNlIGFzIHRoZSBpbnNlcnRfaWQgc28gdGhhdCBkdXBsaWNhdGUgZXZlbnRzXG4gICAgICAvLyBhcmUgYXV0b21hdGljYWxseSBkcm9wcGVkIGJ5IEFtcGxpdHVkZVxuICAgICAgY29uc3QgaGFzaEJ1ZmZlciA9IGF3YWl0IGNyeXB0by5zdWJ0bGUuZGlnZXN0KCdTSEEtMjU2JywgZGF0YSk7XG4gICAgICBjb25zdCBoYXNoQXJyYXkgPSBBcnJheS5mcm9tKG5ldyBVaW50OEFycmF5KGhhc2hCdWZmZXIpKTtcbiAgICAgIGNvbnN0IGN1cnJlbnRJbnNlcnRJZCA9IGhhc2hBcnJheS5tYXAoYiA9PiBiLnRvU3RyaW5nKDE2KS5wYWRTdGFydCgyLCAnMCcpKS5qb2luKCcnKTtcblxuICAgICAgbG9nZ2VyLmxvZ0RlYnVnKGBhbXBsaXR1ZGUgdHJhY2tpbmcgJHtkZWNvcmF0ZWRFdmVudE5hbWV9YCwgbnVsbCwge1xuICAgICAgICBldmVudF9uYW1lOiBkZWNvcmF0ZWRFdmVudE5hbWUsXG4gICAgICAgIHByb3BzOiBtYXBwZWRFdmVudFByb3BzLFxuICAgICAgfSk7XG5cbiAgICAgIHRoaXMuYW1wbGl0dWRlQ2xpZW50LnRyYWNrKFxuICAgICAgICBkZWNvcmF0ZWRFdmVudE5hbWUsXG4gICAgICAgIHtcbiAgICAgICAgICAuLi50aGlzLmdldERlZmF1bHRUcmFja2luZ1Byb3BzKGV2ZW50TmFtZSksXG4gICAgICAgICAgLi4ubWFwcGVkRXZlbnRQcm9wcyxcbiAgICAgICAgICAuLi4obWFwcGVkRXZlbnRQcm9wc1xuICAgICAgICAgICAgPyB0aGlzLmV2ZW50UHJvcHNUb1ByZWZpeGVkRXZlbnRQcm9wcyhldmVudE5hbWUsIG1hcHBlZEV2ZW50UHJvcHMpXG4gICAgICAgICAgICA6IHt9KSxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIC4uLmV2ZW50R3JvdXBzLFxuICAgICAgICAgIGluc2VydF9pZDogY3VycmVudEluc2VydElkLFxuICAgICAgICB9LFxuICAgICAgKTtcblxuICAgICAgLy8gVE9ETzogUHVsbCBnYUVuYWJsZWQgZnJvbSBvcmcgY29uZmlnIHdoZW4gYXZhaWxhYmxlLiBIYXJkY29kZWQgdG8gdHJ1ZSBmb3Igbm93LlxuICAgICAgY29uc3QgZ2FFbmFibGVkID0gdHJ1ZTtcblxuICAgICAgaWYgKEFtcGxpdHVkZVNlcnZpY2UuaXNWM0Vudml2ZUV2ZW50KGV2ZW50TmFtZSkgJiYgZ2FFbmFibGVkKSB7XG4gICAgICAgIGNvbnN0IG1lcmdlZFByb3BzRm9yR0E0ID0ge1xuICAgICAgICAgIC4uLnRoaXMuZ2V0RGVmYXVsdFRyYWNraW5nUHJvcHMoZXZlbnROYW1lKSxcbiAgICAgICAgICAuLi5tYXBwZWRFdmVudFByb3BzLFxuICAgICAgICB9O1xuICAgICAgICBwcm9qZWN0VG9HQTQoZXZlbnROYW1lIGFzIEVudml2ZU1ldHJpY3NFdmVudE5hbWUsIG1lcmdlZFByb3BzRm9yR0E0KTtcbiAgICAgIH0gZWxzZSBpZiAoYWxzb1NlbmRUb0dvb2dsZUFuYWx5dGljcyAmJiBnYUVuYWJsZWQpIHtcbiAgICAgICAgbG9nZ2VyLmxvZ0RlYnVnKCdHQSB0cmFja2luZycsIGRlY29yYXRlZEV2ZW50TmFtZSk7XG4gICAgICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyAmJiB3aW5kb3cuZGF0YUxheWVyKSB7XG4gICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcbiAgICAgICAgICAod2luZG93LmRhdGFMYXllciBhcyBhbnlbXSkucHVzaCh7XG4gICAgICAgICAgICBldmVudDogZGVjb3JhdGVkRXZlbnROYW1lLFxuICAgICAgICAgICAgZXZlbnRQcm9wcyxcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgbG9nZ2VyLmxvZ0Vycm9yKCdFcnJvciB0cmFja2luZyBldmVudCcsIGVyciwge1xuICAgICAgICBldmVudE5hbWUsXG4gICAgICAgIGV2ZW50UHJvcHMsXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICAvLyBFbnN1cmUgdGhhdCBzdXBwbGVtZW50YWwgZGVmYXVsdCBwcm9wcyBhcmUgbWVyZ2VkIHdpdGggdGhlIGV4aXN0aW5nIHByb3BzXG4gIHNldFN1cHBsZW1lbnRhbERlZmF1bHRQcm9wcyhwcm9wczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiB2b2lkIHtcbiAgICB0aGlzLnN1cHBsZW1lbnRhbERlZmF1bHRQcm9wcyA9IHByb3BzO1xuICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBbUJBLE1BQU0sU0FBUyxJQUFJQSxlQUFPLG1CQUFtQjtBQStCN0MsTUFBTUMsd0JBQStDLENBQUMsTUFBTSxXQUFXO0FBRXZFLE1BQU0sd0JBQ0osZ0JBQzRCO0NBQzVCLE1BQU0sOEJBQWMsSUFBSSxLQUF5RDtBQUNqRixFQUFDLGVBQWUsRUFBRSxFQUFFLFNBQVEsTUFBSztBQUMvQixNQUFJLEVBQUUsY0FBYyxRQUFRLEVBQUUsYUFBYSxLQUN6QyxhQUFZLElBQUksRUFBRSxXQUFXLEVBQUU7R0FFakM7QUFDRixRQUFPLHNCQUFzQixRQUFpQyxLQUFLLE9BQU87RUFDeEUsTUFBTSxJQUFJLFlBQVksSUFBSSxHQUFHO0FBQzdC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
315
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW1wbGl0dWRlU2VydmljZS5qcyIsIm5hbWVzIjpbIkxvZ2dlciIsIkVYUEVSSU1FTlRfTkFNRVNQQUNFUzogRXhwZXJpbWVudE5hbWVzcGFjZVtdIiwiZW5yaWNobWVudDogRW5yaWNobWVudFBsdWdpbiIsImVucmljaGVkRXZlbnQ6IEV2ZW50IiwiZ2xvYmFsUHJvcGVydGllczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiIsImN1cnJlbnRBbXBsaXR1ZGVJbnN0YW5jZTogQnJvd3NlckNsaWVudCJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9hbXBsaXR1ZGVTZXJ2aWNlL2FtcGxpdHVkZVNlcnZpY2UudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRXZlbnRzRGlzcGF0Y2hlciwgU3BpZmZ5RXZlbnQgfSBmcm9tICdzcmMvZXZlbnRzJztcbmltcG9ydCB7IExvY2FsU3RvcmFnZUtleXMgfSBmcm9tICdzcmMvY29udGV4dHMvbG9jYWxTdG9yYWdlQ29udGV4dCc7XG5pbXBvcnQgTG9nZ2VyIGZyb20gJ3NyYy9hcHBsaWNhdGlvbi9sb2dnaW5nL2xvZ2dlcic7XG5pbXBvcnQgdHlwZSB7XG4gIEJyb3dzZXJDbGllbnQsXG4gIEVucmljaG1lbnRQbHVnaW4sXG4gIEV2ZW50LFxuICBTZXJ2ZXJab25lVHlwZSxcbn0gZnJvbSAnQGFtcGxpdHVkZS9hbmFseXRpY3MtdHlwZXMnO1xuaW1wb3J0IHsgRmVhdHVyZUZsYWdTZXJ2aWNlIH0gZnJvbSAnc3JjL3NlcnZpY2VzL2ZlYXR1cmVGbGFnU2VydmljZSc7XG5pbXBvcnQgeyBXaWRnZXRUeXBlVjMgfSBmcm9tICdzcmMvY29udGV4dHMvdHlwZXNWMyc7XG5pbXBvcnQgeyBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzIH0gZnJvbSAnc3JjL2FwcGxpY2F0aW9uL21vZGVscy9jaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzJztcbmltcG9ydCB7IEV4cGVyaW1lbnROYW1lc3BhY2UsIE9yZ0NvbmZpZ0V4cGVyaW1lbnRBc3NpZ25tZW50IH0gZnJvbSAnc3JjL2FwcGxpY2F0aW9uL21vZGVscyc7XG5pbXBvcnQgeyBwcm9qZWN0VG9HQTQgfSBmcm9tICcuLi9nYTRQcm9qZWN0aW9uU2VydmljZS9nYTRQcm9qZWN0aW9uU2VydmljZSc7XG5pbXBvcnQgeyBFbnZpdmVNZXRyaWNzRXZlbnROYW1lLCBTcGlmZnlNZXRyaWNzRXZlbnROYW1lIH0gZnJvbSAnLi9ldmVudE5hbWVzJztcblxuLy8gUmUtZXhwb3J0IGV2ZW50IG5hbWVzIGZvciBjb252ZW5pZW5jZVxuZXhwb3J0IHsgRW52aXZlTWV0cmljc0V2ZW50TmFtZSwgU3BpZmZ5TWV0cmljc0V2ZW50TmFtZSB9O1xuXG5jb25zdCBsb2dnZXIgPSBuZXcgTG9nZ2VyKCdhbXBsaXR1ZGVTZXJ2aWNlJyk7XG5cbi8vIEFtcGxpdHVkZSdzIG1hbmFnZWQgQmlnUXVlcnkgZGF0YSBjYW4gc3VyZmFjZSBzdXBwbGVtZW50YXJ5IFVuaWNvZGUgYXMgVStGRkZELlxuLy8gS2VlcCB0aGUgd2lkZ2V0LXRyYWluaW5nIHBheWxvYWQgQVNDSUktc2FmZSB3aXRob3V0IGNoYW5naW5nIHRoZSBkaXNwbGF5ZWQgdGV4dC5cbmNvbnN0IFVURjhfVFJBTlNQT1JUX1BSRUZJWCA9ICdfX3NwaWZmeV91dGY4X186JztcblxuY29uc3QgY29udGFpbnNTdXBwbGVtZW50YXJ5VW5pY29kZSA9ICh2YWx1ZTogc3RyaW5nKTogYm9vbGVhbiA9PlxuICBBcnJheS5mcm9tKHZhbHVlKS5zb21lKGNoYXJhY3RlciA9PiAoY2hhcmFjdGVyLmNvZGVQb2ludEF0KDApID8/IDApID4gMHhmZmZmKTtcblxuY29uc3QgZW5jb2RlU3VwcGxlbWVudGFyeVVuaWNvZGUgPSAodmFsdWU6IHVua25vd24pOiB1bmtub3duID0+IHtcbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ3N0cmluZycpIHtcbiAgICByZXR1cm4gY29udGFpbnNTdXBwbGVtZW50YXJ5VW5pY29kZSh2YWx1ZSlcbiAgICAgID8gYCR7VVRGOF9UUkFOU1BPUlRfUFJFRklYfSR7ZW5jb2RlVVJJQ29tcG9uZW50KHZhbHVlKX1gXG4gICAgICA6IHZhbHVlO1xuICB9XG4gIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSkge1xuICAgIHJldHVybiB2YWx1ZS5tYXAoZW5jb2RlU3VwcGxlbWVudGFyeVVuaWNvZGUpO1xuICB9XG4gIGlmICh2YWx1ZSAhPT0gbnVsbCAmJiB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnKSB7XG4gICAgcmV0dXJuIE9iamVjdC5mcm9tRW50cmllcyhcbiAgICAgIE9iamVjdC5lbnRyaWVzKHZhbHVlKS5tYXAoKFtrZXksIG5lc3RlZFZhbHVlXSkgPT4gW1xuICAgICAgICBrZXksXG4gICAgICAgIGVuY29kZVN1cHBsZW1lbnRhcnlVbmljb2RlKG5lc3RlZFZhbHVlKSxcbiAgICAgIF0pLFxuICAgICk7XG4gIH1cbiAgcmV0dXJuIHZhbHVlO1xufTtcblxuZXhwb3J0IGludGVyZmFjZSBUcmFja0V2ZW50UGFyYW1zIHtcbiAgZXZlbnROYW1lOiBTcGlmZnlNZXRyaWNzRXZlbnROYW1lIHwgRW52aXZlTWV0cmljc0V2ZW50TmFtZTtcbiAgZXZlbnRQcm9wcz86IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICBldmVudEdyb3Vwcz86IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICBhbHNvU2VuZFRvR29vZ2xlQW5hbHl0aWNzPzogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBBbXBsaXR1ZGVTZXJ2aWNlQ29uZmlnIHtcbiAgdXNlcklkOiBzdHJpbmc7XG4gIGFtcGxpdHVkZUFwaUtleTogc3RyaW5nO1xuICBkYXRhUmVzaWRlbmN5OiBzdHJpbmc7XG4gIGVudjogc3RyaW5nO1xuICBjb250ZXh0U291cmNlOiBzdHJpbmc7XG4gIG9yZ1Nob3J0TmFtZTogc3RyaW5nO1xuICBvcmdJZDogc3RyaW5nO1xuICBmZWF0dXJlRmxhZ1NlcnZpY2U6IEZlYXR1cmVGbGFnU2VydmljZTtcbiAgc2hvdzogYm9vbGVhbjtcbiAgZW52aXZlT246IGJvb2xlYW47XG4gIGVuYWJsZWRGZWF0dXJlcz86IFJlY29yZDxzdHJpbmcsIGJvb2xlYW4+O1xuICBnZXRMb2NhbFN0b3JhZ2VJdGVtOiBudWxsIHwgKChrZXk6IHN0cmluZykgPT4gc3RyaW5nIHwgbnVsbCk7XG4gIC8vIENhcHR1cmVkIGF0IGNvbnN0cnVjdGlvbi4gRHJpdmVzIGBleHBlcmltZW50Lntuc30uKmAgZXZlbnQtcHJvcGVydHlcbiAgLy8gZW5yaWNobWVudC4gU291cmNlZCBmcm9tIC92MS9vcmcvY29uZmlnIHdoZW4gdGhlIGB1c2VfdW5pZmllZF9jb25maWdgXG4gIC8vIGdhdGUgaXMgb247IG90aGVyd2lzZSBvbWl0dGVkIChwcm9wcyBlbWl0IGFzIG51bGwpLlxuICBleHBlcmltZW50QXNzaWdubWVudHM/OiBPcmdDb25maWdFeHBlcmltZW50QXNzaWdubWVudFtdO1xufVxuXG4vLyBOYW1lc3BhY2VzIGVtaXR0ZWQgb24gZXZlcnkgZXZlbnQgd2l0aCBjb25zdGFudCBrZXlzLCBzbyB0aGUgQmlnUXVlcnlcbi8vIHNjaGVtYSBpcyBzdGFibGUgcmVnYXJkbGVzcyBvZiB3aGljaCAoaWYgYW55KSBleHBlcmltZW50IGlzIHJ1bm5pbmcuXG4vLyBDdXJyZW50bHkgYG1sYCArIGBtZXJjaGFudGA7IG1hdGNoZXMgYEV4cGVyaW1lbnROYW1lc3BhY2VgLlxuY29uc3QgRVhQRVJJTUVOVF9OQU1FU1BBQ0VTOiBFeHBlcmltZW50TmFtZXNwYWNlW10gPSBbJ21sJywgJ21lcmNoYW50J107XG5cbmNvbnN0IGJ1aWxkRXhwZXJpbWVudFByb3BzID0gKFxuICBhc3NpZ25tZW50czogT3JnQ29uZmlnRXhwZXJpbWVudEFzc2lnbm1lbnRbXSB8IHVuZGVmaW5lZCxcbik6IFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0+IHtcbiAgY29uc3QgYnlOYW1lc3BhY2UgPSBuZXcgTWFwPEV4cGVyaW1lbnROYW1lc3BhY2UsIE9yZ0NvbmZpZ0V4cGVyaW1lbnRBc3NpZ25tZW50PigpO1xuICAoYXNzaWdubWVudHMgPz8gW10pLmZvckVhY2goYSA9PiB7XG4gICAgaWYgKGEubGF5ZXJfbmFtZSAhPSBudWxsICYmIGEubmFtZXNwYWNlICE9IG51bGwpIHtcbiAgICAgIGJ5TmFtZXNwYWNlLnNldChhLm5hbWVzcGFjZSwgYSk7XG4gICAgfVxuICB9KTtcbiAgcmV0dXJuIEVYUEVSSU1FTlRfTkFNRVNQQUNFUy5yZWR1Y2U8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+KChhY2MsIG5zKSA9PiB7XG4gICAgY29uc3QgYSA9IGJ5TmFtZXNwYWNlLmdldChucyk7XG4gICAgYWNjW2BleHBlcmltZW50LiR7bnN9LmxheWVyX25hbWVgXSA9IGE/LmxheWVyX25hbWUgPz8gbnVsbDtcbiAgICBhY2NbYGV4cGVyaW1lbnQuJHtuc30uZXhwZXJpbWVudF9uYW1lYF0gPSBhPy5hbGxvY2F0ZWRfZXhwZXJpbWVudF9uYW1lID8/IG51bGw7XG4gICAgYWNjW2BleHBlcmltZW50LiR7bnN9Lmdyb3VwX25hbWVgXSA9IGE/Lmdyb3VwX25hbWUgPz8gbnVsbDtcbiAgICAvLyBgcnVsZV9pZGAgcmVzZXJ2ZWQg4oCUIGJhY2tlbmQgZG9lc24ndCBjdXJyZW50bHkgc3VyZmFjZSBpdC5cbiAgICBhY2NbYGV4cGVyaW1lbnQuJHtuc30ucnVsZV9pZGBdID0gbnVsbDtcbiAgICByZXR1cm4gYWNjO1xuICB9LCB7fSk7XG59O1xuXG5leHBvcnQgY2xhc3MgQW1wbGl0dWRlU2VydmljZSB7XG4gIHByaXZhdGUgYW1wbGl0dWRlQ2xpZW50OiBCcm93c2VyQ2xpZW50IHwgdW5kZWZpbmVkO1xuXG4gIHByaXZhdGUgaW50ZXJuYWxFdmVudFRyYWNraW5nRW5yaWNobWVudDogRW5yaWNobWVudFBsdWdpbiB8IHVuZGVmaW5lZDtcblxuICBwcml2YXRlIHN1cHBsZW1lbnRhbERlZmF1bHRQcm9wczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4gPSB7fTtcblxuICBwcml2YXRlIGNvbmZpZzogQW1wbGl0dWRlU2VydmljZUNvbmZpZztcblxuICBwcml2YXRlIGluaXRQcm9taXNlOiBQcm9taXNlPHZvaWQ+O1xuXG4gIGNvbnN0cnVjdG9yKGNvbmZpZzogQW1wbGl0dWRlU2VydmljZUNvbmZpZykge1xuICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgIHRoaXMuaW5pdFByb21pc2UgPSB0aGlzLmluaXRpYWxpemUoKTtcbiAgfVxuXG4gIGdldCBpc01vY2tBcGlLZXkoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLmFtcGxpdHVkZUFwaUtleSA9PT0gJ21vY2stYW1wbGl0dWRlLWtleSc7XG4gIH1cblxuICAvLyBNdXN0IG5vdCBkZXBlbmQgb24gYGFtcGxpdHVkZUNsaWVudGA6IHRoZSBTREsgbm93IGxvYWRzIHZpYSBkeW5hbWljXG4gIC8vIGltcG9ydCwgYW5kIEFtcGxpdHVkZVByb3ZpZGVyIHVubW91bnRzIGl0cyBlbnRpcmUgc3VidHJlZSB3aGlsZVxuICAvLyBpc1JlYWR5IGlzIGZhbHNlIOKAlCBnYXRpbmcgb24gdGhlIGNsaWVudCB3b3VsZCBibG9jayB3aWRnZXQgcmVuZGVyaW5nXG4gIC8vIG9uIHRoZSBhbmFseXRpY3MgY2h1bmsgZG93bmxvYWQuIEV2ZW50cyBhd2FpdGVkIGluIHRyYWNrRXZlbnQgcXVldWVcbiAgLy8gYmVoaW5kIGBpbml0UHJvbWlzZWAgaW5zdGVhZC5cbiAgZ2V0IGlzUmVhZHkoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIEJvb2xlYW4oXG4gICAgICB0aGlzLmNvbmZpZy51c2VySWQgJiZcbiAgICAgIHRoaXMuY29uZmlnLmZlYXR1cmVGbGFnU2VydmljZSAmJlxuICAgICAgdGhpcy5jb25maWcuYW1wbGl0dWRlQXBpS2V5ICYmXG4gICAgICAhdGhpcy5pc01vY2tBcGlLZXksXG4gICAgKTtcbiAgfVxuXG4gIC8qKiBSZXNvbHZlcyBvbmNlIFNESyBpbml0aWFsaXphdGlvbiBoYXMgc2V0dGxlZCAobmV2ZXIgcmVqZWN0cykuICovXG4gIGluaXRpYWxpemVkKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmluaXRQcm9taXNlO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRMb2NhbFN0b3JhZ2VJdGVtKGtleTogc3RyaW5nKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmdldExvY2FsU3RvcmFnZUl0ZW0pIHtcbiAgICAgIHJldHVybiB0aGlzLmNvbmZpZy5nZXRMb2NhbFN0b3JhZ2VJdGVtKGtleSk7XG4gICAgfVxuICAgIGlmICh0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJyAmJiB3aW5kb3cubG9jYWxTdG9yYWdlKSB7XG4gICAgICByZXR1cm4gd2luZG93LmxvY2FsU3RvcmFnZS5nZXRJdGVtKGtleSk7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXREZWZhdWx0VHJhY2tpbmdQcm9wcyhcbiAgICBldmVudE5hbWU6IFNwaWZmeU1ldHJpY3NFdmVudE5hbWUgfCBFbnZpdmVNZXRyaWNzRXZlbnROYW1lLFxuICApOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gICAgY29uc3QgZmVhdHVyZUdhdGVzID0gT2JqZWN0LmVudHJpZXModGhpcy5jb25maWcuZmVhdHVyZUZsYWdTZXJ2aWNlLmdldEZlYXR1cmVGbGFncygpKTtcbiAgICBjb25zdCBnYXRlc1Byb3BzID1cbiAgICAgIGZlYXR1cmVHYXRlcy5sZW5ndGggPiAwXG4gICAgICAgID8gZmVhdHVyZUdhdGVzLnJlZHVjZTxSZWNvcmQ8c3RyaW5nLCBib29sZWFuPj4oKGFjYywgW25hbWUsIHZhbHVlXSkgPT4ge1xuICAgICAgICAgICAgaWYgKG5hbWUgJiYgdmFsdWUgIT0gbnVsbCkge1xuICAgICAgICAgICAgICByZXR1cm4geyAuLi5hY2MsIFtgZmVhdHVyZV9nYXRlLiR7bmFtZX1gXTogdmFsdWUgfTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBhY2M7XG4gICAgICAgICAgfSwge30pXG4gICAgICAgIDoge307XG4gICAgY29uc3QgZXhwZXJpbWVudFByb3BzID0gYnVpbGRFeHBlcmltZW50UHJvcHModGhpcy5jb25maWcuZXhwZXJpbWVudEFzc2lnbm1lbnRzKTtcblxuICAgIGNvbnN0IGVudmlyb25tZW50UHJvcHMgPSB7XG4gICAgICAnZW52aXJvbm1lbnQuZXhlY3V0aW9uX2NvbnRleHQnOiAnYnVuZGxlJyxcbiAgICAgICdlbnZpcm9ubWVudC5wYWdlX3VybCc6IHdpbmRvdy5sb2NhdGlvbi5ocmVmLFxuICAgICAgJ2Vudmlyb25tZW50LmVudml2ZV91c2VyX2lkJzogdGhpcy5jb25maWcudXNlcklkLFxuICAgICAgJ2Vudmlyb25tZW50LmV4ZWN1dGlvbl9lbnZpcm9ubWVudCc6IHRoaXMuY29uZmlnLmVudiB8fCAndW5rbm93bicsXG4gICAgICAnZW52aXJvbm1lbnQuY29udGV4dF9zb3VyY2UnOiB0aGlzLmNvbmZpZy5jb250ZXh0U291cmNlLFxuICAgICAgJ2Vudmlyb25tZW50LnNhbGVzX2FnZW50X2VuYWJsZWQnOiB0aGlzLmNvbmZpZy5lbmFibGVkRmVhdHVyZXM/LnNhbGVzQWdlbnQgPz8gZmFsc2UsXG4gICAgICAnZW52aXJvbm1lbnQuc2VhcmNoX2VuYWJsZWQnOiB0aGlzLmNvbmZpZy5lbmFibGVkRmVhdHVyZXM/LnNlYXJjaEFnZW50ID8/IGZhbHNlLFxuICAgICAgJ2Vudmlyb25tZW50LmVudml2ZV9vbl9xdWVyeV9wYXJhbSc6IHRoaXMuY29uZmlnLmVudml2ZU9uLFxuICAgICAgJ2Vudmlyb25tZW50LndpbmRvd19zaG93JzogdGhpcy5jb25maWcuc2hvdyxcbiAgICAgICdlbnZpcm9ubWVudC5lbnZpdmVfZW5hYmxlZCc6IHRoaXMuY29uZmlnLmVuYWJsZWRGZWF0dXJlcz8uZW52aXZlLFxuICAgIH07XG5cbiAgICBjb25zdCBvcmdMZXZlbEFtcGxpdHVkZVRyYWNraW5nUHJvcHMgPSBBbXBsaXR1ZGVTZXJ2aWNlLmlzVjNFbnZpdmVFdmVudChldmVudE5hbWUpXG4gICAgICA/IHtcbiAgICAgICAgICAuLi50aGlzLmNvbmZpZy5mZWF0dXJlRmxhZ1NlcnZpY2UuZ2V0RnVsbEZsYWdWYWx1ZXMoKSxcbiAgICAgICAgICAuLi5leHBlcmltZW50UHJvcHMsXG4gICAgICAgICAgLi4uZW52aXJvbm1lbnRQcm9wcyxcbiAgICAgICAgfVxuICAgICAgOiB7XG4gICAgICAgICAgLi4uZ2F0ZXNQcm9wcyxcbiAgICAgICAgICAuLi5leHBlcmltZW50UHJvcHMsXG4gICAgICAgIH07XG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLm9yZ0xldmVsQW1wbGl0dWRlVHJhY2tpbmdQcm9wcyxcbiAgICAgIC4uLnRoaXMuc3VwcGxlbWVudGFsRGVmYXVsdFByb3BzLFxuICAgICAgYXBwX2lkOiAnY29tbWVyY2UtY2hhdC1yZWFjdC1jb21wb25lbnQnLFxuICAgICAgY2hhdF9pZDogdGhpcy5nZXRMb2NhbFN0b3JhZ2VJdGVtKExvY2FsU3RvcmFnZUtleXMuQ2hhdElkKSxcbiAgICAgIGVudjogdGhpcy5jb25maWcuZW52IHx8ICd1bmtub3duJyxcbiAgICAgIGFwcF9zb3VyY2U6IHRoaXMuY29uZmlnLmNvbnRleHRTb3VyY2UsXG4gICAgICBvcmdfaWQ6IHRoaXMuY29uZmlnLm9yZ0lkLFxuICAgICAgJ29yZy5zaG9ydF9uYW1lJzogdGhpcy5jb25maWcub3JnU2hvcnROYW1lLFxuICAgICAgJ3VzZXIuaWQnOiB0aGlzLmNvbmZpZy51c2VySWQsXG4gICAgICAnY2RwLnVzZXJfaWQnOiBudWxsLFxuICAgICAgJ2NkcC5wcm92aWRlcic6IG51bGwsXG4gICAgICAnZXZlbnQuc291cmNlJzogJ3dlYi1icm93c2VyJyxcbiAgICAgICdldmVudC50eXBlJzogJ3VzZXItYWN0aXZpdHknLFxuICAgICAgJ2V2ZW50LmlkJzogbnVsbCxcbiAgICAgICdldmVudC5jaGFubmVsJzogJ3dlYicsXG4gICAgICAnZXZlbnQudGltZXN0YW1wJzogbnVsbCxcbiAgICB9O1xuICB9XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNsYXNzLW1ldGhvZHMtdXNlLXRoaXNcbiAgcHJpdmF0ZSBldmVudFByb3BzVG9QcmVmaXhlZEV2ZW50UHJvcHMoXG4gICAgZXZlbnROYW1lOiBTcGlmZnlNZXRyaWNzRXZlbnROYW1lIHwgRW52aXZlTWV0cmljc0V2ZW50TmFtZSxcbiAgICBldmVudFByb3BzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICAgIGNvbnN0IHByZWZpeCA9IGV2ZW50TmFtZS50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL1xccysvZywgJ18nKTtcbiAgICByZXR1cm4gT2JqZWN0LmVudHJpZXMoZXZlbnRQcm9wcykucmVkdWNlKFxuICAgICAgKGFjYywgW2tleSwgdmFsdWVdKSA9PiB7XG4gICAgICAgIGFjY1tgJHtwcmVmaXh9LiR7a2V5fWBdID0gdmFsdWU7XG4gICAgICAgIHJldHVybiBhY2M7XG4gICAgICB9LFxuICAgICAge30gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0RXZlbnRUcmFja2luZ0VucmljaG1lbnQoKTogRW5yaWNobWVudFBsdWdpbiB7XG4gICAgaWYgKHRoaXMuaW50ZXJuYWxFdmVudFRyYWNraW5nRW5yaWNobWVudCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gdGhpcy5pbnRlcm5hbEV2ZW50VHJhY2tpbmdFbnJpY2htZW50O1xuICAgIH1cblxuICAgIGNvbnN0IGVucmljaG1lbnQ6IEVucmljaG1lbnRQbHVnaW4gPSB7XG4gICAgICBuYW1lOiAncGFnZS12aWV3LXRyYWNraW5nLWVucmljaG1lbnQnLFxuICAgICAgdHlwZTogJ2VucmljaG1lbnQnLFxuICAgICAgc2V0dXA6IGFzeW5jICgpID0+IHVuZGVmaW5lZCxcbiAgICAgIGV4ZWN1dGU6IGFzeW5jIChldmVudDogRXZlbnQpOiBQcm9taXNlPEV2ZW50PiA9PiB7XG4gICAgICAgIGxldCBlbnJpY2hlZEV2ZW50OiBFdmVudDtcblxuICAgICAgICBjb25zdCBldmVudHNUb0VucmljaCA9IFtcbiAgICAgICAgICAnW0FtcGxpdHVkZV0gUGFnZSBWaWV3ZWQnLFxuICAgICAgICAgIGBbU3BpZmZ5XSAke1NwaWZmeU1ldHJpY3NFdmVudE5hbWUuQnVuZGxlTG9hZGVkfWAsXG4gICAgICAgICAgYFtFbnZpdmVdICR7RW52aXZlTWV0cmljc0V2ZW50TmFtZS5QYWdlVmlld2VkfWAsXG4gICAgICAgIF07XG5cbiAgICAgICAgaWYgKGV2ZW50c1RvRW5yaWNoLmluY2x1ZGVzKGV2ZW50LmV2ZW50X3R5cGUpKSB7XG4gICAgICAgICAgY29uc3QgZ2xvYmFsUHJvcGVydGllczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHt9O1xuXG4gICAgICAgICAgaWYgKHRoaXMuY29uZmlnLnNob3cgIT0gbnVsbCkge1xuICAgICAgICAgICAgZ2xvYmFsUHJvcGVydGllc1snZ2xvYmFsUHJvcGVydGllcy5zaG93J10gPSBTdHJpbmcodGhpcy5jb25maWcuc2hvdyk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGdsb2JhbFByb3BlcnRpZXNbJ2dsb2JhbFByb3BlcnRpZXMuZW52J10gPSBTdHJpbmcodGhpcy5jb25maWcuZW52KTtcblxuICAgICAgICAgIGNvbnN0IGVuYWJsZWRGZWF0dXJlc1Byb3BlcnRpZXMgPSBPYmplY3QuZW50cmllcyhcbiAgICAgICAgICAgIHRoaXMuY29uZmlnPy5lbmFibGVkRmVhdHVyZXMgPz8gKHt9IGFzIFJlY29yZDxzdHJpbmcsIGJvb2xlYW4+KSxcbiAgICAgICAgICApLnJlZHVjZTxSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+PihcbiAgICAgICAgICAgIChhY2MsIFtrZXksIHZhbHVlXSkgPT4gKHtcbiAgICAgICAgICAgICAgLi4uYWNjLFxuICAgICAgICAgICAgICBbYGVuYWJsZWRGZWF0dXJlcy4ke2tleX1gXTogYCR7dmFsdWV9YCxcbiAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAge30sXG4gICAgICAgICAgKTtcblxuICAgICAgICAgIGNvbnN0IHRpbWluZ1Byb3BlcnRpZXMgPSB7XG4gICAgICAgICAgICAndGltaW5nLmVucmljaGVkX2F0X21zJzpcbiAgICAgICAgICAgICAgdHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgPyB3aW5kb3cucGVyZm9ybWFuY2U/Lm5vdygpIDogdW5kZWZpbmVkLFxuICAgICAgICAgIH07XG5cbiAgICAgICAgICBlbnJpY2hlZEV2ZW50ID0ge1xuICAgICAgICAgICAgLi4uZXZlbnQsXG4gICAgICAgICAgICBldmVudF9wcm9wZXJ0aWVzOiB7XG4gICAgICAgICAgICAgIC4uLmV2ZW50LmV2ZW50X3Byb3BlcnRpZXMsXG4gICAgICAgICAgICAgIC4uLnRoaXMuZ2V0RGVmYXVsdFRyYWNraW5nUHJvcHMoXG4gICAgICAgICAgICAgICAgZXZlbnQuZXZlbnRfdHlwZSBhcyBTcGlmZnlNZXRyaWNzRXZlbnROYW1lIHwgRW52aXZlTWV0cmljc0V2ZW50TmFtZSxcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgLi4uZ2xvYmFsUHJvcGVydGllcyxcbiAgICAgICAgICAgICAgLi4uZW5hYmxlZEZlYXR1cmVzUHJvcGVydGllcyxcbiAgICAgICAgICAgICAgLi4udGltaW5nUHJvcGVydGllcyxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBlbnJpY2hlZEV2ZW50ID0gZXZlbnQ7XG4gICAgICAgIH1cblxuICAgICAgICBFdmVudHNEaXNwYXRjaGVyLmRpc3BhdGNoKFNwaWZmeUV2ZW50LkFNUExJVFVERV9FVkVOVCwgZW5yaWNoZWRFdmVudCk7XG5cbiAgICAgICAgcmV0dXJuIGVucmljaGVkRXZlbnQ7XG4gICAgICB9LFxuICAgIH07XG4gICAgdGhpcy5pbnRlcm5hbEV2ZW50VHJhY2tpbmdFbnJpY2htZW50ID0gZW5yaWNobWVudDtcbiAgICByZXR1cm4gZW5yaWNobWVudDtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgaW5pdGlhbGl6ZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBpc1JlYWR5ID0gQm9vbGVhbihcbiAgICAgIHRoaXMuY29uZmlnLnVzZXJJZCAmJiB0aGlzLmNvbmZpZy5mZWF0dXJlRmxhZ1NlcnZpY2UgJiYgdGhpcy5jb25maWcuYW1wbGl0dWRlQXBpS2V5LFxuICAgICk7XG5cbiAgICBpZiAoIWlzUmVhZHkpIHtcbiAgICAgIGxvZ2dlci5sb2dEZWJ1ZygnQW1wbGl0dWRlU2VydmljZSBpcyBub3QgcmVhZHknLCB7XG4gICAgICAgIGlzUmVhZHksXG4gICAgICAgIHVzZXJJZDogdGhpcy5jb25maWcudXNlcklkLFxuICAgICAgICBoYXNGZWF0dXJlRmxhZ1NlcnZpY2U6ICEhdGhpcy5jb25maWcuZmVhdHVyZUZsYWdTZXJ2aWNlLFxuICAgICAgICBoYXNBbXBsaXR1ZGVBcGlLZXk6ICEhdGhpcy5jb25maWcuYW1wbGl0dWRlQXBpS2V5LFxuICAgICAgfSk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuaXNNb2NrQXBpS2V5KSB7XG4gICAgICBsb2dnZXIubG9nV2FybihcbiAgICAgICAgJ01vY2sgQVBJIGtleSBkZXRlY3RlZCDigJQgcnVubmluZyBpbiBtb2NrIG1vZGUsIG5vIGV2ZW50cyB3aWxsIGJlIHNlbnQgdG8gQW1wbGl0dWRlLicsXG4gICAgICAgIHRoaXMuY29uZmlnLmFtcGxpdHVkZUFwaUtleSxcbiAgICAgICk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLmFtcGxpdHVkZUNsaWVudCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gRHluYW1pYyBpbXBvcnQga2VlcHMgdGhlIEFtcGxpdHVkZSBTREsgb3V0IG9mIHRoZSBlYWdlclxuICAgICAgICAvLyBpbmplY3Rpb24tYnVuZGxlIGNodW5rOyB0cmFja0V2ZW50IHF1ZXVlcyBiZWhpbmQgaW5pdFByb21pc2UuXG4gICAgICAgIGNvbnN0IHsgY3JlYXRlSW5zdGFuY2UgfSA9IGF3YWl0IGltcG9ydCgnQGFtcGxpdHVkZS9hbmFseXRpY3MtYnJvd3NlcicpO1xuICAgICAgICBjb25zdCBjdXJyZW50QW1wbGl0dWRlSW5zdGFuY2U6IEJyb3dzZXJDbGllbnQgPSBjcmVhdGVJbnN0YW5jZSgpO1xuXG4gICAgICAgIGN1cnJlbnRBbXBsaXR1ZGVJbnN0YW5jZS5hZGQodGhpcy5nZXRFdmVudFRyYWNraW5nRW5yaWNobWVudCgpKTtcbiAgICAgICAgY3VycmVudEFtcGxpdHVkZUluc3RhbmNlLmluaXQodGhpcy5jb25maWcuYW1wbGl0dWRlQXBpS2V5LCB0aGlzLmNvbmZpZy51c2VySWQsIHtcbiAgICAgICAgICBzZXJ2ZXJab25lOiB0aGlzLmNvbmZpZy5kYXRhUmVzaWRlbmN5IGFzIFNlcnZlclpvbmVUeXBlLFxuICAgICAgICAgIHRyYWNraW5nT3B0aW9uczoge1xuICAgICAgICAgICAgaXBBZGRyZXNzOiB0cnVlLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgYXV0b2NhcHR1cmU6IHtcbiAgICAgICAgICAgIGF0dHJpYnV0aW9uOiB0cnVlLFxuICAgICAgICAgICAgcGFnZVZpZXdzOiB7XG4gICAgICAgICAgICAgIHRyYWNrSGlzdG9yeUNoYW5nZXM6ICdwYXRoT25seScsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgc2Vzc2lvbnM6IGZhbHNlLFxuICAgICAgICAgICAgZm9ybUludGVyYWN0aW9uczogZmFsc2UsXG4gICAgICAgICAgICBmaWxlRG93bmxvYWRzOiBmYWxzZSxcbiAgICAgICAgICB9LFxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5hbXBsaXR1ZGVDbGllbnQgPSBjdXJyZW50QW1wbGl0dWRlSW5zdGFuY2U7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgbG9nZ2VyLmxvZ0Vycm9yKCdGYWlsZWQgdG8gbG9hZCBvciBpbml0aWFsaXplIHRoZSBBbXBsaXR1ZGUgU0RLJywgZXJyKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBzdGF0aWMgbWFwV2lkZ2V0VHlwZVRvQ2hhdENvbXBvbmVudCh3aWRnZXRUeXBlOiB1bmtub3duKTogc3RyaW5nIHtcbiAgICBzd2l0Y2ggKHdpZGdldFR5cGUpIHtcbiAgICAgIGNhc2UgV2lkZ2V0VHlwZVYzLkZsb2F0aW5nQnV0dG9uVjM6XG4gICAgICAgIHJldHVybiAnZmxvYXRpbmdfYnV0dG9uJztcbiAgICAgIGNhc2UgV2lkZ2V0VHlwZVYzLlR5cGluZ0FuaW1hdGlvblYzOlxuICAgICAgY2FzZSBXaWRnZXRUeXBlVjMuQ2hhdFByZXZpZXdWMzpcbiAgICAgIGNhc2UgV2lkZ2V0VHlwZVYzLlNvY2lhbFByb29mVjM6XG4gICAgICAgIHJldHVybiAnZW1iZWRkZWRfd2lkZ2V0JztcbiAgICAgIGNhc2UgV2lkZ2V0VHlwZVYzLlRpdGxlZFByb21wdENhcm91c2VsVjM6XG4gICAgICBjYXNlIFdpZGdldFR5cGVWMy5Qcm9tcHRDYXJvdXNlbFYzOlxuICAgICAgICByZXR1cm4gJ3N1Z2dlc3Rpb25fYmFyJztcbiAgICAgIGNhc2UgV2lkZ2V0VHlwZVYzLlByb2R1Y3RDYXJkVjM6XG4gICAgICAgIHJldHVybiAnaW1hZ2VfcHJvbXB0X2NhcmQnO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuICd1bmtub3duJztcbiAgICB9XG4gIH1cblxuICBzdGF0aWMgbWFwVHJpZ2dlckxvY2F0aW9uVG9DaGF0Q29tcG9uZW50KHRyaWdnZXJMb2NhdGlvbjogdW5rbm93bik6IHN0cmluZyB7XG4gICAgc3dpdGNoICh0cmlnZ2VyTG9jYXRpb24pIHtcbiAgICAgIGNhc2UgQ2hhdEVsZW1lbnREaXNwbGF5TG9jYXRpb25WMy5QUk9EVUNUX0NBUkRfUFJPTVBUX0JVVFRPTjpcbiAgICAgIGNhc2UgQ2hhdEVsZW1lbnREaXNwbGF5TG9jYXRpb25WMy5QUk9EVUNUX0NBUkRfVEVYVF9GSUVMRDpcbiAgICAgICAgcmV0dXJuICdwcm9tcHRfY2FyZCc7XG4gICAgICBjYXNlIENoYXRFbGVtZW50RGlzcGxheUxvY2F0aW9uVjMuUFJPTVBUX0NBUk9VU0VMOlxuICAgICAgY2FzZSBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzLlRJVExFRF9QUk9NUFRfQ0FST1VTRUw6XG4gICAgICAgIHJldHVybiAndG9wX3Jldmlld3Nfc25pcHBldCc7XG4gICAgICBjYXNlIENoYXRFbGVtZW50RGlzcGxheUxvY2F0aW9uVjMuVFlQSU5HX0FOSU1BVElPTjpcbiAgICAgIGNhc2UgQ2hhdEVsZW1lbnREaXNwbGF5TG9jYXRpb25WMy5TT0NJQUxfUFJPT0ZfUFJJTUFSWV9CVVRUT046XG4gICAgICBjYXNlIENoYXRFbGVtZW50RGlzcGxheUxvY2F0aW9uVjMuU09DSUFMX1BST09GX1NFQ09OREFSWV9CVVRUT046XG4gICAgICBjYXNlIENoYXRFbGVtZW50RGlzcGxheUxvY2F0aW9uVjMuU09DSUFMX1BST09GX1RFWFRfRklFTEQ6XG4gICAgICBjYXNlIENoYXRFbGVtZW50RGlzcGxheUxvY2F0aW9uVjMuQ0hBVF9QUkVWSUVXX0NPTVBBUklTT05fUFJPTVBUX0JVVFRPTjpcbiAgICAgIGNhc2UgQ2hhdEVsZW1lbnREaXNwbGF5TG9jYXRpb25WMy5DSEFUX1BSRVZJRVdfQ09NUEFSSVNPTl9URVhUX0ZJRUxEOlxuICAgICAgY2FzZSBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzLkNIQVRfUFJFVklFV19QUk9NUFRfQlVUVE9OOlxuICAgICAgY2FzZSBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzLkNIQVRfUFJFVklFV19URVhUX0ZJRUxEOlxuICAgICAgICByZXR1cm4gJ2NoYXRfcHJldmlldyc7XG4gICAgICBjYXNlIENoYXRFbGVtZW50RGlzcGxheUxvY2F0aW9uVjMuRkxPQVRJTkdfQ0hBVF9PVkVSTEFZOlxuICAgICAgY2FzZSBDaGF0RWxlbWVudERpc3BsYXlMb2NhdGlvblYzLkZMT0FUSU5HX0NIQVRfQ0xPU0VfQlVUVE9OOlxuICAgICAgICByZXR1cm4gJ2luX2NoYXQnO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgcmV0dXJuIHRyaWdnZXJMb2NhdGlvbiBhcyBzdHJpbmc7XG4gICAgfVxuICB9XG5cbiAgLy8gVGhpcyBhbGxvd3MgdXMgdG8gbWFwIHRoZSBldmVudCBwcm9wcyB0byB0aGUgY29ycmVjdCBmb3JtYXQgZm9yIHRoZSBsZWdhY3kgYW1wbGl0dWRlIGV2ZW50c1xuICBzdGF0aWMgbWFwRXZlbnRQcm9wcyh7IGV2ZW50TmFtZSwgZXZlbnRQcm9wcyB9OiBUcmFja0V2ZW50UGFyYW1zKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICAgIGNvbnN0IG1lc3NhZ2VNZXRhZGF0YSA9IGV2ZW50UHJvcHM/Lm1lc3NhZ2VfbWV0YWRhdGEgPz8ge307XG4gICAgY29uc3QgdHJpZ2dlckxvY2F0aW9uID1cbiAgICAgIG1lc3NhZ2VNZXRhZGF0YSAhPT0gbnVsbCAmJlxuICAgICAgdHlwZW9mIG1lc3NhZ2VNZXRhZGF0YSA9PT0gJ29iamVjdCcgJiZcbiAgICAgICd0cmlnZ2VyX2xvY2F0aW9uJyBpbiBtZXNzYWdlTWV0YWRhdGFcbiAgICAgICAgPyBtZXNzYWdlTWV0YWRhdGEudHJpZ2dlcl9sb2NhdGlvblxuICAgICAgICA6IHVuZGVmaW5lZDtcblxuICAgIGlmIChldmVudE5hbWUgPT09IFNwaWZmeU1ldHJpY3NFdmVudE5hbWUuQ2hhdENvbXBvbmVudEV4cGFuZGVkKSB7XG4gICAgICBjb25zdCBtYXBwZWRUcmlnZ2VyTG9jY2F0aW9uID1cbiAgICAgICAgQW1wbGl0dWRlU2VydmljZS5tYXBUcmlnZ2VyTG9jYXRpb25Ub0NoYXRDb21wb25lbnQodHJpZ2dlckxvY2F0aW9uKTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLmV2ZW50UHJvcHMsXG4gICAgICAgIG1lc3NhZ2VfbWV0YWRhdGE6IHtcbiAgICAgICAgICAuLi5tZXNzYWdlTWV0YWRhdGEsXG4gICAgICAgICAgdHJpZ2dlcl9sb2NhdGlvbjogbWFwcGVkVHJpZ2dlckxvY2NhdGlvbixcbiAgICAgICAgICBmdWxsX3RyaWdnZXJfbG9jYXRpb246IHRyaWdnZXJMb2NhdGlvbixcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfVxuXG4gICAgaWYgKGV2ZW50TmFtZSA9PT0gU3BpZmZ5TWV0cmljc0V2ZW50TmFtZS5DaGF0Q29tcG9uZW50VmlzaWJsZSkge1xuICAgICAgY29uc3QgbWFwcGVkV2lkZ2V0VHlwZSA9IEFtcGxpdHVkZVNlcnZpY2UubWFwV2lkZ2V0VHlwZVRvQ2hhdENvbXBvbmVudChcbiAgICAgICAgZXZlbnRQcm9wcz8ud2lkZ2V0X3R5cGUsXG4gICAgICApO1xuICAgICAgY29uc3QgbWFwcGVkVHJpZ2dlckxvY2NhdGlvbiA9XG4gICAgICAgIEFtcGxpdHVkZVNlcnZpY2UubWFwVHJpZ2dlckxvY2F0aW9uVG9DaGF0Q29tcG9uZW50KHRyaWdnZXJMb2NhdGlvbik7XG4gICAgICByZXR1cm4ge1xuICAgICAgICAuLi5ldmVudFByb3BzLFxuICAgICAgICBtZXNzYWdlX21ldGFkYXRhOiB7XG4gICAgICAgICAgLi4ubWVzc2FnZU1ldGFkYXRhLFxuICAgICAgICAgIHRyaWdnZXJfbG9jYXRpb246IG1hcHBlZFRyaWdnZXJMb2NjYXRpb24sXG4gICAgICAgICAgZnVsbF90cmlnZ2VyX2xvY2F0aW9uOiB0cmlnZ2VyTG9jYXRpb24sXG4gICAgICAgIH0sXG4gICAgICAgIGNoYXRfY29tcG9uZW50OiBtYXBwZWRXaWRnZXRUeXBlLFxuICAgICAgICAnY2hhdC5jb21wb25lbnRfdmlzaWJsZSc6IHRydWUsXG4gICAgICB9O1xuICAgIH1cbiAgICByZXR1cm4gZXZlbnRQcm9wcyA/PyB7fTtcbiAgfVxuXG4gIHN0YXRpYyBpc1YzRW52aXZlRXZlbnQoZXZlbnROYW1lOiBTcGlmZnlNZXRyaWNzRXZlbnROYW1lIHwgRW52aXZlTWV0cmljc0V2ZW50TmFtZSk6IGJvb2xlYW4ge1xuICAgIHJldHVybiBPYmplY3QudmFsdWVzKEVudml2ZU1ldHJpY3NFdmVudE5hbWUpLmluY2x1ZGVzKGV2ZW50TmFtZSBhcyBFbnZpdmVNZXRyaWNzRXZlbnROYW1lKTtcbiAgfVxuXG4gIHN0YXRpYyBkZWNvcmF0ZUV2ZW50TmFtZShldmVudE5hbWU6IFNwaWZmeU1ldHJpY3NFdmVudE5hbWUgfCBFbnZpdmVNZXRyaWNzRXZlbnROYW1lKTogc3RyaW5nIHtcbiAgICBpZiAoQW1wbGl0dWRlU2VydmljZS5pc1YzRW52aXZlRXZlbnQoZXZlbnROYW1lKSkge1xuICAgICAgcmV0dXJuIGBbRW52aXZlXSAke2V2ZW50TmFtZX1gO1xuICAgIH1cbiAgICByZXR1cm4gYFtTcGlmZnldICR7ZXZlbnROYW1lfWA7XG4gIH1cblxuICBhc3luYyB0cmFja0V2ZW50KHtcbiAgICBldmVudE5hbWUsXG4gICAgZXZlbnRQcm9wcyxcbiAgICBldmVudEdyb3VwcyxcbiAgICBhbHNvU2VuZFRvR29vZ2xlQW5hbHl0aWNzID0gZmFsc2UsXG4gIH06IFRyYWNrRXZlbnRQYXJhbXMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5pc01vY2tBcGlLZXkpIHJldHVybjtcbiAgICBhd2FpdCB0aGlzLmluaXRQcm9taXNlO1xuICAgIGxvZ2dlci5sb2dEZWJ1ZygnU3VibWl0dGluZyBldmVudCcsIGV2ZW50TmFtZSk7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGRlY29yYXRlZEV2ZW50TmFtZSA9IEFtcGxpdHVkZVNlcnZpY2UuZGVjb3JhdGVFdmVudE5hbWUoZXZlbnROYW1lKTtcblxuICAgICAgaWYgKCF0aGlzLmFtcGxpdHVkZUNsaWVudCkge1xuICAgICAgICBsb2dnZXIubG9nV2FybignYW1wbGl0dWRlIGNsaWVudCB1bmRlZmluZWQnLCB1bmRlZmluZWQsIHtcbiAgICAgICAgICBldmVudF9uYW1lOiBkZWNvcmF0ZWRFdmVudE5hbWUsXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IG1hcHBlZEV2ZW50UHJvcHMgPSBBbXBsaXR1ZGVTZXJ2aWNlLm1hcEV2ZW50UHJvcHMoeyBldmVudE5hbWUsIGV2ZW50UHJvcHMgfSk7XG4gICAgICBjb25zdCBhbXBsaXR1ZGVFdmVudFByb3BzID1cbiAgICAgICAgZXZlbnROYW1lID09PSBFbnZpdmVNZXRyaWNzRXZlbnROYW1lLldpZGdldFRleHRSZXNwb25zZSB8fFxuICAgICAgICBldmVudE5hbWUgPT09IEVudml2ZU1ldHJpY3NFdmVudE5hbWUuV2lkZ2V0VGV4dENsaWNrZWRcbiAgICAgICAgICA/IChlbmNvZGVTdXBwbGVtZW50YXJ5VW5pY29kZShtYXBwZWRFdmVudFByb3BzKSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPilcbiAgICAgICAgICA6IG1hcHBlZEV2ZW50UHJvcHM7XG5cbiAgICAgIGNvbnN0IGV2ZW50RGF0YSA9IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgZXZlbnROYW1lLFxuICAgICAgICBldmVudFByb3BzOiBhbXBsaXR1ZGVFdmVudFByb3BzLFxuICAgICAgICBjcmVhdGVkX2F0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICB9KTtcbiAgICAgIGNvbnN0IGVuY29kZXIgPSBuZXcgVGV4dEVuY29kZXIoKTtcbiAgICAgIGNvbnN0IGRhdGEgPSBlbmNvZGVyLmVuY29kZShldmVudERhdGEpO1xuICAgICAgLy8gY2FsY3VsYXRlIGEgaGFzaCBvZiB0aGUgZXZlbnQgcHJvcGVydGllcyB0byB1c2UgYXMgdGhlIGluc2VydF9pZCBzbyB0aGF0IGR1cGxpY2F0ZSBldmVudHNcbiAgICAgIC8vIGFyZSBhdXRvbWF0aWNhbGx5IGRyb3BwZWQgYnkgQW1wbGl0dWRlXG4gICAgICBjb25zdCBoYXNoQnVmZmVyID0gYXdhaXQgY3J5cHRvLnN1YnRsZS5kaWdlc3QoJ1NIQS0yNTYnLCBkYXRhKTtcbiAgICAgIGNvbnN0IGhhc2hBcnJheSA9IEFycmF5LmZyb20obmV3IFVpbnQ4QXJyYXkoaGFzaEJ1ZmZlcikpO1xuICAgICAgY29uc3QgY3VycmVudEluc2VydElkID0gaGFzaEFycmF5Lm1hcChiID0+IGIudG9TdHJpbmcoMTYpLnBhZFN0YXJ0KDIsICcwJykpLmpvaW4oJycpO1xuXG4gICAgICBsb2dnZXIubG9nRGVidWcoYGFtcGxpdHVkZSB0cmFja2luZyAke2RlY29yYXRlZEV2ZW50TmFtZX1gLCBudWxsLCB7XG4gICAgICAgIGV2ZW50X25hbWU6IGRlY29yYXRlZEV2ZW50TmFtZSxcbiAgICAgICAgcHJvcHM6IGFtcGxpdHVkZUV2ZW50UHJvcHMsXG4gICAgICB9KTtcblxuICAgICAgdGhpcy5hbXBsaXR1ZGVDbGllbnQudHJhY2soXG4gICAgICAgIGRlY29yYXRlZEV2ZW50TmFtZSxcbiAgICAgICAge1xuICAgICAgICAgIC4uLnRoaXMuZ2V0RGVmYXVsdFRyYWNraW5nUHJvcHMoZXZlbnROYW1lKSxcbiAgICAgICAgICAuLi5hbXBsaXR1ZGVFdmVudFByb3BzLFxuICAgICAgICAgIC4uLihhbXBsaXR1ZGVFdmVudFByb3BzXG4gICAgICAgICAgICA/IHRoaXMuZXZlbnRQcm9wc1RvUHJlZml4ZWRFdmVudFByb3BzKGV2ZW50TmFtZSwgYW1wbGl0dWRlRXZlbnRQcm9wcylcbiAgICAgICAgICAgIDoge30pLFxuICAgICAgICB9LFxuICAgICAgICB7XG4gICAgICAgICAgLi4uZXZlbnRHcm91cHMsXG4gICAgICAgICAgaW5zZXJ0X2lkOiBjdXJyZW50SW5zZXJ0SWQsXG4gICAgICAgIH0sXG4gICAgICApO1xuXG4gICAgICAvLyBUT0RPOiBQdWxsIGdhRW5hYmxlZCBmcm9tIG9yZyBjb25maWcgd2hlbiBhdmFpbGFibGUuIEhhcmRjb2RlZCB0byB0cnVlIGZvciBub3cuXG4gICAgICBjb25zdCBnYUVuYWJsZWQgPSB0cnVlO1xuXG4gICAgICBpZiAoQW1wbGl0dWRlU2VydmljZS5pc1YzRW52aXZlRXZlbnQoZXZlbnROYW1lKSAmJiBnYUVuYWJsZWQpIHtcbiAgICAgICAgY29uc3QgbWVyZ2VkUHJvcHNGb3JHQTQgPSB7XG4gICAgICAgICAgLi4udGhpcy5nZXREZWZhdWx0VHJhY2tpbmdQcm9wcyhldmVudE5hbWUpLFxuICAgICAgICAgIC4uLm1hcHBlZEV2ZW50UHJvcHMsXG4gICAgICAgIH07XG4gICAgICAgIHByb2plY3RUb0dBNChldmVudE5hbWUgYXMgRW52aXZlTWV0cmljc0V2ZW50TmFtZSwgbWVyZ2VkUHJvcHNGb3JHQTQpO1xuICAgICAgfSBlbHNlIGlmIChhbHNvU2VuZFRvR29vZ2xlQW5hbHl0aWNzICYmIGdhRW5hYmxlZCkge1xuICAgICAgICBsb2dnZXIubG9nRGVidWcoJ0dBIHRyYWNraW5nJywgZGVjb3JhdGVkRXZlbnROYW1lKTtcbiAgICAgICAgaWYgKHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnICYmIHdpbmRvdy5kYXRhTGF5ZXIpIHtcbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLWV4cGxpY2l0LWFueVxuICAgICAgICAgICh3aW5kb3cuZGF0YUxheWVyIGFzIGFueVtdKS5wdXNoKHtcbiAgICAgICAgICAgIGV2ZW50OiBkZWNvcmF0ZWRFdmVudE5hbWUsXG4gICAgICAgICAgICBldmVudFByb3BzLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBsb2dnZXIubG9nRXJyb3IoJ0Vycm9yIHRyYWNraW5nIGV2ZW50JywgZXJyLCB7XG4gICAgICAgIGV2ZW50TmFtZSxcbiAgICAgICAgZXZlbnRQcm9wcyxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8vIEVuc3VyZSB0aGF0IHN1cHBsZW1lbnRhbCBkZWZhdWx0IHByb3BzIGFyZSBtZXJnZWQgd2l0aCB0aGUgZXhpc3RpbmcgcHJvcHNcbiAgc2V0U3VwcGxlbWVudGFsRGVmYXVsdFByb3BzKHByb3BzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IHZvaWQge1xuICAgIHRoaXMuc3VwcGxlbWVudGFsRGVmYXVs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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@envive-ai/react-hooks",
3
- "version": "0.3.56",
3
+ "version": "0.3.58",
4
4
  "description": "React hooks for connecting to Envive AI services.",
5
5
  "keywords": [
6
6
  "react",
@@ -74,6 +74,10 @@
74
74
  "import": "./dist/application/commerce-api.js",
75
75
  "require": "./dist/application/commerce-api.cjs"
76
76
  },
77
+ "./application/logging": {
78
+ "import": "./dist/application/logging/index.js",
79
+ "require": "./dist/application/logging/index.cjs"
80
+ },
77
81
  "./application/models": {
78
82
  "import": "./dist/application/models/index.js",
79
83
  "require": "./dist/application/models/index.cjs"
@@ -0,0 +1,171 @@
1
+ import {
2
+ ContextEnvEnum,
3
+ ContextSourceEnum,
4
+ ResponseCategory,
5
+ } from '@spiffy-ai/commerce-api-client';
6
+ import { getAtomStore } from 'src/atoms/atomStore/atomStore';
7
+ import { enviveConfigAtom } from 'src/atoms/envive/enviveConfig';
8
+ import { NextMessageRequest, Response } from 'src/application/models';
9
+ import CommerceApiClient from '../commerce-api';
10
+
11
+ const { mockV1NextResponsesPostRaw } = vi.hoisted(() => ({
12
+ mockV1NextResponsesPostRaw: vi.fn(),
13
+ }));
14
+
15
+ vi.mock('@spiffy-ai/commerce-api-client', async (importOriginal: () => Promise<unknown>) => {
16
+ const actual = (await importOriginal()) as typeof import('@spiffy-ai/commerce-api-client');
17
+ return {
18
+ ...actual,
19
+ InferenceApi: vi.fn().mockImplementation(function InferenceApi() {
20
+ return { v1NextResponsesPostRaw: mockV1NextResponsesPostRaw };
21
+ }),
22
+ };
23
+ });
24
+
25
+ const payload: NextMessageRequest = {
26
+ id: 'req-1',
27
+ context: {
28
+ chatId: 'chat-1',
29
+ orgId: 'org-1',
30
+ orgShortName: 'test-org',
31
+ userId: 'user-1',
32
+ source: ContextSourceEnum.App,
33
+ env: ContextEnvEnum.Dev,
34
+ },
35
+ };
36
+
37
+ const productRecord = (id: string, title: string, description = 'A great shoe') =>
38
+ JSON.stringify({
39
+ id,
40
+ category: 'product',
41
+ created_at: '2026-08-28 14:42:58.529065+00:00',
42
+ attributes: {
43
+ id: `sku-${id}`,
44
+ title,
45
+ description,
46
+ url: `https://example.com/${id}`,
47
+ image_url: `https://example.com/${id}.jpg`,
48
+ },
49
+ });
50
+
51
+ const streamFromByteChunks = (chunks: Uint8Array[]): ReadableStream<Uint8Array> =>
52
+ new ReadableStream({
53
+ start(controller) {
54
+ chunks.forEach(chunk => controller.enqueue(chunk));
55
+ controller.close();
56
+ },
57
+ });
58
+
59
+ const streamFromStringChunks = (chunks: string[]): ReadableStream<Uint8Array> => {
60
+ const encoder = new TextEncoder();
61
+ return streamFromByteChunks(chunks.map(chunk => encoder.encode(chunk)));
62
+ };
63
+
64
+ const mockStreamedResponse = (body: ReadableStream<Uint8Array>) => {
65
+ mockV1NextResponsesPostRaw.mockResolvedValueOnce({ raw: { body } });
66
+ };
67
+
68
+ const collectStream = async (): Promise<Response[]> => {
69
+ const results: Response[] = [];
70
+ for await (const response of CommerceApiClient.getNextResponseStreaming(payload)) {
71
+ results.push(response);
72
+ }
73
+ return results;
74
+ };
75
+
76
+ beforeEach(() => {
77
+ vi.clearAllMocks();
78
+ getAtomStore().set(enviveConfigAtom, {
79
+ baseUrl: 'https://commerce-api.test',
80
+ orgLevelApiKey: 'test-key',
81
+ orgId: 'org-1',
82
+ orgShortName: 'test-org',
83
+ env: 'dev',
84
+ contextSource: 'app',
85
+ });
86
+ });
87
+
88
+ describe('CommerceApiClient.getNextResponseStreaming — NDJSON chunk handling', () => {
89
+ it('parses records split mid-line across chunk boundaries', async () => {
90
+ const record1 = productRecord('rec-1', 'Kelly Mary Jane Flat');
91
+ const record2 = productRecord('rec-2', '27 Edit Carla Ballet Flat');
92
+ // Cut each record in the middle of a JSON string, as observed on cellular connections
93
+ mockStreamedResponse(
94
+ streamFromStringChunks([
95
+ record1.slice(0, 120),
96
+ `${record1.slice(120)}\n${record2.slice(0, 80)}`,
97
+ `${record2.slice(80)}\n`,
98
+ ]),
99
+ );
100
+
101
+ const results = await collectStream();
102
+
103
+ expect(results.map(r => r.id)).toEqual(['rec-1', 'rec-2']);
104
+ });
105
+
106
+ it('does not re-prepend a consumed partial to later chunks', async () => {
107
+ const record1 = productRecord('rec-1', 'First');
108
+ const record2 = productRecord('rec-2', 'Second');
109
+ const record3 = productRecord('rec-3', 'Third');
110
+ // record1 completes in chunk 2, which ends cleanly on a newline; the old
111
+ // implementation kept the stale partial and corrupted record3 in chunk 3
112
+ mockStreamedResponse(
113
+ streamFromStringChunks([
114
+ record1.slice(0, 100),
115
+ `${record1.slice(100)}\n${record2}\n`,
116
+ `${record3}\n`,
117
+ ]),
118
+ );
119
+
120
+ const results = await collectStream();
121
+
122
+ expect(results.map(r => r.id)).toEqual(['rec-1', 'rec-2', 'rec-3']);
123
+ });
124
+
125
+ it('flushes a final record that has no trailing newline', async () => {
126
+ const record1 = productRecord('rec-1', 'First');
127
+ const record2 = productRecord('rec-2', 'Last, unterminated');
128
+ mockStreamedResponse(
129
+ streamFromStringChunks([`${record1}\n${record2.slice(0, 90)}`, record2.slice(90)]),
130
+ );
131
+
132
+ const results = await collectStream();
133
+
134
+ expect(results.map(r => r.id)).toEqual(['rec-1', 'rec-2']);
135
+ });
136
+
137
+ it('decodes multi-byte UTF-8 characters split across chunk boundaries', async () => {
138
+ const record = productRecord('rec-1', 'Café flat', 'Voilà — c’est chic');
139
+ const bytes = new TextEncoder().encode(`${record}\n`);
140
+ // Split inside the multi-byte sequence of “é”
141
+ const splitAt = bytes.findIndex(b => b === 0xc3) + 1;
142
+ mockStreamedResponse(streamFromByteChunks([bytes.slice(0, splitAt), bytes.slice(splitAt)]));
143
+
144
+ const results = await collectStream();
145
+
146
+ expect(results).toHaveLength(1);
147
+ expect(results[0].category).toBe(ResponseCategory.Product);
148
+ if (results[0].category === ResponseCategory.Product) {
149
+ expect(results[0].attributes.title).toBe('Café flat');
150
+ expect(results[0].attributes.description).toBe('Voilà — c’est chic');
151
+ }
152
+ });
153
+
154
+ it('drops a malformed line without breaking the rest of the stream', async () => {
155
+ const record = productRecord('rec-1', 'Survivor');
156
+ mockStreamedResponse(streamFromStringChunks([`{"not": "valid json"\n${record}\n[DONE]\n`]));
157
+
158
+ const results = await collectStream();
159
+
160
+ expect(results.map(r => r.id)).toEqual(['rec-1']);
161
+ });
162
+
163
+ it('strips SSE "data: " prefixes and skips [DONE] markers', async () => {
164
+ const record = productRecord('rec-1', 'Prefixed');
165
+ mockStreamedResponse(streamFromStringChunks([`data: ${record}\ndata: [DONE]\n`]));
166
+
167
+ const results = await collectStream();
168
+
169
+ expect(results.map(r => r.id)).toEqual(['rec-1']);
170
+ });
171
+ });
@@ -263,34 +263,24 @@ class CommerceApiClient {
263
263
  const reader = response.raw.body.getReader();
264
264
  const decoder = new TextDecoder('utf-8');
265
265
 
266
+ // Buffer holding the trailing incomplete line of the previous chunk
266
267
  let partial = '';
267
- // TODO this function is recreated every time new data comes from the stream - define it outside of the generator
268
+
268
269
  const safeParse = (line: string): unknown => {
269
270
  try {
270
271
  return JSON.parse(line); // Parse the JSON string
271
272
  } catch (err) {
272
- logger.logError('Error parsing streamed line', err, {
273
- line,
274
- partial,
275
- });
276
- // swallow the error and set the partial to our current chunk
277
- partial = line;
278
- return partial;
273
+ logger.logError('Error parsing streamed line', err, { line });
274
+ return null;
279
275
  }
280
276
  };
281
277
 
282
- // TODO this function is recreated every time new data comes from the stream - define it outside of the generator
283
- const processChunk = (chunk: string): unknown[] => {
284
- // merge the partial with the incoming chunk
285
- const lines = `${partial}${chunk}`.split('\n');
286
-
287
- const parsedLines = lines
278
+ const parseLines = (lines: string[]): unknown[] =>
279
+ lines
288
280
  .map(line => line.replace(/^data: /, '').trim()) // Remove the "data: " prefix
289
281
  .filter(line => line !== '' && line !== '[DONE]') // Remove empty lines and "[DONE]"
290
282
  .map(safeParse)
291
- .filter(v => v); // and filter out the undefined values
292
- return parsedLines;
293
- };
283
+ .filter(v => v != null);
294
284
 
295
285
  while (true) {
296
286
  // eslint-disable-next-line no-await-in-loop
@@ -300,11 +290,12 @@ class CommerceApiClient {
300
290
  break;
301
291
  }
302
292
 
303
- // Massage and parse the chunk of data
304
- const chunk = decoder.decode(value);
305
- const parsedLines = processChunk(chunk);
293
+ // A chunk boundary can fall mid-line, so only parse the lines that
294
+ // are terminated by a newline and hold the rest until the next chunk
295
+ const lines = `${partial}${decoder.decode(value, { stream: true })}`.split('\n');
296
+ partial = lines.pop() ?? '';
306
297
 
307
- for (const parsedLine of parsedLines) {
298
+ for (const parsedLine of parseLines(lines)) {
308
299
  const validatedResponse = validateResponse(parsedLine);
309
300
 
310
301
  if (validatedResponse) {
@@ -312,6 +303,15 @@ class CommerceApiClient {
312
303
  }
313
304
  }
314
305
  }
306
+
307
+ // Flush the remainder of a stream that didn't end with a newline
308
+ for (const parsedLine of parseLines([`${partial}${decoder.decode()}`])) {
309
+ const validatedResponse = validateResponse(parsedLine);
310
+
311
+ if (validatedResponse) {
312
+ yield validatedResponse;
313
+ }
314
+ }
315
315
  } catch (error: unknown) {
316
316
  logger.logError('Failed to get next streaming responses', error, {
317
317
  payloadContext: payload?.context,
@@ -0,0 +1,159 @@
1
+ import Logger from 'src/application/logging/logger';
2
+ import {
3
+ DEFAULT_LOG_LEVEL,
4
+ LOG_LEVEL_STORAGE_KEY,
5
+ getLogLevel,
6
+ isLevelEnabled,
7
+ resetLogLevelForTesting,
8
+ setLogLevel,
9
+ } from 'src/application/logging/logLevel';
10
+
11
+ const setHref = (href: string) => {
12
+ Object.defineProperty(window, 'location', {
13
+ configurable: true,
14
+ value: { ...window.location, href, search: new URL(href).search },
15
+ });
16
+ };
17
+
18
+ describe('logger log level', () => {
19
+ const originalHref = window.location.href;
20
+ let spies: ReturnType<typeof vi.spyOn>[] = [];
21
+ let logger: Logger;
22
+
23
+ beforeEach(() => {
24
+ resetLogLevelForTesting();
25
+ window.localStorage.clear();
26
+ setHref('https://merchant.example.com/products/thing');
27
+ delete (window as { _envive?: unknown })._envive;
28
+ delete (window as { _spiffy?: unknown })._spiffy;
29
+ logger = new Logger('testCaller');
30
+ spies = [
31
+ vi.spyOn(console, 'debug').mockImplementation(() => {}),
32
+ vi.spyOn(console, 'info').mockImplementation(() => {}),
33
+ vi.spyOn(console, 'warn').mockImplementation(() => {}),
34
+ vi.spyOn(console, 'error').mockImplementation(() => {}),
35
+ ];
36
+ });
37
+
38
+ afterEach(() => {
39
+ spies.forEach(spy => spy.mockRestore());
40
+ resetLogLevelForTesting();
41
+ setHref(originalHref);
42
+ });
43
+
44
+ const emitAll = () => {
45
+ logger.logDebug('d');
46
+ logger.logInfo('i');
47
+ logger.logWarn('w', undefined);
48
+ logger.logError('e', undefined);
49
+ };
50
+
51
+ const emitted = () => ({
52
+ debug: (console.debug as unknown as { mock: { calls: unknown[] } }).mock.calls.length,
53
+ info: (console.info as unknown as { mock: { calls: unknown[] } }).mock.calls.length,
54
+ warn: (console.warn as unknown as { mock: { calls: unknown[] } }).mock.calls.length,
55
+ error: (console.error as unknown as { mock: { calls: unknown[] } }).mock.calls.length,
56
+ });
57
+
58
+ it('mutes debug and info but keeps warn and error by default', () => {
59
+ expect(DEFAULT_LOG_LEVEL).toBe('warn');
60
+ expect(getLogLevel()).toBe('warn');
61
+
62
+ emitAll();
63
+
64
+ expect(emitted()).toEqual({ debug: 0, info: 0, warn: 1, error: 1 });
65
+ });
66
+
67
+ it('silences every level at "silent"', () => {
68
+ setLogLevel('silent');
69
+
70
+ emitAll();
71
+
72
+ expect(emitted()).toEqual({ debug: 0, info: 0, warn: 0, error: 0 });
73
+ });
74
+
75
+ it('emits every level at "debug"', () => {
76
+ setLogLevel('debug');
77
+
78
+ emitAll();
79
+
80
+ expect(emitted()).toEqual({ debug: 1, info: 1, warn: 1, error: 1 });
81
+ });
82
+
83
+ it('keeps the existing output format when a level is enabled', () => {
84
+ setLogLevel('debug');
85
+
86
+ logger.logDebug('a message', { extra: true });
87
+
88
+ expect(console.debug).toHaveBeenCalledWith(
89
+ expect.stringMatching(/^DEBUG: \[envive-ai\] .+ - testCaller - a message$/),
90
+ { extra: true },
91
+ );
92
+ });
93
+
94
+ it('honours the window override without a reload', () => {
95
+ expect(isLevelEnabled('debug')).toBe(false);
96
+
97
+ (window as { _envive?: { logLevel?: string } })._envive = { logLevel: 'debug' };
98
+
99
+ expect(isLevelEnabled('debug')).toBe(true);
100
+ expect(getLogLevel()).toBe('debug');
101
+ });
102
+
103
+ it('accepts the _spiffy window override and is case-insensitive', () => {
104
+ (window as { _spiffy?: { logLevel?: string } })._spiffy = { logLevel: 'DEBUG' };
105
+
106
+ expect(getLogLevel()).toBe('debug');
107
+ });
108
+
109
+ it('ignores an unrecognised level and falls back to the default', () => {
110
+ (window as { _envive?: { logLevel?: string } })._envive = { logLevel: 'chatty' };
111
+
112
+ expect(getLogLevel()).toBe(DEFAULT_LOG_LEVEL);
113
+ });
114
+
115
+ it('reads the level from the query param and persists it for later navigations', () => {
116
+ setHref('https://merchant.example.com/products/thing?envive_log_level=debug');
117
+
118
+ expect(getLogLevel()).toBe('debug');
119
+ expect(window.localStorage.getItem(LOG_LEVEL_STORAGE_KEY)).toBe('debug');
120
+ });
121
+
122
+ it('applies a persisted level on a page without the query param', () => {
123
+ window.localStorage.setItem(LOG_LEVEL_STORAGE_KEY, 'info');
124
+
125
+ expect(getLogLevel()).toBe('info');
126
+ });
127
+
128
+ it('lets the query param win over a level pinned programmatically', () => {
129
+ setHref('https://merchant.example.com/products/thing?envive_log_level=debug');
130
+ setLogLevel('silent');
131
+
132
+ expect(getLogLevel()).toBe('debug');
133
+ });
134
+
135
+ it('lets setLogLevel(undefined) fall back to the levers below it', () => {
136
+ window.localStorage.setItem(LOG_LEVEL_STORAGE_KEY, 'info');
137
+ setLogLevel('silent');
138
+ expect(getLogLevel()).toBe('silent');
139
+
140
+ setLogLevel(undefined);
141
+
142
+ expect(getLogLevel()).toBe('info');
143
+ });
144
+
145
+ it('survives localStorage throwing', () => {
146
+ const getItem = vi.spyOn(window.localStorage, 'getItem').mockImplementation(() => {
147
+ throw new Error('blocked');
148
+ });
149
+ const setItem = vi.spyOn(window.localStorage, 'setItem').mockImplementation(() => {
150
+ throw new Error('blocked');
151
+ });
152
+ setHref('https://merchant.example.com/products/thing?envive_log_level=debug');
153
+
154
+ expect(getLogLevel()).toBe('debug');
155
+
156
+ getItem.mockRestore();
157
+ setItem.mockRestore();
158
+ });
159
+ });
@@ -0,0 +1,12 @@
1
+ export { default } from 'src/application/logging/logger';
2
+ export {
3
+ DEFAULT_LOG_LEVEL,
4
+ getLogLevel,
5
+ isLevelEnabled,
6
+ LOG_LEVEL_QUERY_PARAM,
7
+ LOG_LEVEL_STORAGE_KEY,
8
+ LOG_LEVELS,
9
+ resetLogLevelForTesting,
10
+ setLogLevel,
11
+ } from 'src/application/logging/logLevel';
12
+ export type { LogLevel } from 'src/application/logging/logLevel';