use-travel
Version:
A React hook for state time travel with undo, redo, reset, rebase and archive functionalities.
130 lines • 24.7 kB
JavaScript
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