@nlabs/reaktor 1.6.3 → 1.7.0

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 (274) hide show
  1. package/LICENSE +74 -201
  2. package/README.md +48 -16
  3. package/lib/actions/analytics.d.ts +10 -0
  4. package/lib/actions/analytics.js +539 -0
  5. package/lib/actions/apps.js +2 -2
  6. package/lib/actions/connections.js +1 -1
  7. package/lib/actions/content.js +2 -2
  8. package/lib/actions/conversations.js +2 -2
  9. package/lib/actions/crm.d.ts +5 -1
  10. package/lib/actions/crm.js +226 -3
  11. package/lib/actions/crud.js +2 -2
  12. package/lib/actions/dynamodb.d.ts +1 -1
  13. package/lib/actions/dynamodb.js +2 -2
  14. package/lib/actions/email.d.ts +1 -1
  15. package/lib/actions/email.js +2 -2
  16. package/lib/actions/files.js +2 -2
  17. package/lib/actions/flags.d.ts +6 -0
  18. package/lib/actions/flags.js +147 -0
  19. package/lib/actions/groups.js +2 -2
  20. package/lib/actions/images.d.ts +1 -1
  21. package/lib/actions/images.js +2 -2
  22. package/lib/actions/index.d.ts +3 -2
  23. package/lib/actions/index.js +4 -3
  24. package/lib/actions/ios.js +1 -1
  25. package/lib/actions/locations.js +2 -2
  26. package/lib/actions/messages.js +2 -2
  27. package/lib/actions/notifications.d.ts +1 -1
  28. package/lib/actions/notifications.js +2 -2
  29. package/lib/actions/payments.d.ts +3 -3
  30. package/lib/actions/payments.js +144 -189
  31. package/lib/actions/personas.js +1 -1
  32. package/lib/actions/posts.d.ts +0 -4
  33. package/lib/actions/posts.js +2 -4
  34. package/lib/actions/query.js +2 -2
  35. package/lib/actions/reactions.js +2 -2
  36. package/lib/actions/rum.js +143 -45
  37. package/lib/actions/s3.d.ts +1 -1
  38. package/lib/actions/s3.js +2 -2
  39. package/lib/actions/search.js +2 -2
  40. package/lib/actions/sms.js +2 -2
  41. package/lib/actions/statistics.js +2 -2
  42. package/lib/actions/subscriptions.js +2 -2
  43. package/lib/actions/tags.js +2 -2
  44. package/lib/actions/users.js +59 -6
  45. package/lib/actions/videos.js +2 -2
  46. package/lib/adapters/arangoAdapter.js +1 -1
  47. package/lib/adapters/contentAdapter.js +2 -2
  48. package/lib/adapters/fileAdapter.js +1 -1
  49. package/lib/adapters/imageAdapter.js +1 -1
  50. package/lib/adapters/index.js +1 -1
  51. package/lib/adapters/messageAdapter.js +1 -1
  52. package/lib/adapters/postAdapter.js +1 -1
  53. package/lib/adapters/reaktorAdapter.js +1 -1
  54. package/lib/adapters/tagAdapter.d.ts +0 -1
  55. package/lib/adapters/tagAdapter.js +1 -2
  56. package/lib/adapters/userAdapter.d.ts +0 -1
  57. package/lib/adapters/userAdapter.js +1 -2
  58. package/lib/config.js +8 -2
  59. package/lib/constants/collections.d.ts +5 -1
  60. package/lib/constants/collections.js +6 -2
  61. package/lib/handlers/graphqlHandler.d.ts +2 -2
  62. package/lib/handlers/graphqlHandler.js +2 -19
  63. package/lib/index.d.ts +1 -1
  64. package/lib/index.js +2 -2
  65. package/lib/lambdas/actions/websockets.js +2 -2
  66. package/lib/lambdas/authorizer.js +2 -2
  67. package/lib/lambdas/connection.js +2 -2
  68. package/lib/lambdas/utils/message.js +2 -2
  69. package/lib/lambdas/utils/websocket.d.ts +1 -1
  70. package/lib/lambdas/utils/websocket.js +2 -2
  71. package/lib/mocks/conversation.js +1 -1
  72. package/lib/mocks/file.js +1 -1
  73. package/lib/mocks/group.js +1 -1
  74. package/lib/mocks/image.js +1 -1
  75. package/lib/mocks/post.js +1 -1
  76. package/lib/mocks/tag.js +1 -1
  77. package/lib/mocks/user.js +1 -1
  78. package/lib/mocks/video.js +1 -1
  79. package/lib/mutations/analytics/analytics.d.ts +1 -0
  80. package/lib/mutations/analytics/analytics.js +35 -0
  81. package/lib/mutations/content/content.js +2 -2
  82. package/lib/mutations/crm/crm.js +18 -4
  83. package/lib/mutations/events/events.js +2 -2
  84. package/lib/mutations/flags/flags.d.ts +1 -0
  85. package/lib/mutations/flags/flags.js +33 -0
  86. package/lib/mutations/groups/groups.js +2 -2
  87. package/lib/mutations/images/images.js +2 -2
  88. package/lib/mutations/index.d.ts +3 -1
  89. package/lib/mutations/index.js +13 -3
  90. package/lib/mutations/locations/locations.js +2 -2
  91. package/lib/mutations/messages/messages.js +2 -2
  92. package/lib/mutations/personas/personas.js +2 -2
  93. package/lib/mutations/posts/posts.js +2 -2
  94. package/lib/mutations/reactions/reactions.js +2 -2
  95. package/lib/mutations/rum/rum.js +2 -2
  96. package/lib/mutations/statistics/statistics.js +2 -2
  97. package/lib/mutations/subscriptions/subscriptions.js +2 -2
  98. package/lib/mutations/tags/tags.js +2 -2
  99. package/lib/mutations/users/users.js +30 -17
  100. package/lib/mutations/videos/videos.js +2 -2
  101. package/lib/objectTypes/analytics.d.ts +8 -0
  102. package/lib/objectTypes/analytics.js +147 -0
  103. package/lib/objectTypes/app.js +2 -2
  104. package/lib/objectTypes/bankAccount.js +2 -2
  105. package/lib/objectTypes/connection.js +2 -2
  106. package/lib/objectTypes/content.js +2 -2
  107. package/lib/objectTypes/conversation.js +2 -2
  108. package/lib/objectTypes/creditCard.js +2 -2
  109. package/lib/objectTypes/crm.d.ts +5 -0
  110. package/lib/objectTypes/crm.js +64 -2
  111. package/lib/objectTypes/document.js +1 -1
  112. package/lib/objectTypes/error.js +2 -2
  113. package/lib/objectTypes/event.js +2 -2
  114. package/lib/objectTypes/external.js +2 -2
  115. package/lib/objectTypes/file.js +2 -2
  116. package/lib/objectTypes/filter.js +2 -2
  117. package/lib/objectTypes/flag.d.ts +1 -0
  118. package/lib/objectTypes/flag.js +32 -0
  119. package/lib/objectTypes/group.js +2 -2
  120. package/lib/objectTypes/iapSubscription.js +2 -2
  121. package/lib/objectTypes/image.d.ts +1 -1
  122. package/lib/objectTypes/image.js +2 -2
  123. package/lib/objectTypes/index.d.ts +2 -0
  124. package/lib/objectTypes/index.js +3 -1
  125. package/lib/objectTypes/location.js +2 -2
  126. package/lib/objectTypes/message.js +2 -2
  127. package/lib/objectTypes/passcode.js +2 -2
  128. package/lib/objectTypes/persona.js +2 -2
  129. package/lib/objectTypes/plan.js +2 -2
  130. package/lib/objectTypes/post.js +2 -2
  131. package/lib/objectTypes/query.js +2 -2
  132. package/lib/objectTypes/reaction.js +2 -2
  133. package/lib/objectTypes/relation.js +2 -2
  134. package/lib/objectTypes/rum.js +4 -4
  135. package/lib/objectTypes/search.js +2 -2
  136. package/lib/objectTypes/statistics.js +2 -2
  137. package/lib/objectTypes/subscription.js +2 -2
  138. package/lib/objectTypes/tag.js +2 -2
  139. package/lib/objectTypes/user.js +2 -2
  140. package/lib/objectTypes/video.d.ts +1 -1
  141. package/lib/objectTypes/video.js +2 -2
  142. package/lib/queries/analytics/analytics.d.ts +1 -0
  143. package/lib/queries/analytics/analytics.js +41 -0
  144. package/lib/queries/content/content.js +2 -2
  145. package/lib/queries/crm/crm.js +15 -4
  146. package/lib/queries/events/events.js +2 -2
  147. package/lib/queries/flags/flags.d.ts +1 -0
  148. package/lib/queries/flags/flags.js +16 -0
  149. package/lib/queries/groups/groups.js +2 -2
  150. package/lib/queries/images/images.js +2 -2
  151. package/lib/queries/index.d.ts +3 -1
  152. package/lib/queries/index.js +13 -3
  153. package/lib/queries/locations/locations.js +2 -2
  154. package/lib/queries/messages/messages.js +1 -1
  155. package/lib/queries/personas/personas.js +2 -2
  156. package/lib/queries/posts/posts.js +2 -2
  157. package/lib/queries/query/query.js +2 -2
  158. package/lib/queries/reactions/reactions.js +2 -2
  159. package/lib/queries/statistics/statistics.js +2 -2
  160. package/lib/queries/subscriptions/subscriptions.js +1 -1
  161. package/lib/queries/tags/tags.js +2 -2
  162. package/lib/queries/users/users.js +2 -2
  163. package/lib/queries/videos/videos.js +2 -2
  164. package/lib/templates/email/layout.js +1 -1
  165. package/lib/templates/email/passwordForgot.js +1 -1
  166. package/lib/templates/email/passwordRecovery.js +1 -1
  167. package/lib/templates/email/verifyEmail.js +1 -1
  168. package/lib/templates/email/welcome.js +1 -1
  169. package/lib/templates/sms/passwordForgot.js +1 -1
  170. package/lib/templates/sms/passwordRecovery.js +1 -1
  171. package/lib/templates/sms/verifyEmail.js +1 -1
  172. package/lib/templates/sms/verifyPhone.js +1 -1
  173. package/lib/templates/sms/welcome.js +1 -1
  174. package/lib/types/analytics.types.d.ts +92 -0
  175. package/lib/types/analytics.types.js +6 -0
  176. package/lib/types/apps.types.d.ts +1 -1
  177. package/lib/types/apps.types.js +2 -2
  178. package/lib/types/arangodb.types.d.ts +2 -2
  179. package/lib/types/arangodb.types.js +2 -2
  180. package/lib/types/auth.types.d.ts +3 -1
  181. package/lib/types/auth.types.js +2 -2
  182. package/lib/types/connections.types.d.ts +1 -1
  183. package/lib/types/connections.types.js +2 -2
  184. package/lib/types/content.types.d.ts +1 -1
  185. package/lib/types/content.types.js +2 -2
  186. package/lib/types/conversations.types.d.ts +1 -1
  187. package/lib/types/conversations.types.js +2 -2
  188. package/lib/types/crm.types.d.ts +38 -1
  189. package/lib/types/crm.types.js +2 -2
  190. package/lib/types/email.types.d.ts +1 -1
  191. package/lib/types/email.types.js +2 -2
  192. package/lib/types/error.types.js +1 -1
  193. package/lib/types/files.types.d.ts +1 -1
  194. package/lib/types/files.types.js +2 -2
  195. package/lib/types/flags.types.d.ts +28 -0
  196. package/lib/types/flags.types.js +3 -0
  197. package/lib/types/google.types.d.ts +1 -1
  198. package/lib/types/google.types.js +2 -2
  199. package/lib/types/groups.types.d.ts +1 -1
  200. package/lib/types/groups.types.js +2 -2
  201. package/lib/types/images.types.d.ts +1 -1
  202. package/lib/types/images.types.js +2 -2
  203. package/lib/types/index.d.ts +3 -1
  204. package/lib/types/index.js +4 -2
  205. package/lib/types/locations.types.d.ts +1 -1
  206. package/lib/types/locations.types.js +2 -2
  207. package/lib/types/messages.types.d.ts +1 -1
  208. package/lib/types/messages.types.js +2 -2
  209. package/lib/types/notifications.types.d.ts +1 -1
  210. package/lib/types/notifications.types.js +2 -2
  211. package/lib/types/payments.types.d.ts +4 -1
  212. package/lib/types/payments.types.js +2 -2
  213. package/lib/types/personas.types.js +1 -1
  214. package/lib/types/posts.types.d.ts +1 -1
  215. package/lib/types/posts.types.js +2 -2
  216. package/lib/types/rum.types.d.ts +3 -3
  217. package/lib/types/rum.types.js +2 -2
  218. package/lib/types/statistics.types.js +1 -1
  219. package/lib/types/tags.types.d.ts +1 -1
  220. package/lib/types/tags.types.js +2 -2
  221. package/lib/types/users.types.d.ts +1 -1
  222. package/lib/types/users.types.js +2 -2
  223. package/lib/types/videos.types.d.ts +1 -1
  224. package/lib/types/videos.types.js +2 -2
  225. package/lib/types/websockets.types.d.ts +1 -1
  226. package/lib/types/websockets.types.js +2 -2
  227. package/lib/upload.js +4 -9
  228. package/lib/utils/adapterUtils.d.ts +1 -1
  229. package/lib/utils/adapterUtils.js +2 -2
  230. package/lib/utils/analyticsUtils.d.ts +1 -1
  231. package/lib/utils/analyticsUtils.js +2 -2
  232. package/lib/utils/aqlHelpers.d.ts +1 -1
  233. package/lib/utils/aqlHelpers.js +2 -2
  234. package/lib/utils/arangodbUtils.d.ts +9 -1
  235. package/lib/utils/arangodbUtils.js +10 -2
  236. package/lib/utils/authUtils.js +2 -2
  237. package/lib/utils/awsUtils.js +2 -2
  238. package/lib/utils/collectionRegistry.d.ts +1 -1
  239. package/lib/utils/collectionRegistry.js +17 -2
  240. package/lib/utils/contextUtils.d.ts +2 -2
  241. package/lib/utils/contextUtils.js +2 -2
  242. package/lib/utils/dbI18n.example.d.ts +1 -1
  243. package/lib/utils/dbI18n.example.js +2 -2
  244. package/lib/utils/dbI18n.js +2 -2
  245. package/lib/utils/googleTranslate.d.ts +1 -1
  246. package/lib/utils/googleTranslate.js +2 -2
  247. package/lib/utils/graphqlUtils.d.ts +1 -0
  248. package/lib/utils/graphqlUtils.js +1 -1
  249. package/lib/utils/imageUtils.js +1 -1
  250. package/lib/utils/index.d.ts +1 -1
  251. package/lib/utils/index.js +2 -2
  252. package/lib/utils/languageDetection.js +1 -1
  253. package/lib/utils/localeUtils.d.ts +1 -1
  254. package/lib/utils/localeUtils.example.d.ts +1 -1
  255. package/lib/utils/localeUtils.example.js +2 -2
  256. package/lib/utils/localeUtils.js +2 -2
  257. package/lib/utils/middlewareUtils.js +1 -1
  258. package/lib/utils/queryBuilder.d.ts +1 -1
  259. package/lib/utils/queryBuilder.js +2 -2
  260. package/lib/utils/schemaUtils.js +1 -1
  261. package/lib/utils/sessionUtils.js +2 -2
  262. package/lib/utils/stripeUtils.d.ts +1 -1
  263. package/lib/utils/stripeUtils.js +2 -2
  264. package/lib/utils/templateUtils.js +1 -1
  265. package/lib/utils/testUtils.js +2 -2
  266. package/lib/utils/translationQueue.d.ts +1 -1
  267. package/lib/utils/translationQueue.example.d.ts +1 -1
  268. package/lib/utils/translationQueue.example.js +2 -2
  269. package/lib/utils/translationQueue.js +2 -2
  270. package/lib/videoProcessing.js +1 -1
  271. package/package.json +43 -80
  272. package/lib/actions/websockets.d.ts +0 -20
  273. package/lib/actions/websockets.js +0 -164
  274. package/lib/mocks/nlabs.png +0 -0
@@ -0,0 +1,539 @@
1
+ /**
2
+ * Copyright (c) 2026-Present, Nitrogen Labs, Inc.
3
+ * All rights reserved. Use is subject to the accompanying commercial LICENSE.
4
+ */ import { CreateAppMonitorCommand, GetAppMonitorCommand, GetAppMonitorDataCommand, RUMClient } from '@aws-sdk/client-rum';
5
+ import { createHash, randomUUID } from 'crypto';
6
+ import { Config } from '../config.js';
7
+ import { COLLECTIONS } from '../constants/collections.js';
8
+ import { ErrorTypes } from '../types/error.types.js';
9
+ import { UserError } from '../utils/analyticsUtils.js';
10
+ import { useDb } from '../utils/arangodbUtils.js';
11
+ import { isAdminUser } from '../utils/sessionUtils.js';
12
+ import { addItem, getItemBy, getListBy, updateItem } from './crud.js';
13
+ const MAX_APP_ID_LENGTH = 128;
14
+ const MAX_DOMAIN_LENGTH = 253;
15
+ const MAX_MONITOR_NAME_LENGTH = 255;
16
+ const MAX_RUM_PAGES = 10;
17
+ const MAX_RUM_RESULTS = 100;
18
+ const VALID_RANGE_DAYS = new Set([
19
+ 1,
20
+ 7,
21
+ 30
22
+ ]);
23
+ const collectionInitialization = new Map();
24
+ const normalizeText = (value, maxLength)=>String(value || '').trim().slice(0, maxLength);
25
+ const normalizeDomain = (value)=>normalizeText(value, MAX_DOMAIN_LENGTH).replace(/^https?:\/\//i, '').split('/')[0].split(':')[0].toLowerCase();
26
+ const normalizeMonitorName = (value, appId)=>normalizeText(value || appId, MAX_MONITOR_NAME_LENGTH).replace(/[^a-zA-Z0-9-_]/g, '-');
27
+ const getDocumentKey = (appId)=>createHash('sha256').update(appId).digest('hex');
28
+ const requireAdmin = (context)=>{
29
+ if (!isAdminUser(context.session)) {
30
+ throw new UserError(ErrorTypes.INVALID_SESSION);
31
+ }
32
+ };
33
+ const requireSession = (context)=>{
34
+ const session = context.session;
35
+ if (!session?.userId) {
36
+ throw new UserError(ErrorTypes.INVALID_SESSION);
37
+ }
38
+ return session;
39
+ };
40
+ const initializeAnalyticsCollection = async (databaseName)=>{
41
+ const cacheKey = databaseName || 'reaktor';
42
+ const existingInitialization = collectionInitialization.get(cacheKey);
43
+ if (existingInitialization) {
44
+ return existingInitialization;
45
+ }
46
+ const initialization = (async ()=>{
47
+ const database = useDb(databaseName);
48
+ const collection = database.collection(COLLECTIONS.ANALYTICS);
49
+ if (!await collection.exists()) {
50
+ try {
51
+ await database.createCollection(COLLECTIONS.ANALYTICS);
52
+ } catch (error) {
53
+ if (!await collection.exists()) {
54
+ throw error;
55
+ }
56
+ }
57
+ }
58
+ await collection.ensureIndex({
59
+ fields: [
60
+ 'analyticsId'
61
+ ],
62
+ type: 'persistent',
63
+ unique: true
64
+ });
65
+ await collection.ensureIndex({
66
+ fields: [
67
+ 'appId'
68
+ ],
69
+ type: 'persistent',
70
+ unique: true
71
+ });
72
+ })();
73
+ collectionInitialization.set(cacheKey, initialization);
74
+ try {
75
+ await initialization;
76
+ } catch (error) {
77
+ collectionInitialization.delete(cacheKey);
78
+ throw error;
79
+ }
80
+ };
81
+ const getRumClient = (context)=>{
82
+ const awsConfig = context.config?.aws || Config.get('aws', {});
83
+ const { accessKeyId, secretAccessKey, sessionToken } = awsConfig.credentials || {};
84
+ const usesRuntimeCredentials = Boolean(process.env.AWS_SESSION_TOKEN && accessKeyId === process.env.AWS_ACCESS_KEY_ID && secretAccessKey === process.env.AWS_SECRET_ACCESS_KEY);
85
+ const credentials = accessKeyId && secretAccessKey && !usesRuntimeCredentials ? {
86
+ accessKeyId,
87
+ secretAccessKey,
88
+ ...sessionToken ? {
89
+ sessionToken
90
+ } : {}
91
+ } : undefined;
92
+ return new RUMClient({
93
+ ...credentials ? {
94
+ credentials
95
+ } : {},
96
+ ...awsConfig.region ? {
97
+ region: awsConfig.region
98
+ } : {}
99
+ });
100
+ };
101
+ const findAwsMonitor = async (client, monitorName)=>{
102
+ try {
103
+ const response = await client.send(new GetAppMonitorCommand({
104
+ Name: monitorName
105
+ }));
106
+ const monitorId = normalizeText(response.AppMonitor?.Id, 36);
107
+ return monitorId ? {
108
+ domain: normalizeDomain(response.AppMonitor?.Domain),
109
+ id: monitorId,
110
+ name: normalizeText(response.AppMonitor?.Name || monitorName, MAX_MONITOR_NAME_LENGTH)
111
+ } : null;
112
+ } catch (error) {
113
+ if (error.name === 'ResourceNotFoundException') {
114
+ return null;
115
+ }
116
+ throw error;
117
+ }
118
+ };
119
+ const findOrCreateAwsMonitor = async (context, appId, domain, monitorName)=>{
120
+ const client = getRumClient(context);
121
+ const existingMonitor = await findAwsMonitor(client, monitorName);
122
+ if (existingMonitor) {
123
+ return {
124
+ domain: existingMonitor.domain || domain,
125
+ id: existingMonitor.id,
126
+ name: existingMonitor.name
127
+ };
128
+ }
129
+ const response = await client.send(new CreateAppMonitorCommand({
130
+ CustomEvents: {
131
+ Status: 'ENABLED'
132
+ },
133
+ CwLogEnabled: false,
134
+ Domain: domain,
135
+ Name: monitorName,
136
+ Platform: 'Web',
137
+ Tags: {
138
+ appId,
139
+ managedBy: 'reaktor'
140
+ }
141
+ }));
142
+ const monitorId = normalizeText(response.Id, 36);
143
+ if (!monitorId) {
144
+ throw new UserError('invalid_rum_monitor');
145
+ }
146
+ return {
147
+ domain,
148
+ id: monitorId,
149
+ name: monitorName
150
+ };
151
+ };
152
+ const parseMonitorInput = (input)=>{
153
+ const appId = normalizeText(input?.appId, MAX_APP_ID_LENGTH);
154
+ const appName = normalizeText(input?.appName || input?.monitorName || appId, MAX_MONITOR_NAME_LENGTH);
155
+ const domain = normalizeDomain(input?.domain);
156
+ const monitorName = normalizeMonitorName(input?.monitorName, appId);
157
+ if (!appId || !domain || !monitorName) {
158
+ throw new UserError(ErrorTypes.INVALID_ARGUMENTS);
159
+ }
160
+ return {
161
+ appId,
162
+ appName,
163
+ domain,
164
+ monitorName
165
+ };
166
+ };
167
+ export const getAnalyticsByAppId = async (context, appIdInput)=>{
168
+ requireAdmin(context);
169
+ const appId = normalizeText(appIdInput, MAX_APP_ID_LENGTH);
170
+ if (!appId) {
171
+ throw new UserError(ErrorTypes.INVALID_ARGUMENTS);
172
+ }
173
+ await initializeAnalyticsCollection(context.databaseName);
174
+ return getItemBy(context, COLLECTIONS.ANALYTICS, {
175
+ appId
176
+ });
177
+ };
178
+ export const getAnalyticsConfiguration = async (context, appIdInput)=>{
179
+ const appId = normalizeText(appIdInput, MAX_APP_ID_LENGTH);
180
+ if (!appId) {
181
+ throw new UserError(ErrorTypes.INVALID_ARGUMENTS);
182
+ }
183
+ await initializeAnalyticsCollection(context.databaseName);
184
+ const analytics = await getItemBy(context, COLLECTIONS.ANALYTICS, {
185
+ appId
186
+ });
187
+ return analytics ? {
188
+ analyticsId: analytics.analyticsId,
189
+ appId: analytics.appId
190
+ } : null;
191
+ };
192
+ export const getAnalyticsById = async (context, analyticsIdInput)=>{
193
+ const analyticsId = normalizeText(analyticsIdInput, 36);
194
+ if (!analyticsId) {
195
+ return null;
196
+ }
197
+ await initializeAnalyticsCollection(context.databaseName);
198
+ return getItemBy(context, COLLECTIONS.ANALYTICS, {
199
+ analyticsId
200
+ });
201
+ };
202
+ export const createAnalyticsMonitor = async (context, input)=>{
203
+ const session = requireSession(context);
204
+ const { appId, appName, domain, monitorName } = parseMonitorInput(input);
205
+ await initializeAnalyticsCollection(context.databaseName);
206
+ const existing = await getItemBy(context, COLLECTIONS.ANALYTICS, {
207
+ appId
208
+ });
209
+ if (existing) {
210
+ if (isAdminUser(session) || (existing.userIds || []).includes(session.userId)) {
211
+ return existing;
212
+ }
213
+ throw new UserError(ErrorTypes.INVALID_SESSION);
214
+ }
215
+ const monitor = await findOrCreateAwsMonitor(context, appId, domain, monitorName);
216
+ return addItem(context, COLLECTIONS.ANALYTICS, {
217
+ _key: getDocumentKey(appId),
218
+ analyticsId: randomUUID(),
219
+ appId,
220
+ appName,
221
+ domain: monitor.domain,
222
+ monitorId: monitor.id,
223
+ monitorName: monitor.name,
224
+ userIds: [
225
+ session.userId
226
+ ]
227
+ });
228
+ };
229
+ export const regenerateAnalyticsId = async (context, analyticsIdInput)=>{
230
+ const session = requireSession(context);
231
+ const analyticsId = normalizeText(analyticsIdInput, 36);
232
+ if (!analyticsId) {
233
+ throw new UserError(ErrorTypes.INVALID_ARGUMENTS);
234
+ }
235
+ await initializeAnalyticsCollection(context.databaseName);
236
+ const existing = await getItemBy(context, COLLECTIONS.ANALYTICS, {
237
+ analyticsId
238
+ });
239
+ const hasAccess = existing && (isAdminUser(session) || (existing.userIds || []).includes(session.userId));
240
+ if (!existing?._key || !hasAccess) {
241
+ throw new UserError('invalid_rum_app');
242
+ }
243
+ return updateItem(context, COLLECTIONS.ANALYTICS, existing._key, {
244
+ analyticsId: randomUUID()
245
+ });
246
+ };
247
+ const parseJsonRecord = (value)=>{
248
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
249
+ return value;
250
+ }
251
+ if (typeof value !== 'string') {
252
+ return {};
253
+ }
254
+ try {
255
+ const parsed = JSON.parse(value);
256
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
257
+ } catch {
258
+ return {};
259
+ }
260
+ };
261
+ const getProperty = (properties, metadata, aliases)=>{
262
+ const normalizedAliases = new Set(aliases.map((alias)=>alias.toLowerCase().replace(/[^a-z0-9]/g, '')));
263
+ const entries = [
264
+ ...Object.entries(properties),
265
+ ...Object.entries(metadata)
266
+ ];
267
+ return entries.find(([key])=>normalizedAliases.has(key.toLowerCase().replace(/[^a-z0-9]/g, '')))?.[1];
268
+ };
269
+ const normalizeRumEvent = (rawEvent)=>{
270
+ try {
271
+ const source = JSON.parse(rawEvent);
272
+ const details = parseJsonRecord(source.event_details);
273
+ const metadata = parseJsonRecord(source.metadata);
274
+ const properties = parseJsonRecord(details.properties);
275
+ const timestamp = Number(source.event_timestamp || 0);
276
+ if (!Number.isFinite(timestamp) || timestamp <= 0) {
277
+ return null;
278
+ }
279
+ const getText = (aliases, fallback = '')=>normalizeText(getProperty(properties, metadata, aliases) ?? fallback, 256);
280
+ const durationValue = Number(getProperty(properties, metadata, [
281
+ 'duration',
282
+ 'durationMs',
283
+ 'latency',
284
+ 'loadTime',
285
+ 'pageLoadTime'
286
+ ]) || 0);
287
+ const path = normalizeText(details.path, 256);
288
+ const type = normalizeText(source.event_type, 256);
289
+ return {
290
+ appId: normalizeText(details.appId || metadata.applicationId, MAX_APP_ID_LENGTH),
291
+ browserLanguage: getText([
292
+ 'browserLanguage'
293
+ ], 'Unknown'),
294
+ browserName: getText([
295
+ 'browserName'
296
+ ], 'Unknown'),
297
+ browserVersion: getText([
298
+ 'browserVersion'
299
+ ]),
300
+ deviceType: getText([
301
+ 'deviceType'
302
+ ], 'Unknown'),
303
+ duration: Number.isFinite(durationValue) && durationValue > 0 ? durationValue : 0,
304
+ eventId: normalizeText(source.event_id, 128),
305
+ interaction: getText([
306
+ 'interaction'
307
+ ], normalizeText(details.name || type, 128)),
308
+ name: normalizeText(details.name || type, 128),
309
+ osName: getText([
310
+ 'osName'
311
+ ], 'Unknown'),
312
+ osVersion: getText([
313
+ 'osVersion'
314
+ ]),
315
+ outcome: getText([
316
+ 'outcome'
317
+ ]),
318
+ pageId: getText([
319
+ 'pageId',
320
+ 'pageID'
321
+ ], path || 'Unknown'),
322
+ parentPage: getText([
323
+ 'parentPageId',
324
+ 'parentPage'
325
+ ]),
326
+ path,
327
+ sessionId: normalizeText(source.sessionId || parseJsonRecord(source.user_details).sessionId, 128),
328
+ timestamp,
329
+ type,
330
+ viewId: getText([
331
+ 'viewId'
332
+ ])
333
+ };
334
+ } catch {
335
+ return null;
336
+ }
337
+ };
338
+ const createBreakdown = (values, total)=>{
339
+ const counts = values.reduce((result, value)=>{
340
+ const label = value || 'Unknown';
341
+ result.set(label, (result.get(label) || 0) + 1);
342
+ return result;
343
+ }, new Map());
344
+ return Array.from(counts, ([label, count])=>({
345
+ count,
346
+ label,
347
+ percentage: total > 0 ? Math.round(count / total * 1000) / 10 : 0
348
+ })).sort((left, right)=>right.count - left.count || left.label.localeCompare(right.label));
349
+ };
350
+ const percentile = (values, percentileValue)=>{
351
+ if (values.length === 0) {
352
+ return 0;
353
+ }
354
+ const sorted = values.toSorted((left, right)=>left - right);
355
+ const index = Math.max(0, Math.ceil(percentileValue * sorted.length) - 1);
356
+ return sorted[index];
357
+ };
358
+ const createViewPerformance = (events)=>{
359
+ const views = events.reduce((result, event)=>{
360
+ if (event.type !== 'view_performance' && event.name !== 'view_performance' || !event.viewId || event.outcome === 'cancelled') {
361
+ return result;
362
+ }
363
+ const samples = result.get(event.viewId) || [];
364
+ samples.push(event);
365
+ result.set(event.viewId, samples);
366
+ return result;
367
+ }, new Map());
368
+ return Array.from(views, ([viewId, samples])=>{
369
+ const durations = samples.map((event)=>event.duration);
370
+ const percentage = (outcome)=>Math.round(samples.filter((event)=>event.outcome === outcome).length / samples.length * 1000) / 10;
371
+ return {
372
+ failureRate: percentage('failure'),
373
+ p50: percentile(durations, 0.5),
374
+ p75: percentile(durations, 0.75),
375
+ p95: percentile(durations, 0.95),
376
+ sampleCount: samples.length,
377
+ timeoutRate: percentage('timeout'),
378
+ viewId
379
+ };
380
+ }).sort((left, right)=>right.sampleCount - left.sampleCount || left.viewId.localeCompare(right.viewId));
381
+ };
382
+ const getAuthorizedAnalytics = async (context, analyticsId)=>{
383
+ const session = requireSession(context);
384
+ const analytics = await getAnalyticsById(context, analyticsId);
385
+ const hasAccess = analytics && (isAdminUser(session) || (analytics.userIds || []).includes(session.userId));
386
+ if (!analytics || !hasAccess) {
387
+ throw new UserError(ErrorTypes.INVALID_SESSION);
388
+ }
389
+ return analytics;
390
+ };
391
+ const getAnalyticsAppName = async (context, analytics)=>{
392
+ if (analytics.appName) {
393
+ return analytics.appName;
394
+ }
395
+ const app = await getItemBy(context, COLLECTIONS.APPS, {
396
+ conditions: [
397
+ {
398
+ field: 'appId',
399
+ operator: '==',
400
+ value: analytics.appId
401
+ },
402
+ {
403
+ field: '_key',
404
+ operator: '==',
405
+ value: analytics.appId
406
+ }
407
+ ],
408
+ logic: 'OR'
409
+ });
410
+ return normalizeText(app?.displayName || app?.name || analytics.monitorName || analytics.appId, MAX_MONITOR_NAME_LENGTH);
411
+ };
412
+ export const getAvailableAnalytics = async (context)=>{
413
+ const session = requireSession(context);
414
+ await initializeAnalyticsCollection(context.databaseName);
415
+ const analytics = await getListBy(context, COLLECTIONS.ANALYTICS, {}, {
416
+ sort: [
417
+ {
418
+ direction: 'ASC',
419
+ field: 'appName'
420
+ }
421
+ ]
422
+ });
423
+ const available = analytics.filter((item)=>isAdminUser(session) || (item.userIds || []).includes(session.userId));
424
+ const enriched = await Promise.all(available.map(async (item)=>({
425
+ ...item,
426
+ appName: await getAnalyticsAppName(context, item),
427
+ userIds: item.userIds || []
428
+ })));
429
+ return enriched.sort((left, right)=>(left.appName || '').localeCompare(right.appName || ''));
430
+ };
431
+ export const setAnalyticsAccess = async (context, analyticsIdInput, userIdsInput)=>{
432
+ requireAdmin(context);
433
+ const analyticsId = normalizeText(analyticsIdInput, 36);
434
+ const analytics = await getAnalyticsById(context, analyticsId);
435
+ if (!analytics?._key) {
436
+ throw new UserError('invalid_rum_app');
437
+ }
438
+ const userIds = Array.from(new Set((userIdsInput || []).map((userId)=>normalizeText(userId, 128)).filter(Boolean))).slice(0, 100);
439
+ return updateItem(context, COLLECTIONS.ANALYTICS, analytics._key, {
440
+ userIds
441
+ });
442
+ };
443
+ export const getAnalyticsDashboard = async (context, analyticsIdInput, rangeDaysInput)=>{
444
+ const analyticsId = normalizeText(analyticsIdInput, 36);
445
+ const rangeDays = Math.floor(Number(rangeDaysInput));
446
+ if (!analyticsId || !VALID_RANGE_DAYS.has(rangeDays)) {
447
+ throw new UserError(ErrorTypes.INVALID_ARGUMENTS);
448
+ }
449
+ const analytics = await getAuthorizedAnalytics(context, analyticsId);
450
+ const endTime = Date.now();
451
+ const startTime = endTime - rangeDays * 24 * 60 * 60 * 1000 + (rangeDays === 30 ? 60_000 : 0);
452
+ const client = getRumClient(context);
453
+ const rawEvents = [];
454
+ let nextToken;
455
+ let page = 0;
456
+ do {
457
+ const response = await client.send(new GetAppMonitorDataCommand({
458
+ MaxResults: MAX_RUM_RESULTS,
459
+ Name: analytics.monitorName,
460
+ ...nextToken ? {
461
+ NextToken: nextToken
462
+ } : {},
463
+ TimeRange: {
464
+ After: startTime,
465
+ Before: endTime
466
+ }
467
+ }));
468
+ rawEvents.push(...response.Events || []);
469
+ nextToken = response.NextToken;
470
+ page += 1;
471
+ }while (nextToken && page < MAX_RUM_PAGES)
472
+ const events = rawEvents.map(normalizeRumEvent).filter((event)=>Boolean(event && event.appId === analytics.appId)).sort((left, right)=>right.timestamp - left.timestamp);
473
+ const sessions = new Set(events.map((event)=>event.sessionId).filter(Boolean));
474
+ const interactionEvents = events.filter((event)=>event.type !== 'page_view' && event.type !== 'view_performance');
475
+ const errorEvents = events.filter((event)=>/error|exception|failure/i.test(`${event.type} ${event.name}`));
476
+ const durations = events.map((event)=>event.duration).filter((duration)=>duration > 0);
477
+ const bucketSize = rangeDays === 1 ? 60 * 60 * 1000 : 24 * 60 * 60 * 1000;
478
+ const buckets = new Map();
479
+ for(let timestamp = Math.floor(startTime / bucketSize) * bucketSize; timestamp <= endTime; timestamp += bucketSize){
480
+ buckets.set(timestamp, {
481
+ errors: 0,
482
+ events: 0,
483
+ interactions: 0,
484
+ sessions: new Set()
485
+ });
486
+ }
487
+ events.forEach((event)=>{
488
+ const bucket = buckets.get(Math.floor(event.timestamp / bucketSize) * bucketSize);
489
+ if (!bucket) {
490
+ return;
491
+ }
492
+ bucket.events += 1;
493
+ bucket.interactions += event.type === 'page_view' || event.type === 'view_performance' ? 0 : 1;
494
+ bucket.errors += /error|exception|failure/i.test(`${event.type} ${event.name}`) ? 1 : 0;
495
+ if (event.sessionId) {
496
+ bucket.sessions.add(event.sessionId);
497
+ }
498
+ });
499
+ return {
500
+ analyticsId: analytics.analyticsId,
501
+ appId: analytics.appId,
502
+ appName: await getAnalyticsAppName(context, analytics),
503
+ averageDuration: durations.length ? Math.round(durations.reduce((total, duration)=>total + duration, 0) / durations.length) : 0,
504
+ browsers: createBreakdown(events.map((event)=>[
505
+ event.browserName,
506
+ event.browserVersion
507
+ ].filter(Boolean).join(' ')), events.length),
508
+ devices: createBreakdown(events.map((event)=>event.deviceType), events.length),
509
+ endTime,
510
+ errorCount: errorEvents.length,
511
+ eventCount: events.length,
512
+ generatedAt: Date.now(),
513
+ interactions: createBreakdown(interactionEvents.map((event)=>event.interaction || event.name), interactionEvents.length),
514
+ languages: createBreakdown(events.map((event)=>event.browserLanguage), events.length),
515
+ monitorId: analytics.monitorId,
516
+ monitorName: analytics.monitorName,
517
+ operatingSystems: createBreakdown(events.map((event)=>[
518
+ event.osName,
519
+ event.osVersion
520
+ ].filter(Boolean).join(' ')), events.length),
521
+ pages: createBreakdown(events.map((event)=>event.pageId || event.path), events.length),
522
+ pageViewCount: events.filter((event)=>event.type === 'page_view').length,
523
+ partial: Boolean(nextToken),
524
+ performanceSamples: durations.length,
525
+ recentEvents: events.slice(0, 100).map(({ appId: _appId, ...event })=>event),
526
+ sessionCount: sessions.size,
527
+ startTime,
528
+ timeline: Array.from(buckets, ([timestamp, bucket])=>({
529
+ errors: bucket.errors,
530
+ events: bucket.events,
531
+ interactions: bucket.interactions,
532
+ sessions: bucket.sessions.size,
533
+ timestamp
534
+ })),
535
+ viewPerformance: createViewPerformance(events)
536
+ };
537
+ };
538
+
539
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9wcml2YXRlL3RtcC9ubGFicy1yZWFrdG9yLXJlbGVhc2UuZU1RNVNoL2NoZWNrb3V0L3BhY2thZ2VzL3JlYWt0b3Ivc3JjL2FjdGlvbnMvYW5hbHl0aWNzLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ29weXJpZ2h0IChjKSAyMDI2LVByZXNlbnQsIE5pdHJvZ2VuIExhYnMsIEluYy5cbiAqIEFsbCByaWdodHMgcmVzZXJ2ZWQuIFVzZSBpcyBzdWJqZWN0IHRvIHRoZSBhY2NvbXBhbnlpbmcgY29tbWVyY2lhbCBMSUNFTlNFLlxuICovXG5pbXBvcnQge1xuICBDcmVhdGVBcHBNb25pdG9yQ29tbWFuZCxcbiAgR2V0QXBwTW9uaXRvckNvbW1hbmQsXG4gIEdldEFwcE1vbml0b3JEYXRhQ29tbWFuZCxcbiAgUlVNQ2xpZW50XG59IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1ydW0nO1xuaW1wb3J0IHtjcmVhdGVIYXNoLCByYW5kb21VVUlEfSBmcm9tICdjcnlwdG8nO1xuXG5pbXBvcnQge0NvbmZpZ30gZnJvbSAnLi4vY29uZmlnLmpzJztcbmltcG9ydCB7Q09MTEVDVElPTlN9IGZyb20gJy4uL2NvbnN0YW50cy9jb2xsZWN0aW9ucy5qcyc7XG5pbXBvcnQge0Vycm9yVHlwZXN9IGZyb20gJy4uL3R5cGVzL2Vycm9yLnR5cGVzLmpzJztcbmltcG9ydCB7VXNlckVycm9yfSBmcm9tICcuLi91dGlscy9hbmFseXRpY3NVdGlscy5qcyc7XG5pbXBvcnQge3VzZURifSBmcm9tICcuLi91dGlscy9hcmFuZ29kYlV0aWxzLmpzJztcbmltcG9ydCB7aXNBZG1pblVzZXJ9IGZyb20gJy4uL3V0aWxzL3Nlc3Npb25VdGlscy5qcyc7XG5pbXBvcnQge2FkZEl0ZW0sIGdldEl0ZW1CeSwgZ2V0TGlzdEJ5LCB1cGRhdGVJdGVtfSBmcm9tICcuL2NydWQuanMnO1xuXG5pbXBvcnQgdHlwZSB7QXdzQ29uZmlnfSBmcm9tICcuLi9jb25maWcuanMnO1xuaW1wb3J0IHR5cGUge0FwcFR5cGV9IGZyb20gJy4uL3R5cGVzL2FwcHMudHlwZXMuanMnO1xuaW1wb3J0IHR5cGUge1xuICBBbmFseXRpY3NDb25maWd1cmF0aW9uVHlwZSxcbiAgQW5hbHl0aWNzRGFzaGJvYXJkVHlwZSxcbiAgQW5hbHl0aWNzRXZlbnRUeXBlLFxuICBBbmFseXRpY3NUeXBlLFxuICBDcmVhdGVBbmFseXRpY3NNb25pdG9ySW5wdXRUeXBlXG59IGZyb20gJy4uL3R5cGVzL2FuYWx5dGljcy50eXBlcy5qcyc7XG5pbXBvcnQgdHlwZSB7QXBpQ29udGV4dH0gZnJvbSAnLi4vdHlwZXMvYXV0aC50eXBlcy5qcyc7XG5pbXBvcnQgdHlwZSB7U2Vzc2lvbn0gZnJvbSAnLi4vdXRpbHMvc2Vzc2lvblV0aWxzLmpzJztcblxuY29uc3QgTUFYX0FQUF9JRF9MRU5HVEggPSAxMjg7XG5jb25zdCBNQVhfRE9NQUlOX0xFTkdUSCA9IDI1MztcbmNvbnN0IE1BWF9NT05JVE9SX05BTUVfTEVOR1RIID0gMjU1O1xuY29uc3QgTUFYX1JVTV9QQUdFUyA9IDEwO1xuY29uc3QgTUFYX1JVTV9SRVNVTFRTID0gMTAwO1xuY29uc3QgVkFMSURfUkFOR0VfREFZUyA9IG5ldyBTZXQoWzEsIDcsIDMwXSk7XG5jb25zdCBjb2xsZWN0aW9uSW5pdGlhbGl6YXRpb24gPSBuZXcgTWFwPHN0cmluZywgUHJvbWlzZTx2b2lkPj4oKTtcblxuY29uc3Qgbm9ybWFsaXplVGV4dCA9ICh2YWx1ZTogdW5rbm93biwgbWF4TGVuZ3RoOiBudW1iZXIpOiBzdHJpbmcgPT5cbiAgU3RyaW5nKHZhbHVlIHx8ICcnKS50cmltKCkuc2xpY2UoMCwgbWF4TGVuZ3RoKTtcblxuY29uc3Qgbm9ybWFsaXplRG9tYWluID0gKHZhbHVlOiB1bmtub3duKTogc3RyaW5nID0+IG5vcm1hbGl6ZVRleHQodmFsdWUsIE1BWF9ET01BSU5fTEVOR1RIKVxuICAucmVwbGFjZSgvXmh0dHBzPzpcXC9cXC8vaSwgJycpXG4gIC5zcGxpdCgnLycpWzBdXG4gIC5zcGxpdCgnOicpWzBdXG4gIC50b0xvd2VyQ2FzZSgpO1xuXG5jb25zdCBub3JtYWxpemVNb25pdG9yTmFtZSA9ICh2YWx1ZTogdW5rbm93biwgYXBwSWQ6IHN0cmluZyk6IHN0cmluZyA9PlxuICBub3JtYWxpemVUZXh0KHZhbHVlIHx8IGFwcElkLCBNQVhfTU9OSVRPUl9OQU1FX0xFTkdUSCkucmVwbGFjZSgvW15hLXpBLVowLTktX10vZywgJy0nKTtcblxuY29uc3QgZ2V0RG9jdW1lbnRLZXkgPSAoYXBwSWQ6IHN0cmluZyk6IHN0cmluZyA9PlxuICBjcmVhdGVIYXNoKCdzaGEyNTYnKS51cGRhdGUoYXBwSWQpLmRpZ2VzdCgnaGV4Jyk7XG5cbmNvbnN0IHJlcXVpcmVBZG1pbiA9IChjb250ZXh0OiBBcGlDb250ZXh0KTogdm9pZCA9PiB7XG4gIGlmKCFpc0FkbWluVXNlcihjb250ZXh0LnNlc3Npb24gYXMgU2Vzc2lvbikpIHtcbiAgICB0aHJvdyBuZXcgVXNlckVycm9yKEVycm9yVHlwZXMuSU5WQUxJRF9TRVNTSU9OKTtcbiAgfVxufTtcblxuY29uc3QgcmVxdWlyZVNlc3Npb24gPSAoY29udGV4dDogQXBpQ29udGV4dCk6IFNlc3Npb24gPT4ge1xuICBjb25zdCBzZXNzaW9uID0gY29udGV4dC5zZXNzaW9uIGFzIFNlc3Npb247XG5cbiAgaWYoIXNlc3Npb24/LnVzZXJJZCkge1xuICAgIHRocm93IG5ldyBVc2VyRXJyb3IoRXJyb3JUeXBlcy5JTlZBTElEX1NFU1NJT04pO1xuICB9XG5cbiAgcmV0dXJuIHNlc3Npb247XG59O1xuXG5jb25zdCBpbml0aWFsaXplQW5hbHl0aWNzQ29sbGVjdGlvbiA9IGFzeW5jIChkYXRhYmFzZU5hbWU/OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgY29uc3QgY2FjaGVLZXkgPSBkYXRhYmFzZU5hbWUgfHwgJ3JlYWt0b3InO1xuICBjb25zdCBleGlzdGluZ0luaXRpYWxpemF0aW9uID0gY29sbGVjdGlvbkluaXRpYWxpemF0aW9uLmdldChjYWNoZUtleSk7XG5cbiAgaWYoZXhpc3RpbmdJbml0aWFsaXphdGlvbikge1xuICAgIHJldHVybiBleGlzdGluZ0luaXRpYWxpemF0aW9uO1xuICB9XG5cbiAgY29uc3QgaW5pdGlhbGl6YXRpb24gPSAoYXN5bmMgKCkgPT4ge1xuICAgIGNvbnN0IGRhdGFiYXNlID0gdXNlRGIoZGF0YWJhc2VOYW1lKTtcbiAgICBjb25zdCBjb2xsZWN0aW9uID0gZGF0YWJhc2UuY29sbGVjdGlvbihDT0xMRUNUSU9OUy5BTkFMWVRJQ1MpO1xuXG4gICAgaWYoIWF3YWl0IGNvbGxlY3Rpb24uZXhpc3RzKCkpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGRhdGFiYXNlLmNyZWF0ZUNvbGxlY3Rpb24oQ09MTEVDVElPTlMuQU5BTFlUSUNTKTtcbiAgICAgIH0gY2F0Y2goZXJyb3IpIHtcbiAgICAgICAgaWYoIWF3YWl0IGNvbGxlY3Rpb24uZXhpc3RzKCkpIHtcbiAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGF3YWl0IGNvbGxlY3Rpb24uZW5zdXJlSW5kZXgoe2ZpZWxkczogWydhbmFseXRpY3NJZCddLCB0eXBlOiAncGVyc2lzdGVudCcsIHVuaXF1ZTogdHJ1ZX0pO1xuICAgIGF3YWl0IGNvbGxlY3Rpb24uZW5zdXJlSW5kZXgoe2ZpZWxkczogWydhcHBJZCddLCB0eXBlOiAncGVyc2lzdGVudCcsIHVuaXF1ZTogdHJ1ZX0pO1xuICB9KSgpO1xuXG4gIGNvbGxlY3Rpb25Jbml0aWFsaXphdGlvbi5zZXQoY2FjaGVLZXksIGluaXRpYWxpemF0aW9uKTtcblxuICB0cnkge1xuICAgIGF3YWl0IGluaXRpYWxpemF0aW9uO1xuICB9IGNhdGNoKGVycm9yKSB7XG4gICAgY29sbGVjdGlvbkluaXRpYWxpemF0aW9uLmRlbGV0ZShjYWNoZUtleSk7XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbn07XG5cbmNvbnN0IGdldFJ1bUNsaWVudCA9IChjb250ZXh0OiBBcGlDb250ZXh0KTogUlVNQ2xpZW50ID0+IHtcbiAgY29uc3QgYXdzQ29uZmlnOiBBd3NDb25maWcgPSBjb250ZXh0LmNvbmZpZz8uYXdzIHx8IENvbmZpZy5nZXQ8QXdzQ29uZmlnPignYXdzJywge30pO1xuICBjb25zdCB7YWNjZXNzS2V5SWQsIHNlY3JldEFjY2Vzc0tleSwgc2Vzc2lvblRva2VufSA9IGF3c0NvbmZpZy5jcmVkZW50aWFscyB8fCB7fTtcbiAgY29uc3QgdXNlc1J1bnRpbWVDcmVkZW50aWFscyA9IEJvb2xlYW4oXG4gICAgcHJvY2Vzcy5lbnYuQVdTX1NFU1NJT05fVE9LRU4gJiZcbiAgICBhY2Nlc3NLZXlJZCA9PT0gcHJvY2Vzcy5lbnYuQVdTX0FDQ0VTU19LRVlfSUQgJiZcbiAgICBzZWNyZXRBY2Nlc3NLZXkgPT09IHByb2Nlc3MuZW52LkFXU19TRUNSRVRfQUNDRVNTX0tFWVxuICApO1xuICBjb25zdCBjcmVkZW50aWFscyA9IGFjY2Vzc0tleUlkICYmIHNlY3JldEFjY2Vzc0tleSAmJiAhdXNlc1J1bnRpbWVDcmVkZW50aWFscyA/IHtcbiAgICBhY2Nlc3NLZXlJZCxcbiAgICBzZWNyZXRBY2Nlc3NLZXksXG4gICAgLi4uKHNlc3Npb25Ub2tlbiA/IHtzZXNzaW9uVG9rZW59IDoge30pXG4gIH0gOiB1bmRlZmluZWQ7XG5cbiAgcmV0dXJuIG5ldyBSVU1DbGllbnQoe1xuICAgIC4uLihjcmVkZW50aWFscyA/IHtjcmVkZW50aWFsc30gOiB7fSksXG4gICAgLi4uKGF3c0NvbmZpZy5yZWdpb24gPyB7cmVnaW9uOiBhd3NDb25maWcucmVnaW9ufSA6IHt9KVxuICB9KTtcbn07XG5cbmNvbnN0IGZpbmRBd3NNb25pdG9yID0gYXN5bmMgKFxuICBjbGllbnQ6IFJVTUNsaWVudCxcbiAgbW9uaXRvck5hbWU6IHN0cmluZ1xuKTogUHJvbWlzZTx7ZG9tYWluPzogc3RyaW5nOyBpZDogc3RyaW5nOyBuYW1lOiBzdHJpbmd9IHwgbnVsbD4gPT4ge1xuICB0cnkge1xuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgY2xpZW50LnNlbmQobmV3IEdldEFwcE1vbml0b3JDb21tYW5kKHtOYW1lOiBtb25pdG9yTmFtZX0pKTtcbiAgICBjb25zdCBtb25pdG9ySWQgPSBub3JtYWxpemVUZXh0KHJlc3BvbnNlLkFwcE1vbml0b3I/LklkLCAzNik7XG5cbiAgICByZXR1cm4gbW9uaXRvcklkID8ge1xuICAgICAgZG9tYWluOiBub3JtYWxpemVEb21haW4ocmVzcG9uc2UuQXBwTW9uaXRvcj8uRG9tYWluKSxcbiAgICAgIGlkOiBtb25pdG9ySWQsXG4gICAgICBuYW1lOiBub3JtYWxpemVUZXh0KHJlc3BvbnNlLkFwcE1vbml0b3I/Lk5hbWUgfHwgbW9uaXRvck5hbWUsIE1BWF9NT05JVE9SX05BTUVfTEVOR1RIKVxuICAgIH0gOiBudWxsO1xuICB9IGNhdGNoKGVycm9yKSB7XG4gICAgaWYoKGVycm9yIGFzIHtuYW1lPzogc3RyaW5nfSkubmFtZSA9PT0gJ1Jlc291cmNlTm90Rm91bmRFeGNlcHRpb24nKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICB0aHJvdyBlcnJvcjtcbiAgfVxufTtcblxuY29uc3QgZmluZE9yQ3JlYXRlQXdzTW9uaXRvciA9IGFzeW5jIChcbiAgY29udGV4dDogQXBpQ29udGV4dCxcbiAgYXBwSWQ6IHN0cmluZyxcbiAgZG9tYWluOiBzdHJpbmcsXG4gIG1vbml0b3JOYW1lOiBzdHJpbmdcbik6IFByb21pc2U8e2RvbWFpbjogc3RyaW5nOyBpZDogc3RyaW5nOyBuYW1lOiBzdHJpbmd9PiA9PiB7XG4gIGNvbnN0IGNsaWVudCA9IGdldFJ1bUNsaWVudChjb250ZXh0KTtcbiAgY29uc3QgZXhpc3RpbmdNb25pdG9yID0gYXdhaXQgZmluZEF3c01vbml0b3IoY2xpZW50LCBtb25pdG9yTmFtZSk7XG5cbiAgaWYoZXhpc3RpbmdNb25pdG9yKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGRvbWFpbjogZXhpc3RpbmdNb25pdG9yLmRvbWFpbiB8fCBkb21haW4sXG4gICAgICBpZDogZXhpc3RpbmdNb25pdG9yLmlkLFxuICAgICAgbmFtZTogZXhpc3RpbmdNb25pdG9yLm5hbWVcbiAgICB9O1xuICB9XG5cbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBjbGllbnQuc2VuZChuZXcgQ3JlYXRlQXBwTW9uaXRvckNvbW1hbmQoe1xuICAgIEN1c3RvbUV2ZW50czoge1N0YXR1czogJ0VOQUJMRUQnfSxcbiAgICBDd0xvZ0VuYWJsZWQ6IGZhbHNlLFxuICAgIERvbWFpbjogZG9tYWluLFxuICAgIE5hbWU6IG1vbml0b3JOYW1lLFxuICAgIFBsYXRmb3JtOiAnV2ViJyxcbiAgICBUYWdzOiB7YXBwSWQsIG1hbmFnZWRCeTogJ3JlYWt0b3InfVxuICB9KSk7XG4gIGNvbnN0IG1vbml0b3JJZCA9IG5vcm1hbGl6ZVRleHQocmVzcG9uc2UuSWQsIDM2KTtcblxuICBpZighbW9uaXRvcklkKSB7XG4gICAgdGhyb3cgbmV3IFVzZXJFcnJvcignaW52YWxpZF9ydW1fbW9uaXRvcicpO1xuICB9XG5cbiAgcmV0dXJuIHtkb21haW4sIGlkOiBtb25pdG9ySWQsIG5hbWU6IG1vbml0b3JOYW1lfTtcbn07XG5cbmNvbnN0IHBhcnNlTW9uaXRvcklucHV0ID0gKFxuICBpbnB1dDogQ3JlYXRlQW5hbHl0aWNzTW9uaXRvcklucHV0VHlwZVxuKToge2FwcElkOiBzdHJpbmc7IGFwcE5hbWU6IHN0cmluZzsgZG9tYWluOiBzdHJpbmc7IG1vbml0b3JOYW1lOiBzdHJpbmd9ID0+IHtcbiAgY29uc3QgYXBwSWQgPSBub3JtYWxpemVUZXh0KGlucHV0Py5hcHBJZCwgTUFYX0FQUF9JRF9MRU5HVEgpO1xuICBjb25zdCBhcHBOYW1lID0gbm9ybWFsaXplVGV4dChpbnB1dD8uYXBwTmFtZSB8fCBpbnB1dD8ubW9uaXRvck5hbWUgfHwgYXBwSWQsIE1BWF9NT05JVE9SX05BTUVfTEVOR1RIKTtcbiAgY29uc3QgZG9tYWluID0gbm9ybWFsaXplRG9tYWluKGlucHV0Py5kb21haW4pO1xuICBjb25zdCBtb25pdG9yTmFtZSA9IG5vcm1hbGl6ZU1vbml0b3JOYW1lKGlucHV0Py5tb25pdG9yTmFtZSwgYXBwSWQpO1xuXG4gIGlmKCFhcHBJZCB8fCAhZG9tYWluIHx8ICFtb25pdG9yTmFtZSkge1xuICAgIHRocm93IG5ldyBVc2VyRXJyb3IoRXJyb3JUeXBlcy5JTlZBTElEX0FSR1VNRU5UUyk7XG4gIH1cblxuICByZXR1cm4ge2FwcElkLCBhcHBOYW1lLCBkb21haW4sIG1vbml0b3JOYW1lfTtcbn07XG5cbmV4cG9ydCBjb25zdCBnZXRBbmFseXRpY3NCeUFwcElkID0gYXN5bmMgKFxuICBjb250ZXh0OiBBcGlDb250ZXh0LFxuICBhcHBJZElucHV0OiBzdHJpbmdcbik6IFByb21pc2U8QW5hbHl0aWNzVHlwZSB8IG51bGw+ID0+IHtcbiAgcmVxdWlyZUFkbWluKGNvbnRleHQpO1xuICBjb25zdCBhcHBJZCA9IG5vcm1hbGl6ZVRleHQoYXBwSWRJbnB1dCwgTUFYX0FQUF9JRF9MRU5HVEgpO1xuXG4gIGlmKCFhcHBJZCkge1xuICAgIHRocm93IG5ldyBVc2VyRXJyb3IoRXJyb3JUeXBlcy5JTlZBTElEX0FSR1VNRU5UUyk7XG4gIH1cblxuICBhd2FpdCBpbml0aWFsaXplQW5hbHl0aWNzQ29sbGVjdGlvbihjb250ZXh0LmRhdGFiYXNlTmFtZSk7XG4gIHJldHVybiBnZXRJdGVtQnk8QW5hbHl0aWNzVHlwZT4oY29udGV4dCwgQ09MTEVDVElPTlMuQU5BTFlUSUNTLCB7YXBwSWR9KTtcbn07XG5cbmV4cG9ydCBjb25zdCBnZXRBbmFseXRpY3NDb25maWd1cmF0aW9uID0gYXN5bmMgKFxuICBjb250ZXh0OiBBcGlDb250ZXh0LFxuICBhcHBJZElucHV0OiBzdHJpbmdcbik6IFByb21pc2U8QW5hbHl0aWNzQ29uZmlndXJhdGlvblR5cGUgfCBudWxsPiA9PiB7XG4gIGNvbnN0IGFwcElkID0gbm9ybWFsaXplVGV4dChhcHBJZElucHV0LCBNQVhfQVBQX0lEX0xFTkdUSCk7XG5cbiAgaWYoIWFwcElkKSB7XG4gICAgdGhyb3cgbmV3IFVzZXJFcnJvcihFcnJvclR5cGVzLklOVkFMSURfQVJHVU1FTlRTKTtcbiAgfVxuXG4gIGF3YWl0IGluaXRpYWxpemVBbmFseXRpY3NDb2xsZWN0aW9uKGNvbnRleHQuZGF0YWJhc2VOYW1lKTtcbiAgY29uc3QgYW5hbHl0aWNzID0gYXdhaXQgZ2V0SXRlbUJ5PEFuYWx5dGljc1R5cGU+KGNvbnRleHQsIENPTExFQ1RJT05TLkFOQUxZVElDUywge2FwcElkfSk7XG5cbiAgcmV0dXJuIGFuYWx5dGljcyA/IHthbmFseXRpY3NJZDogYW5hbHl0aWNzLmFuYWx5dGljc0lkLCBhcHBJZDogYW5hbHl0aWNzLmFwcElkfSA6IG51bGw7XG59O1xuXG5leHBvcnQgY29uc3QgZ2V0QW5hbHl0aWNzQnlJZCA9IGFzeW5jIChcbiAgY29udGV4dDogQXBpQ29udGV4dCxcbiAgYW5hbHl0aWNzSWRJbnB1dDogc3RyaW5nXG4pOiBQcm9taXNlPEFuYWx5dGljc1R5cGUgfCBudWxsPiA9PiB7XG4gIGNvbnN0IGFuYWx5dGljc0lkID0gbm9ybWFsaXplVGV4dChhbmFseXRpY3NJZElucHV0LCAzNik7XG5cbiAgaWYoIWFuYWx5dGljc0lkKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBhd2FpdCBpbml0aWFsaXplQW5hbHl0aWNzQ29sbGVjdGlvbihjb250ZXh0LmRhdGFiYXNlTmFtZSk7XG4gIHJldHVybiBnZXRJdGVtQnk8QW5hbHl0aWNzVHlwZT4oY29udGV4dCwgQ09MTEVDVElPTlMuQU5BTFlUSUNTLCB7YW5hbHl0aWNzSWR9KTtcbn07XG5cbmV4cG9ydCBjb25zdCBjcmVhdGVBbmFseXRpY3NNb25pdG9yID0gYXN5bmMgKFxuICBjb250ZXh0OiBBcGlDb250ZXh0LFxuICBpbnB1dDogQ3JlYXRlQW5hbHl0aWNzTW9uaXRvcklucHV0VHlwZVxuKTogUHJvbWlzZTxBbmFseXRpY3NUeXBlPiA9PiB7XG4gIGNvbnN0IHNlc3Npb24gPSByZXF1aXJlU2Vzc2lvbihjb250ZXh0KTtcbiAgY29uc3Qge2FwcElkLCBhcHBOYW1lLCBkb21haW4sIG1vbml0b3JOYW1lfSA9IHBhcnNlTW9uaXRvcklucHV0KGlucHV0KTtcbiAgYXdhaXQgaW5pdGlhbGl6ZUFuYWx5dGljc0NvbGxlY3Rpb24oY29udGV4dC5kYXRhYmFzZU5hbWUpO1xuICBjb25zdCBleGlzdGluZyA9IGF3YWl0IGdldEl0ZW1CeTxBbmFseXRpY3NUeXBlPihjb250ZXh0LCBDT0xMRUNUSU9OUy5BTkFMWVRJQ1MsIHthcHBJZH0pO1xuXG4gIGlmKGV4aXN0aW5nKSB7XG4gICAgaWYoaXNBZG1pblVzZXIoc2Vzc2lvbikgfHwgKGV4aXN0aW5nLnVzZXJJZHMgfHwgW10pLmluY2x1ZGVzKHNlc3Npb24udXNlcklkKSkge1xuICAgICAgcmV0dXJuIGV4aXN0aW5nO1xuICAgIH1cblxuICAgIHRocm93IG5ldyBVc2VyRXJyb3IoRXJyb3JUeXBlcy5JTlZBTElEX1NFU1NJT04pO1xuICB9XG5cbiAgY29uc3QgbW9uaXRvciA9IGF3YWl0IGZpbmRPckNyZWF0ZUF3c01vbml0b3IoY29udGV4dCwgYXBwSWQsIGRvbWFpbiwgbW9uaXRvck5hbWUpO1xuICByZXR1cm4gYWRkSXRlbTxBbmFseXRpY3NUeXBlPihjb250ZXh0LCBDT0xMRUNUSU9OUy5BTkFMWVRJQ1MsIHtcbiAgICBfa2V5OiBnZXREb2N1bWVudEtleShhcHBJZCksXG4gICAgYW5hbHl0aWNzSWQ6IHJhbmRvbVVVSUQoKSxcbiAgICBhcHBJZCxcbiAgICBhcHBOYW1lLFxuICAgIGRvbWFpbjogbW9uaXRvci5kb21haW4sXG4gICAgbW9uaXRvcklkOiBtb25pdG9yLmlkLFxuICAgIG1vbml0b3JOYW1lOiBtb25pdG9yLm5hbWUsXG4gICAgdXNlcklkczogW3Nlc3Npb24udXNlcklkXVxuICB9KTtcbn07XG5cbmV4cG9ydCBjb25zdCByZWdlbmVyYXRlQW5hbHl0aWNzSWQgPSBhc3luYyAoXG4gIGNvbnRleHQ6IEFwaUNvbnRleHQsXG4gIGFuYWx5dGljc0lkSW5wdXQ6IHN0cmluZ1xuKTogUHJvbWlzZTxBbmFseXRpY3NUeXBlPiA9PiB7XG4gIGNvbnN0IHNlc3Npb24gPSByZXF1aXJlU2Vzc2lvbihjb250ZXh0KTtcbiAgY29uc3QgYW5hbHl0aWNzSWQgPSBub3JtYWxpemVUZXh0KGFuYWx5dGljc0lkSW5wdXQsIDM2KTtcblxuICBpZighYW5hbHl0aWNzSWQpIHtcbiAgICB0aHJvdyBuZXcgVXNlckVycm9yKEVycm9yVHlwZXMuSU5WQUxJRF9BUkdVTUVOVFMpO1xuICB9XG5cbiAgYXdhaXQgaW5pdGlhbGl6ZUFuYWx5dGljc0NvbGxlY3Rpb24oY29udGV4dC5kYXRhYmFzZU5hbWUpO1xuICBjb25zdCBleGlzdGluZyA9IGF3YWl0IGdldEl0ZW1CeTxBbmFseXRpY3NUeXBlPihjb250ZXh0LCBDT0xMRUNUSU9OUy5BTkFMWVRJQ1MsIHthbmFseXRpY3NJZH0pO1xuICBjb25zdCBoYXNBY2Nlc3MgPSBleGlzdGluZyAmJiAoXG4gICAgaXNBZG1pblVzZXIoc2Vzc2lvbikgfHwgKGV4aXN0aW5nLnVzZXJJZHMgfHwgW10pLmluY2x1ZGVzKHNlc3Npb24udXNlcklkKVxuICApO1xuXG4gIGlmKCFleGlzdGluZz8uX2tleSB8fCAhaGFzQWNjZXNzKSB7XG4gICAgdGhyb3cgbmV3IFVzZXJFcnJvcignaW52YWxpZF9ydW1fYXBwJyk7XG4gIH1cblxuICByZXR1cm4gdXBkYXRlSXRlbTxBbmFseXRpY3NUeXBlPihcbiAgICBjb250ZXh0LFxuICAgIENPTExFQ1RJT05TLkFOQUxZVElDUyxcbiAgICBleGlzdGluZy5fa2V5LFxuICAgIHthbmFseXRpY3NJZDogcmFuZG9tVVVJRCgpfVxuICApO1xufTtcblxudHlwZSBBd3NSdW1SZWNvcmQgPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiAmIHtcbiAgZXZlbnRfZGV0YWlscz86IHVua25vd247XG4gIG1ldGFkYXRhPzogdW5rbm93bjtcbn07XG5cbnR5cGUgTm9ybWFsaXplZFJ1bUV2ZW50ID0gQW5hbHl0aWNzRXZlbnRUeXBlICYge3JlYWRvbmx5IGFwcElkOiBzdHJpbmd9O1xuXG5jb25zdCBwYXJzZUpzb25SZWNvcmQgPSAodmFsdWU6IHVua25vd24pOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9PiB7XG4gIGlmKHZhbHVlICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgcmV0dXJuIHZhbHVlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICB9XG5cbiAgaWYodHlwZW9mIHZhbHVlICE9PSAnc3RyaW5nJykge1xuICAgIHJldHVybiB7fTtcbiAgfVxuXG4gIHRyeSB7XG4gICAgY29uc3QgcGFyc2VkID0gSlNPTi5wYXJzZSh2YWx1ZSk7XG4gICAgcmV0dXJuIHBhcnNlZCAmJiB0eXBlb2YgcGFyc2VkID09PSAnb2JqZWN0JyAmJiAhQXJyYXkuaXNBcnJheShwYXJzZWQpXG4gICAgICA/IHBhcnNlZCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPlxuICAgICAgOiB7fTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIHt9O1xuICB9XG59O1xuXG5jb25zdCBnZXRQcm9wZXJ0eSA9IChcbiAgcHJvcGVydGllczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4gIG1ldGFkYXRhOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPixcbiAgYWxpYXNlczogcmVhZG9ubHkgc3RyaW5nW11cbik6IHVua25vd24gPT4ge1xuICBjb25zdCBub3JtYWxpemVkQWxpYXNlcyA9IG5ldyBTZXQoYWxpYXNlcy5tYXAoKGFsaWFzKSA9PiBhbGlhcy50b0xvd2VyQ2FzZSgpLnJlcGxhY2UoL1teYS16MC05XS9nLCAnJykpKTtcbiAgY29uc3QgZW50cmllcyA9IFsuLi5PYmplY3QuZW50cmllcyhwcm9wZXJ0aWVzKSwgLi4uT2JqZWN0LmVudHJpZXMobWV0YWRhdGEpXTtcbiAgcmV0dXJuIGVudHJpZXMuZmluZCgoW2tleV0pID0+IG5vcm1hbGl6ZWRBbGlhc2VzLmhhcyhrZXkudG9Mb3dlckNhc2UoKS5yZXBsYWNlKC9bXmEtejAtOV0vZywgJycpKSk/LlsxXTtcbn07XG5cbmNvbnN0IG5vcm1hbGl6ZVJ1bUV2ZW50ID0gKHJhd0V2ZW50OiBzdHJpbmcpOiBOb3JtYWxpemVkUnVtRXZlbnQgfCBudWxsID0+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBzb3VyY2UgPSBKU09OLnBhcnNlKHJhd0V2ZW50KSBhcyBBd3NSdW1SZWNvcmQ7XG4gICAgY29uc3QgZGV0YWlscyA9IHBhcnNlSnNvblJlY29yZChzb3VyY2UuZXZlbnRfZGV0YWlscyk7XG4gICAgY29uc3QgbWV0YWRhdGEgPSBwYXJzZUpzb25SZWNvcmQoc291cmNlLm1ldGFkYXRhKTtcbiAgICBjb25zdCBwcm9wZXJ0aWVzID0gcGFyc2VKc29uUmVjb3JkKGRldGFpbHMucHJvcGVydGllcyk7XG4gICAgY29uc3QgdGltZXN0YW1wID0gTnVtYmVyKHNvdXJjZS5ldmVudF90aW1lc3RhbXAgfHwgMCk7XG5cbiAgICBpZighTnVtYmVyLmlzRmluaXRlKHRpbWVzdGFtcCkgfHwgdGltZXN0YW1wIDw9IDApIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IGdldFRleHQgPSAoYWxpYXNlczogcmVhZG9ubHkgc3RyaW5nW10sIGZhbGxiYWNrID0gJycpOiBzdHJpbmcgPT5cbiAgICAgIG5vcm1hbGl6ZVRleHQoZ2V0UHJvcGVydHkocHJvcGVydGllcywgbWV0YWRhdGEsIGFsaWFzZXMpID8/IGZhbGxiYWNrLCAyNTYpO1xuICAgIGNvbnN0IGR1cmF0aW9uVmFsdWUgPSBOdW1iZXIoZ2V0UHJvcGVydHkocHJvcGVydGllcywgbWV0YWRhdGEsIFtcbiAgICAgICdkdXJhdGlvbicsXG4gICAgICAnZHVyYXRpb25NcycsXG4gICAgICAnbGF0ZW5jeScsXG4gICAgICAnbG9hZFRpbWUnLFxuICAgICAgJ3BhZ2VMb2FkVGltZSdcbiAgICBdKSB8fCAwKTtcbiAgICBjb25zdCBwYXRoID0gbm9ybWFsaXplVGV4dChkZXRhaWxzLnBhdGgsIDI1Nik7XG4gICAgY29uc3QgdHlwZSA9IG5vcm1hbGl6ZVRleHQoc291cmNlLmV2ZW50X3R5cGUsIDI1Nik7XG5cbiAgICByZXR1cm4ge1xuICAgICAgYXBwSWQ6IG5vcm1hbGl6ZVRleHQoZGV0YWlscy5hcHBJZCB8fCBtZXRhZGF0YS5hcHBsaWNhdGlvbklkLCBNQVhfQVBQX0lEX0xFTkdUSCksXG4gICAgICBicm93c2VyTGFuZ3VhZ2U6IGdldFRleHQoWydicm93c2VyTGFuZ3VhZ2UnXSwgJ1Vua25vd24nKSxcbiAgICAgIGJyb3dzZXJOYW1lOiBnZXRUZXh0KFsnYnJvd3Nlck5hbWUnXSwgJ1Vua25vd24nKSxcbiAgICAgIGJyb3dzZXJWZXJzaW9uOiBnZXRUZXh0KFsnYnJvd3NlclZlcnNpb24nXSksXG4gICAgICBkZXZpY2VUeXBlOiBnZXRUZXh0KFsnZGV2aWNlVHlwZSddLCAnVW5rbm93bicpLFxuICAgICAgZHVyYXRpb246IE51bWJlci5pc0Zpbml0ZShkdXJhdGlvblZhbHVlKSAmJiBkdXJhdGlvblZhbHVlID4gMCA/IGR1cmF0aW9uVmFsdWUgOiAwLFxuICAgICAgZXZlbnRJZDogbm9ybWFsaXplVGV4dChzb3VyY2UuZXZlbnRfaWQsIDEyOCksXG4gICAgICBpbnRlcmFjdGlvbjogZ2V0VGV4dChbJ2ludGVyYWN0aW9uJ10sIG5vcm1hbGl6ZVRleHQoZGV0YWlscy5uYW1lIHx8IHR5cGUsIDEyOCkpLFxuICAgICAgbmFtZTogbm9ybWFsaXplVGV4dChkZXRhaWxzLm5hbWUgfHwgdHlwZSwgMTI4KSxcbiAgICAgIG9zTmFtZTogZ2V0VGV4dChbJ29zTmFtZSddLCAnVW5rbm93bicpLFxuICAgICAgb3NWZXJzaW9uOiBnZXRUZXh0KFsnb3NWZXJzaW9uJ10pLFxuICAgICAgb3V0Y29tZTogZ2V0VGV4dChbJ291dGNvbWUnXSksXG4gICAgICBwYWdlSWQ6IGdldFRleHQoWydwYWdlSWQnLCAncGFnZUlEJ10sIHBhdGggfHwgJ1Vua25vd24nKSxcbiAgICAgIHBhcmVudFBhZ2U6IGdldFRleHQoWydwYXJlbnRQYWdlSWQnLCAncGFyZW50UGFnZSddKSxcbiAgICAgIHBhdGgsXG4gICAgICBzZXNzaW9uSWQ6IG5vcm1hbGl6ZVRleHQoc291cmNlLnNlc3Npb25JZCB8fCBwYXJzZUpzb25SZWNvcmQoc291cmNlLnVzZXJfZGV0YWlscykuc2Vzc2lvbklkLCAxMjgpLFxuICAgICAgdGltZXN0YW1wLFxuICAgICAgdHlwZSxcbiAgICAgIHZpZXdJZDogZ2V0VGV4dChbJ3ZpZXdJZCddKVxuICAgIH07XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBudWxsO1xuICB9XG59O1xuXG5jb25zdCBjcmVhdGVCcmVha2Rvd24gPSAodmFsdWVzOiByZWFkb25seSBzdHJpbmdbXSwgdG90YWw6IG51bWJlcikgPT4ge1xuICBjb25zdCBjb3VudHMgPSB2YWx1ZXMucmVkdWNlPE1hcDxzdHJpbmcsIG51bWJlcj4+KChyZXN1bHQsIHZhbHVlKSA9PiB7XG4gICAgY29uc3QgbGFiZWwgPSB2YWx1ZSB8fCAnVW5rbm93bic7XG4gICAgcmVzdWx0LnNldChsYWJlbCwgKHJlc3VsdC5nZXQobGFiZWwpIHx8IDApICsgMSk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfSwgbmV3IE1hcCgpKTtcblxuICByZXR1cm4gQXJyYXkuZnJvbShjb3VudHMsIChbbGFiZWwsIGNvdW50XSkgPT4gKHtcbiAgICBjb3VudCxcbiAgICBsYWJlbCxcbiAgICBwZXJjZW50YWdlOiB0b3RhbCA+IDAgPyBNYXRoLnJvdW5kKChjb3VudCAvIHRvdGFsKSAqIDEwMDApIC8gMTAgOiAwXG4gIH0pKS5zb3J0KChsZWZ0LCByaWdodCkgPT4gcmlnaHQuY291bnQgLSBsZWZ0LmNvdW50IHx8IGxlZnQubGFiZWwubG9jYWxlQ29tcGFyZShyaWdodC5sYWJlbCkpO1xufTtcblxuY29uc3QgcGVyY2VudGlsZSA9ICh2YWx1ZXM6IHJlYWRvbmx5IG51bWJlcltdLCBwZXJjZW50aWxlVmFsdWU6IG51bWJlcik6IG51bWJlciA9PiB7XG4gIGlmKHZhbHVlcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gMDtcbiAgfVxuXG4gIGNvbnN0IHNvcnRlZCA9IHZhbHVlcy50b1NvcnRlZCgobGVmdCwgcmlnaHQpID0+IGxlZnQgLSByaWdodCk7XG4gIGNvbnN0IGluZGV4ID0gTWF0aC5tYXgoMCwgTWF0aC5jZWlsKHBlcmNlbnRpbGVWYWx1ZSAqIHNvcnRlZC5sZW5ndGgpIC0gMSk7XG4gIHJldHVybiBzb3J0ZWRbaW5kZXhdO1xufTtcblxuY29uc3QgY3JlYXRlVmlld1BlcmZvcm1hbmNlID0gKGV2ZW50czogcmVhZG9ubHkgTm9ybWFsaXplZFJ1bUV2ZW50W10pID0+IHtcbiAgY29uc3Qgdmlld3MgPSBldmVudHMucmVkdWNlPE1hcDxzdHJpbmcsIE5vcm1hbGl6ZWRSdW1FdmVudFtdPj4oKHJlc3VsdCwgZXZlbnQpID0+IHtcbiAgICBpZihcbiAgICAgIChldmVudC50eXBlICE9PSAndmlld19wZXJmb3JtYW5jZScgJiYgZXZlbnQubmFtZSAhPT0gJ3ZpZXdfcGVyZm9ybWFuY2UnKVxuICAgICAgfHwgIWV2ZW50LnZpZXdJZFxuICAgICAgfHwgZXZlbnQub3V0Y29tZSA9PT0gJ2NhbmNlbGxlZCdcbiAgICApIHtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuXG4gICAgY29uc3Qgc2FtcGxlcyA9IHJlc3VsdC5nZXQoZXZlbnQudmlld0lkKSB8fCBbXTtcbiAgICBzYW1wbGVzLnB1c2goZXZlbnQpO1xuICAgIHJlc3VsdC5zZXQoZXZlbnQudmlld0lkLCBzYW1wbGVzKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9LCBuZXcgTWFwKCkpO1xuXG4gIHJldHVybiBBcnJheS5mcm9tKHZpZXdzLCAoW3ZpZXdJZCwgc2FtcGxlc10pID0+IHtcbiAgICBjb25zdCBkdXJhdGlvbnMgPSBzYW1wbGVzLm1hcCgoZXZlbnQpID0+IGV2ZW50LmR1cmF0aW9uKTtcbiAgICBjb25zdCBwZXJjZW50YWdlID0gKG91dGNvbWU6IHN0cmluZyk6IG51bWJlciA9PiBNYXRoLnJvdW5kKFxuICAgICAgKHNhbXBsZXMuZmlsdGVyKChldmVudCkgPT4gZXZlbnQub3V0Y29tZSA9PT0gb3V0Y29tZSkubGVuZ3RoIC8gc2FtcGxlcy5sZW5ndGgpICogMTAwMFxuICAgICkgLyAxMDtcblxuICAgIHJldHVybiB7XG4gICAgICBmYWlsdXJlUmF0ZTogcGVyY2VudGFnZSgnZmFpbHVyZScpLFxuICAgICAgcDUwOiBwZXJjZW50aWxlKGR1cmF0aW9ucywgMC41KSxcbiAgICAgIHA3NTogcGVyY2VudGlsZShkdXJhdGlvbnMsIDAuNzUpLFxuICAgICAgcDk1OiBwZXJjZW50aWxlKGR1cmF0aW9ucywgMC45NSksXG4gICAgICBzYW1wbGVDb3VudDogc2FtcGxlcy5sZW5ndGgsXG4gICAgICB0aW1lb3V0UmF0ZTogcGVyY2VudGFnZSgndGltZW91dCcpLFxuICAgICAgdmlld0lkXG4gICAgfTtcbiAgfSkuc29ydCgobGVmdCwgcmlnaHQpID0+IHJpZ2h0LnNhbXBsZUNvdW50IC0gbGVmdC5zYW1wbGVDb3VudCB8fCBsZWZ0LnZpZXdJZC5sb2NhbGVDb21wYXJlKHJpZ2h0LnZpZXdJZCkpO1xufTtcblxuY29uc3QgZ2V0QXV0aG9yaXplZEFuYWx5dGljcyA9IGFzeW5jIChcbiAgY29udGV4dDogQXBpQ29udGV4dCxcbiAgYW5hbHl0aWNzSWQ6IHN0cmluZ1xuKTogUHJvbWlzZTxBbmFseXRpY3NUeXBlPiA9PiB7XG4gIGNvbnN0IHNlc3Npb24gPSByZXF1aXJlU2Vzc2lvbihjb250ZXh0KTtcbiAgY29uc3QgYW5hbHl0aWNzID0gYXdhaXQgZ2V0QW5hbHl0aWNzQnlJZChjb250ZXh0LCBhbmFseXRpY3NJZCk7XG4gIGNvbnN0IGhhc0FjY2VzcyA9IGFuYWx5dGljcyAmJiAoXG4gICAgaXNBZG1pblVzZXIoc2Vzc2lvbikgfHwgKGFuYWx5dGljcy51c2VySWRzIHx8IFtdKS5pbmNsdWRlcyhzZXNzaW9uLnVzZXJJZClcbiAgKTtcblxuICBpZighYW5hbHl0aWNzIHx8ICFoYXNBY2Nlc3MpIHtcbiAgICB0aHJvdyBuZXcgVXNlckVycm9yKEVycm9yVHlwZXMuSU5WQUxJRF9TRVNTSU9OKTtcbiAgfVxuXG4gIHJldHVybiBhbmFseXRpY3M7XG59O1xuXG5jb25zdCBnZXRBbmFseXRpY3NBcHBOYW1lID0gYXN5bmMgKGNvbnRleHQ6IEFwaUNvbnRleHQsIGFuYWx5dGljczogQW5hbHl0aWNzVHlwZSk6IFByb21pc2U8c3RyaW5nPiA9PiB7XG4gIGlmKGFuYWx5dGljcy5hcHBOYW1lKSB7XG4gICAgcmV0dXJuIGFuYWx5dGljcy5hcHBOYW1lO1xuICB9XG5cbiAgY29uc3QgYXBwID0gYXdhaXQgZ2V0SXRlbUJ5PEFwcFR5cGU+KGNvbnRleHQsIENPTExFQ1RJT05TLkFQUFMsIHtcbiAgICBjb25kaXRpb25zOiBbXG4gICAgICB7ZmllbGQ6ICdhcHBJZCcsIG9wZXJhdG9yOiAnPT0nLCB2YWx1ZTogYW5hbHl0aWNzLmFwcElkfSxcbiAgICAgIHtmaWVsZDogJ19rZXknLCBvcGVyYXRvcjogJz09JywgdmFsdWU6IGFuYWx5dGljcy5hcHBJZH1cbiAgICBdLFxuICAgIGxvZ2ljOiAnT1InXG4gIH0pO1xuXG4gIHJldHVybiBub3JtYWxpemVUZXh0KGFwcD8uZGlzcGxheU5hbWUgfHwgYXBwPy5uYW1lIHx8IGFuYWx5dGljcy5tb25pdG9yTmFtZSB8fCBhbmFseXRpY3MuYXBwSWQsIE1BWF9NT05JVE9SX05BTUVfTEVOR1RIKTtcbn07XG5cbmV4cG9ydCBjb25zdCBnZXRBdmFpbGFibGVBbmFseXRpY3MgPSBhc3luYyAoY29udGV4dDogQXBpQ29udGV4dCk6IFByb21pc2U8QW5hbHl0aWNzVHlwZVtdPiA9PiB7XG4gIGNvbnN0IHNlc3Npb24gPSByZXF1aXJlU2Vzc2lvbihjb250ZXh0KTtcbiAgYXdhaXQgaW5pdGlhbGl6ZUFuYWx5dGljc0NvbGxlY3Rpb24oY29udGV4dC5kYXRhYmFzZU5hbWUpO1xuICBjb25zdCBhbmFseXRpY3MgPSBhd2FpdCBnZXRMaXN0Qnk8QW5hbHl0aWNzVHlwZT4oY29udGV4dCwgQ09MTEVDVElPTlMuQU5BTFlUSUNTLCB7fSwge1xuICAgIHNvcnQ6IFt7ZGlyZWN0aW9uOiAnQVNDJywgZmllbGQ6ICdhcHBOYW1lJ31dXG4gIH0pO1xuXG4gIGNvbnN0IGF2YWlsYWJsZSA9IGFuYWx5dGljcy5maWx0ZXIoKGl0ZW0pID0+XG4gICAgaXNBZG1pblVzZXIoc2Vzc2lvbikgfHwgKGl0ZW0udXNlcklkcyB8fCBbXSkuaW5jbHVkZXMoc2Vzc2lvbi51c2VySWQpKTtcblxuICBjb25zdCBlbnJpY2hlZCA9IGF3YWl0IFByb21pc2UuYWxsKGF2YWlsYWJsZS5tYXAoYXN5bmMgKGl0ZW0pID0+ICh7XG4gICAgLi4uaXRlbSxcbiAgICBhcHBOYW1lOiBhd2FpdCBnZXRBbmFseXRpY3NBcHBOYW1lKGNvbnRleHQsIGl0ZW0pLFxuICAgIHVzZXJJZHM6IGl0ZW0udXNlcklkcyB8fCBbXVxuICB9KSkpO1xuXG4gIHJldHVybiBlbnJpY2hlZC5zb3J0KChsZWZ0LCByaWdodCkgPT5cbiAgICAobGVmdC5hcHBOYW1lIHx8ICcnKS5sb2NhbGVDb21wYXJlKHJpZ2h0LmFwcE5hbWUgfHwgJycpKTtcbn07XG5cbmV4cG9ydCBjb25zdCBzZXRBbmFseXRpY3NBY2Nlc3MgPSBhc3luYyAoXG4gIGNvbnRleHQ6IEFwaUNvbnRleHQsXG4gIGFuYWx5dGljc0lkSW5wdXQ6IHN0cmluZyxcbiAgdXNlcklkc0lucHV0OiByZWFkb25seSBzdHJpbmdbXVxuKTogUHJvbWlzZTxBbmFseXRpY3NUeXBlPiA9PiB7XG4gIHJlcXVpcmVBZG1pbihjb250ZXh0KTtcbiAgY29uc3QgYW5hbHl0aWNzSWQgPSBub3JtYWxpemVUZXh0KGFuYWx5dGljc0lkSW5wdXQsIDM2KTtcbiAgY29uc3QgYW5hbHl0aWNzID0gYXdhaXQgZ2V0QW5hbHl0aWNzQnlJZChjb250ZXh0LCBhbmFseXRpY3NJZCk7XG5cbiAgaWYoIWFuYWx5dGljcz8uX2tleSkge1xuICAgIHRocm93IG5ldyBVc2VyRXJyb3IoJ2ludmFsaWRfcnVtX2FwcCcpO1xuICB9XG5cbiAgY29uc3QgdXNlcklkcyA9IEFycmF5LmZyb20obmV3IFNldCgodXNlcklkc0lucHV0IHx8IFtdKVxuICAgIC5tYXAoKHVzZXJJZCkgPT4gbm9ybWFsaXplVGV4dCh1c2VySWQsIDEyOCkpXG4gICAgLmZpbHRlcihCb29sZWFuKSkpXG4gICAgLnNsaWNlKDAsIDEwMCk7XG5cbiAgcmV0dXJuIHVwZGF0ZUl0ZW08QW5hbHl0aWNzVHlwZT4oY29udGV4dCwgQ09MTEVDVElPTlMuQU5BTFlUSUNTLCBhbmFseXRpY3MuX2tleSwge3VzZXJJZHN9KTtcbn07XG5cbmV4cG9ydCBjb25zdCBnZXRBbmFseXRpY3NEYXNoYm9hcmQgPSBhc3luYyAoXG4gIGNvbnRleHQ6IEFwaUNvbnRleHQsXG4gIGFuYWx5dGljc0lkSW5wdXQ6IHN0cmluZyxcbiAgcmFuZ2VEYXlzSW5wdXQ6IG51bWJlclxuKTogUHJvbWlzZTxBbmFseXRpY3NEYXNoYm9hcmRUeXBlPiA9PiB7XG4gIGNvbnN0IGFuYWx5dGljc0lkID0gbm9ybWFsaXplVGV4dChhbmFseXRpY3NJZElucHV0LCAzNik7XG4gIGNvbnN0IHJhbmdlRGF5cyA9IE1hdGguZmxvb3IoTnVtYmVyKHJhbmdlRGF5c0lucHV0KSk7XG5cbiAgaWYoIWFuYWx5dGljc0lkIHx8ICFWQUxJRF9SQU5HRV9EQVlTLmhhcyhyYW5nZURheXMpKSB7XG4gICAgdGhyb3cgbmV3IFVzZXJFcnJvcihFcnJvclR5cGVzLklOVkFMSURfQVJHVU1FTlRTKTtcbiAgfVxuXG4gIGNvbnN0IGFuYWx5dGljcyA9IGF3YWl0IGdldEF1dGhvcml6ZWRBbmFseXRpY3MoY29udGV4dCwgYW5hbHl0aWNzSWQpO1xuICBjb25zdCBlbmRUaW1lID0gRGF0ZS5ub3coKTtcbiAgY29uc3Qgc3RhcnRUaW1lID0gZW5kVGltZSAtIHJhbmdlRGF5cyAqIDI0ICogNjAgKiA2MCAqIDEwMDAgKyAocmFuZ2VEYXlzID09PSAzMCA/IDYwXzAwMCA6IDApO1xuICBjb25zdCBjbGllbnQgPSBnZXRSdW1DbGllbnQoY29udGV4dCk7XG4gIGNvbnN0IHJhd0V2ZW50czogc3RyaW5nW10gPSBbXTtcbiAgbGV0IG5leHRUb2tlbjogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICBsZXQgcGFnZSA9IDA7XG5cbiAgZG8ge1xuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgY2xpZW50LnNlbmQobmV3IEdldEFwcE1vbml0b3JEYXRhQ29tbWFuZCh7XG4gICAgICBNYXhSZXN1bHRzOiBNQVhfUlVNX1JFU1VMVFMsXG4gICAgICBOYW1lOiBhbmFseXRpY3MubW9uaXRvck5hbWUsXG4gICAgICAuLi4obmV4dFRva2VuID8ge05leHRUb2tlbjogbmV4dFRva2VufSA6IHt9KSxcbiAgICAgIFRpbWVSYW5nZToge0FmdGVyOiBzdGFydFRpbWUsIEJlZm9yZTogZW5kVGltZX1cbiAgICB9KSk7XG4gICAgcmF3RXZlbnRzLnB1c2goLi4uKHJlc3BvbnNlLkV2ZW50cyB8fCBbXSkpO1xuICAgIG5leHRUb2tlbiA9IHJlc3BvbnNlLk5leHRUb2tlbjtcbiAgICBwYWdlICs9IDE7XG4gIH0gd2hpbGUobmV4dFRva2VuICYmIHBhZ2UgPCBNQVhfUlVNX1BBR0VTKTtcblxuICBjb25zdCBldmVudHMgPSByYXdFdmVudHNcbiAgICAubWFwKG5vcm1hbGl6ZVJ1bUV2ZW50KVxuICAgIC5maWx0ZXIoKGV2ZW50KTogZXZlbnQgaXMgTm9ybWFsaXplZFJ1bUV2ZW50ID0+IEJvb2xlYW4oZXZlbnQgJiYgZXZlbnQuYXBwSWQgPT09IGFuYWx5dGljcy5hcHBJZCkpXG4gICAgLnNvcnQoKGxlZnQsIHJpZ2h0KSA9PiByaWdodC50aW1lc3RhbXAgLSBsZWZ0LnRpbWVzdGFtcCk7XG4gIGNvbnN0IHNlc3Npb25zID0gbmV3IFNldChldmVudHMubWFwKChldmVudCkgPT4gZXZlbnQuc2Vzc2lvbklkKS5maWx0ZXIoQm9vbGVhbikpO1xuICBjb25zdCBpbnRlcmFjdGlvbkV2ZW50cyA9IGV2ZW50cy5maWx0ZXIoKGV2ZW50KSA9PiAoXG4gICAgZXZlbnQudHlwZSAhPT0gJ3BhZ2VfdmlldycgJiYgZXZlbnQudHlwZSAhPT0gJ3ZpZXdfcGVyZm9ybWFuY2UnXG4gICkpO1xuICBjb25zdCBlcnJvckV2ZW50cyA9IGV2ZW50cy5maWx0ZXIoKGV2ZW50KSA9PiAvZXJyb3J8ZXhjZXB0aW9ufGZhaWx1cmUvaS50ZXN0KGAke2V2ZW50LnR5cGV9ICR7ZXZlbnQubmFtZX1gKSk7XG4gIGNvbnN0IGR1cmF0aW9ucyA9IGV2ZW50cy5tYXAoKGV2ZW50KSA9PiBldmVudC5kdXJhdGlvbikuZmlsdGVyKChkdXJhdGlvbikgPT4gZHVyYXRpb24gPiAwKTtcbiAgY29uc3QgYnVja2V0U2l6ZSA9IHJhbmdlRGF5cyA9PT0gMSA/IDYwICogNjAgKiAxMDAwIDogMjQgKiA2MCAqIDYwICogMTAwMDtcbiAgY29uc3QgYnVja2V0cyA9IG5ldyBNYXA8bnVtYmVyLCB7ZXJyb3JzOiBudW1iZXI7IGV2ZW50czogbnVtYmVyOyBpbnRlcmFjdGlvbnM6IG51bWJlcjsgc2Vzc2lvbnM6IFNldDxzdHJpbmc+fT4oKTtcblxuICBmb3IobGV0IHRpbWVzdGFtcCA9IE1hdGguZmxvb3Ioc3RhcnRUaW1lIC8gYnVja2V0U2l6ZSkgKiBidWNrZXRTaXplOyB0aW1lc3RhbXAgPD0gZW5kVGltZTsgdGltZXN0YW1wICs9IGJ1Y2tldFNpemUpIHtcbiAgICBidWNrZXRzLnNldCh0aW1lc3RhbXAsIHtlcnJvcnM6IDAsIGV2ZW50czogMCwgaW50ZXJhY3Rpb25zOiAwLCBzZXNzaW9uczogbmV3IFNldCgpfSk7XG4gIH1cblxuICBldmVudHMuZm9yRWFjaCgoZXZlbnQpID0+IHtcbiAgICBjb25zdCBidWNrZXQgPSBidWNrZXRzLmdldChNYXRoLmZsb29yKGV2ZW50LnRpbWVzdGFtcCAvIGJ1Y2tldFNpemUpICogYnVja2V0U2l6ZSk7XG4gICAgaWYoIWJ1Y2tldCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBidWNrZXQuZXZlbnRzICs9IDE7XG4gICAgYnVja2V0LmludGVyYWN0aW9ucyArPSBldmVudC50eXBlID09PSAncGFnZV92aWV3JyB8fCBldmVudC50eXBlID09PSAndmlld19wZXJmb3JtYW5jZScgPyAwIDogMTtcbiAgICBidWNrZXQuZXJyb3JzICs9IC9lcnJvcnxleGNlcHRpb258ZmFpbHVyZS9pLnRlc3QoYCR7ZXZlbnQudHlwZX0gJHtldmVudC5uYW1lfWApID8gMSA6IDA7XG4gICAgaWYoZXZlbnQuc2Vzc2lvbklkKSB7XG4gICAgICBidWNrZXQuc2Vzc2lvbnMuYWRkKGV2ZW50LnNlc3Npb25JZCk7XG4gICAgfVxuICB9KTtcblxuICByZXR1cm4ge1xuICAgIGFuYWx5dGljc0lkOiBhbmFseXRpY3MuYW5hbHl0aWNzSWQsXG4gICAgYXBwSWQ6IGFuYWx5dGljcy5hcHBJZCxcbiAgICBhcHBOYW1lOiBhd2FpdCBnZXRBbmFseXRpY3NBcHBOYW1lKGNvbnRleHQsIGFuYWx5dGljcyksXG4gICAgYXZlcmFnZUR1cmF0aW9uOiBkdXJhdGlvbnMubGVuZ3RoXG4gICAgICA/IE1hdGgucm91bmQoZHVyYXRpb25zLnJlZHVjZSgodG90YWwsIGR1cmF0aW9uKSA9PiB0b3RhbCArIGR1cmF0aW9uLCAwKSAvIGR1cmF0aW9ucy5sZW5ndGgpXG4gICAgICA6IDAsXG4gICAgYnJvd3NlcnM6IGNyZWF0ZUJyZWFrZG93bihldmVudHMubWFwKChldmVudCkgPT4gW2V2ZW50LmJyb3dzZXJOYW1lLCBldmVudC5icm93c2VyVmVyc2lvbl0uZmlsdGVyKEJvb2xlYW4pLmpvaW4oJyAnKSksIGV2ZW50cy5sZW5ndGgpLFxuICAgIGRldmljZXM6IGNyZWF0ZUJyZWFrZG93bihldmVudHMubWFwKChldmVudCkgPT4gZXZlbnQuZGV2aWNlVHlwZSksIGV2ZW50cy5sZW5ndGgpLFxuICAgIGVuZFRpbWUsXG4gICAgZXJyb3JDb3VudDogZXJyb3JFdmVudHMubGVuZ3RoLFxuICAgIGV2ZW50Q291bnQ6IGV2ZW50cy5sZW5ndGgsXG4gICAgZ2VuZXJhdGVkQXQ6IERhdGUubm93KCksXG4gICAgaW50ZXJhY3Rpb25zOiBjcmVhdGVCcmVha2Rvd24oaW50ZXJhY3Rpb25FdmVudHMubWFwKChldmVudCkgPT4gZXZlbnQuaW50ZXJhY3Rpb24gfHwgZXZlbnQubmFtZSksIGludGVyYWN0aW9uRXZlbnRzLmxlbmd0aCksXG4gICAgbGFuZ3VhZ2VzOiBjcmVhdGVCcmVha2Rvd24oZXZlbnRzLm1hcCgoZXZlbnQpID0+IGV2ZW50LmJyb3dzZXJMYW5ndWFnZSksIGV2ZW50cy5sZW5ndGgpLFxuICAgIG1vbml0b3JJZDogYW5hbHl0aWNzLm1vbml0b3JJZCxcbiAgICBtb25pdG9yTmFtZTogYW5hbHl0aWNzLm1vbml0b3JOYW1lLFxuICAgIG9wZXJhdGluZ1N5c3RlbXM6IGNyZWF0ZUJyZWFrZG93bihldmVudHMubWFwKChldmVudCkgPT4gW2V2ZW50Lm9zTmFtZSwgZXZlbnQub3NWZXJzaW9uXS5maWx0ZXIoQm9vbGVhbikuam9pbignICcpKSwgZXZlbnRzLmxlbmd0aCksXG4gICAgcGFnZXM6IGNyZWF0ZUJyZWFrZG93bihldmVudHMubWFwKChldmVudCkgPT4gZXZlbnQucGFnZUlkIHx8IGV2ZW50LnBhdGgpLCBldmVudHMubGVuZ3RoKSxcbiAgICBwYWdlVmlld0NvdW50OiBldmVudHMuZmlsdGVyKChldmVudCkgPT4gZXZlbnQudHlwZSA9PT0gJ3BhZ2VfdmlldycpLmxlbmd0aCxcbiAgICBwYXJ0aWFsOiBCb29sZWFuKG5leHRUb2tlbiksXG4gICAgcGVyZm9ybWFuY2VTYW1wbGVzOiBkdXJhdGlvbnMubGVuZ3RoLFxuICAgIHJlY2VudEV2ZW50czogZXZlbnRzLnNsaWNlKDAsIDEwMCkubWFwKCh7YXBwSWQ6IF9hcHBJZCwgLi4uZXZlbnR9KSA9PiBldmVudCksXG4gICAgc2Vzc2lvbkNvdW50OiBzZXNzaW9ucy5zaXplLFxuICAgIHN0YXJ0VGltZSxcbiAgICB0aW1lbGluZTogQXJyYXkuZnJvbShidWNrZXRzLCAoW3RpbWVzdGFtcCwgYnVja2V0XSkgPT4gKHtcbiAgICAgIGVycm9yczogYnVja2V0LmVycm9ycyxcbiAgICAgIGV2ZW50czogYnVja2V0LmV2ZW50cyxcbiAgICAgIGludGVyYWN0aW9uczogYnVja2V0LmludGVyYWN0aW9ucyxcbiAgICAgIHNlc3Npb25zOiBidWNrZXQuc2Vzc2lvbnMuc2l6ZSxcbiAgICAgIHRpbWVzdGFtcFxuICAgIH0pKSxcbiAgICB2aWV3UGVyZm9ybWFuY2U6IGNyZWF0ZVZpZXdQZXJmb3JtYW5jZShldmVudHMpXG4gIH07XG59O1xuIl0sIm5hbWVzIjpbIkNyZWF0ZUFwcE1vbml0b3JDb21tYW5kIiwiR2V0QXBwTW9uaXRvckNvbW1hbmQiLCJHZXRBcHBNb25pdG9yRGF0YUNvbW1hbmQiLCJSVU1DbGllbnQiLCJjcmVhdGVIYXNoIiwicmFuZG9tVVVJRCIsIkNvbmZpZyIsIkNPTExFQ1RJT05TIiwiRXJyb3JUeXBlcyIsIlVzZXJFcnJvciIsInVzZURiIiwiaXNBZG1pblVzZXIiLCJhZGRJdGVtIiwiZ2V0SXRlbUJ5IiwiZ2V0TGlzdEJ5IiwidXBkYXRlSXRlbSIsIk1BWF9BUFBfSURfTEVOR1RIIiwiTUFYX0RPTUFJTl9MRU5HVEgiLCJNQVhfTU9OSVRPUl9OQU1FX0xFTkdUSCIsIk1BWF9SVU1fUEFHRVMiLCJNQVhfUlVNX1JFU1VMVFMiLCJWQUxJRF9SQU5HRV9EQVlTIiwiU2V0IiwiY29sbGVjdGlvbkluaXRpYWxpemF0aW9uIiwiTWFwIiwibm9ybWFsaXplVGV4dCIsInZhbHVlIiwibWF4TGVuZ3RoIiwiU3RyaW5nIiwidHJpbSIsInNsaWNlIiwibm9ybWFsaXplRG9tYWluIiwicmVwbGFjZSIsInNwbGl0IiwidG9Mb3dlckNhc2UiLCJub3JtYWxpemVNb25pdG9yTmFtZSIsImFwcElkIiwiZ2V0RG9jdW1lbnRLZXkiLCJ1cGRhdGUiLCJkaWdlc3QiLCJyZXF1aXJlQWRtaW4iLCJjb250ZXh0Iiwic2Vzc2lvbiIsIklOVkFMSURfU0VTU0lPTiIsInJlcXVpcmVTZXNzaW9uIiwidXNlcklkIiwiaW5pdGlhbGl6ZUFuYWx5dGljc0NvbGxlY3Rpb24iLCJkYXRhYmFzZU5hbWUiLCJjYWNoZUtleSIsImV4aXN0aW5nSW5pdGlhbGl6YXRpb24iLCJnZXQiLCJpbml0aWFsaXphdGlvbiIsImRhdGFiYXNlIiwiY29sbGVjdGlvbiIsIkFOQUxZVElDUyIsImV4aXN0cyIsImNyZWF0ZUNvbGxlY3Rpb24iLCJlcnJvciIsImVuc3VyZUluZGV4IiwiZmllbGRzIiwidHlwZSIsInVuaXF1ZSIsInNldCIsImRlbGV0ZSIsImdldFJ1bUNsaWVudCIsImF3c0NvbmZpZyIsImNvbmZpZyIsImF3cyIsImFjY2Vzc0tleUlkIiwic2VjcmV0QWNjZXNzS2V5Iiwic2Vzc2lvblRva2VuIiwiY3JlZGVudGlhbHMiLCJ1c2VzUnVudGltZUNyZWRlbnRpYWxzIiwiQm9vbGVhbiIsInByb2Nlc3MiLCJlbnYiLCJBV1NfU0VTU0lPTl9UT0tFTiIsIkFXU19BQ0NFU1NfS0VZX0lEIiwiQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZIiwidW5kZWZpbmVkIiwicmVnaW9uIiwiZmluZEF3c01vbml0b3IiLCJjbGllbnQiLCJtb25pdG9yTmFtZSIsInJlc3BvbnNlIiwic2VuZCIsIk5hbWUiLCJtb25pdG9ySWQiLCJBcHBNb25pdG9yIiwiSWQiLCJkb21haW4iLCJEb21haW4iLCJpZCIsIm5hbWUiLCJmaW5kT3JDcmVhdGVBd3NNb25pdG9yIiwiZXhpc3RpbmdNb25pdG9yIiwiQ3VzdG9tRXZlbnRzIiwiU3RhdHVzIiwiQ3dMb2dFbmFibGVkIiwiUGxhdGZvcm0iLCJUYWdzIiwibWFuYWdlZEJ5IiwicGFyc2VNb25pdG9ySW5wdXQiLCJpbnB1dCIsImFwcE5hbWUiLCJJTlZBTElEX0FSR1VNRU5UUyIsImdldEFuYWx5dGljc0J5QXBwSWQiLCJhcHBJZElucHV0IiwiZ2V0QW5hbHl0aWNzQ29uZmlndXJhdGlvbiIsImFuYWx5dGljcyIsImFuYWx5dGljc0lkIiwiZ2V0QW5hbHl0aWNzQnlJZCIsImFuYWx5dGljc0lkSW5wdXQiLCJjcmVhdGVBbmFseXRpY3NNb25pdG9yIiwiZXhpc3RpbmciLCJ1c2VySWRzIiwiaW5jbHVkZXMiLCJtb25pdG9yIiwiX2tleSIsInJlZ2VuZXJhdGVBbmFseXRpY3NJZCIsImhhc0FjY2VzcyIsInBhcnNlSnNvblJlY29yZCIsIkFycmF5IiwiaXNBcnJheSIsInBhcnNlZCIsIkpTT04iLCJwYXJzZSIsImdldFByb3BlcnR5IiwicHJvcGVydGllcyIsIm1ldGFkYXRhIiwiYWxpYXNlcyIsIm5vcm1hbGl6ZWRBbGlhc2VzIiwibWFwIiwiYWxpYXMiLCJlbnRyaWVzIiwiT2JqZWN0IiwiZmluZCIsImtleSIsImhhcyIsIm5vcm1hbGl6ZVJ1bUV2ZW50IiwicmF3RXZlbnQiLCJzb3VyY2UiLCJkZXRhaWxzIiwiZXZlbnRfZGV0YWlscyIsInRpbWVzdGFtcCIsIk51bWJlciIsImV2ZW50X3RpbWVzdGFtcCIsImlzRmluaXRlIiwiZ2V0VGV4dCIsImZhbGxiYWNrIiwiZHVyYXRpb25WYWx1ZSIsInBhdGgiLCJldmVudF90eXBlIiwiYXBwbGljYXRpb25JZCIsImJyb3dzZXJMYW5ndWFnZSIsImJyb3dzZXJOYW1lIiwiYnJvd3NlclZlcnNpb24iLCJkZXZpY2VUeXBlIiwiZHVyYXRpb24iLCJldmVudElkIiwiZXZlbnRfaWQiLCJpbnRlcmFjdGlvbiIsIm9zTmFtZSIsIm9zVmVyc2lvbiIsIm91dGNvbWUiLCJwYWdlSWQiLCJwYXJlbnRQYWdlIiwic2Vzc2lvbklkIiwidXNlcl9kZXRhaWxzIiwidmlld0lkIiwiY3JlYXRlQnJlYWtkb3duIiwidmFsdWVzIiwidG90YWwiLCJjb3VudHMiLCJyZWR1Y2UiLCJyZXN1bHQiLCJsYWJlbCIsImZyb20iLCJjb3VudCIsInBlcmNlbnRhZ2UiLCJNYXRoIiwicm91bmQiLCJzb3J0IiwibGVmdCIsInJpZ2h0IiwibG9jYWxlQ29tcGFyZSIsInBlcmNlbnRpbGUiLCJwZXJjZW50aWxlVmFsdWUiLCJsZW5ndGgiLCJzb3J0ZWQiLCJ0b1NvcnRlZCIsImluZGV4IiwibWF4IiwiY2VpbCIsImNyZWF0ZVZpZXdQZXJmb3JtYW5jZSIsImV2ZW50cyIsInZpZXdzIiwiZXZlbnQiLCJzYW1wbGVzIiwicHVzaCIsImR1cmF0aW9ucyIsImZpbHRlciIsImZhaWx1cmVSYXRlIiwicDUwIiwicDc1IiwicDk1Iiwic2FtcGxlQ291bnQiLCJ0aW1lb3V0UmF0ZSIsImdldEF1dGhvcml6ZWRBbmFseXRpY3MiLCJnZXRBbmFseXRpY3NBcHBOYW1lIiwiYXBwIiwiQVBQUyIsImNvbmRpdGlvbnMiLCJmaWVsZCIsIm9wZXJhdG9yIiwibG9naWMiLCJkaXNwbGF5TmFtZSIsImdldEF2YWlsYWJsZUFuYWx5dGljcyIsImRpcmVjdGlvbiIsImF2YWlsYWJsZSIsIml0ZW0iLCJlbnJpY2hlZCIsIlByb21pc2UiLCJhbGwiLCJzZXRBbmFseXRpY3NBY2Nlc3MiLCJ1c2VySWRzSW5wdXQiLCJnZXRBbmFseXRpY3NEYXNoYm9hcmQiLCJyYW5nZURheXNJbnB1dCIsInJhbmdlRGF5cyIsImZsb29yIiwiZW5kVGltZSIsIkRhdGUiLCJub3ciLCJzdGFydFRpbWUiLCJyYXdFdmVudHMiLCJuZXh0VG9rZW4iLCJwYWdlIiwiTWF4UmVzdWx0cyIsIk5leHRUb2tlbiIsIlRpbWVSYW5nZSIsIkFmdGVyIiwiQmVmb3JlIiwiRXZlbnRzIiwic2Vzc2lvbnMiLCJpbnRlcmFjdGlvbkV2ZW50cyIsImVycm9yRXZlbnRzIiwidGVzdCIsImJ1Y2tldFNpemUiLCJidWNrZXRzIiwiZXJyb3JzIiwiaW50ZXJhY3Rpb25zIiwiZm9yRWFjaCIsImJ1Y2tldCIsImFkZCIsImF2ZXJhZ2VEdXJhdGlvbiIsImJyb3dzZXJzIiwiam9pbiIsImRldmljZXMiLCJlcnJvckNvdW50IiwiZXZlbnRDb3VudCIsImdlbmVyYXRlZEF0IiwibGFuZ3VhZ2VzIiwib3BlcmF0aW5nU3lzdGVtcyIsInBhZ2VzIiwicGFnZVZpZXdDb3VudCIsInBhcnRpYWwiLCJwZXJmb3JtYW5jZVNhbXBsZXMiLCJyZWNlbnRFdmVudHMiLCJfYXBwSWQiLCJzZXNzaW9uQ291bnQiLCJzaXplIiwidGltZWxpbmUi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