AutorÃa | Ultima modificación | Ver Log |
{"version":3,"file":"statemanager.min.js","sources":["../../../src/local/reactive/statemanager.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Reactive simple state manager.\n *\n * The state manager contains the state data, trigger update events and\n * can lock and unlock the state data.\n *\n * This file contains the three main element
s of the state manager:\n * - State manager: the public class to alter the state, dispatch events and process update messages.\n * - Proxy handler: a private class to keep track of the state object changes.\n * - StateMap class: a private class extending Map class that triggers event when a state list is modifed.\n *\n * @module core/local/reactive/statemanager\n * @class StateManager\n * @copyright 2021 Ferran Recio <ferran@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Logger from 'core/local/reactive/logger';\n\n/**\n * State manager class.\n *\n * This class handle the reactive state and ensure only valid mutations can modify the state.\n * It also provide methods to apply batch state update messages (see processUpdates function doc\n * for more details on update messages).\n *\n * Implementing a deep state manager is complex and will require many frontend resources. To keep\n * the state fast and simple, the state can ONLY store two kind of
data:\n * - Object with attributes\n * - Sets of objects with id attributes.\n *\n * This is an example of a valid state:\n *\n * {\n * course: {\n * name: 'course name',\n * shortname: 'courseshort',\n * sectionlist: [21, 34]\n * },\n * sections: [\n * {id: 21, name: 'Topic 1', visible: true},\n * {id: 34, name: 'Topic 2', visible: false,\n * ],\n * }\n *\n * The following cases are NOT allowed at a state ROOT level (throws an exception if they are assigned):\n * - Simple values (strings, boolean...).\n * - Arrays of simple values.\n * - Array of objects without ID attribute (all arrays will be converted to maps and requires an ID).\n *\n * Thanks to those limitations it can simplify the state update messages and the event names. If You\n * need to store simple data, just group them in an object.\n *\n * To grant any state change triggers the proper events, the class uses two private structures:\n * - proxy handler: any object stored in the state is proxied using this class
.\n * - StateMap class: any object set in the state will be converted to StateMap using the\n * objects id attribute.\n */\nexport default class StateManager {\n\n /**\n * Create a basic reactive state store.\n *\n * The state manager is meant to work with native JS events. To ensure each reactive module can use\n * it in its own way, the parent element must provide a valid event dispatcher function and an optional\n * DOM element to anchor the event.\n *\n * @param {function} dispatchEvent the function to dispatch the custom event when the state changes.\n * @param {element} target the state changed custom event target (document if none provided)\n */\n constructor(dispatchEvent, target) {\n\n // The dispatch event function.\n /** @package */\n this.dispatchEvent = dispatchEvent;\n\n // The DOM container to trigger events.\n /** @package */\n this.target = target ?? document;\n\n // State can be altered freely until
initial state is set.\n /** @package */\n this.readonly = false;\n\n // List of state changes pending to be published as events.\n /** @package */\n this.eventsToPublish = [];\n\n // The update state types functions.\n /** @package */\n this.updateTypes = {\n \"create\": this.defaultCreate.bind(this),\n \"update\": this.defaultUpdate.bind(this),\n \"delete\": this.defaultDelete.bind(this),\n \"put\": this.defaultPut.bind(this),\n \"override\": this.defaultOverride.bind(this),\n \"remove\": this.defaultRemove.bind(this),\n \"prepareFields\": this.defaultPrepareFields.bind(this),\n };\n\n // The state_loaded event is special because it only happens one but all components\n // may react to that state, even if they are registered after the setIinitialState.\n // For these reason we use a promise for that event.\n this.initialPromise = new Promise
((resolve) => {\n const initialStateDone = (event) => {\n resolve(event.detail.state);\n };\n this.target.addEventListener('state:loaded', initialStateDone);\n });\n\n this.logger = new Logger();\n }\n\n /**\n * Loads the initial state.\n *\n * Note this method will trigger a state changed event with \"state:loaded\" actionname.\n *\n * The state mode will be set to read only when the initial state is loaded.\n *\n * @param {object} initialState\n */\n setInitialState(initialState) {\n\n if (this.state !== undefined) {\n throw Error('Initial state can only be initialized ones');\n }\n\n // Create the state object.\n const state = new Proxy({}, new Handler('state', this, true));\n for (const [prop, propValue] of Object.entries(initialState)) {\n state[prop] = propValue;\n }\n this.state = state;\n\n // When the state is loaded we c
an lock it to prevent illegal changes.\n this.readonly = true;\n\n this.dispatchEvent({\n action: 'state:loaded',\n state: this.state,\n }, this.target);\n }\n\n /**\n * Generate a promise that will be resolved when the initial state is loaded.\n *\n * In most cases the final state will be loaded using an ajax call. This is the reason\n * why states manager are created unlocked and won't be reactive until the initial state is set.\n *\n * @return {Promise} the resulting promise\n */\n getInitialPromise() {\n return this.initialPromise;\n }\n\n /**\n * Locks or unlocks the state to prevent illegal updates.\n *\n * Mutations use this method to modify the state. Once the state is updated, they must\n * block again the state.\n *\n * All changes done while the state is writable will be registered using registerStateAction.\n * When the state is set again to read only the method will trigger _p
ublishEvents to communicate\n * changes to all watchers.\n *\n * @param {bool} readonly if the state is in read only mode enabled\n */\n setReadOnly(readonly) {\n\n this.readonly = readonly;\n\n let mode = 'off';\n\n // When the state is in readonly again is time to publish all pending events.\n if (this.readonly) {\n mode = 'on';\n this._publishEvents();\n }\n\n // Dispatch a read only event.\n this.dispatchEvent({\n action: `readmode:${mode}`,\n state: this.state,\n element: null,\n }, this.target);\n }\n\n /**\n * Add methods to process update state messages.\n *\n * The state manager provide a default update, create and delete methods. However,\n * some applications may require to override the default methods or even add new ones\n * like \"refresh\" or \"error\".\n *\n * @param {Object} newFunctions the new update types functions.\n */\n
addUpdateTypes(newFunctions) {\n for (const [updateType, updateFunction] of Object.entries(newFunctions)) {\n if (typeof updateFunction === 'function') {\n this.updateTypes[updateType] = updateFunction.bind(newFunctions);\n }\n }\n }\n\n /**\n * Process a state updates array and do all the necessary changes.\n *\n * Note this method unlocks the state while it is executing and relocks it\n * when finishes.\n *\n * @param {array} updates\n * @param {Object} updateTypes optional functions to override the default update types.\n */\n processUpdates(updates, updateTypes) {\n if (!Array.isArray(updates)) {\n throw Error('State updates must be an array');\n }\n this.setReadOnly(false);\n updates.forEach((update) => {\n if (update.name === undefined) {\n throw Error('Missing state update name');\n }\n this.processUpdate(\n
update.name,\n update.action,\n update.fields,\n updateTypes\n );\n });\n this.setReadOnly(true);\n }\n\n /**\n * Process a single state update.\n *\n * Note this method will not lock or unlock the state by itself.\n *\n * @param {string} updateName the state element to update\n * @param {string} action to action to perform\n * @param {object} fields the new data\n * @param {Object} updateTypes optional functions to override the default update types.\n */\n processUpdate(updateName, action, fields, updateTypes) {\n\n if (!fields) {\n throw Error('Missing state update fields');\n }\n\n if (updateTypes === undefined) {\n updateTypes = {};\n }\n\n action = action ?? 'update';\n\n const method = updateTypes[action] ?? this.updateTypes[action];\n\n if (method === undefined) {\n throw Error(`Unkown update action ${actio
n}`);\n }\n\n // Some state data may require some cooking before sending to the\n // state. Reactive instances can overrdide the default fieldDefaults\n // method to add extra logic to all updates.\n const prepareFields = updateTypes.prepareFields ?? this.updateTypes.prepareFields;\n\n method(this, updateName, prepareFields(this, updateName, fields));\n }\n\n /**\n * Prepare fields for processing.\n *\n * This method is used to add default values or calculations from the frontend side.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n * @returns {Object} final fields data\n */\n defaultPrepareFields(stateManager, updateName, fields) {\n return fields;\n }\n\n\n /**\n * Process a create state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the s
tate element to update\n * @param {Object} fields the new data\n */\n defaultCreate(stateManager, updateName, fields) {\n\n let state = stateManager.state;\n\n // Create can be applied only to lists, not to objects.\n if (state[updateName] instanceof StateMap) {\n state[updateName].add(fields);\n return;\n }\n state[updateName] = fields;\n }\n\n /**\n * Process a delete state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n */\n defaultDelete(stateManager, updateName, fields) {\n\n // Get the current value.\n let current = stateManager.get(updateName, fields.id);\n if (!current) {\n throw Error(`Inexistent ${updateName} ${fields.id}`);\n }\n\n // Process deletion.\n let state = stateManager.state;\n\n if (state[updateName] instanceof St
ateMap) {\n state[updateName].delete(fields.id);\n return;\n }\n delete state[updateName];\n }\n\n /**\n * Process a remove state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n */\n defaultRemove(stateManager, updateName, fields) {\n\n // Get the current value.\n let current = stateManager.get(updateName, fields.id);\n if (!current) {\n return;\n }\n\n // Process deletion.\n let state = stateManager.state;\n\n if (state[updateName] instanceof StateMap) {\n state[updateName].delete(fields.id);\n return;\n }\n delete state[updateName];\n }\n\n /**\n * Process a update state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {
Object} fields the new data\n */\n defaultUpdate(stateManager, updateName, fields) {\n\n // Get the current value.\n let current = stateManager.get(updateName, fields.id);\n if (!current) {\n throw Error(`Inexistent ${updateName} ${fields.id}`);\n }\n\n // Execute updates.\n for (const [fieldName, fieldValue] of Object.entries(fields)) {\n current[fieldName] = fieldValue;\n }\n }\n\n /**\n * Process a put state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n */\n defaultPut(stateManager, updateName, fields) {\n\n // Get the current value.\n let current = stateManager.get(updateName, fields.id);\n if (current) {\n // Update attributes.\n for (const [fieldName, fieldValue] of Object.entries(fields)) {\n current[fieldName] = field
Value;\n }\n } else {\n // Create new object.\n let state = stateManager.state;\n if (state[updateName] instanceof StateMap) {\n state[updateName].add(fields);\n return;\n }\n state[updateName] = fields;\n }\n }\n\n /**\n * Process an override state message.\n *\n * @param {Object} stateManager the state manager\n * @param {String} updateName the state element to update\n * @param {Object} fields the new data\n */\n defaultOverride(stateManager, updateName, fields) {\n\n // Get the current value.\n let current = stateManager.get(updateName, fields.id);\n if (current) {\n // Remove any unnecessary fields.\n for (const [fieldName] of Object.entries(current)) {\n if (fields[fieldName] === undefined) {\n delete current[fieldName];\n }\n }\n // Update field.\n
for (const [fieldName, fieldValue] of Object.entries(fields)) {\n current[fieldName] = fieldValue;\n }\n } else {\n // Create the element if not exists.\n let state = stateManager.state;\n if (state[updateName] instanceof StateMap) {\n state[updateName].add(fields);\n return;\n }\n state[updateName] = fields;\n }\n }\n\n /**\n * Set the logger class instance.\n *\n * Reactive instances can provide alternative loggers to provide advanced logging.\n * @param {Logger} logger\n */\n setLogger(logger) {\n this.logger = logger;\n }\n\n /**\n * Add a new log entry into the reactive logger.\n * @param {LoggerEntry} entry\n */\n addLoggerEntry(entry) {\n this.logger.add(entry);\n }\n\n /**\n * Get an element from the state or form an alternative state object.\n *\n * The altstate param is used by extern
al update functions that gets the current\n * state as param.\n *\n * @param {String} name the state object name\n * @param {*} id and object id for state maps.\n * @return {Object|undefined} the state object found\n */\n get(name, id) {\n const state = this.state;\n\n let current = state[name];\n if (current instanceof StateMap) {\n if (id === undefined) {\n throw Error(`Missing id for ${name} state update`);\n }\n current = state[name].get(id);\n }\n\n return current;\n }\n\n /**\n * Get all element ids from the given state.\n *\n * @param {String} name the state object name\n * @return {Array} the element ids.\n */\n getIds(name) {\n const state = this.state;\n const current = state[name];\n if (!(current instanceof StateMap)) {\n throw Error(`${name} is not an instance of StateMap`);\n }\n return [...state[name].keys()];\n
}\n\n /**\n * Register a state modification and generate the necessary events.\n *\n * This method is used mainly by proxy helpers to dispatch state change event.\n * However, mutations can use it to inform components about non reactive changes\n * in the state (only the two first levels of the state are reactive).\n *\n * Each action can produce several events:\n * - The specific attribute updated, created or deleter (example: \"cm.visible:updated\")\n * - The general state object updated, created or deleted (example: \"cm:updated\")\n * - If the element has an ID attribute, the specific event with id (example: \"cm[42].visible:updated\")\n * - If the element has an ID attribute, the general event with id (example: \"cm[42]:updated\")\n * - A generic state update event \"state:update\"\n *\n * @param {string} field the affected state field name\n * @param {string|null} prop the affecter field property (null if affect the full object)\n *
@param {string} action the action done (created/updated/deleted)\n * @param {*} data the affected data\n */\n registerStateAction(field, prop, action, data) {\n\n let parentAction = 'updated';\n\n if (prop !== null) {\n this.eventsToPublish.push({\n eventName: `${field}.${prop}:${action}`,\n eventData: data,\n action,\n });\n } else {\n parentAction = action;\n }\n\n // Trigger extra events if the element has an ID attribute.\n if (data.id !== undefined) {\n if (prop !== null) {\n this.eventsToPublish.push({\n eventName: `${field}[${data.id}].${prop}:${action}`,\n eventData: data,\n action,\n });\n }\n this.eventsToPublish.push({\n eventName: `${field}[${data.id}]:${parentAction}`,\n eventData: data,\n action: parent
Action,\n });\n }\n\n // Register the general change.\n this.eventsToPublish.push({\n eventName: `${field}:${parentAction}`,\n eventData: data,\n action: parentAction,\n });\n\n // Register state updated event.\n this.eventsToPublish.push({\n eventName: `state:updated`,\n eventData: data,\n action: 'updated',\n });\n }\n\n /**\n * Internal method to publish events.\n *\n * This is a private method, it will be invoked when the state is set back to read only mode.\n */\n _publishEvents() {\n const fieldChanges = this.eventsToPublish;\n this.eventsToPublish = [];\n\n // Dispatch a transaction start event.\n this.dispatchEvent({\n action: 'transaction:start',\n state: this.state,\n element: null,\n changes: fieldChanges,\n }, this.target);\n\n // State changes can be registered i
n any order. However it will avoid many\n // components errors if they are sorted to have creations-updates-deletes in case\n // some component needs to create or destroy DOM elements before updating them.\n fieldChanges.sort((a, b) => {\n const weights = {\n created: 0,\n updated: 1,\n deleted: 2,\n };\n const aweight = weights[a.action] ?? 0;\n const bweight = weights[b.action] ?? 0;\n // In case both have the same weight, the eventName length decide.\n if (aweight === bweight) {\n return a.eventName.length - b.eventName.length;\n }\n return aweight - bweight;\n });\n\n // List of the published events to prevent redundancies.\n let publishedEvents = new Set();\n\n fieldChanges.forEach((event) => {\n\n const eventkey = `${event.eventName}.${event.eventData.id ?? 0}`;\n\n if (!publishedEvents.
has(eventkey)) {\n this.dispatchEvent({\n action: event.eventName,\n state: this.state,\n element: event.eventData\n }, this.target);\n\n publishedEvents.add(eventkey);\n }\n });\n\n // Dispatch a transaction end event.\n this.dispatchEvent({\n action: 'transaction:end',\n state: this.state,\n element: null,\n }, this.target);\n }\n}\n\n// Proxy helpers.\n\n/**\n * The proxy handler.\n *\n * This class will inform any value change directly to the state manager.\n *\n * The proxied variable will throw an error if it is altered when the state manager is\n * in read only mode.\n */\nclass Handler {\n\n /**\n * Class constructor.\n *\n * @param {string} name the variable name used for identify triggered actions\n * @param {StateManager} stateManager the state manager object\n * @param {boolean} proxyValues if new va
lues must be proxied (used only at state root level)\n */\n constructor(name, stateManager, proxyValues) {\n this.name = name;\n this.stateManager = stateManager;\n this.proxyValues = proxyValues ?? false;\n }\n\n /**\n * Set trap to trigger events when the state changes.\n *\n * @param {object} obj the source object (not proxied)\n * @param {string} prop the attribute to set\n * @param {*} value the value to save\n * @param {*} receiver the proxied element to be attached to events\n * @returns {boolean} if the value is set\n */\n set(obj, prop, value, receiver) {\n\n // Only mutations should be able to set state values.\n if (this.stateManager.readonly) {\n throw new Error(`State locked. Use mutations to change ${prop} value in ${this.name}.`);\n }\n\n // Check any data change.\n if (JSON.stringify(obj[prop]) === JSON.stringify(value)) {\n return true;\n }\n\n const act
ion = (obj[prop] !== undefined) ? 'updated' : 'created';\n\n // Proxy value if necessary (used at state root level).\n if (this.proxyValues) {\n if (Array.isArray(value)) {\n obj[prop] = new StateMap(prop, this.stateManager).loadValues(value);\n } else {\n obj[prop] = new Proxy(value, new Handler(prop, this.stateManager));\n }\n } else {\n obj[prop] = value;\n }\n\n // If the state is not ready yet means the initial state is not yet loaded.\n if (this.stateManager.state === undefined) {\n return true;\n }\n\n this.stateManager.registerStateAction(this.name, prop, action, receiver);\n\n return true;\n }\n\n /**\n * Delete property trap to trigger state change events.\n *\n * @param {*} obj the affected object (not proxied)\n * @param {*} prop the prop to delete\n * @returns {boolean} if prop is deleted\n */\n deleteProperty(obj, pr
op) {\n // Only mutations should be able to set state values.\n if (this.stateManager.readonly) {\n throw new Error(`State locked. Use mutations to delete ${prop} in ${this.name}.`);\n }\n if (prop in obj) {\n\n delete obj[prop];\n\n this.stateManager.registerStateAction(this.name, prop, 'deleted', obj);\n }\n return true;\n }\n}\n\n/**\n * Class to add events dispatching to the JS Map class.\n *\n * When the state has a list of objects (with IDs) it will be converted into a StateMap.\n * StateMap is used almost in the same way as a regular JS map. Because all elements have an\n * id attribute, it has some specific methods:\n * - add: a convenient method to add an element without specifying the key (\"id\" attribute will be used as a key).\n * - loadValues: to add many elements at once wihout specifying keys (\"id\" attribute will be used).\n *\n * Apart, the main difference between regular Map and MapState is that this one will
inform any change to the\n * state manager.\n */\nclass StateMap extends Map {\n\n /**\n * Create a reactive Map.\n *\n * @param {string} name the property name\n * @param {StateManager} stateManager the state manager\n * @param {iterable} iterable an iterable object to create the Map\n */\n constructor(name, stateManager, iterable) {\n // We don't have any \"this\" until be call super.\n super(iterable);\n this.name = name;\n this.stateManager = stateManager;\n }\n\n /**\n * Set an element into the map.\n *\n * Each value needs it's own id attribute. Objects without id will be rejected.\n * The function will throw an error if the value id and the key are not the same.\n *\n * @param {*} key the key to store\n * @param {*} value the value to store\n * @returns {Map} the resulting Map object\n */\n set(key, value) {\n\n // Only mutations should be able to set state values.\n if (this.stateManag
er.readonly) {\n throw new Error(`State locked. Use mutations to change ${key} value in ${this.name}.`);\n }\n\n // Normalize keys as string to prevent json decoding errors.\n key = this.normalizeKey(key);\n\n this.checkValue(value);\n\n if (key === undefined || key === null) {\n throw Error('State lists keys cannot be null or undefined');\n }\n\n // ID is mandatory and should be the same as the key.\n if (this.normalizeKey(value.id) !== key) {\n throw new Error(`State error: ${this.name} list element ID (${value.id}) and key (${key}) mismatch`);\n }\n\n const action = (super.has(key)) ? 'updated' : 'created';\n\n // Save proxied data into the list.\n const result = super.set(key, new Proxy(value, new Handler(this.name, this.stateManager)));\n\n // If the state is not ready yet means the initial state is not yet loaded.\n if (this.stateManager.state === undefined) {\n
return result;\n }\n\n this.stateManager.registerStateAction(this.name, null, action, super.get(key));\n\n return result;\n }\n\n /**\n * Check if a value is valid to be stored in a a State List.\n *\n * Only objects with id attribute can be stored in State lists.\n *\n * This method throws an error if the value is not valid.\n *\n * @param {object} value (with ID)\n */\n checkValue(value) {\n if (!typeof value === 'object' && value !== null) {\n throw Error('State lists can contain objects only');\n }\n\n if (value.id === undefined) {\n throw Error('State lists elements must contain at least an id attribute');\n }\n }\n\n /**\n * Return a normalized key value for state map.\n *\n * Regular maps uses strict key comparissons but state maps are indexed by ID.JSON conversions\n * and webservices sometimes do unexpected types conversions so we convert any integer key to string.\n
*\n * @param {*} key the provided key\n * @returns {string}\n */\n normalizeKey(key) {\n return String(key).valueOf();\n }\n\n /**\n * Insert a new element int a list.\n *\n * Each value needs it's own id attribute. Objects withouts id will be rejected.\n *\n * @param {object} value the value to add (needs an id attribute)\n * @returns {Map} the resulting Map object\n */\n add(value) {\n this.checkValue(value);\n return this.set(value.id, value);\n }\n\n /**\n * Return a state map element.\n *\n * @param {*} key the element id\n * @return {Object}\n */\n get(key) {\n return super.get(this.normalizeKey(key));\n }\n\n /**\n * Check whether an element with the specified key exists or not.\n *\n * @param {*} key the key to find\n * @return {boolean}\n */\n has(key) {\n return super.has(this.normalizeKey(key));\n }\n\n /**\n * Delete an element from the map
.\n *\n * @param {*} key\n * @returns {boolean}\n */\n delete(key) {\n // State maps uses only string keys to avoid strict comparisons.\n key = this.normalizeKey(key);\n\n // Only mutations should be able to set state values.\n if (this.stateManager.readonly) {\n throw new Error(`State locked. Use mutations to change ${key} value in ${this.name}.`);\n }\n\n const previous = super.get(key);\n\n const result = super.delete(key);\n if (!result) {\n return result;\n }\n\n this.stateManager.registerStateAction(this.name, null, 'deleted', previous);\n\n return result;\n }\n\n /**\n * Return a suitable structure for JSON conversion.\n *\n * This function is needed because new values are compared in JSON. StateMap has Private\n * attributes which cannot be stringified (like this.stateManager which will produce an\n * infinite recursivity).\n *\n * @returns {array}\n
*/\n toJSON() {\n let result = [];\n this.forEach((value) => {\n result.push(value);\n });\n return result;\n }\n\n /**\n * Insert a full list of values using the id attributes as keys.\n *\n * This method is used mainly to initialize the list. Note each element is indexed by its \"id\" attribute.\n * This is a basic restriction of StateMap. All elements need an id attribute, otherwise it won't be saved.\n *\n * @param {iterable} values the values to load\n * @returns {StateMap} return the this value\n */\n loadValues(values) {\n values.forEach((data) => {\n this.checkValue(data);\n let key = data.id;\n let newvalue = new Proxy(data, new Handler(this.name, this.stateManager));\n this.set(key, newvalue);\n });\n return this;\n }\n}\n"],"names":["constructor","dispatchEvent","target","document","readonly","eventsToPublish","updateTypes","this","defaultCreate
","bind","defaultUpdate","defaultDelete","defaultPut","defaultOverride","defaultRemove","defaultPrepareFields","initialPromise","Promise","resolve","addEventListener","event","detail","state","logger","Logger","setInitialState","initialState","undefined","Error","Proxy","Handler","prop","propValue","Object","entries","action","getInitialPromise","setReadOnly","mode","_publishEvents","element","addUpdateTypes","newFunctions","updateType","updateFunction","processUpdates","updates","Array","isArray","forEach","update","name","processUpdate","fields","updateName","method","prepareFields","stateManager","StateMap","add","get","id","delete","current","fieldName","fieldValue","setLogger","addLoggerEntry","entry","getIds","keys","registerStateAction","field","data","parentAction","push","eventName","eventData","fieldChanges","changes","sort","a","b","weights","created","updated","deleted","aweight","bweight","length","publishedEvents","Set","eventkey","has","proxyValues","set","obj","value","receiver","JSON","strin
gify","loadValues","deleteProperty","Map","iterable","key","normalizeKey","checkValue","super","result","String","valueOf","previous","toJSON","values","newvalue"],"mappings":"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"}