@nlabs/reaktor 1.7.0 → 1.9.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 (230) hide show
  1. package/README.md +75 -0
  2. package/lib/actions/analytics.js +1 -1
  3. package/lib/actions/apps.js +1 -1
  4. package/lib/actions/connections.js +1 -1
  5. package/lib/actions/content.js +1 -1
  6. package/lib/actions/conversations.js +1 -1
  7. package/lib/actions/crm.d.ts +7 -1
  8. package/lib/actions/crm.js +224 -17
  9. package/lib/actions/crud.js +1 -1
  10. package/lib/actions/dynamodb.js +1 -1
  11. package/lib/actions/email.js +1 -1
  12. package/lib/actions/files.js +1 -1
  13. package/lib/actions/flags.js +1 -1
  14. package/lib/actions/groups.js +1 -1
  15. package/lib/actions/images.js +1 -1
  16. package/lib/actions/index.js +1 -1
  17. package/lib/actions/ios.js +1 -1
  18. package/lib/actions/locations.js +1 -1
  19. package/lib/actions/messages.js +1 -1
  20. package/lib/actions/notifications.js +1 -1
  21. package/lib/actions/payments.js +1 -1
  22. package/lib/actions/personas.js +1 -1
  23. package/lib/actions/posts.js +1 -1
  24. package/lib/actions/query.js +1 -1
  25. package/lib/actions/reactions.js +1 -1
  26. package/lib/actions/rum.js +14 -9
  27. package/lib/actions/s3.js +1 -1
  28. package/lib/actions/search.js +1 -1
  29. package/lib/actions/sms.js +1 -1
  30. package/lib/actions/statistics.js +1 -1
  31. package/lib/actions/subscriptions.js +1 -1
  32. package/lib/actions/tags.js +1 -1
  33. package/lib/actions/users.d.ts +4 -1
  34. package/lib/actions/users.js +28 -7
  35. package/lib/actions/videos.js +1 -1
  36. package/lib/adapters/arangoAdapter.js +1 -1
  37. package/lib/adapters/contentAdapter.js +1 -1
  38. package/lib/adapters/fileAdapter.js +1 -1
  39. package/lib/adapters/imageAdapter.js +1 -1
  40. package/lib/adapters/index.js +1 -1
  41. package/lib/adapters/messageAdapter.js +1 -1
  42. package/lib/adapters/postAdapter.js +1 -1
  43. package/lib/adapters/reaktorAdapter.js +1 -1
  44. package/lib/adapters/tagAdapter.js +1 -1
  45. package/lib/adapters/userAdapter.js +1 -1
  46. package/lib/config.js +1 -1
  47. package/lib/constants/collections.d.ts +12 -0
  48. package/lib/constants/collections.js +13 -1
  49. package/lib/durableChat/conversations.d.ts +62 -0
  50. package/lib/durableChat/conversations.js +729 -0
  51. package/lib/durableChat/errors.d.ts +6 -0
  52. package/lib/durableChat/errors.js +11 -0
  53. package/lib/durableChat/events.d.ts +22 -0
  54. package/lib/durableChat/events.js +50 -0
  55. package/lib/durableChat/index.d.ts +8 -0
  56. package/lib/durableChat/index.js +10 -0
  57. package/lib/durableChat/membership.d.ts +24 -0
  58. package/lib/durableChat/membership.js +85 -0
  59. package/lib/durableChat/messageTypes.d.ts +76 -0
  60. package/lib/durableChat/messageTypes.js +3 -0
  61. package/lib/durableChat/messages.d.ts +16 -0
  62. package/lib/durableChat/messages.js +257 -0
  63. package/lib/durableChat/transaction.d.ts +10 -0
  64. package/lib/durableChat/transaction.js +53 -0
  65. package/lib/durableChat/types.d.ts +81 -0
  66. package/lib/durableChat/types.js +3 -0
  67. package/lib/handlers/graphqlHandler.js +1 -1
  68. package/lib/index.d.ts +4 -1
  69. package/lib/index.js +4 -2
  70. package/lib/lambdas/actions/websockets.js +1 -1
  71. package/lib/lambdas/authorizer.js +1 -1
  72. package/lib/lambdas/connection.js +1 -1
  73. package/lib/lambdas/utils/message.js +1 -1
  74. package/lib/lambdas/utils/websocket.js +1 -1
  75. package/lib/mocks/conversation.js +1 -1
  76. package/lib/mocks/file.js +1 -1
  77. package/lib/mocks/group.js +1 -1
  78. package/lib/mocks/image.js +1 -1
  79. package/lib/mocks/post.js +1 -1
  80. package/lib/mocks/tag.js +1 -1
  81. package/lib/mocks/user.js +1 -1
  82. package/lib/mocks/video.js +1 -1
  83. package/lib/mutations/analytics/analytics.js +1 -1
  84. package/lib/mutations/content/content.js +1 -1
  85. package/lib/mutations/crm/crm.js +43 -11
  86. package/lib/mutations/events/events.js +1 -1
  87. package/lib/mutations/flags/flags.js +1 -1
  88. package/lib/mutations/groups/groups.js +1 -1
  89. package/lib/mutations/images/images.js +1 -1
  90. package/lib/mutations/index.js +1 -1
  91. package/lib/mutations/locations/locations.js +1 -1
  92. package/lib/mutations/messages/messages.js +1 -1
  93. package/lib/mutations/personas/personas.js +1 -1
  94. package/lib/mutations/posts/posts.js +1 -1
  95. package/lib/mutations/reactions/reactions.js +1 -1
  96. package/lib/mutations/rum/rum.js +1 -1
  97. package/lib/mutations/statistics/statistics.js +1 -1
  98. package/lib/mutations/subscriptions/subscriptions.js +1 -1
  99. package/lib/mutations/tags/tags.js +1 -1
  100. package/lib/mutations/users/users.js +1 -1
  101. package/lib/mutations/videos/videos.js +1 -1
  102. package/lib/objectTypes/analytics.js +1 -1
  103. package/lib/objectTypes/app.js +1 -1
  104. package/lib/objectTypes/bankAccount.js +1 -1
  105. package/lib/objectTypes/connection.js +1 -1
  106. package/lib/objectTypes/content.js +1 -1
  107. package/lib/objectTypes/conversation.js +1 -1
  108. package/lib/objectTypes/creditCard.js +1 -1
  109. package/lib/objectTypes/crm.js +5 -1
  110. package/lib/objectTypes/document.js +1 -1
  111. package/lib/objectTypes/error.js +1 -1
  112. package/lib/objectTypes/event.js +1 -1
  113. package/lib/objectTypes/external.js +1 -1
  114. package/lib/objectTypes/file.js +1 -1
  115. package/lib/objectTypes/filter.js +1 -1
  116. package/lib/objectTypes/flag.js +5 -2
  117. package/lib/objectTypes/group.js +1 -1
  118. package/lib/objectTypes/iapSubscription.js +1 -1
  119. package/lib/objectTypes/image.js +1 -1
  120. package/lib/objectTypes/index.js +1 -1
  121. package/lib/objectTypes/location.js +1 -1
  122. package/lib/objectTypes/message.js +1 -1
  123. package/lib/objectTypes/passcode.js +1 -1
  124. package/lib/objectTypes/persona.js +1 -1
  125. package/lib/objectTypes/plan.js +1 -1
  126. package/lib/objectTypes/post.js +1 -1
  127. package/lib/objectTypes/query.js +1 -1
  128. package/lib/objectTypes/reaction.js +1 -1
  129. package/lib/objectTypes/relation.js +1 -1
  130. package/lib/objectTypes/rum.js +1 -1
  131. package/lib/objectTypes/search.js +1 -1
  132. package/lib/objectTypes/statistics.js +1 -1
  133. package/lib/objectTypes/subscription.js +1 -1
  134. package/lib/objectTypes/tag.js +1 -1
  135. package/lib/objectTypes/user.js +9 -1
  136. package/lib/objectTypes/video.js +1 -1
  137. package/lib/queries/analytics/analytics.js +1 -1
  138. package/lib/queries/content/content.js +1 -1
  139. package/lib/queries/crm/crm.js +41 -13
  140. package/lib/queries/events/events.js +1 -1
  141. package/lib/queries/flags/flags.js +1 -1
  142. package/lib/queries/groups/groups.js +1 -1
  143. package/lib/queries/images/images.js +1 -1
  144. package/lib/queries/index.js +1 -1
  145. package/lib/queries/locations/locations.js +1 -1
  146. package/lib/queries/messages/messages.js +1 -1
  147. package/lib/queries/personas/personas.js +1 -1
  148. package/lib/queries/posts/posts.js +1 -1
  149. package/lib/queries/query/query.js +1 -1
  150. package/lib/queries/reactions/reactions.js +1 -1
  151. package/lib/queries/statistics/statistics.js +1 -1
  152. package/lib/queries/subscriptions/subscriptions.js +1 -1
  153. package/lib/queries/tags/tags.js +1 -1
  154. package/lib/queries/users/users.js +1 -1
  155. package/lib/queries/videos/videos.js +1 -1
  156. package/lib/templates/email/layout.js +1 -1
  157. package/lib/templates/email/passwordForgot.js +1 -1
  158. package/lib/templates/email/passwordRecovery.js +1 -1
  159. package/lib/templates/email/verifyEmail.js +1 -1
  160. package/lib/templates/email/welcome.js +1 -1
  161. package/lib/templates/sms/passwordForgot.js +1 -1
  162. package/lib/templates/sms/passwordRecovery.js +1 -1
  163. package/lib/templates/sms/verifyEmail.js +1 -1
  164. package/lib/templates/sms/verifyPhone.js +1 -1
  165. package/lib/templates/sms/welcome.js +1 -1
  166. package/lib/types/analytics.types.js +1 -1
  167. package/lib/types/apps.types.js +1 -1
  168. package/lib/types/arangodb.types.d.ts +1 -1
  169. package/lib/types/arangodb.types.js +1 -1
  170. package/lib/types/auth.types.d.ts +1 -0
  171. package/lib/types/auth.types.js +1 -1
  172. package/lib/types/connections.types.js +1 -1
  173. package/lib/types/content.types.js +1 -1
  174. package/lib/types/conversations.types.js +1 -1
  175. package/lib/types/crm.types.d.ts +20 -1
  176. package/lib/types/crm.types.js +1 -1
  177. package/lib/types/email.types.js +1 -1
  178. package/lib/types/error.types.js +1 -1
  179. package/lib/types/files.types.js +1 -1
  180. package/lib/types/flags.types.js +1 -1
  181. package/lib/types/google.types.js +1 -1
  182. package/lib/types/groups.types.js +1 -1
  183. package/lib/types/images.types.js +1 -1
  184. package/lib/types/index.js +1 -1
  185. package/lib/types/locations.types.js +1 -1
  186. package/lib/types/messages.types.js +1 -1
  187. package/lib/types/notifications.types.js +1 -1
  188. package/lib/types/payments.types.js +1 -1
  189. package/lib/types/personas.types.js +1 -1
  190. package/lib/types/posts.types.js +1 -1
  191. package/lib/types/rum.types.js +1 -1
  192. package/lib/types/statistics.types.js +1 -1
  193. package/lib/types/tags.types.js +1 -1
  194. package/lib/types/users.types.d.ts +1 -0
  195. package/lib/types/users.types.js +1 -1
  196. package/lib/types/videos.types.js +1 -1
  197. package/lib/types/websockets.types.js +1 -1
  198. package/lib/upload.js +1 -1
  199. package/lib/utils/adapterUtils.js +1 -1
  200. package/lib/utils/analyticsUtils.js +1 -1
  201. package/lib/utils/appContext.d.ts +2 -0
  202. package/lib/utils/appContext.js +10 -0
  203. package/lib/utils/aqlHelpers.js +1 -1
  204. package/lib/utils/arangodbUtils.js +1 -1
  205. package/lib/utils/authUtils.js +1 -1
  206. package/lib/utils/awsUtils.js +1 -1
  207. package/lib/utils/collectionRegistry.js +1 -1
  208. package/lib/utils/contextUtils.js +1 -1
  209. package/lib/utils/dbI18n.example.js +1 -1
  210. package/lib/utils/dbI18n.js +1 -1
  211. package/lib/utils/googleTranslate.js +1 -1
  212. package/lib/utils/graphqlUtils.d.ts +1 -0
  213. package/lib/utils/graphqlUtils.js +1 -1
  214. package/lib/utils/imageUtils.js +1 -1
  215. package/lib/utils/index.js +1 -1
  216. package/lib/utils/languageDetection.js +1 -1
  217. package/lib/utils/localeUtils.example.js +1 -1
  218. package/lib/utils/localeUtils.js +1 -1
  219. package/lib/utils/middlewareUtils.js +1 -1
  220. package/lib/utils/queryBuilder.js +1 -1
  221. package/lib/utils/schemaUtils.js +1 -1
  222. package/lib/utils/sessionUtils.d.ts +4 -0
  223. package/lib/utils/sessionUtils.js +1 -1
  224. package/lib/utils/stripeUtils.js +2 -1
  225. package/lib/utils/templateUtils.js +1 -1
  226. package/lib/utils/testUtils.js +1 -1
  227. package/lib/utils/translationQueue.example.js +1 -1
  228. package/lib/utils/translationQueue.js +1 -1
  229. package/lib/videoProcessing.js +1 -1
  230. package/package.json +19 -19
@@ -0,0 +1,729 @@
1
+ /* eslint-disable no-await-in-loop, no-underscore-dangle -- Ordered transactions and Arango identities. */ import { createHash, randomUUID } from 'node:crypto';
2
+ import { ChatError } from './errors.js';
3
+ import { appendChatEvent } from './events.js';
4
+ import { createMembershipAuthorizer, isChatIdentifier } from './membership.js';
5
+ import { createDurableTransaction } from './transaction.js';
6
+ const hash = (input)=>createHash('sha256').update(JSON.stringify(input)).digest('hex');
7
+ const inviteMilliseconds = 7 * 24 * 60 * 60 * 1000;
8
+ const membershipDto = (record)=>({
9
+ conversationId: record.conversationId,
10
+ epoch: record.epoch,
11
+ joinSequence: record.joinSequence,
12
+ leftSequence: record.leftSequence,
13
+ personaId: record.personaId,
14
+ revision: record.revision,
15
+ role: record.role,
16
+ state: record.state
17
+ });
18
+ const inviteDto = (record)=>({
19
+ conversationId: record.conversationId,
20
+ expiresAt: record.expiresAt,
21
+ inviteId: record._key,
22
+ invitedByPersonaId: record.invitedByPersonaId,
23
+ revision: record.revision,
24
+ state: record.state
25
+ });
26
+ export const createDurableConversationRepository = (context, identity, options)=>{
27
+ const audience = options.audience || 'mobile';
28
+ const transact = createDurableTransaction(context, {
29
+ exclusive: [
30
+ 'chatInvites',
31
+ 'chatOutbox',
32
+ 'chatRateLimits',
33
+ 'chatRequests',
34
+ 'conversations',
35
+ 'hasChatMembership',
36
+ 'hasConversation'
37
+ ],
38
+ read: [
39
+ 'hasConnection',
40
+ 'personas',
41
+ 'users',
42
+ ...options.readCollections || []
43
+ ]
44
+ }, options.query);
45
+ const summary = (conversation, member, audience, includeName = false)=>({
46
+ activeMemberCount: conversation.activeMemberCount,
47
+ conversationId: conversation._key,
48
+ kind: conversation.kind,
49
+ membership: membershipDto(member),
50
+ membershipRevision: conversation.membershipRevision,
51
+ name: includeName ? options.projectName(conversation, audience) : undefined,
52
+ state: conversation.state
53
+ });
54
+ const actor = async (read)=>{
55
+ const allowed = await read(`LET persona = DOCUMENT('personas', @personaId)
56
+ LET user = DOCUMENT('users', @userId)
57
+ RETURN persona != null AND persona.userId == @userId AND persona.active != false
58
+ AND user != null AND user.active != false`, {
59
+ ...identity
60
+ });
61
+ if (allowed !== true) {
62
+ throw new ChatError('forbidden');
63
+ }
64
+ await adult(read, identity);
65
+ };
66
+ const adult = (read, who)=>options.authorizeIdentity(read, who);
67
+ const connected = async (read, from, personaId)=>{
68
+ const target = await read(`LET persona = DOCUMENT('personas', @personaId)
69
+ LET user = DOCUMENT('users', persona.userId)
70
+ LET following = FIRST(FOR edge IN hasConnection FILTER edge._from == @from AND edge._to == @to
71
+ AND edge.type == 'follow' AND edge.active != false LIMIT 1 RETURN true)
72
+ LET followed = FIRST(FOR edge IN hasConnection FILTER edge._to == @from AND edge._from == @to
73
+ AND edge.type == 'follow' AND edge.active != false LIMIT 1 RETURN true)
74
+ LET blocked = FIRST(FOR edge IN hasConnection FILTER edge.type == 'block'
75
+ AND ((edge._from IN [@from, @fromUser] AND edge._to IN [@to, CONCAT('users/', persona.userId)])
76
+ OR (edge._to IN [@from, @fromUser] AND edge._from IN [@to, CONCAT('users/', persona.userId)])) LIMIT 1 RETURN true)
77
+ FILTER persona != null AND user != null AND persona.active != false AND user.active != false
78
+ AND persona.userId != @actorUser AND following == true AND followed == true AND blocked != true
79
+ RETURN {personaId: @personaId, userId: persona.userId}`, {
80
+ actorUser: from.userId,
81
+ from: `personas/${from.personaId}`,
82
+ fromUser: `users/${from.userId}`,
83
+ personaId,
84
+ to: `personas/${personaId}`
85
+ });
86
+ if (!target) {
87
+ throw new ChatError('forbidden');
88
+ }
89
+ await adult(read, target);
90
+ return target;
91
+ };
92
+ const conversation = async (read, id)=>{
93
+ const value = await read('RETURN DOCUMENT("conversations", @id)', {
94
+ id
95
+ });
96
+ if (!value || value.chatSchemaVersion !== 1 || value.state === 'closed') {
97
+ throw new ChatError('forbidden');
98
+ }
99
+ return value;
100
+ };
101
+ const member = async (read, conversationId, personaId)=>{
102
+ const value = await read(`FOR member IN hasChatMembership FILTER member.conversationId == @conversationId
103
+ AND member.personaId == @personaId AND member.state == 'active' LIMIT 1 RETURN member`, {
104
+ conversationId,
105
+ personaId
106
+ });
107
+ if (!value) {
108
+ throw new ChatError('forbidden');
109
+ }
110
+ return value;
111
+ };
112
+ const access = (read, conversationId, purpose = 'participants')=>createMembershipAuthorizer(read)(identity, {
113
+ conversationId,
114
+ purpose
115
+ });
116
+ const checkRevision = (entry, expected)=>{
117
+ if (entry.membershipRevision !== expected || entry.kind !== 'group') {
118
+ throw new ChatError('conflict');
119
+ }
120
+ };
121
+ const event = async (read, conversationId, epoch, resourceId, revision)=>{
122
+ await appendChatEvent(read, {
123
+ actorPersonaId: identity.personaId,
124
+ conversationId,
125
+ kind: 'membership.changed',
126
+ ...epoch ? {
127
+ membershipEpoch: epoch
128
+ } : {},
129
+ resourceId,
130
+ revision
131
+ });
132
+ };
133
+ const receipt = async (read, action, input)=>{
134
+ const key = hash([
135
+ identity.userId,
136
+ identity.personaId,
137
+ action,
138
+ input.idempotencyKey
139
+ ]);
140
+ const payloadHash = hash(input);
141
+ const prior = await read('RETURN DOCUMENT("chatRequests", @key)', {
142
+ key
143
+ });
144
+ if (prior && prior.payloadHash !== payloadHash) {
145
+ throw new ChatError('conflict');
146
+ }
147
+ return {
148
+ prior: prior?.result,
149
+ save: (result)=>read('INSERT @record IN chatRequests RETURN true', {
150
+ record: {
151
+ _key: key,
152
+ added: Date.now(),
153
+ ownerUserId: identity.userId,
154
+ payloadHash,
155
+ personaId: identity.personaId,
156
+ result
157
+ }
158
+ })
159
+ };
160
+ };
161
+ const rateLimit = async (read)=>{
162
+ const key = hash([
163
+ identity.userId,
164
+ Math.floor(Date.now() / 3600000)
165
+ ]);
166
+ const allowed = await read(`LET bucket = DOCUMENT('chatRateLimits', @key)
167
+ FILTER bucket == null OR bucket.count < 50
168
+ UPSERT {_key: @key} INSERT {_key: @key, count: 1, userId: @userId} UPDATE {count: OLD.count + 1, userId: @userId}
169
+ IN chatRateLimits RETURN true`, {
170
+ key,
171
+ userId: identity.userId
172
+ });
173
+ if (allowed !== true) {
174
+ throw new ChatError('rate_limited');
175
+ }
176
+ };
177
+ const addMember = async (read, conversationId, who, role, joinSequence)=>{
178
+ const id = randomUUID();
179
+ const eventSequence = await read('RETURN DOCUMENT("conversations", @conversationId).lastEventSequence || 0', {
180
+ conversationId
181
+ });
182
+ const record = {
183
+ _from: `personas/${who.personaId}`,
184
+ _key: id,
185
+ _to: `conversations/${conversationId}`,
186
+ acceptedAt: Date.now(),
187
+ conversationId,
188
+ currentKey: `${conversationId}:${who.personaId}`,
189
+ epoch: randomUUID(),
190
+ joinEventSequence: joinSequence > 0 ? (eventSequence || 0) + 1 : 0,
191
+ joinSequence,
192
+ personaId: who.personaId,
193
+ revision: 1,
194
+ role,
195
+ state: 'active',
196
+ userId: who.userId
197
+ };
198
+ await read('INSERT @record IN hasChatMembership RETURN true', {
199
+ record
200
+ });
201
+ await read(`UPSERT {_from: @from, _to: @to}
202
+ INSERT {_from: @from, _to: @to, added: @now, conversationId: @conversationId, userId: @userId}
203
+ UPDATE {} IN hasConversation RETURN true`, {
204
+ conversationId,
205
+ from: `users/${who.userId}`,
206
+ now: Date.now(),
207
+ to: `conversations/${conversationId}`,
208
+ userId: who.userId
209
+ });
210
+ return record;
211
+ };
212
+ const addInvite = async (read, conversationId, who)=>{
213
+ const id = randomUUID();
214
+ const record = {
215
+ _key: id,
216
+ conversationId,
217
+ expiresAt: Date.now() + inviteMilliseconds,
218
+ inviteId: id,
219
+ invitedByPersonaId: identity.personaId,
220
+ inviteePersonaId: who.personaId,
221
+ inviteeUserId: who.userId,
222
+ revision: 1,
223
+ state: 'pending'
224
+ };
225
+ await read('INSERT @record IN chatInvites RETURN true', {
226
+ record: {
227
+ ...record,
228
+ createdAt: Date.now(),
229
+ currentKey: `${conversationId}:${who.personaId}`,
230
+ role: 'member'
231
+ }
232
+ });
233
+ await event(read, conversationId, '', id, 1);
234
+ return inviteDto(record);
235
+ };
236
+ const expireInvites = async (read, conversationId)=>{
237
+ await read(`FOR invite IN chatInvites FILTER invite.conversationId == @conversationId
238
+ AND invite.state == 'pending' AND invite.expiresAt <= @now
239
+ UPDATE invite WITH {currentKey: null, resolvedAt: @now, revision: invite.revision + 1, state: 'expired'}
240
+ IN chatInvites RETURN true`, {
241
+ conversationId,
242
+ now: Date.now()
243
+ });
244
+ };
245
+ const getOrCreateDirect = (input)=>transact(async (read)=>{
246
+ await actor(read);
247
+ const target = await connected(read, identity, input.participantPersonaId);
248
+ const pair = hash([
249
+ 'direct-v1',
250
+ ...[
251
+ identity.personaId,
252
+ target.personaId
253
+ ].sort()
254
+ ]);
255
+ const existing = await read('FOR c IN conversations FILTER c.directPairKey == @pair LIMIT 1 RETURN c', {
256
+ pair
257
+ });
258
+ if (existing) {
259
+ await access(read, existing._key);
260
+ return summary(existing, await member(read, existing._key, identity.personaId), audience);
261
+ }
262
+ await rateLimit(read);
263
+ const id = `direct-${pair}`;
264
+ const record = {
265
+ _key: id,
266
+ activeMemberCount: 2,
267
+ chatSchemaVersion: 1,
268
+ kind: 'direct',
269
+ lastSequence: 0,
270
+ membershipRevision: 1,
271
+ state: 'active'
272
+ };
273
+ await read('INSERT @record IN conversations RETURN true', {
274
+ record: {
275
+ ...record,
276
+ added: Date.now(),
277
+ createdByPersonaId: identity.personaId,
278
+ directPairKey: pair,
279
+ isDirect: true
280
+ }
281
+ });
282
+ const own = await addMember(read, id, identity, 'member', 0);
283
+ await addMember(read, id, target, 'member', 0);
284
+ await event(read, id, own.epoch, id, 1);
285
+ return summary(record, own, audience);
286
+ });
287
+ const createGroup = (input)=>transact(async (read)=>{
288
+ await actor(read);
289
+ const request = await receipt(read, 'createGroup', input);
290
+ if (request.prior) {
291
+ return request.prior;
292
+ }
293
+ await rateLimit(read);
294
+ const targets = [];
295
+ for (const personaId of input.memberPersonaIds){
296
+ targets.push(await connected(read, identity, personaId));
297
+ }
298
+ const id = randomUUID();
299
+ const contentVersion = hash([
300
+ 'group-name',
301
+ input.name
302
+ ]);
303
+ const record = {
304
+ _key: id,
305
+ activeMemberCount: 1,
306
+ chatSchemaVersion: 1,
307
+ kind: 'group',
308
+ lastSequence: 0,
309
+ membershipRevision: 1,
310
+ name: input.name,
311
+ nameContentVersion: contentVersion,
312
+ nameDecision: options.classifyName(input.name, contentVersion),
313
+ state: 'forming'
314
+ };
315
+ await read('INSERT @record IN conversations RETURN true', {
316
+ record: {
317
+ ...record,
318
+ added: Date.now(),
319
+ createdByPersonaId: identity.personaId,
320
+ isDirect: false
321
+ }
322
+ });
323
+ const own = await addMember(read, id, identity, 'owner', 0);
324
+ for (const target of targets){
325
+ await addInvite(read, id, target);
326
+ }
327
+ await event(read, id, own.epoch, id, 1);
328
+ const result = summary(record, own, audience);
329
+ await request.save(result);
330
+ return result;
331
+ });
332
+ const inviteMember = (input)=>transact(async (read)=>{
333
+ await actor(read);
334
+ const request = await receipt(read, 'invite', input);
335
+ if (request.prior) {
336
+ return request.prior;
337
+ }
338
+ const own = await access(read, input.conversationId, 'invite');
339
+ checkRevision(own, input.expectedRevision);
340
+ const target = await connected(read, identity, input.personaId);
341
+ await expireInvites(read, input.conversationId);
342
+ const prior = await read('FOR invite IN chatInvites FILTER invite.currentKey == @key LIMIT 1 RETURN invite', {
343
+ key: `${input.conversationId}:${input.personaId}`
344
+ });
345
+ if (prior) {
346
+ const result = inviteDto(prior);
347
+ await request.save(result);
348
+ return result;
349
+ }
350
+ const counts = await read(`RETURN {
351
+ active: LENGTH(FOR member IN hasChatMembership FILTER member.conversationId == @conversationId
352
+ AND member.personaId == @personaId AND member.state == 'active' RETURN 1) > 0,
353
+ total: LENGTH(FOR member IN hasChatMembership FILTER member.conversationId == @conversationId
354
+ AND member.state == 'active' RETURN 1) + LENGTH(FOR invite IN chatInvites
355
+ FILTER invite.conversationId == @conversationId AND invite.state == 'pending' AND invite.expiresAt > @now RETURN 1)
356
+ }`, {
357
+ conversationId: input.conversationId,
358
+ now: Date.now(),
359
+ personaId: input.personaId
360
+ });
361
+ if (!counts || counts.active || counts.total >= 25) {
362
+ throw new ChatError('conflict');
363
+ }
364
+ await rateLimit(read);
365
+ const result = await addInvite(read, input.conversationId, target);
366
+ await request.save(result);
367
+ return result;
368
+ });
369
+ const resolveInvite = (input, accept)=>transact(async (read)=>{
370
+ await actor(read);
371
+ const request = await receipt(read, accept ? 'accept' : 'decline', input);
372
+ if (request.prior) {
373
+ return request.prior;
374
+ }
375
+ const invite = await read('RETURN DOCUMENT("chatInvites", @id)', {
376
+ id: input.inviteId
377
+ });
378
+ if (!invite || invite.inviteePersonaId !== identity.personaId || invite.inviteeUserId !== identity.userId || invite.state !== 'pending' || invite.expiresAt <= Date.now()) {
379
+ throw new ChatError('forbidden');
380
+ }
381
+ if (invite.revision !== input.expectedRevision) {
382
+ throw new ChatError('conflict');
383
+ }
384
+ const current = await conversation(read, invite.conversationId);
385
+ if (current.kind !== 'group') {
386
+ throw new ChatError('forbidden');
387
+ }
388
+ let result;
389
+ if (accept) {
390
+ const inviter = await member(read, current._key, invite.invitedByPersonaId);
391
+ if (![
392
+ 'owner',
393
+ 'admin'
394
+ ].includes(inviter.role) || current.activeMemberCount >= 25) {
395
+ throw new ChatError('forbidden');
396
+ }
397
+ await adult(read, {
398
+ personaId: inviter.personaId,
399
+ userId: inviter.userId
400
+ });
401
+ await connected(read, {
402
+ personaId: inviter.personaId,
403
+ userId: inviter.userId
404
+ }, identity.personaId);
405
+ const own = await addMember(read, current._key, identity, 'member', current.lastSequence + 1);
406
+ const updated = await read(`UPDATE @id WITH {
407
+ activeMemberCount: @count, membershipRevision: @revision, state: @state}
408
+ IN conversations RETURN NEW`, {
409
+ count: current.activeMemberCount + 1,
410
+ id: current._key,
411
+ revision: current.membershipRevision + 1,
412
+ state: current.activeMemberCount + 1 >= 3 ? 'active' : current.state
413
+ });
414
+ if (!updated) {
415
+ throw new ChatError('unavailable');
416
+ }
417
+ await event(read, current._key, own.epoch, own._key, updated.membershipRevision);
418
+ result = summary(updated, own, audience);
419
+ } else {
420
+ result = {
421
+ ...inviteDto(invite),
422
+ revision: invite.revision + 1,
423
+ state: 'declined'
424
+ };
425
+ await event(read, current._key, '', invite._key, invite.revision + 1);
426
+ }
427
+ await read(`UPDATE @id WITH {currentKey: null, resolvedAt: @now, revision: @revision, state: @state}
428
+ IN chatInvites RETURN true`, {
429
+ id: invite._key,
430
+ now: Date.now(),
431
+ revision: invite.revision + 1,
432
+ state: accept ? 'accepted' : 'declined'
433
+ });
434
+ await request.save(result);
435
+ return result;
436
+ });
437
+ const changeMembership = (action, input)=>transact(async (read)=>{
438
+ await actor(read);
439
+ const request = await receipt(read, action, input);
440
+ if (request.prior) {
441
+ return request.prior;
442
+ }
443
+ const ownAccess = await access(read, input.conversationId);
444
+ checkRevision(ownAccess, input.expectedRevision);
445
+ const current = await conversation(read, input.conversationId);
446
+ const own = await member(read, current._key, identity.personaId);
447
+ const targetPersonaId = 'personaId' in input ? input.personaId : identity.personaId;
448
+ const target = targetPersonaId === identity.personaId ? own : await member(read, current._key, targetPersonaId);
449
+ const leaving = action === 'leave' || action === 'remove';
450
+ if ((action === 'role' || action === 'transfer') && (own.role !== 'owner' || target._key === own._key)) {
451
+ throw new ChatError('forbidden');
452
+ }
453
+ if (action === 'remove' && (target._key === own._key || target.role === 'owner' || ![
454
+ 'owner',
455
+ 'admin'
456
+ ].includes(own.role) || own.role === 'admin' && target.role !== 'member')) {
457
+ throw new ChatError('forbidden');
458
+ }
459
+ if (action === 'leave' && own.role === 'owner' && current.activeMemberCount > 1) {
460
+ throw new ChatError('conflict');
461
+ }
462
+ if (action === 'role' && target.role === 'owner') {
463
+ throw new ChatError('forbidden');
464
+ }
465
+ if (action === 'transfer' || action === 'role' && 'role' in input && input.role === 'admin') {
466
+ await adult(read, {
467
+ personaId: target.personaId,
468
+ userId: target.userId
469
+ });
470
+ }
471
+ let ownResult = own;
472
+ if (action === 'transfer') {
473
+ await read('UPDATE @id WITH {role: "admin", revision: @revision} IN hasChatMembership RETURN true', {
474
+ id: own._key,
475
+ revision: own.revision + 1
476
+ });
477
+ await read('UPDATE @id WITH {role: "owner", revision: @revision} IN hasChatMembership RETURN true', {
478
+ id: target._key,
479
+ revision: target.revision + 1
480
+ });
481
+ ownResult = {
482
+ ...own,
483
+ revision: own.revision + 1,
484
+ role: 'admin'
485
+ };
486
+ } else if (action === 'role' && 'role' in input) {
487
+ await read('UPDATE @id WITH {role: @role, revision: @revision} IN hasChatMembership RETURN true', {
488
+ id: target._key,
489
+ revision: target.revision + 1,
490
+ role: input.role
491
+ });
492
+ } else if (leaving) {
493
+ const state = action === 'leave' ? 'left' : 'removed';
494
+ await read(`UPDATE @id WITH {currentKey: null, endedAt: @now, endedByPersonaId: @actor,
495
+ leftSequence: @sequence, revision: @revision, state: @state} IN hasChatMembership RETURN true`, {
496
+ actor: identity.personaId,
497
+ id: target._key,
498
+ now: Date.now(),
499
+ revision: target.revision + 1,
500
+ sequence: current.lastSequence,
501
+ state
502
+ });
503
+ if (action === 'leave') {
504
+ ownResult = {
505
+ ...own,
506
+ leftSequence: current.lastSequence,
507
+ revision: own.revision + 1,
508
+ state
509
+ };
510
+ }
511
+ await read(`FOR edge IN hasConversation FILTER edge._from == @from AND edge._to == @to
512
+ LET remaining = FIRST(FOR member IN hasChatMembership FILTER member.conversationId == @conversationId
513
+ AND member.userId == @userId AND member.state == 'active' LIMIT 1 RETURN true)
514
+ FILTER remaining != true REMOVE edge IN hasConversation RETURN true`, {
515
+ conversationId: current._key,
516
+ from: `users/${target.userId}`,
517
+ to: `conversations/${current._key}`,
518
+ userId: target.userId
519
+ });
520
+ }
521
+ if (leaving || action === 'role' && 'role' in input && input.role === 'member') {
522
+ await read(`FOR invite IN chatInvites FILTER invite.conversationId == @conversationId
523
+ AND invite.state == 'pending' AND (invite.invitedByPersonaId == @personaId OR @close)
524
+ UPDATE invite WITH {currentKey: null, resolvedAt: @now, revision: invite.revision + 1, state: 'revoked'}
525
+ IN chatInvites RETURN true`, {
526
+ close: leaving && current.activeMemberCount === 1,
527
+ conversationId: current._key,
528
+ now: Date.now(),
529
+ personaId: target.personaId
530
+ });
531
+ }
532
+ const count = current.activeMemberCount - (leaving ? 1 : 0);
533
+ const updated = await read(`UPDATE @id WITH {
534
+ activeMemberCount: @count, membershipRevision: @revision, state: @state}
535
+ IN conversations RETURN NEW`, {
536
+ count,
537
+ id: current._key,
538
+ revision: current.membershipRevision + 1,
539
+ state: count === 0 ? 'closed' : current.state
540
+ });
541
+ if (!updated) {
542
+ throw new ChatError('unavailable');
543
+ }
544
+ await event(read, current._key, target.epoch, target._key, updated.membershipRevision);
545
+ const result = summary(updated, ownResult, audience);
546
+ await request.save(result);
547
+ return result;
548
+ });
549
+ const listConversations = (input)=>transact(async (read)=>{
550
+ await actor(read);
551
+ const ids = await read(`RETURN (FOR member IN hasChatMembership
552
+ FILTER member.personaId == @personaId AND member.userId == @userId AND member.state == 'active'
553
+ AND member.conversationId > @after SORT member.conversationId LIMIT @limit RETURN member.conversationId)`, {
554
+ ...identity,
555
+ after: input.after || '',
556
+ limit: input.limit || 25
557
+ });
558
+ const results = [];
559
+ for (const id of ids || []){
560
+ try {
561
+ await access(read, id);
562
+ const current = await conversation(read, id);
563
+ if (current.kind === 'direct') {
564
+ const peer = await read(`FOR member IN hasChatMembership FILTER member.conversationId == @id
565
+ AND member.personaId != @personaId AND member.state == 'active' LIMIT 1 RETURN member`, {
566
+ id,
567
+ personaId: identity.personaId
568
+ });
569
+ if (!peer) {
570
+ continue;
571
+ }
572
+ await connected(read, identity, peer.personaId);
573
+ }
574
+ results.push(summary(current, await member(read, id, identity.personaId), audience, true));
575
+ } catch (error) {
576
+ if (!(error instanceof ChatError) || error.code !== 'forbidden') {
577
+ throw error;
578
+ }
579
+ }
580
+ }
581
+ return results;
582
+ });
583
+ const listInvites = (input)=>transact(async (read)=>{
584
+ await actor(read);
585
+ const records = await read(`RETURN (FOR invite IN chatInvites
586
+ FILTER invite.inviteePersonaId == @personaId AND invite.inviteeUserId == @userId AND invite.state == 'pending'
587
+ AND invite.expiresAt > @now AND invite._key > @after
588
+ LET conversation = DOCUMENT('conversations', invite.conversationId)
589
+ LET inviter = DOCUMENT('personas', invite.invitedByPersonaId)
590
+ LET inviterUser = DOCUMENT('users', inviter.userId)
591
+ LET inviterMembership = FIRST(FOR member IN hasChatMembership
592
+ FILTER member.conversationId == invite.conversationId AND member.personaId == invite.invitedByPersonaId
593
+ AND member.userId == inviter.userId AND member.state == 'active' AND member.role IN ['owner', 'admin']
594
+ LIMIT 1 RETURN true)
595
+ LET blocked = FIRST(FOR edge IN hasConnection FILTER edge.type == 'block'
596
+ AND ((edge._from IN [CONCAT('personas/', @personaId), CONCAT('users/', @userId)]
597
+ AND edge._to IN [CONCAT('personas/', invite.invitedByPersonaId), CONCAT('users/', inviter.userId)])
598
+ OR (edge._to IN [CONCAT('personas/', @personaId), CONCAT('users/', @userId)]
599
+ AND edge._from IN [CONCAT('personas/', invite.invitedByPersonaId), CONCAT('users/', inviter.userId)]))
600
+ LIMIT 1 RETURN true)
601
+ FILTER conversation.chatSchemaVersion == 1 AND conversation.state IN ['active', 'forming']
602
+ AND inviter != null AND inviterUser != null AND inviter.active != false AND inviterUser.active != false
603
+ AND inviterMembership == true AND blocked != true
604
+ SORT invite._key LIMIT @limit RETURN invite)`, {
605
+ ...identity,
606
+ after: input.after || '',
607
+ limit: input.limit || 25,
608
+ now: Date.now()
609
+ });
610
+ return (records || []).map(inviteDto);
611
+ });
612
+ const getParticipants = (input)=>transact(async (read)=>{
613
+ await actor(read);
614
+ await access(read, input.conversationId);
615
+ const records = await read(`RETURN (FOR member IN hasChatMembership
616
+ FILTER member.conversationId == @conversationId AND member.state == 'active'
617
+ LET persona = DOCUMENT('personas', member.personaId) LET user = DOCUMENT('users', member.userId)
618
+ LET blocked = FIRST(FOR edge IN hasConnection FILTER edge.type == 'block'
619
+ AND ((edge._from IN [@viewerPersona, @viewerUser]
620
+ AND edge._to IN [CONCAT('personas/', member.personaId), CONCAT('users/', member.userId)])
621
+ OR (edge._to IN [@viewerPersona, @viewerUser]
622
+ AND edge._from IN [CONCAT('personas/', member.personaId), CONCAT('users/', member.userId)])) LIMIT 1 RETURN true)
623
+ FILTER persona != null AND user != null AND persona.active != false AND user.active != false
624
+ AND persona.userId == member.userId AND blocked != true
625
+ SORT member.personaId LIMIT 25 RETURN member)`, {
626
+ conversationId: input.conversationId,
627
+ viewerPersona: `personas/${identity.personaId}`,
628
+ viewerUser: `users/${identity.userId}`
629
+ });
630
+ return (records || []).map(membershipDto);
631
+ });
632
+ return {
633
+ acceptInvite: (input)=>resolveInvite(input, true),
634
+ changeMemberRole: (input)=>changeMembership('role', input),
635
+ createGroup,
636
+ declineInvite: (input)=>resolveInvite(input, false),
637
+ getOrCreateDirect,
638
+ getParticipants,
639
+ inviteMember,
640
+ leaveConversation: (input)=>changeMembership('leave', input),
641
+ listConversations,
642
+ listInvites,
643
+ removeMember: (input)=>changeMembership('remove', input),
644
+ transferOwnership: (input)=>changeMembership('transfer', input)
645
+ };
646
+ };
647
+ const valid = (condition)=>{
648
+ if (!condition) {
649
+ throw new ChatError('invalid_request');
650
+ }
651
+ };
652
+ const mutation = (input)=>{
653
+ valid(Boolean(input) && isChatIdentifier(input.idempotencyKey) && Number.isSafeInteger(input.expectedRevision) && input.expectedRevision > 0);
654
+ };
655
+ const conversationMutation = (input)=>{
656
+ mutation(input);
657
+ valid(isChatIdentifier(input.conversationId));
658
+ };
659
+ const targetedMutation = (input)=>{
660
+ conversationMutation(input);
661
+ valid(isChatIdentifier(input.personaId));
662
+ };
663
+ const list = (input)=>valid(Boolean(input) && (input.limit === undefined || Number.isSafeInteger(input.limit) && input.limit > 0 && input.limit <= 50) && (input.after === undefined || isChatIdentifier(input.after)));
664
+ export const createDurableConversationActions = (identity, repository)=>{
665
+ if (!identity || !isChatIdentifier(identity.personaId) || !isChatIdentifier(identity.userId)) {
666
+ throw new ChatError('forbidden');
667
+ }
668
+ return {
669
+ acceptInvite: async (input)=>{
670
+ mutation(input);
671
+ valid(isChatIdentifier(input.inviteId));
672
+ return repository.acceptInvite(input);
673
+ },
674
+ changeMemberRole: async (input)=>{
675
+ targetedMutation(input);
676
+ valid([
677
+ 'admin',
678
+ 'member'
679
+ ].includes(input.role));
680
+ return repository.changeMemberRole(input);
681
+ },
682
+ createGroup: async (input)=>{
683
+ valid(Boolean(input) && isChatIdentifier(input.idempotencyKey) && typeof input.name === 'string' && input.name.trim().length > 0 && input.name.length <= 200 && Array.isArray(input.memberPersonaIds) && input.memberPersonaIds.length >= 2 && input.memberPersonaIds.length <= 24 && input.memberPersonaIds.every((id)=>isChatIdentifier(id) && id !== identity.personaId) && new Set(input.memberPersonaIds).size === input.memberPersonaIds.length);
684
+ return repository.createGroup(input);
685
+ },
686
+ declineInvite: async (input)=>{
687
+ mutation(input);
688
+ valid(isChatIdentifier(input.inviteId));
689
+ return repository.declineInvite(input);
690
+ },
691
+ getOrCreateDirect: async (input)=>{
692
+ valid(Boolean(input) && isChatIdentifier(input.participantPersonaId) && input.participantPersonaId !== identity.personaId);
693
+ return repository.getOrCreateDirect(input);
694
+ },
695
+ getParticipants: async (input)=>{
696
+ valid(Boolean(input) && isChatIdentifier(input.conversationId));
697
+ return repository.getParticipants(input);
698
+ },
699
+ inviteMember: async (input)=>{
700
+ targetedMutation(input);
701
+ valid(input.personaId !== identity.personaId);
702
+ return repository.inviteMember(input);
703
+ },
704
+ leaveConversation: async (input)=>{
705
+ conversationMutation(input);
706
+ return repository.leaveConversation(input);
707
+ },
708
+ listConversations: async (input = {})=>{
709
+ list(input);
710
+ return repository.listConversations(input);
711
+ },
712
+ listInvites: async (input = {})=>{
713
+ list(input);
714
+ return repository.listInvites(input);
715
+ },
716
+ removeMember: async (input)=>{
717
+ targetedMutation(input);
718
+ valid(input.personaId !== identity.personaId);
719
+ return repository.removeMember(input);
720
+ },
721
+ transferOwnership: async (input)=>{
722
+ targetedMutation(input);
723
+ valid(input.personaId !== identity.personaId);
724
+ return repository.transferOwnership(input);
725
+ }
726
+ };
727
+ };
728
+
729
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50Ly5jb2RleC13b3JrdHJlZXMvcmVha3Rvci1waGFudGF6ZS1jaGF0L3BhY2thZ2VzL3JlYWt0b3Ivc3JjL2R1cmFibGVDaGF0L2NvbnZlcnNhdGlvbnMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgbm8tYXdhaXQtaW4tbG9vcCwgbm8tdW5kZXJzY29yZS1kYW5nbGUgLS0gT3JkZXJlZCB0cmFuc2FjdGlvbnMgYW5kIEFyYW5nbyBpZGVudGl0aWVzLiAqL1xuaW1wb3J0IHtjcmVhdGVIYXNoLCByYW5kb21VVUlEfSBmcm9tICdub2RlOmNyeXB0byc7XG5cbmltcG9ydCB7Q2hhdEVycm9yfSBmcm9tICcuL2Vycm9ycy5qcyc7XG5pbXBvcnQge2FwcGVuZENoYXRFdmVudH0gZnJvbSAnLi9ldmVudHMuanMnO1xuaW1wb3J0IHtjcmVhdGVNZW1iZXJzaGlwQXV0aG9yaXplciwgaXNDaGF0SWRlbnRpZmllcn0gZnJvbSAnLi9tZW1iZXJzaGlwLmpzJztcbmltcG9ydCB7Y3JlYXRlRHVyYWJsZVRyYW5zYWN0aW9ufSBmcm9tICcuL3RyYW5zYWN0aW9uLmpzJztcblxuaW1wb3J0IHR5cGUge0FwaUNvbnRleHR9IGZyb20gJy4uL3R5cGVzL2F1dGgudHlwZXMuanMnO1xuaW1wb3J0IHR5cGUge0F1dGhvcml6ZWRNZW1iZXJzaGlwLCBDaGF0SWRlbnRpdHksIENoYXRRdWVyeX0gZnJvbSAnLi9tZW1iZXJzaGlwLmpzJztcbmltcG9ydCB0eXBlIHtcbiAgQWNjZXB0SW52aXRlSW5wdXQsIENoYW5nZU1lbWJlclJvbGVJbnB1dCwgQ29udmVyc2F0aW9uSW52aXRlLCBDb252ZXJzYXRpb25NZW1iZXJzaGlwLFxuICBDb252ZXJzYXRpb25TdW1tYXJ5LCBDcmVhdGVEaXJlY3RJbnB1dCwgQ3JlYXRlR3JvdXBJbnB1dCwgSW52aXRlTWVtYmVySW5wdXQsXG4gIExlYXZlQ29udmVyc2F0aW9uSW5wdXQsIE1lbWJlcnNoaXBSb2xlLCBSZW1vdmVNZW1iZXJJbnB1dCwgVHJhbnNmZXJPd25lcnNoaXBJbnB1dFxufSBmcm9tICcuL3R5cGVzLmpzJztcblxuZXhwb3J0IGludGVyZmFjZSBDb252ZXJzYXRpb25SZWNvcmQge1xuICByZWFkb25seSBfa2V5OiBzdHJpbmc7XG4gIHJlYWRvbmx5IGFjdGl2ZU1lbWJlckNvdW50OiBudW1iZXI7XG4gIHJlYWRvbmx5IGNoYXRTY2hlbWFWZXJzaW9uOiBudW1iZXI7XG4gIHJlYWRvbmx5IGtpbmQ6ICdkaXJlY3QnIHwgJ2dyb3VwJztcbiAgcmVhZG9ubHkgbGFzdFNlcXVlbmNlOiBudW1iZXI7XG4gIHJlYWRvbmx5IG1lbWJlcnNoaXBSZXZpc2lvbjogbnVtYmVyO1xuICByZWFkb25seSBuYW1lPzogc3RyaW5nO1xuICByZWFkb25seSBuYW1lQ29udGVudFZlcnNpb24/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IG5hbWVEZWNpc2lvbj86IHVua25vd247XG4gIHJlYWRvbmx5IHN0YXRlOiAnYWN0aXZlJyB8ICdjbG9zZWQnIHwgJ2Zvcm1pbmcnO1xufVxuaW50ZXJmYWNlIE1lbWJlclJlY29yZCBleHRlbmRzIENvbnZlcnNhdGlvbk1lbWJlcnNoaXAge1xuICByZWFkb25seSBfa2V5OiBzdHJpbmc7XG4gIHJlYWRvbmx5IHVzZXJJZDogc3RyaW5nO1xufVxuaW50ZXJmYWNlIEludml0ZVJlY29yZCBleHRlbmRzIENvbnZlcnNhdGlvbkludml0ZSB7XG4gIHJlYWRvbmx5IF9rZXk6IHN0cmluZztcbiAgcmVhZG9ubHkgaW52aXRlZVBlcnNvbmFJZDogc3RyaW5nO1xuICByZWFkb25seSBpbnZpdGVlVXNlcklkOiBzdHJpbmc7XG59XG5leHBvcnQgaW50ZXJmYWNlIENoYXRMaXN0SW5wdXQge1xuICByZWFkb25seSBhZnRlcj86IHN0cmluZztcbiAgcmVhZG9ubHkgbGltaXQ/OiBudW1iZXI7XG59XG5jb25zdCBoYXNoID0gKGlucHV0OiB1bmtub3duKTogc3RyaW5nID0+IGNyZWF0ZUhhc2goJ3NoYTI1NicpLnVwZGF0ZShKU09OLnN0cmluZ2lmeShpbnB1dCkpLmRpZ2VzdCgnaGV4Jyk7XG5jb25zdCBpbnZpdGVNaWxsaXNlY29uZHMgPSA3ICogMjQgKiA2MCAqIDYwICogMTAwMDtcbmNvbnN0IG1lbWJlcnNoaXBEdG8gPSAocmVjb3JkOiBDb252ZXJzYXRpb25NZW1iZXJzaGlwKTogQ29udmVyc2F0aW9uTWVtYmVyc2hpcCA9PiAoe1xuICBjb252ZXJzYXRpb25JZDogcmVjb3JkLmNvbnZlcnNhdGlvbklkLCBlcG9jaDogcmVjb3JkLmVwb2NoLCBqb2luU2VxdWVuY2U6IHJlY29yZC5qb2luU2VxdWVuY2UsXG4gIGxlZnRTZXF1ZW5jZTogcmVjb3JkLmxlZnRTZXF1ZW5jZSwgcGVyc29uYUlkOiByZWNvcmQucGVyc29uYUlkLCByZXZpc2lvbjogcmVjb3JkLnJldmlzaW9uLFxuICByb2xlOiByZWNvcmQucm9sZSwgc3RhdGU6IHJlY29yZC5zdGF0ZVxufSk7XG5jb25zdCBpbnZpdGVEdG8gPSAocmVjb3JkOiBJbnZpdGVSZWNvcmQpOiBDb252ZXJzYXRpb25JbnZpdGUgPT4gKHtcbiAgY29udmVyc2F0aW9uSWQ6IHJlY29yZC5jb252ZXJzYXRpb25JZCwgZXhwaXJlc0F0OiByZWNvcmQuZXhwaXJlc0F0LCBpbnZpdGVJZDogcmVjb3JkLl9rZXksXG4gIGludml0ZWRCeVBlcnNvbmFJZDogcmVjb3JkLmludml0ZWRCeVBlcnNvbmFJZCwgcmV2aXNpb246IHJlY29yZC5yZXZpc2lvbiwgc3RhdGU6IHJlY29yZC5zdGF0ZVxufSk7XG5leHBvcnQgaW50ZXJmYWNlIER1cmFibGVDb252ZXJzYXRpb25PcHRpb25zIHtcbiAgcmVhZG9ubHkgYXVkaWVuY2U/OiAnbW9iaWxlJyB8ICd3ZWInO1xuICAvKiogQWxsIHBvbGljeSByZWFkcyBtdXN0IHVzZSB0aGlzIHF1ZXJ5OyBleGNlcHRpb25zIGZhaWwgdGhlIHRyYW5zYWN0aW9uLiAqL1xuICByZWFkb25seSBhdXRob3JpemVJZGVudGl0eTogKHJlYWQ6IENoYXRRdWVyeSwgaWRlbnRpdHk6IENoYXRJZGVudGl0eSkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgcmVhZG9ubHkgY2xhc3NpZnlOYW1lOiAobmFtZTogc3RyaW5nLCBjb250ZW50VmVyc2lvbjogc3RyaW5nKSA9PiB1bmtub3duO1xuICByZWFkb25seSBwcm9qZWN0TmFtZTogKGNvbnZlcnNhdGlvbjogQ29udmVyc2F0aW9uUmVjb3JkLCBhdWRpZW5jZTogJ21vYmlsZScgfCAnd2ViJykgPT4gc3RyaW5nIHwgdW5kZWZpbmVkO1xuICByZWFkb25seSByZWFkQ29sbGVjdGlvbnM/OiByZWFkb25seSBzdHJpbmdbXTtcbiAgLyoqIENhbGxlciBvd25zIHRyYW5zYWN0aW9uL2xvY2tzL2NvbW1pdCB3aGVuIHN1cHBsaWVkOyBubyBpbmRlcGVuZGVudCBkYXRhYmFzZSBhY2Nlc3Mgb2NjdXJzLiAqL1xuICByZWFkb25seSBxdWVyeT86IENoYXRRdWVyeTtcbn1cblxuZXhwb3J0IGNvbnN0IGNyZWF0ZUR1cmFibGVDb252ZXJzYXRpb25SZXBvc2l0b3J5ID0gKFxuICBjb250ZXh0OiBBcGlDb250ZXh0LCBpZGVudGl0eTogQ2hhdElkZW50aXR5LCBvcHRpb25zOiBEdXJhYmxlQ29udmVyc2F0aW9uT3B0aW9uc1xuKSA9PiB7XG4gIGNvbnN0IGF1ZGllbmNlID0gb3B0aW9ucy5hdWRpZW5jZSB8fCAnbW9iaWxlJztcbiAgY29uc3QgdHJhbnNhY3QgPSBjcmVhdGVEdXJhYmxlVHJhbnNhY3Rpb24oY29udGV4dCwge2V4Y2x1c2l2ZTogW1xuICAgICdjaGF0SW52aXRlcycsICdjaGF0T3V0Ym94JywgJ2NoYXRSYXRlTGltaXRzJywgJ2NoYXRSZXF1ZXN0cycsICdjb252ZXJzYXRpb25zJywgJ2hhc0NoYXRNZW1iZXJzaGlwJywgJ2hhc0NvbnZlcnNhdGlvbidcbiAgXSwgcmVhZDogWydoYXNDb25uZWN0aW9uJywgJ3BlcnNvbmFzJywgJ3VzZXJzJywgLi4uKG9wdGlvbnMucmVhZENvbGxlY3Rpb25zIHx8IFtdKV19LCBvcHRpb25zLnF1ZXJ5KTtcbiAgY29uc3Qgc3VtbWFyeSA9IChjb252ZXJzYXRpb246IENvbnZlcnNhdGlvblJlY29yZCwgbWVtYmVyOiBDb252ZXJzYXRpb25NZW1iZXJzaGlwLFxuICAgIGF1ZGllbmNlOiAnbW9iaWxlJyB8ICd3ZWInLCBpbmNsdWRlTmFtZSA9IGZhbHNlKTogQ29udmVyc2F0aW9uU3VtbWFyeSA9PiAoe1xuICAgIGFjdGl2ZU1lbWJlckNvdW50OiBjb252ZXJzYXRpb24uYWN0aXZlTWVtYmVyQ291bnQsIGNvbnZlcnNhdGlvbklkOiBjb252ZXJzYXRpb24uX2tleSxcbiAgICBraW5kOiBjb252ZXJzYXRpb24ua2luZCwgbWVtYmVyc2hpcDogbWVtYmVyc2hpcER0byhtZW1iZXIpLCBtZW1iZXJzaGlwUmV2aXNpb246IGNvbnZlcnNhdGlvbi5tZW1iZXJzaGlwUmV2aXNpb24sXG4gICAgbmFtZTogaW5jbHVkZU5hbWUgPyBvcHRpb25zLnByb2plY3ROYW1lKGNvbnZlcnNhdGlvbiwgYXVkaWVuY2UpIDogdW5kZWZpbmVkLCBzdGF0ZTogY29udmVyc2F0aW9uLnN0YXRlXG4gIH0pO1xuICBjb25zdCBhY3RvciA9IGFzeW5jIChyZWFkOiBDaGF0UXVlcnkpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBjb25zdCBhbGxvd2VkID0gYXdhaXQgcmVhZDxib29sZWFuPihgTEVUIHBlcnNvbmEgPSBET0NVTUVOVCgncGVyc29uYXMnLCBAcGVyc29uYUlkKVxuICAgICAgTEVUIHVzZXIgPSBET0NVTUVOVCgndXNlcnMnLCBAdXNlcklkKVxuICAgICAgUkVUVVJOIHBlcnNvbmEgIT0gbnVsbCBBTkQgcGVyc29uYS51c2VySWQgPT0gQHVzZXJJZCBBTkQgcGVyc29uYS5hY3RpdmUgIT0gZmFsc2VcbiAgICAgICAgQU5EIHVzZXIgIT0gbnVsbCBBTkQgdXNlci5hY3RpdmUgIT0gZmFsc2VgLCB7Li4uaWRlbnRpdHl9KTtcbiAgICBpZihhbGxvd2VkICE9PSB0cnVlKSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdmb3JiaWRkZW4nKTtcbiAgICB9XG4gICAgYXdhaXQgYWR1bHQocmVhZCwgaWRlbnRpdHkpO1xuICB9O1xuICBjb25zdCBhZHVsdCA9IChyZWFkOiBDaGF0UXVlcnksIHdobzogQ2hhdElkZW50aXR5KTogUHJvbWlzZTx2b2lkPiA9PiBvcHRpb25zLmF1dGhvcml6ZUlkZW50aXR5KHJlYWQsIHdobyk7XG4gIGNvbnN0IGNvbm5lY3RlZCA9IGFzeW5jIChyZWFkOiBDaGF0UXVlcnksIGZyb206IENoYXRJZGVudGl0eSwgcGVyc29uYUlkOiBzdHJpbmcpOiBQcm9taXNlPENoYXRJZGVudGl0eT4gPT4ge1xuICAgIGNvbnN0IHRhcmdldCA9IGF3YWl0IHJlYWQ8Q2hhdElkZW50aXR5PihgTEVUIHBlcnNvbmEgPSBET0NVTUVOVCgncGVyc29uYXMnLCBAcGVyc29uYUlkKVxuICAgICAgTEVUIHVzZXIgPSBET0NVTUVOVCgndXNlcnMnLCBwZXJzb25hLnVzZXJJZClcbiAgICAgIExFVCBmb2xsb3dpbmcgPSBGSVJTVChGT1IgZWRnZSBJTiBoYXNDb25uZWN0aW9uIEZJTFRFUiBlZGdlLl9mcm9tID09IEBmcm9tIEFORCBlZGdlLl90byA9PSBAdG9cbiAgICAgICAgQU5EIGVkZ2UudHlwZSA9PSAnZm9sbG93JyBBTkQgZWRnZS5hY3RpdmUgIT0gZmFsc2UgTElNSVQgMSBSRVRVUk4gdHJ1ZSlcbiAgICAgIExFVCBmb2xsb3dlZCA9IEZJUlNUKEZPUiBlZGdlIElOIGhhc0Nvbm5lY3Rpb24gRklMVEVSIGVkZ2UuX3RvID09IEBmcm9tIEFORCBlZGdlLl9mcm9tID09IEB0b1xuICAgICAgICBBTkQgZWRnZS50eXBlID09ICdmb2xsb3cnIEFORCBlZGdlLmFjdGl2ZSAhPSBmYWxzZSBMSU1JVCAxIFJFVFVSTiB0cnVlKVxuICAgICAgTEVUIGJsb2NrZWQgPSBGSVJTVChGT1IgZWRnZSBJTiBoYXNDb25uZWN0aW9uIEZJTFRFUiBlZGdlLnR5cGUgPT0gJ2Jsb2NrJ1xuICAgICAgICBBTkQgKChlZGdlLl9mcm9tIElOIFtAZnJvbSwgQGZyb21Vc2VyXSBBTkQgZWRnZS5fdG8gSU4gW0B0bywgQ09OQ0FUKCd1c2Vycy8nLCBwZXJzb25hLnVzZXJJZCldKVxuICAgICAgICAgIE9SIChlZGdlLl90byBJTiBbQGZyb20sIEBmcm9tVXNlcl0gQU5EIGVkZ2UuX2Zyb20gSU4gW0B0bywgQ09OQ0FUKCd1c2Vycy8nLCBwZXJzb25hLnVzZXJJZCldKSkgTElNSVQgMSBSRVRVUk4gdHJ1ZSlcbiAgICAgIEZJTFRFUiBwZXJzb25hICE9IG51bGwgQU5EIHVzZXIgIT0gbnVsbCBBTkQgcGVyc29uYS5hY3RpdmUgIT0gZmFsc2UgQU5EIHVzZXIuYWN0aXZlICE9IGZhbHNlXG4gICAgICAgIEFORCBwZXJzb25hLnVzZXJJZCAhPSBAYWN0b3JVc2VyIEFORCBmb2xsb3dpbmcgPT0gdHJ1ZSBBTkQgZm9sbG93ZWQgPT0gdHJ1ZSBBTkQgYmxvY2tlZCAhPSB0cnVlXG4gICAgICBSRVRVUk4ge3BlcnNvbmFJZDogQHBlcnNvbmFJZCwgdXNlcklkOiBwZXJzb25hLnVzZXJJZH1gLCB7XG4gICAgICBhY3RvclVzZXI6IGZyb20udXNlcklkLCBmcm9tOiBgcGVyc29uYXMvJHtmcm9tLnBlcnNvbmFJZH1gLCBmcm9tVXNlcjogYHVzZXJzLyR7ZnJvbS51c2VySWR9YCxcbiAgICAgIHBlcnNvbmFJZCwgdG86IGBwZXJzb25hcy8ke3BlcnNvbmFJZH1gXG4gICAgfSk7XG4gICAgaWYoIXRhcmdldCkge1xuICAgICAgdGhyb3cgbmV3IENoYXRFcnJvcignZm9yYmlkZGVuJyk7XG4gICAgfVxuICAgIGF3YWl0IGFkdWx0KHJlYWQsIHRhcmdldCk7XG4gICAgcmV0dXJuIHRhcmdldDtcbiAgfTtcbiAgY29uc3QgY29udmVyc2F0aW9uID0gYXN5bmMgKHJlYWQ6IENoYXRRdWVyeSwgaWQ6IHN0cmluZyk6IFByb21pc2U8Q29udmVyc2F0aW9uUmVjb3JkPiA9PiB7XG4gICAgY29uc3QgdmFsdWUgPSBhd2FpdCByZWFkPENvbnZlcnNhdGlvblJlY29yZD4oJ1JFVFVSTiBET0NVTUVOVChcImNvbnZlcnNhdGlvbnNcIiwgQGlkKScsIHtpZH0pO1xuICAgIGlmKCF2YWx1ZSB8fCB2YWx1ZS5jaGF0U2NoZW1hVmVyc2lvbiAhPT0gMSB8fCB2YWx1ZS5zdGF0ZSA9PT0gJ2Nsb3NlZCcpIHtcbiAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ2ZvcmJpZGRlbicpO1xuICAgIH1cbiAgICByZXR1cm4gdmFsdWU7XG4gIH07XG4gIGNvbnN0IG1lbWJlciA9IGFzeW5jIChyZWFkOiBDaGF0UXVlcnksIGNvbnZlcnNhdGlvbklkOiBzdHJpbmcsIHBlcnNvbmFJZDogc3RyaW5nKTogUHJvbWlzZTxNZW1iZXJSZWNvcmQ+ID0+IHtcbiAgICBjb25zdCB2YWx1ZSA9IGF3YWl0IHJlYWQ8TWVtYmVyUmVjb3JkPihgRk9SIG1lbWJlciBJTiBoYXNDaGF0TWVtYmVyc2hpcCBGSUxURVIgbWVtYmVyLmNvbnZlcnNhdGlvbklkID09IEBjb252ZXJzYXRpb25JZFxuICAgICAgQU5EIG1lbWJlci5wZXJzb25hSWQgPT0gQHBlcnNvbmFJZCBBTkQgbWVtYmVyLnN0YXRlID09ICdhY3RpdmUnIExJTUlUIDEgUkVUVVJOIG1lbWJlcmAsIHtjb252ZXJzYXRpb25JZCwgcGVyc29uYUlkfSk7XG4gICAgaWYoIXZhbHVlKSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdmb3JiaWRkZW4nKTtcbiAgICB9XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9O1xuICBjb25zdCBhY2Nlc3MgPSAocmVhZDogQ2hhdFF1ZXJ5LCBjb252ZXJzYXRpb25JZDogc3RyaW5nLCBwdXJwb3NlOiAnaW52aXRlJyB8ICdwYXJ0aWNpcGFudHMnID0gJ3BhcnRpY2lwYW50cycpID0+XG4gICAgY3JlYXRlTWVtYmVyc2hpcEF1dGhvcml6ZXIocmVhZCkoaWRlbnRpdHksIHtjb252ZXJzYXRpb25JZCwgcHVycG9zZX0pO1xuICBjb25zdCBjaGVja1JldmlzaW9uID0gKGVudHJ5OiBBdXRob3JpemVkTWVtYmVyc2hpcCwgZXhwZWN0ZWQ6IG51bWJlcik6IHZvaWQgPT4ge1xuICAgIGlmKGVudHJ5Lm1lbWJlcnNoaXBSZXZpc2lvbiAhPT0gZXhwZWN0ZWQgfHwgZW50cnkua2luZCAhPT0gJ2dyb3VwJykge1xuICAgICAgdGhyb3cgbmV3IENoYXRFcnJvcignY29uZmxpY3QnKTtcbiAgICB9XG4gIH07XG4gIGNvbnN0IGV2ZW50ID0gYXN5bmMgKFxuICAgIHJlYWQ6IENoYXRRdWVyeSwgY29udmVyc2F0aW9uSWQ6IHN0cmluZywgZXBvY2g6IHN0cmluZywgcmVzb3VyY2VJZDogc3RyaW5nLCByZXZpc2lvbjogbnVtYmVyXG4gICkgPT4ge1xuICAgIGF3YWl0IGFwcGVuZENoYXRFdmVudChyZWFkLCB7YWN0b3JQZXJzb25hSWQ6IGlkZW50aXR5LnBlcnNvbmFJZCwgY29udmVyc2F0aW9uSWQsIGtpbmQ6ICdtZW1iZXJzaGlwLmNoYW5nZWQnLFxuICAgICAgLi4uKGVwb2NoID8ge21lbWJlcnNoaXBFcG9jaDogZXBvY2h9IDoge30pLCByZXNvdXJjZUlkLCByZXZpc2lvbn0pO1xuICB9O1xuICBjb25zdCByZWNlaXB0ID0gYXN5bmMgPFQ+KHJlYWQ6IENoYXRRdWVyeSwgYWN0aW9uOiBzdHJpbmcsIGlucHV0OiB7cmVhZG9ubHkgaWRlbXBvdGVuY3lLZXk6IHN0cmluZ30pID0+IHtcbiAgICBjb25zdCBrZXkgPSBoYXNoKFtpZGVudGl0eS51c2VySWQsIGlkZW50aXR5LnBlcnNvbmFJZCwgYWN0aW9uLCBpbnB1dC5pZGVtcG90ZW5jeUtleV0pO1xuICAgIGNvbnN0IHBheWxvYWRIYXNoID0gaGFzaChpbnB1dCk7XG4gICAgY29uc3QgcHJpb3IgPSBhd2FpdCByZWFkPHtyZWFkb25seSBwYXlsb2FkSGFzaDogc3RyaW5nOyByZWFkb25seSByZXN1bHQ6IFR9PignUkVUVVJOIERPQ1VNRU5UKFwiY2hhdFJlcXVlc3RzXCIsIEBrZXkpJywge2tleX0pO1xuICAgIGlmKHByaW9yICYmIHByaW9yLnBheWxvYWRIYXNoICE9PSBwYXlsb2FkSGFzaCkge1xuICAgICAgdGhyb3cgbmV3IENoYXRFcnJvcignY29uZmxpY3QnKTtcbiAgICB9XG4gICAgcmV0dXJuIHtwcmlvcjogcHJpb3I/LnJlc3VsdCwgc2F2ZTogKHJlc3VsdDogVCkgPT4gcmVhZCgnSU5TRVJUIEByZWNvcmQgSU4gY2hhdFJlcXVlc3RzIFJFVFVSTiB0cnVlJywge1xuICAgICAgcmVjb3JkOiB7X2tleToga2V5LCBhZGRlZDogRGF0ZS5ub3coKSwgb3duZXJVc2VySWQ6IGlkZW50aXR5LnVzZXJJZCwgcGF5bG9hZEhhc2gsXG4gICAgICAgIHBlcnNvbmFJZDogaWRlbnRpdHkucGVyc29uYUlkLCByZXN1bHR9XG4gICAgfSl9O1xuICB9O1xuICBjb25zdCByYXRlTGltaXQgPSBhc3luYyAocmVhZDogQ2hhdFF1ZXJ5KTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgY29uc3Qga2V5ID0gaGFzaChbaWRlbnRpdHkudXNlcklkLCBNYXRoLmZsb29yKERhdGUubm93KCkgLyAzNjAwMDAwKV0pO1xuICAgIGNvbnN0IGFsbG93ZWQgPSBhd2FpdCByZWFkPGJvb2xlYW4+KGBMRVQgYnVja2V0ID0gRE9DVU1FTlQoJ2NoYXRSYXRlTGltaXRzJywgQGtleSlcbiAgICAgIEZJTFRFUiBidWNrZXQgPT0gbnVsbCBPUiBidWNrZXQuY291bnQgPCA1MFxuICAgICAgVVBTRVJUIHtfa2V5OiBAa2V5fSBJTlNFUlQge19rZXk6IEBrZXksIGNvdW50OiAxLCB1c2VySWQ6IEB1c2VySWR9IFVQREFURSB7Y291bnQ6IE9MRC5jb3VudCArIDEsIHVzZXJJZDogQHVzZXJJZH1cbiAgICAgIElOIGNoYXRSYXRlTGltaXRzIFJFVFVSTiB0cnVlYCwge2tleSwgdXNlcklkOiBpZGVudGl0eS51c2VySWR9KTtcbiAgICBpZihhbGxvd2VkICE9PSB0cnVlKSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdyYXRlX2xpbWl0ZWQnKTtcbiAgICB9XG4gIH07XG4gIGNvbnN0IGFkZE1lbWJlciA9IGFzeW5jIChcbiAgICByZWFkOiBDaGF0UXVlcnksIGNvbnZlcnNhdGlvbklkOiBzdHJpbmcsIHdobzogQ2hhdElkZW50aXR5LCByb2xlOiBNZW1iZXJzaGlwUm9sZSwgam9pblNlcXVlbmNlOiBudW1iZXJcbiAgKTogUHJvbWlzZTxNZW1iZXJSZWNvcmQ+ID0+IHtcbiAgICBjb25zdCBpZCA9IHJhbmRvbVVVSUQoKTtcbiAgICBjb25zdCBldmVudFNlcXVlbmNlID0gYXdhaXQgcmVhZDxudW1iZXI+KFxuICAgICAgJ1JFVFVSTiBET0NVTUVOVChcImNvbnZlcnNhdGlvbnNcIiwgQGNvbnZlcnNhdGlvbklkKS5sYXN0RXZlbnRTZXF1ZW5jZSB8fCAwJywge2NvbnZlcnNhdGlvbklkfSk7XG4gICAgY29uc3QgcmVjb3JkID0ge19mcm9tOiBgcGVyc29uYXMvJHt3aG8ucGVyc29uYUlkfWAsIF9rZXk6IGlkLCBfdG86IGBjb252ZXJzYXRpb25zLyR7Y29udmVyc2F0aW9uSWR9YCxcbiAgICAgIGFjY2VwdGVkQXQ6IERhdGUubm93KCksIGNvbnZlcnNhdGlvbklkLCBjdXJyZW50S2V5OiBgJHtjb252ZXJzYXRpb25JZH06JHt3aG8ucGVyc29uYUlkfWAsIGVwb2NoOiByYW5kb21VVUlEKCksXG4gICAgICBqb2luRXZlbnRTZXF1ZW5jZTogam9pblNlcXVlbmNlID4gMCA/IChldmVudFNlcXVlbmNlIHx8IDApICsgMSA6IDAsIGpvaW5TZXF1ZW5jZSwgcGVyc29uYUlkOiB3aG8ucGVyc29uYUlkLCByZXZpc2lvbjogMSwgcm9sZSwgc3RhdGU6ICdhY3RpdmUnIGFzIGNvbnN0LCB1c2VySWQ6IHdoby51c2VySWR9O1xuICAgIGF3YWl0IHJlYWQoJ0lOU0VSVCBAcmVjb3JkIElOIGhhc0NoYXRNZW1iZXJzaGlwIFJFVFVSTiB0cnVlJywge3JlY29yZH0pO1xuICAgIGF3YWl0IHJlYWQoYFVQU0VSVCB7X2Zyb206IEBmcm9tLCBfdG86IEB0b31cbiAgICAgIElOU0VSVCB7X2Zyb206IEBmcm9tLCBfdG86IEB0bywgYWRkZWQ6IEBub3csIGNvbnZlcnNhdGlvbklkOiBAY29udmVyc2F0aW9uSWQsIHVzZXJJZDogQHVzZXJJZH1cbiAgICAgIFVQREFURSB7fSBJTiBoYXNDb252ZXJzYXRpb24gUkVUVVJOIHRydWVgLCB7XG4gICAgICBjb252ZXJzYXRpb25JZCwgZnJvbTogYHVzZXJzLyR7d2hvLnVzZXJJZH1gLCBub3c6IERhdGUubm93KCksIHRvOiBgY29udmVyc2F0aW9ucy8ke2NvbnZlcnNhdGlvbklkfWAsIHVzZXJJZDogd2hvLnVzZXJJZFxuICAgIH0pO1xuICAgIHJldHVybiByZWNvcmQ7XG4gIH07XG4gIGNvbnN0IGFkZEludml0ZSA9IGFzeW5jIChyZWFkOiBDaGF0UXVlcnksIGNvbnZlcnNhdGlvbklkOiBzdHJpbmcsIHdobzogQ2hhdElkZW50aXR5KTogUHJvbWlzZTxDb252ZXJzYXRpb25JbnZpdGU+ID0+IHtcbiAgICBjb25zdCBpZCA9IHJhbmRvbVVVSUQoKTtcbiAgICBjb25zdCByZWNvcmQ6IEludml0ZVJlY29yZCA9IHtfa2V5OiBpZCwgY29udmVyc2F0aW9uSWQsIGV4cGlyZXNBdDogRGF0ZS5ub3coKSArIGludml0ZU1pbGxpc2Vjb25kcyxcbiAgICAgIGludml0ZUlkOiBpZCwgaW52aXRlZEJ5UGVyc29uYUlkOiBpZGVudGl0eS5wZXJzb25hSWQsIGludml0ZWVQZXJzb25hSWQ6IHdoby5wZXJzb25hSWQsIGludml0ZWVVc2VySWQ6IHdoby51c2VySWQsIHJldmlzaW9uOiAxLCBzdGF0ZTogJ3BlbmRpbmcnfTtcbiAgICBhd2FpdCByZWFkKCdJTlNFUlQgQHJlY29yZCBJTiBjaGF0SW52aXRlcyBSRVRVUk4gdHJ1ZScsIHtyZWNvcmQ6IHsuLi5yZWNvcmQsXG4gICAgICBjcmVhdGVkQXQ6IERhdGUubm93KCksIGN1cnJlbnRLZXk6IGAke2NvbnZlcnNhdGlvbklkfToke3doby5wZXJzb25hSWR9YCwgcm9sZTogJ21lbWJlcid9fSk7XG4gICAgYXdhaXQgZXZlbnQocmVhZCwgY29udmVyc2F0aW9uSWQsICcnLCBpZCwgMSk7XG4gICAgcmV0dXJuIGludml0ZUR0byhyZWNvcmQpO1xuICB9O1xuICBjb25zdCBleHBpcmVJbnZpdGVzID0gYXN5bmMgKHJlYWQ6IENoYXRRdWVyeSwgY29udmVyc2F0aW9uSWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgIGF3YWl0IHJlYWQoYEZPUiBpbnZpdGUgSU4gY2hhdEludml0ZXMgRklMVEVSIGludml0ZS5jb252ZXJzYXRpb25JZCA9PSBAY29udmVyc2F0aW9uSWRcbiAgICAgIEFORCBpbnZpdGUuc3RhdGUgPT0gJ3BlbmRpbmcnIEFORCBpbnZpdGUuZXhwaXJlc0F0IDw9IEBub3dcbiAgICAgIFVQREFURSBpbnZpdGUgV0lUSCB7Y3VycmVudEtleTogbnVsbCwgcmVzb2x2ZWRBdDogQG5vdywgcmV2aXNpb246IGludml0ZS5yZXZpc2lvbiArIDEsIHN0YXRlOiAnZXhwaXJlZCd9XG4gICAgICBJTiBjaGF0SW52aXRlcyBSRVRVUk4gdHJ1ZWAsIHtjb252ZXJzYXRpb25JZCwgbm93OiBEYXRlLm5vdygpfSk7XG4gIH07XG4gIGNvbnN0IGdldE9yQ3JlYXRlRGlyZWN0ID0gKGlucHV0OiBDcmVhdGVEaXJlY3RJbnB1dCk6IFByb21pc2U8Q29udmVyc2F0aW9uU3VtbWFyeT4gPT4gdHJhbnNhY3QoYXN5bmMgKHJlYWQpID0+IHtcbiAgICBhd2FpdCBhY3RvcihyZWFkKTtcbiAgICBjb25zdCB0YXJnZXQgPSBhd2FpdCBjb25uZWN0ZWQocmVhZCwgaWRlbnRpdHksIGlucHV0LnBhcnRpY2lwYW50UGVyc29uYUlkKTtcbiAgICBjb25zdCBwYWlyID0gaGFzaChbJ2RpcmVjdC12MScsIC4uLltpZGVudGl0eS5wZXJzb25hSWQsIHRhcmdldC5wZXJzb25hSWRdLnNvcnQoKV0pO1xuICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgcmVhZDxDb252ZXJzYXRpb25SZWNvcmQ+KCdGT1IgYyBJTiBjb252ZXJzYXRpb25zIEZJTFRFUiBjLmRpcmVjdFBhaXJLZXkgPT0gQHBhaXIgTElNSVQgMSBSRVRVUk4gYycsIHtwYWlyfSk7XG4gICAgaWYoZXhpc3RpbmcpIHtcbiAgICAgIGF3YWl0IGFjY2VzcyhyZWFkLCBleGlzdGluZy5fa2V5KTtcbiAgICAgIHJldHVybiBzdW1tYXJ5KGV4aXN0aW5nLCBhd2FpdCBtZW1iZXIocmVhZCwgZXhpc3RpbmcuX2tleSwgaWRlbnRpdHkucGVyc29uYUlkKSwgYXVkaWVuY2UpO1xuICAgIH1cbiAgICBhd2FpdCByYXRlTGltaXQocmVhZCk7XG4gICAgY29uc3QgaWQgPSBgZGlyZWN0LSR7cGFpcn1gO1xuICAgIGNvbnN0IHJlY29yZDogQ29udmVyc2F0aW9uUmVjb3JkID0ge19rZXk6IGlkLCBhY3RpdmVNZW1iZXJDb3VudDogMiwgY2hhdFNjaGVtYVZlcnNpb246IDEsXG4gICAgICBraW5kOiAnZGlyZWN0JywgbGFzdFNlcXVlbmNlOiAwLCBtZW1iZXJzaGlwUmV2aXNpb246IDEsIHN0YXRlOiAnYWN0aXZlJ307XG4gICAgYXdhaXQgcmVhZCgnSU5TRVJUIEByZWNvcmQgSU4gY29udmVyc2F0aW9ucyBSRVRVUk4gdHJ1ZScsIHtyZWNvcmQ6IHsuLi5yZWNvcmQsXG4gICAgICBhZGRlZDogRGF0ZS5ub3coKSwgY3JlYXRlZEJ5UGVyc29uYUlkOiBpZGVudGl0eS5wZXJzb25hSWQsIGRpcmVjdFBhaXJLZXk6IHBhaXIsIGlzRGlyZWN0OiB0cnVlfX0pO1xuICAgIGNvbnN0IG93biA9IGF3YWl0IGFkZE1lbWJlcihyZWFkLCBpZCwgaWRlbnRpdHksICdtZW1iZXInLCAwKTtcbiAgICBhd2FpdCBhZGRNZW1iZXIocmVhZCwgaWQsIHRhcmdldCwgJ21lbWJlcicsIDApO1xuICAgIGF3YWl0IGV2ZW50KHJlYWQsIGlkLCBvd24uZXBvY2gsIGlkLCAxKTtcbiAgICByZXR1cm4gc3VtbWFyeShyZWNvcmQsIG93biwgYXVkaWVuY2UpO1xuICB9KTtcbiAgY29uc3QgY3JlYXRlR3JvdXAgPSAoaW5wdXQ6IENyZWF0ZUdyb3VwSW5wdXQpOiBQcm9taXNlPENvbnZlcnNhdGlvblN1bW1hcnk+ID0+IHRyYW5zYWN0KGFzeW5jIChyZWFkKSA9PiB7XG4gICAgYXdhaXQgYWN0b3IocmVhZCk7XG4gICAgY29uc3QgcmVxdWVzdCA9IGF3YWl0IHJlY2VpcHQ8Q29udmVyc2F0aW9uU3VtbWFyeT4ocmVhZCwgJ2NyZWF0ZUdyb3VwJywgaW5wdXQpO1xuICAgIGlmKHJlcXVlc3QucHJpb3IpIHtcbiAgICAgIHJldHVybiByZXF1ZXN0LnByaW9yO1xuICAgIH1cbiAgICBhd2FpdCByYXRlTGltaXQocmVhZCk7XG4gICAgY29uc3QgdGFyZ2V0czogQ2hhdElkZW50aXR5W10gPSBbXTtcbiAgICBmb3IoY29uc3QgcGVyc29uYUlkIG9mIGlucHV0Lm1lbWJlclBlcnNvbmFJZHMpIHtcbiAgICAgIHRhcmdldHMucHVzaChhd2FpdCBjb25uZWN0ZWQocmVhZCwgaWRlbnRpdHksIHBlcnNvbmFJZCkpO1xuICAgIH1cbiAgICBjb25zdCBpZCA9IHJhbmRvbVVVSUQoKTtcbiAgICBjb25zdCBjb250ZW50VmVyc2lvbiA9IGhhc2goWydncm91cC1uYW1lJywgaW5wdXQubmFtZV0pO1xuICAgIGNvbnN0IHJlY29yZDogQ29udmVyc2F0aW9uUmVjb3JkID0ge19rZXk6IGlkLCBhY3RpdmVNZW1iZXJDb3VudDogMSwgY2hhdFNjaGVtYVZlcnNpb246IDEsIGtpbmQ6ICdncm91cCcsXG4gICAgICBsYXN0U2VxdWVuY2U6IDAsIG1lbWJlcnNoaXBSZXZpc2lvbjogMSwgbmFtZTogaW5wdXQubmFtZSwgbmFtZUNvbnRlbnRWZXJzaW9uOiBjb250ZW50VmVyc2lvbixcbiAgICAgIG5hbWVEZWNpc2lvbjogb3B0aW9ucy5jbGFzc2lmeU5hbWUoaW5wdXQubmFtZSwgY29udGVudFZlcnNpb24pLCBzdGF0ZTogJ2Zvcm1pbmcnfTtcbiAgICBhd2FpdCByZWFkKCdJTlNFUlQgQHJlY29yZCBJTiBjb252ZXJzYXRpb25zIFJFVFVSTiB0cnVlJywge3JlY29yZDogey4uLnJlY29yZCxcbiAgICAgIGFkZGVkOiBEYXRlLm5vdygpLCBjcmVhdGVkQnlQZXJzb25hSWQ6IGlkZW50aXR5LnBlcnNvbmFJZCwgaXNEaXJlY3Q6IGZhbHNlfX0pO1xuICAgIGNvbnN0IG93biA9IGF3YWl0IGFkZE1lbWJlcihyZWFkLCBpZCwgaWRlbnRpdHksICdvd25lcicsIDApO1xuICAgIGZvcihjb25zdCB0YXJnZXQgb2YgdGFyZ2V0cykge1xuICAgICAgYXdhaXQgYWRkSW52aXRlKHJlYWQsIGlkLCB0YXJnZXQpO1xuICAgIH1cbiAgICBhd2FpdCBldmVudChyZWFkLCBpZCwgb3duLmVwb2NoLCBpZCwgMSk7XG4gICAgY29uc3QgcmVzdWx0ID0gc3VtbWFyeShyZWNvcmQsIG93biwgYXVkaWVuY2UpO1xuICAgIGF3YWl0IHJlcXVlc3Quc2F2ZShyZXN1bHQpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH0pO1xuICBjb25zdCBpbnZpdGVNZW1iZXIgPSAoaW5wdXQ6IEludml0ZU1lbWJlcklucHV0KTogUHJvbWlzZTxDb252ZXJzYXRpb25JbnZpdGU+ID0+IHRyYW5zYWN0KGFzeW5jIChyZWFkKSA9PiB7XG4gICAgYXdhaXQgYWN0b3IocmVhZCk7XG4gICAgY29uc3QgcmVxdWVzdCA9IGF3YWl0IHJlY2VpcHQ8Q29udmVyc2F0aW9uSW52aXRlPihyZWFkLCAnaW52aXRlJywgaW5wdXQpO1xuICAgIGlmKHJlcXVlc3QucHJpb3IpIHtcbiAgICAgIHJldHVybiByZXF1ZXN0LnByaW9yO1xuICAgIH1cbiAgICBjb25zdCBvd24gPSBhd2FpdCBhY2Nlc3MocmVhZCwgaW5wdXQuY29udmVyc2F0aW9uSWQsICdpbnZpdGUnKTtcbiAgICBjaGVja1JldmlzaW9uKG93biwgaW5wdXQuZXhwZWN0ZWRSZXZpc2lvbik7XG4gICAgY29uc3QgdGFyZ2V0ID0gYXdhaXQgY29ubmVjdGVkKHJlYWQsIGlkZW50aXR5LCBpbnB1dC5wZXJzb25hSWQpO1xuICAgIGF3YWl0IGV4cGlyZUludml0ZXMocmVhZCwgaW5wdXQuY29udmVyc2F0aW9uSWQpO1xuICAgIGNvbnN0IHByaW9yID0gYXdhaXQgcmVhZDxJbnZpdGVSZWNvcmQ+KCdGT1IgaW52aXRlIElOIGNoYXRJbnZpdGVzIEZJTFRFUiBpbnZpdGUuY3VycmVudEtleSA9PSBAa2V5IExJTUlUIDEgUkVUVVJOIGludml0ZScsXG4gICAgICB7a2V5OiBgJHtpbnB1dC5jb252ZXJzYXRpb25JZH06JHtpbnB1dC5wZXJzb25hSWR9YH0pO1xuICAgIGlmKHByaW9yKSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBpbnZpdGVEdG8ocHJpb3IpO1xuICAgICAgYXdhaXQgcmVxdWVzdC5zYXZlKHJlc3VsdCk7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cbiAgICBjb25zdCBjb3VudHMgPSBhd2FpdCByZWFkPHtyZWFkb25seSBhY3RpdmU6IGJvb2xlYW47IHJlYWRvbmx5IHRvdGFsOiBudW1iZXJ9PihgUkVUVVJOIHtcbiAgICAgIGFjdGl2ZTogTEVOR1RIKEZPUiBtZW1iZXIgSU4gaGFzQ2hhdE1lbWJlcnNoaXAgRklMVEVSIG1lbWJlci5jb252ZXJzYXRpb25JZCA9PSBAY29udmVyc2F0aW9uSWRcbiAgICAgICAgQU5EIG1lbWJlci5wZXJzb25hSWQgPT0gQHBlcnNvbmFJZCBBTkQgbWVtYmVyLnN0YXRlID09ICdhY3RpdmUnIFJFVFVSTiAxKSA+IDAsXG4gICAgICB0b3RhbDogTEVOR1RIKEZPUiBtZW1iZXIgSU4gaGFzQ2hhdE1lbWJlcnNoaXAgRklMVEVSIG1lbWJlci5jb252ZXJzYXRpb25JZCA9PSBAY29udmVyc2F0aW9uSWRcbiAgICAgICAgQU5EIG1lbWJlci5zdGF0ZSA9PSAnYWN0aXZlJyBSRVRVUk4gMSkgKyBMRU5HVEgoRk9SIGludml0ZSBJTiBjaGF0SW52aXRlc1xuICAgICAgICBGSUxURVIgaW52aXRlLmNvbnZlcnNhdGlvbklkID09IEBjb252ZXJzYXRpb25JZCBBTkQgaW52aXRlLnN0YXRlID09ICdwZW5kaW5nJyBBTkQgaW52aXRlLmV4cGlyZXNBdCA+IEBub3cgUkVUVVJOIDEpXG4gICAgfWAsIHtjb252ZXJzYXRpb25JZDogaW5wdXQuY29udmVyc2F0aW9uSWQsIG5vdzogRGF0ZS5ub3coKSwgcGVyc29uYUlkOiBpbnB1dC5wZXJzb25hSWR9KTtcbiAgICBpZighY291bnRzIHx8IGNvdW50cy5hY3RpdmUgfHwgY291bnRzLnRvdGFsID49IDI1KSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdjb25mbGljdCcpO1xuICAgIH1cbiAgICBhd2FpdCByYXRlTGltaXQocmVhZCk7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgYWRkSW52aXRlKHJlYWQsIGlucHV0LmNvbnZlcnNhdGlvbklkLCB0YXJnZXQpO1xuICAgIGF3YWl0IHJlcXVlc3Quc2F2ZShyZXN1bHQpO1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH0pO1xuICBjb25zdCByZXNvbHZlSW52aXRlID0gKFxuICAgIGlucHV0OiBBY2NlcHRJbnZpdGVJbnB1dCwgYWNjZXB0OiBib29sZWFuXG4gICk6IFByb21pc2U8Q29udmVyc2F0aW9uU3VtbWFyeSB8IENvbnZlcnNhdGlvbkludml0ZT4gPT5cbiAgICB0cmFuc2FjdChhc3luYyAocmVhZCkgPT4ge1xuICAgICAgYXdhaXQgYWN0b3IocmVhZCk7XG4gICAgICBjb25zdCByZXF1ZXN0ID0gYXdhaXQgcmVjZWlwdDxDb252ZXJzYXRpb25TdW1tYXJ5IHwgQ29udmVyc2F0aW9uSW52aXRlPihyZWFkLCBhY2NlcHQgPyAnYWNjZXB0JyA6ICdkZWNsaW5lJywgaW5wdXQpO1xuICAgICAgaWYocmVxdWVzdC5wcmlvcikge1xuICAgICAgICByZXR1cm4gcmVxdWVzdC5wcmlvcjtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGludml0ZSA9IGF3YWl0IHJlYWQ8SW52aXRlUmVjb3JkPignUkVUVVJOIERPQ1VNRU5UKFwiY2hhdEludml0ZXNcIiwgQGlkKScsIHtpZDogaW5wdXQuaW52aXRlSWR9KTtcbiAgICAgIGlmKCFpbnZpdGUgfHwgaW52aXRlLmludml0ZWVQZXJzb25hSWQgIT09IGlkZW50aXR5LnBlcnNvbmFJZCB8fCBpbnZpdGUuaW52aXRlZVVzZXJJZCAhPT0gaWRlbnRpdHkudXNlcklkXG4gICAgICAgIHx8IGludml0ZS5zdGF0ZSAhPT0gJ3BlbmRpbmcnIHx8IGludml0ZS5leHBpcmVzQXQgPD0gRGF0ZS5ub3coKSkge1xuICAgICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdmb3JiaWRkZW4nKTtcbiAgICAgIH1cbiAgICAgIGlmKGludml0ZS5yZXZpc2lvbiAhPT0gaW5wdXQuZXhwZWN0ZWRSZXZpc2lvbikge1xuICAgICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdjb25mbGljdCcpO1xuICAgICAgfVxuICAgICAgY29uc3QgY3VycmVudCA9IGF3YWl0IGNvbnZlcnNhdGlvbihyZWFkLCBpbnZpdGUuY29udmVyc2F0aW9uSWQpO1xuICAgICAgaWYoY3VycmVudC5raW5kICE9PSAnZ3JvdXAnKSB7XG4gICAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ2ZvcmJpZGRlbicpO1xuICAgICAgfVxuICAgICAgbGV0IHJlc3VsdDogQ29udmVyc2F0aW9uU3VtbWFyeSB8IENvbnZlcnNhdGlvbkludml0ZTtcbiAgICAgIGlmKGFjY2VwdCkge1xuICAgICAgICBjb25zdCBpbnZpdGVyID0gYXdhaXQgbWVtYmVyKHJlYWQsIGN1cnJlbnQuX2tleSwgaW52aXRlLmludml0ZWRCeVBlcnNvbmFJZCk7XG4gICAgICAgIGlmKCFbJ293bmVyJywgJ2FkbWluJ10uaW5jbHVkZXMoaW52aXRlci5yb2xlKSB8fCBjdXJyZW50LmFjdGl2ZU1lbWJlckNvdW50ID49IDI1KSB7XG4gICAgICAgICAgdGhyb3cgbmV3IENoYXRFcnJvcignZm9yYmlkZGVuJyk7XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgYWR1bHQocmVhZCwge3BlcnNvbmFJZDogaW52aXRlci5wZXJzb25hSWQsIHVzZXJJZDogaW52aXRlci51c2VySWR9KTtcbiAgICAgICAgYXdhaXQgY29ubmVjdGVkKHJlYWQsIHtwZXJzb25hSWQ6IGludml0ZXIucGVyc29uYUlkLCB1c2VySWQ6IGludml0ZXIudXNlcklkfSwgaWRlbnRpdHkucGVyc29uYUlkKTtcbiAgICAgICAgY29uc3Qgb3duID0gYXdhaXQgYWRkTWVtYmVyKHJlYWQsIGN1cnJlbnQuX2tleSwgaWRlbnRpdHksICdtZW1iZXInLCBjdXJyZW50Lmxhc3RTZXF1ZW5jZSArIDEpO1xuICAgICAgICBjb25zdCB1cGRhdGVkID0gYXdhaXQgcmVhZDxDb252ZXJzYXRpb25SZWNvcmQ+KGBVUERBVEUgQGlkIFdJVEgge1xuICAgICAgICAgIGFjdGl2ZU1lbWJlckNvdW50OiBAY291bnQsIG1lbWJlcnNoaXBSZXZpc2lvbjogQHJldmlzaW9uLCBzdGF0ZTogQHN0YXRlfVxuICAgICAgICAgIElOIGNvbnZlcnNhdGlvbnMgUkVUVVJOIE5FV2AsIHtjb3VudDogY3VycmVudC5hY3RpdmVNZW1iZXJDb3VudCArIDEsIGlkOiBjdXJyZW50Ll9rZXksXG4gICAgICAgICAgcmV2aXNpb246IGN1cnJlbnQubWVtYmVyc2hpcFJldmlzaW9uICsgMSxcbiAgICAgICAgICBzdGF0ZTogY3VycmVudC5hY3RpdmVNZW1iZXJDb3VudCArIDEgPj0gMyA/ICdhY3RpdmUnIDogY3VycmVudC5zdGF0ZX0pO1xuICAgICAgICBpZighdXBkYXRlZCkge1xuICAgICAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ3VuYXZhaWxhYmxlJyk7XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgZXZlbnQocmVhZCwgY3VycmVudC5fa2V5LCBvd24uZXBvY2gsIG93bi5fa2V5LCB1cGRhdGVkLm1lbWJlcnNoaXBSZXZpc2lvbik7XG4gICAgICAgIHJlc3VsdCA9IHN1bW1hcnkodXBkYXRlZCwgb3duLCBhdWRpZW5jZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXN1bHQgPSB7Li4uaW52aXRlRHRvKGludml0ZSksIHJldmlzaW9uOiBpbnZpdGUucmV2aXNpb24gKyAxLCBzdGF0ZTogJ2RlY2xpbmVkJ307XG4gICAgICAgIGF3YWl0IGV2ZW50KHJlYWQsIGN1cnJlbnQuX2tleSwgJycsIGludml0ZS5fa2V5LCBpbnZpdGUucmV2aXNpb24gKyAxKTtcbiAgICAgIH1cbiAgICAgIGF3YWl0IHJlYWQoYFVQREFURSBAaWQgV0lUSCB7Y3VycmVudEtleTogbnVsbCwgcmVzb2x2ZWRBdDogQG5vdywgcmV2aXNpb246IEByZXZpc2lvbiwgc3RhdGU6IEBzdGF0ZX1cbiAgICAgICAgSU4gY2hhdEludml0ZXMgUkVUVVJOIHRydWVgLCB7aWQ6IGludml0ZS5fa2V5LCBub3c6IERhdGUubm93KCksIHJldmlzaW9uOiBpbnZpdGUucmV2aXNpb24gKyAxLFxuICAgICAgICBzdGF0ZTogYWNjZXB0ID8gJ2FjY2VwdGVkJyA6ICdkZWNsaW5lZCd9KTtcbiAgICAgIGF3YWl0IHJlcXVlc3Quc2F2ZShyZXN1bHQpO1xuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9KTtcbiAgY29uc3QgY2hhbmdlTWVtYmVyc2hpcCA9IChcbiAgICBhY3Rpb246ICdsZWF2ZScgfCAncmVtb3ZlJyB8ICdyb2xlJyB8ICd0cmFuc2ZlcicsXG4gICAgaW5wdXQ6IENoYW5nZU1lbWJlclJvbGVJbnB1dCB8IExlYXZlQ29udmVyc2F0aW9uSW5wdXQgfCBSZW1vdmVNZW1iZXJJbnB1dFxuICApOiBQcm9taXNlPENvbnZlcnNhdGlvblN1bW1hcnk+ID0+IHRyYW5zYWN0KGFzeW5jIChyZWFkKSA9PiB7XG4gICAgYXdhaXQgYWN0b3IocmVhZCk7XG4gICAgY29uc3QgcmVxdWVzdCA9IGF3YWl0IHJlY2VpcHQ8Q29udmVyc2F0aW9uU3VtbWFyeT4ocmVhZCwgYWN0aW9uLCBpbnB1dCk7XG4gICAgaWYocmVxdWVzdC5wcmlvcikge1xuICAgICAgcmV0dXJuIHJlcXVlc3QucHJpb3I7XG4gICAgfVxuICAgIGNvbnN0IG93bkFjY2VzcyA9IGF3YWl0IGFjY2VzcyhyZWFkLCBpbnB1dC5jb252ZXJzYXRpb25JZCk7XG4gICAgY2hlY2tSZXZpc2lvbihvd25BY2Nlc3MsIGlucHV0LmV4cGVjdGVkUmV2aXNpb24pO1xuICAgIGNvbnN0IGN1cnJlbnQgPSBhd2FpdCBjb252ZXJzYXRpb24ocmVhZCwgaW5wdXQuY29udmVyc2F0aW9uSWQpO1xuICAgIGNvbnN0IG93biA9IGF3YWl0IG1lbWJlcihyZWFkLCBjdXJyZW50Ll9rZXksIGlkZW50aXR5LnBlcnNvbmFJZCk7XG4gICAgY29uc3QgdGFyZ2V0UGVyc29uYUlkID0gJ3BlcnNvbmFJZCcgaW4gaW5wdXQgPyBpbnB1dC5wZXJzb25hSWQgOiBpZGVudGl0eS5wZXJzb25hSWQ7XG4gICAgY29uc3QgdGFyZ2V0ID0gdGFyZ2V0UGVyc29uYUlkID09PSBpZGVudGl0eS5wZXJzb25hSWQgPyBvd24gOiBhd2FpdCBtZW1iZXIocmVhZCwgY3VycmVudC5fa2V5LCB0YXJnZXRQZXJzb25hSWQpO1xuICAgIGNvbnN0IGxlYXZpbmcgPSBhY3Rpb24gPT09ICdsZWF2ZScgfHwgYWN0aW9uID09PSAncmVtb3ZlJztcbiAgICBpZigoYWN0aW9uID09PSAncm9sZScgfHwgYWN0aW9uID09PSAndHJhbnNmZXInKSAmJiAob3duLnJvbGUgIT09ICdvd25lcicgfHwgdGFyZ2V0Ll9rZXkgPT09IG93bi5fa2V5KSkge1xuICAgICAgdGhyb3cgbmV3IENoYXRFcnJvcignZm9yYmlkZGVuJyk7XG4gICAgfVxuICAgIGlmKGFjdGlvbiA9PT0gJ3JlbW92ZScgJiYgKHRhcmdldC5fa2V5ID09PSBvd24uX2tleSB8fCB0YXJnZXQucm9sZSA9PT0gJ293bmVyJ1xuICAgICAgfHwgIVsnb3duZXInLCAnYWRtaW4nXS5pbmNsdWRlcyhvd24ucm9sZSkgfHwgKG93bi5yb2xlID09PSAnYWRtaW4nICYmIHRhcmdldC5yb2xlICE9PSAnbWVtYmVyJykpKSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdmb3JiaWRkZW4nKTtcbiAgICB9XG4gICAgaWYoYWN0aW9uID09PSAnbGVhdmUnICYmIG93bi5yb2xlID09PSAnb3duZXInICYmIGN1cnJlbnQuYWN0aXZlTWVtYmVyQ291bnQgPiAxKSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdjb25mbGljdCcpO1xuICAgIH1cbiAgICBpZihhY3Rpb24gPT09ICdyb2xlJyAmJiB0YXJnZXQucm9sZSA9PT0gJ293bmVyJykge1xuICAgICAgdGhyb3cgbmV3IENoYXRFcnJvcignZm9yYmlkZGVuJyk7XG4gICAgfVxuICAgIGlmKGFjdGlvbiA9PT0gJ3RyYW5zZmVyJyB8fCAoYWN0aW9uID09PSAncm9sZScgJiYgJ3JvbGUnIGluIGlucHV0ICYmIGlucHV0LnJvbGUgPT09ICdhZG1pbicpKSB7XG4gICAgICBhd2FpdCBhZHVsdChyZWFkLCB7cGVyc29uYUlkOiB0YXJnZXQucGVyc29uYUlkLCB1c2VySWQ6IHRhcmdldC51c2VySWR9KTtcbiAgICB9XG4gICAgbGV0IG93blJlc3VsdCA9IG93bjtcbiAgICBpZihhY3Rpb24gPT09ICd0cmFuc2ZlcicpIHtcbiAgICAgIGF3YWl0IHJlYWQoJ1VQREFURSBAaWQgV0lUSCB7cm9sZTogXCJhZG1pblwiLCByZXZpc2lvbjogQHJldmlzaW9ufSBJTiBoYXNDaGF0TWVtYmVyc2hpcCBSRVRVUk4gdHJ1ZScsXG4gICAgICAgIHtpZDogb3duLl9rZXksIHJldmlzaW9uOiBvd24ucmV2aXNpb24gKyAxfSk7XG4gICAgICBhd2FpdCByZWFkKCdVUERBVEUgQGlkIFdJVEgge3JvbGU6IFwib3duZXJcIiwgcmV2aXNpb246IEByZXZpc2lvbn0gSU4gaGFzQ2hhdE1lbWJlcnNoaXAgUkVUVVJOIHRydWUnLFxuICAgICAgICB7aWQ6IHRhcmdldC5fa2V5LCByZXZpc2lvbjogdGFyZ2V0LnJldmlzaW9uICsgMX0pO1xuICAgICAgb3duUmVzdWx0ID0gey4uLm93biwgcmV2aXNpb246IG93bi5yZXZpc2lvbiArIDEsIHJvbGU6ICdhZG1pbid9O1xuICAgIH0gZWxzZSBpZihhY3Rpb24gPT09ICdyb2xlJyAmJiAncm9sZScgaW4gaW5wdXQpIHtcbiAgICAgIGF3YWl0IHJlYWQoJ1VQREFURSBAaWQgV0lUSCB7cm9sZTogQHJvbGUsIHJldmlzaW9uOiBAcmV2aXNpb259IElOIGhhc0NoYXRNZW1iZXJzaGlwIFJFVFVSTiB0cnVlJyxcbiAgICAgICAge2lkOiB0YXJnZXQuX2tleSwgcmV2aXNpb246IHRhcmdldC5yZXZpc2lvbiArIDEsIHJvbGU6IGlucHV0LnJvbGV9KTtcbiAgICB9IGVsc2UgaWYobGVhdmluZykge1xuICAgICAgY29uc3Qgc3RhdGUgPSBhY3Rpb24gPT09ICdsZWF2ZScgPyAnbGVmdCcgOiAncmVtb3ZlZCc7XG4gICAgICBhd2FpdCByZWFkKGBVUERBVEUgQGlkIFdJVEgge2N1cnJlbnRLZXk6IG51bGwsIGVuZGVkQXQ6IEBub3csIGVuZGVkQnlQZXJzb25hSWQ6IEBhY3RvcixcbiAgICAgICAgbGVmdFNlcXVlbmNlOiBAc2VxdWVuY2UsIHJldmlzaW9uOiBAcmV2aXNpb24sIHN0YXRlOiBAc3RhdGV9IElOIGhhc0NoYXRNZW1iZXJzaGlwIFJFVFVSTiB0cnVlYCwge1xuICAgICAgICBhY3RvcjogaWRlbnRpdHkucGVyc29uYUlkLCBpZDogdGFyZ2V0Ll9rZXksIG5vdzogRGF0ZS5ub3coKSwgcmV2aXNpb246IHRhcmdldC5yZXZpc2lvbiArIDEsXG4gICAgICAgIHNlcXVlbmNlOiBjdXJyZW50Lmxhc3RTZXF1ZW5jZSwgc3RhdGVcbiAgICAgIH0pO1xuICAgICAgaWYoYWN0aW9uID09PSAnbGVhdmUnKSB7XG4gICAgICAgIG93blJlc3VsdCA9IHsuLi5vd24sIGxlZnRTZXF1ZW5jZTogY3VycmVudC5sYXN0U2VxdWVuY2UsIHJldmlzaW9uOiBvd24ucmV2aXNpb24gKyAxLCBzdGF0ZX07XG4gICAgICB9XG4gICAgICBhd2FpdCByZWFkKGBGT1IgZWRnZSBJTiBoYXNDb252ZXJzYXRpb24gRklMVEVSIGVkZ2UuX2Zyb20gPT0gQGZyb20gQU5EIGVkZ2UuX3RvID09IEB0b1xuICAgICAgICBMRVQgcmVtYWluaW5nID0gRklSU1QoRk9SIG1lbWJlciBJTiBoYXNDaGF0TWVtYmVyc2hpcCBGSUxURVIgbWVtYmVyLmNvbnZlcnNhdGlvbklkID09IEBjb252ZXJzYXRpb25JZFxuICAgICAgICAgIEFORCBtZW1iZXIudXNlcklkID09IEB1c2VySWQgQU5EIG1lbWJlci5zdGF0ZSA9PSAnYWN0aXZlJyBMSU1JVCAxIFJFVFVSTiB0cnVlKVxuICAgICAgICBGSUxURVIgcmVtYWluaW5nICE9IHRydWUgUkVNT1ZFIGVkZ2UgSU4gaGFzQ29udmVyc2F0aW9uIFJFVFVSTiB0cnVlYCwge1xuICAgICAgICBjb252ZXJzYXRpb25JZDogY3VycmVudC5fa2V5LCBmcm9tOiBgdXNlcnMvJHt0YXJnZXQudXNlcklkfWAsIHRvOiBgY29udmVyc2F0aW9ucy8ke2N1cnJlbnQuX2tleX1gLCB1c2VySWQ6IHRhcmdldC51c2VySWRcbiAgICAgIH0pO1xuICAgIH1cbiAgICBpZihsZWF2aW5nIHx8IChhY3Rpb24gPT09ICdyb2xlJyAmJiAncm9sZScgaW4gaW5wdXQgJiYgaW5wdXQucm9sZSA9PT0gJ21lbWJlcicpKSB7XG4gICAgICBhd2FpdCByZWFkKGBGT1IgaW52aXRlIElOIGNoYXRJbnZpdGVzIEZJTFRFUiBpbnZpdGUuY29udmVyc2F0aW9uSWQgPT0gQGNvbnZlcnNhdGlvbklkXG4gICAgICAgIEFORCBpbnZpdGUuc3RhdGUgPT0gJ3BlbmRpbmcnIEFORCAoaW52aXRlLmludml0ZWRCeVBlcnNvbmFJZCA9PSBAcGVyc29uYUlkIE9SIEBjbG9zZSlcbiAgICAgICAgVVBEQVRFIGludml0ZSBXSVRIIHtjdXJyZW50S2V5OiBudWxsLCByZXNvbHZlZEF0OiBAbm93LCByZXZpc2lvbjogaW52aXRlLnJldmlzaW9uICsgMSwgc3RhdGU6ICdyZXZva2VkJ31cbiAgICAgICAgSU4gY2hhdEludml0ZXMgUkVUVVJOIHRydWVgLCB7Y2xvc2U6IGxlYXZpbmcgJiYgY3VycmVudC5hY3RpdmVNZW1iZXJDb3VudCA9PT0gMSxcbiAgICAgICAgY29udmVyc2F0aW9uSWQ6IGN1cnJlbnQuX2tleSwgbm93OiBEYXRlLm5vdygpLCBwZXJzb25hSWQ6IHRhcmdldC5wZXJzb25hSWR9KTtcbiAgICB9XG4gICAgY29uc3QgY291bnQgPSBjdXJyZW50LmFjdGl2ZU1lbWJlckNvdW50IC0gKGxlYXZpbmcgPyAxIDogMCk7XG4gICAgY29uc3QgdXBkYXRlZCA9IGF3YWl0IHJlYWQ8Q29udmVyc2F0aW9uUmVjb3JkPihgVVBEQVRFIEBpZCBXSVRIIHtcbiAgICAgIGFjdGl2ZU1lbWJlckNvdW50OiBAY291bnQsIG1lbWJlcnNoaXBSZXZpc2lvbjogQHJldmlzaW9uLCBzdGF0ZTogQHN0YXRlfVxuICAgICAgSU4gY29udmVyc2F0aW9ucyBSRVRVUk4gTkVXYCwge2NvdW50LCBpZDogY3VycmVudC5fa2V5LCByZXZpc2lvbjogY3VycmVudC5tZW1iZXJzaGlwUmV2aXNpb24gKyAxLFxuICAgICAgc3RhdGU6IGNvdW50ID09PSAwID8gJ2Nsb3NlZCcgOiBjdXJyZW50LnN0YXRlfSk7XG4gICAgaWYoIXVwZGF0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ3VuYXZhaWxhYmxlJyk7XG4gICAgfVxuICAgIGF3YWl0IGV2ZW50KHJlYWQsIGN1cnJlbnQuX2tleSwgdGFyZ2V0LmVwb2NoLCB0YXJnZXQuX2tleSwgdXBkYXRlZC5tZW1iZXJzaGlwUmV2aXNpb24pO1xuICAgIGNvbnN0IHJlc3VsdCA9IHN1bW1hcnkodXBkYXRlZCwgb3duUmVzdWx0LCBhdWRpZW5jZSk7XG4gICAgYXdhaXQgcmVxdWVzdC5zYXZlKHJlc3VsdCk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfSk7XG4gIGNvbnN0IGxpc3RDb252ZXJzYXRpb25zID0gKGlucHV0OiBDaGF0TGlzdElucHV0KTogUHJvbWlzZTxyZWFkb25seSBDb252ZXJzYXRpb25TdW1tYXJ5W10+ID0+XG4gICAgdHJhbnNhY3QoYXN5bmMgKHJlYWQpID0+IHtcbiAgICAgIGF3YWl0IGFjdG9yKHJlYWQpO1xuICAgICAgY29uc3QgaWRzID0gYXdhaXQgcmVhZDxzdHJpbmdbXT4oYFJFVFVSTiAoRk9SIG1lbWJlciBJTiBoYXNDaGF0TWVtYmVyc2hpcFxuICAgICAgRklMVEVSIG1lbWJlci5wZXJzb25hSWQgPT0gQHBlcnNvbmFJZCBBTkQgbWVtYmVyLnVzZXJJZCA9PSBAdXNlcklkIEFORCBtZW1iZXIuc3RhdGUgPT0gJ2FjdGl2ZSdcbiAgICAgICAgQU5EIG1lbWJlci5jb252ZXJzYXRpb25JZCA+IEBhZnRlciBTT1JUIG1lbWJlci5jb252ZXJzYXRpb25JZCBMSU1JVCBAbGltaXQgUkVUVVJOIG1lbWJlci5jb252ZXJzYXRpb25JZClgLCB7XG4gICAgICAgIC4uLmlkZW50aXR5LCBhZnRlcjogaW5wdXQuYWZ0ZXIgfHwgJycsIGxpbWl0OiBpbnB1dC5saW1pdCB8fCAyNVxuICAgICAgfSk7XG4gICAgICBjb25zdCByZXN1bHRzOiBDb252ZXJzYXRpb25TdW1tYXJ5W10gPSBbXTtcbiAgICAgIGZvcihjb25zdCBpZCBvZiBpZHMgfHwgW10pIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCBhY2Nlc3MocmVhZCwgaWQpO1xuICAgICAgICAgIGNvbnN0IGN1cnJlbnQgPSBhd2FpdCBjb252ZXJzYXRpb24ocmVhZCwgaWQpO1xuICAgICAgICAgIGlmKGN1cnJlbnQua2luZCA9PT0gJ2RpcmVjdCcpIHtcbiAgICAgICAgICAgIGNvbnN0IHBlZXIgPSBhd2FpdCByZWFkPE1lbWJlclJlY29yZD4oYEZPUiBtZW1iZXIgSU4gaGFzQ2hhdE1lbWJlcnNoaXAgRklMVEVSIG1lbWJlci5jb252ZXJzYXRpb25JZCA9PSBAaWRcbiAgICAgICAgICAgIEFORCBtZW1iZXIucGVyc29uYUlkICE9IEBwZXJzb25hSWQgQU5EIG1lbWJlci5zdGF0ZSA9PSAnYWN0aXZlJyBMSU1JVCAxIFJFVFVSTiBtZW1iZXJgLCB7aWQsIHBlcnNvbmFJZDogaWRlbnRpdHkucGVyc29uYUlkfSk7XG4gICAgICAgICAgICBpZighcGVlcikge1xuICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGF3YWl0IGNvbm5lY3RlZChyZWFkLCBpZGVudGl0eSwgcGVlci5wZXJzb25hSWQpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXN1bHRzLnB1c2goc3VtbWFyeShjdXJyZW50LCBhd2FpdCBtZW1iZXIocmVhZCwgaWQsIGlkZW50aXR5LnBlcnNvbmFJZCksIGF1ZGllbmNlLCB0cnVlKSk7XG4gICAgICAgIH0gY2F0Y2goZXJyb3IpIHtcbiAgICAgICAgICBpZighKGVycm9yIGluc3RhbmNlb2YgQ2hhdEVycm9yKSB8fCBlcnJvci5jb2RlICE9PSAnZm9yYmlkZGVuJykge1xuICAgICAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gcmVzdWx0cztcbiAgICB9KTtcbiAgY29uc3QgbGlzdEludml0ZXMgPSAoaW5wdXQ6IENoYXRMaXN0SW5wdXQpOiBQcm9taXNlPHJlYWRvbmx5IENvbnZlcnNhdGlvbkludml0ZVtdPiA9PiB0cmFuc2FjdChhc3luYyAocmVhZCkgPT4ge1xuICAgIGF3YWl0IGFjdG9yKHJlYWQpO1xuICAgIGNvbnN0IHJlY29yZHMgPSBhd2FpdCByZWFkPEludml0ZVJlY29yZFtdPihgUkVUVVJOIChGT1IgaW52aXRlIElOIGNoYXRJbnZpdGVzXG4gICAgICBGSUxURVIgaW52aXRlLmludml0ZWVQZXJzb25hSWQgPT0gQHBlcnNvbmFJZCBBTkQgaW52aXRlLmludml0ZWVVc2VySWQgPT0gQHVzZXJJZCBBTkQgaW52aXRlLnN0YXRlID09ICdwZW5kaW5nJ1xuICAgICAgICBBTkQgaW52aXRlLmV4cGlyZXNBdCA+IEBub3cgQU5EIGludml0ZS5fa2V5ID4gQGFmdGVyXG4gICAgICBMRVQgY29udmVyc2F0aW9uID0gRE9DVU1FTlQoJ2NvbnZlcnNhdGlvbnMnLCBpbnZpdGUuY29udmVyc2F0aW9uSWQpXG4gICAgICBMRVQgaW52aXRlciA9IERPQ1VNRU5UKCdwZXJzb25hcycsIGludml0ZS5pbnZpdGVkQnlQZXJzb25hSWQpXG4gICAgICBMRVQgaW52aXRlclVzZXIgPSBET0NVTUVOVCgndXNlcnMnLCBpbnZpdGVyLnVzZXJJZClcbiAgICAgIExFVCBpbnZpdGVyTWVtYmVyc2hpcCA9IEZJUlNUKEZPUiBtZW1iZXIgSU4gaGFzQ2hhdE1lbWJlcnNoaXBcbiAgICAgICAgRklMVEVSIG1lbWJlci5jb252ZXJzYXRpb25JZCA9PSBpbnZpdGUuY29udmVyc2F0aW9uSWQgQU5EIG1lbWJlci5wZXJzb25hSWQgPT0gaW52aXRlLmludml0ZWRCeVBlcnNvbmFJZFxuICAgICAgICAgIEFORCBtZW1iZXIudXNlcklkID09IGludml0ZXIudXNlcklkIEFORCBtZW1iZXIuc3RhdGUgPT0gJ2FjdGl2ZScgQU5EIG1lbWJlci5yb2xlIElOIFsnb3duZXInLCAnYWRtaW4nXVxuICAgICAgICBMSU1JVCAxIFJFVFVSTiB0cnVlKVxuICAgICAgTEVUIGJsb2NrZWQgPSBGSVJTVChGT1IgZWRnZSBJTiBoYXNDb25uZWN0aW9uIEZJTFRFUiBlZGdlLnR5cGUgPT0gJ2Jsb2NrJ1xuICAgICAgICBBTkQgKChlZGdlLl9mcm9tIElOIFtDT05DQVQoJ3BlcnNvbmFzLycsIEBwZXJzb25hSWQpLCBDT05DQVQoJ3VzZXJzLycsIEB1c2VySWQpXVxuICAgICAgICAgIEFORCBlZGdlLl90byBJTiBbQ09OQ0FUKCdwZXJzb25hcy8nLCBpbnZpdGUuaW52aXRlZEJ5UGVyc29uYUlkKSwgQ09OQ0FUKCd1c2Vycy8nLCBpbnZpdGVyLnVzZXJJZCldKVxuICAgICAgICAgIE9SIChlZGdlLl90byBJTiBbQ09OQ0FUKCdwZXJzb25hcy8nLCBAcGVyc29uYUlkKSwgQ09OQ0FUKCd1c2Vycy8nLCBAdXNlcklkKV1cbiAgICAgICAgICBBTkQgZWRnZS5fZnJvbSBJTiBbQ09OQ0FUKCdwZXJzb25hcy8nLCBpbnZpdGUuaW52aXRlZEJ5UGVyc29uYUlkKSwgQ09OQ0FUKCd1c2Vycy8nLCBpbnZpdGVyLnVzZXJJZCldKSlcbiAgICAgICAgTElNSVQgMSBSRVRVUk4gdHJ1ZSlcbiAgICAgIEZJTFRFUiBjb252ZXJzYXRpb24uY2hhdFNjaGVtYVZlcnNpb24gPT0gMSBBTkQgY29udmVyc2F0aW9uLnN0YXRlIElOIFsnYWN0aXZlJywgJ2Zvcm1pbmcnXVxuICAgICAgICBBTkQgaW52aXRlciAhPSBudWxsIEFORCBpbnZpdGVyVXNlciAhPSBudWxsIEFORCBpbnZpdGVyLmFjdGl2ZSAhPSBmYWxzZSBBTkQgaW52aXRlclVzZXIuYWN0aXZlICE9IGZhbHNlXG4gICAgICAgIEFORCBpbnZpdGVyTWVtYmVyc2hpcCA9PSB0cnVlIEFORCBibG9ja2VkICE9IHRydWVcbiAgICAgIFNPUlQgaW52aXRlLl9rZXkgTElNSVQgQGxpbWl0IFJFVFVSTiBpbnZpdGUpYCwgey4uLmlkZW50aXR5LCBhZnRlcjogaW5wdXQuYWZ0ZXIgfHwgJycsIGxpbWl0OiBpbnB1dC5saW1pdCB8fCAyNSwgbm93OiBEYXRlLm5vdygpfSk7XG4gICAgcmV0dXJuIChyZWNvcmRzIHx8IFtdKS5tYXAoaW52aXRlRHRvKTtcbiAgfSk7XG4gIGNvbnN0IGdldFBhcnRpY2lwYW50cyA9IChpbnB1dDoge3JlYWRvbmx5IGNvbnZlcnNhdGlvbklkOiBzdHJpbmd9KTogUHJvbWlzZTxyZWFkb25seSBDb252ZXJzYXRpb25NZW1iZXJzaGlwW10+ID0+XG4gICAgdHJhbnNhY3QoYXN5bmMgKHJlYWQpID0+IHtcbiAgICAgIGF3YWl0IGFjdG9yKHJlYWQpO1xuICAgICAgYXdhaXQgYWNjZXNzKHJlYWQsIGlucHV0LmNvbnZlcnNhdGlvbklkKTtcbiAgICAgIGNvbnN0IHJlY29yZHMgPSBhd2FpdCByZWFkPE1lbWJlclJlY29yZFtdPihgUkVUVVJOIChGT1IgbWVtYmVyIElOIGhhc0NoYXRNZW1iZXJzaGlwXG4gICAgICAgIEZJTFRFUiBtZW1iZXIuY29udmVyc2F0aW9uSWQgPT0gQGNvbnZlcnNhdGlvbklkIEFORCBtZW1iZXIuc3RhdGUgPT0gJ2FjdGl2ZSdcbiAgICAgICAgTEVUIHBlcnNvbmEgPSBET0NVTUVOVCgncGVyc29uYXMnLCBtZW1iZXIucGVyc29uYUlkKSBMRVQgdXNlciA9IERPQ1VNRU5UKCd1c2VycycsIG1lbWJlci51c2VySWQpXG4gICAgICAgIExFVCBibG9ja2VkID0gRklSU1QoRk9SIGVkZ2UgSU4gaGFzQ29ubmVjdGlvbiBGSUxURVIgZWRnZS50eXBlID09ICdibG9jaydcbiAgICAgICAgICBBTkQgKChlZGdlLl9mcm9tIElOIFtAdmlld2VyUGVyc29uYSwgQHZpZXdlclVzZXJdXG4gICAgICAgICAgICBBTkQgZWRnZS5fdG8gSU4gW0NPTkNBVCgncGVyc29uYXMvJywgbWVtYmVyLnBlcnNvbmFJZCksIENPTkNBVCgndXNlcnMvJywgbWVtYmVyLnVzZXJJZCldKVxuICAgICAgICAgIE9SIChlZGdlLl90byBJTiBbQHZpZXdlclBlcnNvbmEsIEB2aWV3ZXJVc2VyXVxuICAgICAgICAgICAgQU5EIGVkZ2UuX2Zyb20gSU4gW0NPTkNBVCgncGVyc29uYXMvJywgbWVtYmVyLnBlcnNvbmFJZCksIENPTkNBVCgndXNlcnMvJywgbWVtYmVyLnVzZXJJZCldKSkgTElNSVQgMSBSRVRVUk4gdHJ1ZSlcbiAgICAgICAgRklMVEVSIHBlcnNvbmEgIT0gbnVsbCBBTkQgdXNlciAhPSBudWxsIEFORCBwZXJzb25hLmFjdGl2ZSAhPSBmYWxzZSBBTkQgdXNlci5hY3RpdmUgIT0gZmFsc2VcbiAgICAgICAgICBBTkQgcGVyc29uYS51c2VySWQgPT0gbWVtYmVyLnVzZXJJZCBBTkQgYmxvY2tlZCAhPSB0cnVlXG4gICAgICAgIFNPUlQgbWVtYmVyLnBlcnNvbmFJZCBMSU1JVCAyNSBSRVRVUk4gbWVtYmVyKWAsIHtcbiAgICAgICAgY29udmVyc2F0aW9uSWQ6IGlucHV0LmNvbnZlcnNhdGlvbklkLCB2aWV3ZXJQZXJzb25hOiBgcGVyc29uYXMvJHtpZGVudGl0eS5wZXJzb25hSWR9YCwgdmlld2VyVXNlcjogYHVzZXJzLyR7aWRlbnRpdHkudXNlcklkfWBcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIChyZWNvcmRzIHx8IFtdKS5tYXAobWVtYmVyc2hpcER0byk7XG4gICAgfSk7XG4gIHJldHVybiB7XG4gICAgYWNjZXB0SW52aXRlOiAoaW5wdXQ6IEFjY2VwdEludml0ZUlucHV0KSA9PiByZXNvbHZlSW52aXRlKGlucHV0LCB0cnVlKSBhcyBQcm9taXNlPENvbnZlcnNhdGlvblN1bW1hcnk+LFxuICAgIGNoYW5nZU1lbWJlclJvbGU6IChpbnB1dDogQ2hhbmdlTWVtYmVyUm9sZUlucHV0KSA9PiBjaGFuZ2VNZW1iZXJzaGlwKCdyb2xlJywgaW5wdXQpLFxuICAgIGNyZWF0ZUdyb3VwLFxuICAgIGRlY2xpbmVJbnZpdGU6IChpbnB1dDogQWNjZXB0SW52aXRlSW5wdXQpID0+IHJlc29sdmVJbnZpdGUoaW5wdXQsIGZhbHNlKSBhcyBQcm9taXNlPENvbnZlcnNhdGlvbkludml0ZT4sXG4gICAgZ2V0T3JDcmVhdGVEaXJlY3QsIGdldFBhcnRpY2lwYW50cywgaW52aXRlTWVtYmVyLFxuICAgIGxlYXZlQ29udmVyc2F0aW9uOiAoaW5wdXQ6IExlYXZlQ29udmVyc2F0aW9uSW5wdXQpID0+IGNoYW5nZU1lbWJlcnNoaXAoJ2xlYXZlJywgaW5wdXQpLFxuICAgIGxpc3RDb252ZXJzYXRpb25zLCBsaXN0SW52aXRlcyxcbiAgICByZW1vdmVNZW1iZXI6IChpbnB1dDogUmVtb3ZlTWVtYmVySW5wdXQpID0+IGNoYW5nZU1lbWJlcnNoaXAoJ3JlbW92ZScsIGlucHV0KSxcbiAgICB0cmFuc2Zlck93bmVyc2hpcDogKGlucHV0OiBUcmFuc2Zlck93bmVyc2hpcElucHV0KSA9PiBjaGFuZ2VNZW1iZXJzaGlwKCd0cmFuc2ZlcicsIGlucHV0KVxuICB9O1xufTtcbmV4cG9ydCB0eXBlIENoYXRSZXBvc2l0b3J5ID0gUmV0dXJuVHlwZTx0eXBlb2YgY3JlYXRlRHVyYWJsZUNvbnZlcnNhdGlvblJlcG9zaXRvcnk+O1xuXG5jb25zdCB2YWxpZCA9IChjb25kaXRpb246IGJvb2xlYW4pOiB2b2lkID0+IHtcbiAgaWYoIWNvbmRpdGlvbikge1xuICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ2ludmFsaWRfcmVxdWVzdCcpO1xuICB9XG59O1xuY29uc3QgbXV0YXRpb24gPSAoaW5wdXQ6IHtyZWFkb25seSBleHBlY3RlZFJldmlzaW9uOiBudW1iZXI7IHJlYWRvbmx5IGlkZW1wb3RlbmN5S2V5OiBzdHJpbmd9KTogdm9pZCA9PiB7XG4gIHZhbGlkKEJvb2xlYW4oaW5wdXQpICYmIGlzQ2hhdElkZW50aWZpZXIoaW5wdXQuaWRlbXBvdGVuY3lLZXkpXG4gICAgJiYgTnVtYmVyLmlzU2FmZUludGVnZXIoaW5wdXQuZXhwZWN0ZWRSZXZpc2lvbikgJiYgaW5wdXQuZXhwZWN0ZWRSZXZpc2lvbiA+IDApO1xufTtcbmNvbnN0IGNvbnZlcnNhdGlvbk11dGF0aW9uID0gKGlucHV0OiBMZWF2ZUNvbnZlcnNhdGlvbklucHV0KTogdm9pZCA9PiB7XG4gIG11dGF0aW9uKGlucHV0KTtcbiAgdmFsaWQoaXNDaGF0SWRlbnRpZmllcihpbnB1dC5jb252ZXJzYXRpb25JZCkpO1xufTtcbmNvbnN0IHRhcmdldGVkTXV0YXRpb24gPSAoaW5wdXQ6IFJlbW92ZU1lbWJlcklucHV0KTogdm9pZCA9PiB7XG4gIGNvbnZlcnNhdGlvbk11dGF0aW9uKGlucHV0KTtcbiAgdmFsaWQoaXNDaGF0SWRlbnRpZmllcihpbnB1dC5wZXJzb25hSWQpKTtcbn07XG5jb25zdCBsaXN0ID0gKGlucHV0OiBDaGF0TGlzdElucHV0KTogdm9pZCA9PiB2YWxpZChCb29sZWFuKGlucHV0KVxuICAmJiAoaW5wdXQubGltaXQgPT09IHVuZGVmaW5lZCB8fCAoTnVtYmVyLmlzU2FmZUludGVnZXIoaW5wdXQubGltaXQpICYmIGlucHV0LmxpbWl0ID4gMCAmJiBpbnB1dC5saW1pdCA8PSA1MCkpXG4gICYmIChpbnB1dC5hZnRlciA9PT0gdW5kZWZpbmVkIHx8IGlzQ2hhdElkZW50aWZpZXIoaW5wdXQuYWZ0ZXIpKSk7XG5cbmV4cG9ydCBjb25zdCBjcmVhdGVEdXJhYmxlQ29udmVyc2F0aW9uQWN0aW9ucyA9IChcbiAgaWRlbnRpdHk6IENoYXRJZGVudGl0eSwgcmVwb3NpdG9yeTogQ2hhdFJlcG9zaXRvcnlcbikgPT4ge1xuICBpZighaWRlbnRpdHkgfHwgIWlzQ2hhdElkZW50aWZpZXIoaWRlbnRpdHkucGVyc29uYUlkKSB8fCAhaXNDaGF0SWRlbnRpZmllcihpZGVudGl0eS51c2VySWQpKSB7XG4gICAgdGhyb3cgbmV3IENoYXRFcnJvcignZm9yYmlkZGVuJyk7XG4gIH1cbiAgcmV0dXJuIHtcbiAgICBhY2NlcHRJbnZpdGU6IGFzeW5jIChpbnB1dDogQWNjZXB0SW52aXRlSW5wdXQpID0+IHtcbiAgICAgIG11dGF0aW9uKGlucHV0KTtcbiAgICAgIHZhbGlkKGlzQ2hhdElkZW50aWZpZXIoaW5wdXQuaW52aXRlSWQpKTtcbiAgICAgIHJldHVybiByZXBvc2l0b3J5LmFjY2VwdEludml0ZShpbnB1dCk7XG4gICAgfSxcbiAgICBjaGFuZ2VNZW1iZXJSb2xlOiBhc3luYyAoaW5wdXQ6IENoYW5nZU1lbWJlclJvbGVJbnB1dCkgPT4ge1xuICAgICAgdGFyZ2V0ZWRNdXRhdGlvbihpbnB1dCk7XG4gICAgICB2YWxpZChbJ2FkbWluJywgJ21lbWJlciddLmluY2x1ZGVzKGlucHV0LnJvbGUpKTtcbiAgICAgIHJldHVybiByZXBvc2l0b3J5LmNoYW5nZU1lbWJlclJvbGUoaW5wdXQpO1xuICAgIH0sXG4gICAgY3JlYXRlR3JvdXA6IGFzeW5jIChpbnB1dDogQ3JlYXRlR3JvdXBJbnB1dCkgPT4ge1xuICAgICAgdmFsaWQoQm9vbGVhbihpbnB1dCkgJiYgaXNDaGF0SWRlbnRpZmllcihpbnB1dC5pZGVtcG90ZW5jeUtleSkgJiYgdHlwZW9mIGlucHV0Lm5hbWUgPT09ICdzdHJpbmcnXG4gICAgICAgICYmIGlucHV0Lm5hbWUudHJpbSgpLmxlbmd0aCA+IDAgJiYgaW5wdXQubmFtZS5sZW5ndGggPD0gMjAwXG4gICAgICAgICYmIEFycmF5LmlzQXJyYXkoaW5wdXQubWVtYmVyUGVyc29uYUlkcylcbiAgICAgICAgJiYgaW5wdXQubWVtYmVyUGVyc29uYUlkcy5sZW5ndGggPj0gMiAmJiBpbnB1dC5tZW1iZXJQZXJzb25hSWRzLmxlbmd0aCA8PSAyNFxuICAgICAgICAmJiBpbnB1dC5tZW1iZXJQZXJzb25hSWRzLmV2ZXJ5KChpZCkgPT4gaXNDaGF0SWRlbnRpZmllcihpZCkgJiYgaWQgIT09IGlkZW50aXR5LnBlcnNvbmFJZClcbiAgICAgICAgJiYgbmV3IFNldChpbnB1dC5tZW1iZXJQZXJzb25hSWRzKS5zaXplID09PSBpbnB1dC5tZW1iZXJQZXJzb25hSWRzLmxlbmd0aCk7XG4gICAgICByZXR1cm4gcmVwb3NpdG9yeS5jcmVhdGVHcm91cChpbnB1dCk7XG4gICAgfSxcbiAgICBkZWNsaW5lSW52aXRlOiBhc3luYyAoaW5wdXQ6IEFjY2VwdEludml0ZUlucHV0KSA9PiB7XG4gICAgICBtdXRhdGlvbihpbnB1dCk7XG4gICAgICB2YWxpZChpc0NoYXRJZGVudGlmaWVyKGlucHV0Lmludml0ZUlkKSk7XG4gICAgICByZXR1cm4gcmVwb3NpdG9yeS5kZWNsaW5lSW52aXRlKGlucHV0KTtcbiAgICB9LFxuICAgIGdldE9yQ3JlYXRlRGlyZWN0OiBhc3luYyAoaW5wdXQ6IENyZWF0ZURpcmVjdElucHV0KSA9PiB7XG4gICAgICB2YWxpZChCb29sZWFuKGlucHV0KSAmJiBpc0NoYXRJZGVudGlmaWVyKGlucHV0LnBhcnRpY2lwYW50UGVyc29uYUlkKVxuICAgICAgICAmJiBpbnB1dC5wYXJ0aWNpcGFudFBlcnNvbmFJZCAhPT0gaWRlbnRpdHkucGVyc29uYUlkKTtcbiAgICAgIHJldHVybiByZXBvc2l0b3J5LmdldE9yQ3JlYXRlRGlyZWN0KGlucHV0KTtcbiAgICB9LFxuICAgIGdldFBhcnRpY2lwYW50czogYXN5bmMgKGlucHV0OiB7cmVhZG9ubHkgY29udmVyc2F0aW9uSWQ6IHN0cmluZ30pID0+IHtcbiAgICAgIHZhbGlkKEJvb2xlYW4oaW5wdXQpICYmIGlzQ2hhdElkZW50aWZpZXIoaW5wdXQuY29udmVyc2F0aW9uSWQpKTtcbiAgICAgIHJldHVybiByZXBvc2l0b3J5LmdldFBhcnRpY2lwYW50cyhpbnB1dCk7XG4gICAgfSxcbiAgICBpbnZpdGVNZW1iZXI6IGFzeW5jIChpbnB1dDogSW52aXRlTWVtYmVySW5wdXQpID0+IHtcbiAgICAgIHRhcmdldGVkTXV0YXRpb24oaW5wdXQpO1xuICAgICAgdmFsaWQoaW5wdXQucGVyc29uYUlkICE9PSBpZGVudGl0eS5wZXJzb25hSWQpO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkuaW52aXRlTWVtYmVyKGlucHV0KTtcbiAgICB9LFxuICAgIGxlYXZlQ29udmVyc2F0aW9uOiBhc3luYyAoaW5wdXQ6IExlYXZlQ29udmVyc2F0aW9uSW5wdXQpID0+IHtcbiAgICAgIGNvbnZlcnNhdGlvbk11dGF0aW9uKGlucHV0KTtcbiAgICAgIHJldHVybiByZXBvc2l0b3J5LmxlYXZlQ29udmVyc2F0aW9uKGlucHV0KTtcbiAgICB9LFxuICAgIGxpc3RDb252ZXJzYXRpb25zOiBhc3luYyAoaW5wdXQ6IENoYXRMaXN0SW5wdXQgPSB7fSkgPT4ge1xuICAgICAgbGlzdChpbnB1dCk7XG4gICAgICByZXR1cm4gcmVwb3NpdG9yeS5saXN0Q29udmVyc2F0aW9ucyhpbnB1dCk7XG4gICAgfSxcbiAgICBsaXN0SW52aXRlczogYXN5bmMgKGlucHV0OiBDaGF0TGlzdElucHV0ID0ge30pID0+IHtcbiAgICAgIGxpc3QoaW5wdXQpO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkubGlzdEludml0ZXMoaW5wdXQpO1xuICAgIH0sXG4gICAgcmVtb3ZlTWVtYmVyOiBhc3luYyAoaW5wdXQ6IFJlbW92ZU1lbWJlcklucHV0KSA9PiB7XG4gICAgICB0YXJnZXRlZE11dGF0aW9uKGlucHV0KTtcbiAgICAgIHZhbGlkKGlucHV0LnBlcnNvbmFJZCAhPT0gaWRlbnRpdHkucGVyc29uYUlkKTtcbiAgICAgIHJldHVybiByZXBvc2l0b3J5LnJlbW92ZU1lbWJlcihpbnB1dCk7XG4gICAgfSxcbiAgICB0cmFuc2Zlck93bmVyc2hpcDogYXN5bmMgKGlucHV0OiBUcmFuc2Zlck93bmVyc2hpcElucHV0KSA9PiB7XG4gICAgICB0YXJnZXRlZE11dGF0aW9uKGlucHV0KTtcbiAgICAgIHZhbGlkKGlucHV0LnBlcnNvbmFJZCAhPT0gaWRlbnRpdHkucGVyc29uYUlkKTtcbiAgICAgIHJldHVybiByZXBvc2l0b3J5LnRyYW5zZmVyT3duZXJzaGlwKGlucHV0KTtcbiAgICB9XG4gIH07XG59O1xuIl0sIm5hbWVzIjpbImNyZWF0ZUhhc2giLCJyYW5kb21VVUlEIiwiQ2hhdEVycm9yIiwiYXBwZW5kQ2hhdEV2ZW50IiwiY3JlYXRlTWVtYmVyc2hpcEF1dGhvcml6ZXIiLCJpc0NoYXRJZGVudGlmaWVyIiwiY3JlYXRlRHVyYWJsZVRyYW5zYWN0aW9uIiwiaGFzaCIsImlucHV0IiwidXBkYXRlIiwiSlNPTiIsInN0cmluZ2lmeSIsImRpZ2VzdCIsImludml0ZU1pbGxpc2Vjb25kcyIsIm1lbWJlcnNoaXBEdG8iLCJyZWNvcmQiLCJjb252ZXJzYXRpb25JZCIsImVwb2NoIiwiam9pblNlcXVlbmNlIiwibGVmdFNlcXVlbmNlIiwicGVyc29uYUlkIiwicmV2aXNpb24iLCJyb2xlIiwic3RhdGUiLCJpbnZpdGVEdG8iLCJleHBpcmVzQXQiLCJpbnZpdGVJZCIsIl9rZXkiLCJpbnZpdGVkQnlQZXJzb25hSWQiLCJjcmVhdGVEdXJhYmxlQ29udmVyc2F0aW9uUmVwb3NpdG9yeSIsImNvbnRleHQiLCJpZGVudGl0eSIsIm9wdGlvbnMiLCJhdWRpZW5jZSIsInRyYW5zYWN0IiwiZXhjbHVzaXZlIiwicmVhZCIsInJlYWRDb2xsZWN0aW9ucyIsInF1ZXJ5Iiwic3VtbWFyeSIsImNvbnZlcnNhdGlvbiIsIm1lbWJlciIsImluY2x1ZGVOYW1lIiwiYWN0aXZlTWVtYmVyQ291bnQiLCJraW5kIiwibWVtYmVyc2hpcCIsIm1lbWJlcnNoaXBSZXZpc2lvbiIsIm5hbWUiLCJwcm9qZWN0TmFtZSIsInVuZGVmaW5lZCIsImFjdG9yIiwiYWxsb3dlZCIsImFkdWx0Iiwid2hvIiwiYXV0aG9yaXplSWRlbnRpdHkiLCJjb25uZWN0ZWQiLCJmcm9tIiwidGFyZ2V0IiwiYWN0b3JVc2VyIiwidXNlcklkIiwiZnJvbVVzZXIiLCJ0byIsImlkIiwidmFsdWUiLCJjaGF0U2NoZW1hVmVyc2lvbiIsImFjY2VzcyIsInB1cnBvc2UiLCJjaGVja1JldmlzaW9uIiwiZW50cnkiLCJleHBlY3RlZCIsImV2ZW50IiwicmVzb3VyY2VJZCIsImFjdG9yUGVyc29uYUlkIiwibWVtYmVyc2hpcEVwb2NoIiwicmVjZWlwdCIsImFjdGlvbiIsImtleSIsImlkZW1wb3RlbmN5S2V5IiwicGF5bG9hZEhhc2giLCJwcmlvciIsInJlc3VsdCIsInNhdmUiLCJhZGRlZCIsIkRhdGUiLCJub3ciLCJvd25lclVzZXJJZCIsInJhdGVMaW1pdCIsIk1hdGgiLCJmbG9vciIsImFkZE1lbWJlciIsImV2ZW50U2VxdWVuY2UiLCJfZnJvbSIsIl90byIsImFjY2VwdGVkQXQiLCJjdXJyZW50S2V5Iiwiam9pbkV2ZW50U2VxdWVuY2UiLCJhZGRJbnZpdGUiLCJpbnZpdGVlUGVyc29uYUlkIiwiaW52aXRlZVVzZXJJZCIsImNyZWF0ZWRBdCIsImV4cGlyZUludml0ZXMiLCJnZXRPckNyZWF0ZURpcmVjdCIsInBhcnRpY2lwYW50UGVyc29uYUlkIiwicGFpciIsInNvcnQiLCJleGlzdGluZyIsImxhc3RTZXF1ZW5jZSIsImNyZWF0ZWRCeVBlcnNvbmFJZCIsImRpcmVjdFBhaXJLZXkiLCJpc0RpcmVjdCIsIm93biIsImNyZWF0ZUdyb3VwIiwicmVxdWVzdCIsInRhcmdldHMiLCJtZW1iZXJQZXJzb25hSWRzIiwicHVzaCIsImNvbnRlbnRWZXJzaW9uIiwibmFtZUNvbnRlbnRWZXJzaW9uIiwibmFtZURlY2lzaW9uIiwiY2xhc3NpZnlOYW1lIiwiaW52aXRlTWVtYmVyIiwiZXhwZWN0ZWRSZXZpc2lvbiIsImNvdW50cyIsImFjdGl2ZSIsInRvdGFsIiwicmVzb2x2ZUludml0ZSIsImFjY2VwdCIsImludml0ZSIsImN1cnJlbnQiLCJpbnZpdGVyIiwiaW5jbHVkZXMiLCJ1cGRhdGVkIiwiY291bnQiLCJjaGFuZ2VNZW1iZXJzaGlwIiwib3duQWNjZXNzIiwidGFyZ2V0UGVyc29uYUlkIiwibGVhdmluZyIsIm93blJlc3VsdCIsInNlcXVlbmNlIiwiY2xvc2UiLCJsaXN0Q29udmVyc2F0aW9ucyIsImlkcyIsImFmdGVyIiwibGltaXQiLCJyZXN1bHRzIiwicGVlciIsImVycm9yIiwiY29kZSIsImxpc3RJbnZpdGVzIiwicmVjb3JkcyIsIm1hcCIsImdldFBhcnRpY2lwYW50cyIsInZpZXdlclBlcnNvbmEiLCJ2aWV3ZXJVc2VyIiwiYWNjZXB0SW52aXRlIiwiY2hhbmdlTWVtYmVyUm9sZSIsImRlY2xpbmVJbnZpdGUiLCJsZWF2ZUNvbnZlcnNhdGlvbiIsInJlbW92ZU1lbWJlciIsInRyYW5zZmVyT3duZXJzaGlwIiwidmFsaWQiLCJjb25kaXRpb24iLCJtdXRhdGlvbiIsIkJvb2xlYW4iLCJOdW1iZXIiLCJpc1NhZmVJbnRlZ2VyIiwiY29udmVyc2F0aW9uTXV0YXRpb24iLCJ0YXJnZXRlZE11dGF0aW9uIiwibGlzdCIsImNyZWF0ZUR1cmFibGVDb252ZXJzYXRpb25BY3Rpb25zIiwicmVwb3NpdG9yeSIsInRyaW0iLCJsZW5ndGgiLCJBcnJheSIsImlzQXJyYXkiLCJldmVyeSIsIlNldCIsInNpemUiXSwibWFwcGluZ3MiOiJBQUFBLHdHQUF3RyxHQUN4RyxTQUFRQSxVQUFVLEVBQUVDLFVBQVUsUUFBTyxjQUFjO0FBRW5ELFNBQVFDLFNBQVMsUUFBTyxjQUFjO0FBQ3RDLFNBQVFDLGVBQWUsUUFBTyxjQUFjO0FBQzVDLFNBQVFDLDBCQUEwQixFQUFFQyxnQkFBZ0IsUUFBTyxrQkFBa0I7QUFDN0UsU0FB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