@nlabs/reaktor 1.16.0 → 1.18.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 (103) hide show
  1. package/README.md +186 -0
  2. package/docs/ASSISTANT.md +138 -0
  3. package/docs/GENERATION.md +29 -2
  4. package/docs/NATIVE_PUSH.md +56 -0
  5. package/lib/actions/alfredFaq.d.ts +80 -0
  6. package/lib/actions/alfredFaq.js +206 -0
  7. package/lib/actions/contentGeneration.d.ts +4 -1
  8. package/lib/actions/contentGeneration.js +83 -27
  9. package/lib/actions/contentGenerationErrors.js +1 -1
  10. package/lib/actions/conversations.d.ts +1 -1
  11. package/lib/actions/conversations.js +19 -9
  12. package/lib/actions/crud.js +26 -1
  13. package/lib/actions/index.d.ts +2 -0
  14. package/lib/actions/index.js +3 -1
  15. package/lib/actions/messages.js +23 -7
  16. package/lib/actions/phoneSupport.d.ts +18 -0
  17. package/lib/actions/phoneSupport.js +170 -0
  18. package/lib/actions/users.js +57 -4
  19. package/lib/assistant/core.d.ts +53 -0
  20. package/lib/assistant/core.js +123 -0
  21. package/lib/assistant/faq.d.ts +26 -0
  22. package/lib/assistant/faq.js +238 -0
  23. package/lib/assistant/faqCore.d.ts +23 -0
  24. package/lib/assistant/faqCore.js +20 -0
  25. package/lib/assistant/index.d.ts +7 -0
  26. package/lib/assistant/index.js +5 -0
  27. package/lib/assistant/service.d.ts +35 -0
  28. package/lib/assistant/service.js +201 -0
  29. package/lib/config.js +3 -2
  30. package/lib/durableChat/conversations.d.ts +62 -0
  31. package/lib/durableChat/conversations.js +729 -0
  32. package/lib/durableChat/errors.d.ts +6 -0
  33. package/lib/durableChat/errors.js +11 -0
  34. package/lib/durableChat/events.d.ts +22 -0
  35. package/lib/durableChat/events.js +50 -0
  36. package/lib/durableChat/index.d.ts +8 -0
  37. package/lib/durableChat/index.js +10 -0
  38. package/lib/durableChat/membership.d.ts +24 -0
  39. package/lib/durableChat/membership.js +85 -0
  40. package/lib/durableChat/messageTypes.d.ts +76 -0
  41. package/lib/durableChat/messageTypes.js +3 -0
  42. package/lib/durableChat/messages.d.ts +16 -0
  43. package/lib/durableChat/messages.js +257 -0
  44. package/lib/durableChat/transaction.d.ts +10 -0
  45. package/lib/durableChat/transaction.js +53 -0
  46. package/lib/durableChat/types.d.ts +81 -0
  47. package/lib/durableChat/types.js +3 -0
  48. package/lib/index.d.ts +4 -4
  49. package/lib/index.js +5 -3
  50. package/lib/lambdas/actions/websockets.js +4 -4
  51. package/lib/nativePush/graphql.d.ts +14 -0
  52. package/lib/nativePush/graphql.js +105 -0
  53. package/lib/nativePush/index.d.ts +7 -0
  54. package/lib/nativePush/index.js +9 -0
  55. package/lib/nativePush/migrations.d.ts +2 -0
  56. package/lib/nativePush/migrations.js +36 -0
  57. package/lib/nativePush/nativePushContract.fixture.d.ts +22 -0
  58. package/lib/nativePush/nativePushContract.fixture.js +27 -0
  59. package/lib/nativePush/payload.d.ts +2 -0
  60. package/lib/nativePush/payload.js +106 -0
  61. package/lib/nativePush/repository.d.ts +9 -0
  62. package/lib/nativePush/repository.js +120 -0
  63. package/lib/nativePush/service.d.ts +2 -0
  64. package/lib/nativePush/service.js +110 -0
  65. package/lib/nativePush/snsProvider.d.ts +17 -0
  66. package/lib/nativePush/snsProvider.js +165 -0
  67. package/lib/nativePush/types.d.ts +89 -0
  68. package/lib/nativePush/types.js +3 -0
  69. package/lib/organizations/accountLifecycle.d.ts +7 -0
  70. package/lib/organizations/accountLifecycle.js +106 -0
  71. package/lib/organizations/authorization.d.ts +9 -0
  72. package/lib/organizations/authorization.js +40 -0
  73. package/lib/organizations/billing.d.ts +38 -0
  74. package/lib/organizations/billing.js +277 -0
  75. package/lib/organizations/billingProvider.d.ts +4 -0
  76. package/lib/organizations/billingProvider.js +287 -0
  77. package/lib/organizations/billingUpdates.d.ts +9 -0
  78. package/lib/organizations/billingUpdates.js +388 -0
  79. package/lib/organizations/index.d.ts +10 -0
  80. package/lib/organizations/index.js +12 -0
  81. package/lib/organizations/mail.d.ts +10 -0
  82. package/lib/organizations/mail.js +60 -0
  83. package/lib/organizations/memberDirectory.d.ts +15 -0
  84. package/lib/organizations/memberDirectory.js +28 -0
  85. package/lib/organizations/membership.d.ts +19 -0
  86. package/lib/organizations/membership.js +248 -0
  87. package/lib/organizations/passwordRecovery.d.ts +9 -0
  88. package/lib/organizations/passwordRecovery.js +50 -0
  89. package/lib/organizations/plans.d.ts +6 -0
  90. package/lib/organizations/plans.js +40 -0
  91. package/lib/organizations/providerPagination.d.ts +6 -0
  92. package/lib/organizations/providerPagination.js +23 -0
  93. package/lib/organizations/reconciliation.d.ts +11 -0
  94. package/lib/organizations/reconciliation.js +400 -0
  95. package/lib/organizations/signup.d.ts +25 -0
  96. package/lib/organizations/signup.js +169 -0
  97. package/lib/organizations/store.d.ts +25 -0
  98. package/lib/organizations/store.js +167 -0
  99. package/lib/organizations/types.d.ts +138 -0
  100. package/lib/organizations/types.js +3 -0
  101. package/lib/organizations/verification.d.ts +3 -0
  102. package/lib/organizations/verification.js +51 -0
  103. package/package.json +16 -3
@@ -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,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50L3JlYWt0b3IvcGFja2FnZXMvcmVha3Rvci9zcmMvZHVyYWJsZUNoYXQvY29udmVyc2F0aW9ucy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBuby1hd2FpdC1pbi1sb29wLCBuby11bmRlcnNjb3JlLWRhbmdsZSAtLSBPcmRlcmVkIHRyYW5zYWN0aW9ucyBhbmQgQXJhbmdvIGlkZW50aXRpZXMuICovXG5pbXBvcnQge2NyZWF0ZUhhc2gsIHJhbmRvbVVVSUR9IGZyb20gJ25vZGU6Y3J5cHRvJztcblxuaW1wb3J0IHtDaGF0RXJyb3J9IGZyb20gJy4vZXJyb3JzLmpzJztcbmltcG9ydCB7YXBwZW5kQ2hhdEV2ZW50fSBmcm9tICcuL2V2ZW50cy5qcyc7XG5pbXBvcnQge2NyZWF0ZU1lbWJlcnNoaXBBdXRob3JpemVyLCBpc0NoYXRJZGVudGlmaWVyfSBmcm9tICcuL21lbWJlcnNoaXAuanMnO1xuaW1wb3J0IHtjcmVhdGVEdXJhYmxlVHJhbnNhY3Rpb259IGZyb20gJy4vdHJhbnNhY3Rpb24uanMnO1xuXG5pbXBvcnQgdHlwZSB7QXBpQ29udGV4dH0gZnJvbSAnLi4vdHlwZXMvYXV0aC50eXBlcy5qcyc7XG5pbXBvcnQgdHlwZSB7QXV0aG9yaXplZE1lbWJlcnNoaXAsIENoYXRJZGVudGl0eSwgQ2hhdFF1ZXJ5fSBmcm9tICcuL21lbWJlcnNoaXAuanMnO1xuaW1wb3J0IHR5cGUge1xuICBBY2NlcHRJbnZpdGVJbnB1dCwgQ2hhbmdlTWVtYmVyUm9sZUlucHV0LCBDb252ZXJzYXRpb25JbnZpdGUsIENvbnZlcnNhdGlvbk1lbWJlcnNoaXAsXG4gIENvbnZlcnNhdGlvblN1bW1hcnksIENyZWF0ZURpcmVjdElucHV0LCBDcmVhdGVHcm91cElucHV0LCBJbnZpdGVNZW1iZXJJbnB1dCxcbiAgTGVhdmVDb252ZXJzYXRpb25JbnB1dCwgTWVtYmVyc2hpcFJvbGUsIFJlbW92ZU1lbWJlcklucHV0LCBUcmFuc2Zlck93bmVyc2hpcElucHV0XG59IGZyb20gJy4vdHlwZXMuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIENvbnZlcnNhdGlvblJlY29yZCB7XG4gIHJlYWRvbmx5IF9rZXk6IHN0cmluZztcbiAgcmVhZG9ubHkgYWN0aXZlTWVtYmVyQ291bnQ6IG51bWJlcjtcbiAgcmVhZG9ubHkgY2hhdFNjaGVtYVZlcnNpb246IG51bWJlcjtcbiAgcmVhZG9ubHkga2luZDogJ2RpcmVjdCcgfCAnZ3JvdXAnO1xuICByZWFkb25seSBsYXN0U2VxdWVuY2U6IG51bWJlcjtcbiAgcmVhZG9ubHkgbWVtYmVyc2hpcFJldmlzaW9uOiBudW1iZXI7XG4gIHJlYWRvbmx5IG5hbWU/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IG5hbWVDb250ZW50VmVyc2lvbj86IHN0cmluZztcbiAgcmVhZG9ubHkgbmFtZURlY2lzaW9uPzogdW5rbm93bjtcbiAgcmVhZG9ubHkgc3RhdGU6ICdhY3RpdmUnIHwgJ2Nsb3NlZCcgfCAnZm9ybWluZyc7XG59XG5pbnRlcmZhY2UgTWVtYmVyUmVjb3JkIGV4dGVuZHMgQ29udmVyc2F0aW9uTWVtYmVyc2hpcCB7XG4gIHJlYWRvbmx5IF9rZXk6IHN0cmluZztcbiAgcmVhZG9ubHkgdXNlcklkOiBzdHJpbmc7XG59XG5pbnRlcmZhY2UgSW52aXRlUmVjb3JkIGV4dGVuZHMgQ29udmVyc2F0aW9uSW52aXRlIHtcbiAgcmVhZG9ubHkgX2tleTogc3RyaW5nO1xuICByZWFkb25seSBpbnZpdGVlUGVyc29uYUlkOiBzdHJpbmc7XG4gIHJlYWRvbmx5IGludml0ZWVVc2VySWQ6IHN0cmluZztcbn1cbmV4cG9ydCBpbnRlcmZhY2UgQ2hhdExpc3RJbnB1dCB7XG4gIHJlYWRvbmx5IGFmdGVyPzogc3RyaW5nO1xuICByZWFkb25seSBsaW1pdD86IG51bWJlcjtcbn1cbmNvbnN0IGhhc2ggPSAoaW5wdXQ6IHVua25vd24pOiBzdHJpbmcgPT4gY3JlYXRlSGFzaCgnc2hhMjU2JykudXBkYXRlKEpTT04uc3RyaW5naWZ5KGlucHV0KSkuZGlnZXN0KCdoZXgnKTtcbmNvbnN0IGludml0ZU1pbGxpc2Vjb25kcyA9IDcgKiAyNCAqIDYwICogNjAgKiAxMDAwO1xuY29uc3QgbWVtYmVyc2hpcER0byA9IChyZWNvcmQ6IENvbnZlcnNhdGlvbk1lbWJlcnNoaXApOiBDb252ZXJzYXRpb25NZW1iZXJzaGlwID0+ICh7XG4gIGNvbnZlcnNhdGlvbklkOiByZWNvcmQuY29udmVyc2F0aW9uSWQsIGVwb2NoOiByZWNvcmQuZXBvY2gsIGpvaW5TZXF1ZW5jZTogcmVjb3JkLmpvaW5TZXF1ZW5jZSxcbiAgbGVmdFNlcXVlbmNlOiByZWNvcmQubGVmdFNlcXVlbmNlLCBwZXJzb25hSWQ6IHJlY29yZC5wZXJzb25hSWQsIHJldmlzaW9uOiByZWNvcmQucmV2aXNpb24sXG4gIHJvbGU6IHJlY29yZC5yb2xlLCBzdGF0ZTogcmVjb3JkLnN0YXRlXG59KTtcbmNvbnN0IGludml0ZUR0byA9IChyZWNvcmQ6IEludml0ZVJlY29yZCk6IENvbnZlcnNhdGlvbkludml0ZSA9PiAoe1xuICBjb252ZXJzYXRpb25JZDogcmVjb3JkLmNvbnZlcnNhdGlvbklkLCBleHBpcmVzQXQ6IHJlY29yZC5leHBpcmVzQXQsIGludml0ZUlkOiByZWNvcmQuX2tleSxcbiAgaW52aXRlZEJ5UGVyc29uYUlkOiByZWNvcmQuaW52aXRlZEJ5UGVyc29uYUlkLCByZXZpc2lvbjogcmVjb3JkLnJldmlzaW9uLCBzdGF0ZTogcmVjb3JkLnN0YXRlXG59KTtcbmV4cG9ydCBpbnRlcmZhY2UgRHVyYWJsZUNvbnZlcnNhdGlvbk9wdGlvbnMge1xuICByZWFkb25seSBhdWRpZW5jZT86ICdtb2JpbGUnIHwgJ3dlYic7XG4gIC8qKiBBbGwgcG9saWN5IHJlYWRzIG11c3QgdXNlIHRoaXMgcXVlcnk7IGV4Y2VwdGlvbnMgZmFpbCB0aGUgdHJhbnNhY3Rpb24uICovXG4gIHJlYWRvbmx5IGF1dGhvcml6ZUlkZW50aXR5OiAocmVhZDogQ2hhdFF1ZXJ5LCBpZGVudGl0eTogQ2hhdElkZW50aXR5KSA9PiBQcm9taXNlPHZvaWQ+O1xuICByZWFkb25seSBjbGFzc2lmeU5hbWU6IChuYW1lOiBzdHJpbmcsIGNvbnRlbnRWZXJzaW9uOiBzdHJpbmcpID0+IHVua25vd247XG4gIHJlYWRvbmx5IHByb2plY3ROYW1lOiAoY29udmVyc2F0aW9uOiBDb252ZXJzYXRpb25SZWNvcmQsIGF1ZGllbmNlOiAnbW9iaWxlJyB8ICd3ZWInKSA9PiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIHJlYWRvbmx5IHJlYWRDb2xsZWN0aW9ucz86IHJlYWRvbmx5IHN0cmluZ1tdO1xuICAvKiogQ2FsbGVyIG93bnMgdHJhbnNhY3Rpb24vbG9ja3MvY29tbWl0IHdoZW4gc3VwcGxpZWQ7IG5vIGluZGVwZW5kZW50IGRhdGFiYXNlIGFjY2VzcyBvY2N1cnMuICovXG4gIHJlYWRvbmx5IHF1ZXJ5PzogQ2hhdFF1ZXJ5O1xufVxuXG5leHBvcnQgY29uc3QgY3JlYXRlRHVyYWJsZUNvbnZlcnNhdGlvblJlcG9zaXRvcnkgPSAoXG4gIGNvbnRleHQ6IEFwaUNvbnRleHQsIGlkZW50aXR5OiBDaGF0SWRlbnRpdHksIG9wdGlvbnM6IER1cmFibGVDb252ZXJzYXRpb25PcHRpb25zXG4pID0+IHtcbiAgY29uc3QgYXVkaWVuY2UgPSBvcHRpb25zLmF1ZGllbmNlIHx8ICdtb2JpbGUnO1xuICBjb25zdCB0cmFuc2FjdCA9IGNyZWF0ZUR1cmFibGVUcmFuc2FjdGlvbihjb250ZXh0LCB7ZXhjbHVzaXZlOiBbXG4gICAgJ2NoYXRJbnZpdGVzJywgJ2NoYXRPdXRib3gnLCAnY2hhdFJhdGVMaW1pdHMnLCAnY2hhdFJlcXVlc3RzJywgJ2NvbnZlcnNhdGlvbnMnLCAnaGFzQ2hhdE1lbWJlcnNoaXAnLCAnaGFzQ29udmVyc2F0aW9uJ1xuICBdLCByZWFkOiBbJ2hhc0Nvbm5lY3Rpb24nLCAncGVyc29uYXMnLCAndXNlcnMnLCAuLi4ob3B0aW9ucy5yZWFkQ29sbGVjdGlvbnMgfHwgW10pXX0sIG9wdGlvbnMucXVlcnkpO1xuICBjb25zdCBzdW1tYXJ5ID0gKGNvbnZlcnNhdGlvbjogQ29udmVyc2F0aW9uUmVjb3JkLCBtZW1iZXI6IENvbnZlcnNhdGlvbk1lbWJlcnNoaXAsXG4gICAgYXVkaWVuY2U6ICdtb2JpbGUnIHwgJ3dlYicsIGluY2x1ZGVOYW1lID0gZmFsc2UpOiBDb252ZXJzYXRpb25TdW1tYXJ5ID0+ICh7XG4gICAgYWN0aXZlTWVtYmVyQ291bnQ6IGNvbnZlcnNhdGlvbi5hY3RpdmVNZW1iZXJDb3VudCwgY29udmVyc2F0aW9uSWQ6IGNvbnZlcnNhdGlvbi5fa2V5LFxuICAgIGtpbmQ6IGNvbnZlcnNhdGlvbi5raW5kLCBtZW1iZXJzaGlwOiBtZW1iZXJzaGlwRHRvKG1lbWJlciksIG1lbWJlcnNoaXBSZXZpc2lvbjogY29udmVyc2F0aW9uLm1lbWJlcnNoaXBSZXZpc2lvbixcbiAgICBuYW1lOiBpbmNsdWRlTmFtZSA/IG9wdGlvbnMucHJvamVjdE5hbWUoY29udmVyc2F0aW9uLCBhdWRpZW5jZSkgOiB1bmRlZmluZWQsIHN0YXRlOiBjb252ZXJzYXRpb24uc3RhdGVcbiAgfSk7XG4gIGNvbnN0IGFjdG9yID0gYXN5bmMgKHJlYWQ6IENoYXRRdWVyeSk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgIGNvbnN0IGFsbG93ZWQgPSBhd2FpdCByZWFkPGJvb2xlYW4+KGBMRVQgcGVyc29uYSA9IERPQ1VNRU5UKCdwZXJzb25hcycsIEBwZXJzb25hSWQpXG4gICAgICBMRVQgdXNlciA9IERPQ1VNRU5UKCd1c2VycycsIEB1c2VySWQpXG4gICAgICBSRVRVUk4gcGVyc29uYSAhPSBudWxsIEFORCBwZXJzb25hLnVzZXJJZCA9PSBAdXNlcklkIEFORCBwZXJzb25hLmFjdGl2ZSAhPSBmYWxzZVxuICAgICAgICBBTkQgdXNlciAhPSBudWxsIEFORCB1c2VyLmFjdGl2ZSAhPSBmYWxzZWAsIHsuLi5pZGVudGl0eX0pO1xuICAgIGlmKGFsbG93ZWQgIT09IHRydWUpIHtcbiAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ2ZvcmJpZGRlbicpO1xuICAgIH1cbiAgICBhd2FpdCBhZHVsdChyZWFkLCBpZGVudGl0eSk7XG4gIH07XG4gIGNvbnN0IGFkdWx0ID0gKHJlYWQ6IENoYXRRdWVyeSwgd2hvOiBDaGF0SWRlbnRpdHkpOiBQcm9taXNlPHZvaWQ+ID0+IG9wdGlvbnMuYXV0aG9yaXplSWRlbnRpdHkocmVhZCwgd2hvKTtcbiAgY29uc3QgY29ubmVjdGVkID0gYXN5bmMgKHJlYWQ6IENoYXRRdWVyeSwgZnJvbTogQ2hhdElkZW50aXR5LCBwZXJzb25hSWQ6IHN0cmluZyk6IFByb21pc2U8Q2hhdElkZW50aXR5PiA9PiB7XG4gICAgY29uc3QgdGFyZ2V0ID0gYXdhaXQgcmVhZDxDaGF0SWRlbnRpdHk+KGBMRVQgcGVyc29uYSA9IERPQ1VNRU5UKCdwZXJzb25hcycsIEBwZXJzb25hSWQpXG4gICAgICBMRVQgdXNlciA9IERPQ1VNRU5UKCd1c2VycycsIHBlcnNvbmEudXNlcklkKVxuICAgICAgTEVUIGZvbGxvd2luZyA9IEZJUlNUKEZPUiBlZGdlIElOIGhhc0Nvbm5lY3Rpb24gRklMVEVSIGVkZ2UuX2Zyb20gPT0gQGZyb20gQU5EIGVkZ2UuX3RvID09IEB0b1xuICAgICAgICBBTkQgZWRnZS50eXBlID09ICdmb2xsb3cnIEFORCBlZGdlLmFjdGl2ZSAhPSBmYWxzZSBMSU1JVCAxIFJFVFVSTiB0cnVlKVxuICAgICAgTEVUIGZvbGxvd2VkID0gRklSU1QoRk9SIGVkZ2UgSU4gaGFzQ29ubmVjdGlvbiBGSUxURVIgZWRnZS5fdG8gPT0gQGZyb20gQU5EIGVkZ2UuX2Zyb20gPT0gQHRvXG4gICAgICAgIEFORCBlZGdlLnR5cGUgPT0gJ2ZvbGxvdycgQU5EIGVkZ2UuYWN0aXZlICE9IGZhbHNlIExJTUlUIDEgUkVUVVJOIHRydWUpXG4gICAgICBMRVQgYmxvY2tlZCA9IEZJUlNUKEZPUiBlZGdlIElOIGhhc0Nvbm5lY3Rpb24gRklMVEVSIGVkZ2UudHlwZSA9PSAnYmxvY2snXG4gICAgICAgIEFORCAoKGVkZ2UuX2Zyb20gSU4gW0Bmcm9tLCBAZnJvbVVzZXJdIEFORCBlZGdlLl90byBJTiBbQHRvLCBDT05DQVQoJ3VzZXJzLycsIHBlcnNvbmEudXNlcklkKV0pXG4gICAgICAgICAgT1IgKGVkZ2UuX3RvIElOIFtAZnJvbSwgQGZyb21Vc2VyXSBBTkQgZWRnZS5fZnJvbSBJTiBbQHRvLCBDT05DQVQoJ3VzZXJzLycsIHBlcnNvbmEudXNlcklkKV0pKSBMSU1JVCAxIFJFVFVSTiB0cnVlKVxuICAgICAgRklMVEVSIHBlcnNvbmEgIT0gbnVsbCBBTkQgdXNlciAhPSBudWxsIEFORCBwZXJzb25hLmFjdGl2ZSAhPSBmYWxzZSBBTkQgdXNlci5hY3RpdmUgIT0gZmFsc2VcbiAgICAgICAgQU5EIHBlcnNvbmEudXNlcklkICE9IEBhY3RvclVzZXIgQU5EIGZvbGxvd2luZyA9PSB0cnVlIEFORCBmb2xsb3dlZCA9PSB0cnVlIEFORCBibG9ja2VkICE9IHRydWVcbiAgICAgIFJFVFVSTiB7cGVyc29uYUlkOiBAcGVyc29uYUlkLCB1c2VySWQ6IHBlcnNvbmEudXNlcklkfWAsIHtcbiAgICAgIGFjdG9yVXNlcjogZnJvbS51c2VySWQsIGZyb206IGBwZXJzb25hcy8ke2Zyb20ucGVyc29uYUlkfWAsIGZyb21Vc2VyOiBgdXNlcnMvJHtmcm9tLnVzZXJJZH1gLFxuICAgICAgcGVyc29uYUlkLCB0bzogYHBlcnNvbmFzLyR7cGVyc29uYUlkfWBcbiAgICB9KTtcbiAgICBpZighdGFyZ2V0KSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdmb3JiaWRkZW4nKTtcbiAgICB9XG4gICAgYXdhaXQgYWR1bHQocmVhZCwgdGFyZ2V0KTtcbiAgICByZXR1cm4gdGFyZ2V0O1xuICB9O1xuICBjb25zdCBjb252ZXJzYXRpb24gPSBhc3luYyAocmVhZDogQ2hhdFF1ZXJ5LCBpZDogc3RyaW5nKTogUHJvbWlzZTxDb252ZXJzYXRpb25SZWNvcmQ+ID0+IHtcbiAgICBjb25zdCB2YWx1ZSA9IGF3YWl0IHJlYWQ8Q29udmVyc2F0aW9uUmVjb3JkPignUkVUVVJOIERPQ1VNRU5UKFwiY29udmVyc2F0aW9uc1wiLCBAaWQpJywge2lkfSk7XG4gICAgaWYoIXZhbHVlIHx8IHZhbHVlLmNoYXRTY2hlbWFWZXJzaW9uICE9PSAxIHx8IHZhbHVlLnN0YXRlID09PSAnY2xvc2VkJykge1xuICAgICAgdGhyb3cgbmV3IENoYXRFcnJvcignZm9yYmlkZGVuJyk7XG4gICAgfVxuICAgIHJldHVybiB2YWx1ZTtcbiAgfTtcbiAgY29uc3QgbWVtYmVyID0gYXN5bmMgKHJlYWQ6IENoYXRRdWVyeSwgY29udmVyc2F0aW9uSWQ6IHN0cmluZywgcGVyc29uYUlkOiBzdHJpbmcpOiBQcm9taXNlPE1lbWJlclJlY29yZD4gPT4ge1xuICAgIGNvbnN0IHZhbHVlID0gYXdhaXQgcmVhZDxNZW1iZXJSZWNvcmQ+KGBGT1IgbWVtYmVyIElOIGhhc0NoYXRNZW1iZXJzaGlwIEZJTFRFUiBtZW1iZXIuY29udmVyc2F0aW9uSWQgPT0gQGNvbnZlcnNhdGlvbklkXG4gICAgICBBTkQgbWVtYmVyLnBlcnNvbmFJZCA9PSBAcGVyc29uYUlkIEFORCBtZW1iZXIuc3RhdGUgPT0gJ2FjdGl2ZScgTElNSVQgMSBSRVRVUk4gbWVtYmVyYCwge2NvbnZlcnNhdGlvbklkLCBwZXJzb25hSWR9KTtcbiAgICBpZighdmFsdWUpIHtcbiAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ2ZvcmJpZGRlbicpO1xuICAgIH1cbiAgICByZXR1cm4gdmFsdWU7XG4gIH07XG4gIGNvbnN0IGFjY2VzcyA9IChyZWFkOiBDaGF0UXVlcnksIGNvbnZlcnNhdGlvbklkOiBzdHJpbmcsIHB1cnBvc2U6ICdpbnZpdGUnIHwgJ3BhcnRpY2lwYW50cycgPSAncGFydGljaXBhbnRzJykgPT5cbiAgICBjcmVhdGVNZW1iZXJzaGlwQXV0aG9yaXplcihyZWFkKShpZGVudGl0eSwge2NvbnZlcnNhdGlvbklkLCBwdXJwb3NlfSk7XG4gIGNvbnN0IGNoZWNrUmV2aXNpb24gPSAoZW50cnk6IEF1dGhvcml6ZWRNZW1iZXJzaGlwLCBleHBlY3RlZDogbnVtYmVyKTogdm9pZCA9PiB7XG4gICAgaWYoZW50cnkubWVtYmVyc2hpcFJldmlzaW9uICE9PSBleHBlY3RlZCB8fCBlbnRyeS5raW5kICE9PSAnZ3JvdXAnKSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdjb25mbGljdCcpO1xuICAgIH1cbiAgfTtcbiAgY29uc3QgZXZlbnQgPSBhc3luYyAoXG4gICAgcmVhZDogQ2hhdFF1ZXJ5LCBjb252ZXJzYXRpb25JZDogc3RyaW5nLCBlcG9jaDogc3RyaW5nLCByZXNvdXJjZUlkOiBzdHJpbmcsIHJldmlzaW9uOiBudW1iZXJcbiAgKSA9PiB7XG4gICAgYXdhaXQgYXBwZW5kQ2hhdEV2ZW50KHJlYWQsIHthY3RvclBlcnNvbmFJZDogaWRlbnRpdHkucGVyc29uYUlkLCBjb252ZXJzYXRpb25JZCwga2luZDogJ21lbWJlcnNoaXAuY2hhbmdlZCcsXG4gICAgICAuLi4oZXBvY2ggPyB7bWVtYmVyc2hpcEVwb2NoOiBlcG9jaH0gOiB7fSksIHJlc291cmNlSWQsIHJldmlzaW9ufSk7XG4gIH07XG4gIGNvbnN0IHJlY2VpcHQgPSBhc3luYyA8VD4ocmVhZDogQ2hhdFF1ZXJ5LCBhY3Rpb246IHN0cmluZywgaW5wdXQ6IHtyZWFkb25seSBpZGVtcG90ZW5jeUtleTogc3RyaW5nfSkgPT4ge1xuICAgIGNvbnN0IGtleSA9IGhhc2goW2lkZW50aXR5LnVzZXJJZCwgaWRlbnRpdHkucGVyc29uYUlkLCBhY3Rpb24sIGlucHV0LmlkZW1wb3RlbmN5S2V5XSk7XG4gICAgY29uc3QgcGF5bG9hZEhhc2ggPSBoYXNoKGlucHV0KTtcbiAgICBjb25zdCBwcmlvciA9IGF3YWl0IHJlYWQ8e3JlYWRvbmx5IHBheWxvYWRIYXNoOiBzdHJpbmc7IHJlYWRvbmx5IHJlc3VsdDogVH0+KCdSRVRVUk4gRE9DVU1FTlQoXCJjaGF0UmVxdWVzdHNcIiwgQGtleSknLCB7a2V5fSk7XG4gICAgaWYocHJpb3IgJiYgcHJpb3IucGF5bG9hZEhhc2ggIT09IHBheWxvYWRIYXNoKSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdjb25mbGljdCcpO1xuICAgIH1cbiAgICByZXR1cm4ge3ByaW9yOiBwcmlvcj8ucmVzdWx0LCBzYXZlOiAocmVzdWx0OiBUKSA9PiByZWFkKCdJTlNFUlQgQHJlY29yZCBJTiBjaGF0UmVxdWVzdHMgUkVUVVJOIHRydWUnLCB7XG4gICAgICByZWNvcmQ6IHtfa2V5OiBrZXksIGFkZGVkOiBEYXRlLm5vdygpLCBvd25lclVzZXJJZDogaWRlbnRpdHkudXNlcklkLCBwYXlsb2FkSGFzaCxcbiAgICAgICAgcGVyc29uYUlkOiBpZGVudGl0eS5wZXJzb25hSWQsIHJlc3VsdH1cbiAgICB9KX07XG4gIH07XG4gIGNvbnN0IHJhdGVMaW1pdCA9IGFzeW5jIChyZWFkOiBDaGF0UXVlcnkpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICBjb25zdCBrZXkgPSBoYXNoKFtpZGVudGl0eS51c2VySWQsIE1hdGguZmxvb3IoRGF0ZS5ub3coKSAvIDM2MDAwMDApXSk7XG4gICAgY29uc3QgYWxsb3dlZCA9IGF3YWl0IHJlYWQ8Ym9vbGVhbj4oYExFVCBidWNrZXQgPSBET0NVTUVOVCgnY2hhdFJhdGVMaW1pdHMnLCBAa2V5KVxuICAgICAgRklMVEVSIGJ1Y2tldCA9PSBudWxsIE9SIGJ1Y2tldC5jb3VudCA8IDUwXG4gICAgICBVUFNFUlQge19rZXk6IEBrZXl9IElOU0VSVCB7X2tleTogQGtleSwgY291bnQ6IDEsIHVzZXJJZDogQHVzZXJJZH0gVVBEQVRFIHtjb3VudDogT0xELmNvdW50ICsgMSwgdXNlcklkOiBAdXNlcklkfVxuICAgICAgSU4gY2hhdFJhdGVMaW1pdHMgUkVUVVJOIHRydWVgLCB7a2V5LCB1c2VySWQ6IGlkZW50aXR5LnVzZXJJZH0pO1xuICAgIGlmKGFsbG93ZWQgIT09IHRydWUpIHtcbiAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ3JhdGVfbGltaXRlZCcpO1xuICAgIH1cbiAgfTtcbiAgY29uc3QgYWRkTWVtYmVyID0gYXN5bmMgKFxuICAgIHJlYWQ6IENoYXRRdWVyeSwgY29udmVyc2F0aW9uSWQ6IHN0cmluZywgd2hvOiBDaGF0SWRlbnRpdHksIHJvbGU6IE1lbWJlcnNoaXBSb2xlLCBqb2luU2VxdWVuY2U6IG51bWJlclxuICApOiBQcm9taXNlPE1lbWJlclJlY29yZD4gPT4ge1xuICAgIGNvbnN0IGlkID0gcmFuZG9tVVVJRCgpO1xuICAgIGNvbnN0IGV2ZW50U2VxdWVuY2UgPSBhd2FpdCByZWFkPG51bWJlcj4oXG4gICAgICAnUkVUVVJOIERPQ1VNRU5UKFwiY29udmVyc2F0aW9uc1wiLCBAY29udmVyc2F0aW9uSWQpLmxhc3RFdmVudFNlcXVlbmNlIHx8IDAnLCB7Y29udmVyc2F0aW9uSWR9KTtcbiAgICBjb25zdCByZWNvcmQgPSB7X2Zyb206IGBwZXJzb25hcy8ke3doby5wZXJzb25hSWR9YCwgX2tleTogaWQsIF90bzogYGNvbnZlcnNhdGlvbnMvJHtjb252ZXJzYXRpb25JZH1gLFxuICAgICAgYWNjZXB0ZWRBdDogRGF0ZS5ub3coKSwgY29udmVyc2F0aW9uSWQsIGN1cnJlbnRLZXk6IGAke2NvbnZlcnNhdGlvbklkfToke3doby5wZXJzb25hSWR9YCwgZXBvY2g6IHJhbmRvbVVVSUQoKSxcbiAgICAgIGpvaW5FdmVudFNlcXVlbmNlOiBqb2luU2VxdWVuY2UgPiAwID8gKGV2ZW50U2VxdWVuY2UgfHwgMCkgKyAxIDogMCwgam9pblNlcXVlbmNlLCBwZXJzb25hSWQ6IHdoby5wZXJzb25hSWQsIHJldmlzaW9uOiAxLCByb2xlLCBzdGF0ZTogJ2FjdGl2ZScgYXMgY29uc3QsIHVzZXJJZDogd2hvLnVzZXJJZH07XG4gICAgYXdhaXQgcmVhZCgnSU5TRVJUIEByZWNvcmQgSU4gaGFzQ2hhdE1lbWJlcnNoaXAgUkVUVVJOIHRydWUnLCB7cmVjb3JkfSk7XG4gICAgYXdhaXQgcmVhZChgVVBTRVJUIHtfZnJvbTogQGZyb20sIF90bzogQHRvfVxuICAgICAgSU5TRVJUIHtfZnJvbTogQGZyb20sIF90bzogQHRvLCBhZGRlZDogQG5vdywgY29udmVyc2F0aW9uSWQ6IEBjb252ZXJzYXRpb25JZCwgdXNlcklkOiBAdXNlcklkfVxuICAgICAgVVBEQVRFIHt9IElOIGhhc0NvbnZlcnNhdGlvbiBSRVRVUk4gdHJ1ZWAsIHtcbiAgICAgIGNvbnZlcnNhdGlvbklkLCBmcm9tOiBgdXNlcnMvJHt3aG8udXNlcklkfWAsIG5vdzogRGF0ZS5ub3coKSwgdG86IGBjb252ZXJzYXRpb25zLyR7Y29udmVyc2F0aW9uSWR9YCwgdXNlcklkOiB3aG8udXNlcklkXG4gICAgfSk7XG4gICAgcmV0dXJuIHJlY29yZDtcbiAgfTtcbiAgY29uc3QgYWRkSW52aXRlID0gYXN5bmMgKHJlYWQ6IENoYXRRdWVyeSwgY29udmVyc2F0aW9uSWQ6IHN0cmluZywgd2hvOiBDaGF0SWRlbnRpdHkpOiBQcm9taXNlPENvbnZlcnNhdGlvbkludml0ZT4gPT4ge1xuICAgIGNvbnN0IGlkID0gcmFuZG9tVVVJRCgpO1xuICAgIGNvbnN0IHJlY29yZDogSW52aXRlUmVjb3JkID0ge19rZXk6IGlkLCBjb252ZXJzYXRpb25JZCwgZXhwaXJlc0F0OiBEYXRlLm5vdygpICsgaW52aXRlTWlsbGlzZWNvbmRzLFxuICAgICAgaW52aXRlSWQ6IGlkLCBpbnZpdGVkQnlQZXJzb25hSWQ6IGlkZW50aXR5LnBlcnNvbmFJZCwgaW52aXRlZVBlcnNvbmFJZDogd2hvLnBlcnNvbmFJZCwgaW52aXRlZVVzZXJJZDogd2hvLnVzZXJJZCwgcmV2aXNpb246IDEsIHN0YXRlOiAncGVuZGluZyd9O1xuICAgIGF3YWl0IHJlYWQoJ0lOU0VSVCBAcmVjb3JkIElOIGNoYXRJbnZpdGVzIFJFVFVSTiB0cnVlJywge3JlY29yZDogey4uLnJlY29yZCxcbiAgICAgIGNyZWF0ZWRBdDogRGF0ZS5ub3coKSwgY3VycmVudEtleTogYCR7Y29udmVyc2F0aW9uSWR9OiR7d2hvLnBlcnNvbmFJZH1gLCByb2xlOiAnbWVtYmVyJ319KTtcbiAgICBhd2FpdCBldmVudChyZWFkLCBjb252ZXJzYXRpb25JZCwgJycsIGlkLCAxKTtcbiAgICByZXR1cm4gaW52aXRlRHRvKHJlY29yZCk7XG4gIH07XG4gIGNvbnN0IGV4cGlyZUludml0ZXMgPSBhc3luYyAocmVhZDogQ2hhdFF1ZXJ5LCBjb252ZXJzYXRpb25JZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgYXdhaXQgcmVhZChgRk9SIGludml0ZSBJTiBjaGF0SW52aXRlcyBGSUxURVIgaW52aXRlLmNvbnZlcnNhdGlvbklkID09IEBjb252ZXJzYXRpb25JZFxuICAgICAgQU5EIGludml0ZS5zdGF0ZSA9PSAncGVuZGluZycgQU5EIGludml0ZS5leHBpcmVzQXQgPD0gQG5vd1xuICAgICAgVVBEQVRFIGludml0ZSBXSVRIIHtjdXJyZW50S2V5OiBudWxsLCByZXNvbHZlZEF0OiBAbm93LCByZXZpc2lvbjogaW52aXRlLnJldmlzaW9uICsgMSwgc3RhdGU6ICdleHBpcmVkJ31cbiAgICAgIElOIGNoYXRJbnZpdGVzIFJFVFVSTiB0cnVlYCwge2NvbnZlcnNhdGlvbklkLCBub3c6IERhdGUubm93KCl9KTtcbiAgfTtcbiAgY29uc3QgZ2V0T3JDcmVhdGVEaXJlY3QgPSAoaW5wdXQ6IENyZWF0ZURpcmVjdElucHV0KTogUHJvbWlzZTxDb252ZXJzYXRpb25TdW1tYXJ5PiA9PiB0cmFuc2FjdChhc3luYyAocmVhZCkgPT4ge1xuICAgIGF3YWl0IGFjdG9yKHJlYWQpO1xuICAgIGNvbnN0IHRhcmdldCA9IGF3YWl0IGNvbm5lY3RlZChyZWFkLCBpZGVudGl0eSwgaW5wdXQucGFydGljaXBhbnRQZXJzb25hSWQpO1xuICAgIGNvbnN0IHBhaXIgPSBoYXNoKFsnZGlyZWN0LXYxJywgLi4uW2lkZW50aXR5LnBlcnNvbmFJZCwgdGFyZ2V0LnBlcnNvbmFJZF0uc29ydCgpXSk7XG4gICAgY29uc3QgZXhpc3RpbmcgPSBhd2FpdCByZWFkPENvbnZlcnNhdGlvblJlY29yZD4oJ0ZPUiBjIElOIGNvbnZlcnNhdGlvbnMgRklMVEVSIGMuZGlyZWN0UGFpcktleSA9PSBAcGFpciBMSU1JVCAxIFJFVFVSTiBjJywge3BhaXJ9KTtcbiAgICBpZihleGlzdGluZykge1xuICAgICAgYXdhaXQgYWNjZXNzKHJlYWQsIGV4aXN0aW5nLl9rZXkpO1xuICAgICAgcmV0dXJuIHN1bW1hcnkoZXhpc3RpbmcsIGF3YWl0IG1lbWJlcihyZWFkLCBleGlzdGluZy5fa2V5LCBpZGVudGl0eS5wZXJzb25hSWQpLCBhdWRpZW5jZSk7XG4gICAgfVxuICAgIGF3YWl0IHJhdGVMaW1pdChyZWFkKTtcbiAgICBjb25zdCBpZCA9IGBkaXJlY3QtJHtwYWlyfWA7XG4gICAgY29uc3QgcmVjb3JkOiBDb252ZXJzYXRpb25SZWNvcmQgPSB7X2tleTogaWQsIGFjdGl2ZU1lbWJlckNvdW50OiAyLCBjaGF0U2NoZW1hVmVyc2lvbjogMSxcbiAgICAgIGtpbmQ6ICdkaXJlY3QnLCBsYXN0U2VxdWVuY2U6IDAsIG1lbWJlcnNoaXBSZXZpc2lvbjogMSwgc3RhdGU6ICdhY3RpdmUnfTtcbiAgICBhd2FpdCByZWFkKCdJTlNFUlQgQHJlY29yZCBJTiBjb252ZXJzYXRpb25zIFJFVFVSTiB0cnVlJywge3JlY29yZDogey4uLnJlY29yZCxcbiAgICAgIGFkZGVkOiBEYXRlLm5vdygpLCBjcmVhdGVkQnlQZXJzb25hSWQ6IGlkZW50aXR5LnBlcnNvbmFJZCwgZGlyZWN0UGFpcktleTogcGFpciwgaXNEaXJlY3Q6IHRydWV9fSk7XG4gICAgY29uc3Qgb3duID0gYXdhaXQgYWRkTWVtYmVyKHJlYWQsIGlkLCBpZGVudGl0eSwgJ21lbWJlcicsIDApO1xuICAgIGF3YWl0IGFkZE1lbWJlcihyZWFkLCBpZCwgdGFyZ2V0LCAnbWVtYmVyJywgMCk7XG4gICAgYXdhaXQgZXZlbnQocmVhZCwgaWQsIG93bi5lcG9jaCwgaWQsIDEpO1xuICAgIHJldHVybiBzdW1tYXJ5KHJlY29yZCwgb3duLCBhdWRpZW5jZSk7XG4gIH0pO1xuICBjb25zdCBjcmVhdGVHcm91cCA9IChpbnB1dDogQ3JlYXRlR3JvdXBJbnB1dCk6IFByb21pc2U8Q29udmVyc2F0aW9uU3VtbWFyeT4gPT4gdHJhbnNhY3QoYXN5bmMgKHJlYWQpID0+IHtcbiAgICBhd2FpdCBhY3RvcihyZWFkKTtcbiAgICBjb25zdCByZXF1ZXN0ID0gYXdhaXQgcmVjZWlwdDxDb252ZXJzYXRpb25TdW1tYXJ5PihyZWFkLCAnY3JlYXRlR3JvdXAnLCBpbnB1dCk7XG4gICAgaWYocmVxdWVzdC5wcmlvcikge1xuICAgICAgcmV0dXJuIHJlcXVlc3QucHJpb3I7XG4gICAgfVxuICAgIGF3YWl0IHJhdGVMaW1pdChyZWFkKTtcbiAgICBjb25zdCB0YXJnZXRzOiBDaGF0SWRlbnRpdHlbXSA9IFtdO1xuICAgIGZvcihjb25zdCBwZXJzb25hSWQgb2YgaW5wdXQubWVtYmVyUGVyc29uYUlkcykge1xuICAgICAgdGFyZ2V0cy5wdXNoKGF3YWl0IGNvbm5lY3RlZChyZWFkLCBpZGVudGl0eSwgcGVyc29uYUlkKSk7XG4gICAgfVxuICAgIGNvbnN0IGlkID0gcmFuZG9tVVVJRCgpO1xuICAgIGNvbnN0IGNvbnRlbnRWZXJzaW9uID0gaGFzaChbJ2dyb3VwLW5hbWUnLCBpbnB1dC5uYW1lXSk7XG4gICAgY29uc3QgcmVjb3JkOiBDb252ZXJzYXRpb25SZWNvcmQgPSB7X2tleTogaWQsIGFjdGl2ZU1lbWJlckNvdW50OiAxLCBjaGF0U2NoZW1hVmVyc2lvbjogMSwga2luZDogJ2dyb3VwJyxcbiAgICAgIGxhc3RTZXF1ZW5jZTogMCwgbWVtYmVyc2hpcFJldmlzaW9uOiAxLCBuYW1lOiBpbnB1dC5uYW1lLCBuYW1lQ29udGVudFZlcnNpb246IGNvbnRlbnRWZXJzaW9uLFxuICAgICAgbmFtZURlY2lzaW9uOiBvcHRpb25zLmNsYXNzaWZ5TmFtZShpbnB1dC5uYW1lLCBjb250ZW50VmVyc2lvbiksIHN0YXRlOiAnZm9ybWluZyd9O1xuICAgIGF3YWl0IHJlYWQoJ0lOU0VSVCBAcmVjb3JkIElOIGNvbnZlcnNhdGlvbnMgUkVUVVJOIHRydWUnLCB7cmVjb3JkOiB7Li4ucmVjb3JkLFxuICAgICAgYWRkZWQ6IERhdGUubm93KCksIGNyZWF0ZWRCeVBlcnNvbmFJZDogaWRlbnRpdHkucGVyc29uYUlkLCBpc0RpcmVjdDogZmFsc2V9fSk7XG4gICAgY29uc3Qgb3duID0gYXdhaXQgYWRkTWVtYmVyKHJlYWQsIGlkLCBpZGVudGl0eSwgJ293bmVyJywgMCk7XG4gICAgZm9yKGNvbnN0IHRhcmdldCBvZiB0YXJnZXRzKSB7XG4gICAgICBhd2FpdCBhZGRJbnZpdGUocmVhZCwgaWQsIHRhcmdldCk7XG4gICAgfVxuICAgIGF3YWl0IGV2ZW50KHJlYWQsIGlkLCBvd24uZXBvY2gsIGlkLCAxKTtcbiAgICBjb25zdCByZXN1bHQgPSBzdW1tYXJ5KHJlY29yZCwgb3duLCBhdWRpZW5jZSk7XG4gICAgYXdhaXQgcmVxdWVzdC5zYXZlKHJlc3VsdCk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfSk7XG4gIGNvbnN0IGludml0ZU1lbWJlciA9IChpbnB1dDogSW52aXRlTWVtYmVySW5wdXQpOiBQcm9taXNlPENvbnZlcnNhdGlvbkludml0ZT4gPT4gdHJhbnNhY3QoYXN5bmMgKHJlYWQpID0+IHtcbiAgICBhd2FpdCBhY3RvcihyZWFkKTtcbiAgICBjb25zdCByZXF1ZXN0ID0gYXdhaXQgcmVjZWlwdDxDb252ZXJzYXRpb25JbnZpdGU+KHJlYWQsICdpbnZpdGUnLCBpbnB1dCk7XG4gICAgaWYocmVxdWVzdC5wcmlvcikge1xuICAgICAgcmV0dXJuIHJlcXVlc3QucHJpb3I7XG4gICAgfVxuICAgIGNvbnN0IG93biA9IGF3YWl0IGFjY2VzcyhyZWFkLCBpbnB1dC5jb252ZXJzYXRpb25JZCwgJ2ludml0ZScpO1xuICAgIGNoZWNrUmV2aXNpb24ob3duLCBpbnB1dC5leHBlY3RlZFJldmlzaW9uKTtcbiAgICBjb25zdCB0YXJnZXQgPSBhd2FpdCBjb25uZWN0ZWQocmVhZCwgaWRlbnRpdHksIGlucHV0LnBlcnNvbmFJZCk7XG4gICAgYXdhaXQgZXhwaXJlSW52aXRlcyhyZWFkLCBpbnB1dC5jb252ZXJzYXRpb25JZCk7XG4gICAgY29uc3QgcHJpb3IgPSBhd2FpdCByZWFkPEludml0ZVJlY29yZD4oJ0ZPUiBpbnZpdGUgSU4gY2hhdEludml0ZXMgRklMVEVSIGludml0ZS5jdXJyZW50S2V5ID09IEBrZXkgTElNSVQgMSBSRVRVUk4gaW52aXRlJyxcbiAgICAgIHtrZXk6IGAke2lucHV0LmNvbnZlcnNhdGlvbklkfToke2lucHV0LnBlcnNvbmFJZH1gfSk7XG4gICAgaWYocHJpb3IpIHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGludml0ZUR0byhwcmlvcik7XG4gICAgICBhd2FpdCByZXF1ZXN0LnNhdmUocmVzdWx0KTtcbiAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgfVxuICAgIGNvbnN0IGNvdW50cyA9IGF3YWl0IHJlYWQ8e3JlYWRvbmx5IGFjdGl2ZTogYm9vbGVhbjsgcmVhZG9ubHkgdG90YWw6IG51bWJlcn0+KGBSRVRVUk4ge1xuICAgICAgYWN0aXZlOiBMRU5HVEgoRk9SIG1lbWJlciBJTiBoYXNDaGF0TWVtYmVyc2hpcCBGSUxURVIgbWVtYmVyLmNvbnZlcnNhdGlvbklkID09IEBjb252ZXJzYXRpb25JZFxuICAgICAgICBBTkQgbWVtYmVyLnBlcnNvbmFJZCA9PSBAcGVyc29uYUlkIEFORCBtZW1iZXIuc3RhdGUgPT0gJ2FjdGl2ZScgUkVUVVJOIDEpID4gMCxcbiAgICAgIHRvdGFsOiBMRU5HVEgoRk9SIG1lbWJlciBJTiBoYXNDaGF0TWVtYmVyc2hpcCBGSUxURVIgbWVtYmVyLmNvbnZlcnNhdGlvbklkID09IEBjb252ZXJzYXRpb25JZFxuICAgICAgICBBTkQgbWVtYmVyLnN0YXRlID09ICdhY3RpdmUnIFJFVFVSTiAxKSArIExFTkdUSChGT1IgaW52aXRlIElOIGNoYXRJbnZpdGVzXG4gICAgICAgIEZJTFRFUiBpbnZpdGUuY29udmVyc2F0aW9uSWQgPT0gQGNvbnZlcnNhdGlvbklkIEFORCBpbnZpdGUuc3RhdGUgPT0gJ3BlbmRpbmcnIEFORCBpbnZpdGUuZXhwaXJlc0F0ID4gQG5vdyBSRVRVUk4gMSlcbiAgICB9YCwge2NvbnZlcnNhdGlvbklkOiBpbnB1dC5jb252ZXJzYXRpb25JZCwgbm93OiBEYXRlLm5vdygpLCBwZXJzb25hSWQ6IGlucHV0LnBlcnNvbmFJZH0pO1xuICAgIGlmKCFjb3VudHMgfHwgY291bnRzLmFjdGl2ZSB8fCBjb3VudHMudG90YWwgPj0gMjUpIHtcbiAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ2NvbmZsaWN0Jyk7XG4gICAgfVxuICAgIGF3YWl0IHJhdGVMaW1pdChyZWFkKTtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBhZGRJbnZpdGUocmVhZCwgaW5wdXQuY29udmVyc2F0aW9uSWQsIHRhcmdldCk7XG4gICAgYXdhaXQgcmVxdWVzdC5zYXZlKHJlc3VsdCk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfSk7XG4gIGNvbnN0IHJlc29sdmVJbnZpdGUgPSAoXG4gICAgaW5wdXQ6IEFjY2VwdEludml0ZUlucHV0LCBhY2NlcHQ6IGJvb2xlYW5cbiAgKTogUHJvbWlzZTxDb252ZXJzYXRpb25TdW1tYXJ5IHwgQ29udmVyc2F0aW9uSW52aXRlPiA9PlxuICAgIHRyYW5zYWN0KGFzeW5jIChyZWFkKSA9PiB7XG4gICAgICBhd2FpdCBhY3RvcihyZWFkKTtcbiAgICAgIGNvbnN0IHJlcXVlc3QgPSBhd2FpdCByZWNlaXB0PENvbnZlcnNhdGlvblN1bW1hcnkgfCBDb252ZXJzYXRpb25JbnZpdGU+KHJlYWQsIGFjY2VwdCA/ICdhY2NlcHQnIDogJ2RlY2xpbmUnLCBpbnB1dCk7XG4gICAgICBpZihyZXF1ZXN0LnByaW9yKSB7XG4gICAgICAgIHJldHVybiByZXF1ZXN0LnByaW9yO1xuICAgICAgfVxuICAgICAgY29uc3QgaW52aXRlID0gYXdhaXQgcmVhZDxJbnZpdGVSZWNvcmQ+KCdSRVRVUk4gRE9DVU1FTlQoXCJjaGF0SW52aXRlc1wiLCBAaWQpJywge2lkOiBpbnB1dC5pbnZpdGVJZH0pO1xuICAgICAgaWYoIWludml0ZSB8fCBpbnZpdGUuaW52aXRlZVBlcnNvbmFJZCAhPT0gaWRlbnRpdHkucGVyc29uYUlkIHx8IGludml0ZS5pbnZpdGVlVXNlcklkICE9PSBpZGVudGl0eS51c2VySWRcbiAgICAgICAgfHwgaW52aXRlLnN0YXRlICE9PSAncGVuZGluZycgfHwgaW52aXRlLmV4cGlyZXNBdCA8PSBEYXRlLm5vdygpKSB7XG4gICAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ2ZvcmJpZGRlbicpO1xuICAgICAgfVxuICAgICAgaWYoaW52aXRlLnJldmlzaW9uICE9PSBpbnB1dC5leHBlY3RlZFJldmlzaW9uKSB7XG4gICAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ2NvbmZsaWN0Jyk7XG4gICAgICB9XG4gICAgICBjb25zdCBjdXJyZW50ID0gYXdhaXQgY29udmVyc2F0aW9uKHJlYWQsIGludml0ZS5jb252ZXJzYXRpb25JZCk7XG4gICAgICBpZihjdXJyZW50LmtpbmQgIT09ICdncm91cCcpIHtcbiAgICAgICAgdGhyb3cgbmV3IENoYXRFcnJvcignZm9yYmlkZGVuJyk7XG4gICAgICB9XG4gICAgICBsZXQgcmVzdWx0OiBDb252ZXJzYXRpb25TdW1tYXJ5IHwgQ29udmVyc2F0aW9uSW52aXRlO1xuICAgICAgaWYoYWNjZXB0KSB7XG4gICAgICAgIGNvbnN0IGludml0ZXIgPSBhd2FpdCBtZW1iZXIocmVhZCwgY3VycmVudC5fa2V5LCBpbnZpdGUuaW52aXRlZEJ5UGVyc29uYUlkKTtcbiAgICAgICAgaWYoIVsnb3duZXInLCAnYWRtaW4nXS5pbmNsdWRlcyhpbnZpdGVyLnJvbGUpIHx8IGN1cnJlbnQuYWN0aXZlTWVtYmVyQ291bnQgPj0gMjUpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdmb3JiaWRkZW4nKTtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCBhZHVsdChyZWFkLCB7cGVyc29uYUlkOiBpbnZpdGVyLnBlcnNvbmFJZCwgdXNlcklkOiBpbnZpdGVyLnVzZXJJZH0pO1xuICAgICAgICBhd2FpdCBjb25uZWN0ZWQocmVhZCwge3BlcnNvbmFJZDogaW52aXRlci5wZXJzb25hSWQsIHVzZXJJZDogaW52aXRlci51c2VySWR9LCBpZGVudGl0eS5wZXJzb25hSWQpO1xuICAgICAgICBjb25zdCBvd24gPSBhd2FpdCBhZGRNZW1iZXIocmVhZCwgY3VycmVudC5fa2V5LCBpZGVudGl0eSwgJ21lbWJlcicsIGN1cnJlbnQubGFzdFNlcXVlbmNlICsgMSk7XG4gICAgICAgIGNvbnN0IHVwZGF0ZWQgPSBhd2FpdCByZWFkPENvbnZlcnNhdGlvblJlY29yZD4oYFVQREFURSBAaWQgV0lUSCB7XG4gICAgICAgICAgYWN0aXZlTWVtYmVyQ291bnQ6IEBjb3VudCwgbWVtYmVyc2hpcFJldmlzaW9uOiBAcmV2aXNpb24sIHN0YXRlOiBAc3RhdGV9XG4gICAgICAgICAgSU4gY29udmVyc2F0aW9ucyBSRVRVUk4gTkVXYCwge2NvdW50OiBjdXJyZW50LmFjdGl2ZU1lbWJlckNvdW50ICsgMSwgaWQ6IGN1cnJlbnQuX2tleSxcbiAgICAgICAgICByZXZpc2lvbjogY3VycmVudC5tZW1iZXJzaGlwUmV2aXNpb24gKyAxLFxuICAgICAgICAgIHN0YXRlOiBjdXJyZW50LmFjdGl2ZU1lbWJlckNvdW50ICsgMSA+PSAzID8gJ2FjdGl2ZScgOiBjdXJyZW50LnN0YXRlfSk7XG4gICAgICAgIGlmKCF1cGRhdGVkKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IENoYXRFcnJvcigndW5hdmFpbGFibGUnKTtcbiAgICAgICAgfVxuICAgICAgICBhd2FpdCBldmVudChyZWFkLCBjdXJyZW50Ll9rZXksIG93bi5lcG9jaCwgb3duLl9rZXksIHVwZGF0ZWQubWVtYmVyc2hpcFJldmlzaW9uKTtcbiAgICAgICAgcmVzdWx0ID0gc3VtbWFyeSh1cGRhdGVkLCBvd24sIGF1ZGllbmNlKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJlc3VsdCA9IHsuLi5pbnZpdGVEdG8oaW52aXRlKSwgcmV2aXNpb246IGludml0ZS5yZXZpc2lvbiArIDEsIHN0YXRlOiAnZGVjbGluZWQnfTtcbiAgICAgICAgYXdhaXQgZXZlbnQocmVhZCwgY3VycmVudC5fa2V5LCAnJywgaW52aXRlLl9rZXksIGludml0ZS5yZXZpc2lvbiArIDEpO1xuICAgICAgfVxuICAgICAgYXdhaXQgcmVhZChgVVBEQVRFIEBpZCBXSVRIIHtjdXJyZW50S2V5OiBudWxsLCByZXNvbHZlZEF0OiBAbm93LCByZXZpc2lvbjogQHJldmlzaW9uLCBzdGF0ZTogQHN0YXRlfVxuICAgICAgICBJTiBjaGF0SW52aXRlcyBSRVRVUk4gdHJ1ZWAsIHtpZDogaW52aXRlLl9rZXksIG5vdzogRGF0ZS5ub3coKSwgcmV2aXNpb246IGludml0ZS5yZXZpc2lvbiArIDEsXG4gICAgICAgIHN0YXRlOiBhY2NlcHQgPyAnYWNjZXB0ZWQnIDogJ2RlY2xpbmVkJ30pO1xuICAgICAgYXdhaXQgcmVxdWVzdC5zYXZlKHJlc3VsdCk7XG4gICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH0pO1xuICBjb25zdCBjaGFuZ2VNZW1iZXJzaGlwID0gKFxuICAgIGFjdGlvbjogJ2xlYXZlJyB8ICdyZW1vdmUnIHwgJ3JvbGUnIHwgJ3RyYW5zZmVyJyxcbiAgICBpbnB1dDogQ2hhbmdlTWVtYmVyUm9sZUlucHV0IHwgTGVhdmVDb252ZXJzYXRpb25JbnB1dCB8IFJlbW92ZU1lbWJlcklucHV0XG4gICk6IFByb21pc2U8Q29udmVyc2F0aW9uU3VtbWFyeT4gPT4gdHJhbnNhY3QoYXN5bmMgKHJlYWQpID0+IHtcbiAgICBhd2FpdCBhY3RvcihyZWFkKTtcbiAgICBjb25zdCByZXF1ZXN0ID0gYXdhaXQgcmVjZWlwdDxDb252ZXJzYXRpb25TdW1tYXJ5PihyZWFkLCBhY3Rpb24sIGlucHV0KTtcbiAgICBpZihyZXF1ZXN0LnByaW9yKSB7XG4gICAgICByZXR1cm4gcmVxdWVzdC5wcmlvcjtcbiAgICB9XG4gICAgY29uc3Qgb3duQWNjZXNzID0gYXdhaXQgYWNjZXNzKHJlYWQsIGlucHV0LmNvbnZlcnNhdGlvbklkKTtcbiAgICBjaGVja1JldmlzaW9uKG93bkFjY2VzcywgaW5wdXQuZXhwZWN0ZWRSZXZpc2lvbik7XG4gICAgY29uc3QgY3VycmVudCA9IGF3YWl0IGNvbnZlcnNhdGlvbihyZWFkLCBpbnB1dC5jb252ZXJzYXRpb25JZCk7XG4gICAgY29uc3Qgb3duID0gYXdhaXQgbWVtYmVyKHJlYWQsIGN1cnJlbnQuX2tleSwgaWRlbnRpdHkucGVyc29uYUlkKTtcbiAgICBjb25zdCB0YXJnZXRQZXJzb25hSWQgPSAncGVyc29uYUlkJyBpbiBpbnB1dCA/IGlucHV0LnBlcnNvbmFJZCA6IGlkZW50aXR5LnBlcnNvbmFJZDtcbiAgICBjb25zdCB0YXJnZXQgPSB0YXJnZXRQZXJzb25hSWQgPT09IGlkZW50aXR5LnBlcnNvbmFJZCA/IG93biA6IGF3YWl0IG1lbWJlcihyZWFkLCBjdXJyZW50Ll9rZXksIHRhcmdldFBlcnNvbmFJZCk7XG4gICAgY29uc3QgbGVhdmluZyA9IGFjdGlvbiA9PT0gJ2xlYXZlJyB8fCBhY3Rpb24gPT09ICdyZW1vdmUnO1xuICAgIGlmKChhY3Rpb24gPT09ICdyb2xlJyB8fCBhY3Rpb24gPT09ICd0cmFuc2ZlcicpICYmIChvd24ucm9sZSAhPT0gJ293bmVyJyB8fCB0YXJnZXQuX2tleSA9PT0gb3duLl9rZXkpKSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdmb3JiaWRkZW4nKTtcbiAgICB9XG4gICAgaWYoYWN0aW9uID09PSAncmVtb3ZlJyAmJiAodGFyZ2V0Ll9rZXkgPT09IG93bi5fa2V5IHx8IHRhcmdldC5yb2xlID09PSAnb3duZXInXG4gICAgICB8fCAhWydvd25lcicsICdhZG1pbiddLmluY2x1ZGVzKG93bi5yb2xlKSB8fCAob3duLnJvbGUgPT09ICdhZG1pbicgJiYgdGFyZ2V0LnJvbGUgIT09ICdtZW1iZXInKSkpIHtcbiAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ2ZvcmJpZGRlbicpO1xuICAgIH1cbiAgICBpZihhY3Rpb24gPT09ICdsZWF2ZScgJiYgb3duLnJvbGUgPT09ICdvd25lcicgJiYgY3VycmVudC5hY3RpdmVNZW1iZXJDb3VudCA+IDEpIHtcbiAgICAgIHRocm93IG5ldyBDaGF0RXJyb3IoJ2NvbmZsaWN0Jyk7XG4gICAgfVxuICAgIGlmKGFjdGlvbiA9PT0gJ3JvbGUnICYmIHRhcmdldC5yb2xlID09PSAnb3duZXInKSB7XG4gICAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdmb3JiaWRkZW4nKTtcbiAgICB9XG4gICAgaWYoYWN0aW9uID09PSAndHJhbnNmZXInIHx8IChhY3Rpb24gPT09ICdyb2xlJyAmJiAncm9sZScgaW4gaW5wdXQgJiYgaW5wdXQucm9sZSA9PT0gJ2FkbWluJykpIHtcbiAgICAgIGF3YWl0IGFkdWx0KHJlYWQsIHtwZXJzb25hSWQ6IHRhcmdldC5wZXJzb25hSWQsIHVzZXJJZDogdGFyZ2V0LnVzZXJJZH0pO1xuICAgIH1cbiAgICBsZXQgb3duUmVzdWx0ID0gb3duO1xuICAgIGlmKGFjdGlvbiA9PT0gJ3RyYW5zZmVyJykge1xuICAgICAgYXdhaXQgcmVhZCgnVVBEQVRFIEBpZCBXSVRIIHtyb2xlOiBcImFkbWluXCIsIHJldmlzaW9uOiBAcmV2aXNpb259IElOIGhhc0NoYXRNZW1iZXJzaGlwIFJFVFVSTiB0cnVlJyxcbiAgICAgICAge2lkOiBvd24uX2tleSwgcmV2aXNpb246IG93bi5yZXZpc2lvbiArIDF9KTtcbiAgICAgIGF3YWl0IHJlYWQoJ1VQREFURSBAaWQgV0lUSCB7cm9sZTogXCJvd25lclwiLCByZXZpc2lvbjogQHJldmlzaW9ufSBJTiBoYXNDaGF0TWVtYmVyc2hpcCBSRVRVUk4gdHJ1ZScsXG4gICAgICAgIHtpZDogdGFyZ2V0Ll9rZXksIHJldmlzaW9uOiB0YXJnZXQucmV2aXNpb24gKyAxfSk7XG4gICAgICBvd25SZXN1bHQgPSB7Li4ub3duLCByZXZpc2lvbjogb3duLnJldmlzaW9uICsgMSwgcm9sZTogJ2FkbWluJ307XG4gICAgfSBlbHNlIGlmKGFjdGlvbiA9PT0gJ3JvbGUnICYmICdyb2xlJyBpbiBpbnB1dCkge1xuICAgICAgYXdhaXQgcmVhZCgnVVBEQVRFIEBpZCBXSVRIIHtyb2xlOiBAcm9sZSwgcmV2aXNpb246IEByZXZpc2lvbn0gSU4gaGFzQ2hhdE1lbWJlcnNoaXAgUkVUVVJOIHRydWUnLFxuICAgICAgICB7aWQ6IHRhcmdldC5fa2V5LCByZXZpc2lvbjogdGFyZ2V0LnJldmlzaW9uICsgMSwgcm9sZTogaW5wdXQucm9sZX0pO1xuICAgIH0gZWxzZSBpZihsZWF2aW5nKSB7XG4gICAgICBjb25zdCBzdGF0ZSA9IGFjdGlvbiA9PT0gJ2xlYXZlJyA/ICdsZWZ0JyA6ICdyZW1vdmVkJztcbiAgICAgIGF3YWl0IHJlYWQoYFVQREFURSBAaWQgV0lUSCB7Y3VycmVudEtleTogbnVsbCwgZW5kZWRBdDogQG5vdywgZW5kZWRCeVBlcnNvbmFJZDogQGFjdG9yLFxuICAgICAgICBsZWZ0U2VxdWVuY2U6IEBzZXF1ZW5jZSwgcmV2aXNpb246IEByZXZpc2lvbiwgc3RhdGU6IEBzdGF0ZX0gSU4gaGFzQ2hhdE1lbWJlcnNoaXAgUkVUVVJOIHRydWVgLCB7XG4gICAgICAgIGFjdG9yOiBpZGVudGl0eS5wZXJzb25hSWQsIGlkOiB0YXJnZXQuX2tleSwgbm93OiBEYXRlLm5vdygpLCByZXZpc2lvbjogdGFyZ2V0LnJldmlzaW9uICsgMSxcbiAgICAgICAgc2VxdWVuY2U6IGN1cnJlbnQubGFzdFNlcXVlbmNlLCBzdGF0ZVxuICAgICAgfSk7XG4gICAgICBpZihhY3Rpb24gPT09ICdsZWF2ZScpIHtcbiAgICAgICAgb3duUmVzdWx0ID0gey4uLm93biwgbGVmdFNlcXVlbmNlOiBjdXJyZW50Lmxhc3RTZXF1ZW5jZSwgcmV2aXNpb246IG93bi5yZXZpc2lvbiArIDEsIHN0YXRlfTtcbiAgICAgIH1cbiAgICAgIGF3YWl0IHJlYWQoYEZPUiBlZGdlIElOIGhhc0NvbnZlcnNhdGlvbiBGSUxURVIgZWRnZS5fZnJvbSA9PSBAZnJvbSBBTkQgZWRnZS5fdG8gPT0gQHRvXG4gICAgICAgIExFVCByZW1haW5pbmcgPSBGSVJTVChGT1IgbWVtYmVyIElOIGhhc0NoYXRNZW1iZXJzaGlwIEZJTFRFUiBtZW1iZXIuY29udmVyc2F0aW9uSWQgPT0gQGNvbnZlcnNhdGlvbklkXG4gICAgICAgICAgQU5EIG1lbWJlci51c2VySWQgPT0gQHVzZXJJZCBBTkQgbWVtYmVyLnN0YXRlID09ICdhY3RpdmUnIExJTUlUIDEgUkVUVVJOIHRydWUpXG4gICAgICAgIEZJTFRFUiByZW1haW5pbmcgIT0gdHJ1ZSBSRU1PVkUgZWRnZSBJTiBoYXNDb252ZXJzYXRpb24gUkVUVVJOIHRydWVgLCB7XG4gICAgICAgIGNvbnZlcnNhdGlvbklkOiBjdXJyZW50Ll9rZXksIGZyb206IGB1c2Vycy8ke3RhcmdldC51c2VySWR9YCwgdG86IGBjb252ZXJzYXRpb25zLyR7Y3VycmVudC5fa2V5fWAsIHVzZXJJZDogdGFyZ2V0LnVzZXJJZFxuICAgICAgfSk7XG4gICAgfVxuICAgIGlmKGxlYXZpbmcgfHwgKGFjdGlvbiA9PT0gJ3JvbGUnICYmICdyb2xlJyBpbiBpbnB1dCAmJiBpbnB1dC5yb2xlID09PSAnbWVtYmVyJykpIHtcbiAgICAgIGF3YWl0IHJlYWQoYEZPUiBpbnZpdGUgSU4gY2hhdEludml0ZXMgRklMVEVSIGludml0ZS5jb252ZXJzYXRpb25JZCA9PSBAY29udmVyc2F0aW9uSWRcbiAgICAgICAgQU5EIGludml0ZS5zdGF0ZSA9PSAncGVuZGluZycgQU5EIChpbnZpdGUuaW52aXRlZEJ5UGVyc29uYUlkID09IEBwZXJzb25hSWQgT1IgQGNsb3NlKVxuICAgICAgICBVUERBVEUgaW52aXRlIFdJVEgge2N1cnJlbnRLZXk6IG51bGwsIHJlc29sdmVkQXQ6IEBub3csIHJldmlzaW9uOiBpbnZpdGUucmV2aXNpb24gKyAxLCBzdGF0ZTogJ3Jldm9rZWQnfVxuICAgICAgICBJTiBjaGF0SW52aXRlcyBSRVRVUk4gdHJ1ZWAsIHtjbG9zZTogbGVhdmluZyAmJiBjdXJyZW50LmFjdGl2ZU1lbWJlckNvdW50ID09PSAxLFxuICAgICAgICBjb252ZXJzYXRpb25JZDogY3VycmVudC5fa2V5LCBub3c6IERhdGUubm93KCksIHBlcnNvbmFJZDogdGFyZ2V0LnBlcnNvbmFJZH0pO1xuICAgIH1cbiAgICBjb25zdCBjb3VudCA9IGN1cnJlbnQuYWN0aXZlTWVtYmVyQ291bnQgLSAobGVhdmluZyA/IDEgOiAwKTtcbiAgICBjb25zdCB1cGRhdGVkID0gYXdhaXQgcmVhZDxDb252ZXJzYXRpb25SZWNvcmQ+KGBVUERBVEUgQGlkIFdJVEgge1xuICAgICAgYWN0aXZlTWVtYmVyQ291bnQ6IEBjb3VudCwgbWVtYmVyc2hpcFJldmlzaW9uOiBAcmV2aXNpb24sIHN0YXRlOiBAc3RhdGV9XG4gICAgICBJTiBjb252ZXJzYXRpb25zIFJFVFVSTiBORVdgLCB7Y291bnQsIGlkOiBjdXJyZW50Ll9rZXksIHJldmlzaW9uOiBjdXJyZW50Lm1lbWJlcnNoaXBSZXZpc2lvbiArIDEsXG4gICAgICBzdGF0ZTogY291bnQgPT09IDAgPyAnY2xvc2VkJyA6IGN1cnJlbnQuc3RhdGV9KTtcbiAgICBpZighdXBkYXRlZCkge1xuICAgICAgdGhyb3cgbmV3IENoYXRFcnJvcigndW5hdmFpbGFibGUnKTtcbiAgICB9XG4gICAgYXdhaXQgZXZlbnQocmVhZCwgY3VycmVudC5fa2V5LCB0YXJnZXQuZXBvY2gsIHRhcmdldC5fa2V5LCB1cGRhdGVkLm1lbWJlcnNoaXBSZXZpc2lvbik7XG4gICAgY29uc3QgcmVzdWx0ID0gc3VtbWFyeSh1cGRhdGVkLCBvd25SZXN1bHQsIGF1ZGllbmNlKTtcbiAgICBhd2FpdCByZXF1ZXN0LnNhdmUocmVzdWx0KTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9KTtcbiAgY29uc3QgbGlzdENvbnZlcnNhdGlvbnMgPSAoaW5wdXQ6IENoYXRMaXN0SW5wdXQpOiBQcm9taXNlPHJlYWRvbmx5IENvbnZlcnNhdGlvblN1bW1hcnlbXT4gPT5cbiAgICB0cmFuc2FjdChhc3luYyAocmVhZCkgPT4ge1xuICAgICAgYXdhaXQgYWN0b3IocmVhZCk7XG4gICAgICBjb25zdCBpZHMgPSBhd2FpdCByZWFkPHN0cmluZ1tdPihgUkVUVVJOIChGT1IgbWVtYmVyIElOIGhhc0NoYXRNZW1iZXJzaGlwXG4gICAgICBGSUxURVIgbWVtYmVyLnBlcnNvbmFJZCA9PSBAcGVyc29uYUlkIEFORCBtZW1iZXIudXNlcklkID09IEB1c2VySWQgQU5EIG1lbWJlci5zdGF0ZSA9PSAnYWN0aXZlJ1xuICAgICAgICBBTkQgbWVtYmVyLmNvbnZlcnNhdGlvbklkID4gQGFmdGVyIFNPUlQgbWVtYmVyLmNvbnZlcnNhdGlvbklkIExJTUlUIEBsaW1pdCBSRVRVUk4gbWVtYmVyLmNvbnZlcnNhdGlvbklkKWAsIHtcbiAgICAgICAgLi4uaWRlbnRpdHksIGFmdGVyOiBpbnB1dC5hZnRlciB8fCAnJywgbGltaXQ6IGlucHV0LmxpbWl0IHx8IDI1XG4gICAgICB9KTtcbiAgICAgIGNvbnN0IHJlc3VsdHM6IENvbnZlcnNhdGlvblN1bW1hcnlbXSA9IFtdO1xuICAgICAgZm9yKGNvbnN0IGlkIG9mIGlkcyB8fCBbXSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IGFjY2VzcyhyZWFkLCBpZCk7XG4gICAgICAgICAgY29uc3QgY3VycmVudCA9IGF3YWl0IGNvbnZlcnNhdGlvbihyZWFkLCBpZCk7XG4gICAgICAgICAgaWYoY3VycmVudC5raW5kID09PSAnZGlyZWN0Jykge1xuICAgICAgICAgICAgY29uc3QgcGVlciA9IGF3YWl0IHJlYWQ8TWVtYmVyUmVjb3JkPihgRk9SIG1lbWJlciBJTiBoYXNDaGF0TWVtYmVyc2hpcCBGSUxURVIgbWVtYmVyLmNvbnZlcnNhdGlvbklkID09IEBpZFxuICAgICAgICAgICAgQU5EIG1lbWJlci5wZXJzb25hSWQgIT0gQHBlcnNvbmFJZCBBTkQgbWVtYmVyLnN0YXRlID09ICdhY3RpdmUnIExJTUlUIDEgUkVUVVJOIG1lbWJlcmAsIHtpZCwgcGVyc29uYUlkOiBpZGVudGl0eS5wZXJzb25hSWR9KTtcbiAgICAgICAgICAgIGlmKCFwZWVyKSB7XG4gICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYXdhaXQgY29ubmVjdGVkKHJlYWQsIGlkZW50aXR5LCBwZWVyLnBlcnNvbmFJZCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJlc3VsdHMucHVzaChzdW1tYXJ5KGN1cnJlbnQsIGF3YWl0IG1lbWJlcihyZWFkLCBpZCwgaWRlbnRpdHkucGVyc29uYUlkKSwgYXVkaWVuY2UsIHRydWUpKTtcbiAgICAgICAgfSBjYXRjaChlcnJvcikge1xuICAgICAgICAgIGlmKCEoZXJyb3IgaW5zdGFuY2VvZiBDaGF0RXJyb3IpIHx8IGVycm9yLmNvZGUgIT09ICdmb3JiaWRkZW4nKSB7XG4gICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiByZXN1bHRzO1xuICAgIH0pO1xuICBjb25zdCBsaXN0SW52aXRlcyA9IChpbnB1dDogQ2hhdExpc3RJbnB1dCk6IFByb21pc2U8cmVhZG9ubHkgQ29udmVyc2F0aW9uSW52aXRlW10+ID0+IHRyYW5zYWN0KGFzeW5jIChyZWFkKSA9PiB7XG4gICAgYXdhaXQgYWN0b3IocmVhZCk7XG4gICAgY29uc3QgcmVjb3JkcyA9IGF3YWl0IHJlYWQ8SW52aXRlUmVjb3JkW10+KGBSRVRVUk4gKEZPUiBpbnZpdGUgSU4gY2hhdEludml0ZXNcbiAgICAgIEZJTFRFUiBpbnZpdGUuaW52aXRlZVBlcnNvbmFJZCA9PSBAcGVyc29uYUlkIEFORCBpbnZpdGUuaW52aXRlZVVzZXJJZCA9PSBAdXNlcklkIEFORCBpbnZpdGUuc3RhdGUgPT0gJ3BlbmRpbmcnXG4gICAgICAgIEFORCBpbnZpdGUuZXhwaXJlc0F0ID4gQG5vdyBBTkQgaW52aXRlLl9rZXkgPiBAYWZ0ZXJcbiAgICAgIExFVCBjb252ZXJzYXRpb24gPSBET0NVTUVOVCgnY29udmVyc2F0aW9ucycsIGludml0ZS5jb252ZXJzYXRpb25JZClcbiAgICAgIExFVCBpbnZpdGVyID0gRE9DVU1FTlQoJ3BlcnNvbmFzJywgaW52aXRlLmludml0ZWRCeVBlcnNvbmFJZClcbiAgICAgIExFVCBpbnZpdGVyVXNlciA9IERPQ1VNRU5UKCd1c2VycycsIGludml0ZXIudXNlcklkKVxuICAgICAgTEVUIGludml0ZXJNZW1iZXJzaGlwID0gRklSU1QoRk9SIG1lbWJlciBJTiBoYXNDaGF0TWVtYmVyc2hpcFxuICAgICAgICBGSUxURVIgbWVtYmVyLmNvbnZlcnNhdGlvbklkID09IGludml0ZS5jb252ZXJzYXRpb25JZCBBTkQgbWVtYmVyLnBlcnNvbmFJZCA9PSBpbnZpdGUuaW52aXRlZEJ5UGVyc29uYUlkXG4gICAgICAgICAgQU5EIG1lbWJlci51c2VySWQgPT0gaW52aXRlci51c2VySWQgQU5EIG1lbWJlci5zdGF0ZSA9PSAnYWN0aXZlJyBBTkQgbWVtYmVyLnJvbGUgSU4gWydvd25lcicsICdhZG1pbiddXG4gICAgICAgIExJTUlUIDEgUkVUVVJOIHRydWUpXG4gICAgICBMRVQgYmxvY2tlZCA9IEZJUlNUKEZPUiBlZGdlIElOIGhhc0Nvbm5lY3Rpb24gRklMVEVSIGVkZ2UudHlwZSA9PSAnYmxvY2snXG4gICAgICAgIEFORCAoKGVkZ2UuX2Zyb20gSU4gW0NPTkNBVCgncGVyc29uYXMvJywgQHBlcnNvbmFJZCksIENPTkNBVCgndXNlcnMvJywgQHVzZXJJZCldXG4gICAgICAgICAgQU5EIGVkZ2UuX3RvIElOIFtDT05DQVQoJ3BlcnNvbmFzLycsIGludml0ZS5pbnZpdGVkQnlQZXJzb25hSWQpLCBDT05DQVQoJ3VzZXJzLycsIGludml0ZXIudXNlcklkKV0pXG4gICAgICAgICAgT1IgKGVkZ2UuX3RvIElOIFtDT05DQVQoJ3BlcnNvbmFzLycsIEBwZXJzb25hSWQpLCBDT05DQVQoJ3VzZXJzLycsIEB1c2VySWQpXVxuICAgICAgICAgIEFORCBlZGdlLl9mcm9tIElOIFtDT05DQVQoJ3BlcnNvbmFzLycsIGludml0ZS5pbnZpdGVkQnlQZXJzb25hSWQpLCBDT05DQVQoJ3VzZXJzLycsIGludml0ZXIudXNlcklkKV0pKVxuICAgICAgICBMSU1JVCAxIFJFVFVSTiB0cnVlKVxuICAgICAgRklMVEVSIGNvbnZlcnNhdGlvbi5jaGF0U2NoZW1hVmVyc2lvbiA9PSAxIEFORCBjb252ZXJzYXRpb24uc3RhdGUgSU4gWydhY3RpdmUnLCAnZm9ybWluZyddXG4gICAgICAgIEFORCBpbnZpdGVyICE9IG51bGwgQU5EIGludml0ZXJVc2VyICE9IG51bGwgQU5EIGludml0ZXIuYWN0aXZlICE9IGZhbHNlIEFORCBpbnZpdGVyVXNlci5hY3RpdmUgIT0gZmFsc2VcbiAgICAgICAgQU5EIGludml0ZXJNZW1iZXJzaGlwID09IHRydWUgQU5EIGJsb2NrZWQgIT0gdHJ1ZVxuICAgICAgU09SVCBpbnZpdGUuX2tleSBMSU1JVCBAbGltaXQgUkVUVVJOIGludml0ZSlgLCB7Li4uaWRlbnRpdHksIGFmdGVyOiBpbnB1dC5hZnRlciB8fCAnJywgbGltaXQ6IGlucHV0LmxpbWl0IHx8IDI1LCBub3c6IERhdGUubm93KCl9KTtcbiAgICByZXR1cm4gKHJlY29yZHMgfHwgW10pLm1hcChpbnZpdGVEdG8pO1xuICB9KTtcbiAgY29uc3QgZ2V0UGFydGljaXBhbnRzID0gKGlucHV0OiB7cmVhZG9ubHkgY29udmVyc2F0aW9uSWQ6IHN0cmluZ30pOiBQcm9taXNlPHJlYWRvbmx5IENvbnZlcnNhdGlvbk1lbWJlcnNoaXBbXT4gPT5cbiAgICB0cmFuc2FjdChhc3luYyAocmVhZCkgPT4ge1xuICAgICAgYXdhaXQgYWN0b3IocmVhZCk7XG4gICAgICBhd2FpdCBhY2Nlc3MocmVhZCwgaW5wdXQuY29udmVyc2F0aW9uSWQpO1xuICAgICAgY29uc3QgcmVjb3JkcyA9IGF3YWl0IHJlYWQ8TWVtYmVyUmVjb3JkW10+KGBSRVRVUk4gKEZPUiBtZW1iZXIgSU4gaGFzQ2hhdE1lbWJlcnNoaXBcbiAgICAgICAgRklMVEVSIG1lbWJlci5jb252ZXJzYXRpb25JZCA9PSBAY29udmVyc2F0aW9uSWQgQU5EIG1lbWJlci5zdGF0ZSA9PSAnYWN0aXZlJ1xuICAgICAgICBMRVQgcGVyc29uYSA9IERPQ1VNRU5UKCdwZXJzb25hcycsIG1lbWJlci5wZXJzb25hSWQpIExFVCB1c2VyID0gRE9DVU1FTlQoJ3VzZXJzJywgbWVtYmVyLnVzZXJJZClcbiAgICAgICAgTEVUIGJsb2NrZWQgPSBGSVJTVChGT1IgZWRnZSBJTiBoYXNDb25uZWN0aW9uIEZJTFRFUiBlZGdlLnR5cGUgPT0gJ2Jsb2NrJ1xuICAgICAgICAgIEFORCAoKGVkZ2UuX2Zyb20gSU4gW0B2aWV3ZXJQZXJzb25hLCBAdmlld2VyVXNlcl1cbiAgICAgICAgICAgIEFORCBlZGdlLl90byBJTiBbQ09OQ0FUKCdwZXJzb25hcy8nLCBtZW1iZXIucGVyc29uYUlkKSwgQ09OQ0FUKCd1c2Vycy8nLCBtZW1iZXIudXNlcklkKV0pXG4gICAgICAgICAgT1IgKGVkZ2UuX3RvIElOIFtAdmlld2VyUGVyc29uYSwgQHZpZXdlclVzZXJdXG4gICAgICAgICAgICBBTkQgZWRnZS5fZnJvbSBJTiBbQ09OQ0FUKCdwZXJzb25hcy8nLCBtZW1iZXIucGVyc29uYUlkKSwgQ09OQ0FUKCd1c2Vycy8nLCBtZW1iZXIudXNlcklkKV0pKSBMSU1JVCAxIFJFVFVSTiB0cnVlKVxuICAgICAgICBGSUxURVIgcGVyc29uYSAhPSBudWxsIEFORCB1c2VyICE9IG51bGwgQU5EIHBlcnNvbmEuYWN0aXZlICE9IGZhbHNlIEFORCB1c2VyLmFjdGl2ZSAhPSBmYWxzZVxuICAgICAgICAgIEFORCBwZXJzb25hLnVzZXJJZCA9PSBtZW1iZXIudXNlcklkIEFORCBibG9ja2VkICE9IHRydWVcbiAgICAgICAgU09SVCBtZW1iZXIucGVyc29uYUlkIExJTUlUIDI1IFJFVFVSTiBtZW1iZXIpYCwge1xuICAgICAgICBjb252ZXJzYXRpb25JZDogaW5wdXQuY29udmVyc2F0aW9uSWQsIHZpZXdlclBlcnNvbmE6IGBwZXJzb25hcy8ke2lkZW50aXR5LnBlcnNvbmFJZH1gLCB2aWV3ZXJVc2VyOiBgdXNlcnMvJHtpZGVudGl0eS51c2VySWR9YFxuICAgICAgfSk7XG4gICAgICByZXR1cm4gKHJlY29yZHMgfHwgW10pLm1hcChtZW1iZXJzaGlwRHRvKTtcbiAgICB9KTtcbiAgcmV0dXJuIHtcbiAgICBhY2NlcHRJbnZpdGU6IChpbnB1dDogQWNjZXB0SW52aXRlSW5wdXQpID0+IHJlc29sdmVJbnZpdGUoaW5wdXQsIHRydWUpIGFzIFByb21pc2U8Q29udmVyc2F0aW9uU3VtbWFyeT4sXG4gICAgY2hhbmdlTWVtYmVyUm9sZTogKGlucHV0OiBDaGFuZ2VNZW1iZXJSb2xlSW5wdXQpID0+IGNoYW5nZU1lbWJlcnNoaXAoJ3JvbGUnLCBpbnB1dCksXG4gICAgY3JlYXRlR3JvdXAsXG4gICAgZGVjbGluZUludml0ZTogKGlucHV0OiBBY2NlcHRJbnZpdGVJbnB1dCkgPT4gcmVzb2x2ZUludml0ZShpbnB1dCwgZmFsc2UpIGFzIFByb21pc2U8Q29udmVyc2F0aW9uSW52aXRlPixcbiAgICBnZXRPckNyZWF0ZURpcmVjdCwgZ2V0UGFydGljaXBhbnRzLCBpbnZpdGVNZW1iZXIsXG4gICAgbGVhdmVDb252ZXJzYXRpb246IChpbnB1dDogTGVhdmVDb252ZXJzYXRpb25JbnB1dCkgPT4gY2hhbmdlTWVtYmVyc2hpcCgnbGVhdmUnLCBpbnB1dCksXG4gICAgbGlzdENvbnZlcnNhdGlvbnMsIGxpc3RJbnZpdGVzLFxuICAgIHJlbW92ZU1lbWJlcjogKGlucHV0OiBSZW1vdmVNZW1iZXJJbnB1dCkgPT4gY2hhbmdlTWVtYmVyc2hpcCgncmVtb3ZlJywgaW5wdXQpLFxuICAgIHRyYW5zZmVyT3duZXJzaGlwOiAoaW5wdXQ6IFRyYW5zZmVyT3duZXJzaGlwSW5wdXQpID0+IGNoYW5nZU1lbWJlcnNoaXAoJ3RyYW5zZmVyJywgaW5wdXQpXG4gIH07XG59O1xuZXhwb3J0IHR5cGUgQ2hhdFJlcG9zaXRvcnkgPSBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVEdXJhYmxlQ29udmVyc2F0aW9uUmVwb3NpdG9yeT47XG5cbmNvbnN0IHZhbGlkID0gKGNvbmRpdGlvbjogYm9vbGVhbik6IHZvaWQgPT4ge1xuICBpZighY29uZGl0aW9uKSB7XG4gICAgdGhyb3cgbmV3IENoYXRFcnJvcignaW52YWxpZF9yZXF1ZXN0Jyk7XG4gIH1cbn07XG5jb25zdCBtdXRhdGlvbiA9IChpbnB1dDoge3JlYWRvbmx5IGV4cGVjdGVkUmV2aXNpb246IG51bWJlcjsgcmVhZG9ubHkgaWRlbXBvdGVuY3lLZXk6IHN0cmluZ30pOiB2b2lkID0+IHtcbiAgdmFsaWQoQm9vbGVhbihpbnB1dCkgJiYgaXNDaGF0SWRlbnRpZmllcihpbnB1dC5pZGVtcG90ZW5jeUtleSlcbiAgICAmJiBOdW1iZXIuaXNTYWZlSW50ZWdlcihpbnB1dC5leHBlY3RlZFJldmlzaW9uKSAmJiBpbnB1dC5leHBlY3RlZFJldmlzaW9uID4gMCk7XG59O1xuY29uc3QgY29udmVyc2F0aW9uTXV0YXRpb24gPSAoaW5wdXQ6IExlYXZlQ29udmVyc2F0aW9uSW5wdXQpOiB2b2lkID0+IHtcbiAgbXV0YXRpb24oaW5wdXQpO1xuICB2YWxpZChpc0NoYXRJZGVudGlmaWVyKGlucHV0LmNvbnZlcnNhdGlvbklkKSk7XG59O1xuY29uc3QgdGFyZ2V0ZWRNdXRhdGlvbiA9IChpbnB1dDogUmVtb3ZlTWVtYmVySW5wdXQpOiB2b2lkID0+IHtcbiAgY29udmVyc2F0aW9uTXV0YXRpb24oaW5wdXQpO1xuICB2YWxpZChpc0NoYXRJZGVudGlmaWVyKGlucHV0LnBlcnNvbmFJZCkpO1xufTtcbmNvbnN0IGxpc3QgPSAoaW5wdXQ6IENoYXRMaXN0SW5wdXQpOiB2b2lkID0+IHZhbGlkKEJvb2xlYW4oaW5wdXQpXG4gICYmIChpbnB1dC5saW1pdCA9PT0gdW5kZWZpbmVkIHx8IChOdW1iZXIuaXNTYWZlSW50ZWdlcihpbnB1dC5saW1pdCkgJiYgaW5wdXQubGltaXQgPiAwICYmIGlucHV0LmxpbWl0IDw9IDUwKSlcbiAgJiYgKGlucHV0LmFmdGVyID09PSB1bmRlZmluZWQgfHwgaXNDaGF0SWRlbnRpZmllcihpbnB1dC5hZnRlcikpKTtcblxuZXhwb3J0IGNvbnN0IGNyZWF0ZUR1cmFibGVDb252ZXJzYXRpb25BY3Rpb25zID0gKFxuICBpZGVudGl0eTogQ2hhdElkZW50aXR5LCByZXBvc2l0b3J5OiBDaGF0UmVwb3NpdG9yeVxuKSA9PiB7XG4gIGlmKCFpZGVudGl0eSB8fCAhaXNDaGF0SWRlbnRpZmllcihpZGVudGl0eS5wZXJzb25hSWQpIHx8ICFpc0NoYXRJZGVudGlmaWVyKGlkZW50aXR5LnVzZXJJZCkpIHtcbiAgICB0aHJvdyBuZXcgQ2hhdEVycm9yKCdmb3JiaWRkZW4nKTtcbiAgfVxuICByZXR1cm4ge1xuICAgIGFjY2VwdEludml0ZTogYXN5bmMgKGlucHV0OiBBY2NlcHRJbnZpdGVJbnB1dCkgPT4ge1xuICAgICAgbXV0YXRpb24oaW5wdXQpO1xuICAgICAgdmFsaWQoaXNDaGF0SWRlbnRpZmllcihpbnB1dC5pbnZpdGVJZCkpO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkuYWNjZXB0SW52aXRlKGlucHV0KTtcbiAgICB9LFxuICAgIGNoYW5nZU1lbWJlclJvbGU6IGFzeW5jIChpbnB1dDogQ2hhbmdlTWVtYmVyUm9sZUlucHV0KSA9PiB7XG4gICAgICB0YXJnZXRlZE11dGF0aW9uKGlucHV0KTtcbiAgICAgIHZhbGlkKFsnYWRtaW4nLCAnbWVtYmVyJ10uaW5jbHVkZXMoaW5wdXQucm9sZSkpO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkuY2hhbmdlTWVtYmVyUm9sZShpbnB1dCk7XG4gICAgfSxcbiAgICBjcmVhdGVHcm91cDogYXN5bmMgKGlucHV0OiBDcmVhdGVHcm91cElucHV0KSA9PiB7XG4gICAgICB2YWxpZChCb29sZWFuKGlucHV0KSAmJiBpc0NoYXRJZGVudGlmaWVyKGlucHV0LmlkZW1wb3RlbmN5S2V5KSAmJiB0eXBlb2YgaW5wdXQubmFtZSA9PT0gJ3N0cmluZydcbiAgICAgICAgJiYgaW5wdXQubmFtZS50cmltKCkubGVuZ3RoID4gMCAmJiBpbnB1dC5uYW1lLmxlbmd0aCA8PSAyMDBcbiAgICAgICAgJiYgQXJyYXkuaXNBcnJheShpbnB1dC5tZW1iZXJQZXJzb25hSWRzKVxuICAgICAgICAmJiBpbnB1dC5tZW1iZXJQZXJzb25hSWRzLmxlbmd0aCA+PSAyICYmIGlucHV0Lm1lbWJlclBlcnNvbmFJZHMubGVuZ3RoIDw9IDI0XG4gICAgICAgICYmIGlucHV0Lm1lbWJlclBlcnNvbmFJZHMuZXZlcnkoKGlkKSA9PiBpc0NoYXRJZGVudGlmaWVyKGlkKSAmJiBpZCAhPT0gaWRlbnRpdHkucGVyc29uYUlkKVxuICAgICAgICAmJiBuZXcgU2V0KGlucHV0Lm1lbWJlclBlcnNvbmFJZHMpLnNpemUgPT09IGlucHV0Lm1lbWJlclBlcnNvbmFJZHMubGVuZ3RoKTtcbiAgICAgIHJldHVybiByZXBvc2l0b3J5LmNyZWF0ZUdyb3VwKGlucHV0KTtcbiAgICB9LFxuICAgIGRlY2xpbmVJbnZpdGU6IGFzeW5jIChpbnB1dDogQWNjZXB0SW52aXRlSW5wdXQpID0+IHtcbiAgICAgIG11dGF0aW9uKGlucHV0KTtcbiAgICAgIHZhbGlkKGlzQ2hhdElkZW50aWZpZXIoaW5wdXQuaW52aXRlSWQpKTtcbiAgICAgIHJldHVybiByZXBvc2l0b3J5LmRlY2xpbmVJbnZpdGUoaW5wdXQpO1xuICAgIH0sXG4gICAgZ2V0T3JDcmVhdGVEaXJlY3Q6IGFzeW5jIChpbnB1dDogQ3JlYXRlRGlyZWN0SW5wdXQpID0+IHtcbiAgICAgIHZhbGlkKEJvb2xlYW4oaW5wdXQpICYmIGlzQ2hhdElkZW50aWZpZXIoaW5wdXQucGFydGljaXBhbnRQZXJzb25hSWQpXG4gICAgICAgICYmIGlucHV0LnBhcnRpY2lwYW50UGVyc29uYUlkICE9PSBpZGVudGl0eS5wZXJzb25hSWQpO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkuZ2V0T3JDcmVhdGVEaXJlY3QoaW5wdXQpO1xuICAgIH0sXG4gICAgZ2V0UGFydGljaXBhbnRzOiBhc3luYyAoaW5wdXQ6IHtyZWFkb25seSBjb252ZXJzYXRpb25JZDogc3RyaW5nfSkgPT4ge1xuICAgICAgdmFsaWQoQm9vbGVhbihpbnB1dCkgJiYgaXNDaGF0SWRlbnRpZmllcihpbnB1dC5jb252ZXJzYXRpb25JZCkpO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkuZ2V0UGFydGljaXBhbnRzKGlucHV0KTtcbiAgICB9LFxuICAgIGludml0ZU1lbWJlcjogYXN5bmMgKGlucHV0OiBJbnZpdGVNZW1iZXJJbnB1dCkgPT4ge1xuICAgICAgdGFyZ2V0ZWRNdXRhdGlvbihpbnB1dCk7XG4gICAgICB2YWxpZChpbnB1dC5wZXJzb25hSWQgIT09IGlkZW50aXR5LnBlcnNvbmFJZCk7XG4gICAgICByZXR1cm4gcmVwb3NpdG9yeS5pbnZpdGVNZW1iZXIoaW5wdXQpO1xuICAgIH0sXG4gICAgbGVhdmVDb252ZXJzYXRpb246IGFzeW5jIChpbnB1dDogTGVhdmVDb252ZXJzYXRpb25JbnB1dCkgPT4ge1xuICAgICAgY29udmVyc2F0aW9uTXV0YXRpb24oaW5wdXQpO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkubGVhdmVDb252ZXJzYXRpb24oaW5wdXQpO1xuICAgIH0sXG4gICAgbGlzdENvbnZlcnNhdGlvbnM6IGFzeW5jIChpbnB1dDogQ2hhdExpc3RJbnB1dCA9IHt9KSA9PiB7XG4gICAgICBsaXN0KGlucHV0KTtcbiAgICAgIHJldHVybiByZXBvc2l0b3J5Lmxpc3RDb252ZXJzYXRpb25zKGlucHV0KTtcbiAgICB9LFxuICAgIGxpc3RJbnZpdGVzOiBhc3luYyAoaW5wdXQ6IENoYXRMaXN0SW5wdXQgPSB7fSkgPT4ge1xuICAgICAgbGlzdChpbnB1dCk7XG4gICAgICByZXR1cm4gcmVwb3NpdG9yeS5saXN0SW52aXRlcyhpbnB1dCk7XG4gICAgfSxcbiAgICByZW1vdmVNZW1iZXI6IGFzeW5jIChpbnB1dDogUmVtb3ZlTWVtYmVySW5wdXQpID0+IHtcbiAgICAgIHRhcmdldGVkTXV0YXRpb24oaW5wdXQpO1xuICAgICAgdmFsaWQoaW5wdXQucGVyc29uYUlkICE9PSBpZGVudGl0eS5wZXJzb25hSWQpO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkucmVtb3ZlTWVtYmVyKGlucHV0KTtcbiAgICB9LFxuICAgIHRyYW5zZmVyT3duZXJzaGlwOiBhc3luYyAoaW5wdXQ6IFRyYW5zZmVyT3duZXJzaGlwSW5wdXQpID0+IHtcbiAgICAgIHRhcmdldGVkTXV0YXRpb24oaW5wdXQpO1xuICAgICAgdmFsaWQoaW5wdXQucGVyc29uYUlkICE9PSBpZGVudGl0eS5wZXJzb25hSWQpO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkudHJhbnNmZXJPd25lcnNoaXAoaW5wdXQpO1xuICAgIH1cbiAgfTtcbn07XG4iXSwibmFtZXMiOlsiY3JlYXRlSGFzaCIsInJhbmRvbVVVSUQiLCJDaGF0RXJyb3IiLCJhcHBlbmRDaGF0RXZlbnQiLCJjcmVhdGVNZW1iZXJzaGlwQXV0aG9yaXplciIsImlzQ2hhdElkZW50aWZpZXIiLCJjcmVhdGVEdXJhYmxlVHJhbnNhY3Rpb24iLCJoYXNoIiwiaW5wdXQiLCJ1cGRhdGUiLCJKU09OIiwic3RyaW5naWZ5IiwiZGlnZXN0IiwiaW52aXRlTWlsbGlzZWNvbmRzIiwibWVtYmVyc2hpcER0byIsInJlY29yZCIsImNvbnZlcnNhdGlvbklkIiwiZXBvY2giLCJqb2luU2VxdWVuY2UiLCJsZWZ0U2VxdWVuY2UiLCJwZXJzb25hSWQiLCJyZXZpc2lvbiIsInJvbGUiLCJzdGF0ZSIsImludml0ZUR0byIsImV4cGlyZXNBdCIsImludml0ZUlkIiwiX2tleSIsImludml0ZWRCeVBlcnNvbmFJZCIsImNyZWF0ZUR1cmFibGVDb252ZXJzYXRpb25SZXBvc2l0b3J5IiwiY29udGV4dCIsImlkZW50aXR5Iiwib3B0aW9ucyIsImF1ZGllbmNlIiwidHJhbnNhY3QiLCJleGNsdXNpdmUiLCJyZWFkIiwicmVhZENvbGxlY3Rpb25zIiwicXVlcnkiLCJzdW1tYXJ5IiwiY29udmVyc2F0aW9uIiwibWVtYmVyIiwiaW5jbHVkZU5hbWUiLCJhY3RpdmVNZW1iZXJDb3VudCIsImtpbmQiLCJtZW1iZXJzaGlwIiwibWVtYmVyc2hpcFJldmlzaW9uIiwibmFtZSIsInByb2plY3ROYW1lIiwidW5kZWZpbmVkIiwiYWN0b3IiLCJhbGxvd2VkIiwiYWR1bHQiLCJ3aG8iLCJhdXRob3JpemVJZGVudGl0eSIsImNvbm5lY3RlZCIsImZyb20iLCJ0YXJnZXQiLCJhY3RvclVzZXIiLCJ1c2VySWQiLCJmcm9tVXNlciIsInRvIiwiaWQiLCJ2YWx1ZSIsImNoYXRTY2hlbWFWZXJzaW9uIiwiYWNjZXNzIiwicHVycG9zZSIsImNoZWNrUmV2aXNpb24iLCJlbnRyeSIsImV4cGVjdGVkIiwiZXZlbnQiLCJyZXNvdXJjZUlkIiwiYWN0b3JQZXJzb25hSWQiLCJtZW1iZXJzaGlwRXBvY2giLCJyZWNlaXB0IiwiYWN0aW9uIiwia2V5IiwiaWRlbXBvdGVuY3lLZXkiLCJwYXlsb2FkSGFzaCIsInByaW9yIiwicmVzdWx0Iiwic2F2ZSIsImFkZGVkIiwiRGF0ZSIsIm5vdyIsIm93bmVyVXNlcklkIiwicmF0ZUxpbWl0IiwiTWF0aCIsImZsb29yIiwiYWRkTWVtYmVyIiwiZXZlbnRTZXF1ZW5jZSIsIl9mcm9tIiwiX3RvIiwiYWNjZXB0ZWRBdCIsImN1cnJlbnRLZXkiLCJqb2luRXZlbnRTZXF1ZW5jZSIsImFkZEludml0ZSIsImludml0ZWVQZXJzb25hSWQiLCJpbnZpdGVlVXNlcklkIiwiY3JlYXRlZEF0IiwiZXhwaXJlSW52aXRlcyIsImdldE9yQ3JlYXRlRGlyZWN0IiwicGFydGljaXBhbnRQZXJzb25hSWQiLCJwYWlyIiwic29ydCIsImV4aXN0aW5nIiwibGFzdFNlcXVlbmNlIiwiY3JlYXRlZEJ5UGVyc29uYUlkIiwiZGlyZWN0UGFpcktleSIsImlzRGlyZWN0Iiwib3duIiwiY3JlYXRlR3JvdXAiLCJyZXF1ZXN0IiwidGFyZ2V0cyIsIm1lbWJlclBlcnNvbmFJZHMiLCJwdXNoIiwiY29udGVudFZlcnNpb24iLCJuYW1lQ29udGVudFZlcnNpb24iLCJuYW1lRGVjaXNpb24iLCJjbGFzc2lmeU5hbWUiLCJpbnZpdGVNZW1iZXIiLCJleHBlY3RlZFJldmlzaW9uIiwiY291bnRzIiwiYWN0aXZlIiwidG90YWwiLCJyZXNvbHZlSW52aXRlIiwiYWNjZXB0IiwiaW52aXRlIiwiY3VycmVudCIsImludml0ZXIiLCJpbmNsdWRlcyIsInVwZGF0ZWQiLCJjb3VudCIsImNoYW5nZU1lbWJlcnNoaXAiLCJvd25BY2Nlc3MiLCJ0YXJnZXRQZXJzb25hSWQiLCJsZWF2aW5nIiwib3duUmVzdWx0Iiwic2VxdWVuY2UiLCJjbG9zZSIsImxpc3RDb252ZXJzYXRpb25zIiwiaWRzIiwiYWZ0ZXIiLCJsaW1pdCIsInJlc3VsdHMiLCJwZWVyIiwiZXJyb3IiLCJjb2RlIiwibGlzdEludml0ZXMiLCJyZWNvcmRzIiwibWFwIiwiZ2V0UGFydGljaXBhbnRzIiwidmlld2VyUGVyc29uYSIsInZpZXdlclVzZXIiLCJhY2NlcHRJbnZpdGUiLCJjaGFuZ2VNZW1iZXJSb2xlIiwiZGVjbGluZUludml0ZSIsImxlYXZlQ29udmVyc2F0aW9uIiwicmVtb3ZlTWVtYmVyIiwidHJhbnNmZXJPd25lcnNoaXAiLCJ2YWxpZCIsImNvbmRpdGlvbiIsIm11dGF0aW9uIiwiQm9vbGVhbiIsIk51bWJlciIsImlzU2FmZUludGVnZXIiLCJjb252ZXJzYXRpb25NdXRhdGlvbiIsInRhcmdldGVkTXV0YXRpb24iLCJsaXN0IiwiY3JlYXRlRHVyYWJsZUNvbnZlcnNhdGlvbkFjdGlvbnMiLCJyZXBvc2l0b3J5IiwidHJpbSIsImxlbmd0aCIsIkFycmF5IiwiaXNBcnJheSIsImV2ZXJ5IiwiU2V0Iiwic2l6ZSJdLCJtYXBwaW5ncyI6IkFBQUEsd0dBQXdHLEdBQ3hHLFNBQVFBLFVBQVUsRUFBRUMsVUFBVSxRQUFPLGNBQWM7QUFFbkQsU0FBUUMsU0FBUyxRQUFPLGNBQWM7QUFDdEMsU0FBUUMsZUFBZSxRQUFPLGNBQWM7QUFDNUMsU0FBUUMsMEJBQTBCLEVBQUVDLGdCQUFnQixRQUFPLGtCQUFrQjtBQUM3RSxTQUFRQyx3QkFBd0IsUUFBTyxtQkFBbUI7QUFtQzFELE1B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