limited-cache 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/index.cjs +273 -380
  3. package/dist/cjs/index.d.cts +91 -76
  4. package/dist/cjs/index.d.cts.map +1 -0
  5. package/dist/esm/index.d.ts +106 -6
  6. package/dist/esm/index.d.ts.map +1 -1
  7. package/dist/esm/index.js +279 -6
  8. package/dist/esm/index.js.map +1 -1
  9. package/package.json +34 -53
  10. package/src/__tests__/LimitedCache.test.ts +2 -2
  11. package/src/__tests__/LimitedCacheObject.test.ts +30 -27
  12. package/src/__tests__/lowLevelFunctions.test.ts +10 -8
  13. package/src/__tests__/scenarios/keys.test.ts +2 -2
  14. package/src/__tests__/scenarios/maxCacheSize.test.ts +8 -5
  15. package/src/__tests__/scenarios/maxCacheTime.test.ts +58 -58
  16. package/src/__tests__/scenarios/values.test.ts +2 -2
  17. package/src/__tests__/typeChecks/LimitedCacheObjectTypes.test.ts +15 -15
  18. package/src/__tests__/typeChecks/LimitedCacheTypes.test.ts +1 -1
  19. package/src/__tests__/typeChecks/lowLevelFunctionsTypes.test.ts +2 -2
  20. package/src/core/LimitedCache.ts +20 -21
  21. package/src/core/LimitedCacheObject.ts +30 -19
  22. package/src/core/defaultOptions.ts +5 -4
  23. package/src/core/limitedCacheUtil.ts +15 -3
  24. package/src/core/lowLevelFunctions.ts +41 -32
  25. package/src/types.ts +16 -3
  26. package/dist/esm/core/LimitedCache.d.ts +0 -4
  27. package/dist/esm/core/LimitedCache.d.ts.map +0 -1
  28. package/dist/esm/core/LimitedCache.js +0 -29
  29. package/dist/esm/core/LimitedCache.js.map +0 -1
  30. package/dist/esm/core/LimitedCacheObject.d.ts +0 -5
  31. package/dist/esm/core/LimitedCacheObject.d.ts.map +0 -1
  32. package/dist/esm/core/LimitedCacheObject.js +0 -52
  33. package/dist/esm/core/LimitedCacheObject.js.map +0 -1
  34. package/dist/esm/core/builtIns.d.ts +0 -12
  35. package/dist/esm/core/builtIns.d.ts.map +0 -1
  36. package/dist/esm/core/builtIns.js +0 -5
  37. package/dist/esm/core/builtIns.js.map +0 -1
  38. package/dist/esm/core/defaultOptions.d.ts +0 -6
  39. package/dist/esm/core/defaultOptions.d.ts.map +0 -1
  40. package/dist/esm/core/defaultOptions.js +0 -14
  41. package/dist/esm/core/defaultOptions.js.map +0 -1
  42. package/dist/esm/core/limitedCacheUtil.d.ts +0 -13
  43. package/dist/esm/core/limitedCacheUtil.d.ts.map +0 -1
  44. package/dist/esm/core/limitedCacheUtil.js +0 -14
  45. package/dist/esm/core/limitedCacheUtil.js.map +0 -1
  46. package/dist/esm/core/lowLevelFunctions.d.ts +0 -14
  47. package/dist/esm/core/lowLevelFunctions.d.ts.map +0 -1
  48. package/dist/esm/core/lowLevelFunctions.js +0 -240
  49. package/dist/esm/core/lowLevelFunctions.js.map +0 -1
  50. package/dist/esm/types.d.ts +0 -58
  51. package/dist/esm/types.d.ts.map +0 -1
  52. package/dist/esm/types.js +0 -2
  53. package/dist/esm/types.js.map +0 -1
  54. package/legacy-types/ts3.5/dist/esm/core/LimitedCache.d.ts +0 -4
  55. package/legacy-types/ts3.5/dist/esm/core/LimitedCacheObject.d.ts +0 -5
  56. package/legacy-types/ts3.5/dist/esm/core/builtIns.d.ts +0 -12
  57. package/legacy-types/ts3.5/dist/esm/core/defaultOptions.d.ts +0 -6
  58. package/legacy-types/ts3.5/dist/esm/core/limitedCacheUtil.d.ts +0 -13
  59. package/legacy-types/ts3.5/dist/esm/core/lowLevelFunctions.d.ts +0 -14
  60. package/legacy-types/ts3.5/dist/esm/index.d.ts +0 -7
  61. package/legacy-types/ts3.5/dist/esm/types.d.ts +0 -61
  62. package/legacy-types/ts4.0/dist/esm/core/LimitedCache.d.ts +0 -4
  63. package/legacy-types/ts4.0/dist/esm/core/LimitedCacheObject.d.ts +0 -5
  64. package/legacy-types/ts4.0/dist/esm/core/builtIns.d.ts +0 -12
  65. package/legacy-types/ts4.0/dist/esm/core/defaultOptions.d.ts +0 -6
  66. package/legacy-types/ts4.0/dist/esm/core/limitedCacheUtil.d.ts +0 -13
  67. package/legacy-types/ts4.0/dist/esm/core/lowLevelFunctions.d.ts +0 -14
  68. package/legacy-types/ts4.0/dist/esm/index.d.ts +0 -7
  69. package/legacy-types/ts4.0/dist/esm/types.d.ts +0 -61
  70. package/legacy-types/ts4.5/dist/esm/core/LimitedCache.d.ts +0 -4
  71. package/legacy-types/ts4.5/dist/esm/core/LimitedCacheObject.d.ts +0 -5
  72. package/legacy-types/ts4.5/dist/esm/core/builtIns.d.ts +0 -12
  73. package/legacy-types/ts4.5/dist/esm/core/defaultOptions.d.ts +0 -6
  74. package/legacy-types/ts4.5/dist/esm/core/limitedCacheUtil.d.ts +0 -13
  75. package/legacy-types/ts4.5/dist/esm/core/lowLevelFunctions.d.ts +0 -14
  76. package/legacy-types/ts4.5/dist/esm/index.d.ts +0 -7
  77. package/legacy-types/ts4.5/dist/esm/types.d.ts +0 -61
  78. package/src/core/builtIns.ts +0 -9
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAE3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/core/defaultOptions.ts","../../src/core/lowLevelFunctions.ts","../../src/core/LimitedCache.ts","../../src/core/LimitedCacheObject.ts","../../src/core/limitedCacheUtil.ts"],"sourcesContent":["import type { LimitedCacheOptionsReadonly } from '../types.js';\n\nconst CURRENT_META_VERSION = 2 as const;\n// Default = 1 year\nconst MAXIMUM_CACHE_TIME: number = 365 * 86400 * 1000;\n\nconst defaultOptions: LimitedCacheOptionsReadonly = {\n // Public\n maxCacheSize: 100,\n maxCacheTime: 86400 * 1000,\n // Development-only\n warnIfItemPurgedBeforeTime: 5000,\n // Private\n opLimit: 200,\n scanLimit: 50,\n};\n\nexport { CURRENT_META_VERSION, defaultOptions, MAXIMUM_CACHE_TIME };\n","import type {\n DefaultItemType,\n LimitedCacheMeta,\n LimitedCacheOptions,\n LimitedCacheOptionsFull,\n LimitedCacheOptionsReadonly,\n} from '../types.js';\nimport { CURRENT_META_VERSION, defaultOptions, MAXIMUM_CACHE_TIME } from './defaultOptions.js';\n\n/* Initialization and options */\n\nconst positiveNumberOrZero = (value: number): number => Math.max(value, 0) || 0;\n\nconst normalizeOptions = (cacheMetaOptions: LimitedCacheOptionsFull): LimitedCacheOptionsFull => {\n Object.assign(cacheMetaOptions, {\n maxCacheSize: positiveNumberOrZero(cacheMetaOptions.maxCacheSize),\n maxCacheTime: positiveNumberOrZero(cacheMetaOptions.maxCacheTime),\n opLimit: positiveNumberOrZero(cacheMetaOptions.opLimit),\n });\n\n if (process.env.NODE_ENV !== 'production') {\n cacheMetaOptions.warnIfItemPurgedBeforeTime = positiveNumberOrZero(\n cacheMetaOptions.warnIfItemPurgedBeforeTime,\n );\n }\n return cacheMetaOptions;\n};\n\nconst isCacheMeta = (cacheMeta: unknown): cacheMeta is LimitedCacheMeta => {\n // @ts-expect-error Duck-typing the unknown value\n return !!cacheMeta?.limitedCacheMetaVersion;\n};\n\nconst upgradeCacheMeta = (cacheMeta: LimitedCacheMeta): void => {\n if (!isCacheMeta(cacheMeta)) {\n throw new Error('Limited-cache metadata is missing: please check your usage');\n }\n if (cacheMeta.limitedCacheMetaVersion !== CURRENT_META_VERSION) {\n // Version is out of date! (Today the only prior version is 1)\n // Version 1: Cache meta cannot be migrated because timestamps and keys are incompatible\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn('Limited-cache metadata is from an incompatible version (1). It must be reset.');\n cacheMeta.limitedCacheMetaVersion = CURRENT_META_VERSION;\n lowLevelReset(cacheMeta);\n }\n};\n\nconst lowLevelSetOptions = <ItemType = DefaultItemType>(\n cacheMeta: LimitedCacheMeta<ItemType>,\n options: LimitedCacheOptions,\n): LimitedCacheOptionsReadonly => {\n upgradeCacheMeta(cacheMeta);\n return normalizeOptions(Object.assign(cacheMeta.options, options));\n};\n\nconst lowLevelInit = <ItemType = DefaultItemType>(\n optionsOrCacheMeta?: LimitedCacheOptions | LimitedCacheMeta<ItemType>,\n): LimitedCacheMeta<ItemType> => {\n if (isCacheMeta(optionsOrCacheMeta)) {\n const existingCacheMeta = optionsOrCacheMeta;\n upgradeCacheMeta(existingCacheMeta);\n return existingCacheMeta;\n }\n // Else: it's options\n const fullOptions = normalizeOptions({\n ...defaultOptions,\n ...optionsOrCacheMeta,\n });\n\n // The cacheMeta is created once, and persists per instance\n const newCacheMeta = lowLevelReset({\n limitedCacheMetaVersion: CURRENT_META_VERSION,\n options: fullOptions,\n } as LimitedCacheMeta<ItemType>);\n\n return newCacheMeta;\n};\n\n/* Internal cache manipulation */\n\nconst _getExpireTime = (cacheMeta: LimitedCacheMeta, cacheKey: string): number => {\n const {\n options: { maxCacheTime },\n keyInfo: { [cacheKey]: keyInfo },\n } = cacheMeta;\n if (!keyInfo) {\n // A missing record is always treated as expired\n return 0;\n }\n // If we have an exact expireTime then honor it. Otherwise it'll depend on the current maxCacheTime.\n const [setTime, expireTime] = keyInfo;\n return expireTime || setTime + (maxCacheTime || MAXIMUM_CACHE_TIME);\n};\n\nconst _cacheKeyHasExpired = (\n cacheMeta: LimitedCacheMeta,\n cacheKey: string,\n now: number,\n): boolean => {\n return _getExpireTime(cacheMeta, cacheKey) < now;\n};\n\nconst lowLevelDoMaintenance = <ItemType = DefaultItemType>(\n cacheMeta: LimitedCacheMeta<ItemType>,\n): LimitedCacheMeta<ItemType> => {\n upgradeCacheMeta(cacheMeta);\n const { cache, keyList, keyInfo } = cacheMeta;\n const now = Date.now();\n\n // Rebuild cache from keyList only, checking timestamps to auto-remove expired\n const [newCache, newKeyList, newKeyInfo] = keyList.reduce(\n (acc, cacheKey) => {\n const [accCache, accKeyList, accKeyInfo] = acc;\n if (!_cacheKeyHasExpired(cacheMeta, cacheKey, now)) {\n accCache[cacheKey] = cache[cacheKey];\n accKeyList.push(cacheKey);\n accKeyInfo[cacheKey] = keyInfo[cacheKey];\n }\n return acc;\n },\n [\n // This manual assertion is required because TypeScript doesn't know that the initial value is of the same type as the accumulator.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n {} as (typeof cacheMeta)['cache'],\n [] as (typeof cacheMeta)['keyList'],\n Object.create(null) as (typeof cacheMeta)['keyInfo'],\n ],\n );\n\n return Object.assign(cacheMeta, {\n cache: newCache,\n keyList: newKeyList,\n keyInfo: newKeyInfo,\n opsLeft: cacheMeta.options.opLimit,\n });\n};\n\nconst _removeFromIndex = (cacheMeta: LimitedCacheMeta, startIndex: number, now: number): void => {\n const { cache, keyList, keyInfo } = cacheMeta;\n\n // Always remove the item requested, and also remove any neighbors who have expired\n let nextIndex = startIndex;\n let nextCacheKey = keyList[startIndex] as string;\n const keyListLength = keyList.length;\n do {\n // Remove the 'next' item\n\n cache[nextCacheKey] = keyInfo[nextCacheKey] = undefined;\n\n // Now advance and decide whether to keep going\n nextIndex++;\n nextCacheKey = keyList[nextIndex] as string;\n } while (nextIndex < keyListLength && _cacheKeyHasExpired(cacheMeta, nextCacheKey, now));\n\n // Remove the index for everything from the startIndex until we stopped\n keyList.splice(startIndex, nextIndex - startIndex);\n};\n\nconst _removeItemsToMakeRoom = (cacheMeta: LimitedCacheMeta, now: number): void => {\n const {\n options: { scanLimit, warnIfItemPurgedBeforeTime },\n cache,\n keyList,\n keyInfo,\n } = cacheMeta;\n\n // These track the soonest-to-expire thing we've found. It may not actually be \"oldest\".\n // By default we'll remove the item at the head of the queue, unless we find something better.\n let oldestItemIndex = 0;\n let oldestExpireTime = _getExpireTime(cacheMeta, keyList[0] as string);\n\n if (oldestExpireTime > now) {\n // The head of the list hasn't yet expired: scan for a better candidate to remove\n let indexToCheck = 0;\n const maxIndexToCheck = Math.min(keyList.length, scanLimit);\n while (indexToCheck < maxIndexToCheck) {\n const cacheKeyForIndex = keyList[indexToCheck] as string;\n const expireTimeForIndex = _getExpireTime(cacheMeta, cacheKeyForIndex);\n\n // We only consider it if it's eligible for expiration: otherwise it can't be a better option\n // than the default head-of-queue\n if (expireTimeForIndex < now) {\n // We found an expired item! This wins automatically\n oldestItemIndex = indexToCheck;\n oldestExpireTime = 0;\n break;\n }\n if (expireTimeForIndex < oldestExpireTime) {\n // We have a new leader\n oldestItemIndex = indexToCheck;\n oldestExpireTime = expireTimeForIndex;\n }\n indexToCheck += 1;\n }\n }\n\n // Warn if the 'oldest' item is more recent than we'd like: this means it cycled into and out of\n // cache too quickly for the cache to be useful.\n if (\n process.env.NODE_ENV !== 'production' &&\n warnIfItemPurgedBeforeTime &&\n oldestExpireTime > now\n ) {\n const oldestItemKey = keyList[oldestItemIndex] as string;\n const [oldestItemSetTime, oldestItemExpireTime] = keyInfo[oldestItemKey] as [number, number];\n\n if (now - oldestItemSetTime < warnIfItemPurgedBeforeTime) {\n // biome-ignore lint/suspicious/noConsole: Dev-only code\n console.warn(\n 'Purged an item from cache while it was still fresh: you may want to increase maxCacheSize',\n {\n currentTime: now,\n key: oldestItemKey,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n item: cache[oldestItemKey],\n setTime: oldestItemSetTime,\n expireTime: oldestItemExpireTime,\n timeInCache: now - oldestItemSetTime,\n },\n );\n }\n }\n\n // Remove the oldest item we found, plus any expired neighbors\n _removeFromIndex(cacheMeta, oldestItemIndex, now);\n};\n\n/* Accessors */\n\nconst lowLevelHas = <ItemType = DefaultItemType>(\n cacheMeta: LimitedCacheMeta<ItemType>,\n cacheKey: string,\n): boolean => {\n upgradeCacheMeta(cacheMeta);\n const { cache } = cacheMeta;\n // biome-ignore lint/suspicious/noPrototypeBuiltins: Keeping the legacy hasOwnProperty to avoid a breaking change\n if (Object.prototype.hasOwnProperty.call(cache, cacheKey) && cache[cacheKey] !== undefined) {\n if (!_cacheKeyHasExpired(cacheMeta, cacheKey, Date.now())) {\n return true;\n }\n // If it's expired, clear the value so that we can short-circuit future lookups\n cache[cacheKey] = undefined;\n }\n return false;\n};\n\nconst lowLevelGetOne = <ItemType = DefaultItemType>(\n cacheMeta: LimitedCacheMeta<ItemType>,\n cacheKey: string,\n): ItemType | undefined => {\n upgradeCacheMeta(cacheMeta);\n if (lowLevelHas(cacheMeta, cacheKey)) {\n return cacheMeta.cache[cacheKey];\n }\n return;\n};\n\nconst lowLevelGetAll = <ItemType = DefaultItemType>(\n cacheMeta: LimitedCacheMeta<ItemType>,\n): Record<string, ItemType> => {\n upgradeCacheMeta(cacheMeta);\n // Remove all expired values, and return whatever's left\n lowLevelDoMaintenance(cacheMeta);\n // Retype because there won't be any `undefined` values after doMaintenance\n return cacheMeta.cache as Record<string, ItemType>;\n};\n\nconst lowLevelSet = <ItemType = DefaultItemType>(\n cacheMeta: LimitedCacheMeta<ItemType>,\n cacheKey: string,\n item: ItemType,\n): LimitedCacheMeta<ItemType> => {\n upgradeCacheMeta(cacheMeta);\n\n const {\n options: { maxCacheSize },\n keyList,\n keyInfo,\n } = cacheMeta;\n\n const now = Date.now();\n const isNew = !keyInfo[cacheKey];\n\n if (cacheMeta.cache[cacheKey] !== item) {\n // The cache itself is immutable (but the rest of cacheMeta is not)\n cacheMeta.cache = {\n ...cacheMeta.cache,\n [cacheKey]: item,\n };\n }\n // We've now set or updated it. Regardless of whether it's new, bump its set time\n // @TODO: expireTime override\n keyInfo[cacheKey] = [now, 0];\n\n if (isNew) {\n // It's a new key: grow the cache, then shrink it if we can\n keyList.push(cacheKey);\n\n cacheMeta.opsLeft--;\n if (cacheMeta.opsLeft <= 0) {\n // Time for an oil change\n lowLevelDoMaintenance(cacheMeta);\n }\n\n if (maxCacheSize && cacheMeta.keyList.length > maxCacheSize) {\n // We're still over the limit: drop at least one item\n _removeItemsToMakeRoom(cacheMeta, now);\n }\n }\n\n if (_cacheKeyHasExpired(cacheMeta, keyList[0] as string, now)) {\n // While we're here, if we need to expire the head of the queue then drop it\n _removeFromIndex(cacheMeta, 0, now);\n }\n\n return cacheMeta;\n};\n\nconst lowLevelRemove = <ItemType = DefaultItemType>(\n cacheMeta: LimitedCacheMeta<ItemType>,\n cacheKey: string,\n): LimitedCacheMeta<ItemType> => {\n upgradeCacheMeta(cacheMeta);\n const { cache, keyInfo } = cacheMeta;\n\n if (keyInfo[cacheKey]) {\n if (cache[cacheKey] !== undefined) {\n cacheMeta.cache = {\n ...cache,\n [cacheKey]: undefined,\n };\n }\n keyInfo[cacheKey] = undefined;\n }\n\n return cacheMeta;\n};\n\nconst lowLevelReset = <ItemType = DefaultItemType>(\n cacheMeta: LimitedCacheMeta<ItemType>,\n): LimitedCacheMeta<ItemType> => {\n upgradeCacheMeta(cacheMeta);\n return Object.assign(cacheMeta, {\n cache: {},\n keyList: [],\n keyInfo: Object.create(null) as Record<string, ItemType>,\n opsLeft: cacheMeta.options.opLimit,\n });\n};\n\nexport {\n isCacheMeta,\n lowLevelDoMaintenance,\n lowLevelGetAll,\n lowLevelGetOne,\n lowLevelHas,\n lowLevelInit,\n lowLevelRemove,\n lowLevelReset,\n lowLevelSet,\n lowLevelSetOptions,\n upgradeCacheMeta,\n};\n","import type {\n DefaultItemType,\n LimitedCacheInstance,\n LimitedCacheMeta,\n LimitedCacheOptions,\n LimitedCacheOptionsReadonly,\n} from '../types.js';\nimport {\n lowLevelDoMaintenance,\n lowLevelGetAll,\n lowLevelGetOne,\n lowLevelHas,\n lowLevelInit,\n lowLevelRemove,\n lowLevelReset,\n lowLevelSet,\n lowLevelSetOptions,\n} from './lowLevelFunctions.js';\n\n// Most public functions just call a low-level function directly, passing the cacheMeta.\n// Doing this via a helper function makes the typeChecks easier, and minifies better.\nconst bindFunctionToCacheMeta = <ItemType, OtherArgs extends unknown[], ReturnValue>(\n fn: (cacheMeta: LimitedCacheMeta<ItemType>, ...otherArgs: OtherArgs) => ReturnValue,\n cacheMeta: LimitedCacheMeta<ItemType>,\n): ((...otherArgs: OtherArgs) => ReturnValue) => fn.bind(null, cacheMeta);\n\nconst LimitedCache = <ItemType = DefaultItemType>(\n options?: LimitedCacheOptions,\n): LimitedCacheInstance<ItemType> => {\n const cacheMeta = lowLevelInit<ItemType>(options);\n\n return {\n get: bindFunctionToCacheMeta(lowLevelGetOne, cacheMeta),\n getAll: bindFunctionToCacheMeta(lowLevelGetAll, cacheMeta),\n has: bindFunctionToCacheMeta(lowLevelHas, cacheMeta),\n set: (cacheKey, item): ItemType => {\n lowLevelSet(cacheMeta, cacheKey, item);\n return item;\n },\n remove: (cacheKey): true => {\n lowLevelRemove(cacheMeta, cacheKey);\n return true;\n },\n reset: bindFunctionToCacheMeta(lowLevelReset, cacheMeta),\n getCacheMeta: (): LimitedCacheMeta<ItemType> => cacheMeta,\n getOptions: (): LimitedCacheOptionsReadonly => cacheMeta.options,\n setOptions: bindFunctionToCacheMeta(lowLevelSetOptions, cacheMeta),\n doMaintenance: bindFunctionToCacheMeta(lowLevelDoMaintenance, cacheMeta),\n };\n};\n\nexport { LimitedCache };\n","import type {\n DefaultItemType,\n LimitedCacheMeta,\n LimitedCacheObjectInstance,\n LimitedCacheOptions,\n} from '../types.js';\nimport {\n lowLevelGetAll,\n lowLevelGetOne,\n lowLevelHas,\n lowLevelInit,\n lowLevelRemove,\n lowLevelSet,\n} from './lowLevelFunctions.js';\n\nconst proxyHandler: ProxyHandler<LimitedCacheMeta> = {\n get: (cacheMeta: LimitedCacheMeta, cacheKey: string) => {\n if (cacheKey === 'hasOwnProperty') {\n return Object.prototype.hasOwnProperty;\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return lowLevelGetOne(cacheMeta, cacheKey);\n },\n getOwnPropertyDescriptor: (cacheMeta: LimitedCacheMeta, cacheKey: string) => {\n const hasResult = lowLevelHas(cacheMeta, cacheKey);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const getResult = lowLevelGetOne(cacheMeta, cacheKey);\n\n if (hasResult) {\n return {\n configurable: true,\n enumerable: hasResult,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value: getResult,\n writable: true,\n };\n }\n return;\n },\n has: lowLevelHas,\n set: <T>(cacheMeta: LimitedCacheMeta, cacheKey: string, item: T): T => {\n lowLevelSet(cacheMeta, cacheKey, item);\n return item;\n },\n deleteProperty: (cacheMeta: LimitedCacheMeta, cacheKey: string): true => {\n lowLevelRemove(cacheMeta, cacheKey);\n return true;\n },\n ownKeys: (cacheMeta: LimitedCacheMeta) => Object.keys(lowLevelGetAll(cacheMeta)),\n};\n\n/**\n * TypeScript's Proxy type models the runtime target, but LimitedCacheObject intentionally returns\n * a facade with a different surface from the internal cache metadata target.\n */\nconst internal_createLimitedCacheObjectProxy = <ItemType = DefaultItemType>(\n cacheMeta: LimitedCacheMeta<ItemType>,\n): LimitedCacheObjectInstance<ItemType> => {\n return new Proxy(cacheMeta, proxyHandler) as unknown as LimitedCacheObjectInstance<ItemType>;\n};\n\n/**\n * So that we can retrieve the cacheMeta for a LimitedCacheObject, without polluting its properties, each proxy\n * is associated back to its internal cacheMeta here.\n */\nconst cacheMetasForProxies = new WeakMap();\n\nconst LimitedCacheObject = <ItemType = DefaultItemType>(\n options?: LimitedCacheOptions,\n): LimitedCacheObjectInstance<ItemType> => {\n const cacheMeta = lowLevelInit<ItemType>(options);\n const limitedCacheObject = internal_createLimitedCacheObjectProxy(cacheMeta);\n\n cacheMetasForProxies.set(limitedCacheObject, cacheMeta);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return limitedCacheObject;\n};\n\nconst getCacheMetaFromObject = (instance: LimitedCacheObjectInstance): LimitedCacheMeta => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return cacheMetasForProxies.get(instance);\n};\n\nexport { getCacheMetaFromObject, LimitedCacheObject };\n","import {\n lowLevelDoMaintenance,\n lowLevelGetAll,\n lowLevelGetOne,\n lowLevelHas,\n lowLevelInit,\n lowLevelRemove,\n lowLevelReset,\n lowLevelSet,\n lowLevelSetOptions,\n} from './lowLevelFunctions.js';\n\ntype LimitedCacheUtil = {\n init: typeof lowLevelInit;\n get: typeof lowLevelGetOne;\n getAll: typeof lowLevelGetAll;\n has: typeof lowLevelHas;\n set: typeof lowLevelSet;\n remove: typeof lowLevelRemove;\n reset: typeof lowLevelReset;\n doMaintenance: typeof lowLevelDoMaintenance;\n setOptions: typeof lowLevelSetOptions;\n};\n\nconst limitedCacheUtil: LimitedCacheUtil = {\n init: lowLevelInit,\n get: lowLevelGetOne,\n getAll: lowLevelGetAll,\n has: lowLevelHas,\n set: lowLevelSet,\n remove: lowLevelRemove,\n reset: lowLevelReset,\n doMaintenance: lowLevelDoMaintenance,\n setOptions: lowLevelSetOptions,\n};\n\nexport { limitedCacheUtil };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "limited-cache",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "A minimal JS cache: like using an object, except it won't grow forever",
5
5
  "keywords": [
6
6
  "limited cache",
@@ -18,12 +18,12 @@
18
18
  "bugs": "https://github.com/spautz/limited-cache/issues",
19
19
  "repository": {
20
20
  "type": "git",
21
- "url": "https://github.com/spautz/limited-cache.git",
21
+ "url": "git+https://github.com/spautz/limited-cache.git",
22
22
  "directory": "packages/limited-cache"
23
23
  },
24
24
  "author": {
25
25
  "name": "Steven Pautz",
26
- "url": "https://github.com/spautz/"
26
+ "url": "https://stevenpautz.com"
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public",
@@ -33,7 +33,6 @@
33
33
  "files": [
34
34
  "dist/",
35
35
  "docs/",
36
- "legacy-types/",
37
36
  "src/",
38
37
  "LICENSE",
39
38
  "*.md"
@@ -42,6 +41,7 @@
42
41
  "type": "module",
43
42
  "exports": {
44
43
  ".": {
44
+ "local-dev": "./src/index.ts",
45
45
  "import": {
46
46
  "types": "./dist/esm/index.d.ts",
47
47
  "default": "./dist/esm/index.js"
@@ -51,16 +51,22 @@
51
51
  "default": "./dist/cjs/index.cjs"
52
52
  }
53
53
  },
54
- "./internal-raw-src": "./src/index.ts",
55
54
  "./package.json": "./package.json"
56
55
  },
57
56
  "main": "./dist/cjs/index.cjs",
58
57
  "module": "./dist/esm/index.js",
59
- "jsnext:main": "./dist/esm/index.js",
60
58
  "types": "./dist/esm/index.d.ts",
61
59
  "sideEffects": false,
62
60
  "dependencies": {},
63
- "devDependencies": {},
61
+ "devDependencies": {
62
+ "@arethetypeswrong/cli": "0.18.3",
63
+ "@size-limit/esbuild": "12.1.0",
64
+ "@size-limit/esbuild-why": "12.1.0",
65
+ "@size-limit/file": "12.1.0",
66
+ "publint": "0.3.21",
67
+ "size-limit": "12.1.0",
68
+ "tsdown": "0.22.3"
69
+ },
64
70
  "peerDependencies": {},
65
71
  "size-limit": [
66
72
  {
@@ -74,54 +80,29 @@
74
80
  "limit": "1.2 kb"
75
81
  }
76
82
  ],
77
- "typesVersions": {
78
- "<4.0": {
79
- "*": [
80
- "./legacy-types/ts3.5/*"
81
- ]
82
- },
83
- "<4.5": {
84
- "*": [
85
- "./legacy-types/ts4.0/*"
86
- ]
87
- },
88
- "<4.7": {
89
- "*": [
90
- "./legacy-types/ts4.5/*"
91
- ]
92
- }
93
- },
94
83
  "scripts": {
95
84
  "____ HOOKS _________________________________________________________": "",
96
- "____ INTEGRATION ___________________________________________________": "",
97
- "clean": "pnpm run build:clean && pnpm run test:clean && rimraf --glob ./node_modules/.cache *.log",
98
- "all": "pnpm run typecheck && pnpm run lint:fix && pnpm run test:coverage && pnpm run build",
99
- "all:readonly": "pnpm run typecheck && pnpm run lint && pnpm run test:quick",
100
- "all:quick": "pnpm run typecheck && pnpm run lint:fix",
101
- "all:ci": "pnpm run typecheck && pnpm run lint && pnpm run test:ci && pnpm pack",
102
- "____ INDIVIDUAL COMMANDS ___________________________________________": "",
103
- "build": "pnpm run build:clean && pnpm run build:cjs && pnpm run build:esm && pnpm run build:verify && pnpm run build:legacytypes",
104
- "build:clean": "rimraf --glob ./dist ./legacy-types ./*.tgz",
105
- "build:cjs": "tsup src/index.ts --format cjs --dts --outDir=./dist/cjs/ --tsconfig=./tsconfig.build-cjs.json",
106
- "build:cjs:watch": "pnpm run build:cjs --watch",
107
- "build:esm": "tsc -p tsconfig.build-esm.json",
108
- "build:esm:watch": "pnpm run build:esm --watch",
109
- "build:legacytypes": "pnpm run build:legacytypes:3.5 && pnpm run build:legacytypes:4.0 && pnpm run build:legacytypes:4.5",
110
- "build:legacytypes:3.5": "downlevel-dts ./dist ./legacy-types/ts3.5/dist --to=3.5",
111
- "build:legacytypes:4.0": "downlevel-dts ./dist ./legacy-types/ts4.0/dist --to=4.0",
112
- "build:legacytypes:4.5": "downlevel-dts ./dist ./legacy-types/ts4.5/dist --to=4.5",
113
- "build:verify": "publint --strict && size-limit",
114
- "build:verifypack": "ls *.tgz | xargs -I {} attw {} --exclude-entrypoints internal-raw-src",
115
- "lint": "eslint . --max-warnings 0",
116
- "lint:fix": "eslint . --max-warnings 0 --fix",
117
- "test": "pnpm run test:coverage",
85
+ "____ STANDARD PACKAGE TASKS ________________________________________": "",
86
+ "all": "turbo run pipeline-all",
87
+ "all:ci": "turbo run pipeline-ci --ui=stream",
88
+ "build": "pnpm run build:clean && pnpm run build:lib && pnpm run build:verify",
89
+ "clean": "pnpm run \"/^.*:clean$/\" && rimraf --glob ./node_modules/.cache ./.turbo ./*.log",
90
+ "dev": "pnpm run build:lib:watch",
91
+ "lint": "biome check --write .",
92
+ "lint:verify": "biome check .",
93
+ "publish:yalc": "pnpm run prepack && yalc publish --no-scripts --sig --push --replace",
118
94
  "test:clean": "rimraf ./coverage",
119
- "test:ci": "pnpm run test:clean && vitest run --coverage",
120
- "test:coverage": "pnpm run test:clean && vitest run --coverage",
121
- "test:quick": "pnpm run test:clean && vitest run --coverage=false",
122
- "test:ui": "pnpm run test:clean && vitest --ui",
123
- "test:watch": "pnpm run test:clean && vitest watch --coverage=false",
124
- "test:watchcoverage": "pnpm run test:clean && vitest watch --coverage",
125
- "typecheck": "tsc -p ./tsconfig.json --noEmit"
95
+ "test": "pnpm run test:clean && vitest run --coverage",
96
+ "test:ci": "pnpm run test:clean && vitest run --coverage --bail 1",
97
+ "test:ui": "pnpm run test:clean && vitest --ui",
98
+ "test:watch": "pnpm run test:clean && vitest watch --coverage",
99
+ "typecheck": "tsgo -p ./tsconfig.json --noEmit",
100
+ "____ BUILD & VALIDATION TASKS ______________________________________": "",
101
+ "build:clean": "rimraf --glob ./dist ./*.tgz",
102
+ "build:lib": "tsdown --config ./tsdown.config.ts",
103
+ "build:lib:watch": "tsdown --config ./tsdown.config.ts --watch",
104
+ "build:verify": "publint --strict && size-limit",
105
+ "build:verifypack": "node --experimental-strip-types ../../scripts/verify-packed-tarballs.ts",
106
+ "size-check": "size-limit --why"
126
107
  }
127
108
  }
@@ -1,7 +1,7 @@
1
- import { describe, beforeEach, expect, it } from 'vitest';
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
2
  import { defaultOptions } from '../core/defaultOptions.js';
3
3
  import { LimitedCache } from '../core/LimitedCache.js';
4
- import { LimitedCacheInstance } from '../types.js';
4
+ import type { LimitedCacheInstance } from '../types.js';
5
5
 
6
6
  describe('LimitedCache', () => {
7
7
  it('initializes without options', () => {
@@ -1,7 +1,10 @@
1
- import { describe, beforeEach, expect, it } from 'vitest';
1
+ // biome-ignore-all lint/performance/noDelete: Delete is explicitly used to unset items for these tests
2
+ // biome-ignore-all lint/suspicious/noAssignInExpressions: Assignment expressions are explicitly tested
3
+
4
+ import { beforeEach, describe, expect, it } from 'vitest';
2
5
  import { defaultOptions } from '../core/defaultOptions.js';
3
- import { LimitedCacheObject, getCacheMetaFromObject } from '../core/LimitedCacheObject.js';
4
- import { LimitedCacheObjectInstance } from '../types.js';
6
+ import { getCacheMetaFromObject, LimitedCacheObject } from '../core/LimitedCacheObject.js';
7
+ import type { LimitedCacheObjectInstance } from '../types.js';
5
8
 
6
9
  describe('LimitedCacheObject', () => {
7
10
  it('initializes without options', () => {
@@ -29,13 +32,13 @@ describe('LimitedCacheObject', () => {
29
32
  });
30
33
 
31
34
  it('has: when missing, via prototype hasOwnProperty', () => {
32
- const result = Object.prototype.hasOwnProperty.call(myCache, 'asdf');
35
+ const result = Object.hasOwn(myCache, 'asdf');
33
36
  expect(result).toEqual(false);
34
37
  });
35
38
 
36
39
  it('has: when missing, via local hasOwnProperty', () => {
37
40
  // eslint-disable-next-line no-prototype-builtins
38
- const result = myCache.hasOwnProperty('asdf');
41
+ const result = Object.hasOwn(myCache, 'asdf');
39
42
  expect(result).toEqual(false);
40
43
  });
41
44
 
@@ -45,71 +48,71 @@ describe('LimitedCacheObject', () => {
45
48
  });
46
49
 
47
50
  it('has: when present, via prototype hasOwnProperty', () => {
48
- myCache.abc = 123;
51
+ myCache['abc'] = 123;
49
52
 
50
- const result = Object.prototype.hasOwnProperty.call(myCache, 'abc');
53
+ const result = Object.hasOwn(myCache, 'abc');
51
54
  expect(result).toEqual(true);
52
55
  });
53
56
 
54
57
  it('has: when present, via local hasOwnProperty', () => {
55
- myCache.abc = 123;
58
+ myCache['abc'] = 123;
56
59
 
57
60
  // eslint-disable-next-line no-prototype-builtins
58
- const result = myCache.hasOwnProperty('abc');
61
+ const result = Object.hasOwn(myCache, 'abc');
59
62
  expect(result).toEqual(true);
60
63
  });
61
64
 
62
65
  it('has: when present, via "in"', () => {
63
- myCache.abc = 123;
66
+ myCache['abc'] = 123;
64
67
 
65
68
  const result = 'abc' in myCache;
66
69
  expect(result).toEqual(true);
67
70
  });
68
71
 
69
72
  it('get: when missing', () => {
70
- const result = myCache.asdf;
73
+ const result = myCache['asdf'];
71
74
  expect(result).toBeUndefined();
72
75
  });
73
76
 
74
77
  it('get: when present', () => {
75
- myCache.abc = 123;
78
+ myCache['abc'] = 123;
76
79
 
77
- const result = myCache.abc;
80
+ const result = myCache['abc'];
78
81
  expect(result).toEqual(123);
79
82
  });
80
83
 
81
84
  it('set: when new', () => {
82
- const result = (myCache.abc = 123);
85
+ const result = (myCache['abc'] = 123);
83
86
  expect(result).toEqual(123);
84
87
  });
85
88
 
86
89
  it('set: when already present', () => {
87
- myCache.abc = 123;
90
+ myCache['abc'] = 123;
88
91
 
89
- const result = (myCache.abc = 456);
92
+ const result = (myCache['abc'] = 456);
90
93
  expect(result).toEqual(456);
91
94
  });
92
95
 
93
96
  it('delete and return value', () => {
94
- myCache.abc = 123;
97
+ myCache['abc'] = 123;
95
98
 
96
- const result = delete myCache.abc;
99
+ const result = delete myCache['abc'];
97
100
  expect(result).toEqual(true);
98
101
  });
99
102
 
100
103
  it('delete, then check', () => {
101
- myCache.abc = 123;
102
- delete myCache.abc;
104
+ myCache['abc'] = 123;
105
+ delete myCache['abc'];
103
106
 
104
- const result = Object.prototype.hasOwnProperty.call(myCache, 'abc');
107
+ const result = Object.hasOwn(myCache, 'abc');
105
108
  expect(result).toEqual(false);
106
109
  });
107
110
 
108
111
  it('delete, then get', () => {
109
- myCache.abc = 123;
110
- delete myCache.abc;
112
+ myCache['abc'] = 123;
113
+ delete myCache['abc'];
111
114
 
112
- const result = myCache.abc;
115
+ const result = myCache['abc'];
113
116
  expect(result).toEqual(undefined);
114
117
  });
115
118
 
@@ -119,15 +122,15 @@ describe('LimitedCacheObject', () => {
119
122
  });
120
123
 
121
124
  it('keys: when populated', () => {
122
- myCache.abc = 123;
125
+ myCache['abc'] = 123;
123
126
 
124
127
  const result = Object.keys(myCache);
125
128
  expect(result).toEqual(['abc']);
126
129
  });
127
130
 
128
131
  it('keys: when all are removed', () => {
129
- myCache.abc = 123;
130
- delete myCache.abc;
132
+ myCache['abc'] = 123;
133
+ delete myCache['abc'];
131
134
 
132
135
  const result = Object.keys(myCache);
133
136
  expect(result).toEqual([]);
@@ -1,17 +1,17 @@
1
- import { describe, beforeEach, expect, it, vitest } from 'vitest';
1
+ import { beforeEach, describe, expect, it, vitest } from 'vitest';
2
2
  import { defaultOptions } from '../core/defaultOptions.js';
3
3
  import {
4
4
  isCacheMeta,
5
- upgradeCacheMeta,
6
- lowLevelInit,
7
- lowLevelGetOne,
8
5
  lowLevelGetAll,
6
+ lowLevelGetOne,
9
7
  lowLevelHas,
10
- lowLevelSet,
8
+ lowLevelInit,
11
9
  lowLevelRemove,
12
10
  lowLevelReset,
11
+ lowLevelSet,
12
+ upgradeCacheMeta,
13
13
  } from '../core/lowLevelFunctions.js';
14
- import { LimitedCacheMeta } from '../types.js';
14
+ import type { LimitedCacheMeta } from '../types.js';
15
15
 
16
16
  describe('lowLevelFunctions', () => {
17
17
  describe('isCacheMeta', () => {
@@ -29,7 +29,6 @@ describe('lowLevelFunctions', () => {
29
29
  });
30
30
  it('rejects invalid cacheMeta shapes', () => {
31
31
  expect(
32
- // @ts-expect-error Invalid cacheMeta shape
33
32
  isCacheMeta({
34
33
  cache: {},
35
34
  }),
@@ -57,7 +56,7 @@ describe('lowLevelFunctions', () => {
57
56
  upgradeCacheMeta({
58
57
  cache: {},
59
58
  });
60
- }).toThrowError();
59
+ }).toThrow();
61
60
  });
62
61
  });
63
62
 
@@ -168,6 +167,7 @@ describe('lowLevelFunctions', () => {
168
167
  });
169
168
 
170
169
  it('get a missing key', () => {
170
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
171
171
  const result = lowLevelGetOne(myCacheMeta, 'abc');
172
172
  expect(result).toBeUndefined();
173
173
  });
@@ -178,6 +178,7 @@ describe('lowLevelFunctions', () => {
178
178
  myCacheMeta.keyList = ['abc'];
179
179
  myCacheMeta.keyInfo['abc'] = [Date.now(), 0];
180
180
 
181
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
181
182
  const result = lowLevelGetOne(myCacheMeta, 'abc');
182
183
  expect(result).toEqual(123);
183
184
  });
@@ -188,6 +189,7 @@ describe('lowLevelFunctions', () => {
188
189
  myCacheMeta.keyList = ['abc'];
189
190
  myCacheMeta.keyInfo['abc'] = [1, 0];
190
191
 
192
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
191
193
  const result = lowLevelGetOne(myCacheMeta, 'abc');
192
194
  expect(result).toEqual(undefined);
193
195
  });
@@ -1,5 +1,5 @@
1
- import { describe, beforeEach, expect, it } from 'vitest';
2
- import { limitedCacheUtil, LimitedCacheMeta } from '../../index.js';
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
+ import { type LimitedCacheMeta, limitedCacheUtil } from '../../index.js';
3
3
 
4
4
  describe('key names', () => {
5
5
  let myCacheMeta: LimitedCacheMeta;
@@ -1,10 +1,10 @@
1
- import { describe, beforeEach, expect, it, vitest } from 'vitest';
2
- import { LimitedCache, LimitedCacheInstance } from '../../index.js';
1
+ import { beforeEach, describe, expect, it, vitest } from 'vitest';
2
+ import { LimitedCache, type LimitedCacheInstance } from '../../index.js';
3
3
 
4
4
  // To avoid race conditions or timing issues, since some expect() checks can take 10+ ms when busy,
5
5
  // we use a long cache timeout even for 'immediate' expiration, and use delays slightly longer than that
6
6
  const CACHE_TIMEOUT = 20;
7
- const timeoutPromise = (): Promise<null> =>
7
+ const timeoutPromise = (): Promise<void> =>
8
8
  new Promise((resolve) => setTimeout(resolve, CACHE_TIMEOUT + 2));
9
9
 
10
10
  describe('maxCacheSize scenarios', () => {
@@ -95,7 +95,7 @@ describe('maxCacheSize scenarios', () => {
95
95
  expect(Object.keys(myCache.getAll())).toEqual(['n=6', 'n=7', 'n=8', 'n=9', 'n=10']);
96
96
  });
97
97
 
98
- it('warns if a freshly-added item is pushed out too quickly', async () => {
98
+ it('warns if a freshly-added item is pushed out too quickly', () => {
99
99
  myCache = LimitedCache({
100
100
  maxCacheSize: 5,
101
101
  maxCacheTime: 1000,
@@ -116,7 +116,10 @@ describe('maxCacheSize scenarios', () => {
116
116
  // And we should have seen a warning about the force-removed key
117
117
  const consoleWarnCalls = consoleWarnSpy.mock.calls;
118
118
  expect(consoleWarnCalls.length).toBe(1);
119
- const [warningString, warningObject] = consoleWarnCalls[0];
119
+ const [warningString, warningObject] = consoleWarnCalls[0] as [
120
+ string,
121
+ { key: string; value: string },
122
+ ];
120
123
  expect(warningString).toBe(
121
124
  'Purged an item from cache while it was still fresh: you may want to increase maxCacheSize',
122
125
  );
@@ -1,10 +1,12 @@
1
- import { describe, beforeEach, expect, it, vitest } from 'vitest';
2
- import { LimitedCacheObject, LimitedCacheObjectInstance } from '../../index.js';
1
+ // biome-ignore-all lint/performance/noDelete: Delete is explicitly used to unset items for these tests
2
+
3
+ import { beforeEach, describe, expect, it, vitest } from 'vitest';
4
+ import { LimitedCacheObject, type LimitedCacheObjectInstance } from '../../index.js';
3
5
 
4
6
  // To avoid race conditions or timing issues, since some expect() checks can take 10+ ms when busy,
5
7
  // we use a long cache timeout even for 'immediate' expiration, and use delays slightly longer than that
6
8
  const CACHE_TIMEOUT = 25;
7
- const timeoutPromise = (): Promise<null> =>
9
+ const timeoutPromise = (): Promise<void> =>
8
10
  new Promise((resolve) => setTimeout(resolve, CACHE_TIMEOUT + 2));
9
11
 
10
12
  describe('maxCacheTime scenarios', () => {
@@ -19,41 +21,39 @@ describe('maxCacheTime scenarios', () => {
19
21
  });
20
22
 
21
23
  it("doesn't have keys for expired items", async () => {
22
- myCache.abc = 123;
23
- myCache.def = 456;
24
+ myCache['abc'] = 123;
25
+ myCache['def'] = 456;
24
26
  await timeoutPromise();
25
- myCache.ghi = 789;
27
+ myCache['ghi'] = 789;
26
28
 
27
- const { hasOwnProperty } = Object.prototype;
28
- expect(hasOwnProperty.call(myCache, 'abc')).toEqual(false);
29
- expect(hasOwnProperty.call(myCache, 'def')).toEqual(false);
29
+ expect(Object.hasOwn(myCache, 'abc')).toEqual(false);
30
+ expect(Object.hasOwn(myCache, 'def')).toEqual(false);
30
31
 
31
32
  expect(Object.keys(myCache)).toEqual(['ghi']);
32
33
  });
33
34
 
34
35
  it('removes expired items on get', async () => {
35
- myCache.abc = 123;
36
- myCache.def = 456;
36
+ myCache['abc'] = 123;
37
+ myCache['def'] = 456;
37
38
  await timeoutPromise();
38
39
 
39
- expect(myCache.abc).toEqual(undefined);
40
- expect(myCache.def).toEqual(undefined);
40
+ expect(myCache['abc']).toEqual(undefined);
41
+ expect(myCache['def']).toEqual(undefined);
41
42
  expect(Object.keys(myCache)).toEqual([]);
42
43
  });
43
44
 
44
45
  it('removes expired items on set', async () => {
45
- myCache.abc = 123;
46
- myCache.def = 456;
46
+ myCache['abc'] = 123;
47
+ myCache['def'] = 456;
47
48
  await timeoutPromise();
48
- myCache.ghi = 789;
49
-
50
- const { hasOwnProperty } = Object.prototype;
51
- expect(hasOwnProperty.call(myCache, 'abc')).toEqual(false);
52
- expect(hasOwnProperty.call(myCache, 'def')).toEqual(false);
53
- expect(hasOwnProperty.call(myCache, 'ghi')).toEqual(true);
54
- expect(myCache.abc).toEqual(undefined);
55
- expect(myCache.def).toEqual(undefined);
56
- expect(myCache.ghi).toEqual(789);
49
+ myCache['ghi'] = 789;
50
+
51
+ expect(Object.hasOwn(myCache, 'abc')).toEqual(false);
52
+ expect(Object.hasOwn(myCache, 'def')).toEqual(false);
53
+ expect(Object.hasOwn(myCache, 'ghi')).toEqual(true);
54
+ expect(myCache['abc']).toEqual(undefined);
55
+ expect(myCache['def']).toEqual(undefined);
56
+ expect(myCache['ghi']).toEqual(789);
57
57
  expect(Object.keys(myCache)).toEqual(['ghi']);
58
58
  });
59
59
 
@@ -65,24 +65,24 @@ describe('maxCacheTime scenarios', () => {
65
65
  warnIfItemPurgedBeforeTime: 0,
66
66
  });
67
67
 
68
- myCache.abc = 123;
68
+ myCache['abc'] = 123;
69
69
  await timeoutPromise();
70
- myCache.def = 456;
70
+ myCache['def'] = 456;
71
71
  await timeoutPromise();
72
- myCache.ghi = 789;
72
+ myCache['ghi'] = 789;
73
73
  await timeoutPromise();
74
74
 
75
75
  // Now write over the 'oldest' key to reset its timestamp
76
- myCache.abc = 123;
76
+ myCache['abc'] = 123;
77
77
 
78
78
  // Now, adding a new value (over maxCacheSize) should remove the actual oldest
79
- myCache.newOne = 100;
80
- expect(myCache.def).toEqual(undefined);
79
+ myCache['newOne'] = 100;
80
+ expect(myCache['def']).toEqual(undefined);
81
81
  expect(Object.keys(myCache)).toEqual(['abc', 'ghi', 'newOne']);
82
82
 
83
83
  // And then the next oldest, after that
84
- myCache.newTwo = 200;
85
- expect(myCache.ghi).toEqual(undefined);
84
+ myCache['newTwo'] = 200;
85
+ expect(myCache['ghi']).toEqual(undefined);
86
86
  expect(Object.keys(myCache)).toEqual(['abc', 'newOne', 'newTwo']);
87
87
  });
88
88
 
@@ -94,36 +94,36 @@ describe('maxCacheTime scenarios', () => {
94
94
  });
95
95
 
96
96
  // This first set will expire after the second set gets added
97
- myCache.abc = 123;
98
- myCache.def = 456;
99
- myCache.ghi = 789;
97
+ myCache['abc'] = 123;
98
+ myCache['def'] = 456;
99
+ myCache['ghi'] = 789;
100
100
  await timeoutPromise();
101
- myCache.jkl = 321;
102
- myCache.mno = 654;
103
- myCache.abc = 1000;
101
+ myCache['jkl'] = 321;
102
+ myCache['mno'] = 654;
103
+ myCache['abc'] = 1000;
104
104
 
105
105
  // At this point nothing has been removed, since reusing 'abc' keeps us within maxCacheSize
106
- expect(myCache.abc).toEqual(1000);
107
- expect(myCache.def).toEqual(456);
108
- expect(myCache.ghi).toEqual(789);
106
+ expect(myCache['abc']).toEqual(1000);
107
+ expect(myCache['def']).toEqual(456);
108
+ expect(myCache['ghi']).toEqual(789);
109
109
 
110
110
  await timeoutPromise();
111
111
 
112
112
  // Now, adding a new value (over maxCacheSize) should remove both of the remaining expired values
113
- myCache.newOne = 100;
114
- expect(myCache.def).toEqual(undefined);
115
- expect(myCache.ghi).toEqual(undefined);
113
+ myCache['newOne'] = 100;
114
+ expect(myCache['def']).toEqual(undefined);
115
+ expect(myCache['ghi']).toEqual(undefined);
116
116
  expect(Object.keys(myCache)).toEqual(['abc', 'jkl', 'mno', 'newOne']);
117
117
 
118
118
  // And then the next can go in without having to remove anything
119
- myCache.newTwo = 200;
120
- expect(myCache.jkl).toEqual(321);
121
- expect(myCache.mno).toEqual(654);
122
- expect(myCache.abc).toEqual(1000);
119
+ myCache['newTwo'] = 200;
120
+ expect(myCache['jkl']).toEqual(321);
121
+ expect(myCache['mno']).toEqual(654);
122
+ expect(myCache['abc']).toEqual(1000);
123
123
  expect(Object.keys(myCache)).toEqual(['abc', 'jkl', 'mno', 'newOne', 'newTwo']);
124
124
  });
125
125
 
126
- it('removes keys for already-removed items first', async () => {
126
+ it('removes keys for already-removed items first', () => {
127
127
  myCache = LimitedCacheObject({
128
128
  maxCacheTime: 0,
129
129
  maxCacheSize: 5,
@@ -132,18 +132,18 @@ describe('maxCacheTime scenarios', () => {
132
132
  });
133
133
 
134
134
  // This first set will expire after the second set gets added
135
- myCache.abc = 123;
136
- myCache.def = 456;
137
- myCache.ghi = 789;
138
- myCache.jkl = 321;
139
- myCache.mno = 654;
140
- myCache.abc = 1000;
135
+ myCache['abc'] = 123;
136
+ myCache['def'] = 456;
137
+ myCache['ghi'] = 789;
138
+ myCache['jkl'] = 321;
139
+ myCache['mno'] = 654;
140
+ myCache['abc'] = 1000;
141
141
 
142
- delete myCache.ghi;
143
- delete myCache.jkl;
142
+ delete myCache['ghi'];
143
+ delete myCache['jkl'];
144
144
 
145
145
  // Now, adding a new value (over maxCacheSize) should not remove anything else
146
- myCache.newOne = 100;
146
+ myCache['newOne'] = 100;
147
147
 
148
148
  expect(Object.keys(myCache)).toEqual(['abc', 'def', 'mno', 'newOne']);
149
149
  });