@nlabs/reaktor 1.17.0 → 1.19.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.
- package/README.md +182 -0
- package/docs/ASSISTANT.md +138 -0
- package/docs/GENERATION.md +42 -5
- package/docs/NATIVE_PUSH.md +56 -0
- package/lib/actions/alfredFaq.d.ts +80 -0
- package/lib/actions/alfredFaq.js +206 -0
- package/lib/actions/analytics.js +1 -1
- package/lib/actions/apps.js +1 -1
- package/lib/actions/connections.js +1 -1
- package/lib/actions/content.js +1 -1
- package/lib/actions/contentGeneration.d.ts +4 -0
- package/lib/actions/contentGeneration.js +82 -10
- package/lib/actions/contentGenerationErrors.js +1 -1
- package/lib/actions/conversations.d.ts +1 -1
- package/lib/actions/conversations.js +19 -9
- package/lib/actions/crm.js +1 -1
- package/lib/actions/crud.js +26 -1
- package/lib/actions/databaseAccounts.js +1 -1
- package/lib/actions/databaseProvisioningLease.js +1 -1
- package/lib/actions/dynamodb.js +1 -1
- package/lib/actions/email.js +1 -1
- package/lib/actions/files.js +1 -1
- package/lib/actions/flags.js +1 -1
- package/lib/actions/geminiVideo.js +1 -1
- package/lib/actions/groups.js +1 -1
- package/lib/actions/higgsfield.js +1 -1
- package/lib/actions/imageGeneration.js +1 -1
- package/lib/actions/images.js +1 -1
- package/lib/actions/index.d.ts +1 -0
- package/lib/actions/index.js +2 -1
- package/lib/actions/ios.js +1 -1
- package/lib/actions/locations.js +1 -1
- package/lib/actions/messages.js +23 -7
- package/lib/actions/notifications.js +1 -1
- package/lib/actions/ollamaGeneration.js +1 -1
- package/lib/actions/openaiResponseSession.js +1 -1
- package/lib/actions/payments.js +1 -1
- package/lib/actions/personas.js +1 -1
- package/lib/actions/phoneSupport.js +1 -1
- package/lib/actions/posts.js +1 -1
- package/lib/actions/query.js +1 -1
- package/lib/actions/reactions.js +1 -1
- package/lib/actions/rum.js +1 -1
- package/lib/actions/s3.js +1 -1
- package/lib/actions/search.js +1 -1
- package/lib/actions/sms.js +1 -1
- package/lib/actions/statistics.js +1 -1
- package/lib/actions/subscriptions.js +1 -1
- package/lib/actions/tags.js +1 -1
- package/lib/actions/users.js +57 -4
- package/lib/actions/videoGeneration.js +1 -1
- package/lib/actions/videos.js +1 -1
- package/lib/adapters/arangoAdapter.js +1 -1
- package/lib/adapters/contentAdapter.js +1 -1
- package/lib/adapters/fileAdapter.js +1 -1
- package/lib/adapters/geminiGenerationAdapter.js +1 -1
- package/lib/adapters/higgsfieldAdapter.js +1 -1
- package/lib/adapters/higgsfieldModelCatalog.js +1 -1
- package/lib/adapters/higgsfieldModels.js +1 -1
- package/lib/adapters/imageAdapter.js +1 -1
- package/lib/adapters/imageGenerationAdapter.js +1 -1
- package/lib/adapters/index.js +1 -1
- package/lib/adapters/messageAdapter.js +1 -1
- package/lib/adapters/postAdapter.js +1 -1
- package/lib/adapters/reaktorAdapter.js +1 -1
- package/lib/adapters/tagAdapter.js +1 -1
- package/lib/adapters/userAdapter.js +1 -1
- package/lib/assistant/core.d.ts +53 -0
- package/lib/assistant/core.js +123 -0
- package/lib/assistant/faq.d.ts +26 -0
- package/lib/assistant/faq.js +238 -0
- package/lib/assistant/faqCore.d.ts +23 -0
- package/lib/assistant/faqCore.js +20 -0
- package/lib/assistant/index.d.ts +7 -0
- package/lib/assistant/index.js +5 -0
- package/lib/assistant/service.d.ts +35 -0
- package/lib/assistant/service.js +201 -0
- package/lib/config.js +1 -1
- package/lib/constants/collections.js +1 -1
- package/lib/durableChat/conversations.d.ts +62 -0
- package/lib/durableChat/conversations.js +729 -0
- package/lib/durableChat/errors.d.ts +6 -0
- package/lib/durableChat/errors.js +11 -0
- package/lib/durableChat/events.d.ts +22 -0
- package/lib/durableChat/events.js +50 -0
- package/lib/durableChat/index.d.ts +8 -0
- package/lib/durableChat/index.js +10 -0
- package/lib/durableChat/membership.d.ts +24 -0
- package/lib/durableChat/membership.js +85 -0
- package/lib/durableChat/messageTypes.d.ts +76 -0
- package/lib/durableChat/messageTypes.js +3 -0
- package/lib/durableChat/messages.d.ts +16 -0
- package/lib/durableChat/messages.js +257 -0
- package/lib/durableChat/transaction.d.ts +10 -0
- package/lib/durableChat/transaction.js +53 -0
- package/lib/durableChat/types.d.ts +81 -0
- package/lib/durableChat/types.js +3 -0
- package/lib/handlers/graphqlHandler.js +1 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.js +5 -3
- package/lib/lambdas/actions/websockets.js +4 -4
- package/lib/lambdas/authorizer.js +1 -1
- package/lib/lambdas/connection.js +1 -1
- package/lib/lambdas/utils/message.js +1 -1
- package/lib/lambdas/utils/websocket.js +1 -1
- package/lib/mocks/conversation.js +1 -1
- package/lib/mocks/file.js +1 -1
- package/lib/mocks/group.js +1 -1
- package/lib/mocks/image.js +1 -1
- package/lib/mocks/post.js +1 -1
- package/lib/mocks/tag.js +1 -1
- package/lib/mocks/user.js +1 -1
- package/lib/mocks/video.js +1 -1
- package/lib/mutations/analytics/analytics.js +1 -1
- package/lib/mutations/content/content.js +1 -1
- package/lib/mutations/crm/crm.js +1 -1
- package/lib/mutations/events/events.js +1 -1
- package/lib/mutations/flags/flags.js +1 -1
- package/lib/mutations/groups/groups.js +1 -1
- package/lib/mutations/higgsfield/higgsfield.js +1 -1
- package/lib/mutations/images/images.js +1 -1
- package/lib/mutations/index.js +1 -1
- package/lib/mutations/locations/locations.js +1 -1
- package/lib/mutations/messages/messages.js +1 -1
- package/lib/mutations/personas/personas.js +1 -1
- package/lib/mutations/posts/posts.js +1 -1
- package/lib/mutations/reactions/reactions.js +1 -1
- package/lib/mutations/rum/rum.js +1 -1
- package/lib/mutations/statistics/statistics.js +1 -1
- package/lib/mutations/subscriptions/subscriptions.js +1 -1
- package/lib/mutations/tags/tags.js +1 -1
- package/lib/mutations/users/users.js +1 -1
- package/lib/mutations/videos/videos.js +1 -1
- package/lib/nativePush/graphql.d.ts +14 -0
- package/lib/nativePush/graphql.js +105 -0
- package/lib/nativePush/index.d.ts +7 -0
- package/lib/nativePush/index.js +9 -0
- package/lib/nativePush/migrations.d.ts +2 -0
- package/lib/nativePush/migrations.js +36 -0
- package/lib/nativePush/nativePushContract.fixture.d.ts +22 -0
- package/lib/nativePush/nativePushContract.fixture.js +27 -0
- package/lib/nativePush/payload.d.ts +2 -0
- package/lib/nativePush/payload.js +106 -0
- package/lib/nativePush/repository.d.ts +9 -0
- package/lib/nativePush/repository.js +120 -0
- package/lib/nativePush/service.d.ts +2 -0
- package/lib/nativePush/service.js +110 -0
- package/lib/nativePush/snsProvider.d.ts +17 -0
- package/lib/nativePush/snsProvider.js +165 -0
- package/lib/nativePush/types.d.ts +89 -0
- package/lib/nativePush/types.js +3 -0
- package/lib/objectTypes/analytics.js +1 -1
- package/lib/objectTypes/app.js +1 -1
- package/lib/objectTypes/bankAccount.js +1 -1
- package/lib/objectTypes/connection.js +1 -1
- package/lib/objectTypes/content.js +1 -1
- package/lib/objectTypes/contentGeneration.js +1 -1
- package/lib/objectTypes/conversation.js +1 -1
- package/lib/objectTypes/creditCard.js +1 -1
- package/lib/objectTypes/crm.js +1 -1
- package/lib/objectTypes/document.js +1 -1
- package/lib/objectTypes/error.js +1 -1
- package/lib/objectTypes/event.js +1 -1
- package/lib/objectTypes/external.js +1 -1
- package/lib/objectTypes/file.js +1 -1
- package/lib/objectTypes/filter.js +1 -1
- package/lib/objectTypes/flag.js +1 -1
- package/lib/objectTypes/group.js +1 -1
- package/lib/objectTypes/higgsfield.js +1 -1
- package/lib/objectTypes/iapSubscription.js +1 -1
- package/lib/objectTypes/image.js +1 -1
- package/lib/objectTypes/imageGeneration.js +1 -1
- package/lib/objectTypes/index.js +1 -1
- package/lib/objectTypes/location.js +1 -1
- package/lib/objectTypes/message.js +1 -1
- package/lib/objectTypes/passcode.js +1 -1
- package/lib/objectTypes/persona.js +1 -1
- package/lib/objectTypes/plan.js +1 -1
- package/lib/objectTypes/post.js +1 -1
- package/lib/objectTypes/query.js +1 -1
- package/lib/objectTypes/reaction.js +1 -1
- package/lib/objectTypes/relation.js +1 -1
- package/lib/objectTypes/rum.js +1 -1
- package/lib/objectTypes/search.js +1 -1
- package/lib/objectTypes/statistics.js +1 -1
- package/lib/objectTypes/subscription.js +1 -1
- package/lib/objectTypes/tag.js +1 -1
- package/lib/objectTypes/user.js +1 -1
- package/lib/objectTypes/video.js +1 -1
- package/lib/objectTypes/videoGeneration.js +1 -1
- package/lib/organizations/accountLifecycle.d.ts +7 -0
- package/lib/organizations/accountLifecycle.js +106 -0
- package/lib/organizations/authorization.d.ts +9 -0
- package/lib/organizations/authorization.js +40 -0
- package/lib/organizations/billing.d.ts +38 -0
- package/lib/organizations/billing.js +277 -0
- package/lib/organizations/billingProvider.d.ts +4 -0
- package/lib/organizations/billingProvider.js +287 -0
- package/lib/organizations/billingUpdates.d.ts +9 -0
- package/lib/organizations/billingUpdates.js +388 -0
- package/lib/organizations/index.d.ts +10 -0
- package/lib/organizations/index.js +12 -0
- package/lib/organizations/mail.d.ts +10 -0
- package/lib/organizations/mail.js +60 -0
- package/lib/organizations/memberDirectory.d.ts +15 -0
- package/lib/organizations/memberDirectory.js +28 -0
- package/lib/organizations/membership.d.ts +19 -0
- package/lib/organizations/membership.js +248 -0
- package/lib/organizations/passwordRecovery.d.ts +9 -0
- package/lib/organizations/passwordRecovery.js +50 -0
- package/lib/organizations/plans.d.ts +6 -0
- package/lib/organizations/plans.js +40 -0
- package/lib/organizations/providerPagination.d.ts +6 -0
- package/lib/organizations/providerPagination.js +23 -0
- package/lib/organizations/reconciliation.d.ts +11 -0
- package/lib/organizations/reconciliation.js +400 -0
- package/lib/organizations/signup.d.ts +25 -0
- package/lib/organizations/signup.js +169 -0
- package/lib/organizations/store.d.ts +25 -0
- package/lib/organizations/store.js +167 -0
- package/lib/organizations/types.d.ts +138 -0
- package/lib/organizations/types.js +3 -0
- package/lib/organizations/verification.d.ts +3 -0
- package/lib/organizations/verification.js +51 -0
- package/lib/queries/analytics/analytics.js +1 -1
- package/lib/queries/content/content.js +1 -1
- package/lib/queries/crm/crm.js +1 -1
- package/lib/queries/events/events.js +1 -1
- package/lib/queries/flags/flags.js +1 -1
- package/lib/queries/groups/groups.js +1 -1
- package/lib/queries/higgsfield/higgsfield.js +1 -1
- package/lib/queries/images/images.js +1 -1
- package/lib/queries/index.js +1 -1
- package/lib/queries/locations/locations.js +1 -1
- package/lib/queries/messages/messages.js +1 -1
- package/lib/queries/personas/personas.js +1 -1
- package/lib/queries/posts/posts.js +1 -1
- package/lib/queries/query/query.js +1 -1
- package/lib/queries/reactions/reactions.js +1 -1
- package/lib/queries/statistics/statistics.js +1 -1
- package/lib/queries/subscriptions/subscriptions.js +1 -1
- package/lib/queries/tags/tags.js +1 -1
- package/lib/queries/users/users.js +1 -1
- package/lib/queries/videos/videos.js +1 -1
- package/lib/templates/email/layout.js +1 -1
- package/lib/templates/email/passwordForgot.js +1 -1
- package/lib/templates/email/passwordRecovery.js +1 -1
- package/lib/templates/email/verifyEmail.js +1 -1
- package/lib/templates/email/welcome.js +1 -1
- package/lib/templates/sms/passwordForgot.js +1 -1
- package/lib/templates/sms/passwordRecovery.js +1 -1
- package/lib/templates/sms/verifyEmail.js +1 -1
- package/lib/templates/sms/verifyPhone.js +1 -1
- package/lib/templates/sms/welcome.js +1 -1
- package/lib/types/analytics.types.js +1 -1
- package/lib/types/apps.types.js +1 -1
- package/lib/types/arangodb.types.js +1 -1
- package/lib/types/auth.types.js +1 -1
- package/lib/types/connections.types.js +1 -1
- package/lib/types/content.types.js +1 -1
- package/lib/types/conversations.types.js +1 -1
- package/lib/types/crm.types.js +1 -1
- package/lib/types/databaseCredentials.types.js +1 -1
- package/lib/types/email.types.js +1 -1
- package/lib/types/error.types.js +1 -1
- package/lib/types/files.types.js +1 -1
- package/lib/types/flags.types.js +1 -1
- package/lib/types/google.types.js +1 -1
- package/lib/types/groups.types.js +1 -1
- package/lib/types/images.types.js +1 -1
- package/lib/types/index.js +1 -1
- package/lib/types/locations.types.js +1 -1
- package/lib/types/messages.types.js +1 -1
- package/lib/types/notifications.types.js +1 -1
- package/lib/types/payments.types.js +1 -1
- package/lib/types/personas.types.js +1 -1
- package/lib/types/posts.types.js +1 -1
- package/lib/types/rum.types.js +1 -1
- package/lib/types/statistics.types.js +1 -1
- package/lib/types/tags.types.js +1 -1
- package/lib/types/users.types.js +1 -1
- package/lib/types/videos.types.js +1 -1
- package/lib/types/websockets.types.js +1 -1
- package/lib/upload.js +1 -1
- package/lib/utils/adapterUtils.js +1 -1
- package/lib/utils/analyticsUtils.js +1 -1
- package/lib/utils/appContext.js +1 -1
- package/lib/utils/aqlHelpers.js +1 -1
- package/lib/utils/arangodbUtils.js +1 -1
- package/lib/utils/authUtils.js +1 -1
- package/lib/utils/awsUtils.js +1 -1
- package/lib/utils/collectionRegistry.js +1 -1
- package/lib/utils/contextUtils.js +1 -1
- package/lib/utils/databaseCredentials.js +1 -1
- package/lib/utils/dbI18n.example.js +1 -1
- package/lib/utils/dbI18n.js +1 -1
- package/lib/utils/generationCallback.js +1 -1
- package/lib/utils/googleTranslate.js +1 -1
- package/lib/utils/graphqlUtils.js +1 -1
- package/lib/utils/imageUtils.js +1 -1
- package/lib/utils/index.js +1 -1
- package/lib/utils/languageDetection.js +1 -1
- package/lib/utils/localeUtils.example.js +1 -1
- package/lib/utils/localeUtils.js +1 -1
- package/lib/utils/mediaAccess.js +1 -1
- package/lib/utils/middlewareUtils.js +1 -1
- package/lib/utils/nodeRealtime.js +1 -1
- package/lib/utils/opaqueToken.js +1 -1
- package/lib/utils/queryBuilder.js +1 -1
- package/lib/utils/schemaUtils.js +1 -1
- package/lib/utils/secretDatabase.js +1 -1
- package/lib/utils/sessionUtils.js +1 -1
- package/lib/utils/stripeUtils.js +1 -1
- package/lib/utils/stripeUtils.ts.bak +1 -1
- package/lib/utils/templateUtils.js +1 -1
- package/lib/utils/testUtils.js +1 -1
- package/lib/utils/translationQueue.example.js +1 -1
- package/lib/utils/translationQueue.js +1 -1
- package/lib/videoProcessing.js +1 -1
- 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,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9wcml2YXRlL3RtcC92YXVsdC1yZWFrdG9yLWNvbXBsZXRpb24vcGFja2FnZXMvcmVha3Rvci9zcmMvZHVyYWJsZUNoYXQvY29udmVyc2F0aW9ucy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBuby1hd2FpdC1pbi1sb29wLCBuby11bmRlcnNjb3JlLWRhbmdsZSAtLSBPcmRlcmVkIHRyYW5zYWN0aW9ucyBhbmQgQXJhbmdvIGlkZW50aXRpZXMuICovXG5pbXBvcnQge2NyZWF0ZUhhc2gsIHJhbmRvbVVVSUR9IGZyb20gJ25vZGU6Y3J5cHRvJztcblxuaW1wb3J0IHtDaGF0RXJyb3J9IGZyb20gJy4vZXJyb3JzLmpzJztcbmltcG9ydCB7YXBwZW5kQ2hhdEV2ZW50fSBmcm9tICcuL2V2ZW50cy5qcyc7XG5pbXBvcnQge2NyZWF0ZU1lbWJlcnNoaXBBdXRob3JpemVyLCBpc0NoYXRJZGVudGlmaWVyfSBmcm9tICcuL21lbWJlcnNoaXAuanMnO1xuaW1wb3J0IHtjcmVhdGVEdXJhYmxlVHJhbnNhY3Rpb259IGZyb20gJy4vdHJhbnNhY3Rpb24uanMnO1xuXG5pbXBvcnQgdHlwZSB7QXBpQ29udGV4dH0gZnJvbSAnLi4vdHlwZXMvYXV0aC50eXBlcy5qcyc7XG5pbXBvcnQgdHlwZSB7QXV0aG9yaXplZE1lbWJlcnNoaXAsIENoYXRJZGVudGl0eSwgQ2hhdFF1ZXJ5fSBmcm9tICcuL21lbWJlcnNoaXAuanMnO1xuaW1wb3J0IHR5cGUge1xuICBBY2NlcHRJbnZpdGVJbnB1dCwgQ2hhbmdlTWVtYmVyUm9sZUlucHV0LCBDb252ZXJzYXRpb25JbnZpdGUsIENvbnZlcnNhdGlvbk1lbWJlcnNoaXAsXG4gIENvbnZlcnNhdGlvblN1bW1hcnksIENyZWF0ZURpcmVjdElucHV0LCBDcmVhdGVHcm91cElucHV0LCBJbnZpdGVNZW1iZXJJbnB1dCxcbiAgTGVhdmVDb252ZXJzYXRpb25JbnB1dCwgTWVtYmVyc2hpcFJvbGUsIFJlbW92ZU1lbWJlcklucHV0LCBUcmFuc2Zlck93bmVyc2hpcElucHV0XG59IGZyb20gJy4vdHlwZXMuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIENvbnZlcnNhdGlvblJlY29yZCB7XG4gIHJlYWRvbmx5IF9rZXk6IHN0cmluZztcbiAgcmVhZG9ubHkgYWN0aXZlTWVtYmVyQ291bnQ6IG51bWJlcjtcbiAgcmVhZG9ubHkgY2hhdFNjaGVtYVZlcnNpb246IG51bWJlcjtcbiAgcmVhZG9ubHkga2luZDogJ2RpcmVjdCcgfCAnZ3JvdXAnO1xuICByZWFkb25seSBsYXN0U2VxdWVuY2U6IG51bWJlcjtcbiAgcmVhZG9ubHkgbWVtYmVyc2hpcFJldmlzaW9uOiBudW1iZXI7XG4gIHJlYWRvbmx5IG5hbWU/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+IHtcbiAgICAgIHRhcmdldGVkTXV0YXRpb24oaW5wdXQpO1xuICAgICAgdmFsaWQoaW5wdXQucGVyc29uYUlkICE9PSBpZGVudGl0eS5wZXJzb25hSWQpO1xuICAgICAgcmV0dXJuIHJlcG9zaXRvcnkudHJhbnNmZXJPd25lcnNoaXAoaW5wdXQpO1xuICAgIH1cbiAgfTtcbn07XG4iXSwibmFtZXMiOlsiY3JlYXRlSGFzaCIsInJhbmRvbVVVSUQiLCJDaGF0RXJyb3IiLCJhcHBlbmRDaGF0RXZlbnQiLCJjcmVhdGVNZW1iZXJzaGlwQXV0aG9yaXplciIsImlzQ2hhdElkZW50aWZpZXIiLCJjcmVhdGVEdXJhYmxlVHJhbnNhY3Rpb24iLCJoYXNoIiwiaW5wdXQiLCJ1cGRhdGUiLCJKU09OIiwic3RyaW5naWZ5IiwiZGlnZXN0IiwiaW52aXRlTWlsbGlzZWNvbmRzIiwibWVtYmVyc2hpcER0byIsInJlY29yZCIsImNvbnZlcnNhdGlvbklkIiwiZXBvY2giLCJqb2luU2VxdWVuY2UiLCJsZWZ0U2VxdWVuY2UiLCJwZXJzb25hSWQiLCJyZXZpc2lvbiIsInJvbGUiLCJzdGF0ZSIsImludml0ZUR0byIsImV4cGlyZXNBdCIsImludml0ZUlkIiwiX2tleSIsImludml0ZWRCeVBlcnNvbmFJZCIsImNyZWF0ZUR1cmFibGVDb252ZXJzYXRpb25SZXBvc2l0b3J5IiwiY29udGV4dCIsImlkZW50aXR5Iiwib3B0aW9ucyIsImF1ZGllbmNlIiwidHJhbnNhY3QiLCJleGNsdXNpdmUiLCJyZWFkIiwicmVhZENvbGxlY3Rpb25zIiwicXVlcnkiLCJzdW1tYXJ5IiwiY29udmVyc2F0aW9uIiwibWVtYmVyIiwiaW5jbHVkZU5hbWUiLCJhY3RpdmVNZW1iZXJDb3VudCIsImtpbmQiLCJtZW1iZXJzaGlwIiwibWVtYmVyc2hpcFJldmlzaW9uIiwibmFtZSIsInByb2plY3ROYW1lIiwidW5kZWZpbmVkIiwiYWN0b3IiLCJhbGxvd2VkIiwiYWR1bHQiLCJ3aG8iLCJhdXRob3JpemVJZGVudGl0eSIsImNvbm5lY3RlZCIsImZyb20iLCJ0YXJnZXQiLCJhY3RvclVzZXIiLCJ1c2VySWQiLCJmcm9tVXNlciIsInRvIiwiaWQiLCJ2YWx1ZSIsImNoYXRTY2hlbWFWZXJzaW9uIiwiYWNjZXNzIiwicHVycG9zZSIsImNoZWNrUmV2aXNpb24iLCJlbnRyeSIsImV4cGVjdGVkIiwiZXZlbnQiLCJyZXNvdXJjZUlkIiwiYWN0b3JQZXJzb25hSWQiLCJtZW1iZXJzaGlwRXBvY2giLCJyZWNlaXB0IiwiYWN0aW9uIiwia2V5IiwiaWRlbXBvdGVuY3lLZXkiLCJwYXlsb2FkSGFzaCIsInByaW9yIiwicmVzdWx0Iiwic2F2ZSIsImFkZGVkIiwiRGF0ZSIsIm5vdyIsIm93bmVyVXNlcklkIiwicmF0ZUxpbWl0IiwiTWF0aCIsImZsb29yIiwiYWRkTWVtYmVyIiwiZXZlbnRTZXF1ZW5jZSIsIl9mcm9tIiwiX3RvIiwiYWNjZXB0ZWRBdCIsImN1cnJlbnRLZXkiLCJqb2luRXZlbnRTZXF1ZW5jZSIsImFkZEludml0ZSIsImludml0ZWVQZXJzb25hSWQiLCJpbnZpdGVlVXNlcklkIiwiY3JlYXRlZEF0IiwiZXhwaXJlSW52aXRlcyIsImdldE9yQ3JlYXRlRGlyZWN0IiwicGFydGljaXBhbnRQZXJzb25hSWQiLCJwYWlyIiwic29ydCIsImV4aXN0aW5nIiwibGFzdFNlcXVlbmNlIiwiY3JlYXRlZEJ5UGVyc29uYUlkIiwiZGlyZWN0UGFpcktleSIsImlzRGlyZWN0Iiwib3duIiwiY3JlYXRlR3JvdXAiLCJyZXF1ZXN0IiwidGFyZ2V0cyIsIm1lbWJlclBlcnNvbmFJZHMiLCJwdXNoIiwiY29udGVudFZlcnNpb24iLCJuYW1lQ29udGVudFZlcnNpb24iLCJuYW1lRGVjaXNpb24iLCJjbGFzc2lmeU5hbWUiLCJpbnZpdGVNZW1iZXIiLCJleHBlY3RlZFJldmlzaW9uIiwiY291bnRzIiwiYWN0aXZlIiwidG90YWwiLCJyZXNvbHZlSW52aXRlIiwiYWNjZXB0IiwiaW52aXRlIiwiY3VycmVudCIsImludml0ZXIiLCJpbmNsdWRlcyIsInVwZGF0ZWQiLCJjb3VudCIsImNoYW5nZU1lbWJlcnNoaXAiLCJvd25BY2Nlc3MiLCJ0YXJnZXRQZXJzb25hSWQiLCJsZWF2aW5nIiwib3duUmVzdWx0Iiwic2VxdWVuY2UiLCJjbG9zZSIsImxpc3RDb252ZXJzYXRpb25zIiwiaWRzIiwiYWZ0ZXIiLCJsaW1pdCIsInJlc3VsdHMiLCJwZWVyIiwiZXJyb3IiLCJjb2RlIiwibGlzdEludml0ZXMiLCJyZWNvcmRzIiwibWFwIiwiZ2V0UGFydGljaXBhbnRzIiwidmlld2VyUGVyc29uYSIsInZpZXdlclVzZXIiLCJhY2NlcHRJbnZpdGUiLCJjaGFuZ2VNZW1iZXJSb2xlIiwiZGVjbGluZUludml0ZSIsImxlYXZlQ29udmVyc2F0aW9uIiwicmVtb3ZlTWVtYmVyIiwidHJhbnNmZXJPd25lcnNoaXAiLCJ2YWxpZCIsImNvbmRpdGlvbiIsIm11dGF0aW9uIiwiQm9vbGVhbiIsIk51bWJlciIsImlzU2FmZUludGVnZXIiLCJjb252ZXJzYXRpb25NdXRhdGlvbiIsInRhcmdldGVkTXV0YXRpb24iLCJsaXN0IiwiY3JlYXRlRHVyYWJsZUNvbnZlcnNhdGlvbkFjdGlvbnMiLCJyZXBvc2l0b3J5IiwidHJpbSIsImxlbmd0aCIsIkFycmF5IiwiaXNBcnJheSIsImV2ZXJ5IiwiU2V0Iiwic2l6ZSJdLCJtYXBwaW5ncyI6IkFBQUEsd0dBQXdHLEdBQ3hHLFNBQVFBLFVBQVUsRUFBRUMsVUFBVSxRQUFPLGNBQWM7QUFFbkQsU0FBUUMsU0FBUyxRQUFPLGNBQWM7QUFDdEMsU0FBUUMsZUFBZSxRQUFPLGNBQWM7QUFDNUMsU0FBUUMsMEJBQTBCLEVBQUVDLGdCQUFnQixRQUFPLGtCQUFrQjtBQUM3RSxTQUFRQyx3QkFBd0IsUUFBTyxtQkFBbUI7QUFtQzFE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
|