PageSourceSearch

https://factchecker.cloud/src/components/Documents/DocumentUpload.tsx

js factchecker.cloud collected 2026-10-02 01:21:18 UTC 83,863 bytes, 790 lines download raw bytes

1import { createHotContext as __vite__createHotContext } from "/@vite/client";import.meta.hot = __vite__createHotContext("/src/components/Documents/DocumentUpload.tsx");const useState = __vite__cjsImport0_react["useState"]; const useEffect = __vite__cjsImport0_react["useEffect"]; const useCallback = __vite__cjsImport0_react["useCallback"]; const useRef = __vite__cjsImport0_react["useRef"];const _jsxDEV = __vite__cjsImport13_react_jsxDevRuntime["jsxDEV"]; const _Fragment = __vite__cjsImport13_react_jsxDevRuntime["Fragment"];import __vite__cjsImport0_react from "/node_modules/.vite/deps/react.js?v=ae253003";
2import axios from "/node_modules/.vite/deps/axios.js?v=ae253003";
3import { App as AntdApp, Modal, Upload, Select, Space, Typography, Progress, Switch, Button } from "/node_modules/.vite/deps/antd.js?v=ae253003";
4import { InboxOutlined } from "/node_modules/.vite/deps/@ant-design_icons.js?v=ae253003";
5import { uploadDocument } from "/src/api/documents.ts";
6import { watchVectorization } from "/src/store/vectorizationWatchStore.ts";
7import { useDocumentStore } from "/src/store/documentStore.ts";
8import { flattenCategories } from "/src/utils/categoryUtils.ts";
9import { extractErrorMessage } from "/src/utils/errorUtils.ts";
10import { FILE_UPLOAD, MODAL_WIDTH, STORAGE_KEYS } from "/src/constants.ts";
11import { getFileExtension } from "/src/utils/fileUtils.ts";
12import { formatFileSize } from "/src/utils/formatters.ts";
13import { safeGetItem, safeSetItem } from "/src/utils/storageUtils.ts";
14var _jsxFileName = "/home/bang/datatek_rag/frontend/src/components/Documents/DocumentUpload.tsx";
15import __vite__cjsImport13_react_jsxDevRuntime from "/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=ae253003";
16var _s = $RefreshSig$();
17const { Dragger } = Upload;
18const { Text } = Typography;
19// axios 취소 에러인지 견고하게 판별한다. axios.isCancel 외에 환경에 따라 name 만 다른
20// CanceledError / AbortError 도 함께 인정한다.
21function isAbortError(err) {
22	if (axios.isCancel(err)) return true;
23	const name = err?.name;
24	return name === "CanceledError" || name === "AbortError";
25}
26export default function DocumentUpload({ open, onClose, onSuccess, defaultCategoryId }) {
27	_s();
28	const { message: messageApi } = AntdApp.useApp();
29	// Why: useDocumentStore() 전체 구독 → 100MB 업로드 progress 이벤트 도중 store 의 모든
30	// mutation 이 dragger + per-file Progress 재렌더 storm. R167/R168/R175 패턴으로 categories
31	// slice 만 구독.
32	const categories = useDocumentStore((s) => s.categories);
33	const [uploading, setUploading] = useState(false);
34	const [fileList, setFileList] = useState([]);
35	const [selectedCategory, setSelectedCategory] = useState(undefined);
36	const [uploadStatuses, setUploadStatuses] = useState({});
37	// Why: R180 의 "preserve autoUpload across reopen" 의도가 useState lazy init 이라
38	// DocumentUpload component 가 unmount 되면 (parent 의 open=false 가 modal 닫고 component 도
39	// unmount) 다음 mount 시 default true 로 reset 되어 사용자 preference silent 손실. localStorage
40	// 로 영구 보존. 'true' / 'false' 명시 문자열 사용.
41	const [autoUpload, setAutoUpload] = useState(() => {
42		const stored = safeGetItem(STORAGE_KEYS.DOCUMENT_UPLOAD_AUTO);
43		if (stored === "false") return false;
44		return true;
45	});
46	// Why: 초기 mount 시 useState init 으로 읽은 값을 즉시 다시 setItem 하면 cross-tab 시나리오
47	// 에서 다른 탭이 더 최신값으로 바꾼 직후 본 탭이 mount 하면 stale 값으로 덮어쓰기 가능.
48	// (storage event listener 가 없어 다른 탭의 변경을 안 보는 상황 + 본 탭의 mount 가 그 이후라도
49	// useState init 이 cache hit 으로 옛 값 읽음.) 첫 mount 의 redundant write 를 skip.
50	const autoUploadDidMountRef = useRef(false);
51	// Why: 진행 중인 업로드 run 을 중단하기 위한 AbortController. 모든 in-flight axios 요청에
52	// 같은 signal 을 넘겨 한 번의 abort() 로 현재 파일을 취소하고 루프를 빠져나간다.
53	const abortRef = useRef(null);
54	useEffect(() => {
55		if (!autoUploadDidMountRef.current) {
56			autoUploadDidMountRef.current = true;
57			return;
58		}
59		safeSetItem(STORAGE_KEYS.DOCUMENT_UPLOAD_AUTO, autoUpload ? "true" : "false");
60	}, [autoUpload]);
61	// Why: 이전엔 lazy-
62	// init 으로만 읽어 Tab A 가 auto-upload OFF 로 바꿔도 이미 mount 된 Tab B 또는
63	// 즉시 reopen 시 stale true 유지 → 사용자가 Tab B 에서 파일 드래그 시 의도치
64	// 않은 자동 업로드 발사 (Tab A 의 preference 무시). storage event 로 cross-tab
65	// sync — 다른 탭의 변경 즉시 반영.
66	useEffect(() => {
67		const onStorage = (e) => {
68			if (e.key === STORAGE_KEYS.DOCUMENT_UPLOAD_AUTO && e.newValue !== null) {
69				const next = e.newValue === "true";
70				setAutoUpload(next);
71			}
72		};
73		window.addEventListener("storage", onStorage);
74		return () => window.removeEventListener("storage", onStorage);
75	}, []);
76	// Why: 업로드 진행 중에 컴포넌트가 unmount 되면(부모가 모달을 제거) in-flight 요청이 남아
77	// 완료 시 setUploadStatuses 가 사라진 컴포넌트에 setState 를 시도한다. unmount 시 남은
78	// 컨트롤러를 abort 해 요청을 끊고 setState-after-unmount 를 방지한다.
79	useEffect(() => {
80		return () => {
81			abortRef.current?.abort();
82		};
83	}, []);
84	// 모달이 닫혔다 다시 열리면 이전 세션의 fileList / uploadStatuses / selectedCategory 가
85	// 그대로 남아 있어 사용자 혼동 유발. open === true 로 바뀔 때 초기화.
86	// Why: autoUpload 는 사용자가 명시적으로 끈 환경 (대량 검토 후 명시적 Upload 클릭) 에서
87	// 모달 닫고 다시 열면 silent 로 다시 켜져, 다음 드래그 시 의도치 않은 자동 업로드 발생. 다른
88	// session-state (fileList 등) 와 달리 사용자 preference 라 reset 대상에서 제외해 보존.
89	// queueMicrotask: setState 가 effect body 에 직접 있으면 React 19 set-state-in-effect 가
90	// 잡는다. microtask 로 옮기면 commit 직후 같은 tick 에 실행되어 사용자 체감 동일.
91	useEffect(() => {
92		if (!open) return;
93		queueMicrotask(() => {
94			setFileList([]);
95			// Why: undefined 로 리셋하면 카테고리 필터가 걸린 목록에서 연 모달이 문맥을 잃는다
96			// (딥링크 유입 + 성공 후 자동 닫힘 → 재오픈 모두). 페이지가 넘겨준 기본값으로 복원.
97			setSelectedCategory(defaultCategoryId);
98			setUploadStatuses({});
99		});
100	}, [open, defaultCategoryId]);
101	const categoryOptions = flattenCategories(categories);
102	const uploadProps = {
103		name: "file",
104		multiple: true,
105		fileList,
106		disabled: uploading,
107		beforeUpload: (file) => {
108			// Clear old status traces when user selects new files after a previous upload run
109			if (!uploading && Object.keys(uploadStatuses).length > 0) {
110				setUploadStatuses({});
111			}
112			// Check file type
113			const fileExt = getFileExtension(file.name);
114			if (!fileExt || !FILE_UPLOAD.SUPPORTED_TYPES.includes(fileExt)) {
115				messageApi.error(`지원하지 않는 파일 형식: ${fileExt || "알 수 없음"}. 지원 형식: ${FILE_UPLOAD.SUPPORTED_TYPES.join(", ")}`);
116				return Upload.LIST_IGNORE;
117			}
118			// Check file size
119			if (file.size > FILE_UPLOAD.MAX_SIZE) {
120				messageApi.error(`"${file.name}" 파일이 너무 큽니다. 최대 크기: ${formatFileSize(FILE_UPLOAD.MAX_SIZE)}`);
121				return Upload.LIST_IGNORE;
122			}
123			let canAdd = true;
124			let duplicate = false;
125			// Check file count (use latest state inside updater to avoid stale length with multi-select)
126			setFileList((prev) => {
127				// 같은 name+size+lastModified 조합이 이미 리스트에 있으면 중복 추가 막기.
128				// Antd 의 uid 는 매번 새로 생겨 dedup 역할을 못하므로 별도 키로 비교.
129				const fileKey = `${file.name}:${file.size}:${file.lastModified ?? ""}`;
130				const alreadyPresent = prev.some((f) => `${f.name}:${f.size ?? ""}:${f.lastModified ?? ""}` === fileKey);
131				if (alreadyPresent) {
132					duplicate = true;
133					return prev;
134				}
135				const activeCount = prev.filter((f) => uploadStatuses[f.uid]?.status !== "success").length;
136				if (activeCount >= FILE_UPLOAD.MAX_FILES) {
137					canAdd = false;
138					return prev;
139				}
140				// Why: RcFile(진짜 File)을 fileList 에 그대로 담으면, 이후 진행률/상태 갱신이
141				// `{ ...f, status: 'uploading' }` 로 스프레드하는 순간 File 이 아닌 평범한 객체가
142				// 된다. File 의 name/size/데이터는 프로토타입 getter 라 스프레드가 복사하지 못한다
143				// (own enumerable 이 비어 있다). 그 객체를 FormData 에 넣으면 "[object Object]"
144				// 문자열이 되어 filename 없는 파트로 전송되고, 서버가 422 "Expected UploadFile,
145				// received: str" 를 낸다. 첫 시도는 원본이라 성공하고 재시도만 실패해 추적이
146				// 어려웠다(운영 로그 201 -> 409 -> 422).
147				// antd 규약대로 UploadFile 래퍼에 담고 원본은 originFileObj 로 참조를 보존한다.
148				// 스프레드는 래퍼만 복사하고 originFileObj 는 참조로 살아남는다.
149				const wrapped = {
150					uid: file.uid,
151					name: file.name,
152					size: file.size,
153					type: file.type,
154					originFileObj: file
155				};
156				return [...prev, wrapped];
157			});
158			if (duplicate) {
159				messageApi.info({
160					key: `upload-dup-${file.name}`,
161					content: `"${file.name}" 은(는) 이미 대기열에 있습니다.`
162				});
163				return Upload.LIST_IGNORE;
164			}
165			if (!canAdd) {
166				messageApi.error({
167					key: "upload-max-files",
168					content: `한 번에 최대 ${FILE_UPLOAD.MAX_FILES}개까지 업로드할 수 있습니다`
169				});
170				return Upload.LIST_IGNORE;
171			}
172			return false;
173		},
174		onRemove: (file) => {
175			if (uploading) return false;
176			setFileList((prev) => prev.filter((f) => f.uid !== file.uid));
177			setUploadStatuses((prev) => {
178				const next = { ...prev };
179				delete next[file.uid];
180				return next;
181			});
182		}
183	};
184	const handleCancel = useCallback(() => {
185		// Why: 업로드 중 X/Esc/mask 클릭/취소로 닫기를 시도하면, 이제는 막는 대신 진행 중인 run 을
186		// abort 한다. handleUpload 의 abort 처리 분기가 이미 올라간 파일을 보존한 채 uploading=false
187		// 로 정리하고 안내를 띄운다. 여기서는 abort 만 트리거하고 실제 닫기/리셋은 사용자가 한 번
188		// 더 닫을 때(또는 abort 처리 후) 수행한다. abort 직후 곧장 onClose 하면 루프가 아직
189		// setUploading(false) 전이라 닫힌 뒤 토스트가 뜨는 옛 혼란이 재발하므로 닫지 않는다.
190		if (uploading) {
191			abortRef.current?.abort();
192			return;
193		}
194		setFileList([]);
195		setSelectedCategory(undefined);
196		setUploadStatuses({});
197		onClose();
198	}, [onClose, uploading]);
199	const handleUpload = useCallback(async () => {
200		if (uploading) {
201			return;
202		}
203		if (fileList.length === 0) {
204			messageApi.warning("업로드할 파일을 1개 이상 선택해주세요");
205			return;
206		}
207		// Why: 혼합 성공/실패 후 사용자가 추가 파일을 드래그하면 beforeUpload 가
208		// uploadStatuses 를 비워 effect 가 handleUpload 를 재실행한다. 이때 성공했던
209		// 파일도 다시 POST /documents/upload 로 올라가 서버에 중복 생성. fileList 의
210		// status='done' 는 남아있으므로 그것으로 성공분을 식별해 skip & 표시 유지.
211		const initialStatuses = fileList.reduce((acc, file) => {
212			if (file.status === "done") {
213				acc[file.uid] = {
214					status: "success",
215					progress: 100,
216					name: file.name
217				};
218			} else {
219				acc[file.uid] = {
220					status: "queued",
221					progress: 0,
222					name: file.name
223				};
224			}
225			return acc;
226		}, {});
227		setUploadStatuses(initialStatuses);
228		// 이번 업로드 run 의 AbortController. "업로드 중단" 버튼 / handleCancel 이 이 signal 을
229		// abort 한다. 모든 파일이 같은 signal 을 공유하므로 한 번의 abort 로 현재 파일이 취소된다.
230		const controller = new AbortController();
231		abortRef.current = controller;
232		setUploading(true);
233		// 업로드 시작 시점의 카테고리 선택을 로컬 변수로 캡처한다. 사용자가 루프 진행 중에
234		// 카테고리 드롭다운을 바꾸면 남은 파일들이 새 카테고리로 올라가 "카테고리 A 선택했는데
235		// B 로 들어감" 이라는 혼란이 발생. 루프 전체가 한 번의 선택으로 일관되게 업로드되게 고정.
236		const uploadCategoryId = selectedCategory;
237		try {
238			let successful = 0;
239			let failed = 0;
240			for (const file of fileList) {
241				// 이미 성공한 파일은 서버 재업로드 방지를 위해 건너뜀 (initialStatuses 에서 'success' 로 보존됨).
242				if (file.status === "done") continue;
243				const uid = file.uid;
244				const rawFile = file.originFileObj;
245				const fileToUpload = rawFile ?? file;
246				const displayName = uploadStatuses[uid]?.name || file.name;
247				// Mark as uploading
248				setUploadStatuses((prev) => ({
249					...prev,
250					[uid]: {
251						...prev[uid] || {
252							progress: 0,
253							name: displayName
254						},
255						status: "uploading",
256						progress: prev[uid]?.progress ?? 0,
257						name: displayName
258					}
259				}));
260				setFileList((prev) => {
261					const existing = prev.find((p) => p.uid === uid);
262					return prev.map((f) => f.uid === uid ? {
263						...f,
264						name: f.name || displayName,
265						status: "uploading",
266						percent: existing?.percent ?? 0
267					} : f);
268				});
269				try {
270					const uploaded = await uploadDocument(fileToUpload, uploadCategoryId, {
271						timeout: FILE_UPLOAD.UPLOAD_TIMEOUT_MS,
272						signal: controller.signal,
273						onUploadProgress: (event) => {
274							// Why: HTTP/2 + gzip 또는 chunked transfer 환경에서 final progress 이벤트가
275							// event.loaded > event.total (101~105%) 또는 event.total === 0 (NaN/Infinity) 로
276							// 도달하는 사례가 있다. 그대로 antd Progress 에 넘기면 막대가 overshoot 되거나
277							// 영원히 "Uploading" 상태로 남는다. 0~100 클램프 + total=0 시 undefined 반환.
278							const rawPercent = event.total ? Math.round(event.loaded / event.total * 100) : undefined;
279							const percent = rawPercent === undefined || !Number.isFinite(rawPercent) ? undefined : Math.min(100, Math.max(0, rawPercent));
280							// Why: onUploadProgress 는 XHR 바이트마다 trigger 될 수 있어, 큰 파일 업로드
281							// 시 초당 수백~수천 번 두 개의 setState 가 실행돼 render storm 이 발생했다.
282							// 백분율이 바뀌지 않았으면 state update 를 건너뛰어 렌더 횟수를 0~100 회로 제한.
283							setUploadStatuses((prev) => {
284								const prevProgress = prev[uid]?.progress;
285								const nextProgress = percent ?? prevProgress ?? 0;
286								if (prev[uid]?.status === "uploading" && prevProgress === nextProgress) {
287									return prev;
288								}
289								return {
290									...prev,
291									[uid]: {
292										...prev[uid] || {
293											status: "uploading",
294											progress: 0,
295											name: displayName
296										},
297										status: "uploading",
298										progress: nextProgress,
299										name: displayName
300									}
301								};
302							});
303							setFileList((prev) => {
304								const existing = prev.find((p) => p.uid === uid);
305								const nextPercent = percent ?? existing?.percent;
306								if (existing && existing.status === "uploading" && existing.percent === nextPercent) {
307									return prev;
308								}
309								return prev.map((f) => f.uid === uid ? {
310									...f,
311									name: f.name || displayName,
312									status: "uploading",
313									percent: nextPercent
314								} : f);
315							});
316						}
317					});
318					// 벡터화가 끝나면 어느 화면에 있든 알림(VectorizationNotifier).
319					watchVectorization([{
320						id: uploaded.id,
321						filename: uploaded.filename
322					}]);
323					successful += 1;
324					setUploadStatuses((prev) => ({
325						...prev,
326						[uid]: {
327							...prev[uid] || {
328								progress: 0,
329								name: displayName
330							},
331							status: "success",
332							progress: 100,
333							name: displayName
334						}
335					}));
336					setFileList((prev) => prev.map((f) => f.uid === uid ? {
337						...f,
338						name: f.name || displayName,
339						status: "done",
340						percent: 100
341					} : f));
342				} catch (error) {
343					// 사용자가 중단(abort)한 경우: 에러가 아니라 "중단됨" 으로 다룬다. 진행 중이던 파일과
344					// 아직 대기열에 남은 파일을 'cancelled' 로 표기하고 루프를 빠져나간다. 이미 성공한
345					// 파일(앞쪽 batch)은 서버에 영속화됐으므로 'success' 그대로 둔다.
346					if (isAbortError(error) || controller.signal.aborted) {
347						// 진행 중 + 대기열 파일을 'cancelled' 로. 이미 'success' 인 파일은 그대로 보존한다.
348						setUploadStatuses((prev) => {
349							const next = { ...prev };
350							for (const [key, st] of Object.entries(prev)) {
351								if (st.status === "uploading" || st.status === "queued") {
352									next[key] = {
353										...st,
354										status: "cancelled"
355									};
356								}
357							}
358							return next;
359						});
360						// Why: fileList 의 status 는 antd Dragger 내부 표기에만 쓰인다. 중단 파일을 'error'
361						// 로 두면 빨간 실패로 보이므로, 성공분('done')은 유지하고 나머지는 중립 상태로 되돌린다.
362						setFileList((prev) => prev.map((f) => f.status === "done" ? f : {
363							...f,
364							status: undefined,
365							error: undefined
366						}));
367						messageApi.info("업로드를 중단했습니다. 이미 올라간 파일은 유지됩니다.");
368						break;
369					}
370					failed += 1;
371					const errorMessage = extractErrorMessage(error, "업로드에 실패했습니다");
372					setUploadStatuses((prev) => ({
373						...prev,
374						[uid]: {
375							...prev[uid] || {
376								progress: 0,
377								name: displayName
378							},
379							status: "error",
380							error: errorMessage,
381							progress: prev[uid]?.progress ?? 0,
382							name: displayName
383						}
384					}));
385					setFileList((prev) => prev.map((f) => f.uid === uid ? {
386						...f,
387						name: f.name || displayName,
388						status: "error",
389						error: new Error(errorMessage)
390					} : f));
391				}
392			}
393			// Why: 중단되면 success/warning/error 요약 토스트와 onSuccess()/handleCancel() 을 건너뛴다.
394			// 이미 성공한 파일이 있었더라도 onSuccess(reload)는 사용자가 모달을 닫을 때 일어나도 늦지
395			// 않으며, 중단 직후 추가 토스트로 사용자를 혼란스럽게 하지 않기 위함.
396			if (controller.signal.aborted) {
397				// 성공분이 있었으면 부모 목록을 갱신해 색인 상태를 볼 수 있게 한다.
398				if (successful > 0) {
399					onSuccess();
400				}
401			} else if (failed === 0) {
402				messageApi.success(`${successful}개 파일이 모두 업로드되었습니다`);
403				onSuccess();
404				handleCancel();
405			} else if (successful > 0) {
406				messageApi.warning(`${successful}개 업로드 완료, ${failed}개 실패`);
407				onSuccess();
408			} else {
409				messageApi.error("모든 업로드가 실패했습니다");
410			}
411		} catch (error) {
412			messageApi.error(extractErrorMessage(error, "업로드에 실패했습니다"));
413		} finally {
414			setUploading(false);
415			// 이번 run 의 컨트롤러 정리. 이후 unmount cleanup 이 stale 컨트롤러를 abort 하지 않도록.
416			if (abortRef.current === controller) {
417				abortRef.current = null;
418			}
419		}
420	}, [
421		fileList,
422		messageApi,
423		onSuccess,
424		selectedCategory,
425		uploadStatuses,
426		handleCancel,
427		uploading
428	]);
429	const uploadStatusCount = Object.keys(uploadStatuses).length;
430	useEffect(() => {
431		if (!autoUpload || uploading) return;
432		if (fileList.length === 0) return;
433		if (uploadStatusCount > 0) return;
434		// queueMicrotask: handleUpload 가 setUploading 등 synchronous setState 를 부르므로
435		// effect 본문 직접 호출은 React 19 set-state-in-effect 룰 위반. microtask 회피.
436		queueMicrotask(() => {
437			void handleUpload();
438		});
439	}, [
440		autoUpload,
441		uploading,
442		fileList.length,
443		uploadStatusCount,
444		handleUpload
445	]);
446	return /* @__PURE__ */ _jsxDEV(Modal, {
447		title: "문서 업로드",
448		open,
449		onOk: handleUpload,
450		onCancel: handleCancel,
451		confirmLoading: uploading,
452		okText: "업로드",
453		cancelText: "취소",
454		width: MODAL_WIDTH.md,
455		// Why: 이제 abort 가 있으므로 진행 중 닫기를 막지 않는다. X/Esc/mask/취소는 handleCancel
456		// 로 모이며, uploading 중이면 in-flight 요청을 abort 한다(루프 abort 분기가 이미 올라간
457		// 파일을 보존하고 uploading=false 로 정리). 닫기는 abort 처리가 끝난 뒤 한 번 더 닫을 때
458		// 수행되므로, 토스트가 사라진 모달 뒤에 뜨던 옛 혼란도 재발하지 않는다.
459		okButtonProps: { disabled: uploading },
460		children: /* @__PURE__ */ _jsxDEV(Space, {
461			orientation: "vertical",
462			style: { width: "100%" },
463			size: "large",
464			children: [
465				/* @__PURE__ */ _jsxDEV("div", { children: [
466					/* @__PURE__ */ _jsxDEV(Text, {
467						strong: true,
468						children: "카테고리 (선택 사항):"
469					}, void 0, false, {
470						fileName: _jsxFileName,
471						lineNumber: 468,
472						columnNumber: 11
473					}, this),
474					/* @__PURE__ */ _jsxDEV(Select, {
475						placeholder: "선택 안 함 (미분류)",
476						style: {
477							width: "100%",
478							marginTop: 8
479						},
480						value: selectedCategory,
481						onChange: setSelectedCategory,
482						allowClear: true,
483						options: categoryOptions,
484						// Why: "드래그 시 자동 업로드"가 켜져 있으면 파일이 fileList 에 들어오는 순간
485						// (microtask) 그 시점의 selectedCategory 로 즉시 업로드가 발사된다. uploadStatuses
486						// 가 채워진 뒤에도 이 Select 를 계속 활성 상태로 두면, 사용자가 "카테고리를
487						// 지정했다"고 느끼지만 이미 발사된 업로드에는 반영되지 않는 기만적 UI가 된다
488						// (실제 문의: "지정했는데 미분류로 감"). 발사된 뒤에는 비활성화해 정직한 신호를
489						// 준다 — 자동 업로드 자체를 막지는 않는다(그건 별도 제품 결정).
490						disabled: Object.keys(uploadStatuses).length > 0
491					}, void 0, false, {
492						fileName: _jsxFileName,
493						lineNumber: 469,
494						columnNumber: 11
495					}, this),
496					/* @__PURE__ */ _jsxDEV(Text, {
497						type: "secondary",
498						style: {
499							fontSize: 12,
500							display: "block",
501							marginTop: 4
502						},
503						children: ["미지정 시 '미분류'로 업로드됩니다. 같은 파일은 카테고리마다 한 번씩만 올릴 수 있습니다.", autoUpload && /* @__PURE__ */ _jsxDEV(_Fragment, { children: [" ", /* @__PURE__ */ _jsxDEV(Text, {
504							type: "warning",
505							style: { fontSize: 12 },
506							children: "드래그 시 자동 업로드가 켜져 있어 파일을 추가하면 즉시 업로드됩니다 — 카테고리는 파일을 추가하기 전에 먼저 선택하세요."
507						}, void 0, false, {
508							fileName: _jsxFileName,
509							lineNumber: 490,
510							columnNumber: 17
511						}, this)] }, void 0, true, {
512							fileName: _jsxFileName,
513							lineNumber: 488,
514							columnNumber: 15
515						}, this)]
516					}, void 0, true, {
517						fileName: _jsxFileName,
518						lineNumber: 485,
519						columnNumber: 11
520					}, this)
521				] }, void 0, true, {
522					fileName: _jsxFileName,
523					lineNumber: 467,
524					columnNumber: 9
525				}, this),
526				/* @__PURE__ */ _jsxDEV(Space, {
527					align: "center",
528					children: [/* @__PURE__ */ _jsxDEV(Switch, {
529						size: "small",
530						"aria-label": "드래그 시 자동 업로드",
531						checked: autoUpload,
532						onChange: setAutoUpload
533					}, void 0, false, {
534						fileName: _jsxFileName,
535						lineNumber: 499,
536						columnNumber: 11
537					}, this), /* @__PURE__ */ _jsxDEV(Text, {
538						type: "secondary",
539						children: "드래그 시 자동 업로드"
540					}, void 0, false, {
541						fileName: _jsxFileName,
542						lineNumber: 500,
543						columnNumber: 11
544					}, this)]
545				}, void 0, true, {
546					fileName: _jsxFileName,
547					lineNumber: 498,
548					columnNumber: 9
549				}, this),
550				/* @__PURE__ */ _jsxDEV(Dragger, {
551					...uploadProps,
552					children: [
553						/* @__PURE__ */ _jsxDEV("p", {
554							className: "ant-upload-drag-icon",
555							children: /* @__PURE__ */ _jsxDEV(InboxOutlined, {}, void 0, false, {
556								fileName: _jsxFileName,
557								lineNumber: 506,
558								columnNumber: 13
559							}, this)
560						}, void 0, false, {
561							fileName: _jsxFileName,
562							lineNumber: 505,
563							columnNumber: 11
564						}, this),
565						/* @__PURE__ */ _jsxDEV("p", {
566							className: "ant-upload-text",
567							children: "파일을 이 영역에 드래그하거나 클릭하여 업로드하세요"
568						}, void 0, false, {
569							fileName: _jsxFileName,
570							lineNumber: 508,
571							columnNumber: 11
572						}, this),
573						/* @__PURE__ */ _jsxDEV("p", {
574							className: "ant-upload-hint",
575							children: [
576								"지원 형식: ",
577								FILE_UPLOAD.SUPPORTED_TYPES.join(", "),
578								/* @__PURE__ */ _jsxDEV("br", {}, void 0, false, {
579									fileName: _jsxFileName,
580									lineNumber: 511,
581									columnNumber: 13
582								}, this),
583								"최대: ",
584								FILE_UPLOAD.MAX_FILES,
585								"개 파일, 각 100MB",
586								/* @__PURE__ */ _jsxDEV("br", {}, void 0, false, {
587									fileName: _jsxFileName,
588									lineNumber: 513,
589									columnNumber: 13
590								}, this),
591								/* @__PURE__ */ _jsxDEV(Text, {
592									type: "secondary",
593									style: { fontSize: 12 },
594									children: "업로드 후 색인(벡터화) 처리가 끝나야 검색·답변에 반영됩니다 — 목록에서 상태를 확인하세요."
595								}, void 0, false, {
596									fileName: _jsxFileName,
597									lineNumber: 514,
598									columnNumber: 13
599								}, this)
600							]
601						}, void 0, true, {
602							fileName: _jsxFileName,
603							lineNumber: 509,
604							columnNumber: 11
605						}, this)
606					]
607				}, void 0, true, {
608					fileName: _jsxFileName,
609					lineNumber: 504,
610					columnNumber: 9
611				}, this),
612				Object.keys(uploadStatuses).length > 0 ? /* @__PURE__ */ _jsxDEV(Space, {
613					orientation: "vertical",
614					style: { width: "100%" },
615					children: [/* @__PURE__ */ _jsxDEV(Text, {
616						strong: true,
617						children: "업로드 진행 상황"
618					}, void 0, false, {
619						fileName: _jsxFileName,
620						lineNumber: 521,
621						columnNumber: 13
622					}, this), /* @__PURE__ */ _jsxDEV("div", {
623						style: {
624							maxHeight: 240,
625							overflowY: "auto"
626						},
627						children: fileList.map((file) => {
628							const state = uploadStatuses[file.uid];
629							if (!state) return null;
630							const progressStatus = state.status === "error" ? "exception" : state.status === "success" ? "success" : state.status === "uploading" ? "active" : "normal";
631							const statusLabel = state.status === "queued" ? "대기" : state.status === "uploading" ? "업로드 중" : state.status === "success" ? "완료" : state.status === "cancelled" ? "중단됨" : "실패";
632							return /* @__PURE__ */ _jsxDEV("div", {
633								style: {
634									display: "flex",
635									flexDirection: "column",
636									gap: 4,
637									marginBottom: 8
638								},
639								children: [/* @__PURE__ */ _jsxDEV("div", {
640									style: {
641										display: "flex",
642										alignItems: "center",
643										gap: 8
644									},
645									children: [
646										/* @__PURE__ */ _jsxDEV(Text, {
647											ellipsis: true,
648											title: file.name,
649											style: { flex: 1 },
650											children: uploadStatuses[file.uid]?.name || file.name
651										}, void 0, false, {
652											fileName: _jsxFileName,
653											lineNumber: 548,
654											columnNumber: 23
655										}, this),
656										/* @__PURE__ */ _jsxDEV(Progress, {
657											percent: state.progress,
658											size: "small",
659											status: progressStatus,
660											style: {
661												flexBasis: 200,
662												marginBottom: 0
663											}
664										}, void 0, false, {
665											fileName: _jsxFileName,
666											lineNumber: 551,
667											columnNumber: 23
668										}, this),
669										/* @__PURE__ */ _jsxDEV(Text, {
670											type: "secondary",
671											style: {
672												width: 80,
673												textAlign: "right"
674											},
675											children: statusLabel
676										}, void 0, false, {
677											fileName: _jsxFileName,
678											lineNumber: 557,
679											columnNumber: 23
680										}, this)
681									]
682								}, void 0, true, {
683									fileName: _jsxFileName,
684									lineNumber: 547,
685									columnNumber: 21
686								}, this), state.error && /* @__PURE__ */ _jsxDEV(Text, {
687									type: "danger",
688									style: { marginLeft: 2 },
689									children: state.error
690								}, void 0, false, {
691									fileName: _jsxFileName,
692									lineNumber: 562,
693									columnNumber: 23
694								}, this)]
695							}, file.uid, true, {
696								fileName: _jsxFileName,
697								lineNumber: 546,
698								columnNumber: 19
699							}, this);
700						})
701					}, void 0, false, {
702						fileName: _jsxFileName,
703						lineNumber: 522,
704						columnNumber: 13
705					}, this)]
706				}, void 0, true, {
707					fileName: _jsxFileName,
708					lineNumber: 520,
709					columnNumber: 11
710				}, this) : fileList.length > 1 && /* @__PURE__ */ _jsxDEV(Text, {
711					type: "secondary",
712					children: [
713						"일괄 업로드: ",
714						fileList.length,
715						"개 파일 선택됨"
716					]
717				}, void 0, true, {
718					fileName: _jsxFileName,
719					lineNumber: 573,
720					columnNumber: 13
721				}, this),
722				uploading && /* @__PURE__ */ _jsxDEV(Space, {
723					orientation: "vertical",
724					style: { width: "100%" },
725					size: 4,
726					children: [/* @__PURE__ */ _jsxDEV(Button, {
727						danger: true,
728						onClick: () => abortRef.current?.abort(),
729						block: true,
730						children: "업로드 중단"
731					}, void 0, false, {
732						fileName: _jsxFileName,
733						lineNumber: 583,
734						columnNumber: 13
735					}, this), /* @__PURE__ */ _jsxDEV(Text, {
736						type: "secondary",
737						style: { fontSize: 12 },
738						children: "업로드가 진행 중입니다. 중단해도 이미 올라간 파일은 유지됩니다."
739					}, void 0, false, {
740						fileName: _jsxFileName,
741						lineNumber: 586,
742						columnNumber: 13
743					}, this)]
744				}, void 0, true, {
745					fileName: _jsxFileName,
746					lineNumber: 582,
747					columnNumber: 11
748				}, this)
749			]
750		}, void 0, true, {
751			fileName: _jsxFileName,
752			lineNumber: 465,
753			columnNumber: 7
754		}, this)
755	}, void 0, false, {
756		fileName: _jsxFileName,
757		lineNumber: 450,
758		columnNumber: 5
759	}, this);
760}
761_s(DocumentUpload, "YrnrbmUq/VyQBQqsOg99ImZycho=", false, function() {
762	return [AntdApp.useApp, useDocumentStore];
763});
764_c = DocumentUpload;
765var _c;
766$RefreshReg$(_c, "DocumentUpload");
767import * as RefreshRuntime from "/@react-refresh";
768const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
769import * as __vite_react_currentExports from "/src/components/Documents/DocumentUpload.tsx";
770if (import.meta.hot && !inWebWorker) {
771  if (!window.$RefreshReg$) {
772    throw new Error(
773      "@vitejs/plugin-react can't detect preamble. Something is wrong."
774    );
775  }
776
777  const currentExports = __vite_react_currentExports;
778  queueMicrotask(() => {
779    RefreshRuntime.registerExportsForReactRefresh("/home/bang/datatek_rag/frontend/src/components/Documents/DocumentUpload.tsx", currentExports);
780    import.meta.hot.accept((nextExports) => {
781      if (!nextExports) return;
782      const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate("/home/bang/datatek_rag/frontend/src/components/Documents/DocumentUpload.tsx", currentExports, nextExports);
783      if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage);
784    });
785  });
786}
787function $RefreshReg$(type, id) { return RefreshRuntime.register(type, "/home/bang/datatek_rag/frontend/src/components/Documents/DocumentUpload.tsx" + ' ' + id); }
788function $RefreshSig$() { return RefreshRuntime.createSignatureFunctionForTransform(); }
789
790//# sourceMappingURL=data:application/json;base64,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QyxNQUFEO1NBQU0sTUFBSztTQUFTLE9BQU8sRUFBRSxZQUFZLEVBQUU7bUJBQ3hDLE1BQU07UUFDSDs7OztnQkFFTDtVQXBCSyxLQUFLOzs7O2NBb0JWO01BRVQsQ0FBQztLQUNFOzs7O2FBQ0E7Ozs7O2VBRVAsU0FBUyxTQUFTLEtBQ2hCLHdCQUFDLE1BQUQ7S0FBTSxNQUFLO2VBQVg7TUFBdUI7TUFDWixTQUFTO01BQU87S0FDckI7Ozs7OztJQU1ULGFBQ0Msd0JBQUMsT0FBRDtLQUFPLGFBQVk7S0FBVyxPQUFPLEVBQUUsT0FBTyxPQUFPO0tBQUcsTUFBTTtlQUE5RCxDQUNFLHdCQUFDLFFBQUQ7TUFBUTtNQUFPLGVBQWUsU0FBUyxTQUFTLE1BQU07TUFBRztnQkFBTTtLQUV2RDs7OztlQUNSLHdCQUFDLE1BQUQ7TUFBTSxNQUFLO01BQVksT0FBTyxFQUFFLFVBQVUsR0FBRztnQkFBRztLQUUxQzs7OzthQUNEOzs7Ozs7R0FFSjs7Ozs7O0NBQ0Y7Ozs7O0FBRVgiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiRG9jdW1lbnRVcGxvYWQudHN4Il0sInZlcnNpb24iOjMsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHVzZVN0YXRlLCB1c2VFZmZlY3QsIHVzZUNhbGxiYWNrLCB1c2VSZWYgfSBmcm9tICdyZWFjdCc7XG5pbXBvcnQgYXhpb3MgZnJvbSAnYXhpb3MnO1xuaW1wb3J0IHsgQXBwIGFzIEFudGRBcHAsIE1vZGFsLCBVcGxvYWQsIFNlbGVjdCwgU3BhY2UsIFR5cG9ncmFwaHksIFByb2dyZXNzLCBTd2l0Y2gsIEJ1dHRvbiB9IGZyb20gJ2FudGQnO1xuaW1wb3J0IHsgSW5ib3hPdXRsaW5lZCB9IGZyb20gJ0BhbnQtZGVzaWduL2ljb25zJztcbmltcG9ydCB0eXBlIHsgVXBsb2FkRmlsZSwgVXBsb2FkUHJvcHMgfSBmcm9tICdhbnRkJztcbmltcG9ydCB7IHVwbG9hZERvY3VtZW50IH0gZnJvbSAnLi4vLi4vYXBpL2RvY3VtZW50cyc7XG5pbXBvcnQgeyB3YXRjaFZlY3Rvcml6YXRpb24gfSBmcm9tICcuLi8uLi9zdG9yZS92ZWN0b3JpemF0aW9uV2F0Y2hTdG9yZSc7XG5pbXBvcnQgeyB1c2VEb2N1bWVudFN0b3JlIH0gZnJvbSAnLi4vLi4vc3RvcmUvZG9jdW1lbnRTdG9yZSc7XG5pbXBvcnQgeyBmbGF0dGVuQ2F0ZWdvcmllcyB9IGZyb20gJy4uLy4uL3V0aWxzL2NhdGVnb3J5VXRpbHMnO1xuaW1wb3J0IHsgZXh0cmFjdEVycm9yTWVzc2FnZSB9IGZyb20gJy4uLy4uL3V0aWxzL2Vycm9yVXRpbHMnO1xuaW1wb3J0IHsgRklMRV9VUExPQUQsIE1PREFMX1dJRFRILCBTVE9SQUdFX0tFWVMgfSBmcm9tICcuLi8uLi9jb25zdGFudHMnO1xuaW1wb3J0IHsgZ2V0RmlsZUV4dGVuc2lvbiB9IGZyb20gJy4uLy4uL3V0aWxzL2ZpbGVVdGlscyc7XG5pbXBvcnQgeyBmb3JtYXRGaWxlU2l6ZSB9IGZyb20gJy4uLy4uL3V0aWxzL2Zvcm1hdHRlcnMnO1xuaW1wb3J0IHsgc2FmZUdldEl0ZW0sIHNhZmVTZXRJdGVtIH0gZnJvbSAnLi4vLi4vdXRpbHMvc3RvcmFnZVV0aWxzJztcblxuY29uc3QgeyBEcmFnZ2VyIH0gPSBVcGxvYWQ7XG5jb25zdCB7IFRleHQgfSA9IFR5cG9ncmFwaHk7XG5cbmludGVyZmFjZSBEb2N1bWVudFVwbG9hZFByb3BzIHtcbiAgb3BlbjogYm9vbGVhbjtcbiAgb25DbG9zZTogKCkgPT4gdm9pZDtcbiAgb25TdWNjZXNzOiAoKSA9PiB2b2lkO1xuICAvKiog7Y6Y7J207KeAKOusuOyEnCDrqqnroZ0p7J2YIO2YhOyerCDsubTthYzqs6Drpqwg7ZWE7YSwLiDrqqjri6zsnbQg7Je066a0IOuVjCDquLDrs7gg7ISg7YOd7Jy866GcIOyDgeyGje2VnOuLpC5cbiAgICogV2h5OiDsubTthYzqs6Drpqwg65Sl66eB7YGs66GcIO2VhO2EsOqwgCDqsbjrprAg66qp66Gd7JeQ7IScIOuqqOuLrOydtCDrr7jshKDtg53snLzroZwg65yo66m0LCDsgqzsmqnsnpDripRcbiAgICogXCLsnbQg7Lm07YWM6rOg66asIOyViOyXkOyEnCDsmKzrprDri6RcIuqzoCDsnbjsi53tlZjripTrjbAg7Iuk7KCc66Gc64qUIOuvuOu2hOulmOuhnCDqsIDqs6Ag7ZWE7YSwIOuVjOusuOyXkFxuICAgKiDrs7TsnbTsp4Drj4Qg7JWK64qU64ukLiDrrLjrp6Ug7IOB7IaN7Jy866GcIOyduOyLneqzvCDrj5nsnpHsnYQg7J287LmY7Iuc7YKo64ukLiDsgqzsmqnsnpDripQg7Jes7KCE7Z6IXG4gICAqIOuqqOuLrOyXkOyEnCDsnpDsnKDroa3qsowg67OA6rK97ZWgIOyImCDsnojri6QuICovXG4gIGRlZmF1bHRDYXRlZ29yeUlkPzogc3RyaW5nO1xufVxuXG50eXBlIFVwbG9hZFN0YXRlID0ge1xuICAvLyBXaHk6ICdjYW5jZWxsZWQnIOuKlCDsgqzsmqnsnpDqsIAg66qF7Iuc7KCB7Jy866GcIOykkeuLqO2VnCDtjIzsnbwo7KeE7ZaJIOykkSArIOuMgOq4sOyXtCnsnYQgJ2Vycm9yJyDsmYAg6rWs67aE7ZW0XG4gIC8vIOykkeumveyggeycvOuhnChcIuykkeuLqOuQqFwiKSDrs7Tsl6zso7zquLAg7JyE7ZWcIOyDge2DnC4gJ2Vycm9yJyDroZwg7ZGc6riw7ZWY66m0IOyEnOuyhC/rhKTtirjsm4ztgawg7Iuk7Yyo7LKY65+8IOuztOyXrFxuICAvLyDsmKTtlbTrpbwg7KSA64ukLlxuICBzdGF0dXM6ICdxdWV1ZWQnIHwgJ3VwbG9hZGluZycgfCAnc3VjY2VzcycgfCAnZXJyb3InIHwgJ2NhbmNlbGxlZCc7XG4gIHByb2dyZXNzOiBudW1iZXI7XG4gIGVycm9yPzogc3RyaW5nO1xuICBuYW1lPzogc3RyaW5nO1xufTtcblxuLy8gYXhpb3Mg7Leo7IaMIOyXkOufrOyduOyngCDqsqzqs6DtlZjqsowg7YyQ67OE7ZWc64ukLiBheGlvcy5pc0NhbmNlbCDsmbjsl5Ag7ZmY6rK97JeQIOuUsOudvCBuYW1lIOunjCDri6Trpbhcbi8vIENhbmNlbGVkRXJyb3IgLyBBYm9ydEVycm9yIOuPhCDtlajqu5gg7J247KCV7ZWc64ukLlxuZnVuY3Rpb24gaXNBYm9ydEVycm9yKGVycjogdW5rbm93bik6IGJvb2xlYW4ge1xuICBpZiAoYXhpb3MuaXNDYW5jZWwoZXJyKSkgcmV0dXJuIHRydWU7XG4gIGNvbnN0IG5hbW
790UgPSAoZXJyIGFzIHsgbmFtZT86IHN0cmluZyB9IHwgbnVsbCB8IHVuZGVmaW5lZCk/Lm5hbWU7XG4gIHJldHVybiBuYW1lID09PSAnQ2FuY2VsZWRFcnJvcicgfHwgbmFtZSA9PT0gJ0Fib3J0RXJyb3InO1xufVxuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBEb2N1bWVudFVwbG9hZCh7IG9wZW4sIG9uQ2xvc2UsIG9uU3VjY2VzcywgZGVmYXVsdENhdGVnb3J5SWQgfTogRG9jdW1lbnRVcGxvYWRQcm9wcykge1xuICBjb25zdCB7IG1lc3NhZ2U6IG1lc3NhZ2VBcGkgfSA9IEFudGRBcHAudXNlQXBwKCk7XG4gIC8vIFdoeTogdXNlRG9jdW1lbnRTdG9yZSgpIOyghOyytCDqtazrj4Ug4oaSIDEwME1CIOyXheuhnOuTnCBwcm9ncmVzcyDsnbTrsqTtirgg64+E7KSRIHN0b3JlIOydmCDrqqjrk6BcbiAgLy8gbXV0YXRpb24g7J20IGRyYWdnZXIgKyBwZXItZmlsZSBQcm9ncmVzcyDsnqzroIzrjZQgc3Rvcm0uIFIxNjcvUjE2OC9SMTc1IO2MqO2EtOycvOuhnCBjYXRlZ29yaWVzXG4gIC8vIHNsaWNlIOunjCDqtazrj4UuXG4gIGNvbnN0IGNhdGVnb3JpZXMgPSB1c2VEb2N1bWVudFN0b3JlKChzKSA9PiBzLmNhdGVnb3JpZXMpO1xuICBjb25zdCBbdXBsb2FkaW5nLCBzZXRVcGxvYWRpbmddID0gdXNlU3RhdGUoZmFsc2UpO1xuICBjb25zdCBbZmlsZUxpc3QsIHNldEZpbGVMaXN0XSA9IHVzZVN0YXRlPFVwbG9hZEZpbGVbXT4oW10pO1xuICBjb25zdCBbc2VsZWN0ZWRDYXRlZ29yeSwgc2V0U2VsZWN0ZWRDYXRlZ29yeV0gPSB1c2VTdGF0ZTxzdHJpbmcgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZCk7XG4gIGNvbnN0IFt1cGxvYWRTdGF0dXNlcywgc2V0VXBsb2FkU3RhdHVzZXNdID0gdXNlU3RhdGU8UmVjb3JkPHN0cmluZywgVXBsb2FkU3RhdGU+Pih7fSk7XG4gIC8vIFdoeTogUjE4MCDsnZggXCJwcmVzZXJ2ZSBhdXRvVXBsb2FkIGFjcm9zcyByZW9wZW5cIiDsnZjrj4TqsIAgdXNlU3RhdGUgbGF6eSBpbml0IOydtOudvFxuICAvLyBEb2N1bWVudFVwbG9hZCBjb21wb25lbnQg6rCAIHVubW91bnQg65CY66m0IChwYXJlbnQg7J2YIG9wZW49ZmFsc2Ug6rCAIG1vZGFsIOuLq+qzoCBjb21wb25lbnQg64+EXG4gIC8vIHVubW91bnQpIOuLpOydjCBtb3VudCDsi5wgZGVmYXVsdCB0cnVlIOuhnCByZXNldCDrkJjslrQg7IKs7Jqp7J6QIHByZWZlcmVuY2Ugc2lsZW50IOyGkOyLpC4gbG9jYWxTdG9yYWdlXG4gIC8vIOuhnCDsmIHqtawg67O07KG0LiAndHJ1ZScgLyAnZmFsc2UnIOuqheyLnCDrrLjsnpDsl7Qg7IKs7JqpLlxuICBjb25zdCBbYXV0b1VwbG9hZCwgc2V0QXV0b1VwbG9hZF0gPSB1c2VTdGF0ZSgoKSA9PiB7XG4gICAgY29uc3Qgc3RvcmVkID0gc2FmZUdldEl0ZW0oU1RPUkFHRV9LRVlTLkRPQ1VNRU5UX1VQTE9BRF9BVVRPKTtcbiAgICBpZiAoc3RvcmVkID09PSAnZmFsc2UnKSByZXR1cm4gZmFsc2U7XG4gICAgcmV0dXJuIHRydWU7XG4gIH0pO1xuICAvLyBXaHk6IOy0iOq4sCBtb3VudCDsi5wgdXNlU3RhdGUgaW5pdCDsnLzroZwg7J297J2AIOqwkuydhCDsponsi5wg64uk7IucIHNldEl0ZW0g7ZWY66m0IGNyb3NzLXRhYiDsi5zrgpjrpqzsmKRcbiAgLy8g7JeQ7IScIOuLpOuluCDtg63snbQg642UIOy1nOyLoOqwkuycvOuhnCDrsJTqvrwg7KeB7ZuEIOuzuCDtg63snbQgbW91bnQg7ZWY66m0IHN0YWxlIOqwkuycvOuhnCDrja7slrTsk7DquLAg6rCA64qlLlxuICAvLyAoc3RvcmFnZSBldmVudCBsaXN0ZW5lciDqsIAg7JeG7Ja0IOuLpOuluCDtg63snZgg67OA6rK97J2EIOyViCDrs7TripQg7IOB7ZmpICsg67O4IO2DreydmCBtb3VudCDqsIAg6re4IOydtO2bhOudvOuPhFxuICAvLyB1c2VTdGF0ZSBpbml0IOydtCBjYWNoZSBoaXQg7Jy866GcIOyYmyDqsJIg7J297J2MLikg7LKrIG1vdW50IOydmCByZWR1bmRhbnQgd3JpdGUg66W8IHNraXAuXG4gIGNvbnN0IGF1dG9VcGxvYWREaWRNb3VudFJlZiA9IHVzZVJlZihmYWxzZSk7XG4gIC8vIFdoeTog7KeE7ZaJIOykkeyduCDsl4XroZzrk5wgcnVuIOydhCDspJHri6jtlZjquLAg7JyE7ZWcIEFib3J0Q29udHJvbGxlci4g66qo65OgIGluLWZsaWdodCBheGlvcyDsmpTssq3sl5BcbiAgLy8g6rCZ7J2AIHNpZ25hbCDsnYQg64SY6rKoIO2VnCDrsojsnZggYWJvcnQoKSDroZwg7ZiE7J6sIO2MjOydvOydhCDst6jshoztlZjqs6Ag66Oo7ZSE66W8IOu5oOyguOuCmOqwhOuLpC5cbiAgY29uc3QgYWJvcnRSZWYgPSB1c2VSZWY8QWJvcnRDb250cm9sbGVyIHwgbnVsbD4obnVsbCk7XG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKCFhdXRvVXBsb2FkRGlkTW91bnRSZWYuY3VycmVudCkge1xuICAgICAgYXV0b1VwbG9hZERpZE1vdW50UmVmLmN1cnJlbnQgPSB0cnVlO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBzYWZlU2V0SXRlbShTVE9SQUdFX0tFWVMuRE9DVU1FTlRfVVBMT0FEX0FVVE8sIGF1dG9VcGxvYWQgPyAndHJ1ZScgOiAnZmFsc2UnKTtcbiAgfSwgW2F1dG9VcGxvYWRdKTtcblxuICAvLyBXaHk6IOydtOyghOyXlCBsYXp5LVxuICAvLyBpbml0IOycvOuhnOunjCDsnb3slrQgVGFiIEEg6rCAIGF1dG8tdXBsb2FkIE9GRiDroZwg67CU6r+U64+EIOydtOuvuCBtb3VudCDrkJwgVGFiIEIg65iQ64qUXG4gIC8vIOymieyLnCByZW9wZW4g7IucIHN0YWxlIHRydWUg7Jyg7KeAIOKGkiDsgqzsmqnsnpDqsIAgVGFiIEIg7JeQ7IScIO2MjOydvCDrk5zrnpjqt7gg7IucIOydmOuPhOy5mFxuICAvLyDslYrsnYAg7J6Q64+ZIOyXheuhnOuTnCDrsJzsgqwgKFRhYiBBIOydmCBwcmVmZXJlbmNlIOustOyLnCkuIHN0b3JhZ2UgZXZlbnQg66GcIGNyb3NzLXRhYlxuICAvLyBzeW5jIOKAlCDri6Trpbgg7YOt7J2YIOuzgOqyvSDsponsi5wg67CY7JiBLlxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNvbnN0IG9uU3RvcmFnZSA9IChlOiBTdG9yYWdlRXZlbnQpID0+IHtcbiAgICAgIGlmIChlLmtleSA9PT0gU1RPUkFHRV9LRVlTLkRPQ1VNRU5UX1VQTE9BRF9BVVRPICYmIGUubmV3VmFsdWUgIT09IG51bGwpIHtcbiAgICAgICAgY29uc3QgbmV4dCA9IGUubmV3VmFsdWUgPT09ICd0cnVlJztcbiAgICAgICAgc2V0QXV0b1VwbG9hZChuZXh0KTtcbiAgICAgIH1cbiAgICB9O1xuICAgIHdpbmRvdy5hZGR
790FdmVudExpc3RlbmVyKCdzdG9yYWdlJywgb25TdG9yYWdlKTtcbiAgICByZXR1cm4gKCkgPT4gd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3N0b3JhZ2UnLCBvblN0b3JhZ2UpO1xuICB9LCBbXSk7XG5cbiAgLy8gV2h5OiDsl4XroZzrk5wg7KeE7ZaJIOykkeyXkCDsu7Ttj6zrhIztirjqsIAgdW5tb3VudCDrkJjrqbQo67aA66qo6rCAIOuqqOuLrOydhCDsoJzqsbApIGluLWZsaWdodCDsmpTssq3snbQg64Ko7JWEXG4gIC8vIOyZhOujjCDsi5wgc2V0VXBsb2FkU3RhdHVzZXMg6rCAIOyCrOudvOynhCDsu7Ttj6zrhIztirjsl5Agc2V0U3RhdGUg66W8IOyLnOuPhO2VnOuLpC4gdW5tb3VudCDsi5wg64Ko7J2AXG4gIC8vIOy7qO2KuOuhpOufrOulvCBhYm9ydCDtlbQg7JqU7LKt7J2EIOuBiuqzoCBzZXRTdGF0ZS1hZnRlci11bm1vdW50IOulvCDrsKnsp4DtlZzri6QuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGFib3J0UmVmLmN1cnJlbnQ/LmFib3J0KCk7XG4gICAgfTtcbiAgfSwgW10pO1xuXG4gIC8vIOuqqOuLrOydtCDri6vtmJTri6Qg64uk7IucIOyXtOumrOuptCDsnbTsoIQg7IS47IWY7J2YIGZpbGVMaXN0IC8gdXBsb2FkU3RhdHVzZXMgLyBzZWxlY3RlZENhdGVnb3J5IOqwgFxuICAvLyDqt7jrjIDroZwg64Ko7JWEIOyeiOyWtCDsgqzsmqnsnpAg7Zi864+ZIOycoOuwnC4gb3BlbiA9PT0gdHJ1ZSDroZwg67CU64CUIOuVjCDstIjquLDtmZQuXG4gIC8vIFdoeTogYXV0b1VwbG9hZCDripQg7IKs7Jqp7J6Q6rCAIOuqheyLnOyggeycvOuhnCDrgYgg7ZmY6rK9ICjrjIDrn4kg6rKA7YagIO2bhCDrqoXsi5zsoIEgVXBsb2FkIO2BtOumrSkg7JeQ7IScXG4gIC8vIOuqqOuLrCDri6vqs6Ag64uk7IucIOyXtOuptCBzaWxlbnQg66GcIOuLpOyLnCDsvJzsoLgsIOuLpOydjCDrk5zrnpjqt7gg7IucIOydmOuPhOy5mCDslYrsnYAg7J6Q64+ZIOyXheuhnOuTnCDrsJzsg50uIOuLpOuluFxuICAvLyBzZXNzaW9uLXN0YXRlIChmaWxlTGlzdCDrk7EpIOyZgCDri6zrpqwg7IKs7Jqp7J6QIHByZWZlcmVuY2Ug6528IHJlc2V0IOuMgOyDgeyXkOyEnCDsoJzsmbjtlbQg67O07KG0LlxuICAvLyBxdWV1ZU1pY3JvdGFzazogc2V0U3RhdGUg6rCAIGVmZmVjdCBib2R5IOyXkCDsp4HsoJEg7J6I7Jy866m0IFJlYWN0IDE5IHNldC1zdGF0ZS1pbi1lZmZlY3Qg6rCAXG4gIC8vIOyeoeuKlOuLpC4gbWljcm90YXNrIOuhnCDsmK7quLDrqbQgY29tbWl0IOynge2bhCDqsJnsnYAgdGljayDsl5Ag7Iuk7ZaJ65CY7Ja0IOyCrOyaqeyekCDssrTqsJAg64+Z7J28LlxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghb3BlbikgcmV0dXJuO1xuICAgIHF1ZXVlTWljcm90YXNrKCgpID0+IHtcbiAgICAgIHNldEZpbGVMaXN0KFtdKTtcbiAgICAgIC8vIFdoeTogdW5kZWZpbmVkIOuhnCDrpqzshYvtlZjrqbQg7Lm07YWM6rOg66asIO2VhO2EsOqwgCDqsbjrprAg66qp66Gd7JeQ7IScIOyXsCDrqqjri6zsnbQg66y466el7J2EIOyeg+uKlOuLpFxuICAgICAgLy8gKOuUpeunge2BrCDsnKDsnoUgKyDshLHqs7Ug7ZuEIOyekOuPmSDri6vtnpgg4oaSIOyerOyYpO2UiCDrqqjrkZApLiDtjpjsnbTsp4DqsIAg64SY6rKo7KSAIOq4sOuzuOqwkuycvOuhnCDrs7Xsm5AuXG4gICAgICBzZXRTZWxlY3RlZENhdGVnb3J5KGRlZmF1bHRDYXRlZ29yeUlkKTtcbiAgICAgIHNldFVwbG9hZFN0YXR1c2VzKHt9KTtcbiAgICB9KTtcbiAgfSwgW29wZW4sIGRlZmF1bHRDYXRlZ29yeUlkXSk7XG5cbiAgY29uc3QgY2F0ZWdvcnlPcHRpb25zID0gZmxhdHRlbkNhdGVnb3JpZXMoY2F0ZWdvcmllcyk7XG5cbiAgY29uc3QgdXBsb2FkUHJvcHM6IFVwbG9hZFByb3BzID0ge1xuICAgIG5hbWU6ICdmaWxlJyxcbiAgICBtdWx0aXBsZTogdHJ1ZSxcbiAgICBmaWxlTGlzdCxcbiAgICBkaXNhYmxlZDogdXBsb2FkaW5nLFxuICAgIGJlZm9yZVVwbG9hZDogKGZpbGUpID0+IHtcbiAgICAgIC8vIENsZWFyIG9sZCBzdGF0dXMgdHJhY2VzIHdoZW4gdXNlciBzZWxlY3RzIG5ldyBmaWxlcyBhZnRlciBhIHByZXZpb3VzIHVwbG9hZCBydW5cbiAgICAgIGlmICghdXBsb2FkaW5nICYmIE9iamVjdC5rZXlzKHVwbG9hZFN0YXR1c2VzKS5sZW5ndGggPiAwKSB7XG4gICAgICAgIHNldFVwbG9hZFN0YXR1c2VzKHt9KTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgZmlsZSB0eXBlXG4gICAgICBjb25zdCBmaWxlRXh0ID0gZ2V0RmlsZUV4dGVuc2lvbihmaWxlLm5hbWUpO1xuICAgICAgaWYgKCFmaWxlRXh0IHx8ICFGSUxFX1VQTE9BRC5TVVBQT1JURURfVFlQRVMuaW5jbHVkZXMoZmlsZUV4dCkpIHtcbiAgICAgICAgbWVzc2FnZUFwaS5lcnJvcihcbiAgICAgICAgICBg7KeA7JuQ7ZWY7KeAIOyViuuKlCDtjIzsnbwg7ZiV7IudOiAke2ZpbGVFeHQgfHwgJ+yVjCDsiJgg7JeG7J2MJ30uIOyngOybkCDtmJXsi506ICR7RklMRV9VUExPQUQuU1VQUE9SVEVEX1RZUEVTLmpvaW4oJywgJyl9YFxuICAgICAgICApO1xuICAgICAgICByZXR1cm4gVXBsb2FkLkxJU1RfSUdOT1JFO1xuICAgICAgfVxuXG4gICAgICAvLyBDaGVjayBmaWxlIHNpemVcbiAgICAgIGlmIChmaWxlLnNpemUgPiBGSUxFX1VQTE9BRC5NQVhfU0laRSkge1xuICAgICAgICBtZXNzYWdlQXBpLmVycm9yKGBcIiR7ZmlsZS5uYW1lfVwiIO2MjOydvOydtCDrhIjrrLQg7YG964uI64ukLiDstZzrjIAg7YGs6riwOiAke2Zvcm1hdEZpbGVTaXplKEZJTEVfVVBMT0FELk1BWF9TSVpFKX1gKTtcbiAgICAgICAgcmV0dXJuIFVwbG9hZC5MSVNUX0lHTk9SRTtcbiAgICAgIH1cblxuICAgICAgbGV0IGNhbkFkZCA9IHRydWU7XG4gICAgICBsZXQgZHVwbGljYXRlID0gZmFsc2U7XG5cbiAgICAgIC8vIENoZWNrIGZpbGUgY291bnQgKHVzZSBsYXRlc3Qgc3RhdGUgaW5zaWRlIHVwZGF0ZXIgdG8gYXZvaWQgc3RhbGUgbGVuZ3RoIHdpdGggbXVsdGktc2VsZWN0KVxuICAgICAgc2V0RmlsZUxpc3QoKHByZXYpID0+IHtcbiAgICAgICAgLy8g6rCZ7J2AIG5hbWUrc2l6ZStsYXN0TW9kaWZpZWQg7KGw7ZWp7J20IOydtOuvuCDrpqzsiqTtirjsl5Ag7J6I7Jy866m0IOykkeuztSDstpTqsIAg66eJ6riwLlxuICAgICAgICAvLyBBbnRkIOydmCB1aWQg64qUIOunpOuyiCDsg4jroZwg7IOd6rKoIGRlZHVwIOyXre2VoOydhCDrqrvtlZjrr4DroZwg67OE64+EIO2CpOuhnCDruYTqtZAuXG4gICAgICAgIGNvbnN0IGZpbGVLZXkgPSBgJHtmaWxlLm5hbWV9OiR7ZmlsZS5zaXplfTokeyhmaWxlIGFzIEZpbGUpLmxhc3RNb2RpZmllZCA/PyAnJ31gO1
790xuICAgICAgICBjb25zdCBhbHJlYWR5UHJlc2VudCA9IHByZXYuc29tZShcbiAgICAgICAgICAoZikgPT4gYCR7Zi5uYW1lfToke2Yuc2l6ZSA/PyAnJ306JHsoZiBhcyBVcGxvYWRGaWxlICYgeyBsYXN0TW9kaWZpZWQ/OiBudW1iZXIgfSkubGFzdE1vZGlmaWVkID8/ICcnfWAgPT09IGZpbGVLZXksXG4gICAgICAgICk7XG4gICAgICAgIGlmIChhbHJlYWR5UHJlc2VudCkge1xuICAgICAgICAgIGR1cGxpY2F0ZSA9IHRydWU7XG4gICAgICAgICAgcmV0dXJuIHByZXY7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgYWN0aXZlQ291bnQgPSBwcmV2LmZpbHRlcigoZikgPT4gdXBsb2FkU3RhdHVzZXNbZi51aWRdPy5zdGF0dXMgIT09ICdzdWNjZXNzJykubGVuZ3RoO1xuICAgICAgICBpZiAoYWN0aXZlQ291bnQgPj0gRklMRV9VUExPQUQuTUFYX0ZJTEVTKSB7XG4gICAgICAgICAgY2FuQWRkID0gZmFsc2U7XG4gICAgICAgICAgcmV0dXJuIHByZXY7XG4gICAgICAgIH1cbiAgICAgICAgLy8gV2h5OiBSY0ZpbGUo7KeE7KecIEZpbGUp7J2EIGZpbGVMaXN0IOyXkCDqt7jrjIDroZwg64u07Jy866m0LCDsnbTtm4Qg7KeE7ZaJ66WgL+yDge2DnCDqsLHsi6DsnbRcbiAgICAgICAgLy8gYHsgLi4uZiwgc3RhdHVzOiAndXBsb2FkaW5nJyB9YCDroZwg7Iqk7ZSE66CI65Oc7ZWY64qUIOyInOqwhCBGaWxlIOydtCDslYTri4wg7Y+J67KU7ZWcIOqwneyytOqwgFxuICAgICAgICAvLyDrkJzri6QuIEZpbGUg7J2YIG5hbWUvc2l6ZS/rjbDsnbTthLDripQg7ZSE66Gc7Yag7YOA7J6FIGdldHRlciDrnbwg7Iqk7ZSE66CI65Oc6rCAIOuzteyCrO2VmOyngCDrqrvtlZzri6RcbiAgICAgICAgLy8gKG93biBlbnVtZXJhYmxlIOydtCDruYTslrQg7J6I64ukKS4g6re4IOqwneyytOulvCBGb3JtRGF0YSDsl5Ag64Sj7Jy866m0IFwiW29iamVjdCBPYmplY3RdXCJcbiAgICAgICAgLy8g66y47J6Q7Je07J20IOuQmOyWtCBmaWxlbmFtZSDsl4bripQg7YyM7Yq466GcIOyghOyGoeuQmOqzoCwg7ISc67KE6rCAIDQyMiBcIkV4cGVjdGVkIFVwbG9hZEZpbGUsXG4gICAgICAgIC8vIHJlY2VpdmVkOiBzdHJcIiDrpbwg64K464ukLiDssqsg7Iuc64+E64qUIOybkOuzuOydtOudvCDshLHqs7XtlZjqs6Ag7J6s7Iuc64+E66eMIOyLpO2MqO2VtCDstpTsoIHsnbRcbiAgICAgICAgLy8g7Ja066Ck7Jug64ukKOyatOyYgSDroZzqt7ggMjAxIC0+IDQwOSAtPiA0MjIpLlxuICAgICAgICAvLyBhbnRkIOq3nOyVveuMgOuhnCBVcGxvYWRGaWxlIOuemO2NvOyXkCDri7Tqs6Ag7JuQ67O47J2AIG9yaWdpbkZpbGVPYmog66GcIOywuOyhsOulvCDrs7TsobTtlZzri6QuXG4gICAgICAgIC8vIOyKpO2UhOugiOuTnOuKlCDrnpjtjbzrp4wg67O17IKs7ZWY6rOgIG9yaWdpbkZpbGVPYmog64qUIOywuOyhsOuhnCDsgrTslYTrgqjripTri6QuXG4gICAgICAgIGNvbnN0IHdyYXBwZWQ6IFVwbG9hZEZpbGUgPSB7XG4gICAgICAgICAgdWlkOiBmaWxlLnVpZCxcbiAgICAgICAgICBuYW1lOiBmaWxlLm5hbWUsXG4gICAgICAgICAgc2l6ZTogZmlsZS5zaXplLFxuICAgICAgICAgIHR5cGU6IGZpbGUudHlwZSxcbiAgICAgICAgICBvcmlnaW5GaWxlT2JqOiBmaWxlLFxuICAgICAgICB9O1xuICAgICAgICByZXR1cm4gWy4uLnByZXYsIHdyYXBwZWRdO1xuICAgICAgfSk7XG5cbiAgICAgIGlmIChkdXBsaWNhdGUpIHtcbiAgICAgICAgbWVzc2FnZUFwaS5pbmZvKHtcbiAgICAgICAgICBrZXk6IGB1cGxvYWQtZHVwLSR7ZmlsZS5uYW1lfWAsXG4gICAgICAgICAgY29udGVudDogYFwiJHtmaWxlLm5hbWV9XCIg7J2AKOuKlCkg7J2066+4IOuMgOq4sOyXtOyXkCDsnojsirXri4jri6QuYCxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBVcGxvYWQuTElTVF9JR05PUkU7XG4gICAgICB9XG5cbiAgICAgIGlmICghY2FuQWRkKSB7XG4gICAgICAgIG1lc3NhZ2VBcGkuZXJyb3Ioe1xuICAgICAgICAgIGtleTogJ3VwbG9hZC1tYXgtZmlsZXMnLFxuICAgICAgICAgIGNvbnRlbnQ6IGDtlZwg67KI7JeQIOy1nOuMgCAke0ZJTEVfVVBMT0FELk1BWF9GSUxFU33qsJzquYzsp4Ag7JeF66Gc65Oc7ZWgIOyImCDsnojsirXri4jri6RgLFxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIFVwbG9hZC5MSVNUX0lHTk9SRTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGZhbHNlOyAvLyBQcmV2ZW50IGF1dG8gdXBsb2FkXG4gICAgfSxcbiAgICBvblJlbW92ZTogKGZpbGUpID0+IHtcbiAgICAgIGlmICh1cGxvYWRpbmcpIHJldHVybiBmYWxzZTtcbiAgICAgIHNldEZpbGVMaXN0KChwcmV2KSA9PiBwcmV2LmZpbHRlcigoZikgPT4gZi51aWQgIT09IGZpbGUudWlkKSk7XG4gICAgICBzZXRVcGxvYWRTdGF0dXNlcygocHJldikgPT4ge1xuICAgICAgICBjb25zdCBuZXh0ID0geyAuLi5wcmV2IH07XG4gICAgICAgIGRlbGV0ZSBuZXh0W2ZpbGUudWlkXTtcbiAgICAgICAgcmV0dXJuIG5leHQ7XG4gICAgICB9KTtcbiAgICB9LFxuICB9O1xuXG4gIGNvbnN0IGhhbmRsZUNhbmNlbCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICAvLyBXaHk6IOyXheuhnOuTnCDspJEgWC9Fc2MvbWFzayDtgbTrpq0v7Leo7IaM66GcIOuLq+q4sOulvCDsi5zrj4TtlZjrqbQsIOydtOygnOuKlCDrp4nripQg64yA7IugIOynhO2WiSDspJHsnbggcnVuIOydhFxuICAgIC8vIGFib3J0IO2VnOuLpC4gaGFuZGxlVXBsb2FkIOydmCBhYm9ydCDsspjrpqwg67aE6riw6rCAIOydtOuvuCDsmKzrnbzqsIQg7YyM7J287J2EIOuztOyhtO2VnCDssYQgdXBsb2FkaW5nPWZhbHNlXG4gICAgLy8g66GcIOygleumrO2VmOqzoCDslYjrgrTrpbwg652E7Jq064ukLiDsl6zquLDshJzripQgYWJvcnQg66eMIO2KuOumrOqxsO2VmOqzoCDsi6TsoJwg64ur6riwL+umrOyFi+ydgCDsgqzsmqnsnpDqsIAg7ZWcIOuyiFxuICAgIC8vIOuNlCDri6vsnYQg65WMKOuYkOuKlCBhYm9ydCDsspjrpqwg7ZuEKSDsiJjtlontlZzri6QuIGFib3J0IOynge2bhCDqs6fsnqUgb25DbG9zZSDtlZjrqbQg66Oo7ZSE6rCAIOyVhOyngVxuICAgIC8vIHNldFVwbG9hZGluZyhmYWxzZSkg7KCE7J206528IOuLq+2ejCDrkqQg7Yag7Iqk7Yq46rCAIOucqOuKlCDsmJsg7Zi8656A7J20IOyerOuwnO2VmOuvgOuhnCDri6vsp4Ag7JWK64qU64ukLlxuICAgIGlmICh1cGxvYWRpbmcpIHtcbiAgICAgIGFib3J0UmVmLmN1cnJlbnQ/LmFib3J0KCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHNldEZpbGVMaXN0KFtdKTtcbiAgICBzZXRTZWxlY3RlZENhdGVnb3J5KHVuZGVmaW5lZCk7XG4gICAgc2V0VXBsb2FkU3RhdHVzZXMoe30pO1xuICAgIG9uQ2xvc2UoKTtcbiAgfSwgW29uQ2xvc2UsIHVwbG9hZGluZ10pO1xuXG4gIGNvbnN0IGhhbmRsZVVwbG9h
790ZCA9IHVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHtcbiAgICBpZiAodXBsb2FkaW5nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGlmIChmaWxlTGlzdC5sZW5ndGggPT09IDApIHtcbiAgICAgIG1lc3NhZ2VBcGkud2FybmluZygn7JeF66Gc65Oc7ZWgIO2MjOydvOydhCAx6rCcIOydtOyDgSDshKDtg53tlbTso7zshLjsmpQnKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBXaHk6IO2YvO2VqSDshLHqs7Uv7Iuk7YyoIO2bhCDsgqzsmqnsnpDqsIAg7LaU6rCAIO2MjOydvOydhCDrk5zrnpjqt7jtlZjrqbQgYmVmb3JlVXBsb2FkIOqwgFxuICAgIC8vIHVwbG9hZFN0YXR1c2VzIOulvCDruYTsm4wgZWZmZWN0IOqwgCBoYW5kbGVVcGxvYWQg66W8IOyerOyLpO2Wie2VnOuLpC4g7J2065WMIOyEseqzte2WiOuNmFxuICAgIC8vIO2MjOydvOuPhCDri6Tsi5wgUE9TVCAvZG9jdW1lbnRzL3VwbG9hZCDroZwg7Jis65286rCAIOyEnOuyhOyXkCDspJHrs7Ug7IOd7ISxLiBmaWxlTGlzdCDsnZhcbiAgICAvLyBzdGF0dXM9J2RvbmUnIOuKlCDrgqjslYTsnojsnLzrr4DroZwg6re46rKD7Jy866GcIOyEseqzteu2hOydhCDsi53rs4TtlbQgc2tpcCAmIO2RnOyLnCDsnKDsp4AuXG4gICAgY29uc3QgaW5pdGlhbFN0YXR1c2VzID0gZmlsZUxpc3QucmVkdWNlPFJlY29yZDxzdHJpbmcsIFVwbG9hZFN0YXRlPj4oKGFjYywgZmlsZSkgPT4ge1xuICAgICAgaWYgKGZpbGUuc3RhdHVzID09PSAnZG9uZScpIHtcbiAgICAgICAgYWNjW2ZpbGUudWlkXSA9IHsgc3RhdHVzOiAnc3VjY2VzcycsIHByb2dyZXNzOiAxMDAsIG5hbWU6IGZpbGUubmFtZSB9O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYWNjW2ZpbGUudWlkXSA9IHsgc3RhdHVzOiAncXVldWVkJywgcHJvZ3Jlc3M6IDAsIG5hbWU6IGZpbGUubmFtZSB9O1xuICAgICAgfVxuICAgICAgcmV0dXJuIGFjYztcbiAgICB9LCB7fSk7XG4gICAgc2V0VXBsb2FkU3RhdHVzZXMoaW5pdGlhbFN0YXR1c2VzKTtcblxuICAgIC8vIOydtOuyiCDsl4XroZzrk5wgcnVuIOydmCBBYm9ydENvbnRyb2xsZXIuIFwi7JeF66Gc65OcIOykkeuLqFwiIOuyhO2KvCAvIGhhbmRsZUNhbmNlbCDsnbQg7J20IHNpZ25hbCDsnYRcbiAgICAvLyBhYm9ydCDtlZzri6QuIOuqqOuToCDtjIzsnbzsnbQg6rCZ7J2AIHNpZ25hbCDsnYQg6rO17Jyg7ZWY66+A66GcIO2VnCDrsojsnZggYWJvcnQg66GcIO2YhOyerCDtjIzsnbzsnbQg7Leo7IaM65Cc64ukLlxuICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKCk7XG4gICAgYWJvcnRSZWYuY3VycmVudCA9IGNvbnRyb2xsZXI7XG5cbiAgICBzZXRVcGxvYWRpbmcodHJ1ZSk7XG4gICAgLy8g7JeF66Gc65OcIOyLnOyekSDsi5zsoJDsnZgg7Lm07YWM6rOg66asIOyEoO2DneydhCDroZzsu6wg67OA7IiY66GcIOy6oeyymO2VnOuLpC4g7IKs7Jqp7J6Q6rCAIOujqO2UhCDsp4Ttlokg7KSR7JeQXG4gICAgLy8g7Lm07YWM6rOg66asIOuTnOuhreuLpOyatOydhCDrsJTqvrjrqbQg64Ko7J2AIO2MjOydvOuTpOydtCDsg4gg7Lm07YWM6rOg66as66GcIOyYrOudvOqwgCBcIuy5tO2FjOqzoOumrCBBIOyEoO2Dne2WiOuKlOuNsFxuICAgIC8vIEIg66GcIOuTpOyWtOqwkFwiIOydtOudvOuKlCDtmLzrnoDsnbQg67Cc7IOdLiDro6jtlIQg7KCE7LK06rCAIO2VnCDrsojsnZgg7ISg7YOd7Jy866GcIOydvOq0gOuQmOqyjCDsl4XroZzrk5zrkJjqsowg6rOg7KCVLlxuICAgIGNvbnN0IHVwbG9hZENhdGVnb3J5SWQgPSBzZWxlY3RlZENhdGVnb3J5O1xuICAgIHRyeSB7XG4gICAgICBsZXQgc3VjY2Vzc2Z1bCA9IDA7XG4gICAgICBsZXQgZmFpbGVkID0gMDtcblxuICAgICAgZm9yIChjb25zdCBmaWxlIG9mIGZpbGVMaXN0KSB7XG4gICAgICAgIC8vIOydtOuvuCDshLHqs7XtlZwg7YyM7J287J2AIOyEnOuyhCDsnqzsl4XroZzrk5wg67Cp7KeA66W8IOychO2VtCDqsbTrhIjrnIAgKGluaXRpYWxTdGF0dXNlcyDsl5DshJwgJ3N1Y2Nlc3MnIOuhnCDrs7TsobTrkKgpLlxuICAgICAgICBpZiAoZmlsZS5zdGF0dXMgPT09ICdkb25lJykgY29udGludWU7XG4gICAgICAgIGNvbnN0IHVpZCA9IGZpbGUudWlkO1xuICAgICAgICBjb25zdCByYXdGaWxlID0gKGZpbGUgYXMgVXBsb2FkRmlsZSkub3JpZ2luRmlsZU9iaiBhcyBGaWxlIHwgdW5kZWZpbmVkO1xuICAgICAgICBjb25zdCBmaWxlVG9VcGxvYWQgPSByYXdGaWxlID8/IChmaWxlIGFzIHVua25vd24gYXMgRmlsZSk7XG4gICAgICAgIGNvbnN0IGRpc3BsYXlOYW1lID0gdXBsb2FkU3RhdHVzZXNbdWlkXT8ubmFtZSB8fCBmaWxlLm5hbWU7XG5cbiAgICAgICAgLy8gTWFyayBhcyB1cGxvYWRpbmdcbiAgICAgICAgc2V0VXBsb2FkU3RhdHVzZXMoKHByZXYpID0+ICh7XG4gICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICBbdWlkXTogeyAuLi4ocHJldlt1aWRdIHx8IHsgcHJvZ3Jlc3M6IDAsIG5hbWU6IGRpc3BsYXlOYW1lIH0pLCBzdGF0dXM6ICd1cGxvYWRpbmcnLCBwcm9ncmVzczogcHJldlt1aWRdPy5wcm9ncmVzcyA/PyAwLCBuYW1lOiBkaXNwbGF5TmFtZSB9LFxuICAgICAgICB9KSk7XG4gICAgICAgIHNldEZpbGVMaXN0KChwcmV2KSA9PiB7XG4gICAgICAgICAgY29uc3QgZXhpc3RpbmcgPSBwcmV2LmZpbmQoKHApID0+IHAudWlkID09PSB1aWQpO1xuICAgICAgICAgIHJldHVybiBwcmV2Lm1hcCgoZikgPT5cbiAgICAgICAgICAgIGYudWlkID09PSB1aWRcbiAgICAgICAgICAgICAgPyB7IC4uLmYsIG5hbWU6IGYubmFtZSB8fCBkaXNwbGF5TmFtZSwgc3RhdHVzOiAndXBsb2FkaW5nJywgcGVyY2VudDogZXhpc3Rpbmc/LnBlcmNlbnQgPz8gMCB9XG4gICAgICAgICAgICAgIDogZlxuICAgICAgICAgICk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgdXBsb2FkZWQgPSBhd2FpdCB1cGxvYWREb2N1bWVudChmaWxlVG9VcGxvYWQsIHVwbG9hZENhdGVnb3J5SWQsIHtcbiAgICAgICAgICAgIHRpbWVvdXQ6IEZJTEVfVVBMT0FELlVQTE9BRF9USU1FT1VUX01TLFxuICAgICAgICAgICAgc2lnbmFsOiBjb250cm9sbGVyLnNpZ25hbCxcbiAgICAgICAgICAgIG9uVXBsb2FkUHJvZ3Jlc3M6IChldmVudCkgPT4ge1xuICAgICAgICAgICAgICAvLyBXaHk6IEhUVFAvMiArIGd6aXAg65iQ64qUIGNodW5rZWQgdHJhbnNmZXIg7ZmY6rK97JeQ7IScIGZpbmFsIHByb2dyZXNzIOydtOuypO2KuOqwgFxuICAgICAgICAgICAgICAvLyBldmVudC5sb2FkZWQgPiBldmVudC50b3RhbCAoMTAxfjEwNSUpIOuYkOuKlCBldmVudC50b3RhbCA9PT0gMCAoTmFOL0luZmluaXR5KSDroZxcbiAgICAgICAgICAgICAgLy8g64+E64us7ZWY64qUIOyCrOuhgOqwgCDsnojri6QuIOq3uOuMgOuhnCBhbnRkIFByb2dyZXNzIOyXkCDrhJjquLDrqbQg66eJ64yA6rCAIG92ZXJzaG9vdCDrkJjqsbDrgphcbiAgICAgICAgICAgICAgLy8g7JiB7JuQ7Z6IIFwiVXBsb2FkaW5nXCIg7IOB7YOc66GcIOu
790CqOuKlOuLpC4gMH4xMDAg7YG0656o7ZSEICsgdG90YWw9MCDsi5wgdW5kZWZpbmVkIOuwmO2ZmC5cbiAgICAgICAgICAgICAgY29uc3QgcmF3UGVyY2VudCA9IGV2ZW50LnRvdGFsXG4gICAgICAgICAgICAgICAgPyBNYXRoLnJvdW5kKChldmVudC5sb2FkZWQgLyBldmVudC50b3RhbCkgKiAxMDApXG4gICAgICAgICAgICAgICAgOiB1bmRlZmluZWQ7XG4gICAgICAgICAgICAgIGNvbnN0IHBlcmNlbnQgPVxuICAgICAgICAgICAgICAgIHJhd1BlcmNlbnQgPT09IHVuZGVmaW5lZCB8fCAhTnVtYmVyLmlzRmluaXRlKHJhd1BlcmNlbnQpXG4gICAgICAgICAgICAgICAgICA/IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgICAgOiBNYXRoLm1pbigxMDAsIE1hdGgubWF4KDAsIHJhd1BlcmNlbnQpKTtcbiAgICAgICAgICAgICAgLy8gV2h5OiBvblVwbG9hZFByb2dyZXNzIOuKlCBYSFIg67CU7J207Yq466eI64ukIHRyaWdnZXIg65CgIOyImCDsnojslrQsIO2BsCDtjIzsnbwg7JeF66Gc65OcXG4gICAgICAgICAgICAgIC8vIOyLnCDstIjri7kg7IiY67CxfuyImOyynCDrsogg65GQIOqwnOydmCBzZXRTdGF0ZSDqsIAg7Iuk7ZaJ64+8IHJlbmRlciBzdG9ybSDsnbQg67Cc7IOd7ZaI64ukLlxuICAgICAgICAgICAgICAvLyDrsLHrtoTsnKjsnbQg67CU64CM7KeAIOyViuyVmOycvOuptCBzdGF0ZSB1cGRhdGUg66W8IOqxtOuEiOubsOyWtCDroIzrjZQg7Zqf7IiY66W8IDB+MTAwIO2ajOuhnCDsoJztlZwuXG4gICAgICAgICAgICAgIHNldFVwbG9hZFN0YXR1c2VzKChwcmV2KSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgcHJldlByb2dyZXNzID0gcHJldlt1aWRdPy5wcm9ncmVzcztcbiAgICAgICAgICAgICAgICBjb25zdCBuZXh0UHJvZ3Jlc3MgPSBwZXJjZW50ID8/IHByZXZQcm9ncmVzcyA/PyAwO1xuICAgICAgICAgICAgICAgIGlmIChwcmV2W3VpZF0/LnN0YXR1cyA9PT0gJ3VwbG9hZGluZycgJiYgcHJldlByb2dyZXNzID09PSBuZXh0UHJvZ3Jlc3MpIHtcbiAgICAgICAgICAgICAgICAgIHJldHVybiBwcmV2O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICAgICAgICAgIFt1aWRdOiB7XG4gICAgICAgICAgICAgICAgICAgIC4uLihwcmV2W3VpZF0gfHwgeyBzdGF0dXM6ICd1cGxvYWRpbmcnLCBwcm9ncmVzczogMCwgbmFtZTogZGlzcGxheU5hbWUgfSksXG4gICAgICAgICAgICAgICAgICAgIHN0YXR1czogJ3VwbG9hZGluZycsXG4gICAgICAgICAgICAgICAgICAgIHByb2dyZXNzOiBuZXh0UHJvZ3Jlc3MsXG4gICAgICAgICAgICAgICAgICAgIG5hbWU6IGRpc3BsYXlOYW1lLFxuICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB9O1xuICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgc2V0RmlsZUxpc3QoKHByZXYpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBleGlzdGluZyA9IHByZXYuZmluZCgocCkgPT4gcC51aWQgPT09IHVpZCk7XG4gICAgICAgICAgICAgICAgY29uc3QgbmV4dFBlcmNlbnQgPSBwZXJjZW50ID8/IGV4aXN0aW5nPy5wZXJjZW50O1xuICAgICAgICAgICAgICAgIGlmIChleGlzdGluZyAmJiBleGlzdGluZy5zdGF0dXMgPT09ICd1cGxvYWRpbmcnICYmIGV4aXN0aW5nLnBlcmNlbnQgPT09IG5leHRQZXJjZW50KSB7XG4gICAgICAgICAgICAgICAgICByZXR1cm4gcHJldjtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIHByZXYubWFwKChmKSA9PlxuICAgICAgICAgICAgICAgICAgZi51aWQgPT09IHVpZFxuICAgICAgICAgICAgICAgICAgICA/IHsgLi4uZiwgbmFtZTogZi5uYW1lIHx8IGRpc3BsYXlOYW1lLCBzdGF0dXM6ICd1cGxvYWRpbmcnLCBwZXJjZW50OiBuZXh0UGVyY2VudCB9XG4gICAgICAgICAgICAgICAgICAgIDogZlxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgICAvLyDrsqHthLDtmZTqsIAg64Gd64KY66m0IOyWtOuKkCDtmZTrqbTsl5Ag7J6I65OgIOyVjOumvChWZWN0b3JpemF0aW9uTm90aWZpZXIpLlxuICAgICAgICAgIHdhdGNoVmVjdG9yaXphdGlvbihbeyBpZDogdXBsb2FkZWQuaWQsIGZpbGVuYW1lOiB1cGxvYWRlZC5maWxlbmFtZSB9XSk7XG5cbiAgICAgICAgICBzdWNjZXNzZnVsICs9IDE7XG4gICAgICAgICAgc2V0VXBsb2FkU3RhdHVzZXMoKHByZXYpID0+ICh7XG4gICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgW3VpZF06IHsgLi4uKHByZXZbdWlkXSB8fCB7IHByb2dyZXNzOiAwLCBuYW1lOiBkaXNwbGF5TmFtZSB9KSwgc3RhdHVzOiAnc3VjY2VzcycsIHByb2dyZXNzOiAxMDAsIG5hbWU6IGRpc3BsYXlOYW1lIH0sXG4gICAgICAgICAgfSkpO1xuICAgICAgICAgIHNldEZpbGVMaXN0KChwcmV2KSA9PlxuICAgICAgICAgICAgcHJldi5tYXAoKGYpID0+XG4gICAgICAgICAgICAgIGYudWlkID09PSB1aWQgPyB7IC4uLmYsIG5hbWU6IGYubmFtZSB8fCBkaXNwbGF5TmFtZSwgc3RhdHVzOiAnZG9uZScsIHBlcmNlbnQ6IDEwMCB9IDogZlxuICAgICAgICAgICAgKVxuICAgICAgICAgICk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgLy8g7IKs7Jqp7J6Q6rCAIOykkeuLqChhYm9ydCntlZwg6rK97JqwOiDsl5Drn6zqsIAg7JWE64uI6528IFwi7KSR64uo65CoXCIg7Jy866GcIOuLpOujrOuLpC4g7KeE7ZaJIOykkeydtOuNmCDtjIzsnbzqs7xcbiAgICAgICAgICAvLyDslYTsp4Eg64yA6riw7Je07JeQIOuCqOydgCDtjIzsnbzsnYQgJ2NhbmNlbGxlZCcg66GcIO2RnOq4sO2VmOqzoCDro6jtlITrpbwg67mg7KC464KY6rCE64ukLiDsnbTrr7gg7ISx6rO17ZWcXG4gICAgICAgICAgLy8g7YyM7J28KOyVnuyqvSBiYXRjaCnsnYAg7ISc67KE7JeQIOyYgeyGje2ZlOuQkOycvOuvgOuhnCAnc3VjY2Vzcycg6re464yA66GcIOuRlOuLpC5cbiAgICAgICAgICBpZiAoaXNBYm9ydEVycm9yKGVycm9yKSB8fCBjb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICAgICAgICAvLyDsp4Ttlokg7KSRICsg64yA6riw7Je0IO2MjOydvOydhCAnY2FuY2VsbGVkJyDroZwuIOydtOuvuCAnc3VjY2Vzcycg7J24IO2MjOydvOydgCDqt7jrjIDroZwg67O07KG07ZWc64ukLlxuICAgICAgICAgICAgc2V0VXBsb2FkU3RhdHVzZXMoKHByZXYpID0+IHtcbiAgICAgICAgICAgICAgY29uc3QgbmV4dCA9IHsgLi4ucHJldiB9O1xuICAgICAgICAgICAgICBmb3IgKGNvbnN0IFtrZXksIHN0XSBvZiBPYmplY3QuZW50cmllcyhwcmV2KSkge1xuICAgICAgICAgICAgICAgIGlmIChzdC5zdGF0dXMgPT09ICd1cGxvYWRpbmcnIHx8IHN0LnN0YXR1cyA9PT0gJ3F1ZXVlZCcpIHtcbiAgICAgICAgICAgICAgICAgIG5leHRba2V5XSA9IHsgLi4uc3QsIHN0YXR1czogJ2NhbmNlbGxlZCcgfTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgI
790H1cbiAgICAgICAgICAgICAgcmV0dXJuIG5leHQ7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIC8vIFdoeTogZmlsZUxpc3Qg7J2YIHN0YXR1cyDripQgYW50ZCBEcmFnZ2VyIOuCtOu2gCDtkZzquLDsl5Drp4wg7JOw7J2464ukLiDspJHri6gg7YyM7J287J2EICdlcnJvcidcbiAgICAgICAgICAgIC8vIOuhnCDrkZDrqbQg67mo6rCEIOyLpO2MqOuhnCDrs7TsnbTrr4DroZwsIOyEseqzteu2hCgnZG9uZScp7J2AIOycoOyngO2VmOqzoCDrgpjrqLjsp4DripQg7KSR66a9IOyDge2DnOuhnCDrkJjrj4zrprDri6QuXG4gICAgICAgICAgICBzZXRGaWxlTGlzdCgocHJldikgPT5cbiAgICAgICAgICAgICAgcHJldi5tYXAoKGYpID0+XG4gICAgICAgICAgICAgICAgZi5zdGF0dXMgPT09ICdkb25lJyA/IGYgOiB7IC4uLmYsIHN0YXR1czogdW5kZWZpbmVkLCBlcnJvcjogdW5kZWZpbmVkIH1cbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIG1lc3NhZ2VBcGkuaW5mbygn7JeF66Gc65Oc66W8IOykkeuLqO2WiOyKteuLiOuLpC4g7J2066+4IOyYrOudvOqwhCDtjIzsnbzsnYAg7Jyg7KeA65Cp64uI64ukLicpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgZmFpbGVkICs9IDE7XG4gICAgICAgICAgY29uc3QgZXJyb3JNZXNzYWdlID0gZXh0cmFjdEVycm9yTWVzc2FnZShlcnJvciwgJ+yXheuhnOuTnOyXkCDsi6TtjKjtlojsirXri4jri6QnKTtcbiAgICAgICAgICBzZXRVcGxvYWRTdGF0dXNlcygocHJldikgPT4gKHtcbiAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICBbdWlkXToge1xuICAgICAgICAgICAgICAuLi4ocHJldlt1aWRdIHx8IHsgcHJvZ3Jlc3M6IDAsIG5hbWU6IGRpc3BsYXlOYW1lIH0pLFxuICAgICAgICAgICAgICBzdGF0dXM6ICdlcnJvcicsXG4gICAgICAgICAgICAgIGVycm9yOiBlcnJvck1lc3NhZ2UsXG4gICAgICAgICAgICAgIHByb2dyZXNzOiBwcmV2W3VpZF0/LnByb2dyZXNzID8/IDAsXG4gICAgICAgICAgICAgIG5hbWU6IGRpc3BsYXlOYW1lLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KSk7XG4gICAgICAgICAgc2V0RmlsZUxpc3QoKHByZXYpID0+XG4gICAgICAgICAgICBwcmV2Lm1hcCgoZikgPT5cbiAgICAgICAgICAgICAgZi51aWQgPT09IHVpZFxuICAgICAgICAgICAgICAgID8geyAuLi5mLCBuYW1lOiBmLm5hbWUgfHwgZGlzcGxheU5hbWUsIHN0YXR1czogJ2Vycm9yJywgZXJyb3I6IG5ldyBFcnJvcihlcnJvck1lc3NhZ2UpIH1cbiAgICAgICAgICAgICAgICA6IGZcbiAgICAgICAgICAgIClcbiAgICAgICAgICApO1xuICAgICAgICAgIC8vIENvbnRpbnVlIHdpdGggcmVtYWluaW5nIGZpbGVzXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gV2h5OiDspJHri6jrkJjrqbQgc3VjY2Vzcy93YXJuaW5nL2Vycm9yIOyalOyVvSDthqDsiqTtirjsmYAgb25TdWNjZXNzKCkvaGFuZGxlQ2FuY2VsKCkg7J2EIOqxtOuEiOubtOuLpC5cbiAgICAgIC8vIOydtOuvuCDshLHqs7XtlZwg7YyM7J287J20IOyeiOyXiOuNlOudvOuPhCBvblN1Y2Nlc3MocmVsb2FkKeuKlCDsgqzsmqnsnpDqsIAg66qo64us7J2EIOuLq+ydhCDrlYwg7J287Ja064KY64+EIOuKpuyngFxuICAgICAgLy8g7JWK7Jy866mwLCDspJHri6gg7KeB7ZuEIOy2lOqwgCDthqDsiqTtirjroZwg7IKs7Jqp7J6Q66W8IO2YvOuegOyKpOufveqyjCDtlZjsp4Ag7JWK6riwIOychO2VqC5cbiAgICAgIGlmIChjb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSB7XG4gICAgICAgIC8vIOyEseqzteu2hOydtCDsnojsl4jsnLzrqbQg67aA66qoIOuqqeuhneydhCDqsLHsi6DtlbQg7IOJ7J24IOyDge2DnOulvCDrs7wg7IiYIOyeiOqyjCDtlZzri6QuXG4gICAgICAgIGlmIChzdWNjZXNzZnVsID4gMCkge1xuICAgICAgICAgIG9uU3VjY2VzcygpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGZhaWxlZCA9PT0gMCkge1xuICAgICAgICBtZXNzYWdlQXBpLnN1Y2Nlc3MoYCR7c3VjY2Vzc2Z1bH3qsJwg7YyM7J287J20IOuqqOuRkCDsl4XroZzrk5zrkJjsl4jsirXri4jri6RgKTtcbiAgICAgICAgb25TdWNjZXNzKCk7XG4gICAgICAgIGhhbmRsZUNhbmNlbCgpOyAvLyBjbG9zZSBhbmQgcmVzZXQgd2hlbiBldmVyeXRoaW5nIHN1Y2NlZWRlZFxuICAgICAgfSBlbHNlIGlmIChzdWNjZXNzZnVsID4gMCkge1xuICAgICAgICBtZXNzYWdlQXBpLndhcm5pbmcoYCR7c3VjY2Vzc2Z1bH3qsJwg7JeF66Gc65OcIOyZhOujjCwgJHtmYWlsZWR96rCcIOyLpO2MqGApO1xuICAgICAgICBvblN1Y2Nlc3MoKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG1lc3NhZ2VBcGkuZXJyb3IoJ+uqqOuToCDsl4XroZzrk5zqsIAg7Iuk7Yyo7ZaI7Iq164uI64ukJyk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIG1lc3NhZ2VBcGkuZXJyb3IoZXh0cmFjdEVycm9yTWVzc2FnZShlcnJvciwgJ+yXheuhnOuTnOyXkCDsi6TtjKjtlojsirXri4jri6QnKSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHNldFVwbG9hZGluZyhmYWxzZSk7XG4gICAgICAvLyDsnbTrsoggcnVuIOydmCDsu6jtirjroaTrn6wg7KCV66asLiDsnbTtm4QgdW5tb3VudCBjbGVhbnVwIOydtCBzdGFsZSDsu6jtirjroaTrn6zrpbwgYWJvcnQg7ZWY7KeAIOyViuuPhOuhnS5cbiAgICAgIGlmIChhYm9ydFJlZi5jdXJyZW50ID09PSBjb250cm9sbGVyKSB7XG4gICAgICAgIGFib3J0UmVmLmN1cnJlbnQgPSBudWxsO1xuICAgICAgfVxuICAgIH1cbiAgfSwgW2ZpbGVMaXN0LCBtZXNzYWdlQXBpLCBvblN1Y2Nlc3MsIHNlbGVjdGVkQ2F0ZWdvcnksIHVwbG9hZFN0YXR1c2VzLCBoYW5kbGVDYW5jZWwsIHVwbG9hZGluZ10pO1xuXG4gIGNvbnN0IHVwbG9hZFN0YXR1c0NvdW50ID0gT2JqZWN0LmtleXModXBsb2FkU3RhdHVzZXMpLmxlbmd0aDtcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghYXV0b1VwbG9hZCB8fCB1cGxvYWRpbmcpIHJldHVybjtcbiAgICBpZiAoZmlsZUxpc3QubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgaWYgKHVwbG9hZFN0YXR1c0NvdW50ID4gMCkgcmV0dXJuO1xuICAgIC8vIHF1ZXVlTWljcm90YXNrOiBoYW5kbGVVcGxvYWQg6rCAIHNldFVwbG9hZGluZyDrk7Egc3luY2hyb25vdXMgc2V0U3RhdGUg66W8IOu2gOultOuvgOuhnFxuICAgIC8vIGVmZmVjdCDrs7jrrLgg7KeB7KCRIO2YuOy2nOydgCBSZWFjdCAxOSBzZXQtc3RhdGUtaW4tZWZmZWN0IOujsCDsnITrsJguIG1pY3JvdGFzayDtmoztlLwuXG4gICAgcXVldWVNaWNyb3Rhc2soKCkgPT4geyB2b2lkIGhhbmRsZVVwbG9h
790ZCgpOyB9KTtcbiAgfSwgW2F1dG9VcGxvYWQsIHVwbG9hZGluZywgZmlsZUxpc3QubGVuZ3RoLCB1cGxvYWRTdGF0dXNDb3VudCwgaGFuZGxlVXBsb2FkXSk7XG5cbiAgcmV0dXJuIChcbiAgICA8TW9kYWxcbiAgICAgIHRpdGxlPVwi66y47IScIOyXheuhnOuTnFwiXG4gICAgICBvcGVuPXtvcGVufVxuICAgICAgb25Paz17aGFuZGxlVXBsb2FkfVxuICAgICAgb25DYW5jZWw9e2hhbmRsZUNhbmNlbH1cbiAgICAgIGNvbmZpcm1Mb2FkaW5nPXt1cGxvYWRpbmd9XG4gICAgICBva1RleHQ9XCLsl4XroZzrk5xcIlxuICAgICAgY2FuY2VsVGV4dD1cIuy3qOyGjFwiXG4gICAgICB3aWR0aD17TU9EQUxfV0lEVEgubWR9XG4gICAgICAvLyBXaHk6IOydtOygnCBhYm9ydCDqsIAg7J6I7Jy866+A66GcIOynhO2WiSDspJEg64ur6riw66W8IOunieyngCDslYrripTri6QuIFgvRXNjL21hc2sv7Leo7IaM64qUIGhhbmRsZUNhbmNlbFxuICAgICAgLy8g66GcIOuqqOydtOupsCwgdXBsb2FkaW5nIOykkeydtOuptCBpbi1mbGlnaHQg7JqU7LKt7J2EIGFib3J0IO2VnOuLpCjro6jtlIQgYWJvcnQg67aE6riw6rCAIOydtOuvuCDsmKzrnbzqsIRcbiAgICAgIC8vIO2MjOydvOydhCDrs7TsobTtlZjqs6AgdXBsb2FkaW5nPWZhbHNlIOuhnCDsoJXrpqwpLiDri6vquLDripQgYWJvcnQg7LKY66as6rCAIOuBneuCnCDrkqQg7ZWcIOuyiCDrjZQg64ur7J2EIOuVjFxuICAgICAgLy8g7IiY7ZaJ65CY66+A66GcLCDthqDsiqTtirjqsIAg7IKs65287KeEIOuqqOuLrCDrkqTsl5Ag65yo642YIOyYmyDtmLzrnoDrj4Qg7J6s67Cc7ZWY7KeAIOyViuuKlOuLpC5cbiAgICAgIG9rQnV0dG9uUHJvcHM9e3sgZGlzYWJsZWQ6IHVwbG9hZGluZyB9fVxuICAgID5cbiAgICAgIDxTcGFjZSBvcmllbnRhdGlvbj1cInZlcnRpY2FsXCIgc3R5bGU9e3sgd2lkdGg6ICcxMDAlJyB9fSBzaXplPVwibGFyZ2VcIj5cbiAgICAgICAgey8qIENhdGVnb3J5IFNlbGVjdG9yICovfVxuICAgICAgICA8ZGl2PlxuICAgICAgICAgIDxUZXh0IHN0cm9uZz7subTthYzqs6DrpqwgKOyEoO2DnSDsgqztla0pOjwvVGV4dD5cbiAgICAgICAgICA8U2VsZWN0XG4gICAgICAgICAgICBwbGFjZWhvbGRlcj1cIuyEoO2DnSDslYgg7ZWoICjrr7jrtoTrpZgpXCJcbiAgICAgICAgICAgIHN0eWxlPXt7IHdpZHRoOiAnMTAwJScsIG1hcmdpblRvcDogOCB9fVxuICAgICAgICAgICAgdmFsdWU9e3NlbGVjdGVkQ2F0ZWdvcnl9XG4gICAgICAgICAgICBvbkNoYW5nZT17c2V0U2VsZWN0ZWRDYXRlZ29yeX1cbiAgICAgICAgICAgIGFsbG93Q2xlYXJcbiAgICAgICAgICAgIG9wdGlvbnM9e2NhdGVnb3J5T3B0aW9uc31cbiAgICAgICAgICAgIC8vIFdoeTogXCLrk5zrnpjqt7gg7IucIOyekOuPmSDsl4XroZzrk5xcIuqwgCDsvJzsoLgg7J6I7Jy866m0IO2MjOydvOydtCBmaWxlTGlzdCDsl5Ag65Ok7Ja07Jik64qUIOyInOqwhFxuICAgICAgICAgICAgLy8gKG1pY3JvdGFzaykg6re4IOyLnOygkOydmCBzZWxlY3RlZENhdGVnb3J5IOuhnCDsponsi5wg7JeF66Gc65Oc6rCAIOuwnOyCrOuQnOuLpC4gdXBsb2FkU3RhdHVzZXNcbiAgICAgICAgICAgIC8vIOqwgCDssYTsm4zsp4Qg65Kk7JeQ64+EIOydtCBTZWxlY3Qg66W8IOqzhOyGjSDtmZzshLEg7IOB7YOc66GcIOuRkOuptCwg7IKs7Jqp7J6Q6rCAIFwi7Lm07YWM6rOg66as66W8XG4gICAgICAgICAgICAvLyDsp4DsoJXtlojri6RcIuqzoCDripDrgbzsp4Drp4wg7J2066+4IOuwnOyCrOuQnCDsl4XroZzrk5zsl5DripQg67CY7JiB65CY7KeAIOyViuuKlCDquLDrp4zsoIEgVUnqsIAg65Cc64ukXG4gICAgICAgICAgICAvLyAo7Iuk7KCcIOusuOydmDogXCLsp4DsoJXtlojripTrjbAg66+467aE66WY66GcIOqwkFwiKS4g67Cc7IKs65CcIOuSpOyXkOuKlCDruYTtmZzshLHtmZTtlbQg7KCV7KeB7ZWcIOyLoO2YuOulvFxuICAgICAgICAgICAgLy8g7KSA64ukIOKAlCDsnpDrj5kg7JeF66Gc65OcIOyekOyytOulvCDrp4nsp4DripQg7JWK64qU64ukKOq3uOqxtCDrs4Trj4Qg7KCc7ZKIIOqysOyglSkuXG4gICAgICAgICAgICBkaXNhYmxlZD17T2JqZWN0LmtleXModXBsb2FkU3RhdHVzZXMpLmxlbmd0aCA+IDB9XG4gICAgICAgICAgLz5cbiAgICAgICAgICB7Lyog7LKY7J2MIOyTsOuKlCDsgqzsmqnsnpDqsIAgXCLsi5zrj4TtlbTrtJDslbwg7JWE64qUXCIg7KCV7LGF7J2EIOuvuOumrCDslYjrgrQoVVgg6rCQ7IKsKS4gKi99XG4gICAgICAgICAgPFRleHQgdHlwZT1cInNlY29uZGFyeVwiIHN0eWxlPXt7IGZvbnRTaXplOiAxMiwgZGlzcGxheTogJ2Jsb2NrJywgbWFyZ2luVG9wOiA0IH19PlxuICAgICAgICAgICAg66+47KeA7KCVIOyLnCAn66+467aE66WYJ+uhnCDsl4XroZzrk5zrkKnri4jri6QuIOqwmeydgCDtjIzsnbzsnYAg7Lm07YWM6rOg66as66eI64ukIO2VnCDrsojslKnrp4wg7Jis66a0IOyImCDsnojsirXri4jri6QuXG4gICAgICAgICAgICB7YXV0b1VwbG9hZCAmJiAoXG4gICAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgICAgeycgJ31cbiAgICAgICAgICAgICAgICA8VGV4dCB0eXBlPVwid2FybmluZ1wiIHN0eWxlPXt7IGZvbnRTaXplOiAxMiB9fT5cbiAgICAgICAgICAgICAgICAgIOuTnOuemOq3uCDsi5wg7J6Q64+ZIOyXheuhnOuTnOqwgCDsvJzsoLgg7J6I7Ja0IO2MjOydvOydhCDstpTqsIDtlZjrqbQg7KaJ7IucIOyXheuhnOuTnOuQqeuLiOuLpCDigJQg7Lm07YWM6rOg66as64qUXG4gICAgICAgICAgICAgICAgICDtjIzsnbzsnYQg7LaU6rCA7ZWY6riwIOyghOyXkCDrqLzsoIAg7ISg7YOd7ZWY7IS47JqULlxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC8+XG4gICAgICAgICAgICApfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxTcGFjZSBhbGlnbj1cImNlbnRlclwiPlxuICAgICAgICAgIDxTd2l0Y2ggc2l6ZT1cInNtYWxsXCIgYXJpYS1sYWJlbD1cIuuTnOuemOq3uCDsi5wg7J6Q64+ZIOyXheuhnOuTnFwiIGNoZWNrZWQ9e2F1dG9VcGxvYWR9IG9uQ2hhbmdlPXtzZXRBdXRvVXBsb2FkfSAvPlxuICAgICAgICAgIDxUZXh0IHR5cGU9XCJzZWNvbmRhcnlcIj7rk5zrnpjqt7gg7IucIOyekOuPmSDsl4XroZzrk5w8L1RleHQ+XG4gICAgICAgIDwvU3BhY2U+XG5cbiAgICAgICAgey8qIEZpbGUg
790VXBsb2FkIERyYWdnZXIgKi99XG4gICAgICAgIDxEcmFnZ2VyIHsuLi51cGxvYWRQcm9wc30+XG4gICAgICAgICAgPHAgY2xhc3NOYW1lPVwiYW50LXVwbG9hZC1kcmFnLWljb25cIj5cbiAgICAgICAgICAgIDxJbmJveE91dGxpbmVkIC8+XG4gICAgICAgICAgPC9wPlxuICAgICAgICAgIDxwIGNsYXNzTmFtZT1cImFudC11cGxvYWQtdGV4dFwiPu2MjOydvOydhCDsnbQg7JiB7Jet7JeQIOuTnOuemOq3uO2VmOqxsOuCmCDtgbTrpq3tlZjsl6wg7JeF66Gc65Oc7ZWY7IS47JqUPC9wPlxuICAgICAgICAgIDxwIGNsYXNzTmFtZT1cImFudC11cGxvYWQtaGludFwiPlxuICAgICAgICAgICAg7KeA7JuQIO2YleyLnToge0ZJTEVfVVBMT0FELlNVUFBPUlRFRF9UWVBFUy5qb2luKCcsICcpfVxuICAgICAgICAgICAgPGJyIC8+XG4gICAgICAgICAgICDstZzrjIA6IHtGSUxFX1VQTE9BRC5NQVhfRklMRVN96rCcIO2MjOydvCwg6rCBIDEwME1CXG4gICAgICAgICAgICA8YnIgLz5cbiAgICAgICAgICAgIDxUZXh0IHR5cGU9XCJzZWNvbmRhcnlcIiBzdHlsZT17eyBmb250U2l6ZTogMTIgfX0+XG4gICAgICAgICAgICAgIOyXheuhnOuTnCDtm4Qg7IOJ7J24KOuyoe2EsO2ZlCkg7LKY66as6rCAIOuBneuCmOyVvCDqsoDsg4nCt+uLteuzgOyXkCDrsJjsmIHrkKnri4jri6Qg4oCUIOuqqeuhneyXkOyEnCDsg4Htg5zrpbwg7ZmV7J247ZWY7IS47JqULlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvcD5cbiAgICAgICAgPC9EcmFnZ2VyPlxuICAgICAgICB7T2JqZWN0LmtleXModXBsb2FkU3RhdHVzZXMpLmxlbmd0aCA+IDAgPyAoXG4gICAgICAgICAgPFNwYWNlIG9yaWVudGF0aW9uPVwidmVydGljYWxcIiBzdHlsZT17eyB3aWR0aDogJzEwMCUnIH19PlxuICAgICAgICAgICAgPFRleHQgc3Ryb25nPuyXheuhnOuTnCDsp4Ttlokg7IOB7ZmpPC9UZXh0PlxuICAgICAgICAgICAgPGRpdiBzdHlsZT17eyBtYXhIZWlnaHQ6IDI0MCwgb3ZlcmZsb3dZOiAnYXV0bycgfX0+XG4gICAgICAgICAgICAgIHtmaWxlTGlzdC5tYXAoKGZpbGUpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBzdGF0ZSA9IHVwbG9hZFN0YXR1c2VzW2ZpbGUudWlkXTtcbiAgICAgICAgICAgICAgICBpZiAoIXN0YXRlKSByZXR1cm4gbnVsbDtcbiAgICAgICAgICAgICAgICBjb25zdCBwcm9ncmVzc1N0YXR1cyA9XG4gICAgICAgICAgICAgICAgICBzdGF0ZS5zdGF0dXMgPT09ICdlcnJvcidcbiAgICAgICAgICAgICAgICAgICAgPyAnZXhjZXB0aW9uJ1xuICAgICAgICAgICAgICAgICAgICA6IHN0YXRlLnN0YXR1cyA9PT0gJ3N1Y2Nlc3MnXG4gICAgICAgICAgICAgICAgICAgICAgPyAnc3VjY2VzcydcbiAgICAgICAgICAgICAgICAgICAgICA6IHN0YXRlLnN0YXR1cyA9PT0gJ3VwbG9hZGluZydcbiAgICAgICAgICAgICAgICAgICAgICAgID8gJ2FjdGl2ZSdcbiAgICAgICAgICAgICAgICAgICAgICAgIDogJ25vcm1hbCc7XG4gICAgICAgICAgICAgICAgY29uc3Qgc3RhdHVzTGFiZWwgPVxuICAgICAgICAgICAgICAgICAgc3RhdGUuc3RhdHVzID09PSAncXVldWVkJ1xuICAgICAgICAgICAgICAgICAgICA/ICfrjIDquLAnXG4gICAgICAgICAgICAgICAgICAgIDogc3RhdGUuc3RhdHVzID09PSAndXBsb2FkaW5nJ1xuICAgICAgICAgICAgICAgICAgICAgID8gJ+yXheuhnOuTnCDspJEnXG4gICAgICAgICAgICAgICAgICAgICAgOiBzdGF0ZS5zdGF0dXMgPT09ICdzdWNjZXNzJ1xuICAgICAgICAgICAgICAgICAgICAgICAgPyAn7JmE66OMJ1xuICAgICAgICAgICAgICAgICAgICAgICAgOiBzdGF0ZS5zdGF0dXMgPT09ICdjYW5jZWxsZWQnXG4gICAgICAgICAgICAgICAgICAgICAgICAgID8gJ+ykkeuLqOuQqCdcbiAgICAgICAgICAgICAgICAgICAgICAgICAgOiAn7Iuk7YyoJztcblxuICAgICAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgICA8ZGl2IGtleT17ZmlsZS51aWR9IHN0eWxlPXt7IGRpc3BsYXk6ICdmbGV4JywgZmxleERpcmVjdGlvbjogJ2NvbHVtbicsIGdhcDogNCwgbWFyZ2luQm90dG9tOiA4IH19PlxuICAgICAgICAgICAgICAgICAgICA8ZGl2IHN0eWxlPXt7IGRpc3BsYXk6ICdmbGV4JywgYWxpZ25JdGVtczogJ2NlbnRlcicsIGdhcDogOCB9fT5cbiAgICAgICAgICAgICAgICAgICAgICA8VGV4dCBlbGxpcHNpcyB0aXRsZT17ZmlsZS5uYW1lfSBzdHlsZT17eyBmbGV4OiAxIH19PlxuICAgICAgICAgICAgICAgICAgICAgICAge3VwbG9hZFN0YXR1c2VzW2ZpbGUudWlkXT8ubmFtZSB8fCBmaWxlLm5hbWV9XG4gICAgICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgICAgIDxQcm9ncmVzc1xuICAgICAgICAgICAgICAgICAgICAgICAgcGVyY2VudD17c3RhdGUucHJvZ3Jlc3N9XG4gICAgICAgICAgICAgICAgICAgICAgICBzaXplPVwic21hbGxcIlxuICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzPXtwcm9ncmVzc1N0YXR1c31cbiAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlPXt7IGZsZXhCYXNpczogMjAwLCBtYXJnaW5Cb3R0b206IDAgfX1cbiAgICAgICAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICAgICAgICAgIDxUZXh0IHR5cGU9XCJzZWNvbmRhcnlcIiBzdHlsZT17eyB3aWR0aDogODAsIHRleHRBbGlnbjogJ3JpZ2h0JyB9fT5cbiAgICAgICAgICAgICAgICAgICAgICAgIHtzdGF0dXNMYWJlbH1cbiAgICAgICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICB7c3RhdGUuZXJyb3IgJiYgKFxuICAgICAgICAgICAgICAgICAgICAgIDxUZXh0IHR5cGU9XCJkYW5nZXJcIiBzdHlsZT17eyBtYXJnaW5MZWZ0OiAyIH19PlxuICAgICAgICAgICAgICAgICAgICAgICAge3N0YXRlLmVycm9yfVxuICAgICAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgIH0pfVxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPC9TcGFjZT5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICBmaWxlTGlzdC5sZW5ndGggPiAxICYmIChcbiAgICAgICAgICAgIDxUZXh0IHR5cGU9XCJzZWNvbmRhcnlcIj5cbiAgICAgICAgICAgICAg7J286rSEIOyXheuhnOuTnDoge2ZpbGVMaXN0Lmxlbmd0aH3qsJwg7YyM7J28IOyEoO2DneuQqFxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIClcbiAgICAgICAgKX1cbiAgICAgICAgey8qIFdoeTog64yA65+JKOy1nOuMgCA1MOqwnC/qsIEgMTAwTUIpIOyXheuhnOuTnCDspJEg7IKs7Jqp7J6Q6rCAIOqwh+2eiOyngCDslYrrj4TroZ0g66qF7Iuc7KCBIOykkeuLqCDrsoTtirzsnYRcbiAgICAgICAgICAgIOygnOqzte2VnOuLpC4g7YG066atIOyLnCDtmITsnqwgaW4tZmxpZ2h0IOyalOyyreydhCBhYm9ydCDtlZjqs6Ag64Ko7J2AIOuMgOq4sOyXtOydhCDrqYjstpjri6QuIOydtOuvuCDsmKzrnbzqsIRcbiAgICAgICAgICAgIO2MjOydvOydgCDshJzrsoTsl5Ag7JiB7IaN7ZmU64+8IOq3uOuMgOuhnCDsnKDsp4DrkJzri6QuICovfVxuICAgICAgICB7dXBsb2FkaW5nICYmIChcbiAgICAgICAgICA8U3B
790hY2Ugb3JpZW50YXRpb249XCJ2ZXJ0aWNhbFwiIHN0eWxlPXt7IHdpZHRoOiAnMTAwJScgfX0gc2l6ZT17NH0+XG4gICAgICAgICAgICA8QnV0dG9uIGRhbmdlciBvbkNsaWNrPXsoKSA9PiBhYm9ydFJlZi5jdXJyZW50Py5hYm9ydCgpfSBibG9jaz5cbiAgICAgICAgICAgICAg7JeF66Gc65OcIOykkeuLqFxuICAgICAgICAgICAgPC9CdXR0b24+XG4gICAgICAgICAgICA8VGV4dCB0eXBlPVwic2Vjb25kYXJ5XCIgc3R5bGU9e3sgZm9udFNpemU6IDEyIH19PlxuICAgICAgICAgICAgICDsl4XroZzrk5zqsIAg7KeE7ZaJIOykkeyeheuLiOuLpC4g7KSR64uo7ZW064+EIOydtOuvuCDsmKzrnbzqsIQg7YyM7J287J2AIOycoOyngOuQqeuLiOuLpC5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8L1NwYWNlPlxuICAgICAgICApfVxuICAgICAgPC9TcGFjZT5cbiAgICA8L01vZGFsPlxuICApO1xufVxuIl19

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.