serverless-spy 0.0.33 → 0.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.jsii +9 -9
  2. package/common/publishSpyEvent.ts +269 -0
  3. package/dist/releasetag.txt +1 -1
  4. package/extension/interceptor.ts +24 -13
  5. package/functions/onConnect.ts +2 -1
  6. package/functions/onDisconnect.ts +2 -1
  7. package/functions/sendMessage.ts +3 -268
  8. package/lib/common/publishSpyEvent.d.ts +4 -0
  9. package/lib/common/publishSpyEvent.js +211 -0
  10. package/lib/common/publishSpyEvent.mjs +205 -0
  11. package/lib/extension/dist/layer/nodejs/node_modules/interceptor.js +13890 -13
  12. package/lib/extension/dist/layer/nodejs/node_modules/interceptor.js.map +4 -4
  13. package/lib/listener/{SpyListener.d.ts → ServerlessSpyListener.d.ts} +12 -12
  14. package/lib/listener/ServerlessSpyListener.js +3 -0
  15. package/lib/listener/ServerlessSpyListener.mjs +2 -0
  16. package/lib/listener/ServerlessSpyListenerParams.d.ts +5 -0
  17. package/lib/listener/ServerlessSpyListenerParams.js +3 -0
  18. package/lib/listener/ServerlessSpyListenerParams.mjs +2 -0
  19. package/lib/listener/SpyHandlers.ts.d.ts +1 -1
  20. package/lib/listener/SpyHandlers.ts.js +1 -1
  21. package/lib/listener/SpyHandlers.ts.mjs +1 -1
  22. package/lib/listener/WaitForParams.d.ts +5 -0
  23. package/lib/listener/WaitForParams.js +3 -0
  24. package/lib/listener/WaitForParams.mjs +2 -0
  25. package/lib/listener/WsListener.d.ts +38 -0
  26. package/lib/listener/WsListener.js +185 -0
  27. package/lib/listener/WsListener.mjs +181 -0
  28. package/lib/listener/createServerlessSpyListener.d.ts +2 -13
  29. package/lib/listener/createServerlessSpyListener.js +5 -163
  30. package/lib/listener/createServerlessSpyListener.mjs +5 -163
  31. package/lib/listener/index.d.ts +1 -1
  32. package/lib/listener/index.js +2 -2
  33. package/lib/listener/index.mjs +2 -2
  34. package/lib/src/ServerlessSpy.d.ts +16 -8
  35. package/lib/src/ServerlessSpy.js +94 -53
  36. package/lib/src/ServerlessSpy.mjs +93 -52
  37. package/lib/src/common/envVariableNames.d.ts +8 -0
  38. package/lib/src/common/envVariableNames.js +15 -0
  39. package/lib/src/common/envVariableNames.mjs +12 -0
  40. package/listener/{SpyListener.ts → ServerlessSpyListener.ts} +12 -13
  41. package/listener/ServerlessSpyListenerParams.ts +6 -0
  42. package/listener/SpyHandlers.ts.ts +1 -1
  43. package/listener/WaitForParams.ts +6 -0
  44. package/listener/WsListener.ts +273 -0
  45. package/listener/createServerlessSpyListener.ts +5 -265
  46. package/listener/index.ts +1 -1
  47. package/package.json +1 -1
  48. package/lib/listener/SpyListener.js +0 -3
  49. package/lib/listener/SpyListener.mjs +0 -2
@@ -1,277 +1,12 @@
1
- import {
2
- ApiGatewayManagementApi,
3
- PostToConnectionCommand,
4
- } from '@aws-sdk/client-apigatewaymanagementapi';
5
- import {
6
- AttributeValue,
7
- DeleteItemCommand,
8
- DynamoDBClient,
9
- ScanCommand,
10
- } from '@aws-sdk/client-dynamodb';
11
- import { unmarshall } from '@aws-sdk/util-dynamodb';
12
-
13
- import {
14
- DynamoDBStreamEvent,
15
- S3Event,
16
- SNSEvent,
17
- EventBridgeEvent,
18
- SQSEvent,
19
- } from 'aws-lambda';
20
- import { DynamoDBSpyEvent } from '../common/spyEvents/DynamoDBSpyEvent';
21
- import { EventBridgeRuleSpyEvent } from '../common/spyEvents/EventBridgeRuleSpyEvent';
22
- import { EventBridgeSpyEvent } from '../common/spyEvents/EventBridgeSpyEvent';
23
- import { S3SpyEvent } from '../common/spyEvents/S3SpyEvent';
24
- import { SnsSubscriptionSpyEvent } from '../common/spyEvents/SnsSubscriptionSpyEvent';
25
- import { SnsTopicSpyEvent } from '../common/spyEvents/SnsTopicSpyEvent';
26
- import { SpyMessage } from '../common/spyEvents/SpyMessage';
27
- import { SqsSpyEvent } from '../common/spyEvents/SqsSpyEvent';
28
-
29
- const ddb = new DynamoDBClient({
30
- region: process.env.AWS_REGION,
31
- });
32
-
33
- const { TABLE_NAME } = process.env;
34
- const endpoint = process.env.WS_ENDPOINT!;
35
-
36
- const apigwManagementApi = new ApiGatewayManagementApi({
37
- apiVersion: '2018-11-29',
38
- endpoint,
39
- });
40
-
41
- let connections: Record<string, AttributeValue>[] | undefined;
42
-
43
- export const handler = async (event: any, context: any) => {
44
- console.log('EVENT', JSON.stringify(event));
45
- // console.log("CONTEXT", JSON.stringify(context));
46
-
47
- const mapping = JSON.parse(process.env.INFRA_MAPPING!);
48
- console.log('mapping', JSON.stringify(mapping));
49
-
50
- let connectionData;
51
-
52
- const scanParams = new ScanCommand({
53
- TableName: process.env.TABLE_NAME as string,
54
- ProjectionExpression: 'connectionId',
55
- });
1
+ import { publishSpyEvent } from '../common/publishSpyEvent';
56
2
 
3
+ export const handler = async (event: any) => {
57
4
  try {
58
- connectionData = await ddb.send(scanParams);
5
+ await publishSpyEvent(event);
59
6
  } catch (e) {
60
7
  console.error(e);
61
8
  return { statusCode: 500, body: (e as Error)?.stack };
62
9
  }
63
- connections = connectionData.Items;
64
-
65
- const postDataPromises: Promise<any>[] = [];
66
-
67
- if (event?.Records && event.Records[0]?.Sns) {
68
- console.log('*** SNS ***');
69
- const eventSns = event as SNSEvent;
70
- for (const record of eventSns.Records) {
71
- const subscriptionArn = record.EventSubscriptionArn;
72
-
73
- let serviceKey: string;
74
- if (mapping[subscriptionArn]) {
75
- // subscription event that could contain filter based on existing subscription
76
- serviceKey = mapping[subscriptionArn];
77
- } else {
78
- // catch all subscription
79
- const topicArn = record.Sns.TopicArn;
80
- serviceKey = mapping[topicArn];
81
- }
82
-
83
- let message: string;
84
-
85
- try {
86
- message = JSON.parse(record.Sns.Message);
87
- } catch {
88
- message = record.Sns.Message;
89
- }
90
-
91
- const spyEventType = getSpyEventType(serviceKey) as
92
- | 'FunctionSnsTopic'
93
- | 'FunctionSnsSubscription';
94
-
95
- const data: SnsTopicSpyEvent | SnsSubscriptionSpyEvent = {
96
- spyEventType,
97
- message,
98
- subject: record.Sns.Subject,
99
- timestamp: record.Sns.Timestamp,
100
- topicArn: record.Sns.TopicArn,
101
- messageId: record.Sns.MessageId,
102
- messageAttributes: record.Sns.MessageAttributes,
103
- };
104
-
105
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = {
106
- data,
107
- serviceKey,
108
- };
109
- postDataPromises.push(postData(fluentEvent));
110
- }
111
- } else if (event?.Records && event.Records[0]?.eventSource === 'aws:sqs') {
112
- console.log('*** SQS ***');
113
- const eventSqs = event as SQSEvent;
114
- for (const record of eventSqs.Records) {
115
- const subscriptionArn = record.eventSourceARN;
116
-
117
- const serviceKey = mapping[subscriptionArn];
118
- let body: string;
119
-
120
- try {
121
- body = JSON.parse(record.body);
122
- } catch {
123
- body = record.body;
124
- }
125
-
126
- const data: SqsSpyEvent = {
127
- spyEventType: 'Sqs',
128
- body,
129
- messageAttributes: record.messageAttributes,
130
- };
131
-
132
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = {
133
- data,
134
- serviceKey,
135
- };
136
- postDataPromises.push(postData(fluentEvent));
137
- }
138
- } else if (event?.Records && event.Records[0]?.s3) {
139
- console.log('*** S3 ***');
140
- const eventS3 = event as S3Event;
141
- for (const record of eventS3.Records) {
142
- const bucketArn = record.s3.bucket.arn;
143
-
144
- const data: S3SpyEvent = {
145
- spyEventType: 'S3',
146
- eventName: record.eventName,
147
- eventTime: record.eventTime,
148
- bucket: record.s3.bucket.name,
149
- key: record.s3.object.key,
150
- };
151
-
152
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = {
153
- data,
154
- serviceKey: mapping[bucketArn],
155
- };
156
- postDataPromises.push(postData(fluentEvent));
157
- }
158
- } else if (event.Records && event.Records[0]?.dynamodb) {
159
- console.log('*** DYNAMODB ***');
160
- const eventDynamoDB = event as DynamoDBStreamEvent;
161
- for (const record of eventDynamoDB.Records) {
162
- let arn = record.eventSourceARN!;
163
- arn = arn.substring(0, arn.indexOf('/stream/'));
164
-
165
- const data: DynamoDBSpyEvent = {
166
- spyEventType: 'DynamoDB',
167
- eventName: record.eventName,
168
- newImage: unmarshall(record.dynamodb?.NewImage as any),
169
- keys: unmarshall(record.dynamodb?.Keys as any),
170
- oldImage: record.dynamodb?.OldImage
171
- ? unmarshall(record.dynamodb?.OldImage as any)
172
- : undefined,
173
- };
174
-
175
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = {
176
- data,
177
- serviceKey: mapping[arn],
178
- };
179
- postDataPromises.push(postData(fluentEvent));
180
- }
181
- } else if (
182
- event.detail &&
183
- event['detail-type'] &&
184
- event.version &&
185
- event.source
186
- ) {
187
- console.log('*** EventBridge ***');
188
- const eventEb = event as EventBridgeEvent<any, any>;
189
-
190
- const serviceKey = mapping.eventBridge; // the is new lambda for each subscription
191
-
192
- const spyEventType = getSpyEventType(serviceKey) as
193
- | 'EventBridge'
194
- | 'EventBridgeRule';
195
-
196
- const message = eventEb.detail;
197
-
198
- const data: EventBridgeSpyEvent | EventBridgeRuleSpyEvent = {
199
- spyEventType,
200
- detail: message,
201
- detailType: eventEb['detail-type'],
202
- source: eventEb.source,
203
- time: eventEb.time,
204
- account: eventEb.account,
205
- };
206
-
207
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = {
208
- data,
209
- serviceKey,
210
- };
211
- postDataPromises.push(postData(fluentEvent));
212
- } else {
213
- console.log('*** OTHER ***');
214
- const fluentEvent: Omit<SpyMessage, 'timestamp'> = event;
215
- postDataPromises.push(postData(fluentEvent));
216
- }
217
-
218
- await Promise.all(postDataPromises);
219
10
 
220
11
  return { statusCode: 200, body: 'Data sent.' };
221
12
  };
222
-
223
- async function postData(spyMessage: Omit<SpyMessage, 'timestamp'>) {
224
- // const postData = JSON.parse(event.body!).data;
225
- console.log('postData', JSON.stringify(spyMessage));
226
-
227
- if (!connections) {
228
- return;
229
- }
230
-
231
- const postCalls = connections.map(async ({ connectionId }) => {
232
- console.log(`Sending message to: ${connectionId.S}`);
233
-
234
- try {
235
- const postToConnectionCommand = new PostToConnectionCommand({
236
- ConnectionId: connectionId.S,
237
- Data: JSON.stringify({
238
- timestamp: new Date().toISOString(),
239
- serviceKey: spyMessage.serviceKey,
240
- data: spyMessage.data,
241
- }) as any,
242
- });
243
-
244
- await apigwManagementApi.send(postToConnectionCommand);
245
- } catch (e) {
246
- console.error(`Fails sending message to: ${connectionId.S}`, e);
247
- console.error(`Status code: ${(e as any).statusCode}`);
248
- if ((e as any).$metadata.httpStatusCode === 410) {
249
- console.log(`Found stale connection, deleting ${connectionId}`);
250
-
251
- const deleteParams = new DeleteItemCommand({
252
- TableName: TABLE_NAME,
253
- Key: { connectionId },
254
- });
255
-
256
- await ddb.send(deleteParams);
257
- } else {
258
- throw e;
259
- }
260
- }
261
- });
262
-
263
- try {
264
- await Promise.all(postCalls);
265
- console.log('Send message finish');
266
- } catch (e) {
267
- return { statusCode: 500, body: (e as Error)?.stack };
268
- }
269
- }
270
-
271
- function getSpyEventType(serviceKey: string) {
272
- if (!serviceKey) {
273
- throw new Error('Missing serviceKey');
274
- }
275
-
276
- return serviceKey.substring(0, serviceKey.indexOf('#'));
277
- }
@@ -0,0 +1,4 @@
1
+ import { SpyMessage } from './spyEvents/SpyMessage';
2
+ export declare function publishSpyEvent(event: any): Promise<void>;
3
+ export declare function postData(spyMessage: Omit<SpyMessage, 'timestamp'>): Promise<void>;
4
+ export declare function getSpyEventType(serviceKey: string): string;
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSpyEventType = exports.postData = exports.publishSpyEvent = void 0;
4
+ const client_apigatewaymanagementapi_1 = require("@aws-sdk/client-apigatewaymanagementapi");
5
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
6
+ const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
7
+ const envVariableNames_1 = require("../src/common/envVariableNames");
8
+ const ddb = new client_dynamodb_1.DynamoDBClient({
9
+ region: process.env.AWS_REGION,
10
+ });
11
+ const apigwManagementApi = new client_apigatewaymanagementapi_1.ApiGatewayManagementApi({
12
+ apiVersion: '2018-11-29',
13
+ endpoint: process.env[envVariableNames_1.envVariableNames.WS_ENDPOINT],
14
+ });
15
+ let connections;
16
+ async function publishSpyEvent(event) {
17
+ console.log('EVENT', JSON.stringify(event));
18
+ const mapping = JSON.parse(process.env[envVariableNames_1.envVariableNames.INFRA_MAPPING]);
19
+ console.log('mapping', JSON.stringify(mapping));
20
+ let connectionData;
21
+ const scanParams = new client_dynamodb_1.ScanCommand({
22
+ TableName: process.env[envVariableNames_1.envVariableNames.TABLE_NAME],
23
+ ProjectionExpression: 'connectionId',
24
+ });
25
+ connectionData = await ddb.send(scanParams);
26
+ connections = connectionData.Items;
27
+ console.log(`connections at ${new Date().toISOString()}`, JSON.stringify(connections));
28
+ const postDataPromises = [];
29
+ if (event?.Records && event.Records[0]?.Sns) {
30
+ console.log('*** SNS ***');
31
+ const eventSns = event;
32
+ for (const record of eventSns.Records) {
33
+ const subscriptionArn = record.EventSubscriptionArn;
34
+ let serviceKey;
35
+ if (mapping[subscriptionArn]) {
36
+ // subscription event that could contain filter based on existing subscription
37
+ serviceKey = mapping[subscriptionArn];
38
+ }
39
+ else {
40
+ // catch all subscription
41
+ const topicArn = record.Sns.TopicArn;
42
+ serviceKey = mapping[topicArn];
43
+ }
44
+ let message;
45
+ try {
46
+ message = JSON.parse(record.Sns.Message);
47
+ }
48
+ catch {
49
+ message = record.Sns.Message;
50
+ }
51
+ const spyEventType = getSpyEventType(serviceKey);
52
+ const data = {
53
+ spyEventType,
54
+ message,
55
+ subject: record.Sns.Subject,
56
+ timestamp: record.Sns.Timestamp,
57
+ topicArn: record.Sns.TopicArn,
58
+ messageId: record.Sns.MessageId,
59
+ messageAttributes: record.Sns.MessageAttributes,
60
+ };
61
+ const fluentEvent = {
62
+ data,
63
+ serviceKey,
64
+ };
65
+ postDataPromises.push(postData(fluentEvent));
66
+ }
67
+ }
68
+ else if (event?.Records && event.Records[0]?.eventSource === 'aws:sqs') {
69
+ console.log('*** SQS ***');
70
+ const eventSqs = event;
71
+ for (const record of eventSqs.Records) {
72
+ const subscriptionArn = record.eventSourceARN;
73
+ const serviceKey = mapping[subscriptionArn];
74
+ let body;
75
+ try {
76
+ body = JSON.parse(record.body);
77
+ }
78
+ catch {
79
+ body = record.body;
80
+ }
81
+ const data = {
82
+ spyEventType: 'Sqs',
83
+ body,
84
+ messageAttributes: record.messageAttributes,
85
+ };
86
+ const fluentEvent = {
87
+ data,
88
+ serviceKey,
89
+ };
90
+ postDataPromises.push(postData(fluentEvent));
91
+ }
92
+ }
93
+ else if (event?.Records && event.Records[0]?.s3) {
94
+ console.log('*** S3 ***');
95
+ const eventS3 = event;
96
+ for (const record of eventS3.Records) {
97
+ const bucketArn = record.s3.bucket.arn;
98
+ const data = {
99
+ spyEventType: 'S3',
100
+ eventName: record.eventName,
101
+ eventTime: record.eventTime,
102
+ bucket: record.s3.bucket.name,
103
+ key: record.s3.object.key,
104
+ };
105
+ const fluentEvent = {
106
+ data,
107
+ serviceKey: mapping[bucketArn],
108
+ };
109
+ postDataPromises.push(postData(fluentEvent));
110
+ }
111
+ }
112
+ else if (event.Records && event.Records[0]?.dynamodb) {
113
+ console.log('*** DYNAMODB ***');
114
+ const eventDynamoDB = event;
115
+ for (const record of eventDynamoDB.Records) {
116
+ let arn = record.eventSourceARN;
117
+ arn = arn.substring(0, arn.indexOf('/stream/'));
118
+ const data = {
119
+ spyEventType: 'DynamoDB',
120
+ eventName: record.eventName,
121
+ newImage: (0, util_dynamodb_1.unmarshall)(record.dynamodb?.NewImage),
122
+ keys: (0, util_dynamodb_1.unmarshall)(record.dynamodb?.Keys),
123
+ oldImage: record.dynamodb?.OldImage
124
+ ? (0, util_dynamodb_1.unmarshall)(record.dynamodb?.OldImage)
125
+ : undefined,
126
+ };
127
+ const fluentEvent = {
128
+ data,
129
+ serviceKey: mapping[arn],
130
+ };
131
+ postDataPromises.push(postData(fluentEvent));
132
+ }
133
+ }
134
+ else if (event.detail &&
135
+ event['detail-type'] &&
136
+ event.version &&
137
+ event.source) {
138
+ console.log('*** EventBridge ***');
139
+ const eventEb = event;
140
+ const serviceKey = mapping.eventBridge; // the is new lambda for each subscription
141
+ const spyEventType = getSpyEventType(serviceKey);
142
+ const message = eventEb.detail;
143
+ const data = {
144
+ spyEventType,
145
+ detail: message,
146
+ detailType: eventEb['detail-type'],
147
+ source: eventEb.source,
148
+ time: eventEb.time,
149
+ account: eventEb.account,
150
+ };
151
+ const fluentEvent = {
152
+ data,
153
+ serviceKey,
154
+ };
155
+ postDataPromises.push(postData(fluentEvent));
156
+ }
157
+ else {
158
+ console.log('*** OTHER ***');
159
+ const fluentEvent = event;
160
+ postDataPromises.push(postData(fluentEvent));
161
+ }
162
+ await Promise.all(postDataPromises);
163
+ }
164
+ exports.publishSpyEvent = publishSpyEvent;
165
+ async function postData(spyMessage) {
166
+ // const postData = JSON.parse(event.body!).data;
167
+ console.log('postData', JSON.stringify(spyMessage));
168
+ if (!connections) {
169
+ return;
170
+ }
171
+ const postCalls = connections.map(async ({ connectionId }) => {
172
+ console.log(`Sending message to: ${connectionId.S}`);
173
+ try {
174
+ const postToConnectionCommand = new client_apigatewaymanagementapi_1.PostToConnectionCommand({
175
+ ConnectionId: connectionId.S,
176
+ Data: JSON.stringify({
177
+ timestamp: new Date().toISOString(),
178
+ serviceKey: spyMessage.serviceKey,
179
+ data: spyMessage.data,
180
+ }),
181
+ });
182
+ await apigwManagementApi.send(postToConnectionCommand);
183
+ }
184
+ catch (e) {
185
+ console.error(`Fails sending message to: ${connectionId.S}`, e);
186
+ console.error(`Status code: ${e.statusCode}`);
187
+ if (e.$metadata.httpStatusCode === 410) {
188
+ console.log(`Found stale connection, deleting ${connectionId}`);
189
+ const deleteParams = new client_dynamodb_1.DeleteItemCommand({
190
+ TableName: process.env[envVariableNames_1.envVariableNames.TABLE_NAME],
191
+ Key: { connectionId },
192
+ });
193
+ await ddb.send(deleteParams);
194
+ }
195
+ else {
196
+ throw e;
197
+ }
198
+ }
199
+ });
200
+ await Promise.all(postCalls);
201
+ console.log('Send message finish');
202
+ }
203
+ exports.postData = postData;
204
+ function getSpyEventType(serviceKey) {
205
+ if (!serviceKey) {
206
+ throw new Error('Missing serviceKey');
207
+ }
208
+ return serviceKey.substring(0, serviceKey.indexOf('#'));
209
+ }
210
+ exports.getSpyEventType = getSpyEventType;
211
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGlzaFNweUV2ZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vY29tbW9uL3B1Ymxpc2hTcHlFdmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw0RkFHaUQ7QUFDakQsOERBS2tDO0FBRWxDLDBEQUFvRDtBQVFwRCxxRUFBa0U7QUFVbEUsTUFBTSxHQUFHLEdBQUcsSUFBSSxnQ0FBYyxDQUFDO0lBQzdCLE1BQU0sRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVU7Q0FDL0IsQ0FBQyxDQUFDO0FBRUgsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLHdEQUF1QixDQUFDO0lBQ3JELFVBQVUsRUFBRSxZQUFZO0lBQ3hCLFFBQVEsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLG1DQUFnQixDQUFDLFdBQVcsQ0FBRTtDQUNyRCxDQUFDLENBQUM7QUFFSCxJQUFJLFdBQXlELENBQUM7QUFFdkQsS0FBSyxVQUFVLGVBQWUsQ0FBQyxLQUFVO0lBQzlDLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztJQUU1QyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUNBQWdCLENBQUMsYUFBYSxDQUFFLENBQUMsQ0FBQztJQUN6RSxPQUFPLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFFaEQsSUFBSSxjQUFjLENBQUM7SUFFbkIsTUFBTSxVQUFVLEdBQUcsSUFBSSw2QkFBVyxDQUFDO1FBQ2pDLFNBQVMsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLG1DQUFnQixDQUFDLFVBQVUsQ0FBVztRQUM3RCxvQkFBb0IsRUFBRSxjQUFjO0tBQ3JDLENBQUMsQ0FBQztJQUVILGNBQWMsR0FBRyxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMs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QyxHQUFHLENBQUMscUJBQXFCLENBQUMsQ0FBQztBQUNyQyxDQUFDO0FBMUNELDRCQTBDQztBQUVELFNBQWdCLGVBQWUsQ0FBQyxVQUFrQjtJQUNoRCxJQUFJLENBQUMsVUFBVSxFQUFFO1FBQ2YsTUFBTSxJQUFJLEtBQUssQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO0tBQ3ZDO0lBRUQsT0FBTyxVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxVQUFVLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7QUFDMUQsQ0FBQztBQU5ELDBDQU1DIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQXBpR2F0ZXdheU1hbmFnZW1lbnRBcGksXG4gIFBvc3RUb0Nvbm5lY3Rpb25Db21tYW5kLFxufSBmcm9tICdAYXdzLXNkay9jbGllbnQtYXBpZ2F0ZXdheW1hbmFnZW1lbnRhcGknO1xuaW1wb3J0IHtcbiAgQXR0cmlidXRlVmFsdWUsXG4gIERlbGV0ZUl0ZW1Db21tYW5kLFxuICBEeW5hbW9EQkNsaWVudCxcbiAgU2NhbkNvbW1hbmQsXG59IGZyb20gJ0Bhd3Mtc2RrL2NsaWVudC1keW5hbW9kYic7XG5cbmltcG9ydCB7IHVubWFyc2hhbGwgfSBmcm9tICdAYXdzLXNkay91dGlsLWR5bmFtb2RiJztcbmltcG9ydCB7XG4gIER5bmFtb0RCU3RyZWFtRXZlbnQsXG4gIFMzRXZlbnQsXG4gIFNOU0V2ZW50LFxuICBFdmVudEJyaWRnZUV2ZW50LFxuICBTUVNFdmVudCxcbn0gZnJvbSAnYXdzLWxhbWJkYSc7XG5pbXBvcnQgeyBlbnZWYXJpYWJsZU5hbWVzIH0gZnJvbSAnLi4vc3JjL2NvbW1vbi9lbnZWYXJpYWJsZU5hbWVzJztcbmltcG9ydCB7IER5bmFtb0RCU3B5RXZlbnQgfSBmcm9tICcuL3NweUV2ZW50cy9EeW5hbW9EQlNweUV2ZW50JztcbmltcG9ydCB7IEV2ZW50QnJpZGdlUnVsZVNweUV2ZW50IH0gZnJvbSAnLi9zcHlFdmVudHMvRXZlbnRCcmlkZ2VSdWxlU3B5RXZlbnQnO1xuaW1wb3J0IHsgRXZlbnRCcmlkZ2VTcHlFdmVudCB9IGZyb20gJy4vc3B5RXZlbnRzL0V2ZW50QnJpZGdlU3B5RXZlbnQnO1xuaW1wb3J0IHsgUzNTcHlFdmVudCB9IGZyb20gJy4vc3B5RXZlbnRzL1MzU3B5RXZlbnQnO1xuaW1wb3J0IHsgU25zU3Vic2NyaXB0aW9uU3B5RXZlbnQgfSBmcm9tICcuL3NweUV2ZW50cy9TbnNTdWJzY3JpcHRpb25TcHlFdmVudCc7XG5pbXBvcnQgeyBTbnNUb3BpY1NweUV2ZW50IH0gZnJvbSAnLi9zcHlFdmVudHMvU25zVG9waWNTcHlFdmVudCc7XG5pbXBvcnQgeyBTcHlNZXNzYWdlIH0gZnJvbSAnLi9zcHlFdmVudHMvU3B5TWVzc2FnZSc7XG5pbXBvcnQgeyBTcXNTcHlFdmVudCB9IGZyb20gJy4vc3B5RXZlbnRzL1Nxc1NweUV2ZW50JztcblxuY29uc3QgZGRiID0gbmV3IER5bmFtb0RCQ2xpZW50KHtcbiAgcmVnaW9uOiBwcm9jZXNzLmVudi5BV1NfUkVHSU9OLFxufSk7XG5cbmNvbnN0IGFwaWd3TWFuYWdlbWVudEFwaSA9IG5ldyBBcGlHYXRld2F5TWFuYWdlbWVudEFwaSh7XG4gIGFwaVZlcnNpb246ICcyMDE4LTExLTI5JyxcbiAgZW5kcG9pbnQ6IHByb2Nlc3MuZW52W2VudlZhcmlhYmxlTmFtZXMuV1NfRU5EUE9JTlRdISxcbn0pO1xuXG5sZXQgY29ubmVjdGlvbnM6IFJlY29yZDxzdHJpbmcsIEF0dHJpYnV0ZVZhbHVlPltdIHwgdW5kZWZpbmVkO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcHVibGlzaFNweUV2ZW50KGV2ZW50OiBhbnkpIHtcbiAgY29uc29sZS5sb2coJ0VWRU5UJywgSlNPTi5zdHJpbmdpZnkoZXZlbnQpKTtcblxuICBjb25zdCBtYXBwaW5nID0gSlNPTi5wYXJzZShwcm9jZXNzLmVudltlbnZWYXJpYWJsZU5hbWVzLklORlJBX01BUFBJTkddISk7XG4gIGNvbnNvbGUubG9nKCdtYXBwaW5nJywgSlNPTi5zdHJpbmdpZnkobWFwcGluZykpO1xuXG4gIGxldCBjb25uZWN0aW9uRGF0YTtcblxuICBjb25zdCBzY2FuUGFyYW1zID0gbmV3IFNjYW5Db21tYW5kKHtcbiAgICBUYWJsZU5hbWU6IHByb2Nlc3MuZW52W2VudlZhcmlhYmxlTmFtZXMuVEFCTEVfTkFNRV0gYXMgc3RyaW5nLFxuICAgIFByb2plY3Rpb25FeHByZXNzaW9uOiAnY29ubmVjdGlvbklkJyxcbiAgfSk7XG5cbiAgY29ubmVjdGlvbkRhdGEgPSBhd2FpdCBkZGIuc2VuZChzY2FuUGFyYW1zKTtcblxuICBjb25uZWN0aW9ucyA9IGNvbm5lY3Rpb25EYXRhLkl0ZW1zO1xuXG4gIGNvbnNvbGUubG9nKFxuICAgIGBjb25uZWN0aW9ucyBhdCAke25ldyBEYXRlKCkudG9JU09TdHJpbmcoKX1gLFxuICAgIEpTT04uc3RyaW5naWZ5KGNvbm5lY3Rpb25zKVxuICApO1xuXG4gIGNvbnN0IHBvc3REYXRhUHJvbWlzZXM6IFByb21pc2U8YW55PltdID0gW107XG5cbiAgaWYgKGV2ZW50Py5SZWNvcmRzICYmIGV2ZW50LlJlY29yZHNbMF0/LlNucykge1xuICAgIGNvbnNvbGUubG9nKCcqKiogU05TICoqKicpO1xuICAgIGNvbnN0IGV2ZW50U25zID0gZXZlbnQgYXMgU05TRXZlbnQ7XG4gICAgZm9yIChjb25zdCByZWNvcmQgb2YgZXZlbnRTbnMuUmVjb3Jkcykge1xuICAgICAgY29uc3Qgc3Vic2NyaXB0aW9uQXJuID0gcmVjb3JkLkV2ZW50U3Vic2NyaXB0aW9uQXJuO1xuXG4gICAgICBsZXQgc2VydmljZUtleTogc3RyaW5nO1xuICAgICAgaWYgKG1hcHBpbmdbc3Vic2NyaXB0aW9uQXJuXSkge1xuICAgICAgICAvLyBzdWJzY3JpcHRpb24gZXZlbnQgdGhhdCBjb3VsZCBjb250YWluIGZpbHRlciBiYXNlZCBvbiBleGlzdGluZyBzdWJzY3JpcHRpb25cbiAgICAgICAgc2VydmljZUtleSA9IG1hcHBpbmdbc3Vic2NyaXB0aW9uQXJuXTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIGNhdGNoIGFsbCBzdWJzY3JpcHRpb25cbiAgICAgICAgY29uc3QgdG9waWNBcm4gPSByZWNvcmQuU25zLlRvcGljQXJuO1xuICAgICAgICBzZXJ2aWNlS2V5ID0gbWFwcGluZ1t0b3BpY0Fybl07XG4gICAgICB9XG5cbiAgICAgIGxldCBtZXNzYWdlOiBzdHJpbmc7XG5cbiAgICAgIHRyeSB7XG4gICAgICAgIG1lc3NhZ2UgPSBKU09OLnBhcnNlKHJlY29yZC5TbnMuTWVzc2FnZSk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgbWVzc2FnZSA9IHJlY29yZC5TbnMuTWVzc2FnZTtcbiAgICAgIH1cblxuICAgICAgY29uc3Qgc3B5RXZlbnRUeXBlID0gZ2V0U3B5RXZlbnRUeXBlKHNlcnZpY2VLZXkpIGFzXG4gICAgICAgIHwgJ0Z1bmN0aW9uU25zVG9waWMnXG4gICAgICAgIHwgJ0Z1bmN0aW9uU25zU3Vic2NyaXB0aW9uJztcblxuICAgICAgY29uc3QgZGF0YTogU25zVG9waWNTcHlFdmVudCB8IFNuc1N1YnNjcmlwdGlvblNweUV2ZW50ID0ge1xuICAgICAgICBzcHlFdmVudFR5cGUsXG4gICAgICAgIG1lc3NhZ2UsXG4gICAgICAgIHN1YmplY3Q6IHJlY29yZC5TbnMuU3ViamVjdCxcbiAgICAgICAgdGltZXN0YW1wOiByZWNvcmQuU25zLlRpbWVzdGFtcCxcbiAgICAgICAgdG9waWNBcm46IHJlY29yZC5TbnMuVG9waWNBcm4sXG4gICAgICAgIG1lc3NhZ2VJZDogcmVjb3JkLlNucy5NZXNzYWdlSWQsXG4gICAgICAgIG1lc3NhZ2VBdHRyaWJ1dGVzOiByZWNvcmQuU25zLk1lc3NhZ2VBdHRyaWJ1dGVzLFxuICAgICAgfTtcblxuICAgICAgY29uc3QgZmx1ZW50RXZlbnQ6IE9taXQ8U3B5TWVzc2FnZSwgJ3RpbWVzdGFtcCc+ID0ge1xuICAgICAgICBkYXRhLFxuICAgICAgICBzZXJ2aWNlS2V5LFxuICAgICAgfTtcbiAgICAgIHBvc3REYXRhUHJvbWlzZXMucHVzaChwb3N0RGF0YShmbHVlbnRFdmVudCkpO1xuICAgIH1cbiAgfSBlbHNlIGlmIChldmVudD8uUmVjb3JkcyAmJiBldmVudC5SZWNvcmRzWzBdPy5ldmVudFNvdXJjZSA9PT0gJ2F3czpzcXMnKSB7XG4gICAgY29uc29sZS5sb2coJyoqKiBTUVMgKioqJyk7XG4gICAgY29uc3QgZXZlbnRTcXMgPSBldmVudCBhcyBTUVNFdmVudDtcbiAgICBmb3IgKGNvbnN0IHJlY29yZCBvZiBldmVudFNxcy5SZWNvcmRzKSB7XG4gICAgICBjb25zdCBzdWJzY3JpcHRpb25Bcm4gPSByZWNvcmQuZXZlbnRTb3VyY2VBUk47XG5cbiAgICAgIGNvbnN0IHNlcnZpY2VLZXkgPSBtYXBwaW5nW3N1YnNjcmlwdGlvbkFybl07XG4gICAgICBsZXQgYm9keTogc3RyaW5nO1xuXG4gICAgICB0cnkge1xuICAgICAgICBib2R5ID0gSlNPTi5wYXJzZShyZWNvcmQuYm9keSk7XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgYm9keSA9IHJlY29yZC5ib2R5O1xuICAgICAgfVxuXG4gICAgICBjb25zdCBkYXRhOiBTcXNTcHlFdmVudCA9IHtcbiAgICAgICAgc3B5RXZlbnRUeXBlOiAnU3FzJyxcbiAgICAgICAgYm9keSxcbiAgICAgICAgbWVzc2FnZUF0dHJpYnV0ZXM6IHJlY29yZC5tZXNzYWdlQXR0cmlidXRlcyxcbiAgICAgIH07XG5cbiAgICAgIGNvbnN0IGZsdWVudEV2ZW50OiBPbWl0PFNweU1lc3NhZ2UsICd0aW1lc3RhbXAnPiA9IHtcbiAgICAgICAgZGF0YSxcbiAgICAgICAgc2VydmljZUtleSxcbiAgICAgIH07XG4gICAgICBwb3N0RGF0YVByb21pc2VzLnB1c2gocG9zdERhdGEoZmx1ZW50RXZlbnQpKTtcbiAgICB9XG4gIH0gZWxzZSBpZiAoZXZlbnQ/LlJlY29yZHMgJiYgZXZlbnQuUmVjb3Jkc1swXT8uczMpIHtcbiAgICBjb25zb2xlLmxvZygnKioqIFMzICoqKicpO1xuICAgIGNvbnN0IGV2ZW50UzMgPSBldmVudCBhcyBTM0V2ZW50O1xuICAgIGZvciAoY29uc3QgcmVjb3JkIG9mIGV2ZW50UzMuUmVjb3Jkcykge1xuICAgICAgY29uc3QgYnVja2V0QXJuID0gcmVjb3JkLnMzLmJ1Y2tldC5hcm47XG5cbiAgICAgIGNvbnN0IGRhdGE6IFMzU3B5RXZlbnQgPSB7XG4gICAgICAgIHNweUV2ZW50VHlwZTogJ1MzJyxcbiAgICAgICAgZXZlbnROYW1lOiByZWNvcmQuZXZlbnROYW1lLFxuICAgICAgICBldmVudFRpbWU6IHJlY29yZC5ldmVudFRpbWUsXG4gICAgICAgIGJ1Y2tldDogcmVjb3JkLnMzLmJ1Y2tldC5uYW1lLFxuICAgICAgICBrZXk6IHJlY29yZC5zMy5vYmplY3Qua2V5LFxuICAgICAgfTtcblxuICAgICAgY29uc3QgZmx1ZW50RXZlbnQ6IE9taXQ8U3B5TWVzc2FnZSwgJ3RpbWVzdGFtcCc+ID0ge1xuICAgICAgICBkYXRhLFxuICAgICAgICBzZXJ2aWNlS2V5OiBtYXBwaW5nW2J1Y2tldEFybl0sXG4gICAgICB9O1xuICAgICAgcG9zdERhdGFQcm9taXNlcy5wdXNoKHBvc3REYXRhKGZsdWVudEV2ZW50KSk7XG4gICAgfVxuICB9IGVsc2UgaWYgKGV2ZW50LlJlY29yZHMgJiYgZXZlbnQuUmVjb3Jkc1swXT8uZHluYW1vZGIpIHtcbiAgICBjb25zb2xlLmxvZygnKioqIERZTkFNT0RCICoqKicpO1xuICAgIGNvbnN0IGV2ZW50RHluYW1vREIgPSBldmVudCBhcyBEeW5hbW9EQlN0cmVhbUV2ZW50O1xuICAgIGZvciAoY29uc3QgcmVjb3JkIG9mIGV2ZW50RHluYW1vREIuUmVjb3Jkcykge1xuICAgICAgbGV0IGFybiA9IHJlY29yZC5ldmVudFNvdXJjZUFSTiE7XG4gICAgICBhcm4gPSBhcm4uc3Vic3RyaW5nKDAsIGFybi5pbmRleE9mKCcvc3RyZWFtLycpKTtcblxuICAgICAgY29uc3QgZGF0YTogRHluYW1vREJTcHlFdmVudCA9IHtcbiAgICAgICAgc3B5RXZlbnRUeXBlOiAnRHluYW1vREInLFxuICAgICAgICBldmVudE5hbWU6IHJlY29yZC5ldmVudE5hbWUsXG4gICAgICAgIG5ld0ltYWdlOiB1bm1hcnNoYWxsKHJlY29yZC5keW5hbW9kYj8uTmV3SW1hZ2UgYXMgYW55KSxcbiAgICAgICAga2V5czogdW5tYXJzaGFsbChyZWNvcmQuZHluYW1vZGI/LktleXMgYXMgYW55KSxcbiAgICAgICAgb2xkSW1hZ2U6IHJlY29yZC5keW5hbW9kYj8uT2xkSW1hZ2VcbiAgICAgICAgICA/IHVubWFyc2hhbGwocmVjb3JkLmR5bmFtb2RiPy5PbGRJbWFnZSBhcyBhbnkpXG4gICAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgICB9O1xuXG4gICAgICBjb25zdCBmbHVlbnRFdmVudDogT21pdDxTcHlNZXNzYWdlLCAndGltZXN0YW1wJz4gPSB7XG4gICAgICAgIGRhdGEsXG4gICAgICAgIHNlcnZpY2VLZXk6IG1hcHBpbmdbYXJuXSxcbiAgICAgIH07XG4gICAgICBwb3N0RGF0YVByb21pc2VzLnB1c2gocG9zdERhdGEoZmx1ZW50RXZlbnQpKTtcbiAgICB9XG4gIH0gZWxzZSBpZiAoXG4gICAgZXZlbnQuZGV0YWlsICYmXG4gICAgZXZlbnRbJ2RldGFpbC10eXBlJ10gJiZcbiAgICBldmVudC52ZXJzaW9uICYmXG4gICAgZXZlbnQuc291cmNlXG4gICkge1xuICAgIGNvbnNvbGUubG9nKCcqKiogRXZlbnRCcmlkZ2UgKioqJyk7XG4gICAgY29uc3QgZXZlbnRFYiA9IGV2ZW50IGFzIEV2ZW50QnJpZGdlRXZlbnQ8YW55LCBhbnk+O1xuXG4gICAgY29uc3Qgc2VydmljZUtleSA9IG1hcHBpbmcuZXZlbnRCcmlkZ2U7IC8vIHRoZSBpcyBuZXcgbGFtYmRhIGZvciBlYWNoIHN1YnNjcmlwdGlvblxuXG4gICAgY29uc3Qgc3B5RXZlbnRUeXBlID0gZ2V0U3B5RXZlbnRUeXBlKHNlcnZpY2VLZXkpIGFzXG4gICAgICB8ICdFdmVudEJyaWRnZSdcbiAgICAgIHwgJ0V2ZW50QnJpZGdlUnVsZSc7XG5cbiAgICBjb25zdCBtZXNzYWdlID0gZXZlbnRFYi5kZXRhaWw7XG5cbiAgICBjb25zdCBkYXRhOiBFdmVudEJyaWRnZVNweUV2ZW50IHwgRXZlbnRCcmlkZ2VSdWxlU3B5RXZlbnQgPSB7XG4gICAgICBzcHlFdmVudFR5cGUsXG4gICAgICBkZXRhaWw6IG1lc3NhZ2UsXG4gICAgICBkZXRhaWxUeXBlOiBldmVudEViWydkZXRhaWwtdHlwZSddLFxuICAgICAgc291cmNlOiBldmVudEViLnNvdXJjZSxcbiAgICAgIHRpbWU6IGV2ZW50RWIudGltZSxcbiAgICAgIGFjY291bnQ6IGV2ZW50RWIuYWNjb3VudCxcbiAgICB9O1xuXG4gICAgY29uc3QgZmx1ZW50RXZlbnQ6IE9taXQ8U3B5TWVzc2FnZSwgJ3RpbWVzdGFtcCc+ID0ge1xuICAgICAgZGF0YSxcbiAgICAgIHNlcnZpY2VLZXksXG4gICAgfTtcbiAgICBwb3N0RGF0YVByb21pc2VzLnB1c2gocG9zdERhdGEoZmx1ZW50RXZlbnQpKTtcbiAgfSBlbHNlIHtcbiAgICBjb25zb2xlLmxvZygnKioqIE9USEVSICoqKicpO1xuICAgIGNvbnN0IGZsdWVudEV2ZW50OiBPbWl0PFNweU1lc3NhZ2UsICd0aW1lc3RhbXAnPiA9IGV2ZW50O1xuICAgIHBvc3REYXRhUHJvbWlzZXMucHVzaChwb3N0RGF0YShmbHVlbnRFdmVudCkpO1xuICB9XG5cbiAgYXdhaXQgUHJvbWlzZS5hbGwocG9zdERhdGFQcm9taXNlcyk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwb3N0RGF0YShzcHlNZXNzYWdlOiBPbWl0PFNweU1lc3NhZ2UsICd0aW1lc3RhbXAnPikge1xuICAvLyBjb25zdCBwb3N0RGF0YSA9IEpTT04ucGFyc2UoZXZlbnQuYm9keSEpLmRhdGE7XG4gIGNvbnNvbGUubG9nKCdwb3N0RGF0YScsIEpTT04uc3RyaW5naWZ5KHNweU1lc3NhZ2UpKTtcblxuICBpZiAoIWNvbm5lY3Rpb25zKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3QgcG9zdENhbGxzID0gY29ubmVjdGlvbnMubWFwKGFzeW5jICh7IGNvbm5lY3Rpb25JZCB9KSA9PiB7XG4gICAgY29uc29sZS5sb2coYFNlbmRpbmcgbWVzc2FnZSB0bzogJHtjb25uZWN0aW9uSWQuU31gKTtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBwb3N0VG9Db25uZWN0aW9uQ29tbWFuZCA9IG5ldyBQb3N0VG9Db25uZWN0aW9uQ29tbWFuZCh7XG4gICAgICAgIENvbm5lY3Rpb25JZDogY29ubmVjdGlvbklkLlMsXG4gICAgICAgIERhdGE6IEpTT04uc3RyaW5naWZ5KHtcbiAgICAgICAgICB0aW1lc3RhbXA6IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKSxcbiAgICAgICAgICBzZXJ2aWNlS2V5OiBzcHlNZXNzYWdlLnNlcnZpY2VLZXksXG4gICAgICAgICAgZGF0YTogc3B5TWVzc2FnZS5kYXRhLFxuICAgICAgICB9KSBhcyBhbnksXG4gICAgICB9KTtcblxuICAgICAgYXdhaXQgYXBpZ3dNYW5hZ2VtZW50QXBpLnNlbmQocG9zdFRvQ29ubmVjdGlvbkNvbW1hbmQpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYEZhaWxzIHNlbmRpbmcgbWVzc2FnZSB0bzogJHtjb25uZWN0aW9uSWQuU31gLCBlKTtcbiAgICAgIGNvbnNvbGUuZXJyb3IoYFN0YXR1cyBjb2RlOiAkeyhlIGFzIGFueSkuc3RhdHVzQ29kZX1gKTtcbiAgICAgIGlmICgoZSBhcyBhbnkpLiRtZXRhZGF0YS5odHRwU3RhdHVzQ29kZSA9PT0gNDEwKSB7XG4gICAgICAgIGNvbnNvbGUubG9nKGBGb3VuZCBzdGFsZSBjb25uZWN0aW9uLCBkZWxldGluZyAke2Nvbm5lY3Rpb25JZH1gKTtcblxuICAgICAgICBjb25zdCBkZWxldGVQYXJhbXMgPSBuZXcgRGVsZXRlSXRlbUNvbW1hbmQoe1xuICAgICAgICAgIFRhYmxlTmFtZTogcHJvY2Vzcy5lbnZbZW52VmFyaWFibGVOYW1lcy5UQUJMRV9OQU1FXSxcbiAgICAgICAgICBLZXk6IHsgY29ubmVjdGlvbklkIH0sXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGF3YWl0IGRkYi5zZW5kKGRlbGV0ZVBhcmFtcyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgYXdhaXQgUHJvbWlzZS5hbGwocG9zdENhbGxzKTtcbiAgY29uc29sZS5sb2coJ1NlbmQgbWVzc2FnZSBmaW5pc2gnKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFNweUV2ZW50VHlwZShzZXJ2aWNlS2V5OiBzdHJpbmcpIHtcbiAgaWYgKCFzZXJ2aWNlS2V5KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdNaXNzaW5nIHNlcnZpY2VLZXknKTtcbiAgfVxuXG4gIHJldHVybiBzZXJ2aWNlS2V5LnN1YnN0cmluZygwLCBzZXJ2aWNlS2V5LmluZGV4T2YoJyMnKSk7XG59XG4iXX0=