"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.PersistedState = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _events = require("events"); var _saferLodashSet = require("@kbn/safer-lodash-set"); var _lodash = require("lodash"); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ function prepSetParams(key, value, path) { // key must be the value, set the entire state using it if (value === undefined && ((0, _lodash.isPlainObject)(key) || path.length > 0)) { // setting entire tree, swap the key and value to write to the state value = key; key = undefined; } // ensure the value being passed in is never mutated return { value: (0, _lodash.cloneDeep)(value), key }; } class PersistedState extends _events.EventEmitter { constructor(value, path) { super(); (0, _defineProperty2.default)(this, "_path", void 0); (0, _defineProperty2.default)(this, "_initialized", void 0); (0, _defineProperty2.default)(this, "_changedState", void 0); (0, _defineProperty2.default)(this, "_defaultState", void 0); (0, _defineProperty2.default)(this, "_mergedState", void 0); this._path = this.setPath(path); // Some validations if (!this._path.length && value && !(0, _lodash.isPlainObject)(value)) { throw new Error('State value must be a plain object'); } value = value || this.getDefault(); // copy passed state values and create internal trackers this.set(value); this._initialized = true; // used to track state changes } get(key, defaultValue) { // no path and no key, get the whole state if (!this.hasPath() && key === undefined) { return this._mergedState; } return (0, _lodash.cloneDeep)((0, _lodash.get)(this._mergedState, this.getIndex(key || ''), defaultValue)); } set(key, value) { const params = prepSetParams(key, value, this._path); const val = this.setValue(params.key, params.value); this.emit('set'); return val; } setSilent(key, value) { const params = prepSetParams(key, value, this._path); if (params.key || params.value) { return this.setValue(params.key, params.value, true); } } clearAllKeys() { Object.getOwnPropertyNames(this._changedState).forEach(key => { this.set(key, null); }); } reset(path) { const keyPath = this.getIndex(path); const origValue = (0, _lodash.get)(this._defaultState, keyPath); const currentValue = (0, _lodash.get)(this._mergedState, keyPath); if (origValue === undefined) { this.cleanPath(path, this._mergedState); } else { (0, _saferLodashSet.set)(this._mergedState, keyPath, origValue); } // clean up the changedState tree this.cleanPath(path, this._changedState); if (!(0, _lodash.isEqual)(currentValue, origValue)) this.emit('change'); } getChanges() { return (0, _lodash.cloneDeep)(this._changedState); } toJSON() { return this.get(); } toString() { return JSON.stringify(this.toJSON()); } fromString(input) { return this.set(JSON.parse(input)); } getIndex(key) { if (key === undefined) return this._path; return [...(this._path || []), ...(0, _lodash.toPath)(key)]; } getPartialIndex(key) { const keyPath = this.getIndex(key); return keyPath.slice(this._path.length); } cleanPath(path, stateTree) { const partialPath = this.getPartialIndex(path); let remove = true; if (Array.isArray(partialPath)) { // recursively delete value tree, when no other keys exist while (partialPath.length > 0) { const lastKey = partialPath.splice(partialPath.length - 1, 1)[0]; const statePath = [...this._path, partialPath]; const stateVal = statePath.length > 0 ? (0, _lodash.get)(stateTree, statePath) : stateTree; // if stateVal isn't an object, do nothing if (!(0, _lodash.isPlainObject)(stateVal)) return; if (remove) delete stateVal[lastKey]; if (Object.keys(stateVal).length > 0) remove = false; } } } getDefault() { return this.hasPath() ? undefined : {}; } setPath(path) { if (Array.isArray(path)) { return path; } if ((0, _lodash.isString)(path)) { return [...this.getIndex(path)]; } return []; } hasPath() { return this._path.length > 0; } setValue(key, value, silent = false) { const self = this; let stateChanged = false; const initialState = !this._initialized; const keyPath = this.getIndex(key); const hasKeyPath = keyPath.length > 0; // if this is the initial state value, save value as the default if (initialState) { this._changedState = {}; if (!this.hasPath() && key === undefined) this._defaultState = value;else this._defaultState = (0, _saferLodashSet.set)({}, keyPath, value); } if (!initialState) { // no path and no key, set the whole state if (!this.hasPath() && key === undefined) { // compare changedState and new state, emit an event when different stateChanged = !(0, _lodash.isEqual)(this._changedState, value); this._changedState = value; this._mergedState = (0, _lodash.cloneDeep)(value); } else { // check for changes at path, emit an event when different const curVal = hasKeyPath ? this.get(keyPath) : this._mergedState; stateChanged = !(0, _lodash.isEqual)(curVal, value); // arrays are merge by index, not desired - ensure they are replaced if (Array.isArray((0, _lodash.get)(this._mergedState, keyPath))) { if (hasKeyPath) { (0, _saferLodashSet.set)(this._mergedState, keyPath, undefined); } else { this._mergedState = undefined; } } if (hasKeyPath) { (0, _saferLodashSet.set)(this._changedState, keyPath, value); } else { this._changedState = (0, _lodash.isPlainObject)(value) ? value : {}; } } } // update the merged state value const targetObj = this._mergedState || (0, _lodash.cloneDeep)(this._defaultState); const sourceObj = (0, _lodash.merge)({}, this._changedState); // handler arguments are (targetValue, sourceValue, key, target, source) const mergeMethod = function (targetValue, sourceValue, mergeKey) { // if not initial state, skip default merge method (ie. return value, see note below) if (!initialState && (0, _lodash.isEqual)(keyPath, self.getIndex(mergeKey))) { // use the sourceValue or fall back to targetValue return sourceValue === undefined ? targetValue : sourceValue; } }; // If `mergeMethod` is provided it is invoked to produce the merged values of the // destination and source properties. // If `mergeMethod` returns `undefined` the default merging method is used this._mergedState = (0, _lodash.mergeWith)(targetObj, sourceObj, mergeMethod); // verify that there are actually changes if ((0, _lodash.isEqual)(this._mergedState, this._defaultState)) this._changedState = {}; if (!silent && stateChanged) this.emit('change', key); return this; } } exports.PersistedState = PersistedState;