UNPKG

use-travel

Version:

A React hook for state time travel with undo, redo, reset, rebase and archive functionalities.

130 lines 24.7 kB
import { useCallback, useEffect, useMemo, useReducer, useRef } from 'react'; import { Travels } from 'travels'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; export function useTravel(initialState, _options = {}) { if (__DEV__) { const { maxHistory = 10, initialPosition = 0, initialPatches } = _options; if (maxHistory <= 0) { console.error(`useTravel: maxHistory must be a positive number, but got ${maxHistory}`); } if (initialPosition < 0) { console.error(`useTravel: initialPosition must be non-negative, but got ${initialPosition}`); } if (initialPatches) { if (!Array.isArray(initialPatches.patches) || !Array.isArray(initialPatches.inversePatches)) { console.error(`useTravel: initialPatches must have 'patches' and 'inversePatches' arrays`); } else if (initialPatches.patches.length !== initialPatches.inversePatches.length) { console.error(`useTravel: initialPatches.patches and initialPatches.inversePatches must have the same length`); } } } // Create Travels instance (only once) const travelsRef = useRef(null); if (!travelsRef.current) { travelsRef.current = new Travels(initialState, Object.assign(Object.assign({}, _options), { mutable: false })); } // Force re-render when state changes const [, forceUpdate] = useReducer((x) => x + 1, 0); // Track if setState has been called in the current render cycle const setStateCalledInRender = useRef(false); // Reset the flag at the start of each render cycle useEffect(() => { setStateCalledInRender.current = false; }); // Subscribe to state changes useEffect(() => { const travels = travelsRef.current; const unsubscribe = travels.subscribe(() => { forceUpdate(); }); return unsubscribe; }, []); const travels = travelsRef.current; // Wrap setState to prevent multiple calls in the same render cycle const cachedSetState = useCallback((updater) => { if (setStateCalledInRender.current) { throw new Error('setState cannot be called multiple times in the same render cycle.'); } setStateCalledInRender.current = true; travels.setState(updater); }, [travels]); // Get the current state const state = travels.getState(); // Create controls object with memoization const cachedControls = useMemo(() => { const baseControls = travels.getControls(); // Create a proxy-like object that always gets fresh values const controls = { get position() { return travels.getPosition(); }, getHistory: () => baseControls.getHistory(), get patches() { return travels.getPatches(); }, back: (amount) => baseControls.back(amount), forward: (amount) => baseControls.forward(amount), reset: () => baseControls.reset(), go: (position) => baseControls.go(position), canBack: () => baseControls.canBack(), canForward: () => baseControls.canForward(), get canUndo() { return baseControls.canBack(); }, get canRedo() { return baseControls.canForward(); }, rebase: () => baseControls.rebase(), // Always include archive and canArchive methods for compatibility // Even in autoArchive mode, archive() can be called (but will warn) archive: () => { if ('archive' in baseControls) { baseControls.archive(); } else { travels.archive(); } }, canArchive: () => { if ('canArchive' in baseControls) { return baseControls.canArchive(); } return travels.canArchive(); }, }; return controls; }, [travels]); return [state, cachedSetState, cachedControls]; } /** * Subscribes to an existing Travels store and bridges it into React via `useSyncExternalStore`. * * The hook keeps React in sync with the store's state and exposes the same tuple shape as {@link useTravel}, but it * does not create or manage the store lifecycle. Mutable Travels instances are rejected because they reuse the same * state reference, which prevents React from observing updates. * * @typeParam S - Shape of the state managed by the travel store. * @typeParam F - Whether draft freezing is enabled. * @typeParam A - Whether the instance auto-archives changes; determines the controls contract. * @typeParam P - Additional patches configuration forwarded to Mutative. * @param travels - Existing Travels instance to bind to React. * @returns A tuple containing the current state, typed updater, and history controls. * @throws {Error} If the provided `Travels` instance was created with `mutable: true`. */ export function useTravelStore(travels) { const isMutable = Boolean(travels === null || travels === void 0 ? void 0 : travels.mutable); if (isMutable) { throw new Error('useTravelStore only supports immutable Travels instances. Remove `mutable: true` or use useTravel instead.'); } const state = useSyncExternalStore(travels.subscribe, travels.getState, travels.getState); const setState = useCallback((updater) => travels.setState(updater), [travels]); const controls = useMemo(() => { const base = travels.getControls(); // Preserve the base controls as own properties so Object.keys and spread keep working. return Object.defineProperties({}, Object.assign(Object.assign({}, Object.getOwnPropertyDescriptors(base)), { canUndo: { get: () => base.canBack(), enumerable: true }, canRedo: { get: () => base.canForward(), enumerable: true } })); }, [travels]); return [state, setState, controls]; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxPQUFPLENBQUM7QUFVNUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUNsQyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQTRGcEUsTUFBTSxVQUFVLFNBQVMsQ0FLdkIsWUFBZSxFQUFFLFdBQW9DLEVBQUU7SUFDdkQsSUFBSSxPQUFPLEVBQUUsQ0FBQztRQUNaLE1BQU0sRUFBRSxVQUFVLEdBQUcsRUFBRSxFQUFFLGVBQWUsR0FBRyxDQUFDLEVBQUUsY0FBYyxFQUFFLEdBQUcsUUFBUSxDQUFDO1FBRTFFLElBQUksVUFBVSxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ3BCLE9BQU8sQ0FBQyxLQUFLLENBQ1gsNERBQTRELFVBQVUsRUFBRSxDQUN6RSxDQUFDO1FBQ0osQ0FBQztRQUVELElBQUksZUFBZSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3hCLE9BQU8sQ0FBQyxLQUFLLENBQ1gsNERBQTRELGVBQWUsRUFBRSxDQUM5RSxDQUFDO1FBQ0osQ0FBQztRQUVELElBQUksY0FBYyxFQUFFLENBQUM7WUFDbkIsSUFDRSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLE9BQU8sQ0FBQztnQkFDdEMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxjQUFjLENBQUMsRUFDN0MsQ0FBQztnQkFDRCxPQUFPLENBQUMsS0FBSyxDQUNYLDJFQUEyRSxDQUM1RSxDQUFDO1lBQ0osQ0FBQztpQkFBTSxJQUNMLGNBQWMsQ0FBQyxPQUFPLENBQUMsTUFBTSxLQUFLLGNBQWMsQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUN0RSxDQUFDO2dCQUNE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QUssR0FBRyxvQkFBb0IsQ0FDaEMsT0FBTyxDQUFDLFNBQVMsRUFDakIsT0FBTyxDQUFDLFFBQVEsRUFDaEIsT0FBTyxDQUFDLFFBQVEsQ0FDakIsQ0FBQztJQUNGLE1BQU0sUUFBUSxHQUFHLFdBQVcsQ0FDMUIsQ0FBQyxPQUFtQixFQUFFLEVBQUUsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxFQUNsRCxDQUFDLE9BQU8sQ0FBQyxDQUNWLENBQUM7SUFDRixNQUFNLFFBQVEsR0FBRyxPQUFPLENBQXFDLEdBQUcsRUFBRTtRQUNoRSxNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDbkMsdUZBQXVGO1FBQ3ZGLE9BQU8sTUFBTSxDQUFDLGdCQUFnQixDQUM1QixFQUFFLGtDQUVHLE1BQU0sQ0FBQyx5QkFBeUIsQ0FBQyxJQUFJLENBQUMsS0FDekMsT0FBTyxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLEVBQ3hELE9BQU8sRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxJQUV4QixDQUFDO0lBQzFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDZCxPQUFPLENBQUMsS0FBb0IsRUFBRSxRQUFRLEVBQUUsUUFBUSxDQUFDLENBQUM7QUFDcEQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHVzZUNhbGxiYWNrLCB1c2VFZmZlY3QsIHVzZU1lbW8sIHVzZVJlZHVjZXIsIHVzZVJlZiB9IGZyb20gJ3JlYWN0JztcbmltcG9ydCB0eXBlIHtcbiAgVHJhdmVsUGF0Y2hlcyxcbiAgVHJhdmVsc09wdGlvbnMsXG4gIFJlYmFzYWJsZU1hbnVhbFRyYXZlbHNDb250cm9scyxcbiAgUmViYXNhYmxlVHJhdmVsc0NvbnRyb2xzLFxuICBWYWx1ZSxcbiAgVXBkYXRlcixcbiAgUGF0Y2hlc09wdGlvbixcbn0gZnJvbSAndHJhdmVscyc7XG5pbXBvcnQgeyBUcmF2ZWxzIH0gZnJvbSAndHJhdmVscyc7XG5pbXBvcnQgeyB1c2VTeW5jRXh0ZXJuYWxTdG9yZSB9IGZyb20gJ3VzZS1zeW5jLWV4dGVybmFsLXN0b3JlL3NoaW0nO1xuXG5leHBvcnQgdHlwZSB7IFRyYXZlbFBhdGNoZXMgfTtcblxuLyoqIEFkZHMgYGNhblVuZG9gIGFuZCBgY2FuUmVkb2AgZmxhZ3MgdG8gYSBjb250cm9scyB0eXBlLiAqL1xuZXhwb3J0IHR5cGUgV2l0aFVuZG9SZWRvRmxhZ3M8Qz4gPSBDICYge1xuICAvKiogV2hldGhlciB1bmRvIGlzIGN1cnJlbnRseSBwb3NzaWJsZS4gKi9cbiAgcmVhZG9ubHkgY2FuVW5kbzogYm9vbGVhbjtcbiAgLyoqIFdoZXRoZXIgcmVkbyBpcyBjdXJyZW50bHkgcG9zc2libGUuICovXG4gIHJlYWRvbmx5IGNhblJlZG86IGJvb2xlYW47XG59O1xuXG50eXBlIFJlc3VsdDxcbiAgUyxcbiAgRiBleHRlbmRzIGJvb2xlYW4sXG4gIEEgZXh0ZW5kcyBib29sZWFuLFxuICBQIGV4dGVuZHMgUGF0Y2hlc09wdGlvbiA9IHt9LFxuPiA9IFtcbiAgVmFsdWU8UywgRj4sXG4gICh1cGRhdGVyOiBVcGRhdGVyPFM+KSA9PiB2b2lkLFxuICBXaXRoVW5kb1JlZG9GbGFnczxcbiAgICBBIGV4dGVuZHMgZmFsc2VcbiAgICAgID8gUmViYXNhYmxlTWFudWFsVHJhdmVsc0NvbnRyb2xzPFMsIEYsIFA+XG4gICAgICA6IFJlYmFzYWJsZVRyYXZlbHNDb250cm9sczxTLCBGLCBQPlxuICA+LFxuXTtcblxuLyoqIENvbnRyb2xzIHJldHVybmVkIGJ5IGB1c2VUcmF2ZWxTdG9yZWAsIGluY2x1ZGluZyBgY2FuVW5kb2AgYW5kIGBjYW5SZWRvYC4gKi9cbmV4cG9ydCB0eXBlIFN0b3JlQ29udHJvbHNXaXRoRmxhZ3M8XG4gIFMsXG4gIEYgZXh0ZW5kcyBib29sZWFuLFxuICBBIGV4dGVuZHMgYm9vbGVhbixcbiAgUCBleHRlbmRzIFBhdGNoZXNPcHRpb24gPSB7fSxcbj4gPSBXaXRoVW5kb1JlZG9GbGFnczxcbiAgQSBleHRlbmRzIHRydWVcbiAgICA/IFJlYmFzYWJsZVRyYXZlbHNDb250cm9sczxTLCBGLCBQPlxuICAgIDogUmViYXNhYmxlTWFudWFsVHJhdmVsc0NvbnRyb2xzPFMsIEYsIFA+XG4+O1xuXG4vKipcbiAqIENyZWF0ZXMgYSBjb21wb25lbnQtc2NvcGVkIFRyYXZlbHMgaW5zdGFuY2Ugd2l0aCB1bmRvL3JlZG8gc3VwcG9ydCBhbmQgcmV0dXJucyBpdHMgcmVhY3RpdmUgQVBJLlxuICpcbiAqIFRoZSBob29rIG1lbW9pc2VzIHRoZSB1bmRlcmx5aW5nIGBUcmF2ZWxzYCBpbnN0YW5jZSBwZXIgY29tcG9uZW50LCB3aXJlcyBpdCB0byBSZWFjdCdzIGxpZmVjeWNsZSwgYW5kIGZvcmNlc1xuICogcmUtcmVuZGVycyB3aGVuZXZlciB0aGUgbWFuYWdlZCBzdGF0ZSBjaGFuZ2VzLiBDb25zdW1lcnMgcmVjZWl2ZSBhIHR1cGxlIGNvbnRhaW5pbmcgdGhlIGN1cnJlbnQgc3RhdGUsIGEgYHNldFN0YXRlYFxuICogdXBkYXRlciB0aGF0IGFjY2VwdHMgZWl0aGVyIGEgbXV0YXRpdmUgZHJhZnQgZnVuY3Rpb24gb3IgcGFydGlhbCBzdGF0ZSwgYW5kIHRoZSBoaXN0b3J5IGNvbnRyb2xzIGV4cG9zZWQgYnlcbiAqIFRyYXZlbHMuXG4gKlxuICogQHR5cGVQYXJhbSBTIC0gU2hhcGUgb2YgdGhlIHN0YXRlIG1hbmFnZWQgYnkgdGhlIHRyYXZlbCBzdG9yZS5cbiAqIEB0eXBlUGFyYW0gRiAtIFdoZXRoZXIgZHJhZnQgZnJlZXppbmcgaXMgZW5hYmxlZC5cbiAqIEB0eXBlUGFyYW0gQSAtIFdoZXRoZXIgdGhlIGluc3RhbmNlIGF1dG8tYXJjaGl2ZXMgY2hhbmdlczsgZGV0ZXJtaW5lcyB0aGUgY29udHJvbHMgY29udHJhY3QuXG4gKiBAdHlwZVBhcmFtIFAgLSBBZGRpdGlvbmFsIHBhdGNoZXMgY29uZmlndXJhdGlvbiBmb3J3YXJkZWQgdG8gTXV0YXRpdmUuXG4gKiBAcGFyYW0gaW5pdGlhbFN0YXRlIC0gVmFsdWUgdXNlZCB0byBpbml0aWFsaXNlIHRoZSB0cmF2ZWwgc3RvcmUuXG4gKiBAcmV0dXJucyBBIHR1cGxlIHdpdGggdGhlIGN1cnJlbnQgc3RhdGUsIHR5cGVkIHVwZGF0ZXIsIGFuZCBoaXN0b3J5IGNvbnRyb2xzLlxuICogQHRocm93cyB7RXJyb3J9IFdoZW4gYHNldFN0YXRlYCBpcyBpbnZva2VkIG11bHRpcGxlIHRpbWVzIHdpdGhpbiB0aGUgc2FtZSByZW5kZXIgY3ljbGUgKGRldmVsb3BtZW50LW9ubHkgZ3VhcmQpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlVHJhdmVsPFMsIEYgZXh0ZW5kcyBib29sZWFuPihcbiAgaW5pdGlhbFN0YXRlOiBTXG4pOiBbXG4gIFZhbHVlPFMsIEY+LFxuICAodXBkYXRlcjogVXBkYXRlcjxTPikgPT4gdm9pZCxcbiAgV2l0aFVuZG9SZWRvRmxhZ3M8UmViYXNhYmxlVHJhdmVsc0NvbnRyb2xzPFMsIEY+Pixcbl07XG5leHBvcnQgZnVuY3Rpb24gdXNlVHJhdmVsPFxuICBTLFxuICBGIGV4dGVuZHMgYm9vbGVhbixcbiAgQSBleHRlbmRzIGJvb2xlYW4sXG4gIFAgZXh0ZW5kcyBQYXRjaGVzT3B0aW9uID0ge30sXG4+KFxuICBpbml0aWFsU3RhdGU6IFMsXG4gIG9wdGlvbnM6IE9taXQ8VHJhdmVsc09wdGlvbnM8RiwgdHJ1ZSwgUD4sICdhdXRvQXJjaGl2ZScgfCAnbXV0YWJsZSc+ICYge1xuICAgIGF1dG9BcmNoaXZlPzogdHJ1ZTtcbiAgfVxuKTogW1xuICBWYWx1ZTxTLCBGPixcbiAgKHVwZGF0ZXI6IFVwZGF0ZXI8Uz4pID0+IHZvaWQsXG4gIFdpdGhVbmRvUmVkb0ZsYWdzPFJlYmFzYWJsZVRyYXZlbHNDb250cm9sczxTLCBGLCBQPj4sXG5dO1xuZXhwb3J0IGZ1bmN0aW9uIHVzZVRyYXZlbDxcbiAgUyxcbiAgRiBleHRlbmRzIGJvb2xlYW4sXG4gIEEgZXh0ZW5kcyBib29sZWFuLFxuICBQIGV4dGVuZHMgUGF0Y2hlc09wdGlvbiA9IHt9LFxuPihcbiAgaW5pdGlhbFN0YXRlOiBTLFxuICBvcHRpb25zOiBPbWl0PFRyYXZlbHNPcHRpb25zPEYsIGZhbHNlLCBQPiwgJ2F1dG9BcmNoaXZlJyB8ICdtdXRhYmxlJz4gJiB7XG4gICAgYXV0b0FyY2hpdmU6IGZhbHNlO1xuICB9XG4pOiBbXG4gIFZhbHVlPFMsIEY+LFxuICAodXBkYXRlcjogVXBkYXRlcjxTPikgPT4gdm9pZCxcbiAgV2l0aFVuZG9SZWRvRmxhZ3M8UmViYXNhYmxlTWFudWFsVHJhdmVsc0NvbnRyb2xzPFMsIEYsIFA+Pixcbl07XG5leHBvcnQgZnVuY3Rpb24gdXNlVHJhdmVsPFxuICBTLFxuICBGIGV4dGVuZHMgYm9vbGVhbixcbiAgQSBleHRlbmRzIGJvb2xlYW4sXG4gIFAgZXh0ZW5kcyBQYXRjaGVzT3B0aW9uID0ge30sXG4+KGluaXRpYWxTdGF0ZTogUywgX29wdGlvbnM6IFRyYXZlbHNPcHRpb25zPEYsIEEsIFA+ID0ge30pOiBSZXN1bHQ8UywgRiwgQSwgUD4ge1xuICBpZiAoX19ERVZfXykge1xuICAgIGNvbnN0IHsgbWF4SGlzdG9yeSA9IDEwLCBpbml0aWFsUG9zaXRpb24gPSAwLCBpbml0aWFsUGF0Y2hlcyB9ID0gX29wdGlvbnM7XG5cbiAgICBpZiAobWF4SGlzdG9yeSA8PSAwKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICBgdXNlVHJhdmVsOiBtYXhIaXN0b3J5IG11c3QgYmUgYSBwb3NpdGl2ZSBudW1iZXIsIGJ1dCBnb3QgJHttYXhIaXN0b3J5fWBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgaWYgKGluaXRpYWxQb3NpdGlvbiA8IDApIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgIGB1c2VUcmF2ZWw6IGluaXRpYWxQb3NpdGlvbiBtdXN0IGJlIG5vbi1uZWdhdGl2ZSwgYnV0IGdvdCAke2luaXRpYWxQb3NpdGlvbn1gXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmIChpbml0aWFsUGF0Y2hlcykge1xuICAgICAgaWYgKFxuICAgICAgICAhQXJyYXkuaXNBcnJheShpbml0aWFsUGF0Y2hlcy5wYXRjaGVzKSB8fFxuICAgICAgICAhQXJyYXkuaXNBcnJheShpbml0aWFsUGF0Y2hlcy5pbnZlcnNlUGF0Y2hlcylcbiAgICAgICkge1xuICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgIGB1c2VUcmF2ZWw6IGluaXRpYWxQYXRjaGVzIG11c3QgaGF2ZSAncGF0Y2hlcycgYW5kICdpbnZlcnNlUGF0Y2hlcycgYXJyYXlzYFxuICAgICAgICApO1xuICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgaW5pdGlhbFBhdGNoZXMucGF0Y2hlcy5sZW5ndGggIT09IGluaXRpYWxQYXRjaGVzLmludmVyc2VQYXRjaGVzLmxlbmd0aFxuICAgICAgKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgICAgYHVzZVRyYXZlbDogaW5pdGlhbFBhdGNoZXMucGF0Y2hlcyBhbmQgaW5pdGlhbFBhdGNoZXMuaW52ZXJzZVBhdGNoZXMgbXVzdCBoYXZlIHRoZSBzYW1lIGxlbmd0aGBcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBDcmVhdGUgVHJhdmVscyBpbnN0YW5jZSAob25seSBvbmNlKVxuICBjb25zdCB0cmF2ZWxzUmVmID0gdXNlUmVmPFRyYXZlbHM8UywgRiwgQSwgUD4gfCBudWxsPihudWxsKTtcbiAgaWYgKCF0cmF2ZWxzUmVmLmN1cnJlbnQpIHtcbiAgICB0cmF2ZWxzUmVmLmN1cnJlbnQgPSBuZXcgVHJhdmVsczxTLCBGLCBBLCBQPihpbml0aWFsU3RhdGUsIHtcbiAgICAgIC4uLl9vcHRpb25zLFxuICAgICAgbXV0YWJsZTogZmFsc2UsXG4gICAgfSk7XG4gIH1cblxuICAvLyBGb3JjZSByZS1yZW5kZXIgd2hlbiBzdGF0ZSBjaGFuZ2VzXG4gIGNvbnN0IFssIGZvcmNlVXBkYXRlXSA9IHVzZVJlZHVjZXIoKHg6IG51bWJlcikgPT4geCArIDEsIDApO1xuXG4gIC8vIFRyYWNrIGlmIHNldFN0YXRlIGhhcyBiZWVuIGNhbGxlZCBpbiB0aGUgY3VycmVudCByZW5kZXIgY3ljbGVcbiAgY29uc3Qgc2V0U3RhdGVDYWxsZWRJblJlbmRlciA9IHVzZVJlZihmYWxzZSk7XG5cbiAgLy8gUmVzZXQgdGhlIGZsYWcgYXQgdGhlIHN0YXJ0IG9mIGVhY2ggcmVuZGVyIGN5Y2xlXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgc2V0U3RhdGVDYWxsZWRJblJlbmRlci5jdXJyZW50ID0gZmFsc2U7XG4gIH0pO1xuXG4gIC8vIFN1YnNjcmliZSB0byBzdGF0ZSBjaGFuZ2VzXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgY29uc3QgdHJhdmVscyA9IHRyYXZlbHNSZWYuY3VycmVudCE7XG4gICAgY29uc3QgdW5zdWJzY3JpYmUgPSB0cmF2ZWxzLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICBmb3JjZVVwZGF0ZSgpO1xuICAgIH0pO1xuICAgIHJldHVybiB1bnN1YnNjcmliZTtcbiAgfSwgW10pO1xuXG4gIGNvbnN0IHRyYXZlbHMgPSB0cmF2ZWxzUmVmLmN1cnJlbnQ7XG5cbiAgLy8gV3JhcCBzZXRTdGF0ZSB0byBwcmV2ZW50IG11bHRpcGxlIGNhbGxzIGluIHRoZSBzYW1lIHJlbmRlciBjeWNsZVxuICBjb25zdCBjYWNoZWRTZXRTdGF0ZSA9IHVzZUNhbGxiYWNrKFxuICAgICh1cGRhdGVyOiBVcGRhdGVyPFM+KSA9PiB7XG4gICAgICBpZiAoc2V0U3RhdGVDYWxsZWRJblJlbmRlci5jdXJyZW50KSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAnc2V0U3RhdGUgY2Fubm90IGJlIGNhbGxlZCBtdWx0aXBsZSB0aW1lcyBpbiB0aGUgc2FtZSByZW5kZXIgY3ljbGUuJ1xuICAgICAgICApO1xuICAgICAgfVxuICAgICAgc2V0U3RhdGVDYWxsZWRJblJlbmRlci5jdXJyZW50ID0gdHJ1ZTtcbiAgICAgIHRyYXZlbHMuc2V0U3RhdGUodXBkYXRlcik7XG4gICAgfSxcbiAgICBbdHJhdmVsc11cbiAgKTtcblxuICAvLyBHZXQgdGhlIGN1cnJlbnQgc3RhdGVcbiAgY29uc3Qgc3RhdGUgPSB0cmF2ZWxzLmdldFN0YXRlKCk7XG5cbiAgLy8gQ3JlYXRlIGNvbnRyb2xzIG9iamVjdCB3aXRoIG1lbW9pemF0aW9uXG4gIGNvbnN0IGNhY2hlZENvbnRyb2xzID0gdXNlTWVtbygoKSA9PiB7XG4gICAgY29uc3QgYmFzZUNvbnRyb2xzID0gdHJhdmVscy5nZXRDb250cm9scygpO1xuXG4gICAgLy8gQ3JlYXRlIGEgcHJveHktbGlrZSBvYmplY3QgdGhhdCBhbHdheXMgZ2V0cyBmcmVzaCB2YWx1ZXNcbiAgICBjb25zdCBjb250cm9sczogYW55ID0ge1xuICAgICAgZ2V0IHBvc2l0aW9uKCkge1xuICAgICAgICByZXR1cm4gdHJhdmVscy5nZXRQb3NpdGlvbigpO1xuICAgICAgfSxcbiAgICAgIGdldEhpc3Rvcnk6ICgpID0+IGJhc2VDb250cm9scy5nZXRIaXN0b3J5KCksXG4gICAgICBnZXQgcGF0Y2hlcygpIHtcbiAgICAgICAgcmV0dXJuIHRyYXZlbHMuZ2V0UGF0Y2hlcygpO1xuICAgICAgfSxcbiAgICAgIGJhY2s6IChhbW91bnQ/OiBudW1iZXIpID0+IGJhc2VDb250cm9scy5iYWNrKGFtb3VudCksXG4gICAgICBmb3J3YXJkOiAoYW1vdW50PzogbnVtYmVyKSA9PiBiYXNlQ29udHJvbHMuZm9yd2FyZChhbW91bnQpLFxuICAgICAgcmVzZXQ6ICgpID0+IGJhc2VDb250cm9scy5yZXNldCgpLFxuICAgICAgZ286IChwb3NpdGlvbjogbnVtYmVyKSA9PiBiYXNlQ29udHJvbHMuZ28ocG9zaXRpb24pLFxuICAgICAgY2FuQmFjazogKCkgPT4gYmFzZUNvbnRyb2xzLmNhbkJhY2soKSxcbiAgICAgIGNhbkZvcndhcmQ6ICgpID0+IGJhc2VDb250cm9scy5jYW5Gb3J3YXJkKCksXG4gICAgICBnZXQgY2FuVW5kbygpIHtcbiAgICAgICAgcmV0dXJuIGJhc2VDb250cm9scy5jYW5CYWNrKCk7XG4gICAgICB9LFxuICAgICAgZ2V0IGNhblJlZG8oKSB7XG4gICAgICAgIHJldHVybiBiYXNlQ29udHJvbHMuY2FuRm9yd2FyZCgpO1xuICAgICAgfSxcbiAgICAgIHJlYmFzZTogKCkgPT4gYmFzZUNvbnRyb2xzLnJlYmFzZSgpLFxuICAgICAgLy8gQWx3YXlzIGluY2x1ZGUgYXJjaGl2ZSBhbmQgY2FuQXJjaGl2ZSBtZXRob2RzIGZvciBjb21wYXRpYmlsaXR5XG4gICAgICAvLyBFdmVuIGluIGF1dG9BcmNoaXZlIG1vZGUsIGFyY2hpdmUoKSBjYW4gYmUgY2FsbGVkIChidXQgd2lsbCB3YXJuKVxuICAgICAgYXJjaGl2ZTogKCkgPT4ge1xuICAgICAgICBpZiAoJ2FyY2hpdmUnIGluIGJhc2VDb250cm9scykge1xuICAgICAgICAgIGJhc2VDb250cm9scy5hcmNoaXZlKCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdHJhdmVscy5hcmNoaXZlKCk7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICBjYW5BcmNoaXZlOiAoKSA9PiB7XG4gICAgICAgIGlmICgnY2FuQXJjaGl2ZScgaW4gYmFzZUNvbnRyb2xzKSB7XG4gICAgICAgICAgcmV0dXJuIGJhc2VDb250cm9scy5jYW5BcmNoaXZlKCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRyYXZlbHMuY2FuQXJjaGl2ZSgpO1xuICAgICAgfSxcbiAgICB9O1xuXG4gICAgcmV0dXJuIGNvbnRyb2xzO1xuICB9LCBbdHJhdmVsc10pO1xuXG4gIHJldHVybiBbc3RhdGUsIGNhY2hlZFNldFN0YXRlLCBjYWNoZWRDb250cm9sc10gYXMgUmVzdWx0PFMsIEYsIEE+O1xufVxuXG4vKipcbiAqIFN1YnNjcmliZXMgdG8gYW4gZXhpc3RpbmcgVHJhdmVscyBzdG9yZSBhbmQgYnJpZGdlcyBpdCBpbnRvIFJlYWN0IHZpYSBgdXNlU3luY0V4dGVybmFsU3RvcmVgLlxuICpcbiAqIFRoZSBob29rIGtlZXBzIFJlYWN0IGluIHN5bmMgd2l0aCB0aGUgc3RvcmUncyBzdGF0ZSBhbmQgZXhwb3NlcyB0aGUgc2FtZSB0dXBsZSBzaGFwZSBhcyB7QGxpbmsgdXNlVHJhdmVsfSwgYnV0IGl0XG4gKiBkb2VzIG5vdCBjcmVhdGUgb3IgbWFuYWdlIHRoZSBzdG9yZSBsaWZlY3ljbGUuIE11dGFibGUgVHJhdmVscyBpbnN0YW5jZXMgYXJlIHJlamVjdGVkIGJlY2F1c2UgdGhleSByZXVzZSB0aGUgc2FtZVxuICogc3RhdGUgcmVmZXJlbmNlLCB3aGljaCBwcmV2ZW50cyBSZWFjdCBmcm9tIG9ic2VydmluZyB1cGRhdGVzLlxuICpcbiAqIEB0eXBlUGFyYW0gUyAtIFNoYXBlIG9mIHRoZSBzdGF0ZSBtYW5hZ2VkIGJ5IHRoZSB0cmF2ZWwgc3RvcmUuXG4gKiBAdHlwZVBhcmFtIEYgLSBXaGV0aGVyIGRyYWZ0IGZyZWV6aW5nIGlzIGVuYWJsZWQuXG4gKiBAdHlwZVBhcmFtIEEgLSBXaGV0aGVyIHRoZSBpbnN0YW5jZSBhdXRvLWFyY2hpdmVzIGNoYW5nZXM7IGRldGVybWluZXMgdGhlIGNvbnRyb2xzIGNvbnRyYWN0LlxuICogQHR5cGVQYXJhbSBQIC0gQWRkaXRpb25hbCBwYXRjaGVzIGNvbmZpZ3VyYXRpb24gZm9yd2FyZGVkIHRvIE11dGF0aXZlLlxuICogQHBhcmFtIHRyYXZlbHMgLSBFeGlzdGluZyBUcmF2ZWxzIGluc3RhbmNlIHRvIGJpbmQgdG8gUmVhY3QuXG4gKiBAcmV0dXJucyBBIHR1cGxlIGNvbnRhaW5pbmcgdGhlIGN1cnJlbnQgc3RhdGUsIHR5cGVkIHVwZGF0ZXIsIGFuZCBoaXN0b3J5IGNvbnRyb2xzLlxuICogQHRocm93cyB7RXJyb3J9IElmIHRoZSBwcm92aWRlZCBgVHJhdmVsc2AgaW5zdGFuY2Ugd2FzIGNyZWF0ZWQgd2l0aCBgbXV0YWJsZTogdHJ1ZWAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1c2VUcmF2ZWxTdG9yZTxcbiAgUyxcbiAgRiBleHRlbmRzIGJvb2xlYW4sXG4gIEEgZXh0ZW5kcyBib29sZWFuLFxuICBQIGV4dGVuZHMgUGF0Y2hlc09wdGlvbiA9IHt9LFxuPihcbiAgdHJhdmVsczogVHJhdmVsczxTLCBGLCBBLCBQPlxuKTogW1xuICBWYWx1ZTxTLCBGPixcbiAgKHVwZGF0ZXI6IFVwZGF0ZXI8Uz4pID0+IHZvaWQsXG4gIFN0b3JlQ29udHJvbHNXaXRoRmxhZ3M8UywgRiwgQSwgUD4sXG5dIHtcbiAgY29uc3QgaXNNdXRhYmxlID0gQm9vbGVhbigodHJhdmVscyBhcyBhbnkpPy5tdXRhYmxlKTtcblxuICBpZiAoaXNNdXRhYmxlKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgJ3VzZVRyYXZlbFN0b3JlIG9ubHkgc3VwcG9ydHMgaW1tdXRhYmxlIFRyYXZlbHMgaW5zdGFuY2VzLiBSZW1vdmUgYG11dGFibGU6IHRydWVgIG9yIHVzZSB1c2VUcmF2ZWwgaW5zdGVhZC4nXG4gICAgKTtcbiAgfVxuICBjb25zdCBzdGF0ZSA9IHVzZVN5bmNFeHRlcm5hbFN0b3JlKFxuICAgIHRyYXZlbHMuc3Vic2NyaWJlLFxuICAgIHRyYXZlbHMuZ2V0U3RhdGUsXG4gICAgdHJhdmVscy5nZXRTdGF0ZVxuICApO1xuICBjb25zdCBzZXRTdGF0ZSA9IHVzZUNhbGxiYWNrKFxuICAgICh1cGRhdGVyOiBVcGRhdGVyPFM+KSA9PiB0cmF2ZWxzLnNldFN0YXRlKHVwZGF0ZXIpLFxuICAgIFt0cmF2ZWxzXVxuICApO1xuICBjb25zdCBjb250cm9scyA9IHVzZU1lbW88U3RvcmVDb250cm9sc1dpdGhGbGFnczxTLCBGLCBBLCBQPj4oKCkgPT4ge1xuICAgIGNvbnN0IGJhc2UgPSB0cmF2ZWxzLmdldENvbnRyb2xzKCk7XG4gICAgLy8gUHJlc2VydmUgdGhlIGJhc2UgY29udHJvbHMgYXMgb3duIHByb3BlcnRpZXMgc28gT2JqZWN0LmtleXMgYW5kIHNwcmVhZCBrZWVwIHdvcmtpbmcuXG4gICAgcmV0dXJuIE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKFxuICAgICAge30sXG4gICAgICB7XG4gICAgICAgIC4uLk9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzKGJhc2UpLFxuICAgICAgICBjYW5VbmRvOiB7IGdldDogKCkgPT4gYmFzZS5jYW5CYWNrKCksIGVudW1lcmFibGU6IHRydWUgfSxcbiAgICAgICAgY2FuUmVkbzogeyBnZXQ6ICgpID0+IGJhc2UuY2FuRm9yd2FyZCgpLCBlbnVtZXJhYmxlOiB0cnVlIH0sXG4gICAgICB9XG4gICAgKSBhcyBTdG9yZUNvbnRyb2xzV2l0aEZsYWdzPFMsIEYsIEEsIFA+O1xuICB9LCBbdHJhdmVsc10pO1xuICByZXR1cm4gW3N0YXRlIGFzIFZhbHVlPFMsIEY+LCBzZXRTdGF0ZSwgY29udHJvbHNdO1xufVxuIl19