cacheable-request
Advanced tools
Comparing version 8.3.1 to 9.0.0
@@ -1,6 +0,10 @@ | ||
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
/// <reference types="node" resolution-mode="require"/> | ||
/// <reference types="node" resolution-mode="require"/> | ||
import EventEmitter from 'node:events'; | ||
declare const CacheableRequest: { | ||
(request: Function, cacheAdapter?: any): (options: any, cb?: ((response: {}) => void) | undefined) => EventEmitter; | ||
(request: Function, cacheAdapter?: any): (options: any, cb?: ((response: Record<string, unknown>) => void) | undefined) => EventEmitter; | ||
addHook(name: string, fn: Function): void; | ||
removeHook(name: string): boolean; | ||
getHook(name: string): any; | ||
runHook(name: string, response: any): Promise<any>; | ||
RequestError: { | ||
@@ -7,0 +11,0 @@ new (error: any): { |
@@ -1,47 +0,20 @@ | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const node_events_1 = __importDefault(require("node:events")); | ||
const node_url_1 = __importDefault(require("node:url")); | ||
const node_crypto_1 = __importDefault(require("node:crypto")); | ||
const node_stream_1 = __importStar(require("node:stream")); | ||
const normalize_url_1 = __importDefault(require("normalize-url")); | ||
const get_stream_1 = __importDefault(require("get-stream")); | ||
const http_cache_semantics_1 = __importDefault(require("http-cache-semantics")); | ||
const responselike_1 = __importDefault(require("responselike")); | ||
const keyv_1 = __importDefault(require("keyv")); | ||
const mimic_response_1 = __importDefault(require("mimic-response")); | ||
const { Readable } = node_stream_1.default; | ||
import EventEmitter from 'node:events'; | ||
import urlLib from 'node:url'; | ||
import crypto from 'node:crypto'; | ||
import stream, { PassThrough as PassThroughStream } from 'node:stream'; | ||
import normalizeUrl from 'normalize-url'; | ||
import getStream from 'get-stream'; | ||
import CachePolicy from 'http-cache-semantics'; | ||
import Response from 'responselike'; | ||
import Keyv from 'keyv'; | ||
import mimicResponse from 'mimic-response'; | ||
const hooks = new Map(); | ||
// eslint-disable-next-line @typescript-eslint/naming-convention | ||
const CacheableRequest = function (request, cacheAdapter) { | ||
let cache = {}; | ||
if (cacheAdapter instanceof keyv_1.default) { | ||
if (cacheAdapter instanceof Keyv) { | ||
cache = cacheAdapter; | ||
} | ||
else { | ||
cache = new keyv_1.default({ | ||
cache = new Keyv({ | ||
uri: (typeof cacheAdapter === 'string' && cacheAdapter) || '', | ||
@@ -58,7 +31,7 @@ store: typeof cacheAdapter !== 'string' && cacheAdapter, | ||
if (typeof options === 'string') { | ||
url = normalizeUrlObject(node_url_1.default.parse(options)); | ||
url = normalizeUrlObject(urlLib.parse(options)); | ||
options = {}; | ||
} | ||
else if (options instanceof node_url_1.default.URL) { | ||
url = normalizeUrlObject(node_url_1.default.parse(options.toString())); | ||
else if (options instanceof urlLib.URL) { | ||
url = normalizeUrlObject(urlLib.parse(options.toString())); | ||
options = {}; | ||
@@ -83,4 +56,4 @@ } | ||
options.headers = Object.fromEntries(Object.entries(options.headers).map(([key, value]) => [key.toLowerCase(), value])); | ||
const ee = new node_events_1.default(); | ||
const normalizedUrlString = (0, normalize_url_1.default)(node_url_1.default.format(url), { | ||
const ee = new EventEmitter(); | ||
const normalizedUrlString = normalizeUrl(urlLib.format(url), { | ||
stripWWW: false, | ||
@@ -95,3 +68,3 @@ removeTrailingSlash: false, | ||
if (options.body && ['POST', 'PATCH', 'PUT'].includes(options.method)) { | ||
if (options.body instanceof Readable) { | ||
if (options.body instanceof stream.Readable) { | ||
// Streamed bodies should completely skip the cache because they may | ||
@@ -103,3 +76,3 @@ // or may not be hashable and in either case the stream would need to | ||
else { | ||
key += `:${node_crypto_1.default.createHash('md5').update(options.body).digest('hex')}`; | ||
key += `:${crypto.createHash('md5').update(options.body).digest('hex')}`; | ||
} | ||
@@ -122,8 +95,8 @@ } | ||
const handler = (response) => { | ||
if (revalidate && !options_.forceRefresh) { | ||
if (revalidate) { | ||
response.status = response.statusCode; | ||
const revalidatedPolicy = http_cache_semantics_1.default.fromObject(revalidate.cachePolicy).revalidatedPolicy(options_, response); | ||
const revalidatedPolicy = CachePolicy.fromObject(revalidate.cachePolicy).revalidatedPolicy(options_, response); | ||
if (!revalidatedPolicy.modified) { | ||
const headers = revalidatedPolicy.policy.responseHeaders(); | ||
response = new responselike_1.default(revalidate.statusCode, headers, revalidate.body, revalidate.url); | ||
const headers = convertHeaders(revalidatedPolicy.policy.responseHeaders()); | ||
response = new Response({ statusCode: revalidate.statusCode, headers, body: revalidate.body, url: revalidate.url }); | ||
response.cachePolicy = revalidatedPolicy.policy; | ||
@@ -134,3 +107,3 @@ response.fromCache = true; | ||
if (!response.fromCache) { | ||
response.cachePolicy = new http_cache_semantics_1.default(options_, response, options_); | ||
response.cachePolicy = new CachePolicy(options_, response, options_); | ||
response.fromCache = false; | ||
@@ -143,3 +116,3 @@ } | ||
try { | ||
const bodyPromise = get_stream_1.default.buffer(response); | ||
const bodyPromise = getStream.buffer(response); | ||
await Promise.race([ | ||
@@ -160,2 +133,9 @@ requestErrorPromise, | ||
} | ||
if (hooks.size > 0) { | ||
/* eslint-disable no-await-in-loop */ | ||
for (const key_ of hooks.keys()) { | ||
value.body = await CacheableRequest.runHook(key_, value.body); | ||
} | ||
/* eslint-enable no-await-in-loop */ | ||
} | ||
await cache.set(key, value, ttl); | ||
@@ -197,10 +177,10 @@ } | ||
const cacheEntry = options_.cache ? await cache.get(key) : undefined; | ||
if (typeof cacheEntry === 'undefined') { | ||
if (typeof cacheEntry === 'undefined' && !options_.forceRefresh) { | ||
makeRequest(options_); | ||
return; | ||
} | ||
const policy = http_cache_semantics_1.default.fromObject(cacheEntry.cachePolicy); | ||
const policy = CachePolicy.fromObject(cacheEntry.cachePolicy); | ||
if (policy.satisfiesWithoutRevalidation(options_) && !options_.forceRefresh) { | ||
const headers = policy.responseHeaders(); | ||
const response = new responselike_1.default(cacheEntry.statusCode, headers, cacheEntry.body, cacheEntry.url); | ||
const headers = convertHeaders(policy.responseHeaders()); | ||
const response = new Response({ statusCode: cacheEntry.statusCode, headers, body: cacheEntry.body, url: cacheEntry.url }); | ||
response.cachePolicy = policy; | ||
@@ -213,2 +193,7 @@ response.fromCache = true; | ||
} | ||
else if (policy.satisfiesWithoutRevalidation(options_) && Date.now() >= policy.timeToLive() && options_.forceRefresh) { | ||
await cache.delete(key); | ||
options_.headers = policy.revalidationHeaders(options_); | ||
makeRequest(options_); | ||
} | ||
else { | ||
@@ -236,5 +221,18 @@ revalidate = cacheEntry; | ||
} | ||
CacheableRequest.addHook = (name, fn) => { | ||
if (!hooks.has(name)) { | ||
hooks.set(name, fn); | ||
} | ||
}; | ||
CacheableRequest.removeHook = (name) => hooks.delete(name); | ||
CacheableRequest.getHook = (name) => hooks.get(name); | ||
CacheableRequest.runHook = async (name, response) => { | ||
if (!response) { | ||
return new CacheableRequest.CacheError(new Error('runHooks requires response argument')); | ||
} | ||
return hooks.get(name)(response); | ||
}; | ||
function cloneResponse(response) { | ||
const clone = new node_stream_1.PassThrough({ autoDestroy: false }); | ||
(0, mimic_response_1.default)(response, clone); | ||
const clone = new PassThroughStream({ autoDestroy: false }); | ||
mimicResponse(response, clone); | ||
return response.pipe(clone); | ||
@@ -266,2 +264,9 @@ } | ||
} | ||
function convertHeaders(headers) { | ||
const result = {}; | ||
for (const name of Object.keys(headers)) { | ||
result[name.toLowerCase()] = headers[name]; | ||
} | ||
return result; | ||
} | ||
CacheableRequest.RequestError = class extends Error { | ||
@@ -281,3 +286,3 @@ constructor(error) { | ||
}; | ||
exports.default = CacheableRequest; | ||
export default CacheableRequest; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "cacheable-request", | ||
"version": "8.3.1", | ||
"version": "9.0.0", | ||
"description": "Wrap native HTTP requests with RFC compliant cache support", | ||
@@ -8,3 +8,4 @@ "license": "MIT", | ||
"author": "Jared Wray <me@jaredwray.com> (http://jaredwray.com)", | ||
"main": "./dist/index.js", | ||
"type": "module", | ||
"exports": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
@@ -15,3 +16,3 @@ "engines": { | ||
"scripts": { | ||
"test": "xo && jest --coverage", | ||
"test": "xo && NODE_OPTIONS=--experimental-vm-modules jest --coverage ", | ||
"build": "tsc --project tsconfig.build.json", | ||
@@ -38,11 +39,9 @@ "clean": "rm -rf node_modules && rm -rf ./coverage && rm -rf ./package-lock.json && rm -rf ./test/testdb.sqlite && rm -rf ./dist" | ||
"http-cache-semantics": "^4.1.0", | ||
"keyv": "^4.3.2", | ||
"mimic-response": "^3.1.0", | ||
"normalize-url": "^6.0.3", | ||
"responselike": "^2.0.0" | ||
"keyv": "^4.3.3", | ||
"mimic-response": "^4.0.0", | ||
"normalize-url": "^7.0.3", | ||
"responselike": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "^7.18.5", | ||
"@babel/preset-env": "^7.18.2", | ||
"@keyv/sqlite": "^3.5.2", | ||
"@keyv/sqlite": "^3.5.3", | ||
"@types/create-test-server": "^3.0.1", | ||
@@ -52,4 +51,4 @@ "@types/delay": "^3.1.0", | ||
"@types/http-cache-semantics": "^4.0.1", | ||
"@types/jest": "^28.1.3", | ||
"@types/node": "^18.0.0", | ||
"@types/jest": "^28.1.6", | ||
"@types/node": "^18.7.3", | ||
"@types/pify": "^5.0.1", | ||
@@ -59,16 +58,22 @@ "@types/responselike": "^1.0.0", | ||
"create-test-server": "3.0.1", | ||
"eslint-plugin-jest": "^26.5.3", | ||
"delay": "^5.0.0", | ||
"jest": "^28.1.1", | ||
"pify": "^5.0.0", | ||
"sqlite3": "^5.0.8", | ||
"ts-jest": "^28.0.5", | ||
"ts-node": "^10.8.1", | ||
"eslint-plugin-jest": "^26.8.2", | ||
"jest": "^28.1.3", | ||
"pify": "^6.0.0", | ||
"sqlite3": "^5.0.11", | ||
"ts-jest": "^28.0.7", | ||
"ts-jest-resolver": "^2.0.0", | ||
"ts-node": "^10.9.1", | ||
"typescript": "^4.7.4", | ||
"xo": "^0.50.0" | ||
"xo": "^0.51.0" | ||
}, | ||
"jest": { | ||
"extensionsToTreatAsEsm": [ | ||
".ts" | ||
], | ||
"resolver": "ts-jest-resolver", | ||
"globals": { | ||
"ts-jest": { | ||
"tsconfig": "./tsconfig.json" | ||
"tsconfig": "./tsconfig.build.json", | ||
"useESM": true | ||
} | ||
@@ -96,4 +101,2 @@ }, | ||
"rules": { | ||
"@typescript-eslint/object-curly-spacing": 0, | ||
"@typescript-eslint/naming-convention": 0, | ||
"@typescript-eslint/no-unsafe-assignment": 0, | ||
@@ -105,8 +108,5 @@ "@typescript-eslint/no-unsafe-call": 0, | ||
"new-cap": 0, | ||
"@typescript-eslint/no-empty-function": 0, | ||
"@typescript-eslint/restrict-plus-operands": 0, | ||
"@typescript-eslint/no-floating-promises": 0, | ||
"@typescript-eslint/await-thenable": 0 | ||
"@typescript-eslint/restrict-plus-operands": 0 | ||
} | ||
} | ||
} |
@@ -14,2 +14,7 @@ # cacheable-request | ||
## ESM Support in version 9 and higher. | ||
We are now using pure esm support in our package. If you need to use commonjs you can use v8 or lower. To learn more about using ESM please read this from `sindresorhus`: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c | ||
## Features | ||
@@ -203,2 +208,23 @@ | ||
## Add Hooks | ||
The hook will pre compute response right before saving it in cache. You can include many hooks and it will run in order you add hook on response object. | ||
```js | ||
CacheableRequest.addHook('response', async (response: any) => { | ||
const buffer = await pm(gunzip)(response); | ||
return buffer.toString(); | ||
}); | ||
const cacheableRequest = CacheableRequest(request, cache); | ||
``` | ||
## Remove Hooks | ||
You can also remove hook by using below | ||
```js | ||
CacheableRequest.removeHook('response'); | ||
``` | ||
**Note:** Database connection errors are emitted here, however `cacheable-request` will attempt to re-request the resource and bypass the cache on a connection error. Therefore a database connection error doesn't necessarily mean the request won't be fulfilled. | ||
@@ -205,0 +231,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
35240
21
305
235
1
Yes
+ Addedlowercase-keys@3.0.0(transitive)
+ Addedmimic-response@4.0.0(transitive)
+ Addednormalize-url@7.2.0(transitive)
+ Addedresponselike@3.0.0(transitive)
- Removedlowercase-keys@2.0.0(transitive)
- Removedmimic-response@3.1.0(transitive)
- Removednormalize-url@6.1.0(transitive)
- Removedresponselike@2.0.1(transitive)
Updatedkeyv@^4.3.3
Updatedmimic-response@^4.0.0
Updatednormalize-url@^7.0.3
Updatedresponselike@^3.0.0