From db54e5cf7a33e13f1fbb779ea854a7edcda7a830 Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Mon, 19 May 2025 16:43:56 -0400 Subject: [PATCH 1/3] Update dev dependencies. --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 751b9a0..70a34d6 100644 --- a/package.json +++ b/package.json @@ -21,22 +21,22 @@ "lru-cache": "^6.0.0" }, "devDependencies": { - "c8": "^7.11.3", - "chai": "^4.3.6", + "c8": "^10.1.3", + "chai": "^4.5.0", "cross-env": "^7.0.3", - "delay": "^5.0.0", + "delay": "^6.0.0", "eslint": "^8.48.0", "eslint-config-digitalbazaar": "^5.0.1", - "eslint-plugin-jsdoc": "^46.5.0", - "eslint-plugin-unicorn": "^48.0.1", + "eslint-plugin-jsdoc": "^50.6.17", + "eslint-plugin-unicorn": "^56.0.1", "karma": "^6.3.20", "karma-chai": "^0.1.0", "karma-chrome-launcher": "^3.1.1", "karma-mocha": "^2.0.1", "karma-mocha-reporter": "^2.2.5", - "karma-sourcemap-loader": "^0.3.8", + "karma-sourcemap-loader": "^0.4.0", "karma-webpack": "^5.0.0", - "mocha": "^10.0.0", + "mocha": "^11.4.0", "mocha-lcov-reporter": "^1.3.0", "webpack": "^5.72.1" }, From 4d231569373dcbb2add1e580c101dfc8df27b08f Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Thu, 22 May 2025 12:05:59 -0400 Subject: [PATCH 2/3] Use `lru-cache@11`. --- CHANGELOG.md | 14 ++++++++++++++ lib/LruCache.js | 39 +++++++++++++++++++++++++-------------- package.json | 2 +- tests/10-api.spec.js | 21 ++++++++++++--------- 4 files changed, 52 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa17a3c..934c1e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # @digitalbazaar/lru-memoize ChangeLog +## 4.0.0 - 2025-mm-dd + +### Changed +- **BREAKING**: Use `lru-cache@11`. This replaces `lru-cache@6` which has a + number of breaking changes that impact any use of this library that + previously accessed the underlying cache interface. The main interface + of this module has only changed in that the options it accepts when + creating the cache need to now conform to v11 of `lru-cache` instead of + v6. The v6 `maxAge` option, if given, will be coerced to `ttl` to match + v11. +- **BREAKING**: The `delete()` method now returns `true` if the passed key was + removed from the cache and `false` if not, matching the v11 `delete()` + interface. Previously, `undefined` was returned in both cases. + ## 3.0.2 - 2023-08-27 ### Fixed diff --git a/lib/LruCache.js b/lib/LruCache.js index 0a58e9e..7ac6d4f 100644 --- a/lib/LruCache.js +++ b/lib/LruCache.js @@ -1,7 +1,7 @@ /*! - * Copyright (c) 2020-2023 Digital Bazaar, Inc. All rights reserved. + * Copyright (c) 2020-2025 Digital Bazaar, Inc. All rights reserved. */ -import LRU from 'lru-cache'; +import {LRUCache as LRU} from 'lru-cache'; /** * LruCache uses the npm module `lru-cache` to memoize promises. @@ -10,8 +10,15 @@ import LRU from 'lru-cache'; * @see https://en.wikipedia.org/wiki/Memoization * @param {object} cacheOptions - Options for `lru-cache`. * See the npm docs for more options. - * @param {number} [cacheOptions.max] - The max size of the cache. - * @param {number} [cacheOptions.maxAge] - The maxAge of an item in ms. + * @param {number} [cacheOptions.max=1000] - The max number of items in the + * cache. If no `max` is specified and no `maxSize` is specified, then `max` + * will default to `1000`, ensuring the cache is bounded. + * @param {number} [cacheOptions.maxSize] - An optional max size for the cache; + * if provided, then the size of each item must be calculated via a provided + * `sizeCalculation` function. + * @param {number} [cacheOptions.ttl] - The time-to-live of an item in ms. + * @param {Function} [cacheOptions.sizeCalculation] - A function that will + * calculate the size of an item; see lru-cache documentation. * @param {boolean} [cacheOptions.updateAgeOnGet=false] - When using * time-expiring entries with maxAge, setting this to true will make * each entry's effective time update to the current time whenever it is @@ -23,9 +30,13 @@ import LRU from 'lru-cache'; * @returns {LruCache} The class. */ export class LruCache { - constructor(cacheOptions = {}) { - this.options = cacheOptions; - this.cache = new LRU(cacheOptions); + constructor(cacheOptions = {max: 1000}) { + if(cacheOptions.maxAge !== undefined) { + throw new TypeError( + '"cacheOptions.maxAge" is no longer supported; use "ttl" instead.'); + } + this.options = {max: 1000, ...cacheOptions}; + this.cache = new LRU(this.options); } /** @@ -36,7 +47,7 @@ export class LruCache { * @returns {undefined} */ delete(key) { - return this.cache.del(key); + return this.cache.delete(key); } /** @@ -61,24 +72,24 @@ export class LruCache { // cache miss const cacheOptions = {...this.options, ...options}; promise = fn(); - // this version only supports `maxAge` and `disposeOnSettle`; a future - // version will support more cache options - const {maxAge} = cacheOptions; - this.cache.set(key, promise, maxAge); + // this version only supports passing `ttl` through to the underlying + // lru-cache instance; a future version will support more cache options + const {ttl} = cacheOptions; + this.cache.set(key, promise, {ttl}); try { await promise; } catch(e) { // if the promise rejects, delete it if the cache entry hasn't changed if(promise === this.cache.get(key)) { - this.cache.del(key); + this.cache.delete(key); } throw e; } // dispose promise once settled (provided the cache entry hasn't changed) if(cacheOptions.disposeOnSettle && promise === this.cache.get(key)) { - this.cache.del(key); + this.cache.delete(key); } return promise; diff --git a/package.json b/package.json index 70a34d6..4799b70 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "lint": "eslint ." }, "dependencies": { - "lru-cache": "^6.0.0" + "lru-cache": "^11.1.0" }, "devDependencies": { "c8": "^10.1.3", diff --git a/tests/10-api.spec.js b/tests/10-api.spec.js index bdf28d6..7576a82 100644 --- a/tests/10-api.spec.js +++ b/tests/10-api.spec.js @@ -1,5 +1,5 @@ /*! - * Copyright (c) 2020-2023 Digital Bazaar, Inc. All rights reserved. + * Copyright (c) 2020-2025 Digital Bazaar, Inc. All rights reserved. */ import delay from 'delay'; import {LruCache} from '../lib/index.js'; @@ -7,7 +7,7 @@ import {LruCache} from '../lib/index.js'; describe('API', () => { it('has the proper exports', async () => { should.exist(LruCache); - const m = new LruCache(); + const m = new LruCache({ttl: 10}); should.exist(m); m.memoize.should.be.a('function'); }); @@ -40,7 +40,7 @@ describe('API', () => { result2.should.eql(result); executedTestFn.should.be.false; }); - it('adds an item to the cache w/custom `maxAge`', async () => { + it('adds an item to the cache w/custom `ttl`', async () => { const m = new LruCache(); let executedTestFn = false; const testFn = async () => { @@ -49,11 +49,11 @@ describe('API', () => { executedTestFn = true; return {success: true, timestamp: Date.now()}; }; - const maxAge = 200; + const ttl = 200; const result = await m.memoize({ key: 'test1', fn: testFn, - options: {maxAge} + options: {ttl} }); should.exist(result); result.success.should.be.true; @@ -71,7 +71,7 @@ describe('API', () => { executedTestFn.should.be.false; // third test should not find the expired result - await delay(maxAge + 1); + await delay(ttl + 1); executedTestFn = false; const result3 = await m.memoize({ key: 'test1', @@ -114,9 +114,12 @@ describe('API', () => { executedTestFn.should.be.false; // delete the cached promise - const r = m.delete('test1'); - // always returns undefined - should.not.exist(r); + const deleted1 = m.delete('test1'); + // should return `true` because `test1` was removed + deleted1.should.equal(true); + // should now return `false` because `test1` was already removed + const deleted2 = m.delete('test1'); + deleted2.should.equal(false); // after deleting the cache entry, testFn should be executed producing // a result with a new timestamp From d8f354b63a601739a73389e9ac066e0b2ab1f2a5 Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Thu, 22 May 2025 12:08:13 -0400 Subject: [PATCH 3/3] Update github actions. --- .github/workflows/main.yml | 89 +++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b635438..dae7706 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,66 +8,67 @@ jobs: timeout-minutes: 10 strategy: matrix: - node-version: [20.x] + node-version: [22.x] steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - name: Run eslint - run: npm run lint + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - name: Run eslint + run: npm run lint test-node: - needs: lint + needs: [lint] runs-on: ubuntu-latest timeout-minutes: 10 strategy: matrix: - node-version: [16.x, 18.x, 20.x] + node-version: [20.x, 22.x] steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - name: Run test with Node.js ${{ matrix.node-version }} - run: npm run test-node + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - name: Run tests with Node.js ${{ matrix.node-version }} + run: npm run test-node test-karma: - needs: lint + needs: [lint] runs-on: ubuntu-latest timeout-minutes: 10 strategy: matrix: - node-version: [20.x] + node-version: [22.x] steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - name: Run karma tests - run: npm run test-karma + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - name: Run karma tests + run: npm run test-karma coverage: needs: [test-node, test-karma] - runs-on: ubuntu-latest timeout-minutes: 10 + runs-on: ubuntu-latest strategy: matrix: - node-version: [20.x] + node-version: [22.x] steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - name: Generate coverage report - run: npm run coverage-ci - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v2 - with: - file: ./coverage/lcov.info - fail_ci_if_error: true + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - name: Generate coverage report + run: npm run coverage-ci + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + file: ./coverage/lcov.info + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }}