{"version":3,"file":"CacheItem.mjs","names":["status: TemporaryRetainStatus","clearTemporaryRetainByCallack: CleanupFn"],"sources":["../src/CacheItem.ts"],"sourcesContent":["import type {\n  CleanupFn,\n  Factory,\n  ItemCleanupPair,\n} from '@isograph/disposable-types';\n\nconst DEFAULT_TEMPORARY_RETAIN_TIME = 5000;\n\nexport type NotInParentCacheAndDisposed = {\n  kind: 'NotInParentCacheAndDisposed';\n};\nexport type NotInParentCacheAndNotDisposed<T> = {\n  kind: 'NotInParentCacheAndNotDisposed';\n  value: T;\n  disposeValue: () => void;\n\n  // Invariant: >0\n  permanentRetainCount: number;\n};\nexport type InParentCacheAndNotDisposed<T> = {\n  kind: 'InParentCacheAndNotDisposed';\n  value: T;\n  disposeValue: () => void;\n  removeFromParentCache: () => void;\n\n  // Invariant: >0\n  temporaryRetainCount: number;\n\n  // Invariant: >= 0\n  permanentRetainCount: number;\n};\n\nexport type CacheItemState<T> =\n  | InParentCacheAndNotDisposed<T>\n  | NotInParentCacheAndNotDisposed<T>\n  | NotInParentCacheAndDisposed;\n\nexport type CacheItemOptions = {\n  temporaryRetainTime: number;\n};\n\n// TODO don't export this class, only export type (interface) instead\n// TODO convert cacheitem impl to a getter and setter and free functions\n\n/**\n * CacheItem:\n *\n * Terminology:\n * - TRC = Temporary Retain Count\n * - PRC = Permanent Retain Count\n *\n * A CacheItem<T> can be in three states:\n *   In parent cache? | Item disposed? | TRC | PRC | Name\n *   -----------------+----------------+-----+-----+-------------------------------\n *   In parent cache  | Not disposed   | >0  | >=0 | InParentCacheAndNotDisposed\n *   Removed          | Not disposed   |  0  |  >0 | NotInParentCacheAndNotDisposed\n *   Removed          | Disposed       |  0  |   0 | NotInParentCacheAndNotDisposed\n *\n * A cache item can only move down rows. As in, if its in the parent cache,\n * it can be removed. It can never be replaced in the parent cache. (If a\n * parent cache becomes full again, it will contain a new CacheItem.) The\n * contained item can be disposed, but never un-disposed.\n *\n * So, the valid transitions are:\n * - InParentCacheAndNotDisposed => NotInParentCacheAndNotDisposed\n * - InParentCacheAndNotDisposed => NotInParentCacheAndDisposed\n * - NotInParentCacheAndNotDisposed => NotInParentCacheAndDisposed\n */\nexport class CacheItem<T> {\n  private __state: CacheItemState<T>;\n  private __options: CacheItemOptions | null;\n\n  // Private. Do not call this constructor directly. Use\n  // createTemporarilyRetainedCacheItem instead. This is because this\n  // constructor creates a CacheItem in an invalid state. It must be\n  // temporarily retained to enter a valid state, and JavaScript doesn't\n  // let you return a tuple from a constructor.\n  constructor(\n    factory: Factory<T>,\n    removeFromParentCache: CleanupFn,\n    options: CacheItemOptions | void,\n  ) {\n    this.__options = options ?? null;\n    const [value, disposeValue] = factory();\n    this.__state = {\n      kind: 'InParentCacheAndNotDisposed',\n      value,\n      disposeValue,\n      removeFromParentCache,\n      // NOTE: we are creating the CacheItem in an invalid state. This is okay, because\n      // we are immediately calling .temporaryRetain.\n      temporaryRetainCount: 0,\n      permanentRetainCount: 0,\n    };\n  }\n\n  getValue(): T {\n    switch (this.__state.kind) {\n      case 'InParentCacheAndNotDisposed': {\n        return this.__state.value;\n      }\n      case 'NotInParentCacheAndNotDisposed': {\n        return this.__state.value;\n      }\n      case 'NotInParentCacheAndDisposed': {\n        throw new Error(\n          'Attempted to access disposed value from CacheItem. ' +\n            'This indicates a bug in react-disposable-state.',\n        );\n      }\n    }\n  }\n\n  permanentRetainIfNotDisposed(\n    disposeOfTemporaryRetain: CleanupFn,\n  ): ItemCleanupPair<T> | null {\n    switch (this.__state.kind) {\n      case 'InParentCacheAndNotDisposed': {\n        let cleared = false;\n        this.__state.permanentRetainCount++;\n        disposeOfTemporaryRetain();\n        return [\n          this.__state.value,\n          () => {\n            if (cleared) {\n              throw new Error(\n                'A permanent retain should only be cleared once. ' +\n                  'This indicates a bug in react-disposable-state.',\n              );\n            }\n            cleared = true;\n            switch (this.__state.kind) {\n              case 'InParentCacheAndNotDisposed': {\n                this.__state.permanentRetainCount--;\n                this.__maybeExitInParentCacheAndNotDisposedState(this.__state);\n                return;\n              }\n              case 'NotInParentCacheAndNotDisposed': {\n                this.__state.permanentRetainCount--;\n                this.__maybeExitNotInParentCacheAndNotDisposedState(\n                  this.__state,\n                );\n                return;\n              }\n              case 'NotInParentCacheAndDisposed': {\n                throw new Error(\n                  'CacheItem was in a disposed state, but there existed a permanent retain. ' +\n                    'This indicates a bug in react-disposable-state.',\n                );\n              }\n            }\n          },\n        ];\n      }\n      case 'NotInParentCacheAndNotDisposed': {\n        let cleared = false;\n        this.__state.permanentRetainCount++;\n        disposeOfTemporaryRetain();\n        return [\n          this.__state.value,\n          () => {\n            if (cleared) {\n              throw new Error(\n                'A permanent retain should only be cleared once. ' +\n                  'This indicates a bug in react-disposable-state.',\n              );\n            }\n            cleared = true;\n            switch (this.__state.kind) {\n              case 'NotInParentCacheAndNotDisposed': {\n                this.__state.permanentRetainCount--;\n                this.__maybeExitNotInParentCacheAndNotDisposedState(\n                  this.__state,\n                );\n                return;\n              }\n              case 'InParentCacheAndNotDisposed':\n              case 'NotInParentCacheAndDisposed': {\n                throw new Error(\n                  'CacheItem was in an unexpected state. ' +\n                    'This indicates a bug in react-disposable-state.',\n                );\n              }\n            }\n          },\n        ];\n      }\n      case 'NotInParentCacheAndDisposed': {\n        // The CacheItem is disposed, so disposeOfTemporaryRetain is a no-op\n        return null;\n      }\n    }\n  }\n\n  temporaryRetain(): CleanupFn {\n    type TemporaryRetainStatus =\n      | 'Uncleared'\n      | 'ClearedByCallback'\n      | 'ClearedByTimeout';\n\n    switch (this.__state.kind) {\n      case 'InParentCacheAndNotDisposed': {\n        let status: TemporaryRetainStatus = 'Uncleared';\n        this.__state.temporaryRetainCount++;\n        const clearTemporaryRetainByCallack: CleanupFn = () => {\n          if (status === 'ClearedByCallback') {\n            throw new Error(\n              'A temporary retain should only be cleared once. ' +\n                'This indicates a bug in react-disposable-state.',\n            );\n          } else if (status === 'Uncleared') {\n            switch (this.__state.kind) {\n              case 'InParentCacheAndNotDisposed': {\n                this.__state.temporaryRetainCount--;\n                this.__maybeExitInParentCacheAndNotDisposedState(this.__state);\n                clearTimeout(timeoutId);\n                return;\n              }\n              case 'NotInParentCacheAndDisposed':\n              case 'NotInParentCacheAndNotDisposed': {\n                throw new Error(\n                  'A temporary retain was cleared, for which the CacheItem is in an invalid state. ' +\n                    'This indicates a bug in react-disposable-state.',\n                );\n              }\n            }\n          }\n        };\n\n        const clearTemporaryRetainByTimeout = () => {\n          status = 'ClearedByTimeout';\n          switch (this.__state.kind) {\n            case 'InParentCacheAndNotDisposed': {\n              this.__state.temporaryRetainCount--;\n              this.__maybeExitInParentCacheAndNotDisposedState(this.__state);\n              return;\n            }\n            case 'NotInParentCacheAndDisposed':\n            case 'NotInParentCacheAndNotDisposed': {\n              throw new Error(\n                'A temporary retain was cleared, for which the CacheItem is in an invalid state. ' +\n                  'This indicates a bug in react-disposable-state.',\n              );\n            }\n          }\n        };\n\n        const timeoutId = setTimeout(\n          clearTemporaryRetainByTimeout,\n          this.__options?.temporaryRetainTime ?? DEFAULT_TEMPORARY_RETAIN_TIME,\n        );\n        return clearTemporaryRetainByCallack;\n      }\n      case 'NotInParentCacheAndDisposed':\n      case 'NotInParentCacheAndNotDisposed': {\n        throw new Error(\n          'temporaryRetain was called, for which the CacheItem is in an invalid state. ' +\n            'This indicates a bug in react-disposable-state.',\n        );\n      }\n    }\n  }\n\n  permanentRetain(): CleanupFn {\n    switch (this.__state.kind) {\n      case 'InParentCacheAndNotDisposed': {\n        let cleared = false;\n        this.__state.permanentRetainCount++;\n        return () => {\n          if (cleared) {\n            throw new Error(\n              'A permanent retain should only be cleared once. ' +\n                'This indicates a bug in react-disposable-state.',\n            );\n          }\n          cleared = true;\n          switch (this.__state.kind) {\n            case 'InParentCacheAndNotDisposed': {\n              this.__state.permanentRetainCount--;\n              this.__maybeExitInParentCacheAndNotDisposedState(this.__state);\n              return;\n            }\n            case 'NotInParentCacheAndNotDisposed': {\n              this.__state.permanentRetainCount--;\n              this.__maybeExitNotInParentCacheAndNotDisposedState(this.__state);\n              return;\n            }\n            case 'NotInParentCacheAndDisposed': {\n              throw new Error(\n                'CacheItem was in a disposed state, but there existed a permanent retain. ' +\n                  'This indicates a bug in react-disposable-state.',\n              );\n            }\n          }\n        };\n      }\n      case 'NotInParentCacheAndNotDisposed': {\n        let cleared = false;\n        this.__state.permanentRetainCount++;\n        return () => {\n          if (cleared) {\n            throw new Error(\n              'A permanent retain should only be cleared once. ' +\n                'This indicates a bug in react-disposable-state.',\n            );\n          }\n          cleared = true;\n          switch (this.__state.kind) {\n            case 'NotInParentCacheAndNotDisposed': {\n              this.__state.permanentRetainCount--;\n              this.__maybeExitNotInParentCacheAndNotDisposedState(this.__state);\n              return;\n            }\n            case 'InParentCacheAndNotDisposed':\n            case 'NotInParentCacheAndDisposed': {\n              throw new Error(\n                'CacheItem was in an unexpected state. ' +\n                  'This indicates a bug in react-disposable-state.',\n              );\n            }\n          }\n        };\n      }\n      case 'NotInParentCacheAndDisposed': {\n        throw new Error(\n          'permanentRetain was called, but the CacheItem is in an invalid state. ' +\n            'This indicates a bug in react-disposable-state.',\n        );\n      }\n    }\n  }\n\n  private __maybeExitInParentCacheAndNotDisposedState(\n    state: InParentCacheAndNotDisposed<T>,\n  ) {\n    if (state.temporaryRetainCount === 0 && state.permanentRetainCount === 0) {\n      state.removeFromParentCache();\n      state.disposeValue();\n      this.__state = {\n        kind: 'NotInParentCacheAndDisposed',\n      };\n    } else if (state.temporaryRetainCount === 0) {\n      state.removeFromParentCache();\n      this.__state = {\n        kind: 'NotInParentCacheAndNotDisposed',\n        value: state.value,\n        disposeValue: state.disposeValue,\n        permanentRetainCount: state.permanentRetainCount,\n      };\n    }\n  }\n\n  private __maybeExitNotInParentCacheAndNotDisposedState(\n    state: NotInParentCacheAndNotDisposed<T>,\n  ) {\n    if (state.permanentRetainCount === 0) {\n      state.disposeValue();\n      this.__state = {\n        kind: 'NotInParentCacheAndDisposed',\n      };\n    }\n  }\n}\n\nexport function createTemporarilyRetainedCacheItem<T>(\n  factory: Factory<T>,\n  removeFromParentCache: CleanupFn,\n  options: CacheItemOptions | void,\n): [CacheItem<T>, CleanupFn] {\n  const cacheItem = new CacheItem(factory, removeFromParentCache, options);\n  const disposeTemporaryRetain = cacheItem.temporaryRetain();\n  return [cacheItem, disposeTemporaryRetain];\n}\n"],"mappings":"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"}