@nlabs/reaktor 1.19.0 → 1.20.0-reactorbox.1
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 +290 -0
- package/docs/GENERATION.md +4 -0
- package/lib/actions/apps.js +17 -2
- package/lib/actions/contentGeneration.d.ts +19 -1
- package/lib/actions/contentGeneration.js +59 -24
- package/lib/actions/contentGenerationSources.d.ts +29 -0
- package/lib/actions/contentGenerationSources.js +103 -0
- package/lib/actions/crm.d.ts +6 -2
- package/lib/actions/crm.js +91 -12
- package/lib/actions/crmCanonicalOutcomes.d.ts +88 -0
- package/lib/actions/crmCanonicalOutcomes.js +398 -0
- package/lib/actions/crmCanonicalPolicy.d.ts +92 -0
- package/lib/actions/crmCanonicalPolicy.js +104 -0
- package/lib/actions/crmCheckoutRecovery.d.ts +72 -0
- package/lib/actions/crmCheckoutRecovery.js +413 -0
- package/lib/actions/crmCheckoutRecoveryPolicy.d.ts +38 -0
- package/lib/actions/crmCheckoutRecoveryPolicy.js +61 -0
- package/lib/actions/crmDeliveryEvents.d.ts +24 -0
- package/lib/actions/crmDeliveryEvents.js +207 -0
- package/lib/actions/crmDeliveryEvidence.d.ts +17 -0
- package/lib/actions/crmDeliveryEvidence.js +58 -0
- package/lib/actions/crmExecution.d.ts +125 -0
- package/lib/actions/crmExecution.js +830 -0
- package/lib/actions/crmFinancialEvidence.d.ts +12 -0
- package/lib/actions/crmFinancialEvidence.js +60 -0
- package/lib/actions/crmInsights.d.ts +60 -0
- package/lib/actions/crmInsights.js +225 -0
- package/lib/actions/crmLeadFollowups.d.ts +66 -0
- package/lib/actions/crmLeadFollowups.js +209 -0
- package/lib/actions/crmOutcomes.d.ts +106 -0
- package/lib/actions/crmOutcomes.js +289 -0
- package/lib/actions/crmRecoveryRetention.d.ts +13 -0
- package/lib/actions/crmRecoveryRetention.js +163 -0
- package/lib/actions/crmReminderDispatch.d.ts +10 -0
- package/lib/actions/crmReminderDispatch.js +65 -0
- package/lib/actions/crmSourceOwnership.d.ts +7 -0
- package/lib/actions/crmSourceOwnership.js +27 -0
- package/lib/actions/crmSupportInsights.d.ts +18 -0
- package/lib/actions/crmSupportInsights.js +47 -0
- package/lib/actions/crmWinback.d.ts +71 -0
- package/lib/actions/crmWinback.js +347 -0
- package/lib/actions/crmWinbackConsent.d.ts +29 -0
- package/lib/actions/crmWinbackConsent.js +158 -0
- package/lib/actions/crmWinbackPolicy.d.ts +33 -0
- package/lib/actions/crmWinbackPolicy.js +115 -0
- package/lib/actions/crmWinbackShopSource.d.ts +15 -0
- package/lib/actions/crmWinbackShopSource.js +63 -0
- package/lib/actions/email.d.ts +4 -1
- package/lib/actions/email.js +25 -3
- package/lib/actions/index.d.ts +56 -0
- package/lib/actions/index.js +42 -1
- package/lib/actions/merchantAccountExperiment.d.ts +50 -0
- package/lib/actions/merchantAccountExperiment.js +424 -0
- package/lib/actions/merchantAccountExperimentConfig.d.ts +7 -0
- package/lib/actions/merchantAccountExperimentConfig.js +32 -0
- package/lib/actions/merchantAccountExperimentHost.d.ts +69 -0
- package/lib/actions/merchantAccountExperimentHost.js +92 -0
- package/lib/actions/merchantAccountExperimentPolicy.d.ts +66 -0
- package/lib/actions/merchantAccountExperimentPolicy.js +121 -0
- package/lib/actions/merchantAccountExperimentRelay.d.ts +10 -0
- package/lib/actions/merchantAccountExperimentRelay.js +75 -0
- package/lib/actions/merchantCalendar.d.ts +34 -0
- package/lib/actions/merchantCalendar.js +119 -0
- package/lib/actions/merchantCalendarBookings.d.ts +17 -0
- package/lib/actions/merchantCalendarBookings.js +113 -0
- package/lib/actions/merchantCalendarDelegation.d.ts +80 -0
- package/lib/actions/merchantCalendarDelegation.js +353 -0
- package/lib/actions/merchantCalendarDiscovery.d.ts +38 -0
- package/lib/actions/merchantCalendarDiscovery.js +124 -0
- package/lib/actions/merchantCalendarEventHost.d.ts +105 -0
- package/lib/actions/merchantCalendarEventHost.js +16 -0
- package/lib/actions/merchantCalendarEventPrivacy.d.ts +7 -0
- package/lib/actions/merchantCalendarEventPrivacy.js +70 -0
- package/lib/actions/merchantCalendarEventRepository.d.ts +121 -0
- package/lib/actions/merchantCalendarEventRepository.js +295 -0
- package/lib/actions/merchantCalendarEventTransport.d.ts +65 -0
- package/lib/actions/merchantCalendarEventTransport.js +132 -0
- package/lib/actions/merchantCalendarEvents.d.ts +172 -0
- package/lib/actions/merchantCalendarEvents.js +365 -0
- package/lib/actions/merchantCalendarInvitationConsent.d.ts +28 -0
- package/lib/actions/merchantCalendarInvitationConsent.js +118 -0
- package/lib/actions/merchantCalendarManagedSource.d.ts +22 -0
- package/lib/actions/merchantCalendarManagedSource.js +24 -0
- package/lib/actions/merchantCalendarOAuth.d.ts +92 -0
- package/lib/actions/merchantCalendarOAuth.js +502 -0
- package/lib/actions/merchantCalendarOAuthConfig.d.ts +4 -0
- package/lib/actions/merchantCalendarOAuthConfig.js +30 -0
- package/lib/actions/merchantCalendarOAuthRepository.d.ts +57 -0
- package/lib/actions/merchantCalendarOAuthRepository.js +108 -0
- package/lib/actions/merchantCalendarOAuthTransport.d.ts +4 -0
- package/lib/actions/merchantCalendarOAuthTransport.js +25 -0
- package/lib/actions/merchantCalendarScopes.d.ts +4 -0
- package/lib/actions/merchantCalendarScopes.js +31 -0
- package/lib/actions/merchantFinance.d.ts +65 -0
- package/lib/actions/merchantFinance.js +244 -0
- package/lib/actions/merchantFinanceProvider.d.ts +57 -0
- package/lib/actions/merchantFinanceProvider.js +180 -0
- package/lib/actions/merchantOperations.d.ts +94 -0
- package/lib/actions/merchantOperations.js +489 -0
- package/lib/actions/providerCredentials.d.ts +35 -0
- package/lib/actions/providerCredentials.js +166 -0
- package/lib/actions/s3.d.ts +10 -7
- package/lib/actions/s3.js +59 -14
- package/lib/actions/supportChannelSources.d.ts +66 -0
- package/lib/actions/supportChannelSources.js +183 -0
- package/lib/actions/supportCommerce.d.ts +62 -0
- package/lib/actions/supportCommerce.js +252 -0
- package/lib/actions/supportConnect.d.ts +39 -0
- package/lib/actions/supportConnect.js +224 -0
- package/lib/actions/supportContext.d.ts +31 -0
- package/lib/actions/supportContext.js +88 -0
- package/lib/actions/supportConversation.d.ts +102 -0
- package/lib/actions/supportConversation.js +100 -0
- package/lib/actions/supportEmailEvents.d.ts +20 -0
- package/lib/actions/supportEmailEvents.js +130 -0
- package/lib/actions/supportEmailHandoff.d.ts +114 -0
- package/lib/actions/supportEmailHandoff.js +356 -0
- package/lib/actions/supportOperations.d.ts +32 -0
- package/lib/actions/supportOperations.js +74 -0
- package/lib/actions/supportPolicy.d.ts +9 -0
- package/lib/actions/supportPolicy.js +44 -0
- package/lib/actions/supportPurposeConsent.d.ts +32 -0
- package/lib/actions/supportPurposeConsent.js +196 -0
- package/lib/actions/supportPurposePrivacy.d.ts +15 -0
- package/lib/actions/supportPurposePrivacy.js +181 -0
- package/lib/actions/supportSemanticReview.d.ts +64 -0
- package/lib/actions/supportSemanticReview.js +239 -0
- package/lib/actions/users.js +22 -3
- package/lib/constants/collections.d.ts +2 -1
- package/lib/constants/collections.js +2 -1
- package/lib/mutations/crm/crm.js +395 -7
- package/lib/queries/crm/crm.js +251 -5
- package/lib/types/auth.types.d.ts +2 -0
- package/lib/types/crm.types.d.ts +6 -0
- package/lib/types/users.types.d.ts +2 -0
- package/lib/utils/authUtils.js +17 -1
- package/lib/utils/graphqlUtils.d.ts +2 -0
- package/lib/utils/stripeUtils.ts.bak +1 -1
- package/package.json +8 -6
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { ApiContext } from '../types/auth.types.js';
|
|
2
|
+
import type { CrmCanonicalEvent, CrmVerifiedFinancialProviderEvidence } from './crmCanonicalPolicy.js';
|
|
3
|
+
import type { CrmOutcomeGrantBounds } from './crmInsights.js';
|
|
4
|
+
export interface CrmCanonicalReadInput {
|
|
5
|
+
appId: string;
|
|
6
|
+
campaignId: string;
|
|
7
|
+
financial?: boolean;
|
|
8
|
+
organizationId: string;
|
|
9
|
+
projectId: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CrmCanonicalFinanceBindingInput extends CrmCanonicalReadInput {
|
|
12
|
+
associationId: string;
|
|
13
|
+
evidenceRef: string;
|
|
14
|
+
expectedRevision: number;
|
|
15
|
+
expectedSourceRevisions: {
|
|
16
|
+
association: number;
|
|
17
|
+
invoice: string;
|
|
18
|
+
nativePayment: string;
|
|
19
|
+
order: string;
|
|
20
|
+
paymentEvidence: string;
|
|
21
|
+
user: string;
|
|
22
|
+
};
|
|
23
|
+
id: string;
|
|
24
|
+
invoiceId: string;
|
|
25
|
+
issuerId: string;
|
|
26
|
+
nativePaymentId: string;
|
|
27
|
+
paymentEvidenceId: string;
|
|
28
|
+
status: 'active' | 'revoked';
|
|
29
|
+
}
|
|
30
|
+
export interface CrmCanonicalFinancialObservation {
|
|
31
|
+
evidence: CrmVerifiedFinancialProviderEvidence;
|
|
32
|
+
id: string;
|
|
33
|
+
issuerId: string;
|
|
34
|
+
nativeOrderId: string;
|
|
35
|
+
revision: string;
|
|
36
|
+
shopifyCustomerId?: string;
|
|
37
|
+
shopifyOrderId?: string;
|
|
38
|
+
status: 'verified_settlement_namespace_unproven';
|
|
39
|
+
}
|
|
40
|
+
export interface CrmCanonicalSnapshot {
|
|
41
|
+
appId: string;
|
|
42
|
+
campaignId: string;
|
|
43
|
+
complete: boolean;
|
|
44
|
+
events: CrmCanonicalEvent[];
|
|
45
|
+
financialObservations: CrmCanonicalFinancialObservation[];
|
|
46
|
+
generatedAt: string;
|
|
47
|
+
held: Record<string, number>;
|
|
48
|
+
issuerIds?: string[];
|
|
49
|
+
organizationId: string;
|
|
50
|
+
projectId: string;
|
|
51
|
+
revision: number;
|
|
52
|
+
schemaVersion: 'crm_canonical_outcomes_v1';
|
|
53
|
+
sourceKind: 'lifecycle' | 'financial';
|
|
54
|
+
validUntil: string;
|
|
55
|
+
}
|
|
56
|
+
export declare const createCrmCanonicalReadKey: (context: ApiContext, appId: string, bounds: CrmOutcomeGrantBounds, financial: boolean, expiresAt: string, now?: () => number) => Promise<{
|
|
57
|
+
appId: string;
|
|
58
|
+
expiresAt: string;
|
|
59
|
+
id: string;
|
|
60
|
+
readKey: string;
|
|
61
|
+
scopes: import("./crmInsights.js").InsightScope[];
|
|
62
|
+
}>;
|
|
63
|
+
/** Binds existing owned evidence by CAS; registration never claims provider settlement authenticity. */
|
|
64
|
+
export declare const registerCrmCanonicalFinanceBinding: (context: ApiContext, input: CrmCanonicalFinanceBindingInput, now?: () => number) => Promise<{
|
|
65
|
+
id: string;
|
|
66
|
+
revision: number;
|
|
67
|
+
status: "active" | "revoked";
|
|
68
|
+
}>;
|
|
69
|
+
/** Complete scoped owner observations; financial rows remain separate until namespace proof. */
|
|
70
|
+
export declare const getCrmCanonicalOutcomes: (context: ApiContext, input: CrmCanonicalReadInput, now?: () => number) => Promise<CrmCanonicalSnapshot>;
|
|
71
|
+
export interface CrmCanonicalCorrectionInput extends CrmCanonicalReadInput {
|
|
72
|
+
associationId: string;
|
|
73
|
+
deleted?: boolean;
|
|
74
|
+
eventId: string;
|
|
75
|
+
evidenceRef: string;
|
|
76
|
+
expectedAssociationRevision: number;
|
|
77
|
+
expectedEventRevision: number;
|
|
78
|
+
expectedSourceRevision: string;
|
|
79
|
+
orderId?: string;
|
|
80
|
+
stage?: 'new' | 'qualified' | 'won' | 'lost' | 'disqualified';
|
|
81
|
+
}
|
|
82
|
+
/** Correct a real owner observation by CAS; never backdate/create an invented event or modify money. */
|
|
83
|
+
export declare const correctCrmCanonicalObservation: (context: ApiContext, input: CrmCanonicalCorrectionInput, now?: () => number) => Promise<{
|
|
84
|
+
associationRevision: number;
|
|
85
|
+
eventId: string;
|
|
86
|
+
eventRevision: number;
|
|
87
|
+
sourceRevision: string;
|
|
88
|
+
}>;
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop, no-underscore-dangle -- Native CAS and ordered source snapshot reconciliation. */ import { aql } from 'arangojs';
|
|
2
|
+
import { scopeContextToApp } from '../utils/appContext.js';
|
|
3
|
+
import { useDb } from '../utils/arangodbUtils.js';
|
|
4
|
+
import { requireCrmAdministrator } from './crm.js';
|
|
5
|
+
import { crmCanonicalDigest, crmCanonicalHistoryEvents } from './crmCanonicalPolicy.js';
|
|
6
|
+
import { ensureOwnerExecutionCollection } from './crmExecution.js';
|
|
7
|
+
import { nativeFinanceSource, readNativeCrmFinancialEvidence, validateNativeFinance } from './crmFinancialEvidence.js';
|
|
8
|
+
import { authorizeCrmInsightSource, createCrmInsightsReadKey } from './crmInsights.js';
|
|
9
|
+
const key = (value)=>typeof value === 'string' && /^[\w.-]{1,160}$/.test(value);
|
|
10
|
+
const validInput = (input)=>Boolean(input && [
|
|
11
|
+
input.appId,
|
|
12
|
+
input.organizationId,
|
|
13
|
+
input.projectId,
|
|
14
|
+
input.campaignId
|
|
15
|
+
].every(key) && (input.financial === undefined || typeof input.financial === 'boolean'));
|
|
16
|
+
export const createCrmCanonicalReadKey = async (context, appId, bounds, financial, expiresAt, now = Date.now)=>{
|
|
17
|
+
requireCrmAdministrator(context);
|
|
18
|
+
const scoped = await scopeContextToApp(context, appId);
|
|
19
|
+
const db = useDb(scoped.databaseName);
|
|
20
|
+
if (!key(bounds?.organizationId) || !await db.query(aql`RETURN DOCUMENT('groups', ${bounds.organizationId})`).then((cursor)=>cursor.next())) {
|
|
21
|
+
throw new Error('canonical_organization_required');
|
|
22
|
+
}
|
|
23
|
+
if (financial) {
|
|
24
|
+
if (!bounds.issuerIds?.length) {
|
|
25
|
+
throw new Error('canonical_issuer_bounds_required');
|
|
26
|
+
}
|
|
27
|
+
for (const issuerId of bounds.issuerIds){
|
|
28
|
+
const issuer = await db.query(aql`RETURN DOCUMENT('merchantInvoiceProfiles', ${issuerId})`).then((cursor)=>cursor.next());
|
|
29
|
+
if (!issuer || issuer.appId !== appId) {
|
|
30
|
+
throw new Error('canonical_owned_issuer_required');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return createCrmInsightsReadKey(context, appId, [
|
|
35
|
+
financial ? 'crm:canonical_finance' : 'crm:canonical_outcomes'
|
|
36
|
+
], expiresAt, now, bounds);
|
|
37
|
+
};
|
|
38
|
+
/** Binds existing owned evidence by CAS; registration never claims provider settlement authenticity. */ export const registerCrmCanonicalFinanceBinding = async (context, input, now = Date.now)=>{
|
|
39
|
+
requireCrmAdministrator(context);
|
|
40
|
+
const revisions = input?.expectedSourceRevisions;
|
|
41
|
+
if (!validInput(input) || ![
|
|
42
|
+
input.id,
|
|
43
|
+
input.associationId,
|
|
44
|
+
input.issuerId,
|
|
45
|
+
input.invoiceId,
|
|
46
|
+
input.paymentEvidenceId,
|
|
47
|
+
input.nativePaymentId
|
|
48
|
+
].every(key) || !input.evidenceRef?.trim() || input.evidenceRef.length > 1000 || !Number.isSafeInteger(input.expectedRevision) || input.expectedRevision < 0 || ![
|
|
49
|
+
'active',
|
|
50
|
+
'revoked'
|
|
51
|
+
].includes(input.status) || !revisions || Object.keys(revisions).sort().join(',') !== 'association,invoice,nativePayment,order,paymentEvidence,user' || !Number.isSafeInteger(revisions.association) || revisions.association < 1 || ![
|
|
52
|
+
revisions.invoice,
|
|
53
|
+
revisions.nativePayment,
|
|
54
|
+
revisions.order,
|
|
55
|
+
revisions.paymentEvidence,
|
|
56
|
+
revisions.user
|
|
57
|
+
].every(key)) {
|
|
58
|
+
throw new Error('invalid_canonical_finance_binding');
|
|
59
|
+
}
|
|
60
|
+
const scoped = await scopeContextToApp(context, input.appId);
|
|
61
|
+
const db = useDb(scoped.databaseName);
|
|
62
|
+
await ensureOwnerExecutionCollection(db, 'crmCanonicalFinanceBindings');
|
|
63
|
+
await ensureOwnerExecutionCollection(db, 'hasCrmCanonicalFinance', true);
|
|
64
|
+
const tx = await db.beginTransaction({
|
|
65
|
+
exclusive: [
|
|
66
|
+
'crmCanonicalFinanceBindings',
|
|
67
|
+
'hasCrmCanonicalFinance'
|
|
68
|
+
],
|
|
69
|
+
read: [
|
|
70
|
+
'hasCrmCampaignOutcome',
|
|
71
|
+
'customerOrders',
|
|
72
|
+
'users',
|
|
73
|
+
'merchantIssuedInvoices',
|
|
74
|
+
'merchantPaymentEvidence',
|
|
75
|
+
'payments',
|
|
76
|
+
'merchantInvoiceProfiles'
|
|
77
|
+
]
|
|
78
|
+
}, {
|
|
79
|
+
lockTimeout: 10
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
const source = await tx.step(()=>nativeFinanceSource(db, input.associationId, input, input.invoiceId, input.paymentEvidenceId, input.nativePaymentId));
|
|
83
|
+
if (!validateNativeFinance(source, input, input.issuerId, now()) || Object.entries(input.expectedSourceRevisions).some(([field, revision])=>field === 'association' ? source.association.revision !== revision : source[field]?._rev !== revision)) {
|
|
84
|
+
throw new Error('canonical_finance_source_conflict');
|
|
85
|
+
}
|
|
86
|
+
const prior = await tx.step(()=>db.query(aql`RETURN DOCUMENT('crmCanonicalFinanceBindings', ${input.id})`)).then((cursor)=>cursor.next());
|
|
87
|
+
const identity = {
|
|
88
|
+
appId: input.appId,
|
|
89
|
+
associationId: input.associationId,
|
|
90
|
+
campaignId: input.campaignId,
|
|
91
|
+
invoiceId: input.invoiceId,
|
|
92
|
+
issuerId: input.issuerId,
|
|
93
|
+
nativePaymentId: input.nativePaymentId,
|
|
94
|
+
organizationId: input.organizationId,
|
|
95
|
+
paymentEvidenceId: input.paymentEvidenceId,
|
|
96
|
+
projectId: input.projectId
|
|
97
|
+
};
|
|
98
|
+
if ((prior?.revision || 0) !== input.expectedRevision || prior?.status === 'revoked' || prior && Object.entries(identity).some(([field, value])=>prior[field] !== value)) {
|
|
99
|
+
throw new Error('canonical_finance_binding_conflict');
|
|
100
|
+
}
|
|
101
|
+
const record = {
|
|
102
|
+
_key: input.id,
|
|
103
|
+
...identity,
|
|
104
|
+
evidenceRef: input.evidenceRef,
|
|
105
|
+
expectedSourceRevisions: input.expectedSourceRevisions,
|
|
106
|
+
id: input.id,
|
|
107
|
+
modifiedAt: now(),
|
|
108
|
+
revision: input.expectedRevision + 1,
|
|
109
|
+
status: input.status
|
|
110
|
+
};
|
|
111
|
+
await tx.step(()=>db.query(aql`UPSERT {_key: ${input.id}} INSERT ${record} UPDATE ${record} IN crmCanonicalFinanceBindings`));
|
|
112
|
+
const edge = {
|
|
113
|
+
_from: `customerOrders/${source.order._key}`,
|
|
114
|
+
_key: input.id,
|
|
115
|
+
_to: `merchantIssuedInvoices/${input.invoiceId}`
|
|
116
|
+
};
|
|
117
|
+
await tx.step(()=>db.query(aql`UPSERT {_key: ${input.id}} INSERT ${edge} UPDATE ${edge} IN hasCrmCanonicalFinance`));
|
|
118
|
+
await tx.commit();
|
|
119
|
+
return {
|
|
120
|
+
id: input.id,
|
|
121
|
+
revision: record.revision,
|
|
122
|
+
status: input.status
|
|
123
|
+
};
|
|
124
|
+
} catch (error) {
|
|
125
|
+
await tx.abort();
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const authorize = async (context, input, at)=>{
|
|
130
|
+
if (!validInput(input) || Object.keys(input).some((key)=>![
|
|
131
|
+
'appId',
|
|
132
|
+
'campaignId',
|
|
133
|
+
'financial',
|
|
134
|
+
'organizationId',
|
|
135
|
+
'projectId'
|
|
136
|
+
].includes(key))) {
|
|
137
|
+
throw new Error('invalid_canonical_read_scope');
|
|
138
|
+
}
|
|
139
|
+
const authority = await authorizeCrmInsightSource(context, input.appId, at);
|
|
140
|
+
const bounds = authority.outcomeBounds;
|
|
141
|
+
if (!authority.scopes.includes(input.financial ? 'crm:canonical_finance' : 'crm:canonical_outcomes') || !bounds || bounds.organizationId !== input.organizationId || bounds.projectId !== input.projectId || !bounds.campaignIds.includes(input.campaignId)) {
|
|
142
|
+
throw new Error('canonical_read_access_required');
|
|
143
|
+
}
|
|
144
|
+
return authority;
|
|
145
|
+
};
|
|
146
|
+
const readHistory = async (db, input)=>db.collection('hasCrmCampaignOutcome').exists().then((exists)=>exists ? db.query(aql`
|
|
147
|
+
FOR association IN hasCrmCampaignOutcome FILTER association.appId == ${input.appId}
|
|
148
|
+
AND association.organizationId == ${input.organizationId} AND association.projectId == ${input.projectId}
|
|
149
|
+
AND association.campaignId == ${input.campaignId} AND association.kind == 'form_submission'
|
|
150
|
+
SORT association.id LIMIT 501
|
|
151
|
+
LET source = DOCUMENT('crmFormSubmissions', association.sourceId)
|
|
152
|
+
LET user = DOCUMENT('users', association.userId)
|
|
153
|
+
RETURN {association, source: source ? {revision: source._rev, history: source.crmOutcomeEvents || [],
|
|
154
|
+
truncated: source.crmOutcomeHistoryTruncated, deleted: source.deleted} : {revision: '', history: [],
|
|
155
|
+
deleted: true},
|
|
156
|
+
suppressed: user == null OR user.deleted == true OR user.crmAnalyticsSuppressed == true}
|
|
157
|
+
`).then((cursor)=>cursor.all()) : []);
|
|
158
|
+
/** Complete scoped owner observations; financial rows remain separate until namespace proof. */ export const getCrmCanonicalOutcomes = async (context, input, now = Date.now)=>{
|
|
159
|
+
const at = now();
|
|
160
|
+
const authority = await authorize(context, input, at);
|
|
161
|
+
const db = useDb(authority.context.databaseName);
|
|
162
|
+
const held = {};
|
|
163
|
+
const addHold = (reason)=>{
|
|
164
|
+
held[reason] = (held[reason] || 0) + 1;
|
|
165
|
+
};
|
|
166
|
+
const rows = input.financial ? [] : await readHistory(db, input);
|
|
167
|
+
let complete = rows.length <= 500;
|
|
168
|
+
let events = [];
|
|
169
|
+
for (const row of rows.slice(0, 500)){
|
|
170
|
+
const result = crmCanonicalHistoryEvents(row, at);
|
|
171
|
+
if (result.held) {
|
|
172
|
+
addHold(result.held);
|
|
173
|
+
}
|
|
174
|
+
complete &&= !result.incomplete;
|
|
175
|
+
events.push(...result.events);
|
|
176
|
+
}
|
|
177
|
+
const financialObservations = [];
|
|
178
|
+
const bindings = input.financial && await db.collection('crmCanonicalFinanceBindings').exists() ? await db.query(aql`FOR binding IN crmCanonicalFinanceBindings FILTER binding.appId == ${input.appId}
|
|
179
|
+
AND binding.organizationId == ${input.organizationId} AND binding.projectId == ${input.projectId}
|
|
180
|
+
AND binding.campaignId == ${input.campaignId}
|
|
181
|
+
AND binding.issuerId IN ${authority.outcomeBounds?.issuerIds || []} SORT binding.id LIMIT 51 RETURN binding`).then((cursor)=>cursor.all()) : [];
|
|
182
|
+
complete &&= bindings.length <= 50;
|
|
183
|
+
for (const binding of bindings.slice(0, 50)){
|
|
184
|
+
if (binding.status !== 'active') {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (!authority.outcomeBounds?.issuerIds?.includes(binding.issuerId)) {
|
|
188
|
+
throw new Error('canonical_issuer_access_required');
|
|
189
|
+
}
|
|
190
|
+
const verification = await readNativeCrmFinancialEvidence(db, input, binding, at);
|
|
191
|
+
if (!verification.evidence) {
|
|
192
|
+
addHold(verification.held);
|
|
193
|
+
if ([
|
|
194
|
+
'provider_verification_unavailable',
|
|
195
|
+
'provider_refund_evidence_incomplete'
|
|
196
|
+
].includes(verification.held)) {
|
|
197
|
+
complete = false;
|
|
198
|
+
}
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const { source } = verification;
|
|
202
|
+
financialObservations.push({
|
|
203
|
+
evidence: verification.evidence,
|
|
204
|
+
id: crmCanonicalDigest([
|
|
205
|
+
input.appId,
|
|
206
|
+
binding.id
|
|
207
|
+
]),
|
|
208
|
+
issuerId: binding.issuerId,
|
|
209
|
+
nativeOrderId: crmCanonicalDigest([
|
|
210
|
+
input.appId,
|
|
211
|
+
source.order._key
|
|
212
|
+
]),
|
|
213
|
+
revision: verification.revision,
|
|
214
|
+
shopifyCustomerId: source.user.shopifyCustomerId,
|
|
215
|
+
shopifyOrderId: source.order.shopifyOrderId,
|
|
216
|
+
status: 'verified_settlement_namespace_unproven'
|
|
217
|
+
});
|
|
218
|
+
addHold('verified_financial_namespace_unproven');
|
|
219
|
+
}
|
|
220
|
+
const current = await authorize(context, input, now());
|
|
221
|
+
if (current.context.databaseName !== authority.context.databaseName || current.expiresAt !== authority.expiresAt || crmCanonicalDigest(current.outcomeBounds) !== crmCanonicalDigest(authority.outcomeBounds) || crmCanonicalDigest(current.scopes) !== crmCanonicalDigest(authority.scopes) || crmCanonicalDigest(rows) !== crmCanonicalDigest(input.financial ? [] : await readHistory(db, input))) {
|
|
222
|
+
throw new Error('canonical_source_or_grant_changed');
|
|
223
|
+
}
|
|
224
|
+
if (events.length > 500) {
|
|
225
|
+
complete = false;
|
|
226
|
+
events = events.slice(0, 500);
|
|
227
|
+
addHold('bounded_history_truncated');
|
|
228
|
+
}
|
|
229
|
+
const snapshot = {
|
|
230
|
+
appId: input.appId,
|
|
231
|
+
campaignId: input.campaignId,
|
|
232
|
+
complete,
|
|
233
|
+
events,
|
|
234
|
+
financialObservations,
|
|
235
|
+
generatedAt: new Date(at).toISOString(),
|
|
236
|
+
held,
|
|
237
|
+
issuerIds: input.financial ? [
|
|
238
|
+
...authority.outcomeBounds.issuerIds
|
|
239
|
+
].sort() : undefined,
|
|
240
|
+
organizationId: input.organizationId,
|
|
241
|
+
projectId: input.projectId,
|
|
242
|
+
revision: 1,
|
|
243
|
+
schemaVersion: 'crm_canonical_outcomes_v1',
|
|
244
|
+
sourceKind: input.financial ? 'financial' : 'lifecycle',
|
|
245
|
+
validUntil: new Date(Math.min(at + 900000, authority.expiresAt || Infinity)).toISOString()
|
|
246
|
+
};
|
|
247
|
+
// A stable monotonic source revision is committed with complete snapshot replacement; incomplete reads never erase
|
|
248
|
+
// history.
|
|
249
|
+
await ensureOwnerExecutionCollection(db, 'crmCanonicalSnapshots');
|
|
250
|
+
await db.collection('crmCanonicalSnapshots').ensureIndex({
|
|
251
|
+
expireAfter: 0,
|
|
252
|
+
fields: [
|
|
253
|
+
'expiresAt'
|
|
254
|
+
],
|
|
255
|
+
type: 'ttl'
|
|
256
|
+
});
|
|
257
|
+
const id = crmCanonicalDigest([
|
|
258
|
+
input.appId,
|
|
259
|
+
input.organizationId,
|
|
260
|
+
input.projectId,
|
|
261
|
+
input.campaignId,
|
|
262
|
+
Boolean(input.financial),
|
|
263
|
+
input.financial ? [
|
|
264
|
+
...authority.outcomeBounds.issuerIds
|
|
265
|
+
].sort() : []
|
|
266
|
+
]);
|
|
267
|
+
const tx = await db.beginTransaction({
|
|
268
|
+
exclusive: [
|
|
269
|
+
'crmCanonicalSnapshots'
|
|
270
|
+
]
|
|
271
|
+
}, {
|
|
272
|
+
lockTimeout: 10
|
|
273
|
+
});
|
|
274
|
+
try {
|
|
275
|
+
const prior = await tx.step(()=>db.query(aql`RETURN DOCUMENT('crmCanonicalSnapshots', ${id})`)).then((cursor)=>cursor.next());
|
|
276
|
+
const digest = crmCanonicalDigest({
|
|
277
|
+
events,
|
|
278
|
+
financialObservations,
|
|
279
|
+
held
|
|
280
|
+
});
|
|
281
|
+
snapshot.revision = prior ? prior.revision + Number(prior.digest !== digest) : 1;
|
|
282
|
+
if (complete) {
|
|
283
|
+
const previous = prior?.events || [];
|
|
284
|
+
snapshot.events = events.map((event)=>{
|
|
285
|
+
const old = previous.find((item)=>item.eventId === event.eventId);
|
|
286
|
+
return {
|
|
287
|
+
...event,
|
|
288
|
+
revision: old ? Math.max(event.revision, old.revision + Number(Boolean(old.deleted))) : event.revision
|
|
289
|
+
};
|
|
290
|
+
});
|
|
291
|
+
snapshot.events.push(...previous.filter((event)=>!events.some((item)=>item.eventId === event.eventId)).map((event)=>({
|
|
292
|
+
...event,
|
|
293
|
+
deleted: true,
|
|
294
|
+
revision: event.revision + Number(!event.deleted)
|
|
295
|
+
})));
|
|
296
|
+
if (snapshot.events.length > 1000) {
|
|
297
|
+
throw new Error('canonical_tombstone_retention_bound');
|
|
298
|
+
}
|
|
299
|
+
const persisted = {
|
|
300
|
+
_key: id,
|
|
301
|
+
digest,
|
|
302
|
+
events: snapshot.events,
|
|
303
|
+
expiresAt: Math.floor(at / 1000) + 30 * 86400,
|
|
304
|
+
revision: snapshot.revision
|
|
305
|
+
};
|
|
306
|
+
await tx.step(()=>db.query(aql`UPSERT {_key: ${id}} INSERT ${persisted}
|
|
307
|
+
UPDATE ${persisted} IN crmCanonicalSnapshots`));
|
|
308
|
+
}
|
|
309
|
+
await tx.commit();
|
|
310
|
+
} catch (error) {
|
|
311
|
+
await tx.abort();
|
|
312
|
+
throw error;
|
|
313
|
+
}
|
|
314
|
+
return snapshot;
|
|
315
|
+
};
|
|
316
|
+
/** Correct a real owner observation by CAS; never backdate/create an invented event or modify money. */ export const correctCrmCanonicalObservation = async (context, input, now = Date.now)=>{
|
|
317
|
+
requireCrmAdministrator(context);
|
|
318
|
+
if (!validInput(input) || !key(input.associationId) || !key(input.expectedSourceRevision) || !/^[a-f0-9]{64}$/.test(input.eventId) || !input.evidenceRef?.trim() || input.evidenceRef.length > 1000 || !Number.isSafeInteger(input.expectedAssociationRevision) || input.expectedAssociationRevision < 1 || !Number.isSafeInteger(input.expectedEventRevision) || input.expectedEventRevision < 1 || input.deleted !== true && ![
|
|
319
|
+
'new',
|
|
320
|
+
'qualified',
|
|
321
|
+
'won',
|
|
322
|
+
'lost',
|
|
323
|
+
'disqualified'
|
|
324
|
+
].includes(input.stage || '')) {
|
|
325
|
+
throw new Error('invalid_canonical_observation_correction');
|
|
326
|
+
}
|
|
327
|
+
const scoped = await scopeContextToApp(context, input.appId);
|
|
328
|
+
const db = useDb(scoped.databaseName);
|
|
329
|
+
const tx = await db.beginTransaction({
|
|
330
|
+
exclusive: [
|
|
331
|
+
'hasCrmCampaignOutcome',
|
|
332
|
+
'crmFormSubmissions'
|
|
333
|
+
],
|
|
334
|
+
read: [
|
|
335
|
+
'customerOrders'
|
|
336
|
+
]
|
|
337
|
+
}, {
|
|
338
|
+
lockTimeout: 10
|
|
339
|
+
});
|
|
340
|
+
const query = (value)=>tx.step(()=>db.query(value));
|
|
341
|
+
try {
|
|
342
|
+
const association = await query(aql`RETURN DOCUMENT('hasCrmCampaignOutcome', ${input.associationId})`).then((cursor)=>cursor.next());
|
|
343
|
+
if (!association || association.appId !== input.appId || association.organizationId !== input.organizationId || association.projectId !== input.projectId || association.campaignId !== input.campaignId || association.kind !== 'form_submission' || association.revision !== input.expectedAssociationRevision) {
|
|
344
|
+
throw new Error('canonical_correction_association_conflict');
|
|
345
|
+
}
|
|
346
|
+
const source = await query(aql`RETURN DOCUMENT('crmFormSubmissions', ${association.sourceId})`).then((cursor)=>cursor.next());
|
|
347
|
+
const index = source?.crmOutcomeEvents?.findIndex((event)=>event.id === input.eventId) ?? -1;
|
|
348
|
+
const event = source?.crmOutcomeEvents?.[index];
|
|
349
|
+
if (!source || source._rev !== input.expectedSourceRevision || source._rev !== association.sourceRevision || !event || event.revision !== input.expectedEventRevision || event.deleted || !Number.isSafeInteger(event.at) || event.at > now()) {
|
|
350
|
+
throw new Error('canonical_correction_source_conflict');
|
|
351
|
+
}
|
|
352
|
+
if (input.stage === 'won' && input.deleted !== true) {
|
|
353
|
+
if (!key(input.orderId)) {
|
|
354
|
+
throw new Error('canonical_won_order_required');
|
|
355
|
+
}
|
|
356
|
+
const order = await query(aql`RETURN DOCUMENT('customerOrders', ${input.orderId})`).then((cursor)=>cursor.next());
|
|
357
|
+
if (!order || order.userId !== source.userId || !order.shopifyOrderId || association.productId && !order.productIds?.includes(association.productId)) {
|
|
358
|
+
throw new Error('canonical_won_order_mismatch');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
const history = source.crmOutcomeEvents.map((value, position)=>position === index ? {
|
|
362
|
+
...value,
|
|
363
|
+
correctedAt: now(),
|
|
364
|
+
correctionEvidenceRef: input.evidenceRef,
|
|
365
|
+
deleted: input.deleted === true || undefined,
|
|
366
|
+
revision: value.revision + 1,
|
|
367
|
+
stage: input.deleted === true ? value.stage : input.stage
|
|
368
|
+
} : value);
|
|
369
|
+
const updates = {
|
|
370
|
+
crmOutcomeEvents: history,
|
|
371
|
+
...index === history.length - 1 && !input.deleted ? {
|
|
372
|
+
crmLeadStage: input.stage
|
|
373
|
+
} : {}
|
|
374
|
+
};
|
|
375
|
+
const saved = await query(aql`UPDATE ${source._key} WITH ${updates} IN crmFormSubmissions RETURN NEW`).then((cursor)=>cursor.next());
|
|
376
|
+
const update = {
|
|
377
|
+
revision: association.revision + 1,
|
|
378
|
+
sourceRevision: saved._rev,
|
|
379
|
+
...index === history.length - 1 && !input.deleted ? {
|
|
380
|
+
orderId: input.stage === 'won' ? input.orderId : null,
|
|
381
|
+
stage: input.stage
|
|
382
|
+
} : {}
|
|
383
|
+
};
|
|
384
|
+
await query(aql`UPDATE ${input.associationId} WITH ${update} IN hasCrmCampaignOutcome`);
|
|
385
|
+
await tx.commit();
|
|
386
|
+
return {
|
|
387
|
+
associationRevision: update.revision,
|
|
388
|
+
eventId: input.eventId,
|
|
389
|
+
eventRevision: event.revision + 1,
|
|
390
|
+
sourceRevision: saved._rev
|
|
391
|
+
};
|
|
392
|
+
} catch (error) {
|
|
393
|
+
await tx.abort();
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9Vc2Vycy9uaXRyb2c3L0RldmVsb3BtZW50L3JlYWt0b3IvcGFja2FnZXMvcmVha3Rvci9zcmMvYWN0aW9ucy9jcm1DYW5vbmljYWxPdXRjb21lcy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBuby1hd2FpdC1pbi1sb29wLCBuby11bmRlcnNjb3JlLWRhbmdsZSAtLSBOYXRpdmUgQ0FTIGFuZCBvcmRlcmVkIHNvdXJjZSBzbmFwc2hvdCByZWNvbmNpbGlhdGlvbi4gKi9cbmltcG9ydCB7YXFsfSBmcm9tICdhcmFuZ29qcyc7XG5cbmltcG9ydCB7c2NvcGVDb250ZXh0VG9BcHB9IGZyb20gJy4uL3V0aWxzL2FwcENvbnRleHQuanMnO1xuaW1wb3J0IHt1c2VEYn0gZnJvbSAnLi4vdXRpbHMvYXJhbmdvZGJVdGlscy5qcyc7XG5pbXBvcnQge3JlcXVpcmVDcm1BZG1pbmlzdHJhdG9yfSBmcm9tICcuL2NybS5qcyc7XG5pbXBvcnQge2NybUNhbm9uaWNhbERpZ2VzdCwgY3JtQ2Fub25pY2FsSGlzdG9yeUV2ZW50c30gZnJvbSAnLi9jcm1DYW5vbmljYWxQb2xpY3kuanMnO1xuaW1wb3J0IHtlbnN1cmVPd25lckV4ZWN1dGlvbkNvbGxlY3Rpb259IGZyb20gJy4vY3JtRXhlY3V0aW9uLmpzJztcbmltcG9ydCB7bmF0aXZlRmluYW5jZVNvdXJjZSwgcmVhZE5hdGl2ZUNybUZpbmFuY2lhbEV2aWRlbmNlLCB2YWxpZGF0ZU5hdGl2ZUZpbmFuY2V9IGZyb20gJy4vY3JtRmluYW5jaWFsRXZpZGVuY2UuanMnO1xuaW1wb3J0IHthdXRob3JpemVDcm1JbnNpZ2h0U291cmNlLCBjcmVhdGVDcm1JbnNpZ2h0c1JlYWRLZXl9IGZyb20gJy4vY3JtSW5zaWdodHMuanMnO1xuXG5pbXBvcnQgdHlwZSB7QXBpQ29udGV4dH0gZnJvbSAnLi4vdHlwZXMvYXV0aC50eXBlcy5qcyc7XG5pbXBvcnQgdHlwZSB7Q3JtQ2Fub25pY2FsRXZlbnQsIENybUNhbm9uaWNhbE5hdGl2ZUhpc3RvcnksXG4gIENybVZlcmlmaWVkRmluYW5jaWFsUHJvdmlkZXJFdmlkZW5jZX0gZnJvbSAnLi9jcm1DYW5vbmljYWxQb2xpY3kuanMnO1xuaW1wb3J0IHR5cGUge0NybU91dGNvbWVHcmFudEJvdW5kc30gZnJvbSAnLi9jcm1JbnNpZ2h0cy5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ3JtQ2Fub25pY2FsUmVhZElucHV0IHtcbiAgYXBwSWQ6IHN0cmluZztcbiAgY2FtcGFpZ25JZDogc3RyaW5nO1xuICBmaW5hbmNpYWw/OiBib29sZWFuO1xuICBvcmdhbml6YXRpb25JZDogc3RyaW5nO1xuICBwcm9qZWN0SWQ6IHN0cmluZztcbn1cbmV4cG9ydCBpbnRlcmZhY2UgQ3JtQ2Fub25pY2FsRmluYW5jZUJpbmRpbmdJbnB1dCBleHRlbmRzIENybUNhbm9uaWNhbFJlYWRJbnB1dCB7XG4gIGFzc29jaWF0aW9uSWQ6IHN0cmluZztcbiAgZXZpZGVuY2VSZWY6IHN0cmluZztcbiAgZXhwZWN0ZWRSZXZpc2lvbjogbnVtYmVyO1xuICBleHBlY3RlZFNvdXJjZVJldmlzaW9uczoge2Fzc29jaWF0aW9uOiBudW1iZXI7IGludm9pY2U6IHN0cmluZzsgbmF0aXZlUGF5bWVudDogc3RyaW5nO1xuICAgIG9yZGVyOiBzdHJpbmc7IHBheW1lbnRFdmlkZW5jZTogc3RyaW5nOyB1c2VyOiBzdHJpbmc7fTtcbiAgaWQ6IHN0cmluZztcbiAgaW52b2ljZUlkOiBzdHJpbmc7XG4gIGlzc3VlcklkOiBzdHJpbmc7XG4gIG5hdGl2ZVBheW1lbnRJZDogc3RyaW5nO1xuICBwYXltZW50RXZpZGVuY2VJZDogc3RyaW5nO1xuICBzdGF0dXM6ICdhY3RpdmUnIHwgJ3Jldm9rZWQnO1xufVxuZXhwb3J0IGludGVyZmFjZSBDcm1DYW5vbmljYWxGaW5hbmNpYWxPYnNlcnZhdGlvbiB7XG4gIGV2aWRlbmNlOiBDcm1WZXJpZmllZEZpbmFuY2lhbFByb3ZpZGVyRXZpZGVuY2U7XG4gIGlkOiBzdHJpbmc7XG4gIGlzc3VlcklkOiBzdHJpbmc7XG4gIG5hdGl2ZU9yZGVySWQ6IHN0cmluZztcbiAgcmV2aXNpb246IHN0cmluZztcbiAgc2hvcGlmeUN1c3RvbWVySWQ/OiBzdHJpbmc7XG4gIHNob3BpZnlPcmRlcklkPzogc3RyaW5nO1xuICBzdGF0dXM6ICd2ZXJpZmllZF9zZXR0bGVtZW50X25hbWVzcGFjZV91bnByb3Zlbic7XG59XG5leHBvcnQgaW50ZXJmYWNlIENybUNhbm9uaWNhbFNuYXBzaG90IHtcbiAgYXBwSWQ6IHN0cmluZztcbiAgY2FtcGFpZ25JZDogc3RyaW5nO1xuICBjb21wbGV0ZTogYm9vbGVhbjtcbiAgZXZlbnRzOiBDcm1DYW5vbmljYWxFdmVudFtdO1xuICBmaW5hbmNpYWxPYnNlcnZhdGlvbnM6IENybUNhbm9uaWNhbEZpbmFuY2lhbE9ic2VydmF0aW9uW107XG4gIGdlbmVyYXRlZEF0OiBzdHJpbmc7XG4gIGhlbGQ6IFJlY29yZDxzdHJpbmcsIG51bWJlcj47XG4gIGlzc3Vlcklkcz86IHN0cmluZ1tdO1xuICBvcmdhbml6YXRpb25JZDogc3RyaW5nO1xuICBwcm9qZWN0SWQ6IHN0cmluZztcbiAgcmV2aXNpb246IG51bWJlcjtcbiAgc2NoZW1hVmVyc2lvbjogJ2NybV9jYW5vbmljYWxfb3V0Y29tZXNfdjEnO1xuICBzb3VyY2VLaW5kOiAnbGlmZWN5Y2xlJyB8ICdmaW5hbmNpYWwnO1xuICB2YWxpZFVudGlsOiBzdHJpbmc7XG59XG5jb25zdCBrZXkgPSAodmFsdWU6IHVua25vd24pOiB2YWx1ZSBpcyBzdHJpbmcgPT4gdHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJyAmJiAvXltcXHcuLV17MSwxNjB9JC8udGVzdCh2YWx1ZSk7XG5jb25zdCB2YWxpZElucHV0ID0gKGlucHV0OiBDcm1DYW5vbmljYWxSZWFkSW5wdXQpOiBib29sZWFuID0+IEJvb2xlYW4oaW5wdXRcbiAgJiYgW2lucHV0LmFwcElkLCBpbnB1dC5vcmdhbml6YXRpb25JZCwgaW5wdXQucHJvamVjdElkLCBpbnB1dC5jYW1wYWlnbklkXS5ldmVyeShrZXkpXG4gICYmIChpbnB1dC5maW5hbmNpYWwgPT09IHVuZGVmaW5lZCB8fCB0eXBlb2YgaW5wdXQuZmluYW5jaWFsID09PSAnYm9vbGVhbicpKTtcbmV4cG9ydCBjb25zdCBjcmVhdGVDcm1DYW5vbmljYWxSZWFkS2V5ID0gYXN5bmMgKFxuICBjb250ZXh0OiBBcGlDb250ZXh0LCBhcHBJZDogc3RyaW5nLCBib3VuZHM6IENybU91dGNvbWVHcmFudEJvdW5kcywgZmluYW5jaWFsOiBib29sZWFuLCBleHBpcmVzQXQ6IHN0cmluZyxcbiAgbm93ID0gRGF0ZS5ub3dcbikgPT4ge1xuICByZXF1aXJlQ3JtQWRtaW5pc3RyYXRvcihjb250ZXh0KTtcbiAgY29uc3Qgc2NvcGVkID0gYXdhaXQgc2NvcGVDb250ZXh0VG9BcHAoY29udGV4dCwgYXBwSWQpO1xuICBjb25zdCBkYiA9IHVzZURiKHNjb3BlZC5kYXRhYmFzZU5hbWUpO1xuICBpZigha2V5KGJvdW5kcz8ub3JnYW5pemF0aW9uSWQpIHx8ICFhd2FpdCBkYi5xdWVyeShhcWxgUkVUVVJOIERPQ1VNRU5UKCdncm91cHMnLCAke2JvdW5kcy5vcmdhbml6YXRpb25JZH0pYClcbiAgICAudGhlbigoY3Vyc29yKSA9PiBjdXJzb3IubmV4dCgpKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignY2Fub25pY2FsX29yZ2FuaXphdGlvbl9yZXF1aXJlZCcpO1xuICB9XG4gIGlmKGZpbmFuY2lhbCkge1xuICAgIGlmKCFib3VuZHMuaXNzdWVySWRzPy5sZW5ndGgpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignY2Fub25pY2FsX2lzc3Vlcl9ib3VuZHNfcmVxdWlyZWQnKTtcbiAgICB9XG4gICAgZm9yKGNvbnN0IGlzc3VlcklkIG9mIGJvdW5kcy5pc3N1ZXJJZHMpIHtcbiAgICAgIGNvbnN0IGlzc3VlciA9IGF3YWl0IGRiLnF1ZXJ5KGFxbGBSRVRVUk4gRE9DVU1FTlQoJ21lcmNoYW50SW52b2ljZVByb2ZpbGVzJywgJHtpc3N1ZXJJZH0pYClcbiAgICAgICAgLnRoZW4oKGN1cnNvcikgPT4gY3Vyc29yLm5leHQoKSk7XG4gICAgICBpZighaXNzdWVyIHx8IGlzc3Vlci5hcHBJZCAhPT0gYXBwSWQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdjYW5vbmljYWxfb3duZWRfaXNzdWVyX3JlcXVpcmVkJyk7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiBjcmVhdGVDcm1JbnNpZ2h0c1JlYWRLZXkoY29udGV4dCwgYXBwSWQsXG4gICAgW2ZpbmFuY2lhbCA/ICdjcm06Y2Fub25pY2FsX2ZpbmFuY2UnIDogJ2NybTpjYW5vbmljYWxfb3V0Y29tZXMnXSwgZXhwaXJlc0F0LCBub3csIGJvdW5kcyk7XG59O1xuLyoqIEJpbmRzIGV4aXN0aW5nIG93bmVkIGV2aWRlbmNlIGJ5IENBUzsgcmVnaXN0cmF0aW9uIG5ldmVyIGNsYWltcyBwcm92aWRlciBzZXR0bGVtZW50IGF1dGhlbnRpY2l0eS4gKi9cbmV4cG9ydCBjb25zdCByZWdpc3RlckNybUNhbm9uaWNhbEZpbmFuY2VCaW5kaW5nID0gYXN5bmMgKFxuICBjb250ZXh0OiBBcGlDb250ZXh0LCBpbnB1dDogQ3JtQ2Fub25pY2FsRmluYW5jZUJpbmRpbmdJbnB1dCwgbm93ID0gRGF0ZS5ub3dcbikgPT4ge1xuICByZXF1aXJlQ3JtQWRtaW5pc3RyYXRvcihjb250ZXh0KTtcbiAgY29uc3QgcmV2aXNpb25zID0gaW5wdXQ/LmV4cGVjdGVkU291cmNlUmV2aXNpb25zO1xuICBpZighdmFsaWRJbnB1dChpbnB1dCkgfHwgIVtpbnB1dC5pZCwgaW5wdXQuYXNzb2NpYXRpb25JZCwgaW5wdXQuaXNzdWVySWQsIGlucHV0Lmludm9pY2VJZCxcbiAgICBpbnB1dC5wYXltZW50RXZpZGVuY2VJZCwgaW5wdXQubmF0aXZlUGF5bWVudElkXS5ldmVyeShrZXkpIHx8ICFpbnB1dC5ldmlkZW5jZVJlZj8udHJpbSgpXG4gICAgfHwgaW5wdXQuZXZpZGVuY2VSZWYubGVuZ3RoID4gMTAwMCB8fCAhTnVtYmVyLmlzU2FmZUludGVnZXIoaW5wdXQuZXhwZWN0ZWRSZXZpc2lvbikgfHwgaW5wdXQuZXhwZWN0ZWRSZXZpc2lvbiA8IDBcbiAgICB8fCAhWydhY3RpdmUnLCAncmV2b2tlZCddLmluY2x1ZGVzKGlucHV0LnN0YXR1cykgfHwgIXJldmlzaW9uc1xuICAgIHx8IE9iamVjdC5rZXlzKHJldmlzaW9ucykuc29ydCgpLmpvaW4oJywnKSAhPT0gJ2Fzc29jaWF0aW9uLGludm9pY2UsbmF0aXZlUGF5bWVudCxvcmRlcixwYXltZW50RXZpZGVuY2UsdXNlcidcbiAgICB8fCAhTnVtYmVyLmlzU2FmZUludGVnZXIocmV2aXNpb25zLmFzc29jaWF0aW9uKSB8fCByZXZpc2lvbnMuYXNzb2NpYXRpb24gPCAxXG4gICAgfHwgIVtyZXZpc2lvbnMuaW52b2ljZSwgcmV2aXNpb25zLm5hdGl2ZVBheW1lbnQsIHJldmlzaW9ucy5vcmRlciwgcmV2aXNpb25zLnBheW1lbnRFdmlkZW5jZSxcbiAgICAgIHJldmlzaW9ucy51c2VyXS5ldmVyeShrZXkpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkX2Nhbm9uaWNhbF9maW5hbmNlX2JpbmRpbmcnKTtcbiAgfVxuICBjb25zdCBzY29wZWQgPSBhd2FpdCBzY29wZUNvbnRleHRUb0FwcChjb250ZXh0LCBpbnB1dC5hcHBJZCk7XG4gIGNvbnN0IGRiID0gdXNlRGIoc2NvcGVkLmRhdGFiYXNlTmFtZSk7XG4gIGF3YWl0IGVuc3VyZU93bmVyRXhlY3V0aW9uQ29sbGVjdGlvbihkYiwgJ2NybUNhbm9uaWNhbEZpbmFuY2VCaW5kaW5ncycpO1xuICBhd2FpdCBlbnN1cmVPd25lckV4ZWN1dGlvbkNvbGxlY3Rpb24oZGIsICdoYXNDcm1DYW5vbmljYWxGaW5hbmNlJywgdHJ1ZSk7XG4gIGNvbnN0IHR4ID0gYXdhaXQgZGIuYmVnaW5UcmFuc2FjdGlvbih7ZXhjbHVzaXZlOiBbJ2NybUNhbm9uaWNhbEZpbmFuY2VCaW5kaW5ncycsICdoYXNDcm1DYW5vbmljYWxGaW5hbmNlJ10sXG4gICAgcmVhZDogWydoYXNDcm1DYW1wYWlnbk91dGNvbWUnLCAnY3VzdG9tZXJPcmRlcnMnLCAndXNlcnMnLCAnbWVyY2hhbnRJc3N1ZWRJbnZvaWNlcycsICdtZXJjaGFudFBheW1lbnRFdmlkZW5jZScsXG4gICAgICAncGF5bWVudHMnLCAnbWVyY2hhbnRJbnZvaWNlUHJvZmlsZXMnXX0sIHtsb2NrVGltZW91dDogMTB9KTtcbiAgdHJ5IHtcbiAgICBjb25zdCBzb3VyY2UgPSBhd2FpdCB0eC5zdGVwKCgpID0+IG5hdGl2ZUZpbmFuY2VTb3VyY2UoZGIsIGlucHV0LmFzc29jaWF0aW9uSWQsIGlucHV0LFxuICAgICAgaW5wdXQuaW52b2ljZUlkLCBpbnB1dC5wYXltZW50RXZpZGVuY2VJZCwgaW5wdXQubmF0aXZlUGF5bWVudElkKSk7XG4gICAgaWYoIXZhbGlkYXRlTmF0aXZlRmluYW5jZShzb3VyY2UsIGlucHV0LCBpbnB1dC5pc3N1ZXJJZCwgbm93KCkpXG4gICAgICB8fCBPYmplY3QuZW50cmllcyhpbnB1dC5leHBlY3RlZFNvdXJjZVJldmlzaW9ucykuc29tZSgoW2ZpZWxkLCByZXZpc2lvbl0pID0+XG4gICAgICAgIChmaWVsZCA9PT0gJ2Fzc29jaWF0aW9uJyA/IHNvdXJjZS5hc3NvY2lhdGlvbi5yZXZpc2lvbiAhPT0gcmV2aXNpb24gOiBzb3VyY2VbZmllbGRdPy5fcmV2ICE9PSByZXZpc2lvbikpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ2Nhbm9uaWNhbF9maW5hbmNlX3NvdXJjZV9jb25mbGljdCcpO1xuICAgIH1cbiAgICBjb25zdCBwcmlvciA9IGF3YWl0IHR4LnN0ZXAoKCkgPT4gZGIucXVlcnkoYXFsYFJFVFVSTiBET0NVTUVOVCgnY3JtQ2Fub25pY2FsRmluYW5jZUJpbmRpbmdzJywgJHtpbnB1dC5pZH0pYCkpXG4gICAgICAudGhlbigoY3Vyc29yKSA9PiBjdXJzb3IubmV4dCgpKTtcbiAgICBjb25zdCBpZGVudGl0eSA9IHthcHBJZDogaW5wdXQuYXBwSWQsIGFzc29jaWF0aW9uSWQ6IGlucHV0LmFzc29jaWF0aW9uSWQsIGNhbXBhaWduSWQ6IGlucHV0LmNhbXBhaWduSWQsXG4gICAgICBpbnZvaWNlSWQ6IGlucHV0Lmludm9pY2VJZCwgaXNzdWVySWQ6IGlucHV0Lmlzc3VlcklkLCBuYXRpdmVQYXltZW50SWQ6IGlucHV0Lm5hdGl2ZVBheW1lbnRJZCxcbiAgICAgIG9yZ2FuaXphdGlvbklkOiBpbnB1dC5vcmdhbml6YXRpb25JZCwgcGF5bWVudEV2aWRlbmNlSWQ6IGlucHV0LnBheW1lbnRFdmlkZW5jZUlkLCBwcm9qZWN0SWQ6IGlucHV0LnByb2plY3RJZH07XG4gICAgaWYoKHByaW9yPy5yZXZpc2lvbiB8fCAwKSAhPT0gaW5wdXQuZXhwZWN0ZWRSZXZpc2lvbiB8fCBwcmlvcj8uc3RhdHVzID09PSAncmV2b2tlZCdcbiAgICAgIHx8IChwcmlvciAmJiBPYmplY3QuZW50cmllcyhpZGVudGl0eSkuc29tZSgoW2ZpZWxkLCB2YWx1ZV0pID0+IHByaW9yW2ZpZWxkXSAhPT0gdmFsdWUpKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdjYW5vbmljYWxfZmluYW5jZV9iaW5kaW5nX2NvbmZsaWN0Jyk7XG4gICAgfVxuICAgIGNvbnN0IHJlY29yZCA9IHtfa2V5OiBpbnB1dC5pZCwgLi4uaWRlbnRpdHksIGV2aWRlbmNlUmVmOiBpbnB1dC5ldmlkZW5jZVJlZixcbiAgICAgIGV4cGVjdGVkU291cmNlUmV2aXNpb25zOiBpbnB1dC5leHBlY3RlZFNvdXJjZVJldmlzaW9ucyxcbiAgICAgIGlkOiBpbnB1dC5pZCwgbW9kaWZpZWRBdDogbm93KCksIHJldmlzaW9uOiBpbnB1dC5leHBlY3RlZFJldmlzaW9uICsgMSwgc3RhdHVzOiBpbnB1dC5zdGF0dXN9O1xuICAgIGF3YWl0IHR4LnN0ZXAoKCkgPT4gZGIucXVlcnkoYXFsYFVQU0VSVCB7X2tleTogJHtpbnB1dC5pZH19IElOU0VSVCAke3JlY29yZH0gVVBEQVRFICR7cmVjb3JkfSBJTiBjcm1DYW5vbmljYWxGaW5hbmNlQmluZGluZ3NgKSk7XG4gICAgY29uc3QgZWRnZSA9IHtfZnJvbTogYGN1c3RvbWVyT3JkZXJzLyR7c291cmNlLm9yZGVyLl9rZXl9YCwgX2tleTogaW5wdXQuaWQsXG4gICAgICBfdG86IGBtZXJjaGFudElzc3VlZEludm9pY2VzLyR7aW5wdXQuaW52b2ljZUlkfWB9O1xuICAgIGF3YWl0IHR4LnN0ZXAoKCkgPT4gZGIucXVlcnkoYXFsYFVQU0VSVCB7X2tleTogJHtpbnB1dC5pZH19IElOU0VSVCAke2VkZ2V9IFVQREFURSAke2VkZ2V9IElOIGhhc0NybUNhbm9uaWNhbEZpbmFuY2VgKSk7XG4gICAgYXdhaXQgdHguY29tbWl0KCk7XG4gICAgcmV0dXJuIHtpZDogaW5wdXQuaWQsIHJldmlzaW9uOiByZWNvcmQucmV2aXNpb24sIHN0YXR1czogaW5wdXQuc3RhdHVzfTtcbiAgfSBjYXRjaChlcnJvcikge1xuICAgIGF3YWl0IHR4LmFib3J0KCk7XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbn07XG5jb25zdCBhdXRob3JpemUgPSBhc3luYyAoY29udGV4dDogQXBpQ29udGV4dCwgaW5wdXQ6IENybUNhbm9uaWNhbFJlYWRJbnB1dCwgYXQ6IG51bWJlcikgPT4ge1xuICBpZighdmFsaWRJbnB1dChpbnB1dCkgfHwgT2JqZWN0LmtleXMoaW5wdXQpLnNvbWUoKGtleSkgPT5cbiAgICAhWydhcHBJZCcsICdjYW1wYWlnbklkJywgJ2ZpbmFuY2lhbCcsICdvcmdhbml6YXRpb25JZCcsICdwcm9qZWN0SWQnXS5pbmNsdWRlcyhrZXkpKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignaW52YWxpZF9jYW5vbmljYWxfcmVhZF9zY29wZScpO1xuICB9XG4gIGNvbnN0IGF1dGhvcml0eSA9IGF3YWl0IGF1dGhvcml6ZUNybUluc2lnaHRTb3VyY2UoY29udGV4dCwgaW5wdXQuYXBwSWQsIGF0KTtcbiAgY29uc3QgYm91bmRzID0gYXV0aG9yaXR5Lm91dGNvbWVCb3VuZHM7XG4gIGlmKCFhdXRob3JpdHkuc2NvcGVzLmluY2x1ZGVzKGlucHV0LmZpbmFuY2lhbCA/ICdjcm06Y2Fub25pY2FsX2ZpbmFuY2UnIDogJ2NybTpjYW5vbmljYWxfb3V0Y29tZXMnKVxuICAgIHx8ICFib3VuZHMgfHwgYm91bmRzLm9yZ2FuaXphdGlvbklkICE9PSBpbnB1dC5vcmdhbml6YXRpb25JZCB8fCBib3VuZHMucHJvamVjdElkICE9PSBpbnB1dC5wcm9qZWN0SWRcbiAgICB8fCAhYm91bmRzLmNhbXBhaWduSWRzLmluY2x1ZGVzKGlucHV0LmNhbXBhaWduSWQpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdjYW5vbmljYWxfcmVhZF9hY2Nlc3NfcmVxdWlyZWQnKTtcbiAgfVxuICByZXR1cm4gYXV0aG9yaXR5O1xufTtcbmNvbnN0IHJlYWRIaXN0b3J5ID0gYXN5bmMgKGRiOiBSZXR1cm5UeXBlPHR5cGVvZiB1c2VEYj4sIGlucHV0OiBDcm1DYW5vbmljYWxSZWFkSW5wdXQpID0+XG4gIGRiLmNvbGxlY3Rpb24oJ2hhc0NybUNhbXBhaWduT3V0Y29tZScpLmV4aXN0cygpLnRoZW4oKGV4aXN0cykgPT4gKGV4aXN0cyA/IGRiLnF1ZXJ5KGFxbGBcbiAgICBGT1IgYXNzb2NpYXRpb24gSU4gaGFzQ3JtQ2FtcGFpZ25PdXRjb21lIEZJTFRFUiBhc3NvY2lhdGlvbi5hcHBJZCA9PSAke2lucHV0LmFwcElkfVxuICAgICAgQU5EIGFzc29jaWF0aW9uLm9yZ2FuaXphdGlvbklkID09ICR7aW5wdXQub3JnYW5pemF0aW9uSWR9IEFORCBhc3NvY2lhdGlvbi5wcm9qZWN0SWQgPT0gJHtpbnB1dC5wcm9qZWN0SWR9XG4gICAgICBBTkQgYXNzb2NpYXRpb24uY2FtcGFpZ25JZCA9PSAke2lucHV0LmNhbXBhaWduSWR9IEFORCBhc3NvY2lhdGlvbi5raW5kID09ICdmb3JtX3N1Ym1pc3Npb24nXG4gICAgU09SVCBhc3NvY2lhdGlvbi5pZCBMSU1JVCA1MDFcbiAgICBMRVQgc291cmNlID0gRE9DVU1FTlQoJ2NybUZvcm1TdWJtaXNzaW9ucycsIGFzc29jaWF0aW9uLnNvdXJjZUlkKVxuICAgIExFVCB1c2VyID0gRE9DVU1FTlQoJ3VzZXJzJywgYXNzb2NpYXRpb24udXNlcklkKVxuICAgIFJFVFVSTiB7YXNzb2NpYXRpb24sIHNvdXJjZTogc291cmNlID8ge3JldmlzaW9uOiBzb3VyY2UuX3JldiwgaGlzdG9yeTogc291cmNlLmNybU91dGNvbWVFdmVudHMgfHwgW10sXG4gICAgICB0cnVuY2F0ZWQ6IHNvdXJjZS5jcm1PdXRjb21lSGlzdG9yeVRydW5jYXRlZCwgZGVsZXRlZDogc291cmNlLmRlbGV0ZWR9IDoge3JldmlzaW9uOiAnJywgaGlzdG9yeTogW10sXG4gICAgICAgIGRlbGV0ZWQ6IHRydWV9LFxuICAgICAgc3VwcHJlc3NlZDogdXNlciA9PSBudWxsIE9SIHVzZXIuZGVsZXRlZCA9PSB0cnVlIE9SIHVzZXIuY3JtQW5hbHl0aWNzU3VwcHJlc3NlZCA9PSB0cnVlfVxuICBgKS50aGVuKChjdXJzb3IpID0+IGN1cnNvci5hbGwoKSkgOiBbXSkpO1xuLyoqIENvbXBsZXRlIHNjb3BlZCBvd25lciBvYnNlcnZhdGlvbnM7IGZpbmFuY2lhbCByb3dzIHJlbWFpbiBzZXBhcmF0ZSB1bnRpbCBuYW1lc3BhY2UgcHJvb2YuICovXG5leHBvcnQgY29uc3QgZ2V0Q3JtQ2Fub25pY2FsT3V0Y29tZXMgPSBhc3luYyAoXG4gIGNvbnRleHQ6IEFwaUNvbnRleHQsIGlucHV0OiBDcm1DYW5vbmljYWxSZWFkSW5wdXQsIG5vdyA9IERhdGUubm93XG4pXG46IFByb21pc2U8Q3JtQ2Fub25pY2FsU25hcHNob3Q+ID0+IHtcbiAgY29uc3QgYXQgPSBub3coKTtcbiAgY29uc3QgYXV0aG9yaXR5ID0gYXdhaXQgYXV0aG9yaXplKGNvbnRleHQsIGlucHV0LCBhdCk7XG4gIGNvbnN0IGRiID0gdXNlRGIoYXV0aG9yaXR5LmNvbnRleHQuZGF0YWJhc2VOYW1lKTtcbiAgY29uc3QgaGVsZDogUmVjb3JkPHN0cmluZywgbnVtYmVyPiA9IHt9O1xuICBjb25zdCBhZGRIb2xkID0gKHJlYXNvbjogc3RyaW5nKSA9PiB7XG4gICAgaGVsZFtyZWFzb25dID0gKGhlbGRbcmVhc29uXSB8fCAwKSArIDE7XG4gIH07XG4gIGNvbnN0IHJvd3MgPSBpbnB1dC5maW5hbmNpYWwgPyBbXSA6IGF3YWl0IHJlYWRIaXN0b3J5KGRiLCBpbnB1dCkgYXMgQ3JtQ2Fub25pY2FsTmF0aXZlSGlzdG9yeVtdO1xuICBsZXQgY29tcGxldGUgPSByb3dzLmxlbmd0aCA8PSA1MDA7XG4gIGxldCBldmVudHM6IENybUNhbm9uaWNhbEV2ZW50W10gPSBbXTtcbiAgZm9yKGNvbnN0IHJvdyBvZiByb3dzLnNsaWNlKDAsIDUwMCkpIHtcbiAgICBjb25zdCByZXN1bHQgPSBjcm1DYW5vbmljYWxIaXN0b3J5RXZlbnRzKHJvdywgYXQpO1xuICAgIGlmKHJlc3VsdC5oZWxkKSB7XG4gICAgICBhZGRIb2xkKHJlc3VsdC5oZWxkKTtcbiAgICB9XG4gICAgY29tcGxldGUgJiY9ICFyZXN1bHQuaW5jb21wbGV0ZTtcbiAgICBldmVudHMucHVzaCguLi5yZXN1bHQuZXZlbnRzKTtcbiAgfVxuICBjb25zdCBmaW5hbmNpYWxPYnNlcnZhdGlvbnM6IENybUNhbm9uaWNhbEZpbmFuY2lhbE9ic2VydmF0aW9uW10gPSBbXTtcbiAgY29uc3QgYmluZGluZ3MgPSBpbnB1dC5maW5hbmNpYWwgJiYgYXdhaXQgZGIuY29sbGVjdGlvbignY3JtQ2Fub25pY2FsRmluYW5jZUJpbmRpbmdzJykuZXhpc3RzKClcbiAgICA/IGF3YWl0IGRiLnF1ZXJ5KGFxbGBGT1IgYmluZGluZyBJTiBjcm1DYW5vbmljYWxGaW5hbmNlQmluZGluZ3MgRklMVEVSIGJpbmRpbmcuYXBwSWQgPT0gJHtpbnB1dC5hcHBJZH1cbiAgICAgIEFORCBiaW5kaW5nLm9yZ2FuaXphdGlvbklkID09ICR7aW5wdXQub3JnYW5pemF0aW9uSWR9IEFORCBiaW5kaW5nLnByb2plY3RJZCA9PSAke2lucHV0LnByb2plY3RJZH1cbiAgICAgIEFORCBiaW5kaW5nLmNhbXBhaWduSWQgPT0gJHtpbnB1dC5jYW1wYWlnbklkfVxuICAgICAgQU5EIGJpbmRpbmcuaXNzdWVySWQgSU4gJHthdXRob3JpdHkub3V0Y29tZUJvdW5kcz8uaXNzdWVySWRzIHx8IFtdfSBTT1JUIGJpbmRpbmcuaWQgTElNSVQgNTEgUkVUVVJOIGJpbmRpbmdgKVxuICAgICAgLnRoZW4oKGN1cnNvcikgPT4gY3Vyc29yLmFsbCgpKSA6IFtdO1xuICBjb21wbGV0ZSAmJj0gYmluZGluZ3MubGVuZ3RoIDw9IDUwO1xuICBmb3IoY29uc3QgYmluZGluZyBvZiBiaW5kaW5ncy5zbGljZSgwLCA1MCkpIHtcbiAgICBpZihiaW5kaW5nLnN0YXR1cyAhPT0gJ2FjdGl2ZScpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBpZighYXV0aG9yaXR5Lm91dGNvbWVCb3VuZHM/Lmlzc3Vlcklkcz8uaW5jbHVkZXMoYmluZGluZy5pc3N1ZXJJZCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignY2Fub25pY2FsX2lzc3Vlcl9hY2Nlc3NfcmVxdWlyZWQnKTtcbiAgICB9XG4gICAgY29uc3QgdmVyaWZpY2F0aW9uID0gYXdhaXQgcmVhZE5hdGl2ZUNybUZpbmFuY2lhbEV2aWRlbmNlKGRiLCBpbnB1dCwgYmluZGluZywgYXQpO1xuICAgIGlmKCF2ZXJpZmljYXRpb24uZXZpZGVuY2UpIHtcbiAgICAgIGFkZEhvbGQodmVyaWZpY2F0aW9uLmhlbGQhKTtcbiAgICAgIGlmKFsncHJvdmlkZXJfdmVyaWZpY2F0aW9uX3VuYXZhaWxhYmxlJywgJ3Byb3ZpZGVyX3JlZnVuZF9ldmlkZW5jZV9pbmNvbXBsZXRlJ10uaW5jbHVkZXModmVyaWZpY2F0aW9uLmhlbGQhKSkge1xuICAgICAgICBjb21wbGV0ZSA9IGZhbHNlO1xuICAgICAgfVxuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIGNvbnN0IHtzb3VyY2V9ID0gdmVyaWZpY2F0aW9uO1xuICAgIGZpbmFuY2lhbE9ic2VydmF0aW9ucy5wdXNoKHtldmlkZW5jZTogdmVyaWZpY2F0aW9uLmV2aWRlbmNlLCBpZDogY3JtQ2Fub25pY2FsRGlnZXN0KFtpbnB1dC5hcHBJZCwgYmluZGluZy5pZF0pLFxuICAgICAgaXNzdWVySWQ6IGJpbmRpbmcuaXNzdWVySWQsIG5hdGl2ZU9yZGVySWQ6IGNybUNhbm9uaWNhbERpZ2VzdChbaW5wdXQuYXBwSWQsIHNvdXJjZS5vcmRlci5fa2V5XSksXG4gICAgICByZXZpc2lvbjogdmVyaWZpY2F0aW9uLnJldmlzaW9uISxcbiAgICAgIHNob3BpZnlDdXN0b21lcklkOiBzb3VyY2UudXNlci5zaG9waWZ5Q3VzdG9tZXJJZCxcbiAgICAgIHNob3BpZnlPcmRlcklkOiBzb3VyY2Uub3JkZXIuc2hvcGlmeU9yZGVySWQsIHN0YXR1czogJ3ZlcmlmaWVkX3NldHRsZW1lbnRfbmFtZXNwYWNlX3VucHJvdmVuJ30pO1xuICAgIGFkZEhvbGQoJ3ZlcmlmaWVkX2ZpbmFuY2lhbF9uYW1lc3BhY2VfdW5wcm92ZW4nKTtcbiAgfVxuICBjb25zdCBjdXJyZW50ID0gYXdhaXQgYXV0aG9yaXplKGNvbnRleHQsIGlucHV0LCBub3coKSk7XG4gIGlmKGN1cnJlbnQuY29udGV4dC5kYXRhYmFzZU5hbWUgIT09IGF1dGhvcml0eS5jb250ZXh0LmRhdGFiYXNlTmFtZSB8fCBjdXJyZW50LmV4cGlyZXNBdCAhPT0gYXV0aG9yaXR5LmV4cGlyZXNBdFxuICAgIHx8IGNybUNhbm9uaWNhbERpZ2VzdChjdXJyZW50Lm91dGNvbWVCb3VuZHMpICE9PSBjcm1DYW5vbmljYWxEaWdlc3QoYXV0aG9yaXR5Lm91dGNvbWVCb3VuZHMpXG4gICAgfHwgY3JtQ2Fub25pY2FsRGlnZXN0KGN1cnJlbnQuc2NvcGVzKSAhPT0gY3JtQ2Fub25pY2FsRGlnZXN0KGF1dGhvcml0eS5zY29wZXMpXG4gICAgfHwgY3JtQ2Fub25pY2FsRGlnZXN0KHJvd3MpICE9PSBjcm1DYW5vbmljYWxEaWdlc3QoaW5wdXQuZmluYW5jaWFsID8gW10gOiBhd2FpdCByZWFkSGlzdG9yeShkYiwgaW5wdXQpKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignY2Fub25pY2FsX3NvdXJjZV9vcl9ncmFudF9jaGFuZ2VkJyk7XG4gIH1cbiAgaWYoZXZlbnRzLmxlbmd0aCA+IDUwMCkge1xuICAgIGNvbXBsZXRlID0gZmFsc2U7XG4gICAgZXZlbnRzID0gZXZlbnRzLnNsaWNlKDAsIDUwMCk7XG4gICAgYWRkSG9sZCgnYm91bmRlZF9oaXN0b3J5X3RydW5jYXRlZCcpO1xuICB9XG4gIGNvbnN0IHNuYXBzaG90OiBDcm1DYW5vbmljYWxTbmFwc2hvdCA9IHthcHBJZDogaW5wdXQuYXBwSWQsIGNhbXBhaWduSWQ6IGlucHV0LmNhbXBhaWduSWQsIGNvbXBsZXRlLFxuICAgIGV2ZW50cywgZmluYW5jaWFsT2JzZXJ2YXRpb25zLCBnZW5lcmF0ZWRBdDogbmV3IERhdGUoYXQpLnRvSVNPU3RyaW5nKCksIGhlbGQsXG4gICAgaXNzdWVySWRzOiBpbnB1dC5maW5hbmNpYWwgPyBbLi4uYXV0aG9yaXR5Lm91dGNvbWVCb3VuZHMhLmlzc3VlcklkcyFdLnNvcnQoKSA6IHVuZGVmaW5lZCxcbiAgICBvcmdhbml6YXRpb25JZDogaW5wdXQub3JnYW5pemF0aW9uSWQsXG4gICAgcHJvamVjdElkOiBpbnB1dC5wcm9qZWN0SWQsIHJldmlzaW9uOiAxLCBzY2hlbWFWZXJzaW9uOiAnY3JtX2Nhbm9uaWNhbF9vdXRjb21lc192MScsXG4gICAgc291cmNlS2luZDogaW5wdXQuZmluYW5jaWFsID8gJ2ZpbmFuY2lhbCcgOiAnbGlmZWN5Y2xlJyxcbiAgICB2YWxpZFVudGlsOiBuZXcgRGF0ZShNYXRoLm1pbihhdCArIDkwMDAwMCwgYXV0aG9yaXR5LmV4cGlyZXNBdCB8fCBJbmZpbml0eSkpLnRvSVNPU3RyaW5nKCl9O1xuICAvLyBBIHN0YWJsZSBtb25vdG9uaWMgc291cmNlIHJldmlzaW9uIGlzIGNvbW1pdHRlZCB3aXRoIGNvbXBsZXRlIHNuYXBzaG90IHJlcGxhY2VtZW50OyBpbmNvbXBsZXRlIHJlYWRzIG5ldmVyIGVyYXNlXG4gIC8vIGhpc3RvcnkuXG4gIGF3YWl0IGVuc3VyZU93bmVyRXhlY3V0aW9uQ29sbGVjdGlvbihkYiwgJ2NybUNhbm9uaWNhbFNuYXBzaG90cycpO1xuICBhd2FpdCBkYi5jb2xsZWN0aW9uKCdjcm1DYW5vbmljYWxTbmFwc2hvdHMnKS5lbnN1cmVJbmRleCh7ZXhwaXJlQWZ0ZXI6IDAsIGZpZWxkczogWydleHBpcmVzQXQnXSwgdHlwZTogJ3R0bCd9KTtcbiAgY29uc3QgaWQgPSBjcm1DYW5vbmljYWxEaWdlc3QoW2lucHV0LmFwcElkLCBpbnB1dC5vcmdhbml6YXRpb25JZCwgaW5wdXQucHJvamVjdElkLCBpbnB1dC5jYW1wYWlnbklkLFxuICAgIEJvb2xlYW4oaW5wdXQuZmluYW5jaWFsKSwgaW5wdXQuZmluYW5jaWFsID8gWy4uLmF1dGhvcml0eS5vdXRjb21lQm91bmRzIS5pc3N1ZXJJZHMhXS5zb3J0KCkgOiBbXV0pO1xuICBjb25zdCB0eCA9IGF3YWl0IGRiLmJlZ2luVHJhbnNhY3Rpb24oe2V4Y2x1c2l2ZTogWydjcm1DYW5vbmljYWxTbmFwc2hvdHMnXX0sIHtsb2NrVGltZW91dDogMTB9KTtcbiAgdHJ5IHtcbiAgICBjb25zdCBwcmlvciA9IGF3YWl0IHR4LnN0ZXAoKCkgPT4gZGIucXVlcnkoYXFsYFJFVFVSTiBET0NVTUVOVCgnY3JtQ2Fub25pY2FsU25hcHNob3RzJywgJHtpZH0pYCkpXG4gICAgICAudGhlbigoY3Vyc29yKSA9PiBjdXJzb3IubmV4dCgpKTtcbiAgICBjb25zdCBkaWdlc3QgPSBjcm1DYW5vbmljYWxEaWdlc3Qoe2V2ZW50cywgZmluYW5jaWFsT2JzZXJ2YXRpb25zLCBoZWxkfSk7XG4gICAgc25hcHNob3QucmV2aXNpb24gPSBwcmlvciA/IHByaW9yLnJldmlzaW9uICsgTnVtYmVyKHByaW9yLmRpZ2VzdCAhPT0gZGlnZXN0KSA6IDE7XG4gICAgaWYoY29tcGxldGUpIHtcbiAgICAgIGNvbnN0IHByZXZpb3VzOiBDcm1DYW5vbmljYWxFdmVudFtdID0gcHJpb3I/LmV2ZW50cyB8fCBbXTtcbiAgICAgIHNuYXBzaG90LmV2ZW50cyA9IGV2ZW50cy5tYXAoKGV2ZW50KSA9PiB7XG4gICAgICAgIGNvbnN0IG9sZCA9IHByZXZpb3VzLmZpbmQoKGl0ZW0pID0+IGl0ZW0uZXZlbnRJZCA9PT0gZXZlbnQuZXZlbnRJZCk7XG4gICAgICAgIHJldHVybiB7Li4uZXZlbnQsIHJldmlzaW9uOiBvbGQgPyBNYXRoLm1heChldmVudC5yZXZpc2lvbixcbiAgICAgICAgICBvbGQucmV2aXNpb24gKyBOdW1iZXIoQm9vbGVhbihvbGQuZGVsZXRlZCkpKSA6IGV2ZW50LnJldmlzaW9ufTtcbiAgICAgIH0pO1xuICAgICAgc25hcHNob3QuZXZlbnRzLnB1c2goLi4ucHJldmlvdXMuZmlsdGVyKChldmVudCkgPT4gIWV2ZW50cy5zb21lKChpdGVtKSA9PiBpdGVtLmV2ZW50SWQgPT09IGV2ZW50LmV2ZW50SWQpKVxuICAgICAgICAubWFwKChldmVudCkgPT4gKHsuLi5ldmVudCwgZGVsZXRlZDogdHJ1ZSBhcyBjb25zdCwgcmV2aXNpb246IGV2ZW50LnJldmlzaW9uICsgTnVtYmVyKCFldmVudC5kZWxldGVkKX0pKSk7XG4gICAgICBpZihzbmFwc2hvdC5ldmVudHMubGVuZ3RoID4gMTAwMCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ2Nhbm9uaWNhbF90b21ic3RvbmVfcmV0ZW50aW9uX2JvdW5kJyk7XG4gICAgICB9XG4gICAgICBjb25zdCBwZXJzaXN0ZWQgPSB7X2tleTogaWQsIGRpZ2VzdCwgZXZlbnRzOiBzbmFwc2hvdC5ldmVudHMsXG4gICAgICAgIGV4cGlyZXNBdDogTWF0aC5mbG9vcihhdCAvIDEwMDApICsgKDMwICogODY0MDApLCByZXZpc2lvbjogc25hcHNob3QucmV2aXNpb259O1xuICAgICAgYXdhaXQgdHguc3RlcCgoKSA9PiBkYi5xdWVyeShhcWxgVVBTRVJUIHtfa2V5OiAke2lkfX0gSU5TRVJUICR7cGVyc2lzdGVkfVxuICAgICAgICBVUERBVEUgJHtwZXJzaXN0ZWR9IElOIGNybUNhbm9uaWNhbFNuYXBzaG90c2ApKTtcbiAgICB9XG4gICAgYXdhaXQgdHguY29tbWl0KCk7XG4gIH0gY2F0Y2goZXJyb3IpIHtcbiAgICBhd2FpdCB0eC5hYm9ydCgpO1xuICAgIHRocm93IGVycm9yO1xuICB9XG4gIHJldHVybiBzbmFwc2hvdDtcbn07XG5leHBvcnQgaW50ZXJmYWNlIENybUNhbm9uaWNhbENvcnJlY3Rpb25JbnB1dCBleHRlbmRzIENybUNhbm9uaWNhbFJlYWRJbnB1dCB7XG4gIGFzc29jaWF0aW9uSWQ6IHN0cmluZztcbiAgZGVsZXRlZD86IGJvb2xlYW47XG4gIGV2ZW50SWQ6IHN0cmluZztcbiAgZXZpZGVuY2VSZWY6IHN0cmluZztcbiAgZXhwZWN0ZWRBc3NvY2lhdGlvblJldmlzaW9uOiBudW1iZXI7XG4gIGV4cGVjdGVkRXZlbnRSZXZpc2lvbjogbnVtYmVyO1xuICBleHBlY3RlZFNvdXJjZVJldmlzaW9uOiBzdHJpbmc7XG4gIG9yZGVySWQ/OiBzdHJpbmc7XG4gIHN0YWdlPzogJ25ldycgfCAncXVhbGlmaWVkJyB8ICd3b24nIHwgJ2xvc3QnIHwgJ2Rpc3F1YWxpZmllZCc7XG59XG4vKiogQ29ycmVjdCBhIHJlYWwgb3duZXIgb2JzZXJ2YXRpb24gYnkgQ0FTOyBuZXZlciBiYWNrZGF0ZS9jcmVhdGUgYW4gaW52ZW50ZWQgZXZlbnQgb3IgbW9kaWZ5IG1vbmV5LiAqL1xuZXhwb3J0IGNvbnN0IGNvcnJlY3RDcm1DYW5vbmljYWxPYnNlcnZhdGlvbiA9IGFzeW5jIChcbiAgY29udGV4dDogQXBpQ29udGV4dCwgaW5wdXQ6IENybUNhbm9uaWNhbENvcnJlY3Rpb25JbnB1dCwgbm93ID0gRGF0ZS5ub3dcbikgPT4ge1xuICByZXF1aXJlQ3JtQWRtaW5pc3RyYXRvcihjb250ZXh0KTtcbiAgaWYoIXZhbGlkSW5wdXQoaW5wdXQpIHx8ICFrZXkoaW5wdXQuYXNzb2NpYXRpb25JZCkgfHwgIWtleShpbnB1dC5leHBlY3RlZFNvdXJjZVJldmlzaW9uKVxuICAgIHx8ICEvXlthLWYwLTldezY0fSQvLnRlc3QoaW5wdXQuZXZlbnRJZCkgfHwgIWlucHV0LmV2aWRlbmNlUmVmPy50cmltKCkgfHwgaW5wdXQuZXZpZGVuY2VSZWYubGVuZ3RoID4gMTAwMFxuICAgIHx8ICFOdW1iZXIuaXNTYWZlSW50ZWdlcihpbnB1dC5leHBlY3RlZEFzc29jaWF0aW9uUmV2aXNpb24pIHx8IGlucHV0LmV4cGVjdGVkQXNzb2NpYXRpb25SZXZpc2lvbiA8IDFcbiAgICB8fCAhTnVtYmVyLmlzU2FmZUludGVnZXIoaW5wdXQuZXhwZWN0ZWRFdmVudFJldmlzaW9uKSB8fCBpbnB1dC5leHBlY3RlZEV2ZW50UmV2aXNpb24gPCAxXG4gICAgfHwgKGlucHV0LmRlbGV0ZWQgIT09IHRydWUgJiYgIVsnbmV3JywgJ3F1YWxpZmllZCcsICd3b24nLCAnbG9zdCcsICdkaXNxdWFsaWZpZWQnXS5pbmNsdWRlcyhpbnB1dC5zdGFnZSB8fCAnJykpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdpbnZhbGlkX2Nhbm9uaWNhbF9vYnNlcnZhdGlvbl9jb3JyZWN0aW9uJyk7XG4gIH1cbiAgY29uc3Qgc2NvcGVkID0gYXdhaXQgc2NvcGVDb250ZXh0VG9BcHAoY29udGV4dCwgaW5wdXQuYXBwSWQpO1xuICBjb25zdCBkYiA9IHVzZURiKHNjb3BlZC5kYXRhYmFzZU5hbWUpO1xuICBjb25zdCB0eCA9IGF3YWl0IGRiLmJlZ2luVHJhbnNhY3Rpb24oe2V4Y2x1c2l2ZTogWydoYXNDcm1DYW1wYWlnbk91dGNvbWUnLCAnY3JtRm9ybVN1Ym1pc3Npb25zJ10sXG4gICAgcmVhZDogWydjdXN0b21lck9yZGVycyddfSwge2xvY2tUaW1lb3V0OiAxMH0pO1xuICBjb25zdCBxdWVyeSA9ICh2YWx1ZTogUmV0dXJuVHlwZTx0eXBlb2YgYXFsPikgPT4gdHguc3RlcCgoKSA9PiBkYi5xdWVyeSh2YWx1ZSkpO1xuICB0cnkge1xuICAgIGNvbnN0IGFzc29jaWF0aW9uID0gYXdhaXQgcXVlcnkoYXFsYFJFVFVSTiBET0NVTUVOVCgnaGFzQ3JtQ2FtcGFpZ25PdXRjb21lJywgJHtpbnB1dC5hc3NvY2lhdGlvbklkfSlgKVxuICAgICAgLnRoZW4oKGN1cnNvcikgPT4gY3Vyc29yLm5leHQoKSkgYXMgQ3JtQ2Fub25pY2FsTmF0aXZlSGlzdG9yeVsnYXNzb2NpYXRpb24nXTtcbiAgICBpZighYXNzb2NpYXRpb24gfHwgYXNzb2NpYXRpb24uYXBwSWQgIT09IGlucHV0LmFwcElkIHx8IGFzc29jaWF0aW9uLm9yZ2FuaXphdGlvbklkICE9PSBpbnB1dC5vcmdhbml6YXRpb25JZFxuICAgICAgfHwgYXNzb2NpYXRpb24ucHJvamVjdElkICE9PSBpbnB1dC5wcm9qZWN0SWQgfHwgYXNzb2NpYXRpb24uY2FtcGFpZ25JZCAhPT0gaW5wdXQuY2FtcGFpZ25JZFxuICAgICAgfHwgYXNzb2NpYXRpb24ua2luZCAhPT0gJ2Zvcm1fc3VibWlzc2lvbicgfHwgYXNzb2NpYXRpb24ucmV2aXNpb24gIT09IGlucHV0LmV4cGVjdGVkQXNzb2NpYXRpb25SZXZpc2lvbikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdjYW5vbmljYWxfY29ycmVjdGlvbl9hc3NvY2lhdGlvbl9jb25mbGljdCcpO1xuICAgIH1cbiAgICBjb25zdCBzb3VyY2UgPSBhd2FpdCBxdWVyeShhcWxgUkVUVVJOIERPQ1VNRU5UKCdjcm1Gb3JtU3VibWlzc2lvbnMnLCAke2Fzc29jaWF0aW9uLnNvdXJjZUlkfSlgKVxuICAgICAgLnRoZW4oKGN1cnNvcikgPT4gY3Vyc29yLm5leHQoKSkgYXMge19rZXk6IHN0cmluZzsgX3Jldjogc3RyaW5nOyB1c2VySWQ6IHN0cmluZztcbiAgICAgIGNybU91dGNvbWVFdmVudHM6IHthdDogbnVtYmVyOyBkZWxldGVkPzogYm9vbGVhbjsgaWQ6IHN0cmluZzsgcmV2aXNpb246IG51bWJlcjsgc3RhZ2U6IHN0cmluZ31bXTt9O1xuICAgIGNvbnN0IGluZGV4ID0gc291cmNlPy5jcm1PdXRjb21lRXZlbnRzPy5maW5kSW5kZXgoKGV2ZW50KSA9PiBldmVudC5pZCA9PT0gaW5wdXQuZXZlbnRJZCkgPz8gLTE7XG4gICAgY29uc3QgZXZlbnQgPSBzb3VyY2U/LmNybU91dGNvbWVFdmVudHM/LltpbmRleF07XG4gICAgaWYoIXNvdXJjZSB8fCBzb3VyY2UuX3JldiAhPT0gaW5wdXQuZXhwZWN0ZWRTb3VyY2VSZXZpc2lvbiB8fCBzb3VyY2UuX3JldiAhPT0gYXNzb2NpYXRpb24uc291cmNlUmV2aXNpb25cbiAgICAgIHx8ICFldmVudCB8fCBldmVudC5yZXZpc2lvbiAhPT0gaW5wdXQuZXhwZWN0ZWRFdmVudFJldmlzaW9uIHx8IGV2ZW50LmRlbGV0ZWRcbiAgICAgIHx8ICFOdW1iZXIuaXNTYWZlSW50ZWdlcihldmVudC5hdCkgfHwgZXZlbnQuYXQgPiBub3coKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdjYW5vbmljYWxfY29ycmVjdGlvbl9zb3VyY2VfY29uZmxpY3QnKTtcbiAgICB9XG4gICAgaWYoaW5wdXQuc3RhZ2UgPT09ICd3b24nICYmIGlucHV0LmRlbGV0ZWQgIT09IHRydWUpIHtcbiAgICAgIGlmKCFrZXkoaW5wdXQub3JkZXJJZCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdjYW5vbmljYWxfd29uX29yZGVyX3JlcXVpcmVkJyk7XG4gICAgICB9XG4gICAgICBjb25zdCBvcmRlciA9IGF3YWl0IHF1ZXJ5KGFxbGBSRVRVUk4gRE9DVU1FTlQoJ2N1c3RvbWVyT3JkZXJzJywgJHtpbnB1dC5vcmRlcklkfSlgKS50aGVuKChjdXJzb3IpID0+IGN1cnNvci5uZXh0KCkpIGFzIHt1c2VySWQ6IHN0cmluZzsgc2hvcGlmeU9yZGVySWQ6IHN0cmluZzsgcHJvZHVjdElkcz86IHN0cmluZ1tdfTtcbiAgICAgIGlmKCFvcmRlciB8fCBvcmRlci51c2VySWQgIT09IHNvdXJjZS51c2VySWQgfHwgIW9yZGVyLnNob3BpZnlPcmRlcklkXG4gICAgICAgIHx8IChhc3NvY2lhdGlvbi5wcm9kdWN0SWQgJiYgIW9yZGVyLnByb2R1Y3RJZHM/LmluY2x1ZGVzKGFzc29jaWF0aW9uLnByb2R1Y3RJZCkpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignY2Fub25pY2FsX3dvbl9vcmRlcl9taXNtYXRjaCcpO1xuICAgICAgfVxuICAgIH1cbiAgICBjb25zdCBoaXN0b3J5ID0gc291cmNlLmNybU91dGNvbWVFdmVudHMubWFwKCh2YWx1ZSwgcG9zaXRpb24pID0+IChwb3NpdGlvbiA9PT0gaW5kZXggPyB7Li4udmFsdWUsXG4gICAgICBjb3JyZWN0ZWRBdDogbm93KCksIGNvcnJlY3Rpb25FdmlkZW5jZVJlZjogaW5wdXQuZXZpZGVuY2VSZWYsIGRlbGV0ZWQ6IGlucHV0LmRlbGV0ZWQgPT09IHRydWUgfHwgdW5kZWZpbmVkLFxuICAgICAgcmV2aXNpb246IHZhbHVlLnJldmlzaW9uICsgMSwgc3RhZ2U6IGlucHV0LmRlbGV0ZWQgPT09IHRydWUgPyB2YWx1ZS5zdGFnZSA6IGlucHV0LnN0YWdlfSA6IHZhbHVlKSk7XG4gICAgY29uc3QgdXBkYXRlcyA9IHtjcm1PdXRjb21lRXZlbnRzOiBoaXN0b3J5LCAuLi4oaW5kZXggPT09IGhpc3RvcnkubGVuZ3RoIC0gMSAmJiAhaW5wdXQuZGVsZXRlZFxuICAgICAgPyB7Y3JtTGVhZFN0YWdlOiBpbnB1dC5zdGFnZX0gOiB7fSl9O1xuICAgIGNvbnN0IHNhdmVkID0gYXdhaXQgcXVlcnkoYXFsYFVQREFURSAke3NvdXJjZS5fa2V5fSBXSVRIICR7dXBkYXRlc30gSU4gY3JtRm9ybVN1Ym1pc3Npb25zIFJFVFVSTiBORVdgKVxuICAgICAgLnRoZW4oKGN1cnNvcikgPT4gY3Vyc29yLm5leHQoKSkgYXMge19yZXY6IHN0cmluZ307XG4gICAgY29uc3QgdXBkYXRlID0ge3JldmlzaW9uOiBhc3NvY2lhdGlvbi5yZXZpc2lvbiArIDEsIHNvdXJjZVJldmlzaW9uOiBzYXZlZC5fcmV2LFxuICAgICAgLi4uKGluZGV4ID09PSBoaXN0b3J5Lmxlbmd0aCAtIDEgJiYgIWlucHV0LmRlbGV0ZWQgPyB7b3JkZXJJZDogaW5wdXQuc3RhZ2UgPT09ICd3b24nID8gaW5wdXQub3JkZXJJZCA6IG51bGwsXG4gICAgICAgIHN0YWdlOiBpbnB1dC5zdGFnZX0gOiB7fSl9O1xuICAgIGF3YWl0IHF1ZXJ5KGFxbGBVUERBVEUgJHtpbnB1dC5hc3NvY2lhdGlvbklkfSBXSVRIICR7dXBkYXRlfSBJTiBoYXNDcm1DYW1wYWlnbk91dGNvbWVgKTtcbiAgICBhd2FpdCB0eC5jb21taXQoKTtcbiAgICByZXR1cm4ge2Fzc29jaWF0aW9uUmV2aXNpb246IHVwZGF0ZS5yZXZpc2lvbiwgZXZlbnRJZDogaW5wdXQuZXZlbnRJZCwgZXZlbnRSZXZpc2lvbjogZXZlbnQucmV2aXNpb24gKyAxLFxuICAgICAgc291cmNlUmV2aXNpb246IHNhdmVkLl9yZXZ9O1xuICB9IGNhdGNoKGVycm9yKSB7XG4gICAgYXdhaXQgdHguYWJvcnQoKTtcbiAgICB0aHJvdyBlcnJvcjtcbiAgfVxufTtcbiJdLCJuYW1lcyI6WyJhcWwiLCJzY29wZUNvbnRleHRUb0FwcCIsInVzZURiIiwicmVxdWlyZUNybUFkbWluaXN0cmF0b3IiLCJjcm1DYW5vbmljYWxEaWdlc3QiLCJjcm1DYW5vbmljYWxIaXN0b3J5RXZlbnRzIiwiZW5zdXJlT3duZXJFeGVjdXRpb25Db2xsZWN0aW9uIiwibmF0aXZlRmluYW5jZVNvdXJjZSIsInJlYWROYXRpdmVDcm1GaW5hbmNpYWxFdmlkZW5jZSIsInZhbGlkYXRlTmF0aXZlRmluYW5jZSIsImF1dGhvcml6ZUNybUluc2lnaHRTb3VyY2UiLCJjcmVhdGVDcm1JbnNpZ2h0c1JlYWRLZXkiLCJrZXkiLCJ2YWx1ZSIsInRlc3QiLCJ2YWxpZElucHV0IiwiaW5wdXQiLCJCb29sZWFuIiwiYXBwSWQiLCJvcmdhbml6YXRpb25JZCIsInByb2plY3RJZCIsImNhbXBhaWduSWQiLCJldmVyeSIsImZpbmFuY2lhbCIsInVuZGVmaW5lZCIsImNyZWF0ZUNybUNhbm9uaWNhbFJlYWRLZXkiLCJjb250ZXh0IiwiYm91bmRzIiwiZXhwaXJlc0F0Iiwibm93IiwiRGF0ZSIsInNjb3BlZCIsImRiIiwiZGF0YWJhc2VOYW1lIiwicXVlcnkiLCJ0aGVuIiwiY3Vyc29yIiwibmV4dCIsIkVycm9yIiwiaXNzdWVySWRzIiwibGVuZ3RoIiwiaXNzdWVySWQiLCJpc3N1ZXIiLCJyZWdpc3RlckNybUNhbm9uaWNhbEZpbmFuY2VCaW5kaW5nIiwicmV2aXNpb25zIiwiZXhwZWN0ZWRTb3VyY2VSZXZpc2lvbnMiLCJpZCIsImFzc29jaWF0aW9uSWQiLCJpbnZvaWNlSWQiLCJwYXltZW50RXZpZGVuY2VJZCIsIm5hdGl2ZVBheW1lbnRJZCIsImV2aWRlbmNlUmVmIiwidHJpbSIsIk51bWJlciIsImlzU2FmZUludGVnZXIiLCJleHBlY3RlZFJldmlzaW9uIiwiaW5jbHVkZXMiLCJzdGF0dXMiLCJPYmplY3QiLCJrZXlzIiwic29ydCIsImpvaW4iLCJhc3NvY2lhdGlvbiIsImludm9pY2UiLCJuYXRpdmVQYXltZW50Iiwib3JkZXIiLCJwYXltZW50RXZpZGVuY2UiLCJ1c2VyIiwidHgiLCJiZWdpblRyYW5zYWN0aW9uIiwiZXhjbHVzaXZlIiwicmVhZCIsImxvY2tUaW1lb3V0Iiwic291cmNlIiwic3RlcCIsImVudHJpZXMiLCJzb21lIiwiZmllbGQiLCJyZXZpc2lvbiIsIl9yZXYiLCJwcmlvciIsImlkZW50aXR5IiwicmVjb3JkIiwiX2tleSIsIm1vZGlmaWVkQXQiLCJlZGdlIiwiX2Zyb20iLCJfdG8iLCJjb21taXQiLCJlcnJvciIsImFib3J0IiwiYXV0aG9yaXplIiwiYXQiLCJhdXRob3JpdHkiLCJvdXRjb21lQm91bmRzIiwic2NvcGVzIiwiY2FtcGFpZ25JZHMiLCJyZWFkSGlzdG9yeSIsImNvbGxlY3Rpb24iLCJleGlzdHMiLCJhbGwiLCJnZXRDcm1DYW5vbmljYWxPdXRjb21lcyIsImhlbGQiLCJhZGRIb2xkIiwicmVhc29uIiwicm93cyIsImNvbXBsZXRlIiwiZXZlbnRzIiwicm93Iiwic2xpY2UiLCJyZXN1bHQiLCJpbmNvbXBsZXRlIiwicHVzaCIsImZpbmFuY2lhbE9ic2VydmF0aW9ucyIsImJpbmRpbmdzIiwiYmluZGluZyIsInZlcmlmaWNhdGlvbiIsImV2aWRlbmNlIiwibmF0aXZlT3JkZXJJZCIsInNob3BpZnlDdXN0b21lcklkIiwic2hvcGlmeU9yZGVySWQiLCJjdXJyZW50Iiwic25hcHNob3QiLCJnZW5lcmF0ZWRBdCIsInRvSVNPU3RyaW5nIiwic2NoZW1hVmVyc2lvbiIsInNvdXJjZUtpbmQiLCJ2YWxpZFVudGlsIiwiTWF0aCIsIm1pbiIsIkluZmluaXR5IiwiZW5zdXJlSW5kZXgiLCJleHBpcmVBZnRlciIsImZpZWxkcyIsInR5cGUiLCJkaWdlc3QiLCJwcmV2aW91cyIsIm1hcCIsImV2ZW50Iiwib2xkIiwiZmluZCIsIml0ZW0iLCJldmVudElkIiwibWF4IiwiZGVsZXRlZCIsImZpbHRlciIsInBlcnNpc3RlZCIsImZsb29yIiwiY29ycmVjdENybUNhbm9uaWNhbE9ic2VydmF0aW9uIiwiZXhwZWN0ZWRTb3VyY2VSZXZpc2lvbiIsImV4cGVjdGVkQXNzb2NpYXRpb25SZXZpc2lvbiIsImV4cGVjdGVkRXZlbnRSZXZpc2lvbiIsInN0YWdlIiwia2luZCIsInNvdXJjZUlkIiwiaW5kZXgiLCJjcm1PdXRjb21lRXZlbnRzIiwiZmluZEluZGV4Iiwic291cmNlUmV2aXNpb24iLCJvcmRlcklkIiwidXNlcklkIiwicHJvZHVjdElkIiwicHJvZHVjdElkcyIsImhpc3RvcnkiLCJwb3NpdGlvbiIsImNvcnJlY3RlZEF0IiwiY29ycmVjdGlvbkV2aWRlbmNlUmVmIiwidXBkYXRlcyIsImNybUxlYWRTdGFnZSIsInNhdmVkIiwidXBkYXRlIiwiYXNzb2NpYXRpb25SZXZpc2lvbiIsImV2ZW50UmV2aXNpb24iXSwibWFwcGluZ3MiOiJBQUFBLG1IQUFtSCxHQUNuSCxTQUFRQSxHQUFHLFFBQU8sV0FBVztBQUU3QixTQUFRQyxpQkFBaUIsUUFBTyx5QkFBeUI7QUFD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
|