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
@@ -0,0 +1,273 @@
1
+ import { WebSocket } from 'ws';
2
+ import { getWebSocketUrl } from '../common/getWebSocketUrl';
3
+ import { FunctionRequestSpyEvent } from '../common/spyEvents/FunctionRequestSpyEvent';
4
+ import { SpyEvent } from '../common/spyEvents/SpyEvent';
5
+ import { SpyMessage } from '../common/spyEvents/SpyMessage';
6
+ import { ServerlessSpyListener } from './ServerlessSpyListener';
7
+ import { ServerlessSpyListenerParams } from './ServerlessSpyListenerParams';
8
+ import { WaitForParams } from './WaitForParams';
9
+
10
+ export class WsListener<TSpyEvents> {
11
+ private messages: SpyMessageStorage[] = [];
12
+ private trackers: Tracker[] = [];
13
+
14
+ private connectionOpenResolve?: () => void;
15
+ private waitForConnection?: Promise<void>;
16
+ private ws?: WebSocket;
17
+ private closed = true;
18
+ private functionPrefix = 'waitFor';
19
+
20
+ async start(params: ServerlessSpyListenerParams) {
21
+ this.waitForConnection = new Promise((resolve) => {
22
+ this.connectionOpenResolve = resolve;
23
+ });
24
+
25
+ const urlSigned = await getWebSocketUrl(
26
+ params.serverlessSpyWsUrl,
27
+ params.credentials
28
+ );
29
+
30
+ this.ws = new WebSocket(urlSigned);
31
+ this.closed = false;
32
+ this.ws.on('open', () => {
33
+ this.connectionOpenResolve!();
34
+ });
35
+ this.ws.on('message', (data) => {
36
+ if (this.closed) return;
37
+
38
+ const message = JSON.parse(data.toString()) as SpyMessageStorage;
39
+
40
+ message.serviceKeyForFunction = message.serviceKey.replace(/#/g, '');
41
+
42
+ if (message.serviceKey.startsWith('Function')) {
43
+ message.functionContextAwsRequestId = (
44
+ message.data as FunctionRequestSpyEvent
45
+ ).context.awsRequestId;
46
+ }
47
+
48
+ this.messages.push(message);
49
+ this.resolveOldTrackerWithNewMessage(message);
50
+ });
51
+ this.ws.on('close', () => {
52
+ this.closed = true;
53
+ //console.log('disconnected ' + new Date().toISOString());
54
+ });
55
+
56
+ await this.waitForConnection;
57
+ }
58
+
59
+ async stop() {
60
+ this.closed = true;
61
+ this.ws!.close();
62
+ }
63
+
64
+ trackerMatchMessage(tracker: Tracker, message: SpyMessageStorage) {
65
+ if (tracker.finished) return;
66
+
67
+ if (
68
+ (tracker.serviceKey && tracker.serviceKey === message.serviceKey) ||
69
+ (tracker.serviceKeyForFunction &&
70
+ tracker.serviceKeyForFunction === message.serviceKeyForFunction)
71
+ ) {
72
+ if (this.trackerMatchCondition(tracker, message)) {
73
+ tracker.finished = true;
74
+
75
+ const spyAndJestMatchers: any = {
76
+ getData: () => message.data,
77
+ };
78
+
79
+ const serviceKeyForFunction = tracker.serviceKeyForFunction;
80
+ if (
81
+ serviceKeyForFunction &&
82
+ serviceKeyForFunction.startsWith('Function') &&
83
+ (serviceKeyForFunction.endsWith('Request') ||
84
+ serviceKeyForFunction.endsWith('Console'))
85
+ ) {
86
+ let serviceKeyForFunctionResponse = serviceKeyForFunction;
87
+
88
+ if (serviceKeyForFunctionResponse.endsWith('Request')) {
89
+ serviceKeyForFunctionResponse =
90
+ serviceKeyForFunctionResponse.substring(
91
+ 0,
92
+ serviceKeyForFunctionResponse.length - 'Request'.length
93
+ );
94
+ } else if (serviceKeyForFunctionResponse.endsWith('Console')) {
95
+ serviceKeyForFunctionResponse =
96
+ serviceKeyForFunctionResponse.substring(
97
+ 0,
98
+ serviceKeyForFunctionResponse.length - 'Console'.length
99
+ );
100
+ }
101
+
102
+ serviceKeyForFunctionResponse += 'Response';
103
+
104
+ spyAndJestMatchers.followedByResponse = (paramsW: WaitForParams) => {
105
+ return this.createWaitForXXXFunc(
106
+ serviceKeyForFunctionResponse,
107
+ (message.data as FunctionRequestSpyEvent).context.awsRequestId
108
+ )(paramsW);
109
+ };
110
+ }
111
+
112
+ const proxy = new Proxy(spyAndJestMatchers, {
113
+ get: function (target: any, objectKey: string) {
114
+ if (target.hasOwnProperty(objectKey)) {
115
+ return target[objectKey];
116
+ } else if (objectKey !== 'then') {
117
+ return function () {
118
+ const jestFunctionToExecute = (expect(message.data) as any)[
119
+ objectKey
120
+ ];
121
+ jestFunctionToExecute.apply(undefined, arguments);
122
+ return proxy;
123
+ };
124
+ }
125
+ },
126
+ });
127
+
128
+ tracker.promiseResolve(proxy);
129
+ return true;
130
+ }
131
+ }
132
+ return false;
133
+ }
134
+
135
+ private resolveTrackerInOldMessages(tracker: Tracker) {
136
+ for (const message of this.messages) {
137
+ if (this.trackerMatchMessage(tracker, message)) {
138
+ return true;
139
+ }
140
+ }
141
+
142
+ return false;
143
+ }
144
+
145
+ private resolveOldTrackerWithNewMessage(message: SpyMessageStorage) {
146
+ for (let index = 0; index < this.trackers.length; index++) {
147
+ const tracker = this.trackers[index];
148
+ if (this.trackerMatchMessage(tracker, message)) {
149
+ this.trackers = this.trackers.splice(index, 1);
150
+ return true;
151
+ }
152
+ }
153
+
154
+ return false;
155
+ }
156
+
157
+ private trackerMatchCondition(tracker: Tracker, message: SpyMessageStorage) {
158
+ const matchCondition =
159
+ (tracker.condition && tracker.condition(message.data)) ||
160
+ !tracker.condition;
161
+
162
+ const matchRequestId =
163
+ (tracker.functionContextAwsRequestId &&
164
+ tracker.functionContextAwsRequestId ===
165
+ message.functionContextAwsRequestId) ||
166
+ !tracker.functionContextAwsRequestId;
167
+
168
+ if (matchCondition && matchRequestId) {
169
+ return true;
170
+ } else {
171
+ if (
172
+ !matchCondition &&
173
+ matchRequestId &&
174
+ !tracker.possibleSpyMessageDataForDebugging
175
+ ) {
176
+ tracker.possibleSpyMessageDataForDebugging = message.data;
177
+ }
178
+ return false;
179
+ }
180
+ }
181
+
182
+ public createWaitForXXXFunc(
183
+ serviceKeyForFunction: string,
184
+ functionContextAwsRequestId?: string
185
+ ) {
186
+ let tracker: Tracker;
187
+
188
+ const promise = new Promise((resolve, reject) => {
189
+ tracker = {
190
+ finished: false,
191
+ promiseResolve: resolve,
192
+ promiseReject: reject,
193
+ serviceKeyForFunction,
194
+ functionContextAwsRequestId,
195
+ };
196
+ });
197
+
198
+ //waitForXXXFunc
199
+ return (paramsW?: WaitForParams<SpyEvent>) => {
200
+ tracker.condition = paramsW?.condition;
201
+
202
+ const timer = setTimeout(() => {
203
+ if (tracker.finished) return;
204
+ tracker.finished = true;
205
+ let message = `Timeout waiting for Serverless Spy message ${serviceKeyForFunction}.`;
206
+
207
+ if (tracker.possibleSpyMessageDataForDebugging) {
208
+ message += ` Similar matching spy event data: ${JSON.stringify(
209
+ tracker.possibleSpyMessageDataForDebugging,
210
+ null,
211
+ 2
212
+ )}`;
213
+ }
214
+
215
+ tracker.promiseReject(new Error(message));
216
+ }, paramsW?.timoutMs || 10000);
217
+
218
+ promise.finally(() => {
219
+ clearTimeout(timer);
220
+ });
221
+
222
+ if (!this.resolveTrackerInOldMessages(tracker)) {
223
+ this.trackers.push(tracker);
224
+ }
225
+
226
+ return promise;
227
+ };
228
+ }
229
+
230
+ public createProxy() {
231
+ const spyListener = {} as ServerlessSpyListener<TSpyEvents>;
232
+
233
+ spyListener.stop = async () => {
234
+ await this.stop();
235
+ };
236
+
237
+ const proxy = new Proxy(spyListener, {
238
+ get: (target: any, objectKey: string) => {
239
+ if (target.hasOwnProperty(objectKey)) {
240
+ return target[objectKey];
241
+ } else if (
242
+ typeof objectKey === 'string' &&
243
+ objectKey.startsWith(this.functionPrefix)
244
+ ) {
245
+ const serviceKeyForFunction = objectKey.substring(
246
+ this.functionPrefix.length
247
+ );
248
+
249
+ return this.createWaitForXXXFunc(serviceKeyForFunction);
250
+ }
251
+ },
252
+ });
253
+
254
+ return proxy as ServerlessSpyListener<TSpyEvents>;
255
+ }
256
+ }
257
+
258
+ type Tracker = {
259
+ promiseResolve: (data: any) => void;
260
+ promiseReject: (data: any) => void;
261
+ finished: boolean;
262
+ serviceKey?: string;
263
+ serviceKeyForFunction?: string;
264
+ condition?: (data: any) => boolean;
265
+ timoutMs?: number;
266
+ functionContextAwsRequestId?: string;
267
+ possibleSpyMessageDataForDebugging?: any;
268
+ };
269
+
270
+ type SpyMessageStorage = SpyMessage & {
271
+ serviceKeyForFunction: string;
272
+ functionContextAwsRequestId?: string;
273
+ };
@@ -1,271 +1,11 @@
1
- import { Credentials } from '@aws-sdk/types';
2
- import { WebSocket } from 'ws';
3
- import { getWebSocketUrl } from '../common/getWebSocketUrl';
4
- import { FunctionRequestSpyEvent } from '../common/spyEvents/FunctionRequestSpyEvent';
5
- import { SpyEvent } from '../common/spyEvents/SpyEvent';
6
- import { SpyMessage } from '../common/spyEvents/SpyMessage';
7
- import { SpyListener } from './SpyListener';
8
-
9
- type ServerlessSpyListenerParams = {
10
- serverlessSpyWsUrl: string;
11
- credentials?: Credentials;
12
- };
1
+ import { ServerlessSpyListenerParams } from './ServerlessSpyListenerParams';
2
+ import { WsListener } from './WsListener';
13
3
 
14
4
  export async function createServerlessSpyListener<TSpyEvents>(
15
5
  params: ServerlessSpyListenerParams
16
6
  ) {
17
- const urlSigned = await getWebSocketUrl(
18
- params.serverlessSpyWsUrl,
19
- params.credentials
20
- );
21
- const ws = new WebSocket(urlSigned);
22
- const messages: SpyMessageStorage[] = [];
23
- let trackers: Tracker[] = [];
24
- let closed = false;
25
- const functionPrefix = 'waitFor';
26
- let connectionOpenResolve: (value: unknown) => void;
27
-
28
- let waitForConnection = new Promise((resolve) => {
29
- connectionOpenResolve = resolve;
30
- });
31
-
32
- ws.on('open', () => {
33
- //console.log('connected ' + new Date().toISOString());
34
- connectionOpenResolve(undefined);
35
- });
36
- ws.on('message', (data) => {
37
- if (closed) return;
38
-
39
- //console.log(`From server: ${data}`);
40
-
41
- const message = JSON.parse(data.toString()) as SpyMessageStorage;
42
-
43
- message.serviceKeyForFunction = message.serviceKey.replace(/#/g, '');
44
-
45
- if (message.serviceKey.startsWith('Function')) {
46
- message.functionContextAwsRequestId = (
47
- message.data as FunctionRequestSpyEvent
48
- ).context.awsRequestId;
49
- }
50
-
51
- messages.push(message);
52
- resolveOldTrackerWithNewMessage(message);
53
- });
54
- ws.on('close', () => {
55
- closed = true;
56
- // console.log("disconnected " + new Date().toISOString());
57
- });
58
-
59
- const trackerMatchMessage = (
60
- tracker: Tracker,
61
- message: SpyMessageStorage
62
- ) => {
63
- if (tracker.finished) return;
64
-
65
- if (
66
- (tracker.serviceKey && tracker.serviceKey === message.serviceKey) ||
67
- (tracker.serviceKeyForFunction &&
68
- tracker.serviceKeyForFunction === message.serviceKeyForFunction)
69
- ) {
70
- if (trackerMatchCondition(tracker, message)) {
71
- tracker.finished = true;
72
-
73
- const spyAndJestMatchers: any = {
74
- getData: () => message.data,
75
- };
76
-
77
- const serviceKeyForFunction = tracker.serviceKeyForFunction;
78
- if (
79
- serviceKeyForFunction &&
80
- serviceKeyForFunction.startsWith('Function') &&
81
- (serviceKeyForFunction.endsWith('Request') ||
82
- serviceKeyForFunction.endsWith('Console'))
83
- ) {
84
- let serviceKeyForFunctionResponse = serviceKeyForFunction;
85
-
86
- if (serviceKeyForFunctionResponse.endsWith('Request')) {
87
- serviceKeyForFunctionResponse =
88
- serviceKeyForFunctionResponse.substring(
89
- 0,
90
- serviceKeyForFunctionResponse.length - 'Request'.length
91
- );
92
- } else if (serviceKeyForFunctionResponse.endsWith('Console')) {
93
- serviceKeyForFunctionResponse =
94
- serviceKeyForFunctionResponse.substring(
95
- 0,
96
- serviceKeyForFunctionResponse.length - 'Console'.length
97
- );
98
- }
99
-
100
- serviceKeyForFunctionResponse += 'Response';
101
-
102
- spyAndJestMatchers.followedByResponse = (paramsW: WaitForParams) => {
103
- return createWaitForXXXFunc(
104
- serviceKeyForFunctionResponse,
105
- paramsW,
106
- (message.data as FunctionRequestSpyEvent).context.awsRequestId
107
- )();
108
- };
109
- }
110
-
111
- const proxy = new Proxy(spyAndJestMatchers, {
112
- get: function (target: any, objectKey: string) {
113
- if (target.hasOwnProperty(objectKey)) {
114
- return target[objectKey];
115
- } else if (objectKey !== 'then') {
116
- return function () {
117
- const jestFunctionToExecute = (expect(message.data) as any)[
118
- objectKey
119
- ];
120
- jestFunctionToExecute.apply(undefined, arguments);
121
- return proxy;
122
- };
123
- }
124
- },
125
- });
126
-
127
- tracker.promiseResolve(proxy);
128
- return true;
129
- }
130
- }
131
- return false;
132
- };
133
-
134
- const resolveTrackerInOldMessages = (tracker: Tracker) => {
135
- for (const message of messages) {
136
- if (trackerMatchMessage(tracker, message)) {
137
- return true;
138
- }
139
- }
140
-
141
- return false;
142
- };
143
-
144
- const resolveOldTrackerWithNewMessage = (message: SpyMessageStorage) => {
145
- for (let index = 0; index < trackers.length; index++) {
146
- const tracker = trackers[index];
147
- if (trackerMatchMessage(tracker, message)) {
148
- trackers = trackers.splice(index, 1);
149
- return true;
150
- }
151
- }
152
-
153
- return false;
154
- };
7
+ const wsListener = new WsListener<TSpyEvents>();
8
+ await wsListener.start(params);
155
9
 
156
- const spyListener = {} as SpyListener<TSpyEvents>;
157
-
158
- spyListener.stop = () => {
159
- closed = true;
160
- ws.close();
161
- };
162
-
163
- const proxy = new Proxy(spyListener, {
164
- get: function (target: any, objectKey: string) {
165
- if (target.hasOwnProperty(objectKey)) {
166
- return target[objectKey];
167
- } else if (
168
- typeof objectKey === 'string' &&
169
- objectKey.startsWith(functionPrefix)
170
- ) {
171
- const paramsW = arguments[0] as WaitForParams;
172
- const serviceKeyForFunction = objectKey.substring(
173
- functionPrefix.length
174
- );
175
-
176
- return createWaitForXXXFunc(serviceKeyForFunction, paramsW);
177
- }
178
- },
179
- });
180
-
181
- await waitForConnection;
182
-
183
- return proxy as SpyListener<TSpyEvents>;
184
-
185
- function trackerMatchCondition(tracker: Tracker, message: SpyMessageStorage) {
186
- const matchCondition =
187
- (tracker.condition && tracker.condition(message.data)) ||
188
- !tracker.condition;
189
-
190
- const matchRequestId =
191
- (tracker.functionContextAwsRequestId &&
192
- tracker.functionContextAwsRequestId ===
193
- message.functionContextAwsRequestId) ||
194
- !tracker.functionContextAwsRequestId;
195
-
196
- // if (tracker.functionContextAwsRequestId) {
197
- // console.log(
198
- // `${tracker.functionContextAwsRequestId} - ${message.functionContextAwsRequestId}`
199
- // );
200
- // }
201
-
202
- return matchCondition && matchRequestId;
203
- }
204
-
205
- function createWaitForXXXFunc(
206
- serviceKeyForFunction: string,
207
- paramsW: WaitForParams<SpyEvent>,
208
- functionContextAwsRequestId?: string
209
- ) {
210
- let tracker: Tracker;
211
-
212
- const promise = new Promise((resolve, reject) => {
213
- tracker = {
214
- finished: false,
215
- promiseResolve: resolve,
216
- promiseReject: reject,
217
- serviceKeyForFunction,
218
- functionContextAwsRequestId,
219
- };
220
- });
221
-
222
- return function waitForXXXFunc() {
223
- tracker.condition = paramsW?.condition;
224
-
225
- const timer = setTimeout(() => {
226
- if (tracker.finished) return;
227
- tracker.finished = true;
228
- tracker.promiseReject(
229
- new Error(
230
- `Timeout waiting for Serverless Spy message ${serviceKeyForFunction}. Received messages so far:\n ${JSON.stringify(
231
- messages,
232
- null,
233
- 2
234
- )}`
235
- )
236
- );
237
- }, paramsW?.timoutMs || 10000);
238
-
239
- promise.finally(() => {
240
- clearTimeout(timer);
241
- });
242
-
243
- if (!resolveTrackerInOldMessages(tracker)) {
244
- trackers.push(tracker);
245
- }
246
-
247
- return promise;
248
- };
249
- }
250
- }
251
-
252
- export interface WaitForParams<TSpyEvent extends SpyEvent = SpyEvent> {
253
- condition?: (event: TSpyEvent) => boolean;
254
- timoutMs?: number;
10
+ return wsListener.createProxy();
255
11
  }
256
-
257
- type Tracker = {
258
- promiseResolve: (data: any) => void;
259
- promiseReject: (data: any) => void;
260
- finished: boolean;
261
- serviceKey?: string;
262
- serviceKeyForFunction?: string;
263
- condition?: (data: any) => boolean;
264
- timoutMs?: number;
265
- functionContextAwsRequestId?: string;
266
- };
267
-
268
- type SpyMessageStorage = SpyMessage & {
269
- serviceKeyForFunction: string;
270
- functionContextAwsRequestId?: string;
271
- };
package/listener/index.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  export * from './createServerlessSpyListener';
2
- export * from './SpyListener';
2
+ export * from './ServerlessSpyListener';
package/package.json CHANGED
@@ -116,7 +116,7 @@
116
116
  "require": "./lib/index.js"
117
117
  },
118
118
  "license": "Apache-2.0",
119
- "version": "0.0.33",
119
+ "version": "0.0.34",
120
120
  "types": "lib/index.d.ts",
121
121
  "stability": "stable",
122
122
  "jsii": {
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3B5TGlzdGVuZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9saXN0ZW5lci9TcHlMaXN0ZW5lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRHluYW1vREJTcHlFdmVudCB9IGZyb20gJy4vLi4vY29tbW9uL3NweUV2ZW50cy9EeW5hbW9EQlNweUV2ZW50JztcbmltcG9ydCB7IEV2ZW50QnJpZGdlUnVsZVNweUV2ZW50IH0gZnJvbSAnLi8uLi9jb21tb24vc3B5RXZlbnRzL0V2ZW50QnJpZGdlUnVsZVNweUV2ZW50JztcbmltcG9ydCB7IEV2ZW50QnJpZGdlU3B5RXZlbnQgfSBmcm9tICcuLy4uL2NvbW1vbi9zcHlFdmVudHMvRXZlbnRCcmlkZ2VTcHlFdmVudCc7XG5pbXBvcnQgeyBGdW5jdGlvbkNvbnNvbGVTcHlFdmVudCB9IGZyb20gJy4vLi4vY29tbW9uL3NweUV2ZW50cy9GdW5jdGlvbkNvbnNvbGVTcHlFdmVudCc7XG5pbXBvcnQgeyBGdW5jdGlvblJlcXVlc3RTcHlFdmVudCB9IGZyb20gJy4vLi4vY29tbW9uL3NweUV2ZW50cy9GdW5jdGlvblJlcXVlc3RTcHlFdmVudCc7XG5pbXBvcnQgeyBGdW5jdGlvblJlc3BvbnNlU3B5RXZlbnQgfSBmcm9tICcuLy4uL2NvbW1vbi9zcHlFdmVudHMvRnVuY3Rpb25SZXNwb25zZVNweUV2ZW50JztcbmltcG9ydCB7IFMzU3B5RXZlbnQgfSBmcm9tICcuLy4uL2NvbW1vbi9zcHlFdmVudHMvUzNTcHlFdmVudCc7XG5pbXBvcnQgeyBTbnNTdWJzY3JpcHRpb25TcHlFdmVudCB9IGZyb20gJy4vLi4vY29tbW9uL3NweUV2ZW50cy9TbnNTdWJzY3JpcHRpb25TcHlFdmVudCc7XG5pbXBvcnQgeyBTbnNUb3BpY1NweUV2ZW50IH0gZnJvbSAnLi8uLi9jb21tb24vc3B5RXZlbnRzL1Nuc1RvcGljU3B5RXZlbnQnO1xuaW1wb3J0IHsgU3FzU3B5RXZlbnQgfSBmcm9tICcuLy4uL2NvbW1vbi9zcHlFdmVudHMvU3FzU3B5RXZlbnQnO1xuXG5pbXBvcnQgeyBXYWl0Rm9yUGFyYW1zIH0gZnJvbSAnLi9jcmVhdGVTZXJ2ZXJsZXNzU3B5TGlzdGVuZXInO1xuaW1wb3J0IHsgUHJldHRpZnlGb3JEaXNwbGF5IH0gZnJvbSAnLi9QcmV0dGlmeUZvckRpc3BsYXknO1xuaW1wb3J0IHtcbiAgRHluYW1vREJTcHlIYW5kbGVyLFxuICBFdmVudEJyaWRnZVNweUhhbmRsZXIsXG4gIEV2ZW50QnJpZGdlUnVsZVNweUhhbmRsZXIsXG4gIFMzU3B5SGFuZGxlcixcbiAgU25zU3Vic2NyaXB0aW9uU3B5SGFuZGxlcixcbiAgU25zVG9waWNTcHlIYW5kbGVyLFxuICBTcXNTcHlIYW5kbGVyLFxuICBGdW5jdGlvblJlcXVlc3RTcHlIYW5kbGVyLFxuICBGdW5jdGlvbkNvbnNvbGVTcHlIYW5kbGVyLFxuICBGdW5jdGlvblJlc3BvbnNlU3B5SGFuZGxlcixcbn0gZnJvbSAnLi9TcHlIYW5kbGVycy50cyc7XG5cbmV4cG9ydCB0eXBlIFNweUxpc3RlbmVyPFRTcHlFdmVudHM+ID0ge1xuICBbUCBpbiBrZXlvZiBGaWx0ZXJDb25kaXRpb25hbGx5PFRTcHlFdmVudHMsIGBEeW5hbW9EQiMke2FueX1gPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxEeW5hbW9EQlNweUV2ZW50Pj5cbiAgICA+XG4gICkgPT4gUHJvbWlzZTxEeW5hbW9EQlNweUhhbmRsZXI8VD4+O1xufSAmIHtcbiAgW1AgaW4ga2V5b2YgRmlsdGVyQ29uZGl0aW9uYWxseTxUU3B5RXZlbnRzLCBgRXZlbnRCcmlkZ2UjJHthbnl9YD4gJlxuICAgIHN0cmluZyBhcyBgd2FpdEZvciR7UH1gXTogPFQgPSBhbnk+KFxuICAgIHBhcmFtPzogUHJldHRpZnlGb3JEaXNwbGF5PFxuICAgICAgV2FpdEZvclBhcmFtczxQcmV0dGlmeUZvckRpc3BsYXk8RXZlbnRCcmlkZ2VTcHlFdmVudDxUPj4+XG4gICAgPlxuICApID0+IFByb21pc2U8RXZlbnRCcmlkZ2VTcHlIYW5kbGVyPFQ+Pjtcbn0gJiB7XG4gIFtQIGluIGtleW9mIEZpbHRlckNvbmRpdGlvbmFsbHk8VFNweUV2ZW50cywgYEV2ZW50QnJpZGdlUnVsZSMke2FueX1gPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxFdmVudEJyaWRnZVJ1bGVTcHlFdmVudDxUPj4+XG4gICAgPlxuICApID0+IFByb21pc2U8RXZlbnRCcmlkZ2VSdWxlU3B5SGFuZGxlcjxUPj47XG59ICYge1xuICBbUCBpbiBrZXlvZiBGaWx0ZXJDb25kaXRpb25hbGx5PFRTcHlFdmVudHMsIGBTMyMke2FueX1gPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiAoXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8V2FpdEZvclBhcmFtczxQcmV0dGlmeUZvckRpc3BsYXk8UzNTcHlFdmVudD4+PlxuICApID0+IFByb21pc2U8UzNTcHlIYW5kbGVyPjtcbn0gJiB7XG4gIFtQIGluIGtleW9mIEZpbHRlckNvbmRpdGlvbmFsbHk8VFNweUV2ZW50cywgYFNuc1N1YnNjcmlwdGlvbiMke2FueX1gPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxTbnNTdWJzY3JpcHRpb25TcHlFdmVudDxUPj4+XG4gICAgPlxuICApID0+IFByb21pc2U8U25zU3Vic2NyaXB0aW9uU3B5SGFuZGxlcjxUPj47XG59ICYge1xuICBbUCBpbiBrZXlvZiBGaWx0ZXJDb25kaXRpb25hbGx5PFRTcHlFdmVudHMsIGBTbnNUb3BpYyMke2FueX1gPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxTbnNUb3BpY1NweUV2ZW50PFQ+Pj5cbiAgICA+XG4gICkgPT4gUHJvbWlzZTxTbnNUb3BpY1NweUhhbmRsZXI8VD4+O1xufSAmIHtcbiAgW1AgaW4ga2V5b2YgRmlsdGVyQ29uZGl0aW9uYWxseTxUU3B5RXZlbnRzLCBgU3FzIyR7YW55fWA+ICZcbiAgICBzdHJpbmcgYXMgYHdhaXRGb3Ike1B9YF06IDxUID0gYW55PihcbiAgICBwYXJhbT86IFByZXR0aWZ5Rm9yRGlzcGxheTxcbiAgICAgIFdhaXRGb3JQYXJhbXM8UHJldHRpZnlGb3JEaXNwbGF5PFNxc1NweUV2ZW50PFQ+Pj5cbiAgICA+XG4gICkgPT4gUHJvbWlzZTxTcXNTcHlIYW5kbGVyPFQ+Pjtcbn0gJiB7XG4gIFtQIGluIGtleW9mIEZpbHRlckNvbmRpdGlvbmFsbHk8VFNweUV2ZW50cywgYEZ1bmN0aW9uIyR7YW55fSNSZXF1ZXN0YD4gJlxuICAgIHN0cmluZyBhcyBgd2FpdEZvciR7UH1gXTogPFQgPSBhbnk+KFxuICAgIHBhcmFtPzogUHJldHRpZnlGb3JEaXNwbGF5PFxuICAgICAgV2FpdEZvclBhcmFtczxQcmV0dGlmeUZvckRpc3BsYXk8RnVuY3Rpb25SZXF1ZXN0U3B5RXZlbnQ8VD4+PlxuICAgID5cbiAgKSA9PiBQcm9taXNlPEZ1bmN0aW9uUmVxdWVzdFNweUhhbmRsZXI8VD4+O1xufSAmIHtcbiAgW1AgaW4ga2V5b2YgRmlsdGVyQ29uZGl0aW9uYWxseTxUU3B5RXZlbnRzLCBgRnVuY3Rpb24jJHthbnl9I0NvbnNvbGVgPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxGdW5jdGlvbkNvbnNvbGVTcHlFdmVudDxUPj4+XG4gICAgPlxuICApID0+IFByb21pc2U8RnVuY3Rpb25Db25zb2xlU3B5SGFuZGxlcjxUPj47XG59ICYge1xuICBbUCBpbiBrZXlvZiBGaWx0ZXJDb25kaXRpb25hbGx5PFRTcHlFdmVudHMsIGBGdW5jdGlvbiMke2FueX0jUmVzcG9uc2VgPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxGdW5jdGlvblJlc3BvbnNlU3B5RXZlbnQ8VD4+PlxuICAgID4gLy8gICAgICBmaWx0ZXI/OiAoZXZlbnQ6IEZ1bmN0aW9uUmVzcG9uc2VTcHlFdmVudDxUPikgPT4gYm9vbGVhblxuICApID0+IFByb21pc2U8RnVuY3Rpb25SZXNwb25zZVNweUhhbmRsZXI8VD4+O1xufSAmIHtcbiAgc3RvcDogKCkgPT4gdm9pZDtcbn07XG5cbnR5cGUgRmlsdGVyQ29uZGl0aW9uYWxseTxTb3VyY2UsIENvbmRpdGlvbj4gPSBQaWNrPFxuICBTb3VyY2UsXG4gIHtcbiAgICBbSyBpbiBrZXlvZiBTb3VyY2VdOiBTb3VyY2VbS10gZXh0ZW5kcyBDb25kaXRpb24gPyBLIDogbmV2ZXI7XG4gIH1ba2V5b2YgU291cmNlXVxuPjtcbiJdfQ==
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3B5TGlzdGVuZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9saXN0ZW5lci9TcHlMaXN0ZW5lci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRHluYW1vREJTcHlFdmVudCB9IGZyb20gJy4vLi4vY29tbW9uL3NweUV2ZW50cy9EeW5hbW9EQlNweUV2ZW50JztcbmltcG9ydCB7IEV2ZW50QnJpZGdlUnVsZVNweUV2ZW50IH0gZnJvbSAnLi8uLi9jb21tb24vc3B5RXZlbnRzL0V2ZW50QnJpZGdlUnVsZVNweUV2ZW50JztcbmltcG9ydCB7IEV2ZW50QnJpZGdlU3B5RXZlbnQgfSBmcm9tICcuLy4uL2NvbW1vbi9zcHlFdmVudHMvRXZlbnRCcmlkZ2VTcHlFdmVudCc7XG5pbXBvcnQgeyBGdW5jdGlvbkNvbnNvbGVTcHlFdmVudCB9IGZyb20gJy4vLi4vY29tbW9uL3NweUV2ZW50cy9GdW5jdGlvbkNvbnNvbGVTcHlFdmVudCc7XG5pbXBvcnQgeyBGdW5jdGlvblJlcXVlc3RTcHlFdmVudCB9IGZyb20gJy4vLi4vY29tbW9uL3NweUV2ZW50cy9GdW5jdGlvblJlcXVlc3RTcHlFdmVudCc7XG5pbXBvcnQgeyBGdW5jdGlvblJlc3BvbnNlU3B5RXZlbnQgfSBmcm9tICcuLy4uL2NvbW1vbi9zcHlFdmVudHMvRnVuY3Rpb25SZXNwb25zZVNweUV2ZW50JztcbmltcG9ydCB7IFMzU3B5RXZlbnQgfSBmcm9tICcuLy4uL2NvbW1vbi9zcHlFdmVudHMvUzNTcHlFdmVudCc7XG5pbXBvcnQgeyBTbnNTdWJzY3JpcHRpb25TcHlFdmVudCB9IGZyb20gJy4vLi4vY29tbW9uL3NweUV2ZW50cy9TbnNTdWJzY3JpcHRpb25TcHlFdmVudCc7XG5pbXBvcnQgeyBTbnNUb3BpY1NweUV2ZW50IH0gZnJvbSAnLi8uLi9jb21tb24vc3B5RXZlbnRzL1Nuc1RvcGljU3B5RXZlbnQnO1xuaW1wb3J0IHsgU3FzU3B5RXZlbnQgfSBmcm9tICcuLy4uL2NvbW1vbi9zcHlFdmVudHMvU3FzU3B5RXZlbnQnO1xuXG5pbXBvcnQgeyBXYWl0Rm9yUGFyYW1zIH0gZnJvbSAnLi9jcmVhdGVTZXJ2ZXJsZXNzU3B5TGlzdGVuZXInO1xuaW1wb3J0IHsgUHJldHRpZnlGb3JEaXNwbGF5IH0gZnJvbSAnLi9QcmV0dGlmeUZvckRpc3BsYXknO1xuaW1wb3J0IHtcbiAgRHluYW1vREJTcHlIYW5kbGVyLFxuICBFdmVudEJyaWRnZVNweUhhbmRsZXIsXG4gIEV2ZW50QnJpZGdlUnVsZVNweUhhbmRsZXIsXG4gIFMzU3B5SGFuZGxlcixcbiAgU25zU3Vic2NyaXB0aW9uU3B5SGFuZGxlcixcbiAgU25zVG9waWNTcHlIYW5kbGVyLFxuICBTcXNTcHlIYW5kbGVyLFxuICBGdW5jdGlvblJlcXVlc3RTcHlIYW5kbGVyLFxuICBGdW5jdGlvbkNvbnNvbGVTcHlIYW5kbGVyLFxuICBGdW5jdGlvblJlc3BvbnNlU3B5SGFuZGxlcixcbn0gZnJvbSAnLi9TcHlIYW5kbGVycy50cyc7XG5cbmV4cG9ydCB0eXBlIFNweUxpc3RlbmVyPFRTcHlFdmVudHM+ID0ge1xuICBbUCBpbiBrZXlvZiBGaWx0ZXJDb25kaXRpb25hbGx5PFRTcHlFdmVudHMsIGBEeW5hbW9EQiMke2FueX1gPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxEeW5hbW9EQlNweUV2ZW50Pj5cbiAgICA+XG4gICkgPT4gUHJvbWlzZTxEeW5hbW9EQlNweUhhbmRsZXI8VD4+O1xufSAmIHtcbiAgW1AgaW4ga2V5b2YgRmlsdGVyQ29uZGl0aW9uYWxseTxUU3B5RXZlbnRzLCBgRXZlbnRCcmlkZ2UjJHthbnl9YD4gJlxuICAgIHN0cmluZyBhcyBgd2FpdEZvciR7UH1gXTogPFQgPSBhbnk+KFxuICAgIHBhcmFtPzogUHJldHRpZnlGb3JEaXNwbGF5PFxuICAgICAgV2FpdEZvclBhcmFtczxQcmV0dGlmeUZvckRpc3BsYXk8RXZlbnRCcmlkZ2VTcHlFdmVudDxUPj4+XG4gICAgPlxuICApID0+IFByb21pc2U8RXZlbnRCcmlkZ2VTcHlIYW5kbGVyPFQ+Pjtcbn0gJiB7XG4gIFtQIGluIGtleW9mIEZpbHRlckNvbmRpdGlvbmFsbHk8VFNweUV2ZW50cywgYEV2ZW50QnJpZGdlUnVsZSMke2FueX1gPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxFdmVudEJyaWRnZVJ1bGVTcHlFdmVudDxUPj4+XG4gICAgPlxuICApID0+IFByb21pc2U8RXZlbnRCcmlkZ2VSdWxlU3B5SGFuZGxlcjxUPj47XG59ICYge1xuICBbUCBpbiBrZXlvZiBGaWx0ZXJDb25kaXRpb25hbGx5PFRTcHlFdmVudHMsIGBTMyMke2FueX1gPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiAoXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8V2FpdEZvclBhcmFtczxQcmV0dGlmeUZvckRpc3BsYXk8UzNTcHlFdmVudD4+PlxuICApID0+IFByb21pc2U8UzNTcHlIYW5kbGVyPjtcbn0gJiB7XG4gIFtQIGluIGtleW9mIEZpbHRlckNvbmRpdGlvbmFsbHk8VFNweUV2ZW50cywgYFNuc1N1YnNjcmlwdGlvbiMke2FueX1gPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxTbnNTdWJzY3JpcHRpb25TcHlFdmVudDxUPj4+XG4gICAgPlxuICApID0+IFByb21pc2U8U25zU3Vic2NyaXB0aW9uU3B5SGFuZGxlcjxUPj47XG59ICYge1xuICBbUCBpbiBrZXlvZiBGaWx0ZXJDb25kaXRpb25hbGx5PFRTcHlFdmVudHMsIGBTbnNUb3BpYyMke2FueX1gPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxTbnNUb3BpY1NweUV2ZW50PFQ+Pj5cbiAgICA+XG4gICkgPT4gUHJvbWlzZTxTbnNUb3BpY1NweUhhbmRsZXI8VD4+O1xufSAmIHtcbiAgW1AgaW4ga2V5b2YgRmlsdGVyQ29uZGl0aW9uYWxseTxUU3B5RXZlbnRzLCBgU3FzIyR7YW55fWA+ICZcbiAgICBzdHJpbmcgYXMgYHdhaXRGb3Ike1B9YF06IDxUID0gYW55PihcbiAgICBwYXJhbT86IFByZXR0aWZ5Rm9yRGlzcGxheTxcbiAgICAgIFdhaXRGb3JQYXJhbXM8UHJldHRpZnlGb3JEaXNwbGF5PFNxc1NweUV2ZW50PFQ+Pj5cbiAgICA+XG4gICkgPT4gUHJvbWlzZTxTcXNTcHlIYW5kbGVyPFQ+Pjtcbn0gJiB7XG4gIFtQIGluIGtleW9mIEZpbHRlckNvbmRpdGlvbmFsbHk8VFNweUV2ZW50cywgYEZ1bmN0aW9uIyR7YW55fSNSZXF1ZXN0YD4gJlxuICAgIHN0cmluZyBhcyBgd2FpdEZvciR7UH1gXTogPFQgPSBhbnk+KFxuICAgIHBhcmFtPzogUHJldHRpZnlGb3JEaXNwbGF5PFxuICAgICAgV2FpdEZvclBhcmFtczxQcmV0dGlmeUZvckRpc3BsYXk8RnVuY3Rpb25SZXF1ZXN0U3B5RXZlbnQ8VD4+PlxuICAgID5cbiAgKSA9PiBQcm9taXNlPEZ1bmN0aW9uUmVxdWVzdFNweUhhbmRsZXI8VD4+O1xufSAmIHtcbiAgW1AgaW4ga2V5b2YgRmlsdGVyQ29uZGl0aW9uYWxseTxUU3B5RXZlbnRzLCBgRnVuY3Rpb24jJHthbnl9I0NvbnNvbGVgPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxGdW5jdGlvbkNvbnNvbGVTcHlFdmVudDxUPj4+XG4gICAgPlxuICApID0+IFByb21pc2U8RnVuY3Rpb25Db25zb2xlU3B5SGFuZGxlcjxUPj47XG59ICYge1xuICBbUCBpbiBrZXlvZiBGaWx0ZXJDb25kaXRpb25hbGx5PFRTcHlFdmVudHMsIGBGdW5jdGlvbiMke2FueX0jUmVzcG9uc2VgPiAmXG4gICAgc3RyaW5nIGFzIGB3YWl0Rm9yJHtQfWBdOiA8VCA9IGFueT4oXG4gICAgcGFyYW0/OiBQcmV0dGlmeUZvckRpc3BsYXk8XG4gICAgICBXYWl0Rm9yUGFyYW1zPFByZXR0aWZ5Rm9yRGlzcGxheTxGdW5jdGlvblJlc3BvbnNlU3B5RXZlbnQ8VD4+PlxuICAgID4gLy8gICAgICBmaWx0ZXI/OiAoZXZlbnQ6IEZ1bmN0aW9uUmVzcG9uc2VTcHlFdmVudDxUPikgPT4gYm9vbGVhblxuICApID0+IFByb21pc2U8RnVuY3Rpb25SZXNwb25zZVNweUhhbmRsZXI8VD4+O1xufSAmIHtcbiAgc3RvcDogKCkgPT4gdm9pZDtcbn07XG5cbnR5cGUgRmlsdGVyQ29uZGl0aW9uYWxseTxTb3VyY2UsIENvbmRpdGlvbj4gPSBQaWNrPFxuICBTb3VyY2UsXG4gIHtcbiAgICBbSyBpbiBrZXlvZiBTb3VyY2VdOiBTb3VyY2VbS10gZXh0ZW5kcyBDb25kaXRpb24gPyBLIDogbmV2ZXI7XG4gIH1ba2V5b2YgU291cmNlXVxuPjtcbiJdfQ==