@garrix82/reactgenie-lib 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +22 -0
- package/.github/workflows/publish.yml +20 -0
- package/LICENSE.txt +201 -0
- package/README.md +621 -0
- package/babel.config.js +29 -0
- package/dist/adapters/__tests__/expo-router-adapter.test.d.ts +1 -0
- package/dist/adapters/expo-router-adapter.d.ts +16 -0
- package/dist/adapters/expo-router-adapter.js +521 -0
- package/dist/adapters/navigation-adapter.d.ts +20 -0
- package/dist/adapters/navigation-adapter.js +137 -0
- package/dist/audio-visualizer.d.ts +14 -0
- package/dist/audio-visualizer.js +123 -0
- package/dist/current-selection.d.ts +27 -0
- package/dist/current-selection.js +94 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.js +37 -0
- package/dist/genie/DateTime.d.ts +66 -0
- package/dist/genie/DateTime.js +399 -0
- package/dist/genie/TimeDelta.d.ts +35 -0
- package/dist/genie/TimeDelta.js +169 -0
- package/dist/genie-view-wrapper.d.ts +1 -0
- package/dist/genie-view-wrapper.js +377 -0
- package/dist/hooks/__tests__/useSpeechRecognition.test.d.ts +1 -0
- package/dist/hooks/useSpeechRecognition.d.ts +28 -0
- package/dist/hooks/useSpeechRecognition.js +118 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +469 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.js +597 -0
- package/dist/logger.remote.test.d.ts +0 -0
- package/dist/modality-provider-v2.d.ts +28 -0
- package/dist/modality-provider-v2.js +1321 -0
- package/dist/modality-provider.d.ts +22 -0
- package/dist/modality-provider.js +373 -0
- package/dist/native-visibility.d.ts +28 -0
- package/dist/native-visibility.js +50 -0
- package/dist/platform/VoiceRecognitionBar.d.ts +17 -0
- package/dist/platform/VoiceRecognitionBar.js +332 -0
- package/dist/platform/components.d.ts +32 -0
- package/dist/platform/components.js +351 -0
- package/dist/platform/events.d.ts +31 -0
- package/dist/platform/events.js +274 -0
- package/dist/platform/index.d.ts +3 -0
- package/dist/platform/index.js +39 -0
- package/dist/platform/types.d.ts +79 -0
- package/dist/platform/types.js +97 -0
- package/dist/react-decorators.d.ts +87 -0
- package/dist/react-decorators.js +368 -0
- package/dist/shared-store.d.ts +74 -0
- package/dist/shared-store.js +589 -0
- package/dist/speech-recognition/__tests__/speech-recognition-groq-transport.test.d.ts +1 -0
- package/dist/speech-recognition/__tests__/speech-recognition-native.test.d.ts +1 -0
- package/dist/speech-recognition/__tests__/speech-recognition-openai-native.test.d.ts +1 -0
- package/dist/speech-recognition/__tests__/speech-recognition-openai.test.d.ts +1 -0
- package/dist/speech-recognition/__tests__/speech-recognition-unified-import.test.d.ts +0 -0
- package/dist/speech-recognition/__tests__/speech-recognition-unified.test.d.ts +1 -0
- package/dist/speech-recognition/speech-recognition-groq.d.ts +21 -0
- package/dist/speech-recognition/speech-recognition-groq.js +409 -0
- package/dist/speech-recognition/speech-recognition-mlx.d.ts +15 -0
- package/dist/speech-recognition/speech-recognition-mlx.js +393 -0
- package/dist/speech-recognition/speech-recognition-native.d.ts +24 -0
- package/dist/speech-recognition/speech-recognition-native.js +632 -0
- package/dist/speech-recognition/speech-recognition-openai-native.d.ts +40 -0
- package/dist/speech-recognition/speech-recognition-openai-native.js +653 -0
- package/dist/speech-recognition/speech-recognition-openai.d.ts +39 -0
- package/dist/speech-recognition/speech-recognition-openai.js +718 -0
- package/dist/speech-recognition/speech-recognition-unified.d.ts +93 -0
- package/dist/speech-recognition/speech-recognition-unified.js +589 -0
- package/dist/speech-recognition/utils/groq-transcription.d.ts +41 -0
- package/dist/speech-recognition/utils/groq-transcription.js +382 -0
- package/dist/speech-recognition.d.ts +7 -0
- package/dist/speech-recognition.js +61 -0
- package/dist/voice-pipeline-telemetry.d.ts +26 -0
- package/dist/voice-pipeline-telemetry.js +15 -0
- package/garrix82-reactgenie-lib-1.3.0.tgz +0 -0
- package/metro/index.js +3 -0
- package/metro/with-genie-registry.js +47 -0
- package/package.json +111 -0
- package/scripts/dry-run.js +23 -0
- package/scripts/generate-genie-registry.js +278 -0
- package/scripts/log-file-test.js +51 -0
- package/scripts/parse.js +26 -0
- package/scripts/prompt.js +19 -0
- package/scripts/set-script.js +200 -0
- package/tsconfig.json +36 -0
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.NativeSpeechRecognizer = void 0;
|
|
7
|
+
exports.getNativeSpeechCapabilities = getNativeSpeechCapabilities;
|
|
8
|
+
exports.isNativeSpeechAvailable = isNativeSpeechAvailable;
|
|
9
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
10
|
+
var _reactNative = require("react-native");
|
|
11
|
+
var _logger = _interopRequireDefault(require("../logger"));
|
|
12
|
+
var _groqTranscription = require("./utils/groq-transcription");
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
15
|
+
/**
|
|
16
|
+
* Native Speech Recognition (iOS/Android)
|
|
17
|
+
* Uses Expo Audio for recording and Groq for transcription
|
|
18
|
+
*
|
|
19
|
+
* REQUIRES: expo-audio + expo-file-system (SDK 53+)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const FileSystem = (() => {
|
|
23
|
+
try {
|
|
24
|
+
// Prefer legacy API when available (Expo SDK 54+ warns on direct legacy usage)
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
26
|
+
return require('expo-file-system/legacy');
|
|
27
|
+
} catch {
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
29
|
+
return require('expo-file-system');
|
|
30
|
+
}
|
|
31
|
+
})();
|
|
32
|
+
const missingAudioError = 'Native audio package not found. Install `expo-audio` and `expo-file-system`, then run a dev client build: npx expo install expo-audio expo-file-system';
|
|
33
|
+
function loadExpoAudioBackend() {
|
|
34
|
+
try {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
36
|
+
const expoAudio = require('expo-audio');
|
|
37
|
+
const AudioModule = expoAudio?.AudioModule;
|
|
38
|
+
const requestRecordingPermissionsAsync = expoAudio?.requestRecordingPermissionsAsync;
|
|
39
|
+
const setAudioModeAsync = expoAudio?.setAudioModeAsync;
|
|
40
|
+
if (!AudioModule || typeof requestRecordingPermissionsAsync !== 'function' || typeof setAudioModeAsync !== 'function') {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const RecordingPresets = expoAudio?.RecordingPresets;
|
|
44
|
+
const AudioQuality = expoAudio?.AudioQuality;
|
|
45
|
+
const IOSOutputFormat = expoAudio?.IOSOutputFormat;
|
|
46
|
+
const preset = RecordingPresets?.HIGH_QUALITY;
|
|
47
|
+
const iosOutputFormat = IOSOutputFormat?.MPEG4AAC ?? 'aac ';
|
|
48
|
+
const iosAudioQuality = AudioQuality?.HIGH ?? 0x60;
|
|
49
|
+
const recordingOptions = {
|
|
50
|
+
extension: preset?.extension ?? '.m4a',
|
|
51
|
+
sampleRate: 16000,
|
|
52
|
+
numberOfChannels: 1,
|
|
53
|
+
bitRate: 128000,
|
|
54
|
+
android: {
|
|
55
|
+
outputFormat: preset?.android?.outputFormat ?? 'mpeg4',
|
|
56
|
+
audioEncoder: preset?.android?.audioEncoder ?? 'aac',
|
|
57
|
+
sampleRate: 16000,
|
|
58
|
+
numberOfChannels: 1,
|
|
59
|
+
bitRate: 128000
|
|
60
|
+
},
|
|
61
|
+
ios: {
|
|
62
|
+
outputFormat: preset?.ios?.outputFormat ?? iosOutputFormat,
|
|
63
|
+
audioQuality: preset?.ios?.audioQuality ?? iosAudioQuality,
|
|
64
|
+
sampleRate: 16000,
|
|
65
|
+
numberOfChannels: 1,
|
|
66
|
+
bitRate: 128000,
|
|
67
|
+
linearPCMBitDepth: 16,
|
|
68
|
+
linearPCMIsBigEndian: false,
|
|
69
|
+
linearPCMIsFloat: false
|
|
70
|
+
},
|
|
71
|
+
web: {
|
|
72
|
+
mimeType: preset?.web?.mimeType ?? 'audio/webm',
|
|
73
|
+
bitsPerSecond: preset?.web?.bitsPerSecond ?? 128000
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
77
|
+
kind: 'expo-audio',
|
|
78
|
+
requestPermissionsAsync: requestRecordingPermissionsAsync,
|
|
79
|
+
setAudioModeAsync,
|
|
80
|
+
createRecording: () => new AudioModule.AudioRecorder(recordingOptions),
|
|
81
|
+
prepareToRecordAsync: recording => recording.prepareToRecordAsync(),
|
|
82
|
+
startAsync: async recording => {
|
|
83
|
+
recording.record();
|
|
84
|
+
},
|
|
85
|
+
stopAsync: async recording => {
|
|
86
|
+
await recording.stop();
|
|
87
|
+
return {
|
|
88
|
+
uri: recording?.uri ?? null
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
getUri: (recording, stopResult) => {
|
|
92
|
+
if (stopResult?.uri) return stopResult.uri;
|
|
93
|
+
return recording?.uri ?? null;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
} catch (e) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const audioBackend = loadExpoAudioBackend();
|
|
101
|
+
if (!audioBackend) {
|
|
102
|
+
_logger.default.error('❌ No audio backend available. Install expo-audio + expo-file-system and run a dev client build.');
|
|
103
|
+
} else {
|
|
104
|
+
_logger.default.info(`🎤 Audio backend ready: ${audioBackend.kind}`);
|
|
105
|
+
}
|
|
106
|
+
const DEFAULT_CHUNK_DURATION_MS = 2000; // Reduced from 3000ms for better responsiveness while maintaining efficiency
|
|
107
|
+
const START_RETRY_DELAY_MS = 150;
|
|
108
|
+
const POST_STOP_COOLDOWN_MS = 120;
|
|
109
|
+
const MAX_START_ATTEMPTS = 3;
|
|
110
|
+
const MAX_CONSECUTIVE_START_FAILURES = 3;
|
|
111
|
+
const MIN_AUDIO_SIZE_FLOOR_BYTES = 256;
|
|
112
|
+
/**
|
|
113
|
+
* Native speech recognizer for iOS/Android using expo-audio
|
|
114
|
+
*/
|
|
115
|
+
const NativeSpeechRecognizer = ({
|
|
116
|
+
shouldListen,
|
|
117
|
+
speechStatusCallback,
|
|
118
|
+
speechResultCallback,
|
|
119
|
+
speechTranslationCallback,
|
|
120
|
+
clientSecret,
|
|
121
|
+
groqApiBaseUrl,
|
|
122
|
+
groqTranscriptionEndpoint,
|
|
123
|
+
stream = true,
|
|
124
|
+
language = 'en',
|
|
125
|
+
onError,
|
|
126
|
+
minAudioSize = 500
|
|
127
|
+
}) => {
|
|
128
|
+
const [isRecording, setIsRecording] = (0, _react.useState)(false);
|
|
129
|
+
const activeRecordingRef = (0, _react.useRef)(null);
|
|
130
|
+
const stopInFlightRef = (0, _react.useRef)(null);
|
|
131
|
+
const chunkLoopAbortRef = (0, _react.useRef)(null);
|
|
132
|
+
const streamAbortRef = (0, _react.useRef)(null);
|
|
133
|
+
const activeChunksRef = (0, _react.useRef)(new Set());
|
|
134
|
+
const committedTranscriptRef = (0, _react.useRef)('');
|
|
135
|
+
const currentChunkTranscriptRef = (0, _react.useRef)('');
|
|
136
|
+
const lastInterimRef = (0, _react.useRef)('');
|
|
137
|
+
(0, _react.useEffect)(() => {
|
|
138
|
+
if (clientSecret) {
|
|
139
|
+
_logger.default.debug('Groq client initialized (native)');
|
|
140
|
+
}
|
|
141
|
+
}, [clientSecret]);
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Request audio permissions
|
|
145
|
+
*/
|
|
146
|
+
const requestPermissions = async () => {
|
|
147
|
+
try {
|
|
148
|
+
if (!audioBackend) {
|
|
149
|
+
throw new Error(missingAudioError);
|
|
150
|
+
}
|
|
151
|
+
_logger.default.info('Requesting microphone permissions...');
|
|
152
|
+
const {
|
|
153
|
+
status,
|
|
154
|
+
granted
|
|
155
|
+
} = await audioBackend.requestPermissionsAsync();
|
|
156
|
+
const isGranted = status === 'granted' || granted === true;
|
|
157
|
+
if (!isGranted) {
|
|
158
|
+
throw new Error('Audio permission not granted. Please enable microphone access in your device settings.');
|
|
159
|
+
}
|
|
160
|
+
_logger.default.info('✅ Microphone permission granted');
|
|
161
|
+
return true;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
_logger.default.error('Permission error:', error);
|
|
164
|
+
if (onError) {
|
|
165
|
+
onError(error);
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
const resetTranscriptState = (0, _react.useCallback)(() => {
|
|
171
|
+
committedTranscriptRef.current = '';
|
|
172
|
+
currentChunkTranscriptRef.current = '';
|
|
173
|
+
lastInterimRef.current = '';
|
|
174
|
+
}, []);
|
|
175
|
+
const commitChunk = (0, _react.useCallback)(chunkText => {
|
|
176
|
+
const combined = (0, _groqTranscription.mergeTranscriptParts)(committedTranscriptRef.current, chunkText);
|
|
177
|
+
committedTranscriptRef.current = combined;
|
|
178
|
+
currentChunkTranscriptRef.current = '';
|
|
179
|
+
}, []);
|
|
180
|
+
const stopRecorderSafely = (0, _react.useCallback)(async (recording, reason) => {
|
|
181
|
+
if (!audioBackend || !recording) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
const inFlight = stopInFlightRef.current;
|
|
185
|
+
if (inFlight && inFlight.recording === recording) {
|
|
186
|
+
_logger.default.debug(`[NativeSR] ♻️ Reusing in-flight stop (${reason})`);
|
|
187
|
+
return inFlight.promise;
|
|
188
|
+
}
|
|
189
|
+
const stopPromise = (async () => {
|
|
190
|
+
try {
|
|
191
|
+
const stopResult = await audioBackend.stopAsync(recording);
|
|
192
|
+
return audioBackend.getUri(recording, stopResult);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
if (isBenignStopError(error)) {
|
|
195
|
+
_logger.default.debug(`[NativeSR] Stop already finalized (${reason})`);
|
|
196
|
+
return audioBackend.getUri(recording);
|
|
197
|
+
}
|
|
198
|
+
throw error;
|
|
199
|
+
} finally {
|
|
200
|
+
if (stopInFlightRef.current?.recording === recording) {
|
|
201
|
+
stopInFlightRef.current = null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
})();
|
|
205
|
+
stopInFlightRef.current = {
|
|
206
|
+
recording,
|
|
207
|
+
promise: stopPromise
|
|
208
|
+
};
|
|
209
|
+
return stopPromise;
|
|
210
|
+
}, []);
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Process recorded audio chunk and send to Groq (streaming)
|
|
214
|
+
*/
|
|
215
|
+
const processAudioChunk = (0, _react.useCallback)(async fileUri => {
|
|
216
|
+
let normalizedUri;
|
|
217
|
+
try {
|
|
218
|
+
_logger.default.debug('[NativeSR] 🔍 Processing audio chunk:', fileUri);
|
|
219
|
+
normalizedUri = normalizeFileUri(fileUri);
|
|
220
|
+
_logger.default.debug('[NativeSR] 📁 Normalized URI:', normalizedUri);
|
|
221
|
+
|
|
222
|
+
// Read file
|
|
223
|
+
_logger.default.debug('[NativeSR] 📖 Reading file info...');
|
|
224
|
+
const fileInfo = await FileSystem.getInfoAsync(normalizedUri);
|
|
225
|
+
_logger.default.debug('[NativeSR] 📊 File info:', JSON.stringify(fileInfo));
|
|
226
|
+
if (!fileInfo.exists) {
|
|
227
|
+
_logger.default.error('[NativeSR] ❌ Audio file missing:', normalizedUri);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const fileSize = 'size' in fileInfo && typeof fileInfo.size === 'number' ? fileInfo.size : null;
|
|
231
|
+
const effectiveMinAudioSize = Math.max(minAudioSize, MIN_AUDIO_SIZE_FLOOR_BYTES);
|
|
232
|
+
if (fileSize !== null && fileSize < effectiveMinAudioSize) {
|
|
233
|
+
_logger.default.warn(`[NativeSR] ⚠️ Audio file too small (${fileSize} bytes, min: ${effectiveMinAudioSize})`);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
_logger.default.info(`[NativeSR] 📤 Uploading ${fileSize ?? 'unknown'} bytes to Groq...`);
|
|
237
|
+
const fileName = getFileNameFromUri(normalizedUri);
|
|
238
|
+
const mimeType = getMimeTypeFromName(fileName);
|
|
239
|
+
const controller = new AbortController();
|
|
240
|
+
streamAbortRef.current = controller;
|
|
241
|
+
const chunkText = await (0, _groqTranscription.streamGroqTranscription)({
|
|
242
|
+
apiKey: clientSecret,
|
|
243
|
+
audioBlob: {
|
|
244
|
+
uri: normalizedUri,
|
|
245
|
+
name: fileName,
|
|
246
|
+
type: mimeType
|
|
247
|
+
},
|
|
248
|
+
mimeType,
|
|
249
|
+
language,
|
|
250
|
+
responseFormat: 'verbose_json',
|
|
251
|
+
stream,
|
|
252
|
+
endpoint: resolveGroqTranscriptionEndpoint(groqApiBaseUrl, groqTranscriptionEndpoint),
|
|
253
|
+
signal: controller.signal,
|
|
254
|
+
onInterim: text => {
|
|
255
|
+
if (!text) return;
|
|
256
|
+
_logger.default.debug(`[NativeSR] 📝 Interim chunk text (${text.length} chars)`);
|
|
257
|
+
currentChunkTranscriptRef.current = text;
|
|
258
|
+
|
|
259
|
+
// Real-time callback like web version
|
|
260
|
+
const combined = (0, _groqTranscription.mergeTranscriptParts)(committedTranscriptRef.current, text);
|
|
261
|
+
if (combined !== lastInterimRef.current) {
|
|
262
|
+
lastInterimRef.current = combined;
|
|
263
|
+
speechStatusCallback(true, combined);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
if (chunkText) {
|
|
268
|
+
_logger.default.info(`[NativeSR] ✅ Chunk transcription complete (${chunkText.length} chars)`);
|
|
269
|
+
commitChunk(chunkText);
|
|
270
|
+
// Final update after chunk completes
|
|
271
|
+
const finalCombined = (0, _groqTranscription.mergeTranscriptParts)(committedTranscriptRef.current, '');
|
|
272
|
+
if (finalCombined !== lastInterimRef.current) {
|
|
273
|
+
lastInterimRef.current = finalCombined;
|
|
274
|
+
speechStatusCallback(true, finalCombined);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
} catch (error) {
|
|
278
|
+
_logger.default.error('[NativeSR] Groq transcription error:', error);
|
|
279
|
+
if (onError) {
|
|
280
|
+
onError(error);
|
|
281
|
+
}
|
|
282
|
+
} finally {
|
|
283
|
+
try {
|
|
284
|
+
if (normalizedUri) {
|
|
285
|
+
await FileSystem.deleteAsync(normalizedUri, {
|
|
286
|
+
idempotent: true
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
} catch (error) {
|
|
290
|
+
_logger.default.warn('[NativeSR] Failed to delete temp audio file:', error);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}, [commitChunk, clientSecret, groqApiBaseUrl, groqTranscriptionEndpoint, stream, language, minAudioSize, onError]);
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Start chunked recording loop for streaming transcription
|
|
297
|
+
*/
|
|
298
|
+
const startChunkLoop = (0, _react.useCallback)(async () => {
|
|
299
|
+
if (!audioBackend) return;
|
|
300
|
+
const controller = new AbortController();
|
|
301
|
+
chunkLoopAbortRef.current = controller;
|
|
302
|
+
_logger.default.info(`[NativeSR] 🔄 Chunk loop started (${DEFAULT_CHUNK_DURATION_MS}ms chunks)`);
|
|
303
|
+
let chunkCount = 0;
|
|
304
|
+
let consecutiveStartFailures = 0;
|
|
305
|
+
while (!controller.signal.aborted) {
|
|
306
|
+
chunkCount++;
|
|
307
|
+
_logger.default.debug(`[NativeSR] 📼 Recording chunk #${chunkCount}...`);
|
|
308
|
+
let recording = null;
|
|
309
|
+
let started = false;
|
|
310
|
+
for (let attempt = 1; attempt <= MAX_START_ATTEMPTS; attempt++) {
|
|
311
|
+
if (controller.signal.aborted) {
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
recording = audioBackend.createRecording();
|
|
315
|
+
activeRecordingRef.current = recording;
|
|
316
|
+
try {
|
|
317
|
+
await audioBackend.prepareToRecordAsync(recording);
|
|
318
|
+
await audioBackend.startAsync(recording);
|
|
319
|
+
_logger.default.debug(`[NativeSR] 🎙️ Chunk #${chunkCount} recording...`);
|
|
320
|
+
started = true;
|
|
321
|
+
break;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
_logger.default.error(`Failed to start chunk #${chunkCount} recording (attempt ${attempt}/${MAX_START_ATTEMPTS}):`, error);
|
|
324
|
+
activeRecordingRef.current = null;
|
|
325
|
+
if (attempt < MAX_START_ATTEMPTS) {
|
|
326
|
+
await sleep(START_RETRY_DELAY_MS);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (!started || !recording) {
|
|
331
|
+
consecutiveStartFailures += 1;
|
|
332
|
+
_logger.default.warn(`[NativeSR] ⚠️ Chunk start failed (${consecutiveStartFailures}/${MAX_CONSECUTIVE_START_FAILURES}).`);
|
|
333
|
+
if (consecutiveStartFailures >= MAX_CONSECUTIVE_START_FAILURES) {
|
|
334
|
+
_logger.default.error('[NativeSR] ❌ Too many consecutive chunk start failures; ending loop.');
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
await sleep(START_RETRY_DELAY_MS);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
consecutiveStartFailures = 0;
|
|
341
|
+
await sleep(DEFAULT_CHUNK_DURATION_MS);
|
|
342
|
+
if (controller.signal.aborted) {
|
|
343
|
+
_logger.default.info('[NativeSR] 🛑 Chunk loop aborted');
|
|
344
|
+
const stillActive = activeRecordingRef.current === recording;
|
|
345
|
+
if (stillActive) {
|
|
346
|
+
try {
|
|
347
|
+
const uri = await stopRecorderSafely(recording, `abort chunk #${chunkCount}`);
|
|
348
|
+
if (uri) {
|
|
349
|
+
_logger.default.info(`[NativeSR] ✅ Final chunk URI (abort): ${uri.substring(0, 50)}...`);
|
|
350
|
+
const chunkPromise = processAudioChunk(uri).catch(error => {
|
|
351
|
+
_logger.default.error('[NativeSR] Failed to process aborted chunk:', error);
|
|
352
|
+
}).finally(() => {
|
|
353
|
+
activeChunksRef.current.delete(chunkPromise);
|
|
354
|
+
});
|
|
355
|
+
activeChunksRef.current.add(chunkPromise);
|
|
356
|
+
} else {
|
|
357
|
+
_logger.default.warn('[NativeSR] ⚠️ No URI for aborted chunk - skipping upload');
|
|
358
|
+
}
|
|
359
|
+
} catch (error) {
|
|
360
|
+
_logger.default.warn('[NativeSR] Failed to stop chunk recording on abort:', error);
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
_logger.default.debug('[NativeSR] Abort detected after external stop; skipping duplicate stop');
|
|
364
|
+
}
|
|
365
|
+
activeRecordingRef.current = null;
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
_logger.default.debug(`[NativeSR] ⏹️ Stopping chunk #${chunkCount}...`);
|
|
370
|
+
const uri = await stopRecorderSafely(recording, `chunk #${chunkCount}`);
|
|
371
|
+
_logger.default.debug(`[NativeSR] ✅ Chunk #${chunkCount} stopped`);
|
|
372
|
+
activeRecordingRef.current = null;
|
|
373
|
+
if (uri) {
|
|
374
|
+
_logger.default.info(`[NativeSR] ✅ Chunk #${chunkCount} URI: ${uri.substring(0, 50)}...`);
|
|
375
|
+
// Process chunks truly in parallel - track active chunks
|
|
376
|
+
const chunkPromise = processAudioChunk(uri).catch(error => {
|
|
377
|
+
_logger.default.error(`[NativeSR] Failed to process chunk #${chunkCount}:`, error);
|
|
378
|
+
}).finally(() => {
|
|
379
|
+
activeChunksRef.current.delete(chunkPromise);
|
|
380
|
+
});
|
|
381
|
+
activeChunksRef.current.add(chunkPromise);
|
|
382
|
+
} else {
|
|
383
|
+
_logger.default.warn(`[NativeSR] ⚠️ No URI for chunk #${chunkCount} - skipping upload`);
|
|
384
|
+
}
|
|
385
|
+
} catch (error) {
|
|
386
|
+
_logger.default.error(`[NativeSR] Failed to stop chunk #${chunkCount}:`, error);
|
|
387
|
+
activeRecordingRef.current = null;
|
|
388
|
+
continue; // Skip this chunk, try next one
|
|
389
|
+
}
|
|
390
|
+
if (!controller.signal.aborted) {
|
|
391
|
+
await sleep(POST_STOP_COOLDOWN_MS);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
_logger.default.info(`[NativeSR] 🏁 Chunk loop ended (processed ${chunkCount} chunks)`);
|
|
395
|
+
}, [processAudioChunk, stopRecorderSafely]);
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Start audio recording
|
|
399
|
+
*/
|
|
400
|
+
const startRecording = (0, _react.useCallback)(async () => {
|
|
401
|
+
try {
|
|
402
|
+
if (!audioBackend) {
|
|
403
|
+
throw new Error(missingAudioError);
|
|
404
|
+
}
|
|
405
|
+
_logger.default.info(`[NativeSR] Start recording (backend=${audioBackend.kind})`);
|
|
406
|
+
|
|
407
|
+
// Request permissions
|
|
408
|
+
const hasPermission = await requestPermissions();
|
|
409
|
+
if (!hasPermission) {
|
|
410
|
+
_logger.default.warn('[NativeSR] Permission denied');
|
|
411
|
+
speechStatusCallback(false, '');
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
await audioBackend.setAudioModeAsync({
|
|
415
|
+
allowsRecording: true,
|
|
416
|
+
playsInSilentMode: true,
|
|
417
|
+
shouldPlayInBackground: false,
|
|
418
|
+
shouldRouteThroughEarpiece: false,
|
|
419
|
+
interruptionMode: 'duckOthers'
|
|
420
|
+
});
|
|
421
|
+
_logger.default.debug('[NativeSR] Audio mode configured');
|
|
422
|
+
resetTranscriptState();
|
|
423
|
+
setIsRecording(true);
|
|
424
|
+
speechStatusCallback(true, '');
|
|
425
|
+
_logger.default.info('[NativeSR] 🎤 Native recording started');
|
|
426
|
+
startChunkLoop();
|
|
427
|
+
} catch (error) {
|
|
428
|
+
_logger.default.error('[NativeSR] Failed to start native recording:', error);
|
|
429
|
+
if (onError) {
|
|
430
|
+
onError(error);
|
|
431
|
+
}
|
|
432
|
+
speechStatusCallback(false, '');
|
|
433
|
+
}
|
|
434
|
+
}, [speechStatusCallback, onError, resetTranscriptState, startChunkLoop]);
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Stop audio recording
|
|
438
|
+
*/
|
|
439
|
+
const stopRecording = (0, _react.useCallback)(async () => {
|
|
440
|
+
if (!isRecording || !audioBackend) return;
|
|
441
|
+
try {
|
|
442
|
+
_logger.default.info('[NativeSR] Stop recording requested');
|
|
443
|
+
if (chunkLoopAbortRef.current) {
|
|
444
|
+
chunkLoopAbortRef.current.abort();
|
|
445
|
+
chunkLoopAbortRef.current = null;
|
|
446
|
+
}
|
|
447
|
+
if (activeRecordingRef.current) {
|
|
448
|
+
const currentRecording = activeRecordingRef.current;
|
|
449
|
+
activeRecordingRef.current = null;
|
|
450
|
+
try {
|
|
451
|
+
_logger.default.info('[NativeSR] ⏹️ Stopping final active recording...');
|
|
452
|
+
const uri = await stopRecorderSafely(currentRecording, 'explicit stop');
|
|
453
|
+
if (uri) {
|
|
454
|
+
_logger.default.info('[NativeSR] ✅ Final recording URI:', uri);
|
|
455
|
+
const chunkPromise = processAudioChunk(uri).catch(error => {
|
|
456
|
+
_logger.default.error('[NativeSR] Failed to process final chunk:', error);
|
|
457
|
+
}).finally(() => {
|
|
458
|
+
activeChunksRef.current.delete(chunkPromise);
|
|
459
|
+
});
|
|
460
|
+
activeChunksRef.current.add(chunkPromise);
|
|
461
|
+
} else {
|
|
462
|
+
_logger.default.warn('[NativeSR] ⚠️ No URI from final recording');
|
|
463
|
+
}
|
|
464
|
+
} catch (error) {
|
|
465
|
+
_logger.default.warn('[NativeSR] Failed to stop active recording:', error);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
setIsRecording(false);
|
|
469
|
+
|
|
470
|
+
// Wait for all active chunks to complete (truly parallel)
|
|
471
|
+
try {
|
|
472
|
+
_logger.default.info(`[NativeSR] ⏳ Waiting for ${activeChunksRef.current.size} active chunks to complete...`);
|
|
473
|
+
await Promise.all(Array.from(activeChunksRef.current));
|
|
474
|
+
_logger.default.info('[NativeSR] ✅ All chunks processed');
|
|
475
|
+
} catch (error) {
|
|
476
|
+
_logger.default.warn('[NativeSR] Some chunks failed:', error);
|
|
477
|
+
}
|
|
478
|
+
const finalText = committedTranscriptRef.current || currentChunkTranscriptRef.current;
|
|
479
|
+
if (speechTranslationCallback && finalText && language && !language.startsWith('en')) {
|
|
480
|
+
try {
|
|
481
|
+
const translated = await (0, _groqTranscription.translateGroqText)({
|
|
482
|
+
apiKey: clientSecret,
|
|
483
|
+
text: finalText,
|
|
484
|
+
language,
|
|
485
|
+
endpoint: resolveGroqChatEndpoint(groqApiBaseUrl)
|
|
486
|
+
});
|
|
487
|
+
if (translated) {
|
|
488
|
+
speechTranslationCallback(translated);
|
|
489
|
+
}
|
|
490
|
+
} catch (error) {
|
|
491
|
+
_logger.default.warn('[NativeSR] Groq translation failed:', error);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
_logger.default.info(`[NativeSR] Final text length: ${finalText.length}`);
|
|
495
|
+
speechStatusCallback(false, '');
|
|
496
|
+
if (finalText) {
|
|
497
|
+
speechResultCallback(finalText);
|
|
498
|
+
}
|
|
499
|
+
resetTranscriptState();
|
|
500
|
+
} catch (error) {
|
|
501
|
+
_logger.default.error('[NativeSR] Failed to stop native recording:', error);
|
|
502
|
+
if (onError) {
|
|
503
|
+
onError(error);
|
|
504
|
+
}
|
|
505
|
+
speechStatusCallback(false, '');
|
|
506
|
+
}
|
|
507
|
+
}, [isRecording, clientSecret, groqApiBaseUrl, language, onError, processAudioChunk, resetTranscriptState, speechResultCallback, speechStatusCallback, speechTranslationCallback, stopRecorderSafely]);
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Handle shouldListen prop changes
|
|
511
|
+
*/
|
|
512
|
+
(0, _react.useEffect)(() => {
|
|
513
|
+
_logger.default.debug(`[NativeSR] shouldListen=${shouldListen} isRecording=${isRecording}`);
|
|
514
|
+
if (shouldListen && !isRecording) {
|
|
515
|
+
startRecording();
|
|
516
|
+
} else if (!shouldListen && isRecording) {
|
|
517
|
+
stopRecording();
|
|
518
|
+
}
|
|
519
|
+
}, [shouldListen, isRecording, startRecording, stopRecording]);
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Cleanup on unmount
|
|
523
|
+
*/
|
|
524
|
+
(0, _react.useEffect)(() => {
|
|
525
|
+
return () => {
|
|
526
|
+
if (chunkLoopAbortRef.current) {
|
|
527
|
+
_logger.default.info('[NativeSR] Cleanup: aborting chunk loop');
|
|
528
|
+
chunkLoopAbortRef.current.abort();
|
|
529
|
+
}
|
|
530
|
+
if (streamAbortRef.current) {
|
|
531
|
+
_logger.default.info('[NativeSR] Cleanup: aborting stream');
|
|
532
|
+
streamAbortRef.current.abort();
|
|
533
|
+
}
|
|
534
|
+
if (activeRecordingRef.current && audioBackend) {
|
|
535
|
+
_logger.default.info('[NativeSR] Cleanup: stopping active recording');
|
|
536
|
+
stopRecorderSafely(activeRecordingRef.current, 'cleanup').catch(() => undefined);
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
}, [stopRecorderSafely]);
|
|
540
|
+
return /*#__PURE__*/_react.default.createElement(_reactNative.View, null);
|
|
541
|
+
};
|
|
542
|
+
exports.NativeSpeechRecognizer = NativeSpeechRecognizer;
|
|
543
|
+
function sleep(ms) {
|
|
544
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
545
|
+
}
|
|
546
|
+
function isBenignStopError(error) {
|
|
547
|
+
const message = error instanceof Error ? error.message : typeof error === 'string' ? error : '';
|
|
548
|
+
return /stop failed|already|not recording/i.test(message);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Convert base64 string to Blob
|
|
553
|
+
*/
|
|
554
|
+
function getFileNameFromUri(uri) {
|
|
555
|
+
const cleanUri = uri.split('?')[0];
|
|
556
|
+
const lastSlash = cleanUri.lastIndexOf('/');
|
|
557
|
+
const fileName = lastSlash >= 0 ? cleanUri.slice(lastSlash + 1) : cleanUri;
|
|
558
|
+
if (fileName && fileName.includes('.')) {
|
|
559
|
+
return fileName;
|
|
560
|
+
}
|
|
561
|
+
return `audio-${Date.now()}.m4a`;
|
|
562
|
+
}
|
|
563
|
+
function getMimeTypeFromName(name) {
|
|
564
|
+
const ext = name.split('.').pop()?.toLowerCase() || '';
|
|
565
|
+
switch (ext) {
|
|
566
|
+
case 'm4a':
|
|
567
|
+
return 'audio/m4a';
|
|
568
|
+
case 'aac':
|
|
569
|
+
return 'audio/aac';
|
|
570
|
+
case 'mp3':
|
|
571
|
+
return 'audio/mpeg';
|
|
572
|
+
case 'mp4':
|
|
573
|
+
return 'audio/mp4';
|
|
574
|
+
case 'wav':
|
|
575
|
+
return 'audio/wav';
|
|
576
|
+
case 'webm':
|
|
577
|
+
return 'audio/webm';
|
|
578
|
+
case 'ogg':
|
|
579
|
+
return 'audio/ogg';
|
|
580
|
+
default:
|
|
581
|
+
return 'audio/m4a';
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
function resolveGroqTranscriptionEndpoint(baseUrl, overrideEndpoint) {
|
|
585
|
+
if (overrideEndpoint) return overrideEndpoint;
|
|
586
|
+
if (!baseUrl) return undefined;
|
|
587
|
+
const trimmed = baseUrl.replace(/\/+$/, '');
|
|
588
|
+
if (trimmed.endsWith('/openai/v1')) {
|
|
589
|
+
return `${trimmed}/audio/transcriptions`;
|
|
590
|
+
}
|
|
591
|
+
return `${trimmed}/openai/v1/audio/transcriptions`;
|
|
592
|
+
}
|
|
593
|
+
function resolveGroqChatEndpoint(baseUrl) {
|
|
594
|
+
if (!baseUrl) return undefined;
|
|
595
|
+
const trimmed = baseUrl.replace(/\/+$/, '');
|
|
596
|
+
if (trimmed.endsWith('/openai/v1')) {
|
|
597
|
+
return `${trimmed}/chat/completions`;
|
|
598
|
+
}
|
|
599
|
+
return `${trimmed}/openai/v1/chat/completions`;
|
|
600
|
+
}
|
|
601
|
+
function normalizeFileUri(uri) {
|
|
602
|
+
if (!uri) return uri;
|
|
603
|
+
if (uri.startsWith('file://') || uri.startsWith('content://')) {
|
|
604
|
+
return uri;
|
|
605
|
+
}
|
|
606
|
+
if (uri.startsWith('/')) {
|
|
607
|
+
return `file://${uri}`;
|
|
608
|
+
}
|
|
609
|
+
return uri;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Check if native speech recognition is available
|
|
614
|
+
*/
|
|
615
|
+
function isNativeSpeechAvailable() {
|
|
616
|
+
return (_reactNative.Platform.OS === 'ios' || _reactNative.Platform.OS === 'android') && !!audioBackend;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Get native speech capabilities
|
|
621
|
+
*/
|
|
622
|
+
function getNativeSpeechCapabilities() {
|
|
623
|
+
return {
|
|
624
|
+
available: isNativeSpeechAvailable(),
|
|
625
|
+
platform: _reactNative.Platform.OS,
|
|
626
|
+
supportsVAD: true,
|
|
627
|
+
supportsAudioLevels: true,
|
|
628
|
+
requiresExpoAudio: true,
|
|
629
|
+
backend: audioBackend?.kind ?? 'none'
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVXaWxkY2FyZCIsInJlcXVpcmUiLCJfcmVhY3ROYXRpdmUiLCJfbG9nZ2VyIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIl9ncm9xVHJhbnNjcmlwdGlvbiIsImUiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsInQiLCJXZWFrTWFwIiwiciIsIm4iLCJvIiwiaSIsImYiLCJfX3Byb3RvX18iLCJoYXMiLCJnZXQiLCJzZXQiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsIkZpbGVTeXN0ZW0iLCJtaXNzaW5nQXVkaW9FcnJvciIsImxvYWRFeHBvQXVkaW9CYWNrZW5kIiwiZXhwb0F1ZGlvIiwiQXVkaW9Nb2R1bGUiLCJyZXF1ZXN0UmVjb3JkaW5nUGVybWlzc2lvbnNBc3luYyIsInNldEF1ZGlvTW9kZUFzeW5jIiwiUmVjb3JkaW5nUHJlc2V0cyIsIkF1ZGlvUXVhbGl0eSIsIklPU091dHB1dEZvcm1hdCIsInByZXNldCIsIkhJR0hfUVVBTElUWSIsImlvc091dHB1dEZvcm1hdCIsIk1QRUc0QUFDIiwiaW9zQXVkaW9RdWFsaXR5IiwiSElHSCIsInJlY29yZGluZ09wdGlvbnMiLCJleHRlbnNpb24iLCJzYW1wbGVSYXRlIiwibnVtYmVyT2ZDaGFubmVscyIsImJpdFJhdGUiLCJhbmRyb2lkIiwib3V0cHV0Rm9ybWF0IiwiYXVkaW9FbmNvZGVyIiwiaW9zIiwiYXVkaW9RdWFsaXR5IiwibGluZWFyUENNQml0RGVwdGgiLCJsaW5lYXJQQ01Jc0JpZ0VuZGlhbiIsImxpbmVhclBDTUlzRmxvYXQiLCJ3ZWIiLCJtaW1lVHlwZSIsImJpdHNQZXJTZWNvbmQiLCJraW5kIiwicmVxdWVzdFBlcm1pc3Npb25zQXN5bmMiLCJjcmVhdGVSZWNvcmRpbmciLCJBdWRpb1JlY29yZGVyIiwicHJlcGFyZVRvUmVjb3JkQXN5bmMiLCJyZWNvcmRpbmciLCJzdGFydEFzeW5jIiwicmVjb3JkIiwic3RvcEFzeW5jIiwic3RvcCIsInVyaSIsImdldFVyaSIsInN0b3BSZXN1bHQiLCJhdWRpb0JhY2tlbmQiLCJsb2dnZXIiLCJlcnJvciIsImluZm8iLCJERUZBVUxUX0NIVU5LX0RVUkFUSU9OX01TIiwiU1RBUlRfUkVUUllfREVMQVlfTVMiLCJQT1NUX1NUT1BfQ09PTERPV05fTVMiLCJNQVhfU1RBUlRfQVRURU1QVFMiLCJNQVhfQ09OU0VDVVRJVkVfU1RBUlRfRkFJTFVSRVMiLCJNSU5fQVVESU9fU0laRV9GTE9PUl9CWVRFUyIsIk5hdGl2ZVNwZWVjaFJlY29nbml6ZXIiLCJzaG91bGRMaXN0ZW4iLCJzcGVlY2hTdGF0dXNDYWxsYmFjayIsInNwZWVjaFJlc3VsdENhbGxiYWNrIiwic3BlZWNoVHJhbnNsYXRpb25DYWxsYmFjayIsImNsaWVudFNlY3JldCIsImdyb3FBcGlCYXNlVXJsIiwiZ3JvcVRyYW5zY3JpcHRpb25FbmRwb2ludCIsInN0cmVhbSIsImxhbmd1YWdlIiwib25FcnJvciIsIm1pbkF1ZGlvU2l6ZSIsImlzUmVjb3JkaW5nIiwic2V0SXNSZWNvcmRpbmciLCJ1c2VTdGF0ZSIsImFjdGl2ZVJlY29yZGluZ1JlZiIsInVzZVJlZiIsInN0b3BJbkZsaWdodFJlZiIsImNodW5rTG9vcEFib3J0UmVmIiwic3RyZWFtQWJvcnRSZWYiLCJhY3RpdmVDaHVua3NSZWYiLCJTZXQiLCJjb21taXR0ZWRUcmFuc2NyaXB0UmVmIiwiY3VycmVudENodW5rVHJhbnNjcmlwdFJlZiIsImxhc3RJbnRlcmltUmVmIiwidXNlRWZmZWN0IiwiZGVidWciLCJyZXF1ZXN0UGVybWlzc2lvbnMiLCJFcnJvciIsInN0YXR1cyIsImdyYW50ZWQiLCJpc0dyYW50ZWQiLCJyZXNldFRyYW5zY3JpcHRTdGF0ZSIsInVzZUNhbGxiYWNrIiwiY3VycmVudCIsImNvbW1pdENodW5rIiwiY2h1bmtUZXh0IiwiY29tYmluZWQiLCJtZXJnZVRyYW5zY3JpcHRQYXJ0cyIsInN0b3BSZWNvcmRlclNhZmVseSIsInJlYXNvbiIsImluRmxpZ2h0IiwicHJvbWlzZSIsInN0b3BQcm9taXNlIiwiaXNCZW5pZ25TdG9wRXJyb3IiLCJwcm9jZXNzQXVkaW9DaHVuayIsImZpbGVVcmkiLCJub3JtYWxpemVkVXJpIiwibm9ybWFsaXplRmlsZVVyaSIsImZpbGVJbmZvIiwiZ2V0SW5mb0FzeW5jIiwiSlNPTiIsInN0cmluZ2lmeSIsImV4aXN0cyIsImZpbGVTaXplIiwic2l6ZSIsImVmZmVjdGl2ZU1pbkF1ZGlvU2l6ZSIsIk1hdGgiLCJtYXgiLCJ3YXJuIiwiZmlsZU5hbWUiLCJnZXRGaWxlTmFtZUZyb21VcmkiLCJnZXRNaW1lVHlwZUZyb21OYW1lIiwiY29udHJvbGxlciIsIkFib3J0Q29udHJvbGxlciIsInN0cmVhbUdyb3FUcmFuc2NyaXB0aW9uIiwiYXBpS2V5IiwiYXVkaW9CbG9iIiwibmFtZSIsInR5cGUiLCJyZXNwb25zZUZvcm1hdCIsImVuZHBvaW50IiwicmVzb2x2ZUdyb3FUcmFuc2NyaXB0aW9uRW5kcG9pbnQiLCJzaWduYWwiLCJvbkludGVyaW0iLCJ0ZXh0IiwibGVuZ3RoIiwiZmluYWxDb21iaW5lZCIsImRlbGV0ZUFzeW5jIiwiaWRlbXBvdGVudCIsInN0YXJ0Q2h1bmtMb29wIiwiY2h1bmtDb3VudCIsImNvbnNlY3V0aXZlU3RhcnRGYWlsdXJlcyIsImFib3J0ZWQiLCJzdGFydGVkIiwiYXR0ZW1wdCIsInNsZWVwIiwic3RpbGxBY3RpdmUiLCJzdWJzdHJpbmciLCJjaHVua1Byb21pc2UiLCJjYXRjaCIsImZpbmFsbHkiLCJkZWxldGUiLCJhZGQiLCJzdGFydFJlY29yZGluZyIsImhhc1Blcm1pc3Npb24iLCJhbGxvd3NSZWNvcmRpbmciLCJwbGF5c0luU2lsZW50TW9kZSIsInNob3VsZFBsYXlJbkJhY2tncm91bmQiLCJzaG91bGRSb3V0ZVRocm91Z2hFYXJwaWVjZSIsImludGVycnVwdGlvbk1vZGUiLCJzdG9wUmVjb3JkaW5nIiwiYWJvcnQiLCJjdXJyZW50UmVjb3JkaW5nIiwiUHJvbWlzZSIsImFsbCIsIkFycmF5IiwiZnJvbSIsImZpbmFsVGV4dCIsInN0YXJ0c1dpdGgiLCJ0cmFuc2xhdGVkIiwidHJhbnNsYXRlR3JvcVRleHQiLCJyZXNvbHZlR3JvcUNoYXRFbmRwb2ludCIsInVuZGVmaW5lZCIsImNyZWF0ZUVsZW1lbnQiLCJWaWV3IiwiZXhwb3J0cyIsIm1zIiwicmVzb2x2ZSIsInNldFRpbWVvdXQiLCJtZXNzYWdlIiwidGVzdCIsImNsZWFuVXJpIiwic3BsaXQiLCJsYXN0U2xhc2giLCJsYXN0SW5kZXhPZiIsInNsaWNlIiwiaW5jbHVkZXMiLCJEYXRlIiwibm93IiwiZXh0IiwicG9wIiwidG9Mb3dlckNhc2UiLCJiYXNlVXJsIiwib3ZlcnJpZGVFbmRwb2ludCIsInRyaW1tZWQiLCJyZXBsYWNlIiwiZW5kc1dpdGgiLCJpc05hdGl2ZVNwZWVjaEF2YWlsYWJsZSIsIlBsYXRmb3JtIiwiT1MiLCJnZXROYXRpdmVTcGVlY2hDYXBhYmlsaXRpZXMiLCJhdmFpbGFibGUiLCJwbGF0Zm9ybSIsInN1cHBvcnRzVkFEIiwic3VwcG9ydHNBdWRpb0xldmVscyIsInJlcXVpcmVzRXhwb0F1ZGlvIiwiYmFja2VuZCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zcGVlY2gtcmVjb2duaXRpb24vc3BlZWNoLXJlY29nbml0aW9uLW5hdGl2ZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBOYXRpdmUgU3BlZWNoIFJlY29nbml0aW9uIChpT1MvQW5kcm9pZClcbiAqIFVzZXMgRXhwbyBBdWRpbyBmb3IgcmVjb3JkaW5nIGFuZCBHcm9xIGZvciB0cmFuc2NyaXB0aW9uXG4gKlxuICogUkVRVUlSRVM6IGV4cG8tYXVkaW8gKyBleHBvLWZpbGUtc3lzdGVtIChTREsgNTMrKVxuICovXG5cbmltcG9ydCBSZWFjdCwgeyB1c2VFZmZlY3QsIHVzZVN0YXRlLCB1c2VSZWYsIHVzZUNhbGxiYWNrIH0gZnJvbSAncmVhY3QnO1xuaW1wb3J0IHsgVmlldywgUGxhdGZvcm0gfSBmcm9tICdyZWFjdC1uYXRpdmUnO1xudHlwZSBGaWxlU3lzdGVtTW9kdWxlID0gdHlwZW9mIGltcG9ydCgnZXhwby1maWxlLXN5c3RlbScpO1xuXG5jb25zdCBGaWxlU3lzdGVtOiBGaWxlU3lzdGVtTW9kdWxlID0gKCgpID0+IHtcbiAgdHJ5IHtcbiAgICAvLyBQcmVmZXIgbGVnYWN5IEFQSSB3aGVuIGF2YWlsYWJsZSAoRXhwbyBTREsgNTQrIHdhcm5zIG9uIGRpcmVjdCBsZWdhY3kgdXNhZ2UpXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby12YXItcmVxdWlyZXNcbiAgICByZXR1cm4gcmVxdWlyZSgnZXhwby1maWxlLXN5c3RlbS9sZWdhY3knKTtcbiAgfSBjYXRjaCB7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby12YXItcmVxdWlyZXNcbiAgICByZXR1cm4gcmVxdWlyZSgnZXhwby1maWxlLXN5c3RlbScpO1xuICB9XG59KSgpO1xuaW1wb3J0IGxvZ2dlciBmcm9tICcuLi9sb2dnZXInO1xuaW1wb3J0IHtcbiAgbWVyZ2VUcmFuc2NyaXB0UGFydHMsXG4gIHN0cmVhbUdyb3FUcmFuc2NyaXB0aW9uLFxuICB0cmFuc2xhdGVHcm9xVGV4dCxcbn0gZnJvbSAnLi91dGlscy9ncm9xLXRyYW5zY3JpcHRpb24nO1xuXG50eXBlIEF1ZGlvQmFja2VuZCA9IHtcbiAga2luZDogJ2V4cG8tYXVkaW8nO1xuICByZXF1ZXN0UGVybWlzc2lvbnNBc3luYzogKCkgPT4gUHJvbWlzZTx7IHN0YXR1cz86IHN0cmluZzsgZ3JhbnRlZD86IGJvb2xlYW4gfT47XG4gIHNldEF1ZGlvTW9kZUFzeW5jOiAobW9kZTogYW55KSA9PiBQcm9taXNlPHZvaWQ+O1xuICBjcmVhdGVSZWNvcmRpbmc6ICgpID0+IGFueTtcbiAgcHJlcGFyZVRvUmVjb3JkQXN5bmM6IChyZWNvcmRpbmc6IGFueSkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgc3RhcnRBc3luYzogKHJlY29yZGluZzogYW55KSA9PiBQcm9taXNlPHZvaWQ+O1xuICBzdG9wQXN5bmM6IChyZWNvcmRpbmc6IGFueSkgPT4gUHJvbWlzZTx7IHVyaT86IHN0cmluZyB8IG51bGwgfSB8IHZvaWQ+O1xuICBnZXRVcmk6IChyZWNvcmRpbmc6IGFueSwgc3RvcFJlc3VsdD86IGFueSkgPT4gc3RyaW5nIHwgbnVsbDtcbn07XG5cbmNvbnN0IG1pc3NpbmdBdWRpb0Vycm9yID1cbiAgJ05hdGl2ZSBhdWRpbyBwYWNrYWdlIG5vdCBmb3VuZC4gSW5zdGFsbCBgZXhwby1hdWRpb2AgYW5kIGBleHBvLWZpbGUtc3lzdGVtYCwgdGhlbiBydW4gYSBkZXYgY2xpZW50IGJ1aWxkOiBucHggZXhwbyBpbnN0YWxsIGV4cG8tYXVkaW8gZXhwby1maWxlLXN5c3RlbSc7XG5cbmZ1bmN0aW9uIGxvYWRFeHBvQXVkaW9CYWNrZW5kKCk6IEF1ZGlvQmFja2VuZCB8IG51bGwge1xuICB0cnkge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdmFyLXJlcXVpcmVzXG4gICAgY29uc3QgZXhwb0F1ZGlvID0gcmVxdWlyZSgnZXhwby1hdWRpbycpO1xuICAgIGNvbnN0IEF1ZGlvTW9kdWxlID0gZXhwb0F1ZGlvPy5BdWRpb01vZHVsZTtcbiAgICBjb25zdCByZXF1ZXN0UmVjb3JkaW5nUGVybWlzc2lvbnNBc3luYyA9IGV4cG9BdWRpbz8ucmVxdWVzdFJlY29yZGluZ1Blcm1pc3Npb25zQXN5bmM7XG4gICAgY29uc3Qgc2V0QXVkaW9Nb2RlQXN5bmMgPSBleHBvQXVkaW8/LnNldEF1ZGlvTW9kZUFzeW5jO1xuXG4gICAgaWYgKCFBdWRpb01vZHVsZSB8fCB0eXBlb2YgcmVxdWVzdFJlY29yZGluZ1Blcm1pc3Npb25zQXN5bmMgIT09ICdmdW5jdGlvbicgfHwgdHlwZW9mIHNldEF1ZGlvTW9kZUFzeW5jICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBjb25zdCBSZWNvcmRpbmdQcmVzZXRzID0gZXhwb0F1ZGlvPy5SZWNvcmRpbmdQcmVzZXRzO1xuICAgIGNvbnN0IEF1ZGlvUXVhbGl0eSA9IGV4cG9BdWRpbz8uQXVkaW9RdWFsaXR5O1xuICAgIGNvbnN0IElPU091dHB1dEZvcm1hdCA9IGV4cG9BdWRpbz8uSU9TT3V0cHV0Rm9ybWF0O1xuXG4gICAgY29uc3QgcHJlc2V0ID0gUmVjb3JkaW5nUHJlc2V0cz8uSElHSF9RVUFMSVRZO1xuICAgIGNvbnN0IGlvc091dHB1dEZvcm1hdCA9IElPU091dHB1dEZvcm1hdD8uTVBFRzRBQUMgPz8gJ2FhYyAnO1xuICAgIGNvbnN0IGlvc0F1ZGlvUXVhbGl0eSA9IEF1ZGlvUXVhbGl0eT8uSElHSCA/PyAweDYwO1xuXG4gICAgY29uc3QgcmVjb3JkaW5nT3B0aW9ucyA9IHtcbiAgICAgIGV4dGVuc2lvbjogcHJlc2V0Py5leHRlbnNpb24gPz8gJy5tNGEnLFxuICAgICAgc2FtcGxlUmF0ZTogMTYwMDAsXG4gICAgICBudW1iZXJPZkNoYW5uZWxzOiAxLFxuICAgICAgYml0UmF0ZTogMTI4MDAwLFxuICAgICAgYW5kcm9pZDoge1xuICAgICAgICBvdXRwdXRGb3JtYXQ6IHByZXNldD8uYW5kcm9pZD8ub3V0cHV0Rm9ybWF0ID8/ICdtcGVnNCcsXG4gICAgICAgIGF1ZGlvRW5jb2RlcjogcHJlc2V0Py5hbmRyb2lkPy5hdWRpb0VuY29kZXIgPz8gJ2FhYycsXG4gICAgICAgIHNhbXBsZVJhdGU6IDE2MDAwLFxuICAgICAgICBudW1iZXJPZkNoYW5uZWxzOiAxLFxuICAgICAgICBiaXRSYXRlOiAxMjgwMDAsXG4gICAgICB9LFxuICAgICAgaW9zOiB7XG4gICAgICAgIG91dHB1dEZvcm1hdDogcHJlc2V0Py5pb3M/Lm91dHB1dEZvcm1hdCA/PyBpb3NPdXRwdXRGb3JtYXQsXG4gICAgICAgIGF1ZGlvUXVhbGl0eTogcHJlc2V0Py5pb3M/LmF1ZGlvUXVhbGl0eSA/PyBpb3NBdWRpb1F1YWxpdHksXG4gICAgICAgIHNhbXBsZVJhdGU6IDE2MDAwLFxuICAgICAgICBudW1iZXJPZkNoYW5uZWxzOiAxLFxuICAgICAgICBiaXRSYXRlOiAxMjgwMDAsXG4gICAgICAgIGxpbmVhclBDTUJpdERlcHRoOiAxNixcbiAgICAgICAgbGluZWFyUENNSXNCaWdFbmRpYW46IGZhbHNlLFxuICAgICAgICBsaW5lYXJQQ01Jc0Zsb2F0OiBmYWxzZSxcbiAgICAgIH0sXG4gICAgICB3ZWI6IHtcbiAgICAgICAgbWltZVR5cGU6IHByZXNldD8ud2ViPy5taW1lVHlwZSA/PyAnYXVkaW8vd2VibScsXG4gICAgICAgIGJpdHNQZXJTZWNvbmQ6IHByZXNldD8ud2ViPy5iaXRzUGVyU2Vjb25kID8/IDEyODAwMCxcbiAgICAgIH0sXG4gICAgfTtcblxuICAgIHJldHVybiB7XG4gICAgICBraW5kOiAnZXhwby1hdWRpbycsXG4gICAgICByZXF1ZXN0UGVybWlzc2lvbnNBc3luYzogcmVxdWVzdFJlY29yZGluZ1Blcm1pc3Npb25zQXN5bmMsXG4gICAgICBzZXRBdWRpb01vZGVBc3luYyxcbiAgICAgIGNyZWF0ZVJlY29yZGluZzogKCkgPT4gbmV3IEF1ZGlvTW9kdWxlLkF1ZGlvUmVjb3JkZXIocmVjb3JkaW5nT3B0aW9ucyksXG4gICAgICBwcmVwYXJlVG9SZWNvcmRBc3luYzogKHJlY29yZGluZykgPT4gcmVjb3JkaW5nLnByZXBhcmVUb1JlY29yZEFzeW5jKCksXG4gICAgICBzdGFydEFzeW5jOiBhc3luYyAocmVjb3JkaW5nKSA9PiB7XG4gICAgICAgIHJlY29yZGluZy5yZWNvcmQoKTtcbiAgICAgIH0sXG4gICAgICBzdG9wQXN5bmM6IGFzeW5jIChyZWNvcmRpbmcpID0+IHtcbiAgICAgICAgYXdhaXQgcmVjb3JkaW5nLnN0b3AoKTtcbiAgICAgICAgcmV0dXJuIHsgdXJpOiByZWNvcmRpbmc/LnVyaSA/PyBudWxsIH07XG4gICAgICB9LFxuICAgICAgZ2V0VXJpOiAocmVjb3JkaW5nLCBzdG9wUmVzdWx0KSA9PiB7XG4gICAgICAgIGlmIChzdG9wUmVzdWx0Py51cmkpIHJldHVybiBzdG9wUmVzdWx0LnVyaTtcbiAgICAgICAgcmV0dXJuIHJlY29yZGluZz8udXJpID8/IG51bGw7XG4gICAgICB9LFxuICAgIH07XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxufVxuXG5jb25zdCBhdWRpb0JhY2tlbmQ6IEF1ZGlvQmFja2VuZCB8IG51bGwgPSBsb2FkRXhwb0F1ZGlvQmFja2VuZCgpO1xuXG5pZiAoIWF1ZGlvQmFja2VuZCkge1xuICBsb2dnZXIuZXJyb3IoXG4gICAgJ+KdjCBObyBhdWRpbyBiYWNrZW5kIGF2YWlsYWJsZS4gSW5zdGFsbCBleHBvLWF1ZGlvICsgZXhwby1maWxlLXN5c3RlbSBhbmQgcnVuIGEgZGV2IGNsaWVudCBidWlsZC4nLFxuICApO1xufSBlbHNlIHtcbiAgbG9nZ2VyLmluZm8oYPCfjqQgQXVkaW8gYmFja2VuZCByZWFkeTogJHthdWRpb0JhY2tlbmQua2luZH1gKTtcbn1cblxuY29uc3QgREVGQVVMVF9DSFVOS19EVVJBVElPTl9NUyA9IDIwMDA7IC8vIFJlZHVjZWQgZnJvbSAzMDAwbXMgZm9yIGJldHRlciByZXNwb25zaXZlbmVzcyB3aGlsZSBtYWludGFpbmluZyBlZmZpY2llbmN5XG5jb25zdCBTVEFSVF9SRVRSWV9ERUxBWV9NUyA9IDE1MDtcbmNvbnN0IFBPU1RfU1RPUF9DT09MRE9XTl9NUyA9IDEyMDtcbmNvbnN0IE1BWF9TVEFSVF9BVFRFTVBUUyA9IDM7XG5jb25zdCBNQVhfQ09OU0VDVVRJVkVfU1RBUlRfRkFJTFVSRVMgPSAzO1xuY29uc3QgTUlOX0FVRElPX1NJWkVfRkxPT1JfQllURVMgPSAyNTY7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTmF0aXZlU3BlZWNoUmVjb2duaXplclByb3BzIHtcbiAgc2hvdWxkTGlzdGVuOiBib29sZWFuO1xuICBzcGVlY2hTdGF0dXNDYWxsYmFjazogKHN0YXR1czogYm9vbGVhbiwgdHJhbnNjcmlwdDogc3RyaW5nKSA9PiB2b2lkO1xuICBzcGVlY2hSZXN1bHRDYWxsYmFjazogKHJlc3VsdDogc3RyaW5nKSA9PiB2b2lkO1xuICBzcGVlY2hUcmFuc2xhdGlvbkNhbGxiYWNrPzogKHRyYW5zbGF0aW9uOiBzdHJpbmcpID0+IHZvaWQ7XG4gIGNsaWVudFNlY3JldDogc3RyaW5nO1xuICBncm9xQXBpQmFzZVVybD86IHN0cmluZztcbiAgZ3JvcVRyYW5zY3JpcHRpb25FbmRwb2ludD86IHN0cmluZztcbiAgc3RyZWFtPzogYm9vbGVhbjtcbiAgbGFuZ3VhZ2U/OiBzdHJpbmc7XG4gIG9uRXJyb3I/OiAoZXJyb3I6IEVycm9yKSA9PiB2b2lkO1xuICBtaW5BdWRpb1NpemU/OiBudW1iZXI7IC8vIE1pbmltdW0gYXVkaW8gc2l6ZSBpbiBieXRlcyAoZGVmYXVsdCAxMDAwKVxufVxuXG4vKipcbiAqIE5hdGl2ZSBzcGVlY2ggcmVjb2duaXplciBmb3IgaU9TL0FuZHJvaWQgdXNpbmcgZXhwby1hdWRpb1xuICovXG5leHBvcnQgY29uc3QgTmF0aXZlU3BlZWNoUmVjb2duaXplcjogUmVhY3QuRkM8TmF0aXZlU3BlZWNoUmVjb2duaXplclByb3BzPiA9ICh7XG4gIHNob3VsZExpc3RlbixcbiAgc3BlZWNoU3RhdHVzQ2FsbGJhY2ssXG4gIHNwZWVjaFJlc3VsdENhbGxiYWNrLFxuICBzcGVlY2hUcmFuc2xhdGlvbkNhbGxiYWNrLFxuICBjbGllbnRTZWNyZXQsXG4gIGdyb3FBcGlCYXNlVXJsLFxuICBncm9xVHJhbnNjcmlwdGlvbkVuZHBvaW50LFxuICBzdHJlYW0gPSB0cnVlLFxuICBsYW5ndWFnZSA9ICdlbicsXG4gIG9uRXJyb3IsXG4gIG1pbkF1ZGlvU2l6ZSA9IDUwMCxcbn0pID0+IHtcbiAgY29uc3QgW2lzUmVjb3JkaW5nLCBzZXRJc1JlY29yZGluZ10gPSB1c2VTdGF0ZShmYWxzZSk7XG4gIGNvbnN0IGFjdGl2ZVJlY29yZGluZ1JlZiA9IHVzZVJlZjxhbnk+KG51bGwpO1xuICBjb25zdCBzdG9wSW5GbGlnaHRSZWYgPSB1c2VSZWY8eyByZWNvcmRpbmc6IGFueTsgcHJvbWlzZTogUHJvbWlzZTxzdHJpbmcgfCBudWxsPiB9IHwgbnVsbD4obnVsbCk7XG4gIGNvbnN0IGNodW5rTG9vcEFib3J0UmVmID0gdXNlUmVmPEFib3J0Q29udHJvbGxlciB8IG51bGw+KG51bGwpO1xuICBjb25zdCBzdHJlYW1BYm9ydFJlZiA9IHVzZVJlZjxBYm9ydENvbnRyb2xsZXIgfCBudWxsPihudWxsKTtcbiAgY29uc3QgYWN0aXZlQ2h1bmtzUmVmID0gdXNlUmVmPFNldDxQcm9taXNlPHZvaWQ+Pj4obmV3IFNldCgpKTtcbiAgY29uc3QgY29tbWl0dGVkVHJhbnNjcmlwdFJlZiA9IHVzZVJlZjxzdHJpbmc+KCcnKTtcbiAgY29uc3QgY3VycmVudENodW5rVHJhbnNjcmlwdFJlZiA9IHVzZVJlZjxzdHJpbmc+KCcnKTtcbiAgY29uc3QgbGFzdEludGVyaW1SZWYgPSB1c2VSZWY8c3RyaW5nPignJyk7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoY2xpZW50U2VjcmV0KSB7XG4gICAgICBsb2dnZXIuZGVidWcoJ0dyb3EgY2xpZW50IGluaXRpYWxpemVkIChuYXRpdmUpJyk7XG4gICAgfVxuICB9LCBbY2xpZW50U2VjcmV0XSk7XG5cbiAgLyoqXG4gICAqIFJlcXVlc3QgYXVkaW8gcGVybWlzc2lvbnNcbiAgICovXG4gIGNvbnN0IHJlcXVlc3RQZXJtaXNzaW9ucyA9IGFzeW5jICgpID0+IHtcbiAgICB0cnkge1xuICAgICAgaWYgKCFhdWRpb0JhY2tlbmQpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKG1pc3NpbmdBdWRpb0Vycm9yKTtcbiAgICAgIH1cbiAgICAgIGxvZ2dlci5pbmZvKCdSZXF1ZXN0aW5nIG1pY3JvcGhvbmUgcGVybWlzc2lvbnMuLi4nKTtcbiAgICAgIGNvbnN0IHsgc3RhdHVzLCBncmFudGVkIH0gPSBhd2FpdCBhdWRpb0JhY2tlbmQucmVxdWVzdFBlcm1pc3Npb25zQXN5bmMoKTtcbiAgICAgIGNvbnN0IGlzR3JhbnRlZCA9IHN0YXR1cyA9PT0gJ2dyYW50ZWQnIHx8IGdyYW50ZWQgPT09IHRydWU7XG4gICAgICBpZiAoIWlzR3JhbnRlZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ0F1ZGlvIHBlcm1pc3Npb24gbm90IGdyYW50ZWQuIFBsZWFzZSBlbmFibGUgbWljcm9waG9uZSBhY2Nlc3MgaW4geW91ciBkZXZpY2Ugc2V0dGluZ3MuJyk7XG4gICAgICB9XG4gICAgICBsb2dnZXIuaW5mbygn4pyFIE1pY3JvcGhvbmUgcGVybWlzc2lvbiBncmFudGVkJyk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdQZXJtaXNzaW9uIGVycm9yOicsIGVycm9yKTtcbiAgICAgIGlmIChvbkVycm9yKSB7XG4gICAgICAgIG9uRXJyb3IoZXJyb3IgYXMgRXJyb3IpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfTtcblxuICBjb25zdCByZXNldFRyYW5zY3JpcHRTdGF0ZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBjb21taXR0ZWRUcmFuc2NyaXB0UmVmLmN1cnJlbnQgPSAnJztcbiAgICBjdXJyZW50Q2h1bmtUcmFuc2NyaXB0UmVmLmN1cnJlbnQgPSAnJztcbiAgICBsYXN0SW50ZXJpbVJlZi5jdXJyZW50ID0gJyc7XG4gIH0sIFtdKTtcblxuICBjb25zdCBjb21taXRDaHVuayA9IHVzZUNhbGxiYWNrKChjaHVua1RleHQ6IHN0cmluZykgPT4ge1xuICAgIGNvbnN0IGNvbWJpbmVkID0gbWVyZ2VUcmFuc2NyaXB0UGFydHMoXG4gICAgICBjb21taXR0ZWRUcmFuc2NyaXB0UmVmLmN1cnJlbnQsXG4gICAgICBjaHVua1RleHRcbiAgICApO1xuICAgIGNvbW1pdHRlZFRyYW5zY3JpcHRSZWYuY3VycmVudCA9IGNvbWJpbmVkO1xuICAgIGN1cnJlbnRDaHVua1RyYW5zY3JpcHRSZWYuY3VycmVudCA9ICcnO1xuICB9LCBbXSk7XG5cbiAgY29uc3Qgc3RvcFJlY29yZGVyU2FmZWx5ID0gdXNlQ2FsbGJhY2soXG4gICAgYXN5bmMgKHJlY29yZGluZzogYW55LCByZWFzb246IHN0cmluZyk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4gPT4ge1xuICAgICAgaWYgKCFhdWRpb0JhY2tlbmQgfHwgIXJlY29yZGluZykge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cblxuICAgICAgY29uc3QgaW5GbGlnaHQgPSBzdG9wSW5GbGlnaHRSZWYuY3VycmVudDtcbiAgICAgIGlmIChpbkZsaWdodCAmJiBpbkZsaWdodC5yZWNvcmRpbmcgPT09IHJlY29yZGluZykge1xuICAgICAgICBsb2dnZXIuZGVidWcoYFtOYXRpdmVTUl0g4pm777iPICBSZXVzaW5nIGluLWZsaWdodCBzdG9wICgke3JlYXNvbn0pYCk7XG4gICAgICAgIHJldHVybiBpbkZsaWdodC5wcm9taXNlO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBzdG9wUHJvbWlzZSA9IChhc3luYyAoKSA9PiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3Qgc3RvcFJlc3VsdCA9IGF3YWl0IGF1ZGlvQmFja2VuZC5zdG9wQXN5bmMocmVjb3JkaW5nKTtcbiAgICAgICAgICByZXR1cm4gYXVkaW9CYWNrZW5kLmdldFVyaShyZWNvcmRpbmcsIHN0b3BSZXN1bHQpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGlmIChpc0JlbmlnblN0b3BFcnJvcihlcnJvcikpIHtcbiAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhgW05hdGl2ZVNSXSBTdG9wIGFscmVhZHkgZmluYWxpemVkICgke3JlYXNvbn0pYCk7XG4gICAgICAgICAgICByZXR1cm4gYXVkaW9CYWNrZW5kLmdldFVyaShyZWNvcmRpbmcpO1xuICAgICAgICAgIH1cbiAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICBpZiAoc3RvcEluRmxpZ2h0UmVmLmN1cnJlbnQ/LnJlY29yZGluZyA9PT0gcmVjb3JkaW5nKSB7XG4gICAgICAgICAgICBzdG9wSW5GbGlnaHRSZWYuY3VycmVudCA9IG51bGw7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9KSgpO1xuXG4gICAgICBzdG9wSW5GbGlnaHRSZWYuY3VycmVudCA9IHtcbiAgICAgICAgcmVjb3JkaW5nLFxuICAgICAgICBwcm9taXNlOiBzdG9wUHJvbWlzZSxcbiAgICAgIH07XG4gICAgICByZXR1cm4gc3RvcFByb21pc2U7XG4gICAgfSxcbiAgICBbXSxcbiAgKTtcblxuICAvKipcbiAgICogUHJvY2VzcyByZWNvcmRlZCBhdWRpbyBjaHVuayBhbmQgc2VuZCB0byBHcm9xIChzdHJlYW1pbmcpXG4gICAqL1xuICBjb25zdCBwcm9jZXNzQXVkaW9DaHVuayA9IHVzZUNhbGxiYWNrKGFzeW5jIChmaWxlVXJpOiBzdHJpbmcpID0+IHtcbiAgICBsZXQgbm9ybWFsaXplZFVyaTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG4gICAgdHJ5IHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnW05hdGl2ZVNSXSDwn5SNIFByb2Nlc3NpbmcgYXVkaW8gY2h1bms6JywgZmlsZVVyaSk7XG4gICAgICBub3JtYWxpemVkVXJpID0gbm9ybWFsaXplRmlsZVVyaShmaWxlVXJpKTtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnW05hdGl2ZVNSXSDwn5OBIE5vcm1hbGl6ZWQgVVJJOicsIG5vcm1hbGl6ZWRVcmkpO1xuICAgICAgXG4gICAgICAvLyBSZWFkIGZpbGVcbiAgICAgIGxvZ2dlci5kZWJ1ZygnW05hdGl2ZVNSXSDwn5OWIFJlYWRpbmcgZmlsZSBpbmZvLi4uJyk7XG4gICAgICBjb25zdCBmaWxlSW5mbyA9IGF3YWl0IEZpbGVTeXN0ZW0uZ2V0SW5mb0FzeW5jKG5vcm1hbGl6ZWRVcmkpO1xuICAgICAgbG9nZ2VyLmRlYnVnKCdbTmF0aXZlU1JdIPCfk4ogRmlsZSBpbmZvOicsIEpTT04uc3RyaW5naWZ5KGZpbGVJbmZvKSk7XG5cbiAgICAgIGlmICghZmlsZUluZm8uZXhpc3RzKSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignW05hdGl2ZVNSXSDinYwgQXVkaW8gZmlsZSBtaXNzaW5nOicsIG5vcm1hbGl6ZWRVcmkpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGZpbGVTaXplID1cbiAgICAgICAgJ3NpemUnIGluIGZpbGVJbmZvICYmIHR5cGVvZiBmaWxlSW5mby5zaXplID09PSAnbnVtYmVyJ1xuICAgICAgICAgID8gZmlsZUluZm8uc2l6ZVxuICAgICAgICAgIDogbnVsbDtcblxuICAgICAgY29uc3QgZWZmZWN0aXZlTWluQXVkaW9TaXplID0gTWF0aC5tYXgobWluQXVkaW9TaXplLCBNSU5fQVVESU9fU0laRV9GTE9PUl9CWVRFUyk7XG4gICAgICBpZiAoZmlsZVNpemUgIT09IG51bGwgJiYgZmlsZVNpemUgPCBlZmZlY3RpdmVNaW5BdWRpb1NpemUpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oXG4gICAgICAgICAgYFtOYXRpdmVTUl0g4pqg77iPICBBdWRpbyBmaWxlIHRvbyBzbWFsbCAoJHtmaWxlU2l6ZX0gYnl0ZXMsIG1pbjogJHtlZmZlY3RpdmVNaW5BdWRpb1NpemV9KWBcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgYFtOYXRpdmVTUl0g8J+TpCBVcGxvYWRpbmcgJHtmaWxlU2l6ZSA/PyAndW5rbm93bid9IGJ5dGVzIHRvIEdyb3EuLi5gXG4gICAgICApO1xuXG4gICAgICBjb25zdCBmaWxlTmFtZSA9IGdldEZpbGVOYW1lRnJvbVVyaShub3JtYWxpemVkVXJpKTtcbiAgICAgIGNvbnN0IG1pbWVUeXBlID0gZ2V0TWltZVR5cGVGcm9tTmFtZShmaWxlTmFtZSk7XG5cbiAgICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgICBzdHJlYW1BYm9ydFJlZi5jdXJyZW50ID0gY29udHJvbGxlcjtcblxuICAgICAgY29uc3QgY2h1bmtUZXh0ID0gYXdhaXQgc3RyZWFtR3JvcVRyYW5zY3JpcHRpb24oe1xuICAgICAgICBhcGlLZXk6IGNsaWVudFNlY3JldCxcbiAgICAgICAgYXVkaW9CbG9iOiB7XG4gICAgICAgICAgdXJpOiBub3JtYWxpemVkVXJpLFxuICAgICAgICAgIG5hbWU6IGZpbGVOYW1lLFxuICAgICAgICAgIHR5cGU6IG1pbWVUeXBlLFxuICAgICAgICB9LFxuICAgICAgICBtaW1lVHlwZSxcbiAgICAgICAgbGFuZ3VhZ2UsXG4gICAgICAgIHJlc3BvbnNlRm9ybWF0OiAndmVyYm9zZV9qc29uJyxcbiAgICAgICAgc3RyZWFtLFxuICAgICAgICBlbmRwb2ludDogcmVzb2x2ZUdyb3FUcmFuc2NyaXB0aW9uRW5kcG9pbnQoXG4gICAgICAgICAgZ3JvcUFwaUJhc2VVcmwsXG4gICAgICAgICAgZ3JvcVRyYW5zY3JpcHRpb25FbmRwb2ludCxcbiAgICAgICAgKSxcbiAgICAgICAgc2lnbmFsOiBjb250cm9sbGVyLnNpZ25hbCxcbiAgICAgICAgb25JbnRlcmltOiAodGV4dCkgPT4ge1xuICAgICAgICAgIGlmICghdGV4dCkgcmV0dXJuO1xuICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhgW05hdGl2ZVNSXSDwn5OdIEludGVyaW0gY2h1bmsgdGV4dCAoJHt0ZXh0Lmxlbmd0aH0gY2hhcnMpYCk7XG4gICAgICAgICAgY3VycmVudENodW5rVHJhbnNjcmlwdFJlZi5jdXJyZW50ID0gdGV4dDtcbiAgICAgICAgICBcbiAgICAgICAgICAvLyBSZWFsLXRpbWUgY2FsbGJhY2sgbGlrZSB3ZWIgdmVyc2lvblxuICAgICAgICAgIGNvbnN0IGNvbWJpbmVkID0gbWVyZ2VUcmFuc2NyaXB0UGFydHMoY29tbWl0dGVkVHJhbnNjcmlwdFJlZi5jdXJyZW50LCB0ZXh0KTtcbiAgICAgICAgICBpZiAoY29tYmluZWQgIT09IGxhc3RJbnRlcmltUmVmLmN1cnJlbnQpIHtcbiAgICAgICAgICAgIGxhc3RJbnRlcmltUmVmLmN1cnJlbnQgPSBjb21iaW5lZDtcbiAgICAgICAgICAgIHNwZWVjaFN0YXR1c0NhbGxiYWNrKHRydWUsIGNvbWJpbmVkKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICB9KTtcblxuICAgICAgaWYgKGNodW5rVGV4dCkge1xuICAgICAgICBsb2dnZXIuaW5mbyhgW05hdGl2ZVNSXSDinIUgQ2h1bmsgdHJhbnNjcmlwdGlvbiBjb21wbGV0ZSAoJHtjaHVua1RleHQubGVuZ3RofSBjaGFycylgKTtcbiAgICAgICAgY29tbWl0Q2h1bmsoY2h1bmtUZXh0KTtcbiAgICAgICAgLy8gRmluYWwgdXBkYXRlIGFmdGVyIGNodW5rIGNvbXBsZXRlc1xuICAgICAgICBjb25zdCBmaW5hbENvbWJpbmVkID0gbWVyZ2VUcmFuc2NyaXB0UGFydHMoY29tbWl0dGVkVHJhbnNjcmlwdFJlZi5jdXJyZW50LCAnJyk7XG4gICAgICAgIGlmIChmaW5hbENvbWJpbmVkICE9PSBsYXN0SW50ZXJpbVJlZi5jdXJyZW50KSB7XG4gICAgICAgICAgbGFzdEludGVyaW1SZWYuY3VycmVudCA9IGZpbmFsQ29tYmluZWQ7XG4gICAgICAgICAgc3BlZWNoU3RhdHVzQ2FsbGJhY2sodHJ1ZSwgZmluYWxDb21iaW5lZCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdbTmF0aXZlU1JdIEdyb3EgdHJhbnNjcmlwdGlvbiBlcnJvcjonLCBlcnJvcik7XG4gICAgICBpZiAob25FcnJvcikge1xuICAgICAgICBvbkVycm9yKGVycm9yIGFzIEVycm9yKTtcbiAgICAgIH1cbiAgICB9IGZpbmFsbHkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgaWYgKG5vcm1hbGl6ZWRVcmkpIHtcbiAgICAgICAgICBhd2FpdCBGaWxlU3lzdGVtLmRlbGV0ZUFzeW5jKG5vcm1hbGl6ZWRVcmksIHsgaWRlbXBvdGVudDogdHJ1ZSB9KTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ1tOYXRpdmVTUl0gRmFpbGVkIHRvIGRlbGV0ZSB0ZW1wIGF1ZGlvIGZpbGU6JywgZXJyb3IpO1xuICAgICAgfVxuICAgIH1cbiAgfSwgW1xuICAgIGNvbW1pdENodW5rLFxuICAgIGNsaWVudFNlY3JldCxcbiAgICBncm9xQXBpQmFzZVVybCxcbiAgICBncm9xVHJhbnNjcmlwdGlvbkVuZHBvaW50LFxuICAgIHN0cmVhbSxcbiAgICBsYW5ndWFnZSxcbiAgICBtaW5BdWRpb1NpemUsXG4gICAgb25FcnJvcixcbiAgXSk7XG5cbiAgLyoqXG4gICAqIFN0YXJ0IGNodW5rZWQgcmVjb3JkaW5nIGxvb3AgZm9yIHN0cmVhbWluZyB0cmFuc2NyaXB0aW9uXG4gICAqL1xuICBjb25zdCBzdGFydENodW5rTG9vcCA9IHVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHtcbiAgICBpZiAoIWF1ZGlvQmFja2VuZCkgcmV0dXJuO1xuICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgY2h1bmtMb29wQWJvcnRSZWYuY3VycmVudCA9IGNvbnRyb2xsZXI7XG5cbiAgICBsb2dnZXIuaW5mbyhgW05hdGl2ZVNSXSDwn5SEIENodW5rIGxvb3Agc3RhcnRlZCAoJHtERUZBVUxUX0NIVU5LX0RVUkFUSU9OX01TfW1zIGNodW5rcylgKTtcbiAgICBsZXQgY2h1bmtDb3VudCA9IDA7XG4gICAgbGV0IGNvbnNlY3V0aXZlU3RhcnRGYWlsdXJlcyA9IDA7XG5cbiAgICB3aGlsZSAoIWNvbnRyb2xsZXIuc2lnbmFsLmFib3J0ZWQpIHtcbiAgICAgIGNodW5rQ291bnQrKztcbiAgICAgIGxvZ2dlci5kZWJ1ZyhgW05hdGl2ZVNSXSDwn5O8IFJlY29yZGluZyBjaHVuayAjJHtjaHVua0NvdW50fS4uLmApO1xuICAgICAgXG4gICAgICBsZXQgcmVjb3JkaW5nOiBhbnkgfCBudWxsID0gbnVsbDtcbiAgICAgIGxldCBzdGFydGVkID0gZmFsc2U7XG5cbiAgICAgIGZvciAobGV0IGF0dGVtcHQgPSAxOyBhdHRlbXB0IDw9IE1BWF9TVEFSVF9BVFRFTVBUUzsgYXR0ZW1wdCsrKSB7XG4gICAgICAgIGlmIChjb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cblxuICAgICAgICByZWNvcmRpbmcgPSBhdWRpb0JhY2tlbmQuY3JlYXRlUmVjb3JkaW5nKCk7XG4gICAgICAgIGFjdGl2ZVJlY29yZGluZ1JlZi5jdXJyZW50ID0gcmVjb3JkaW5nO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgYXdhaXQgYXVkaW9CYWNrZW5kLnByZXBhcmVUb1JlY29yZEFzeW5jKHJlY29yZGluZyk7XG4gICAgICAgICAgYXdhaXQgYXVkaW9CYWNrZW5kLnN0YXJ0QXN5bmMocmVjb3JkaW5nKTtcbiAgICAgICAgICBsb2dnZXIuZGVidWcoYFtOYXRpdmVTUl0g8J+Ome+4jyAgQ2h1bmsgIyR7Y2h1bmtDb3VudH0gcmVjb3JkaW5nLi4uYCk7XG4gICAgICAgICAgc3RhcnRlZCA9IHRydWU7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgbG9nZ2VyLmVycm9yKFxuICAgICAgICAgICAgYEZhaWxlZCB0byBzdGFydCBjaHVuayAjJHtjaHVua0NvdW50fSByZWNvcmRpbmcgKGF0dGVtcHQgJHthdHRlbXB0fS8ke01BWF9TVEFSVF9BVFRFTVBUU30pOmAsXG4gICAgICAgICAgICBlcnJvclxuICAgICAgICAgICk7XG4gICAgICAgICAgYWN0aXZlUmVjb3JkaW5nUmVmLmN1cnJlbnQgPSBudWxsO1xuICAgICAgICAgIGlmIChhdHRlbXB0IDwgTUFYX1NUQVJUX0FUVEVNUFRTKSB7XG4gICAgICAgICAgICBhd2FpdCBzbGVlcChTVEFSVF9SRVRSWV9ERUxBWV9NUyk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmICghc3RhcnRlZCB8fCAhcmVjb3JkaW5nKSB7XG4gICAgICAgIGNvbnNlY3V0aXZlU3RhcnRGYWlsdXJlcyArPSAxO1xuICAgICAgICBsb2dnZXIud2FybihcbiAgICAgICAgICBgW05hdGl2ZVNSXSDimqDvuI8gIENodW5rIHN0YXJ0IGZhaWxlZCAoJHtjb25zZWN1dGl2ZVN0YXJ0RmFpbHVyZXN9LyR7TUFYX0NPTlNFQ1VUSVZFX1NUQVJUX0ZBSUxVUkVTfSkuYFxuICAgICAgICApO1xuICAgICAgICBpZiAoY29uc2VjdXRpdmVTdGFydEZhaWx1cmVzID49IE1BWF9DT05TRUNVVElWRV9TVEFSVF9GQUlMVVJFUykge1xuICAgICAgICAgIGxvZ2dlci5lcnJvcignW05hdGl2ZVNSXSDinYwgVG9vIG1hbnkgY29uc2VjdXRpdmUgY2h1bmsgc3RhcnQgZmFpbHVyZXM7IGVuZGluZyBsb29wLicpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IHNsZWVwKFNUQVJUX1JFVFJZX0RFTEFZX01TKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGNvbnNlY3V0aXZlU3RhcnRGYWlsdXJlcyA9IDA7XG5cbiAgICAgIGF3YWl0IHNsZWVwKERFRkFVTFRfQ0hVTktfRFVSQVRJT05fTVMpO1xuXG4gICAgICBpZiAoY29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkge1xuICAgICAgICBsb2dnZXIuaW5mbygnW05hdGl2ZVNSXSDwn5uRIENodW5rIGxvb3AgYWJvcnRlZCcpO1xuICAgICAgICBjb25zdCBzdGlsbEFjdGl2ZSA9IGFjdGl2ZVJlY29yZGluZ1JlZi5jdXJyZW50ID09PSByZWNvcmRpbmc7XG4gICAgICAgIGlmIChzdGlsbEFjdGl2ZSkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCB1cmkgPSBhd2FpdCBzdG9wUmVjb3JkZXJTYWZlbHkocmVjb3JkaW5nLCBgYWJvcnQgY2h1bmsgIyR7Y2h1bmtDb3VudH1gKTtcbiAgICAgICAgICAgIGlmICh1cmkpIHtcbiAgICAgICAgICAgICAgbG9nZ2VyLmluZm8oYFtOYXRpdmVTUl0g4pyFIEZpbmFsIGNodW5rIFVSSSAoYWJvcnQpOiAke3VyaS5zdWJzdHJpbmcoMCwgNTApfS4uLmApO1xuICAgICAgICAgICAgICBjb25zdCBjaHVua1Byb21pc2UgPSBwcm9jZXNzQXVkaW9DaHVuayh1cmkpXG4gICAgICAgICAgICAgICAgLmNhdGNoKChlcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgICAgbG9nZ2VyLmVycm9yKCdbTmF0aXZlU1JdIEZhaWxlZCB0byBwcm9jZXNzIGFib3J0ZWQgY2h1bms6JywgZXJyb3IpO1xuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgYWN0aXZlQ2h1bmtzUmVmLmN1cnJlbnQuZGVsZXRlKGNodW5rUHJvbWlzZSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgIGFjdGl2ZUNodW5rc1JlZi5jdXJyZW50LmFkZChjaHVua1Byb21pc2UpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oJ1tOYXRpdmVTUl0g4pqg77iPICBObyBVUkkgZm9yIGFib3J0ZWQgY2h1bmsgLSBza2lwcGluZyB1cGxvYWQnKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgbG9nZ2VyLndhcm4oJ1tOYXRpdmVTUl0gRmFpbGVkIHRvIHN0b3AgY2h1bmsgcmVjb3JkaW5nIG9uIGFib3J0OicsIGVycm9yKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbG9nZ2VyLmRlYnVnKCdbTmF0aXZlU1JdIEFib3J0IGRldGVjdGVkIGFmdGVyIGV4dGVybmFsIHN0b3A7IHNraXBwaW5nIGR1cGxpY2F0ZSBzdG9wJyk7XG4gICAgICAgIH1cbiAgICAgICAgYWN0aXZlUmVjb3JkaW5nUmVmLmN1cnJlbnQgPSBudWxsO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cblxuICAgICAgdHJ5IHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKGBbTmF0aXZlU1JdIOKPue+4jyAgU3RvcHBpbmcgY2h1bmsgIyR7Y2h1bmtDb3VudH0uLi5gKTtcbiAgICAgICAgY29uc3QgdXJpID0gYXdhaXQgc3RvcFJlY29yZGVyU2FmZWx5KHJlY29yZGluZywgYGNodW5rICMke2NodW5rQ291bnR9YCk7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhgW05hdGl2ZVNSXSDinIUgQ2h1bmsgIyR7Y2h1bmtDb3VudH0gc3RvcHBlZGApO1xuICAgICAgICBhY3RpdmVSZWNvcmRpbmdSZWYuY3VycmVudCA9IG51bGw7XG5cbiAgICAgICAgaWYgKHVyaSkge1xuICAgICAgICAgIGxvZ2dlci5pbmZvKGBbTmF0aXZlU1JdIOKchSBDaHVuayAjJHtjaHVua0NvdW50fSBVUkk6ICR7dXJpLnN1YnN0cmluZygwLCA1MCl9Li4uYCk7XG4gICAgICAgICAgLy8gUHJvY2VzcyBjaHVua3MgdHJ1bHkgaW4gcGFyYWxsZWwgLSB0cmFjayBhY3RpdmUgY2h1bmtzXG4gICAgICAgICAgY29uc3QgY2h1bmtQcm9taXNlID0gcHJvY2Vzc0F1ZGlvQ2h1bmsodXJpKVxuICAgICAgICAgICAgLmNhdGNoKChlcnJvcikgPT4ge1xuICAgICAgICAgICAgICBsb2dnZXIuZXJyb3IoYFtOYXRpdmVTUl0gRmFpbGVkIHRvIHByb2Nlc3MgY2h1bmsgIyR7Y2h1bmtDb3VudH06YCwgZXJyb3IpO1xuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5maW5hbGx5KCgpID0+IHtcbiAgICAgICAgICAgICAgYWN0aXZlQ2h1bmtzUmVmLmN1cnJlbnQuZGVsZXRlKGNodW5rUHJvbWlzZSk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICBhY3RpdmVDaHVua3NSZWYuY3VycmVudC5hZGQoY2h1bmtQcm9taXNlKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsb2dnZXIud2FybihgW05hdGl2ZVNSXSDimqDvuI8gIE5vIFVSSSBmb3IgY2h1bmsgIyR7Y2h1bmtDb3VudH0gLSBza2lwcGluZyB1cGxvYWRgKTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKGBbTmF0aXZlU1JdIEZhaWxlZCB0byBzdG9wIGNodW5rICMke2NodW5rQ291bnR9OmAsIGVycm9yKTtcbiAgICAgICAgYWN0aXZlUmVjb3JkaW5nUmVmLmN1cnJlbnQgPSBudWxsO1xuICAgICAgICBjb250aW51ZTsgLy8gU2tpcCB0aGlzIGNodW5rLCB0cnkgbmV4dCBvbmVcbiAgICAgIH1cblxuICAgICAgaWYgKCFjb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICAgIGF3YWl0IHNsZWVwKFBPU1RfU1RPUF9DT09MRE9XTl9NUyk7XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIGxvZ2dlci5pbmZvKGBbTmF0aXZlU1JdIPCfj4EgQ2h1bmsgbG9vcCBlbmRlZCAocHJvY2Vzc2VkICR7Y2h1bmtDb3VudH0gY2h1bmtzKWApO1xuICB9LCBbcHJvY2Vzc0F1ZGlvQ2h1bmssIHN0b3BSZWNvcmRlclNhZmVseV0pO1xuXG4gIC8qKlxuICAgKiBTdGFydCBhdWRpbyByZWNvcmRpbmdcbiAgICovXG4gIGNvbnN0IHN0YXJ0UmVjb3JkaW5nID0gdXNlQ2FsbGJhY2soYXN5bmMgKCkgPT4ge1xuICAgIHRyeSB7XG4gICAgICBpZiAoIWF1ZGlvQmFja2VuZCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IobWlzc2luZ0F1ZGlvRXJyb3IpO1xuICAgICAgfVxuXG4gICAgICBsb2dnZXIuaW5mbyhgW05hdGl2ZVNSXSBTdGFydCByZWNvcmRpbmcgKGJhY2tlbmQ9JHthdWRpb0JhY2tlbmQua2luZH0pYCk7XG5cbiAgICAgIC8vIFJlcXVlc3QgcGVybWlzc2lvbnNcbiAgICAgIGNvbnN0IGhhc1Blcm1pc3Npb24gPSBhd2FpdCByZXF1ZXN0UGVybWlzc2lvbnMoKTtcbiAgICAgIGlmICghaGFzUGVybWlzc2lvbikge1xuICAgICAgICBsb2dnZXIud2FybignW05hdGl2ZVNSXSBQZXJtaXNzaW9uIGRlbmllZCcpO1xuICAgICAgICBzcGVlY2hTdGF0dXNDYWxsYmFjayhmYWxzZSwgJycpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGF3YWl0IGF1ZGlvQmFja2VuZC5zZXRBdWRpb01vZGVBc3luYyh7XG4gICAgICAgIGFsbG93c1JlY29yZGluZzogdHJ1ZSxcbiAgICAgICAgcGxheXNJblNpbGVudE1vZGU6IHRydWUsXG4gICAgICAgIHNob3VsZFBsYXlJbkJhY2tncm91bmQ6IGZhbHNlLFxuICAgICAgICBzaG91bGRSb3V0ZVRocm91Z2hFYXJwaWVjZTogZmFsc2UsXG4gICAgICAgIGludGVycnVwdGlvbk1vZGU6ICdkdWNrT3RoZXJzJyxcbiAgICAgIH0pO1xuXG4gICAgICBsb2dnZXIuZGVidWcoJ1tOYXRpdmVTUl0gQXVkaW8gbW9kZSBjb25maWd1cmVkJyk7XG4gICAgICByZXNldFRyYW5zY3JpcHRTdGF0ZSgpO1xuICAgICAgc2V0SXNSZWNvcmRpbmcodHJ1ZSk7XG4gICAgICBzcGVlY2hTdGF0dXNDYWxsYmFjayh0cnVlLCAnJyk7XG5cbiAgICAgIGxvZ2dlci5pbmZvKCdbTmF0aXZlU1JdIPCfjqQgTmF0aXZlIHJlY29yZGluZyBzdGFydGVkJyk7XG4gICAgICBzdGFydENodW5rTG9vcCgpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ1tOYXRpdmVTUl0gRmFpbGVkIHRvIHN0YXJ0IG5hdGl2ZSByZWNvcmRpbmc6JywgZXJyb3IpO1xuICAgICAgaWYgKG9uRXJyb3IpIHtcbiAgICAgICAgb25FcnJvcihlcnJvciBhcyBFcnJvcik7XG4gICAgICB9XG4gICAgICBzcGVlY2hTdGF0dXNDYWxsYmFjayhmYWxzZSwgJycpO1xuICAgIH1cbiAgfSwgW3NwZWVjaFN0YXR1c0NhbGxiYWNrLCBvbkVycm9yLCByZXNldFRyYW5zY3JpcHRTdGF0ZSwgc3RhcnRDaHVua0xvb3BdKTtcblxuICAvKipcbiAgICogU3RvcCBhdWRpbyByZWNvcmRpbmdcbiAgICovXG4gIGNvbnN0IHN0b3BSZWNvcmRpbmcgPSB1c2VDYWxsYmFjayhhc3luYyAoKSA9PiB7XG4gICAgaWYgKCFpc1JlY29yZGluZyB8fCAhYXVkaW9CYWNrZW5kKSByZXR1cm47XG5cbiAgICB0cnkge1xuICAgICAgbG9nZ2VyLmluZm8oJ1tOYXRpdmVTUl0gU3RvcCByZWNvcmRpbmcgcmVxdWVzdGVkJyk7XG4gICAgICBpZiAoY2h1bmtMb29wQWJvcnRSZWYuY3VycmVudCkge1xuICAgICAgICBjaHVua0xvb3BBYm9ydFJlZi5jdXJyZW50LmFib3J0KCk7XG4gICAgICAgIGNodW5rTG9vcEFib3J0UmVmLmN1cnJlbnQgPSBudWxsO1xuICAgICAgfVxuXG4gICAgICBpZiAoYWN0aXZlUmVjb3JkaW5nUmVmLmN1cnJlbnQpIHtcbiAgICAgICAgY29uc3QgY3VycmVudFJlY29yZGluZyA9IGFjdGl2ZVJlY29yZGluZ1JlZi5jdXJyZW50O1xuICAgICAgICBhY3RpdmVSZWNvcmRpbmdSZWYuY3VycmVudCA9IG51bGw7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgbG9nZ2VyLmluZm8oJ1tOYXRpdmVTUl0g4o+577iPICBTdG9wcGluZyBmaW5hbCBhY3RpdmUgcmVjb3JkaW5nLi4uJyk7XG4gICAgICAgICAgY29uc3QgdXJpID0gYXdhaXQgc3RvcFJlY29yZGVyU2FmZWx5KGN1cnJlbnRSZWNvcmRpbmcsICdleHBsaWNpdCBzdG9wJyk7XG4gICAgICAgICAgaWYgKHVyaSkge1xuICAgICAgICAgICAgbG9nZ2VyLmluZm8oJ1tOYXRpdmVTUl0g4pyFIEZpbmFsIHJlY29yZGluZyBVUkk6JywgdXJpKTtcbiAgICAgICAgICAgIGNvbnN0IGNodW5rUHJvbWlzZSA9IHByb2Nlc3NBdWRpb0NodW5rKHVyaSlcbiAgICAgICAgICAgICAgLmNhdGNoKChlcnJvcikgPT4ge1xuICAgICAgICAgICAgICAgIGxvZ2dlci5lcnJvcignW05hdGl2ZVNSXSBGYWlsZWQgdG8gcHJvY2VzcyBmaW5hbCBjaHVuazonLCBlcnJvcik7XG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgIC5maW5hbGx5KCgpID0+IHtcbiAgICAgICAgICAgICAgICBhY3RpdmVDaHVua3NSZWYuY3VycmVudC5kZWxldGUoY2h1bmtQcm9taXNlKTtcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBhY3RpdmVDaHVua3NSZWYuY3VycmVudC5hZGQoY2h1bmtQcm9taXNlKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbG9nZ2VyLndhcm4oJ1tOYXRpdmVTUl0g4pqg77iPICBObyBVUkkgZnJvbSBmaW5hbCByZWNvcmRpbmcnKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgbG9nZ2VyLndhcm4oJ1tOYXRpdmVTUl0gRmFpbGVkIHRvIHN0b3AgYWN0aXZlIHJlY29yZGluZzonLCBlcnJvcik7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgc2V0SXNSZWNvcmRpbmcoZmFsc2UpO1xuXG4gICAgICAvLyBXYWl0IGZvciBhbGwgYWN0aXZlIGNodW5rcyB0byBjb21wbGV0ZSAodHJ1bHkgcGFyYWxsZWwpXG4gICAgICB0cnkge1xuICAgICAgICBsb2dnZXIuaW5mbyhgW05hdGl2ZVNSXSDij7MgV2FpdGluZyBmb3IgJHthY3RpdmVDaHVua3NSZWYuY3VycmVudC5zaXplfSBhY3RpdmUgY2h1bmtzIHRvIGNvbXBsZXRlLi4uYCk7XG4gICAgICAgIGF3YWl0IFByb21pc2UuYWxsKEFycmF5LmZyb20oYWN0aXZlQ2h1bmtzUmVmLmN1cnJlbnQpKTtcbiAgICAgICAgbG9nZ2VyLmluZm8oJ1tOYXRpdmVTUl0g4pyFIEFsbCBjaHVua3MgcHJvY2Vzc2VkJyk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dnZXIud2FybignW05hdGl2ZVNSXSBTb21lIGNodW5rcyBmYWlsZWQ6JywgZXJyb3IpO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBmaW5hbFRleHQgPVxuICAgICAgICBjb21taXR0ZWRUcmFuc2NyaXB0UmVmLmN1cnJlbnQgfHxcbiAgICAgICAgY3VycmVudENodW5rVHJhbnNjcmlwdFJlZi5jdXJyZW50O1xuXG4gICAgICBpZiAoXG4gICAgICAgIHNwZWVjaFRyYW5zbGF0aW9uQ2FsbGJhY2sgJiZcbiAgICAgICAgZmluYWxUZXh0ICYmXG4gICAgICAgIGxhbmd1YWdlICYmXG4gICAgICAgICFsYW5ndWFnZS5zdGFydHNXaXRoKCdlbicpXG4gICAgICApIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCB0cmFuc2xhdGVkID0gYXdhaXQgdHJhbnNsYXRlR3JvcVRleHQoe1xuICAgICAgICAgICAgYXBpS2V5OiBjbGllbnRTZWNyZXQsXG4gICAgICAgICAgICB0ZXh0OiBmaW5hbFRleHQsXG4gICAgICAgICAgICBsYW5ndWFnZSxcbiAgICAgICAgICAgIGVuZHBvaW50OiByZXNvbHZlR3JvcUNoYXRFbmRwb2ludChncm9xQXBpQmFzZVVybCksXG4gICAgICAgICAgfSk7XG4gICAgICAgICAgaWYgKHRyYW5zbGF0ZWQpIHtcbiAgICAgICAgICAgIHNwZWVjaFRyYW5zbGF0aW9uQ2FsbGJhY2sodHJhbnNsYXRlZCk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGxvZ2dlci53YXJuKCdbTmF0aXZlU1JdIEdyb3EgdHJhbnNsYXRpb24gZmFpbGVkOicsIGVycm9yKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBsb2dnZXIuaW5mbyhgW05hdGl2ZVNSXSBGaW5hbCB0ZXh0IGxlbmd0aDogJHtmaW5hbFRleHQubGVuZ3RofWApO1xuICAgICAgc3BlZWNoU3RhdHVzQ2FsbGJhY2soZmFsc2UsICcnKTtcbiAgICAgIGlmIChmaW5hbFRleHQpIHtcbiAgICAgICAgc3BlZWNoUmVzdWx0Q2FsbGJhY2soZmluYWxUZXh0KTtcbiAgICAgIH1cblxuICAgICAgcmVzZXRUcmFuc2NyaXB0U3RhdGUoKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdbTmF0aXZlU1JdIEZhaWxlZCB0byBzdG9wIG5hdGl2ZSByZWNvcmRpbmc6JywgZXJyb3IpO1xuICAgICAgaWYgKG9uRXJyb3IpIHtcbiAgICAgICAgb25FcnJvcihlcnJvciBhcyBFcnJvcik7XG4gICAgICB9XG4gICAgICBzcGVlY2hTdGF0dXNDYWxsYmFjayhmYWxzZSwgJycpO1xuICAgIH1cbiAgfSwgW1xuICAgIGlzUmVjb3JkaW5nLFxuICAgIGNsaWVudFNlY3JldCxcbiAgICBncm9xQXBpQmFzZVVybCxcbiAgICBsYW5ndWFnZSxcbiAgICBvbkVycm9yLFxuICAgIHByb2Nlc3NBdWRpb0NodW5rLFxuICAgIHJlc2V0VHJhbnNjcmlwdFN0YXRlLFxuICAgIHNwZWVjaFJlc3VsdENhbGxiYWNrLFxuICAgIHNwZWVjaFN0YXR1c0NhbGxiYWNrLFxuICAgIHNwZWVjaFRyYW5zbGF0aW9uQ2FsbGJhY2ssXG4gICAgc3RvcFJlY29yZGVyU2FmZWx5LFxuICBdKTtcblxuICAvKipcbiAgICogSGFuZGxlIHNob3VsZExpc3RlbiBwcm9wIGNoYW5nZXNcbiAgICovXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgbG9nZ2VyLmRlYnVnKGBbTmF0aXZlU1JdIHNob3VsZExpc3Rlbj0ke3Nob3VsZExpc3Rlbn0gaXNSZWNvcmRpbmc9JHtpc1JlY29yZGluZ31gKTtcbiAgICBpZiAoc2hvdWxkTGlzdGVuICYmICFpc1JlY29yZGluZykge1xuICAgICAgc3RhcnRSZWNvcmRpbmcoKTtcbiAgICB9IGVsc2UgaWYgKCFzaG91bGRMaXN0ZW4gJiYgaXNSZWNvcmRpbmcpIHtcbiAgICAgIHN0b3BSZWNvcmRpbmcoKTtcbiAgICB9XG4gIH0sIFtzaG91bGRMaXN0ZW4sIGlzUmVjb3JkaW5nLCBzdGFydFJlY29yZGluZywgc3RvcFJlY29yZGluZ10pO1xuXG4gIC8qKlxuICAgKiBDbGVhbnVwIG9uIHVubW91bnRcbiAgICovXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGlmIChjaHVua0xvb3BBYm9ydFJlZi5jdXJyZW50KSB7XG4gICAgICAgIGxvZ2dlci5pbmZvKCdbTmF0aXZlU1JdIENsZWFudXA6IGFib3J0aW5nIGNodW5rIGxvb3AnKTtcbiAgICAgICAgY2h1bmtMb29wQWJvcnRSZWYuY3VycmVudC5hYm9ydCgpO1xuICAgICAgfVxuICAgICAgaWYgKHN0cmVhbUFib3J0UmVmLmN1cnJlbnQpIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oJ1tOYXRpdmVTUl0gQ2xlYW51cDogYWJvcnRpbmcgc3RyZWFtJyk7XG4gICAgICAgIHN0cmVhbUFib3J0UmVmLmN1cnJlbnQuYWJvcnQoKTtcbiAgICAgIH1cbiAgICAgIGlmIChhY3RpdmVSZWNvcmRpbmdSZWYuY3VycmVudCAmJiBhdWRpb0JhY2tlbmQpIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oJ1tOYXRpdmVTUl0gQ2xlYW51cDogc3RvcHBpbmcgYWN0aXZlIHJlY29yZGluZycpO1xuICAgICAgICBzdG9wUmVjb3JkZXJTYWZlbHkoYWN0aXZlUmVjb3JkaW5nUmVmLmN1cnJlbnQsICdjbGVhbnVwJykuY2F0Y2goKCkgPT4gdW5kZWZpbmVkKTtcbiAgICAgIH1cbiAgICB9O1xuICB9LCBbc3RvcFJlY29yZGVyU2FmZWx5XSk7XG5cbiAgcmV0dXJuIDxWaWV3IC8+O1xufTtcblxuZnVuY3Rpb24gc2xlZXAobXM6IG51bWJlcikge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgbXMpKTtcbn1cblxuZnVuY3Rpb24gaXNCZW5pZ25TdG9wRXJyb3IoZXJyb3I6IHVua25vd24pOiBib29sZWFuIHtcbiAgY29uc3QgbWVzc2FnZSA9XG4gICAgZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiB0eXBlb2YgZXJyb3IgPT09ICdzdHJpbmcnID8gZXJyb3IgOiAnJztcbiAgcmV0dXJuIC9zdG9wIGZhaWxlZHxhbHJlYWR5fG5vdCByZWNvcmRpbmcvaS50ZXN0KG1lc3NhZ2UpO1xufVxuXG4vKipcbiAqIENvbnZlcnQgYmFzZTY0IHN0cmluZyB0byBCbG9iXG4gKi9cbmZ1bmN0aW9uIGdldEZpbGVOYW1lRnJvbVVyaSh1cmk6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IGNsZWFuVXJpID0gdXJpLnNwbGl0KCc/JylbMF07XG4gIGNvbnN0IGxhc3RTbGFzaCA9IGNsZWFuVXJpLmxhc3RJbmRleE9mKCcvJyk7XG4gIGNvbnN0IGZpbGVOYW1lID0gbGFzdFNsYXNoID49IDAgPyBjbGVhblVyaS5zbGljZShsYXN0U2xhc2ggKyAxKSA6IGNsZWFuVXJpO1xuICBpZiAoZmlsZU5hbWUgJiYgZmlsZU5hbWUuaW5jbHVkZXMoJy4nKSkge1xuICAgIHJldHVybiBmaWxlTmFtZTtcbiAgfVxuICByZXR1cm4gYGF1ZGlvLSR7RGF0ZS5ub3coKX0ubTRhYDtcbn1cblxuZnVuY3Rpb24gZ2V0TWltZVR5cGVGcm9tTmFtZShuYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBleHQgPSBuYW1lLnNwbGl0KCcuJykucG9wKCk/LnRvTG93ZXJDYXNlKCkgfHwgJyc7XG4gIHN3aXRjaCAoZXh0KSB7XG4gICAgY2FzZSAnbTRhJzpcbiAgICAgIHJldHVybiAnYXVkaW8vbTRhJztcbiAgICBjYXNlICdhYWMnOlxuICAgICAgcmV0dXJuICdhdWRpby9hYWMnO1xuICAgIGNhc2UgJ21wMyc6XG4gICAgICByZXR1cm4gJ2F1ZGlvL21wZWcnO1xuICAgIGNhc2UgJ21wNCc6XG4gICAgICByZXR1cm4gJ2F1ZGlvL21wNCc7XG4gICAgY2FzZSAnd2F2JzpcbiAgICAgIHJldHVybiAnYXVkaW8vd2F2JztcbiAgICBjYXNlICd3ZWJtJzpcbiAgICAgIHJldHVybiAnYXVkaW8vd2VibSc7XG4gICAgY2FzZSAnb2dnJzpcbiAgICAgIHJldHVybiAnYXVkaW8vb2dnJztcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuICdhdWRpby9tNGEnO1xuICB9XG59XG5cbmZ1bmN0aW9uIHJlc29sdmVHcm9xVHJhbnNjcmlwdGlvbkVuZHBvaW50KFxuICBiYXNlVXJsPzogc3RyaW5nLFxuICBvdmVycmlkZUVuZHBvaW50Pzogc3RyaW5nLFxuKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgaWYgKG92ZXJyaWRlRW5kcG9pbnQpIHJldHVybiBvdmVycmlkZUVuZHBvaW50O1xuICBpZiAoIWJhc2VVcmwpIHJldHVybiB1bmRlZmluZWQ7XG4gIGNvbnN0IHRyaW1tZWQgPSBiYXNlVXJsLnJlcGxhY2UoL1xcLyskLywgJycpO1xuICBpZiAodHJpbW1lZC5lbmRzV2l0aCgnL29wZW5haS92MScpKSB7XG4gICAgcmV0dXJuIGAke3RyaW1tZWR9L2F1ZGlvL3RyYW5zY3JpcHRpb25zYDtcbiAgfVxuICByZXR1cm4gYCR7dHJpbW1lZH0vb3BlbmFpL3YxL2F1ZGlvL3RyYW5zY3JpcHRpb25zYDtcbn1cblxuZnVuY3Rpb24gcmVzb2x2ZUdyb3FDaGF0RW5kcG9pbnQoYmFzZVVybD86IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGlmICghYmFzZVVybCkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgY29uc3QgdHJpbW1lZCA9IGJhc2VVcmwucmVwbGFjZSgvXFwvKyQvLCAnJyk7XG4gIGlmICh0cmltbWVkLmVuZHNXaXRoKCcvb3BlbmFpL3YxJykpIHtcbiAgICByZXR1cm4gYCR7dHJpbW1lZH0vY2hhdC9jb21wbGV0aW9uc2A7XG4gIH1cbiAgcmV0dXJuIGAke3RyaW1tZWR9L29wZW5haS92MS9jaGF0L2NvbXBsZXRpb25zYDtcbn1cblxuZnVuY3Rpb24gbm9ybWFsaXplRmlsZVVyaSh1cmk6IHN0cmluZyk6IHN0cmluZyB7XG4gIGlmICghdXJpKSByZXR1cm4gdXJpO1xuICBpZiAodXJpLnN0YXJ0c1dpdGgoJ2ZpbGU6Ly8nKSB8fCB1cmkuc3RhcnRzV2l0aCgnY29udGVudDovLycpKSB7XG4gICAgcmV0dXJuIHVyaTtcbiAgfVxuICBpZiAodXJpLnN0YXJ0c1dpdGgoJy8nKSkge1xuICAgIHJldHVybiBgZmlsZTovLyR7dXJpfWA7XG4gIH1cbiAgcmV0dXJuIHVyaTtcbn1cblxuLyoqXG4gKiBDaGVjayBpZiBuYXRpdmUgc3BlZWNoIHJlY29nbml0aW9uIGlzIGF2YWlsYWJsZVxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNOYXRpdmVTcGVlY2hBdmFpbGFibGUoKTogYm9vbGVhbiB7XG4gIHJldHVybiAoUGxhdGZvcm0uT1MgPT09ICdpb3MnIHx8IFBsYXRmb3JtLk9TID09PSAnYW5kcm9pZCcpICYmICEhYXVkaW9CYWNrZW5kO1xufVxuXG4vKipcbiAqIEdldCBuYXRpdmUgc3BlZWNoIGNhcGFiaWxpdGllc1xuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TmF0aXZlU3BlZWNoQ2FwYWJpbGl0aWVzKCkge1xuICByZXR1cm4ge1xuICAgIGF2YWlsYWJsZTogaXNOYXRpdmVTcGVlY2hBdmFpbGFibGUoKSxcbiAgICBwbGF0Zm9ybTogUGxhdGZvcm0uT1MsXG4gICAgc3VwcG9ydHNWQUQ6IHRydWUsXG4gICAgc3VwcG9ydHNBdWRpb0xldmVsczogdHJ1ZSxcbiAgICByZXF1aXJlc0V4cG9BdWRpbzogdHJ1ZSxcbiAgICBiYWNrZW5kOiBhdWRpb0JhY2tlbmQ/LmtpbmQgPz8gJ25vbmUnLFxuICB9O1xufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7OztBQU9BLElBQUFBLE1BQUEsR0FBQUMsdUJBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFDLFlBQUEsR0FBQUQsT0FBQTtBQWFBLElBQUFFLE9BQUEsR0FBQUMsc0JBQUEsQ0FBQUgsT0FBQTtBQUNB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LEVBQUUsRUFBRSxDQUFDO0VBQzNDLElBQUlELE9BQU8sQ0FBQ0UsUUFBUSxDQUFDLFlBQVksQ0FBQyxFQUFFO0lBQ2xDLE9BQU8sR0FBR0YsT0FBTyx1QkFBdUI7RUFDMUM7RUFDQSxPQUFPLEdBQUdBLE9BQU8saUNBQWlDO0FBQ3BEO0FBRUEsU0FBU3ZCLHVCQUF1QkEsQ0FBQ3FCLE9BQWdCLEVBQXNCO0VBQ3JFLElBQUksQ0FBQ0EsT0FBTyxFQUFFLE9BQU9wQixTQUFTO0VBQzlCLE1BQU1zQixPQUFPLEdBQUdGLE9BQU8sQ0FBQ0csT0FBTyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUM7RUFDM0MsSUFBSUQsT0FBTyxDQUFDRSxRQUFRLENBQUMsWUFBWSxDQUFDLEVBQUU7SUFDbEMsT0FBTyxHQUFHRixPQUFPLG1CQUFtQjtFQUN0QztFQUNBLE9BQU8sR0FBR0EsT0FBTyw2QkFBNkI7QUFDaEQ7QUFFQSxTQUFTdkYsZ0JBQWdCQSxDQUFDN0QsR0FBVyxFQUFVO0VBQzdDLElBQUksQ0FBQ0EsR0FBRyxFQUFFLE9BQU9BLEdBQUc7RUFDcEIsSUFBSUEsR0FBRyxDQUFDMEgsVUFBVSxDQUFDLFNBQVMsQ0FBQyxJQUFJMUgsR0FBRyxDQUFDMEgsVUFBVSxDQUFDLFlBQVksQ0FBQyxFQUFFO0lBQzdELE9BQU8xSCxHQUFHO0VBQ1o7RUFDQSxJQUFJQSxHQUFHLENBQUMwSCxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUU7SUFDdkIsT0FBTyxVQUFVMUgsR0FBRyxFQUFFO0VBQ3hCO0VBQ0EsT0FBT0EsR0FBRztBQUNaOztBQUVBO0FBQ0E7QUFDQTtBQUNPLFNBQVN1Six1QkFBdUJBLENBQUEsRUFBWTtFQUNqRCxPQUFPLENBQUNDLHFCQUFRLENBQUNDLEVBQUUsS0FBSyxLQUFLLElBQUlELHFCQUFRLENBQUNDLEVBQUUsS0FBSyxTQUFTLEtBQUssQ0FBQyxDQUFDdEosWUFBWTtBQUMvRTs7QUFFQTtBQUNBO0FBQ0E7QUFDTyxTQUFTdUosMkJBQTJCQSxDQUFBLEVBQUc7RUFDNUMsT0FBTztJQUNMQyxTQUFTLEVBQUVKLHVCQUF1QixDQUFDLENBQUM7SUFDcENLLFFBQVEsRUFBRUoscUJBQVEsQ0FBQ0MsRUFBRTtJQUNyQkksV0FBVyxFQUFFLElBQUk7SUFDakJDLG1CQUFtQixFQUFFLElBQUk7SUFDekJDLGlCQUFpQixFQUFFLElBQUk7SUFDdkJDLE9BQU8sRUFBRTdKLFlBQVksRUFBRWIsSUFBSSxJQUFJO0VBQ2pDLENBQUM7QUFDSCIsImlnbm9yZUxpc3QiOltdfQ==
|