File size: 34,135 Bytes
064bfd6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import React, { useCallback, useRef, useState } from 'react';
import { getModeFromInput } from 'src/components/PromptInput/inputModes.js';
import { useNotifications } from 'src/context/notifications.js';
import { ConfigurableShortcutHint } from '../components/ConfigurableShortcutHint.js';
import { FOOTER_TEMPORARY_STATUS_TIMEOUT } from '../components/PromptInput/Notifications.js';
import { getHistory } from '../history.js';
import { Text } from '../ink.js';
import type { PromptInputMode } from '../types/textInputTypes.js';
import type { HistoryEntry, PastedContent } from '../utils/config.js';
export type HistoryMode = PromptInputMode;

// Load history entries in chunks to reduce disk reads on rapid keypresses
const HISTORY_CHUNK_SIZE = 10;

// Shared state for batching concurrent load requests into a single disk read
// Mode filter is included to ensure we don't mix filtered and unfiltered caches
let pendingLoad: Promise<HistoryEntry[]> | null = null;
let pendingLoadTarget = 0;
let pendingLoadModeFilter: HistoryMode | undefined = undefined;
async function loadHistoryEntries(minCount: number, modeFilter?: HistoryMode): Promise<HistoryEntry[]> {
  // Round up to next chunk to avoid repeated small reads
  const target = Math.ceil(minCount / HISTORY_CHUNK_SIZE) * HISTORY_CHUNK_SIZE;

  // If a load is already pending with the same mode filter and will satisfy our needs, wait for it
  if (pendingLoad && pendingLoadTarget >= target && pendingLoadModeFilter === modeFilter) {
    return pendingLoad;
  }

  // If a load is pending but won't satisfy our needs or has different filter, we need to wait for it
  // to complete first, then start a new one (can't interrupt an ongoing read)
  if (pendingLoad) {
    await pendingLoad;
  }

  // Start a new load
  pendingLoadTarget = target;
  pendingLoadModeFilter = modeFilter;
  pendingLoad = (async () => {
    const entries: HistoryEntry[] = [];
    let loaded = 0;
    for await (const entry of getHistory()) {
      // If mode filter is specified, only include entries that match the mode
      if (modeFilter) {
        const entryMode = getModeFromInput(entry.display);
        if (entryMode !== modeFilter) {
          continue;
        }
      }
      entries.push(entry);
      loaded++;
      if (loaded >= pendingLoadTarget) break;
    }
    return entries;
  })();
  try {
    return await pendingLoad;
  } finally {
    pendingLoad = null;
    pendingLoadTarget = 0;
    pendingLoadModeFilter = undefined;
  }
}
export function useArrowKeyHistory(onSetInput: (value: string, mode: HistoryMode, pastedContents: Record<number, PastedContent>) => void, currentInput: string, pastedContents: Record<number, PastedContent>, setCursorOffset?: (offset: number) => void, currentMode?: HistoryMode): {
  historyIndex: number;
  setHistoryIndex: (index: number) => void;
  onHistoryUp: () => void;
  onHistoryDown: () => boolean;
  resetHistory: () => void;
  dismissSearchHint: () => void;
} {
  const [historyIndex, setHistoryIndex] = useState(0);
  const [lastShownHistoryEntry, setLastShownHistoryEntry] = useState<(HistoryEntry & {
    mode?: HistoryMode;
  }) | undefined>(undefined);
  const hasShownSearchHintRef = useRef(false);
  const {
    addNotification,
    removeNotification
  } = useNotifications();

  // Cache loaded history entries
  const historyCache = useRef<HistoryEntry[]>([]);
  // Track which mode filter the cache was loaded with
  const historyCacheModeFilter = useRef<HistoryMode | undefined>(undefined);

  // Synchronous tracker for history index to avoid stale closure issues
  // React state updates are async, so rapid keypresses can see stale values
  const historyIndexRef = useRef(0);

  // Track the mode filter that was active when history navigation started
  // This is set on the first arrow press and stays fixed until reset
  const initialModeFilterRef = useRef<HistoryMode | undefined>(undefined);

  // Refs to track current input values for draft preservation
  // These ensure we capture the draft with the latest values, not stale closure values
  const currentInputRef = useRef(currentInput);
  const pastedContentsRef = useRef(pastedContents);
  const currentModeRef = useRef(currentMode);

  // Keep refs in sync with props (synchronous update on each render)
  currentInputRef.current = currentInput;
  pastedContentsRef.current = pastedContents;
  currentModeRef.current = currentMode;
  const setInputWithCursor = useCallback((value: string, mode: HistoryMode, contents: Record<number, PastedContent>, cursorToStart = false): void => {
    onSetInput(value, mode, contents);
    setCursorOffset?.(cursorToStart ? 0 : value.length);
  }, [onSetInput, setCursorOffset]);
  const updateInput = useCallback((input: HistoryEntry | undefined, cursorToStart_0 = false): void => {
    if (!input || !input.display) return;
    const mode_0 = getModeFromInput(input.display);
    const value_0 = mode_0 === 'bash' ? input.display.slice(1) : input.display;
    setInputWithCursor(value_0, mode_0, input.pastedContents ?? {}, cursorToStart_0);
  }, [setInputWithCursor]);
  const showSearchHint = useCallback((): void => {
    addNotification({
      key: 'search-history-hint',
      jsx: <Text dimColor>
          <ConfigurableShortcutHint action="history:search" context="Global" fallback="ctrl+r" description="search history" />
        </Text>,
      priority: 'immediate',
      timeoutMs: FOOTER_TEMPORARY_STATUS_TIMEOUT
    });
  }, [addNotification]);
  const onHistoryUp = useCallback((): void => {
    // Capture and increment synchronously to handle rapid keypresses
    const targetIndex = historyIndexRef.current;
    historyIndexRef.current++;
    const inputAtPress = currentInputRef.current;
    const pastedContentsAtPress = pastedContentsRef.current;
    const modeAtPress = currentModeRef.current;
    if (targetIndex === 0) {
      initialModeFilterRef.current = modeAtPress === 'bash' ? modeAtPress : undefined;

      // Save draft synchronously using refs for the latest values
      // This ensures we capture the draft before any async operations or re-renders
      const hasInput = inputAtPress.trim() !== '';
      setLastShownHistoryEntry(hasInput ? {
        display: inputAtPress,
        pastedContents: pastedContentsAtPress,
        mode: modeAtPress
      } : undefined);
    }
    const modeFilter = initialModeFilterRef.current;
    void (async () => {
      const neededCount = targetIndex + 1; // How many entries we need

      // If mode filter changed, invalidate cache
      if (historyCacheModeFilter.current !== modeFilter) {
        historyCache.current = [];
        historyCacheModeFilter.current = modeFilter;
        historyIndexRef.current = 0;
      }

      // Load more entries if needed
      if (historyCache.current.length < neededCount) {
        // Batches concurrent requests - rapid keypresses share a single disk read
        const entries = await loadHistoryEntries(neededCount, modeFilter);
        // Only update cache if we loaded more than currently cached
        // (handles race condition where multiple loads complete out of order)
        if (entries.length > historyCache.current.length) {
          historyCache.current = entries;
        }
      }

      // Check if we can navigate
      if (targetIndex >= historyCache.current.length) {
        // Rollback the ref since we can't navigate
        historyIndexRef.current--;
        // Keep the draft intact - user stays on their current input
        return;
      }
      const newIndex = targetIndex + 1;
      setHistoryIndex(newIndex);
      updateInput(historyCache.current[targetIndex], true);

      // Show hint once per session after navigating through 2 history entries
      if (newIndex >= 2 && !hasShownSearchHintRef.current) {
        hasShownSearchHintRef.current = true;
        showSearchHint();
      }
    })();
  }, [updateInput, showSearchHint]);
  const onHistoryDown = useCallback((): boolean => {
    // Use the ref for consistent reads
    const currentIndex = historyIndexRef.current;
    if (currentIndex > 1) {
      historyIndexRef.current--;
      setHistoryIndex(currentIndex - 1);
      updateInput(historyCache.current[currentIndex - 2]);
    } else if (currentIndex === 1) {
      historyIndexRef.current = 0;
      setHistoryIndex(0);
      if (lastShownHistoryEntry) {
        // Restore the draft with its saved mode if available
        const savedMode = lastShownHistoryEntry.mode;
        if (savedMode) {
          setInputWithCursor(lastShownHistoryEntry.display, savedMode, lastShownHistoryEntry.pastedContents ?? {});
        } else {
          updateInput(lastShownHistoryEntry);
        }
      } else {
        // When in filtered mode, stay in that mode when clearing input
        setInputWithCursor('', initialModeFilterRef.current ?? 'prompt', {});
      }
    }
    return currentIndex <= 0;
  }, [lastShownHistoryEntry, updateInput, setInputWithCursor]);
  const resetHistory = useCallback((): void => {
    setLastShownHistoryEntry(undefined);
    setHistoryIndex(0);
    historyIndexRef.current = 0;
    initialModeFilterRef.current = undefined;
    removeNotification('search-history-hint');
    historyCache.current = [];
    historyCacheModeFilter.current = undefined;
  }, [removeNotification]);
  const dismissSearchHint = useCallback((): void => {
    removeNotification('search-history-hint');
  }, [removeNotification]);
  return {
    historyIndex,
    setHistoryIndex,
    onHistoryUp,
    onHistoryDown,
    resetHistory,
    dismissSearchHint
  };
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlUmVmIiwidXNlU3RhdGUiLCJnZXRNb2RlRnJvbUlucHV0IiwidXNlTm90aWZpY2F0aW9ucyIsIkNvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCIsIkZPT1RFUl9URU1QT1JBUllfU1RBVFVTX1RJTUVPVVQiLCJnZXRIaXN0b3J5IiwiVGV4dCIsIlByb21wdElucHV0TW9kZSIsIkhpc3RvcnlFbnRyeSIsIlBhc3RlZENvbnRlbnQiLCJIaXN0b3J5TW9kZSIsIkhJU1RPUllfQ0hVTktfU0laRSIsInBlbmRpbmdMb2FkIiwiUHJvbWlzZSIsInBlbmRpbmdMb2FkVGFyZ2V0IiwicGVuZGluZ0xvYWRNb2RlRmlsdGVyIiwidW5kZWZpbmVkIiwibG9hZEhpc3RvcnlFbnRyaWVzIiwibWluQ291bnQiLCJtb2RlRmlsdGVyIiwidGFyZ2V0IiwiTWF0aCIsImNlaWwiLCJlbnRyaWVzIiwibG9hZGVkIiwiZW50cnkiLCJlbnRyeU1vZGUiLCJkaXNwbGF5IiwicHVzaCIsInVzZUFycm93S2V5SGlzdG9yeSIsIm9uU2V0SW5wdXQiLCJ2YWx1ZSIsIm1vZGUiLCJwYXN0ZWRDb250ZW50cyIsIlJlY29yZCIsImN1cnJlbnRJbnB1dCIsInNldEN1cnNvck9mZnNldCIsIm9mZnNldCIsImN1cnJlbnRNb2RlIiwiaGlzdG9yeUluZGV4Iiwic2V0SGlzdG9yeUluZGV4IiwiaW5kZXgiLCJvbkhpc3RvcnlVcCIsIm9uSGlzdG9yeURvd24iLCJyZXNldEhpc3RvcnkiLCJkaXNtaXNzU2VhcmNoSGludCIsImxhc3RTaG93bkhpc3RvcnlFbnRyeSIsInNldExhc3RTaG93bkhpc3RvcnlFbnRyeSIsImhhc1Nob3duU2VhcmNoSGludFJlZiIsImFkZE5vdGlmaWNhdGlvbiIsInJlbW92ZU5vdGlmaWNhdGlvbiIsImhpc3RvcnlDYWNoZSIsImhpc3RvcnlDYWNoZU1vZGVGaWx0ZXIiLCJoaXN0b3J5SW5kZXhSZWYiLCJpbml0aWFsTW9kZUZpbHRlclJlZiIsImN1cnJlbnRJbnB1dFJlZiIsInBhc3RlZENvbnRlbnRzUmVmIiwiY3VycmVudE1vZGVSZWYiLCJjdXJyZW50Iiwic2V0SW5wdXRXaXRoQ3Vyc29yIiwiY29udGVudHMiLCJjdXJzb3JUb1N0YXJ0IiwibGVuZ3RoIiwidXBkYXRlSW5wdXQiLCJpbnB1dCIsInNsaWNlIiwic2hvd1NlYXJjaEhpbnQiLCJrZXkiLCJqc3giLCJwcmlvcml0eSIsInRpbWVvdXRNcyIsInRhcmdldEluZGV4IiwiaW5wdXRBdFByZXNzIiwicGFzdGVkQ29udGVudHNBdFByZXNzIiwibW9kZUF0UHJlc3MiLCJoYXNJbnB1dCIsInRyaW0iLCJuZWVkZWRDb3VudCIsIm5ld0luZGV4IiwiY3VycmVudEluZGV4Iiwic2F2ZWRNb2RlIl0sInNvdXJjZXMiOlsidXNlQXJyb3dLZXlIaXN0b3J5LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdXNlQ2FsbGJhY2ssIHVzZVJlZiwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGdldE1vZGVGcm9tSW5wdXQgfSBmcm9tICdzcmMvY29tcG9uZW50cy9Qcm9tcHRJbnB1dC9pbnB1dE1vZGVzLmpzJ1xuaW1wb3J0IHsgdXNlTm90aWZpY2F0aW9ucyB9IGZyb20gJ3NyYy9jb250ZXh0L25vdGlmaWNhdGlvbnMuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9jb21wb25lbnRzL0NvbmZpZ3VyYWJsZVNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IEZPT1RFUl9URU1QT1JBUllfU1RBVFVTX1RJTUVPVVQgfSBmcm9tICcuLi9jb21wb25lbnRzL1Byb21wdElucHV0L05vdGlmaWNhdGlvbnMuanMnXG5pbXBvcnQgeyBnZXRIaXN0b3J5IH0gZnJvbSAnLi4vaGlzdG9yeS5qcydcbmltcG9ydCB7IFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IFByb21wdElucHV0TW9kZSB9IGZyb20gJy4uL3R5cGVzL3RleHRJbnB1dFR5cGVzLmpzJ1xuaW1wb3J0IHR5cGUgeyBIaXN0b3J5RW50cnksIFBhc3RlZENvbnRlbnQgfSBmcm9tICcuLi91dGlscy9jb25maWcuanMnXG5cbmV4cG9ydCB0eXBlIEhpc3RvcnlNb2RlID0gUHJvbXB0SW5wdXRNb2RlXG5cbi8vIExvYWQgaGlzdG9yeSBlbnRyaWVzIGluIGNodW5rcyB0byByZWR1Y2UgZGlzayByZWFkcyBvbiByYXBpZCBrZXlwcmVzc2VzXG5jb25zdCBISVNUT1JZX0NIVU5LX1NJWkUgPSAxMFxuXG4vLyBTaGFyZWQgc3RhdGUgZm9yIGJhdGNoaW5nIGNvbmN1cnJlbnQgbG9hZCByZXF1ZXN0cyBpbnRvIGEgc2luZ2xlIGRpc2sgcmVhZFxuLy8gTW9kZSBmaWx0ZXIgaXMgaW5jbHVkZWQgdG8gZW5zdXJlIHdlIGRvbid0IG1peCBmaWx0ZXJlZCBhbmQgdW5maWx0ZXJlZCBjYWNoZXNcbmxldCBwZW5kaW5nTG9hZDogUHJvbWlzZTxIaXN0b3J5RW50cnlbXT4gfCBudWxsID0gbnVsbFxubGV0IHBlbmRpbmdMb2FkVGFyZ2V0ID0gMFxubGV0IHBlbmRpbmdMb2FkTW9kZUZpbHRlcjogSGlzdG9yeU1vZGUgfCB1bmRlZmluZWQgPSB1bmRlZmluZWRcblxuYXN5bmMgZnVuY3Rpb24gbG9hZEhpc3RvcnlFbnRyaWVzKFxuICBtaW5Db3VudDogbnVtYmVyLFxuICBtb2RlRmlsdGVyPzogSGlzdG9yeU1vZGUsXG4pOiBQcm9taXNlPEhpc3RvcnlFbnRyeVtdPiB7XG4gIC8vIFJvdW5kIHVwIHRvIG5leHQgY2h1bmsgdG8gYXZvaWQgcmVwZWF0ZWQgc21hbGwgcmVhZHNcbiAgY29uc3QgdGFyZ2V0ID0gTWF0aC5jZWlsKG1pbkNvdW50IC8gSElTVE9SWV9DSFVOS19TSVpFKSAqIEhJU1RPUllfQ0hVTktfU0laRVxuXG4gIC8vIElmIGEgbG9hZCBpcyBhbHJlYWR5IHBlbmRpbmcgd2l0aCB0aGUgc2FtZSBtb2RlIGZpbHRlciBhbmQgd2lsbCBzYXRpc2Z5IG91ciBuZWVkcywgd2FpdCBmb3IgaXRcbiAgaWYgKFxuICAgIHBlbmRpbmdMb2FkICYmXG4gICAgcGVuZGluZ0xvYWRUYXJnZXQgPj0gdGFyZ2V0ICYmXG4gICAgcGVuZGluZ0xvYWRNb2RlRmlsdGVyID09PSBtb2RlRmlsdGVyXG4gICkge1xuICAgIHJldHVybiBwZW5kaW5nTG9hZFxuICB9XG5cbiAgLy8gSWYgYSBsb2FkIGlzIHBlbmRpbmcgYnV0IHdvbid0IHNhdGlzZnkgb3VyIG5lZWRzIG9yIGhhcyBkaWZmZXJlbnQgZmlsdGVyLCB3ZSBuZWVkIHRvIHdhaXQgZm9yIGl0XG4gIC8vIHRvIGNvbXBsZXRlIGZpcnN0LCB0aGVuIHN0YXJ0IGEgbmV3IG9uZSAoY2FuJ3QgaW50ZXJydXB0IGFuIG9uZ29pbmcgcmVhZClcbiAgaWYgKHBlbmRpbmdMb2FkKSB7XG4gICAgYXdhaXQgcGVuZGluZ0xvYWRcbiAgfVxuXG4gIC8vIFN0YXJ0IGEgbmV3IGxvYWRcbiAgcGVuZGluZ0xvYWRUYXJnZXQgPSB0YXJnZXRcbiAgcGVuZGluZ0xvYWRNb2RlRmlsdGVyID0gbW9kZUZpbHRlclxuICBwZW5kaW5nTG9hZCA9IChhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgZW50cmllczogSGlzdG9yeUVudHJ5W10gPSBbXVxuICAgIGxldCBsb2FkZWQgPSAwXG4gICAgZm9yIGF3YWl0IChjb25zdCBlbnRyeSBvZiBnZXRIaXN0b3J5KCkpIHtcbiAgICAgIC8vIElmIG1vZGUgZmlsdGVyIGlzIHNwZWNpZmllZCwgb25seSBpbmNsdWRlIGVudHJpZXMgdGhhdCBtYXRjaCB0aGUgbW9kZVxuICAgICAgaWYgKG1vZGVGaWx0ZXIpIHtcbiAgICAgICAgY29uc3QgZW50cnlNb2RlID0gZ2V0TW9kZUZyb21JbnB1dChlbnRyeS5kaXNwbGF5KVxuICAgICAgICBpZiAoZW50cnlNb2RlICE9PSBtb2RlRmlsdGVyKSB7XG4gICAgICAgICAgY29udGludWVcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZW50cmllcy5wdXNoKGVudHJ5KVxuICAgICAgbG9hZGVkKytcbiAgICAgIGlmIChsb2FkZWQgPj0gcGVuZGluZ0xvYWRUYXJnZXQpIGJyZWFrXG4gICAgfVxuICAgIHJldHVybiBlbnRyaWVzXG4gIH0pKClcblxuICB0cnkge1xuICAgIHJldHVybiBhd2FpdCBwZW5kaW5nTG9hZFxuICB9IGZpbmFsbHkge1xuICAgIHBlbmRpbmdMb2FkID0gbnVsbFxuICAgIHBlbmRpbmdMb2FkVGFyZ2V0ID0gMFxuICAgIHBlbmRpbmdMb2FkTW9kZUZpbHRlciA9IHVuZGVmaW5lZFxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VBcnJvd0tleUhpc3RvcnkoXG4gIG9uU2V0SW5wdXQ6IChcbiAgICB2YWx1ZTogc3RyaW5nLFxuICAgIG1vZGU6IEhpc3RvcnlNb2RlLFxuICAgIHBhc3RlZENvbnRlbnRzOiBSZWNvcmQ8bnVtYmVyLCBQYXN0ZWRDb250ZW50PixcbiAgKSA9PiB2b2lkLFxuICBjdXJyZW50SW5wdXQ6IHN0cmluZyxcbiAgcGFzdGVkQ29udGVudHM6IFJlY29yZDxudW1iZXIsIFBhc3RlZENvbnRlbnQ+LFxuICBzZXRDdXJzb3JPZmZzZXQ/OiAob2Zmc2V0OiBudW1iZXIpID0+IHZvaWQsXG4gIGN1cnJlbnRNb2RlPzogSGlzdG9yeU1vZGUsXG4pOiB7XG4gIGhpc3RvcnlJbmRleDogbnVtYmVyXG4gIHNldEhpc3RvcnlJbmRleDogKGluZGV4OiBudW1iZXIpID0+IHZvaWRcbiAgb25IaXN0b3J5VXA6ICgpID0+IHZvaWRcbiAgb25IaXN0b3J5RG93bjogKCkgPT4gYm9vbGVhblxuICByZXNldEhpc3Rvcnk6ICgpID0+IHZvaWRcbiAgZGlzbWlzc1NlYXJjaEhpbnQ6ICgpID0+IHZvaWRcbn0ge1xuICBjb25zdCBbaGlzdG9yeUluZGV4LCBzZXRIaXN0b3J5SW5kZXhdID0gdXNlU3RhdGUoMClcbiAgY29uc3QgW2xhc3RTaG93bkhpc3RvcnlFbnRyeSwgc2V0TGFzdFNob3duSGlzdG9yeUVudHJ5XSA9IHVzZVN0YXRlPFxuICAgIChIaXN0b3J5RW50cnkgJiB7IG1vZGU/OiBIaXN0b3J5TW9kZSB9KSB8IHVuZGVmaW5lZFxuICA+KHVuZGVmaW5lZClcbiAgY29uc3QgaGFzU2hvd25TZWFyY2hIaW50UmVmID0gdXNlUmVmKGZhbHNlKVxuICBjb25zdCB7IGFkZE5vdGlmaWNhdGlvbiwgcmVtb3ZlTm90aWZpY2F0aW9uIH0gPSB1c2VOb3RpZmljYXRpb25zKClcblxuICAvLyBDYWNoZSBsb2FkZWQgaGlzdG9yeSBlbnRyaWVzXG4gIGNvbnN0IGhpc3RvcnlDYWNoZSA9IHVzZVJlZjxIaXN0b3J5RW50cnlbXT4oW10pXG4gIC8vIFRyYWNrIHdoaWNoIG1vZGUgZmlsdGVyIHRoZSBjYWNoZSB3YXMgbG9hZGVkIHdpdGhcbiAgY29uc3QgaGlzdG9yeUNhY2hlTW9kZUZpbHRlciA9IHVzZVJlZjxIaXN0b3J5TW9kZSB8IHVuZGVmaW5lZD4odW5kZWZpbmVkKVxuXG4gIC8vIFN5bmNocm9ub3VzIHRyYWNrZXIgZm9yIGhpc3RvcnkgaW5kZXggdG8gYXZvaWQgc3RhbGUgY2xvc3VyZSBpc3N1ZXNcbiAgLy8gUmVhY3Qgc3RhdGUgdXBkYXRlcyBhcmUgYXN5bmMsIHNvIHJhcGlkIGtleXByZXNzZXMgY2FuIHNlZSBzdGFsZSB2YWx1ZXNcbiAgY29uc3QgaGlzdG9yeUluZGV4UmVmID0gdXNlUmVmKDApXG5cbiAgLy8gVHJhY2sgdGhlIG1vZGUgZmlsdGVyIHRoYXQgd2FzIGFjdGl2ZSB3aGVuIGhpc3RvcnkgbmF2aWdhdGlvbiBzdGFydGVkXG4gIC8vIFRoaXMgaXMgc2V0IG9uIHRoZSBmaXJzdCBhcnJvdyBwcmVzcyBhbmQgc3RheXMgZml4ZWQgdW50aWwgcmVzZXRcbiAgY29uc3QgaW5pdGlhbE1vZGVGaWx0ZXJSZWYgPSB1c2VSZWY8SGlzdG9yeU1vZGUgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZClcblxuICAvLyBSZWZzIHRvIHRyYWNrIGN1cnJlbnQgaW5wdXQgdmFsdWVzIGZvciBkcmFmdCBwcmVzZXJ2YXRpb25cbiAgLy8gVGhlc2UgZW5zdXJlIHdlIGNhcHR1cmUgdGhlIGRyYWZ0IHdpdGggdGhlIGxhdGVzdCB2YWx1ZXMsIG5vdCBzdGFsZSBjbG9zdXJlIHZhbHVlc1xuICBjb25zdCBjdXJyZW50SW5wdXRSZWYgPSB1c2VSZWYoY3VycmVudElucHV0KVxuICBjb25zdCBwYXN0ZWRDb250ZW50c1JlZiA9IHVzZVJlZihwYXN0ZWRDb250ZW50cylcbiAgY29uc3QgY3VycmVudE1vZGVSZWYgPSB1c2VSZWYoY3VycmVudE1vZGUpXG5cbiAgLy8gS2VlcCByZWZzIGluIHN5bmMgd2l0aCBwcm9wcyAoc3luY2hyb25vdXMgdXBkYXRlIG9uIGVhY2ggcmVuZGVyKVxuICBjdXJyZW50SW5wdXRSZWYuY3VycmVudCA9IGN1cnJlbnRJbnB1dFxuICBwYXN0ZWRDb250ZW50c1JlZi5jdXJyZW50ID0gcGFzdGVkQ29udGVudHNcbiAgY3VycmVudE1vZGVSZWYuY3VycmVudCA9IGN1cnJlbnRNb2RlXG5cbiAgY29uc3Qgc2V0SW5wdXRXaXRoQ3Vyc29yID0gdXNlQ2FsbGJhY2soXG4gICAgKFxuICAgICAgdmFsdWU6IHN0cmluZyxcbiAgICAgIG1vZGU6IEhpc3RvcnlNb2RlLFxuICAgICAgY29udGVudHM6IFJlY29yZDxudW1iZXIsIFBhc3RlZENvbnRlbnQ+LFxuICAgICAgY3Vyc29yVG9TdGFydCA9IGZhbHNlLFxuICAgICk6IHZvaWQgPT4ge1xuICAgICAgb25TZXRJbnB1dCh2YWx1ZSwgbW9kZSwgY29udGVudHMpXG4gICAgICBzZXRDdXJzb3JPZmZzZXQ/LihjdXJzb3JUb1N0YXJ0ID8gMCA6IHZhbHVlLmxlbmd0aClcbiAgICB9LFxuICAgIFtvblNldElucHV0LCBzZXRDdXJzb3JPZmZzZXRdLFxuICApXG5cbiAgY29uc3QgdXBkYXRlSW5wdXQgPSB1c2VDYWxsYmFjayhcbiAgICAoaW5wdXQ6IEhpc3RvcnlFbnRyeSB8IHVuZGVmaW5lZCwgY3Vyc29yVG9TdGFydCA9IGZhbHNlKTogdm9pZCA9PiB7XG4gICAgICBpZiAoIWlucHV0IHx8ICFpbnB1dC5kaXNwbGF5KSByZXR1cm5cblxuICAgICAgY29uc3QgbW9kZSA9IGdldE1vZGVGcm9tSW5wdXQoaW5wdXQuZGlzcGxheSlcbiAgICAgIGNvbnN0IHZhbHVlID0gbW9kZSA9PT0gJ2Jhc2gnID8gaW5wdXQuZGlzcGxheS5zbGljZSgxKSA6IGlucHV0LmRpc3BsYXlcblxuICAgICAgc2V0SW5wdXRXaXRoQ3Vyc29yKHZhbHVlLCBtb2RlLCBpbnB1dC5wYXN0ZWRDb250ZW50cyA/PyB7fSwgY3Vyc29yVG9TdGFydClcbiAgICB9LFxuICAgIFtzZXRJbnB1dFdpdGhDdXJzb3JdLFxuICApXG5cbiAgY29uc3Qgc2hvd1NlYXJjaEhpbnQgPSB1c2VDYWxsYmFjaygoKTogdm9pZCA9PiB7XG4gICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgIGtleTogJ3NlYXJjaC1oaXN0b3J5LWhpbnQnLFxuICAgICAganN4OiAoXG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgIGFjdGlvbj1cImhpc3Rvcnk6c2VhcmNoXCJcbiAgICAgICAgICAgIGNvbnRleHQ9XCJHbG9iYWxcIlxuICAgICAgICAgICAgZmFsbGJhY2s9XCJjdHJsK3JcIlxuICAgICAgICAgICAgZGVzY3JpcHRpb249XCJzZWFyY2ggaGlzdG9yeVwiXG4gICAgICAgICAgLz5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKSxcbiAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgIHRpbWVvdXRNczogRk9PVEVSX1RFTVBPUkFSWV9TVEFUVVNfVElNRU9VVCxcbiAgICB9KVxuICB9LCBbYWRkTm90aWZpY2F0aW9uXSlcblxuICBjb25zdCBvbkhpc3RvcnlVcCA9IHVzZUNhbGxiYWNrKCgpOiB2b2lkID0+IHtcbiAgICAvLyBDYXB0dXJlIGFuZCBpbmNyZW1lbnQgc3luY2hyb25vdXNseSB0byBoYW5kbGUgcmFwaWQga2V5cHJlc3Nlc1xuICAgIGNvbnN0IHRhcmdldEluZGV4ID0gaGlzdG9yeUluZGV4UmVmLmN1cnJlbnRcbiAgICBoaXN0b3J5SW5kZXhSZWYuY3VycmVudCsrXG5cbiAgICBjb25zdCBpbnB1dEF0UHJlc3MgPSBjdXJyZW50SW5wdXRSZWYuY3VycmVudFxuICAgIGNvbnN0IHBhc3RlZENvbnRlbnRzQXRQcmVzcyA9IHBhc3RlZENvbnRlbnRzUmVmLmN1cnJlbnRcbiAgICBjb25zdCBtb2RlQXRQcmVzcyA9IGN1cnJlbnRNb2RlUmVmLmN1cnJlbnRcblxuICAgIGlmICh0YXJnZXRJbmRleCA9PT0gMCkge1xuICAgICAgaW5pdGlhbE1vZGVGaWx0ZXJSZWYuY3VycmVudCA9XG4gICAgICAgIG1vZGVBdFByZXNzID09PSAnYmFzaCcgPyBtb2RlQXRQcmVzcyA6IHVuZGVmaW5lZFxuXG4gICAgICAvLyBTYXZlIGRyYWZ0IHN5bmNocm9ub3VzbHkgdXNpbmcgcmVmcyBmb3IgdGhlIGxhdGVzdCB2YWx1ZXNcbiAgICAgIC8vIFRoaXMgZW5zdXJlcyB3ZSBjYXB0dXJlIHRoZSBkcmFmdCBiZWZvcmUgYW55IGFzeW5jIG9wZXJhdGlvbnMgb3IgcmUtcmVuZGVyc1xuICAgICAgY29uc3QgaGFzSW5wdXQgPSBpbnB1dEF0UHJlc3MudHJpbSgpICE9PSAnJ1xuICAgICAgc2V0TGFzdFNob3duSGlzdG9yeUVudHJ5KFxuICAgICAgICBoYXNJbnB1dFxuICAgICAgICAgID8ge1xuICAgICAgICAgICAgICBkaXNwbGF5OiBpbnB1dEF0UHJlc3MsXG4gICAgICAgICAgICAgIHBhc3RlZENvbnRlbnRzOiBwYXN0ZWRDb250ZW50c0F0UHJlc3MsXG4gICAgICAgICAgICAgIG1vZGU6IG1vZGVBdFByZXNzLFxuICAgICAgICAgICAgfVxuICAgICAgICAgIDogdW5kZWZpbmVkLFxuICAgICAgKVxuICAgIH1cblxuICAgIGNvbnN0IG1vZGVGaWx0ZXIgPSBpbml0aWFsTW9kZUZpbHRlclJlZi5jdXJyZW50XG5cbiAgICB2b2lkIChhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBuZWVkZWRDb3VudCA9IHRhcmdldEluZGV4ICsgMSAvLyBIb3cgbWFueSBlbnRyaWVzIHdlIG5lZWRcblxuICAgICAgLy8gSWYgbW9kZSBmaWx0ZXIgY2hhbmdlZCwgaW52YWxpZGF0ZSBjYWNoZVxuICAgICAgaWYgKGhpc3RvcnlDYWNoZU1vZGVGaWx0ZXIuY3VycmVudCAhPT0gbW9kZUZpbHRlcikge1xuICAgICAgICBoaXN0b3J5Q2FjaGUuY3VycmVudCA9IFtdXG4gICAgICAgIGhpc3RvcnlDYWNoZU1vZGVGaWx0ZXIuY3VycmVudCA9IG1vZGVGaWx0ZXJcbiAgICAgICAgaGlzdG9yeUluZGV4UmVmLmN1cnJlbnQgPSAwXG4gICAgICB9XG5cbiAgICAgIC8vIExvYWQgbW9yZSBlbnRyaWVzIGlmIG5lZWRlZFxuICAgICAgaWYgKGhpc3RvcnlDYWNoZS5jdXJyZW50Lmxlbmd0aCA8IG5lZWRlZENvdW50KSB7XG4gICAgICAgIC8vIEJhdGNoZXMgY29uY3VycmVudCByZXF1ZXN0cyAtIHJhcGlkIGtleXByZXNzZXMgc2hhcmUgYSBzaW5nbGUgZGlzayByZWFkXG4gICAgICAgIGNvbnN0IGVudHJpZXMgPSBhd2FpdCBsb2FkSGlzdG9yeUVudHJpZXMobmVlZGVkQ291bnQsIG1vZGVGaWx0ZXIpXG4gICAgICAgIC8vIE9ubHkgdXBkYXRlIGNhY2hlIGlmIHdlIGxvYWRlZCBtb3JlIHRoYW4gY3VycmVudGx5IGNhY2hlZFxuICAgICAgICAvLyAoaGFuZGxlcyByYWNlIGNvbmRpdGlvbiB3aGVyZSBtdWx0aXBsZSBsb2FkcyBjb21wbGV0ZSBvdXQgb2Ygb3JkZXIpXG4gICAgICAgIGlmIChlbnRyaWVzLmxlbmd0aCA+IGhpc3RvcnlDYWNoZS5jdXJyZW50Lmxlbmd0aCkge1xuICAgICAgICAgIGhpc3RvcnlDYWNoZS5jdXJyZW50ID0gZW50cmllc1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGlmIHdlIGNhbiBuYXZpZ2F0ZVxuICAgICAgaWYgKHRhcmdldEluZGV4ID49IGhpc3RvcnlDYWNoZS5jdXJyZW50Lmxlbmd0aCkge1xuICAgICAgICAvLyBSb2xsYmFjayB0aGUgcmVmIHNpbmNlIHdlIGNhbid0IG5hdmlnYXRlXG4gICAgICAgIGhpc3RvcnlJbmRleFJlZi5jdXJyZW50LS1cbiAgICAgICAgLy8gS2VlcCB0aGUgZHJhZnQgaW50YWN0IC0gdXNlciBzdGF5cyBvbiB0aGVpciBjdXJyZW50IGlucHV0XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICBjb25zdCBuZXdJbmRleCA9IHRhcmdldEluZGV4ICsgMVxuICAgICAgc2V0SGlzdG9yeUluZGV4KG5ld0luZGV4KVxuICAgICAgdXBkYXRlSW5wdXQoaGlzdG9yeUNhY2hlLmN1cnJlbnRbdGFyZ2V0SW5kZXhdLCB0cnVlKVxuXG4gICAgICAvLyBTaG93IGhpbnQgb25jZSBwZXIgc2Vzc2lvbiBhZnRlciBuYXZpZ2F0aW5nIHRocm91Z2ggMiBoaXN0b3J5IGVudHJpZXNcbiAgICAgIGlmIChuZXdJbmRleCA+PSAyICYmICFoYXNTaG93blNlYXJjaEhpbnRSZWYuY3VycmVudCkge1xuICAgICAgICBoYXNTaG93blNlYXJjaEhpbnRSZWYuY3VycmVudCA9IHRydWVcbiAgICAgICAgc2hvd1NlYXJjaEhpbnQoKVxuICAgICAgfVxuICAgIH0pKClcbiAgfSwgW3VwZGF0ZUlucHV0LCBzaG93U2VhcmNoSGludF0pXG5cbiAgY29uc3Qgb25IaXN0b3J5RG93biA9IHVzZUNhbGxiYWNrKCgpOiBib29sZWFuID0+IHtcbiAgICAvLyBVc2UgdGhlIHJlZiBmb3IgY29uc2lzdGVudCByZWFkc1xuICAgIGNvbnN0IGN1cnJlbnRJbmRleCA9IGhpc3RvcnlJbmRleFJlZi5jdXJyZW50XG4gICAgaWYgKGN1cnJlbnRJbmRleCA+IDEpIHtcbiAgICAgIGhpc3RvcnlJbmRleFJlZi5jdXJyZW50LS1cbiAgICAgIHNldEhpc3RvcnlJbmRleChjdXJyZW50SW5kZXggLSAxKVxuICAgICAgdXBkYXRlSW5wdXQoaGlzdG9yeUNhY2hlLmN1cnJlbnRbY3VycmVudEluZGV4IC0gMl0pXG4gICAgfSBlbHNlIGlmIChjdXJyZW50SW5kZXggPT09IDEpIHtcbiAgICAgIGhpc3RvcnlJbmRleFJlZi5jdXJyZW50ID0gMFxuICAgICAgc2V0SGlzdG9yeUluZGV4KDApXG4gICAgICBpZiAobGFzdFNob3duSGlzdG9yeUVudHJ5KSB7XG4gICAgICAgIC8vIFJlc3RvcmUgdGhlIGRyYWZ0IHdpdGggaXRzIHNhdmVkIG1vZGUgaWYgYXZhaWxhYmxlXG4gICAgICAgIGNvbnN0IHNhdmVkTW9kZSA9IGxhc3RTaG93bkhpc3RvcnlFbnRyeS5tb2RlXG4gICAgICAgIGlmIChzYXZlZE1vZGUpIHtcbiAgICAgICAgICBzZXRJbnB1dFdpdGhDdXJzb3IoXG4gICAgICAgICAgICBsYXN0U2hvd25IaXN0b3J5RW50cnkuZGlzcGxheSxcbiAgICAgICAgICAgIHNhdmVkTW9kZSxcbiAgICAgICAgICAgIGxhc3RTaG93bkhpc3RvcnlFbnRyeS5wYXN0ZWRDb250ZW50cyA/PyB7fSxcbiAgICAgICAgICApXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdXBkYXRlSW5wdXQobGFzdFNob3duSGlzdG9yeUVudHJ5KVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBXaGVuIGluIGZpbHRlcmVkIG1vZGUsIHN0YXkgaW4gdGhhdCBtb2RlIHdoZW4gY2xlYXJpbmcgaW5wdXRcbiAgICAgICAgc2V0SW5wdXRXaXRoQ3Vyc29yKCcnLCBpbml0aWFsTW9kZUZpbHRlclJlZi5jdXJyZW50ID8/ICdwcm9tcHQnLCB7fSlcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGN1cnJlbnRJbmRleCA8PSAwXG4gIH0sIFtsYXN0U2hvd25IaXN0b3J5RW50cnksIHVwZGF0ZUlucHV0LCBzZXRJbnB1dFdpdGhDdXJzb3JdKVxuXG4gIGNvbnN0IHJlc2V0SGlzdG9yeSA9IHVzZUNhbGxiYWNrKCgpOiB2b2lkID0+IHtcbiAgICBzZXRMYXN0U2hvd25IaXN0b3J5RW50cnkodW5kZWZpbmVkKVxuICAgIHNldEhpc3RvcnlJbmRleCgwKVxuICAgIGhpc3RvcnlJbmRleFJlZi5jdXJyZW50ID0gMFxuICAgIGluaXRpYWxNb2RlRmlsdGVyUmVmLmN1cnJlbnQgPSB1bmRlZmluZWRcbiAgICByZW1vdmVOb3RpZmljYXRpb24oJ3NlYXJjaC1oaXN0b3J5LWhpbnQnKVxuICAgIGhpc3RvcnlDYWNoZS5jdXJyZW50ID0gW11cbiAgICBoaXN0b3J5Q2FjaGVNb2RlRmlsdGVyLmN1cnJlbnQgPSB1bmRlZmluZWRcbiAgfSwgW3JlbW92ZU5vdGlmaWNhdGlvbl0pXG5cbiAgY29uc3QgZGlzbWlzc1NlYXJjaEhpbnQgPSB1c2VDYWxsYmFjaygoKTogdm9pZCA9PiB7XG4gICAgcmVtb3ZlTm90aWZpY2F0aW9uKCdzZWFyY2gtaGlzdG9yeS1oaW50JylcbiAgfSwgW3JlbW92ZU5vdGlmaWNhdGlvbl0pXG5cbiAgcmV0dXJuIHtcbiAgICBoaXN0b3J5SW5kZXgsXG4gICAgc2V0SGlzdG9yeUluZGV4LFxuICAgIG9uSGlzdG9yeVVwLFxuICAgIG9uSGlzdG9yeURvd24sXG4gICAgcmVzZXRIaXN0b3J5LFxuICAgIGRpc21pc3NTZWFyY2hIaW50LFxuICB9XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssSUFBSUMsV0FBVyxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQzVELFNBQVNDLGdCQUFnQixRQUFRLDBDQUEwQztBQUMzRSxTQUFTQyxnQkFBZ0IsUUFBUSw4QkFBOEI7QUFDL0QsU0FBU0Msd0JBQXdCLFFBQVEsMkNBQTJDO0FBQ3BGLFNBQVNDLCtCQUErQixRQUFRLDRDQUE0QztBQUM1RixTQUFTQyxVQUFVLFFBQVEsZUFBZTtBQUMxQyxTQUFTQyxJQUFJLFFBQVEsV0FBVztBQUNoQyxjQUFjQyxlQUFlLFFBQVEsNEJBQTRCO0FBQ2pFLGNBQWNDLFlBQVksRUFBRUMsYUFBYSxRQUFRLG9CQUFvQjtBQUVyRSxPQUFPLEtB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