{"version":3,"file":"y-keyvalue.cjs","sources":["../y-keyvalue.js"],"sourcesContent":["import * as Y from 'yjs' // eslint-disable-line\nimport { Observable } from 'lib0/observable'\n\n/**\n * @template T\n * @extends Observable<'change'>\n *\n * Yjs maps don't perform well when there are a lot of entries that are frequently updated in alternating order.\n * This is a more efficient key-value store that allows frequently updating alternating entries.\n *\n * Note that this implementation does not support any nested Yjs types.\n *\n * Rough concept: We store { key, val } pairs in an Y.Array. When you write a new entry,\n * we append the { key, val } pair to the yarray and remove all existing entries with the same key.\n * Items that are more to the right have precedence.\n *\n * This type fires events similarly to Y.Map\n *\n * ```js\n * yefficientmap.on('change', changes => {\n *   changes // => Map<string, { action: 'delete', oldValue: T } | { action: 'update', oldValue: T, newValue: T } | { action: 'add', newValue: T }>\n * })\n * ```\n */\nexport class YKeyValue extends Observable {\n  /**\n   * @param {Y.Array<{ key: string, val: T }>} yarray\n   */\n  constructor (yarray) {\n    super()\n    this.yarray = yarray\n    this.doc = /** @type {Y.Doc} */ (yarray.doc)\n    /**\n     * Store the key-val pair so we can do identity-based comparisons.\n     *\n     * @type {Map<string, { key: string, val: T }>}\n     */\n    this.map = new Map()\n    {\n      // initialize the map and cleanup duplicate items\n      const arr = yarray.toArray()\n      this.doc.transact(() => {\n        for (let i = arr.length - 1; i >= 0; i--) {\n          const v = arr[i]\n          if (this.map.has(v.key)) {\n            // entry already exists, remove the current item\n            yarray.delete(i)\n          } else {\n            this.map.set(v.key, v)\n          }\n        }\n      })\n    }\n    // track when new items are added/removed and update this.map\n    yarray.observe((event, tr) => {\n      /**\n       * This is the change-event we calculate. It works similarly to Y.MapEvent.keys (I added support for newValue)\n       *\n       * https://docs.yjs.dev/api/shared-types/y.map\n       *\n       * @type {Map<string, { action: 'delete', oldValue: T } | { action: 'update', oldValue: T, newValue: T } | { action: 'add', newValue: T }>}\n       */\n      const changes = new Map()\n      /**\n       * @type {Array<Y.Item>}\n       */\n      const addedItems = Array.from(event.changes.added)\n      event.changes.deleted.forEach(ditem => {\n        ditem.content.getContent().forEach(c => {\n          // deleted item was the current value\n          if (this.map.get(c.key) === c) {\n            this.map.delete(c.key)\n            changes.set(c.key, { action: 'delete', oldValue: c.val })\n          }\n        })\n      })\n      /**\n       * @type {Map<string, { key: string, val: T }>}\n       */\n      const addedVals = new Map()\n      addedItems.map(item => item.content.getContent()).flat().forEach(v => {\n        addedVals.set(v.key, v)\n      })\n\n      /**\n       * A set of ids to remove.\n       *\n       * If an item was added, but it doesn't overwrite something\n       * (because an older item is more to the right),\n       * we will remove it in the cleanup process.\n       *\n       * We don't know the index, so we have to cache it here.\n       *\n       * @type {Set<string>}\n       */\n      const itemsToRemove = new Set()\n      const vals = yarray.toArray()\n      this.doc.transact(_tr => {\n        /**\n         * Iterate from right to left and update the map while we find the items in addedVals\n         */\n        for (let i = vals.length - 1; i >= 0 && (addedVals.size > 0 || itemsToRemove.size > 0); i--) {\n          const currVal = vals[i]\n          if (itemsToRemove.has(currVal.key)) {\n            itemsToRemove.delete(currVal.key)\n            yarray.delete(i, 1)\n          } else if (addedVals.get(currVal.key) === currVal) {\n            // a new item was inserted that is the latest value\n            const prevValue = this.map.get(currVal.key)\n            if (prevValue) {\n              // There was an entry that existed before.\n              // We just have to delete the previous item\n              itemsToRemove.add(currVal.key)\n              // and fire an \"update\" event\n              changes.set(currVal.key, { action: 'update', oldValue: prevValue.val, newValue: currVal.val })\n            } else {\n              // if the item was properly updated, there should already be a 'delete' event\n              const delEvent = changes.get(currVal.key)\n              if (delEvent && delEvent.action === 'delete') {\n                changes.set(currVal.key, { action: 'update', newValue: currVal.val, oldValue: delEvent.oldValue })\n              } else {\n                // fire an \"add\" event\n                changes.set(currVal.key, { action: 'add', newValue: currVal.val })\n              }\n            }\n            addedVals.delete(currVal.key)\n            this.map.set(currVal.key, currVal)\n          } else if (addedVals.has(currVal.key)) {\n            // The entry didn't change, we have to remove the added value\n            itemsToRemove.add(currVal.key)\n            addedVals.delete(currVal.key)\n          }\n        }\n      })\n      if (changes.size > 0) {\n        this.emit('change', [changes, tr])\n      }\n    })\n  }\n\n  /**\n   * @param {string} key\n   * @param {T} val\n   */\n  set (key, val) {\n    this.doc.transact(_tr => {\n      // if this value existed before, we will delete it first\n      if (this.map.has(key)) {\n        this.delete(key)\n      }\n      this.yarray.push([{ key, val }])\n    })\n  }\n\n  /**\n   * @param {string} key\n   */\n  delete (key) {\n    let i = 0\n    // eslint-disable-next-line no-unused-vars\n    for (const val of this.yarray) {\n      if (val.key === key) {\n        this.yarray.delete(i)\n        break\n      }\n      i++\n    }\n  }\n\n  /**\n   * @param {string} key\n   * @return {T | undefined}\n   */\n  get (key) {\n    const v = this.map.get(key)\n    return v && v.val\n  }\n\n  /**\n   * @param {string} key\n   */\n  has (key) {\n    return this.map.has(key)\n  }\n}\n"],"names":["Observable"],"mappings":"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"}