diff --git a/package-lock.json b/package-lock.json index efd961bbb4..307e735cfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1103,6 +1103,37 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/serialize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.1.tgz", + "integrity": "sha512-kKXeynfORDGPUEEl2PvTExM2zs+IldC6ZD8jPcfvI351MDNtfMlw9V9s4XZXuJNDK2qR5gbEKxRyoYx3quHUVQ==", + "dependencies": { + "buffer": "^6.0.3" + } + }, + "node_modules/@keyv/serialize/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/@lerna/create": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/@lerna/create/-/create-8.1.3.tgz", @@ -5487,7 +5518,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "devOptional": true, "funding": [ { "type": "github", @@ -5956,6 +5986,15 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/cacheable": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.8.1.tgz", + "integrity": "sha512-BQU8fJtRc02n0TVitAUMJ1EfeG32lQl3tof+8exwxb/hRMk2oGMlzDFiaPvkD6mi7XLEP7Wb3cOjhHKVmP3jnw==", + "dependencies": { + "hookified": "^1.1.0", + "keyv": "^5.0.3" + } + }, "node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -5983,6 +6022,14 @@ "node": ">=14.16" } }, + "node_modules/cacheable/node_modules/keyv": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.1.2.tgz", + "integrity": "sha512-gOWSYwaPJsBNB8r+ptJDHLgyw7C7YPeta3gDHfn9kEYMgWNGqw2m+GOvfl6V2CsaiuNfxd1LFz7pnXD5KsFU3w==", + "dependencies": { + "@keyv/serialize": "*" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -6403,7 +6450,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/config-chain": { "version": "1.1.13", @@ -9483,6 +9531,20 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-entry-cache/node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -9587,22 +9649,19 @@ } }, "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.1.tgz", + "integrity": "sha512-5erc2YgI7Xheo1JCAaRvpiwSaUQl/rtJmeHGIXD3mqpUyuGhFptBEMNy3TuuUFYyuTkjRUODFTr4EIwa6dO9eA==", "dependencies": { - "flatted": "^3.2.7", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=12.0.0" + "cacheable": "^1.8.1", + "flatted": "^3.3.1", + "hookified": "^1.4.0" } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" }, "node_modules/follow-redirects": { "version": "1.15.6", @@ -9789,7 +9848,8 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -10166,6 +10226,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -10203,6 +10264,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10212,6 +10274,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -10684,6 +10747,11 @@ "node": ">=8" } }, + "node_modules/hookified": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.4.0.tgz", + "integrity": "sha512-P2ETS9IFdI9g/qcpu0/ZYTiOPECBK4M07CQT+yAoMZr8eltlh4Lt7aizKscUS+ZzRyUpAPloFZmPNH74jQ7q6w==" + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -10803,7 +10871,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "devOptional": true, "funding": [ { "type": "github", @@ -10943,6 +11010,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -10951,7 +11019,8 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "node_modules/ini": { "version": "1.3.8", @@ -12096,7 +12165,8 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true }, "node_modules/json-parse-better-errors": { "version": "1.0.2", @@ -12212,6 +12282,7 @@ "version": "4.5.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -15812,6 +15883,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "devOptional": true, "dependencies": { "wrappy": "1" } @@ -16545,6 +16617,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -17944,6 +18017,8 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -21445,7 +21520,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "devOptional": true }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -21685,7 +21761,7 @@ "dependencies": { "content-type": "^1.0.5", "find-cache-dir": "^3.3.2", - "flat-cache": "^3.0.4", + "flat-cache": "^6.1.1", "lodash": "^4.17.15", "ssri": "^12.0.0", "timeout-signal": "^1.1.0", diff --git a/packages/node/package.json b/packages/node/package.json index f38d9114b1..429f93bde1 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -27,7 +27,7 @@ "dependencies": { "content-type": "^1.0.5", "find-cache-dir": "^3.3.2", - "flat-cache": "^3.0.4", + "flat-cache": "^6.1.1", "lodash": "^4.17.15", "ssri": "^12.0.0", "timeout-signal": "^1.1.0", diff --git a/packages/node/src/lib/get-project-base-url.ts b/packages/node/src/lib/get-project-base-url.ts index b74eb9185f..5434bf74c1 100644 --- a/packages/node/src/lib/get-project-base-url.ts +++ b/packages/node/src/lib/get-project-base-url.ts @@ -1,7 +1,7 @@ import crypto from 'crypto'; import findCacheDir from 'find-cache-dir'; -import flatCache from 'flat-cache'; +import { FlatCache } from 'flat-cache'; import timeoutSignal from 'timeout-signal'; import pkg from '../../package.json'; @@ -9,6 +9,8 @@ import config from '../config'; import { logger } from './logger'; +export const cache = new FlatCache(); + export function getCache(readmeApiKey: string) { const encodedApiKey = Buffer.from(`${readmeApiKey}:`).toString('base64'); const cacheDir = findCacheDir({ name: pkg.name, create: true }); @@ -18,16 +20,16 @@ export function getCache(readmeApiKey: string) { // automatically get refreshed when the package is updated/installed. const cacheKey = `${pkg.name}-${pkg.version}-${fsSafeApikey}`; - return flatCache.load(cacheKey, cacheDir); + return cache.load(cacheKey, cacheDir); } export async function getProjectBaseUrl(readmeApiKey: string, requestTimeout = config.timeout): Promise { const encodedApiKey = Buffer.from(`${readmeApiKey}:`).toString('base64'); - const cache = getCache(readmeApiKey); + getCache(readmeApiKey); // Does the cache exist? If it doesn't, let's fill it. If it does, let's see if it's stale. Caches should have a TTL // of 1 day. - const lastUpdated = cache.getKey('lastUpdated'); + const lastUpdated = cache.getKey('lastUpdated'); if ( lastUpdated === undefined || @@ -76,7 +78,7 @@ export async function getProjectBaseUrl(readmeApiKey: string, requestTimeout = c return baseUrl; } - const cachedBaseUrl = cache.getKey('baseUrl'); + const cachedBaseUrl = cache.getKey('baseUrl'); logger.verbose({ message: 'Retrieved baseUrl from cache.', args: { baseUrl: cachedBaseUrl } }); return cachedBaseUrl; } diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index 2fbfe9d27e..0f4988cddc 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -15,7 +15,7 @@ import { describe, afterAll, beforeEach, afterEach, expect, it } from 'vitest'; import pkg from '../package.json'; import * as readmeio from '../src'; import config from '../src/config'; -import { getCache } from '../src/lib/get-project-base-url'; +import { getCache, cache } from '../src/lib/get-project-base-url'; import { setBackoff } from '../src/lib/metrics-log'; import getReadMeApiMock from './helpers/getReadMeApiMock'; @@ -60,7 +60,7 @@ function doMetricsHeadersMatch(headers: Headers) { describe('#metrics', function () { beforeEach(function () { server.listen(); - const cache = getCache(apiKey); + getCache(apiKey); cache.setKey('lastUpdated', Date.now()); cache.setKey('baseUrl', 'https://docs.example.com'); @@ -69,7 +69,7 @@ describe('#metrics', function () { afterEach(function () { server.resetHandlers(); - getCache(apiKey).destroy(); + cache.destroy(); }); // Close server after all tests @@ -146,7 +146,7 @@ describe('#metrics', function () { app = express(); app.use((req, res, next) => { const logId = readmeio.log(apiKey, req, res, incomingGroup, { logger: mockLogger }); - res.setHeader('x-log-id', logId); + res.setHeader('x-log-id', logId!); return next(); }); app.get('/test', (req, res) => res.sendStatus(200)); @@ -531,7 +531,7 @@ describe('#metrics', function () { it('should fetch the `baseLogUrl` if not passed', function () { expect.assertions(1); // Invalidating the cache so we do a fetch from the API - const cache = getCache(apiKey); + getCache(apiKey); const lastUpdated = new Date(); lastUpdated.setDate(lastUpdated.getDate() - 2); cache.setKey('lastUpdated', lastUpdated.getTime()); diff --git a/packages/node/test/lib/get-project-base-url.test.ts b/packages/node/test/lib/get-project-base-url.test.ts index 08e7ad6ed1..80b49d7cba 100644 --- a/packages/node/test/lib/get-project-base-url.test.ts +++ b/packages/node/test/lib/get-project-base-url.test.ts @@ -4,7 +4,7 @@ import { describe, beforeAll, afterAll, afterEach, expect, it } from 'vitest'; import { getProjectBaseUrl } from '../../src'; import config from '../../src/config'; -import { getCache } from '../../src/lib/get-project-base-url'; +import { getCache, cache } from '../../src/lib/get-project-base-url'; import getReadMeApiMock from '../helpers/getReadMeApiMock'; const apiKey = 'mockReadMeApiKey'; @@ -15,7 +15,7 @@ const restHandlers = [getReadMeApiMock(baseLogUrl)]; const server = setupServer(...restHandlers); function hydrateCache(lastUpdated: number) { - const cache = getCache(apiKey); + getCache(apiKey); cache.setKey('lastUpdated', lastUpdated); cache.setKey('baseUrl', baseLogUrl); @@ -30,7 +30,8 @@ describe('get-project-base-url', function () { afterEach(function () { server.resetHandlers(); - getCache(apiKey).destroy(); + getCache(apiKey); + cache.destroy(); }); // Close server after all tests @@ -40,26 +41,26 @@ describe('get-project-base-url', function () { it('should not call the API for project data if the cache is fresh', async function () { await getProjectBaseUrl(apiKey, 2000); - expect(getCache(apiKey).getKey('baseUrl')).toStrictEqual(baseLogUrl); - const lastUpdated = getCache(apiKey).getKey('lastUpdated'); + expect(cache.getKey('baseUrl')).toStrictEqual(baseLogUrl); + const lastUpdated = cache.getKey('lastUpdated'); await getProjectBaseUrl(apiKey, 2000); - expect(getCache(apiKey).getKey('lastUpdated')).toStrictEqual(lastUpdated); + expect(cache.getKey('lastUpdated')).toStrictEqual(lastUpdated); }); it('should populate the cache if not present', async function () { await getProjectBaseUrl(apiKey, 2000); - expect(getCache(apiKey).getKey('baseUrl')).toStrictEqual(baseLogUrl); + expect(cache.getKey('baseUrl')).toStrictEqual(baseLogUrl); }); it('should refresh the cache if stale', async function () { // Hydrate and postdate the cache to two days ago so it'll be seen as stale. hydrateCache(Math.round(Date.now() / 1000 - 86400 * 2)); - expect(getCache(apiKey).getKey('baseUrl')).toStrictEqual(baseLogUrl); + expect(cache.getKey('baseUrl')).toStrictEqual(baseLogUrl); - const lastUpdated = getCache(apiKey).getKey('lastUpdated'); + const lastUpdated = cache.getKey('lastUpdated'); await getProjectBaseUrl(apiKey, 2000); - expect(getCache(apiKey).getKey('baseUrl')).toStrictEqual(baseLogUrl); - expect(getCache(apiKey).getKey('lastUpdated')).not.toStrictEqual(lastUpdated); + expect(cache.getKey('baseUrl')).toStrictEqual(baseLogUrl); + expect(cache.getKey('lastUpdated')).not.toStrictEqual(lastUpdated); }); it('should temporarily set baseUrl to null if the call to the ReadMe API fails for whatever reason', async function () { @@ -80,6 +81,6 @@ describe('get-project-base-url', function () { ); await getProjectBaseUrl(apiKey, 2000); - expect(getCache(apiKey).getKey('baseUrl')).toBeNull(); + expect(cache.getKey('baseUrl')).toBeNull(); }); });