Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
0c8c0d4520 | |||
2f14847a01 | |||
e3ecf3b981 | |||
05a146fef1 | |||
dba1e2fbd3 | |||
ad572f3f29 | |||
6023b56164 | |||
675ed9bbf2 | |||
998d18ae84 | |||
d5ab4d4b18 | |||
847b330489 | |||
dae57d2604 | |||
6cd42272c4 | |||
e8ad8da0bb | |||
45ca35f057 | |||
af1e977620 | |||
ef5939f571 | |||
e356e485b6 | |||
a11a135234 | |||
524308aa61 | |||
bffaa2aadc | |||
b8da556091 |
@ -1,4 +1,4 @@
|
|||||||
module.exports = {
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"commonjs": true,
|
"commonjs": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
@ -15,5 +15,7 @@ module.exports = {
|
|||||||
"ecmaVersion": 2018
|
"ecmaVersion": 2018
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"import/extensions": "off",
|
||||||
|
"import/prefer-default-export": "off"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -105,3 +105,5 @@ dist
|
|||||||
|
|
||||||
# Other
|
# Other
|
||||||
.cache
|
.cache
|
||||||
|
|
||||||
|
commonjs
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
.eslintrc.js
|
.eslintrc.json
|
||||||
test
|
test
|
||||||
.cache
|
.cache
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
128
README.md
128
README.md
@ -22,86 +22,35 @@ The next time you `fetch('http://google.com')`, the response will be returned fr
|
|||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
This module aims to expose the same API as `node-fetch` does for the most common use cases, but may not support some of the less common functions, properties, and use cases.
|
This module's fetch function has almost the exact same API as node-fetch, and you should consult [the node-fetch documentation](https://www.npmjs.com/package/node-fetch) for how to use it.
|
||||||
|
|
||||||
### const fetch = require('node-fetch-cache');
|
This module just adds one extra function to the response object:
|
||||||
|
|
||||||
Load the module.
|
### res.ejectFromCache(): Promise\<void\>
|
||||||
|
|
||||||
### await fetch(resource [, init])
|
This function can be used to eject the response from the cache, so that the next request will perform a true HTTP request rather than returning a cached response.
|
||||||
|
|
||||||
Same arguments as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
This module caches ALL responses, even those with 4xx and 5xx response statuses. You can use this function to uncache such responses if desired. For example:
|
||||||
|
|
||||||
Returns a **CachedResponse**.
|
```js
|
||||||
|
const fetch = require('node-fetch-cache');
|
||||||
|
|
||||||
### await CachedResponse.ejectFromCache()
|
fetch('http://google.com')
|
||||||
|
.then(async response => {
|
||||||
Eject the response from the cache, so that the next request will perform a true HTTP request rather than returning a cached response.
|
if (!response.ok) {
|
||||||
|
await response.ejectFromCache();
|
||||||
Keep in mind that this module caches **all** responses, even if they return errors. You might want to use this function in certain cases like receiving a 5xx response status, so that you can retry requests.
|
throw new Error('Non-okay response from google.com');
|
||||||
|
} else {
|
||||||
### await CachedResponse.text()
|
return response.text();
|
||||||
|
}
|
||||||
Returns the body as a string, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
}).then(text => console.log(text));
|
||||||
|
```
|
||||||
### await CachedResponse.json()
|
|
||||||
|
|
||||||
Returns the body as a JavaScript object, parsed from JSON, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### await CachedResponse.buffer()
|
|
||||||
|
|
||||||
Returns the body as a Buffer, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### CachedResponse.status
|
|
||||||
|
|
||||||
Returns the HTTP status code of the response, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### CachedResponse.statusText
|
|
||||||
|
|
||||||
Returns a text represention of the response status, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### CachedResponse.ok
|
|
||||||
|
|
||||||
Returns true if the request returned a successful response status, false otherwise, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### CachedResponse.redirected
|
|
||||||
|
|
||||||
Returns true if the request was redirected, false otherwise, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### CachedResponse.headers
|
|
||||||
|
|
||||||
Returns a **ResponseHeaders** object representing the headers of the response, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### ResponseHeaders.entries()
|
|
||||||
|
|
||||||
Returns the raw headers as an array of `[key, value]` pairs, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### ResponseHeaders.keys()
|
|
||||||
|
|
||||||
Returns an array of all header keys, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### ResponseHeaders.values()
|
|
||||||
|
|
||||||
Returns an array of all header values, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### ResponseHeaders.get(key)
|
|
||||||
|
|
||||||
Returns the value of the header with the given key, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### ResponseHeaders.has(key)
|
|
||||||
|
|
||||||
Returns true if the headers has a value for the given key, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
### ResponseHeaders.raw
|
|
||||||
|
|
||||||
Returns the headers as an object of `{ "key": "value" }` pairs, same as [node-fetch](https://www.npmjs.com/package/node-fetch).
|
|
||||||
|
|
||||||
## Streaming
|
## Streaming
|
||||||
|
|
||||||
This module supports streams like [node-fetch](https://www.npmjs.com/package/node-fetch) does, but with a couple of caveats you should be aware of if you want to use streams.
|
This module does not support Stream request bodies, except for fs.ReadStream. And when using fs.ReadStream, the cache key is generated based only on the path of the stream, not its content. That means if you stream `/my/desktop/image.png` twice, you will get a cached response the second time, **even if the content of image.png has changed**.
|
||||||
|
|
||||||
1. Response bodies are always read into memory even if you stream them to disk. That means if you need to stream large responses that don't fit into RAM, this module may be unsuitable.
|
Streams don't quite play nice with the concept of caching based on request characteristics, because we would have to read the stream to the end to find out what's in it and hash it into a proper cache key.
|
||||||
2. When streaming a request body with fs.ReadStream, the cache key is generated based only on the path of the stream, not its content. That means if you stream `/my/desktop/image.png` twice, you will get a cached response the second time, **even if the content of image.png has changed**. This module may be unsuitable if you need to stream files in requests and the content of those files can change.
|
|
||||||
|
|
||||||
## Cache Customization
|
## Cache Customization
|
||||||
|
|
||||||
@ -139,6 +88,8 @@ const { fetchBuilder, FileSystemCache } = require('node-fetch-cache');
|
|||||||
const fetch = fetchBuilder.withCache(new FileSystemCache(options));
|
const fetch = fetchBuilder.withCache(new FileSystemCache(options));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
cacheDirectory: '/my/cache/directory/path', // Specify where to keep the cache. If undefined, '.cache' is used by default. If this directory does not exist, it will be created.
|
cacheDirectory: '/my/cache/directory/path', // Specify where to keep the cache. If undefined, '.cache' is used by default. If this directory does not exist, it will be created.
|
||||||
@ -148,42 +99,17 @@ const fetch = fetchBuilder.withCache(new FileSystemCache(options));
|
|||||||
|
|
||||||
### Provide Your Own
|
### Provide Your Own
|
||||||
|
|
||||||
You can implement a caching delegate yourself. The cache simply needs to be an object that has `set(key, value)`, `get(key)`, and `remove(key)` functions.
|
You can implement a caching delegate yourself. The cache simply needs to be an object that has `set(key, bodyStream, bodyMeta)`, `get(key)`, and `remove(key)` functions.
|
||||||
|
|
||||||
The set function must accept a key (which will be a string) and a value (which will be a JSON-serializable JS object) and store them.
|
Check the built-in [MemoryCache](https://github.com/mistval/node-fetch-cache/blob/master/src/classes/caching/memory_cache.js) and [FileSystemCache](https://github.com/mistval/node-fetch-cache/blob/master/src/classes/caching/file_system_cache.js) for examples.
|
||||||
|
|
||||||
The get function should accept a key and return whatever value was set for that key (or `undefined`/`null` if there is no value for that key).
|
The set function must accept a key (which will be a string), a body stream, and a metadata object (which will be a JSON-serializable JS object). It must store these, and then return an object with a `bodyStream` property, containing a fresh, unread stream of the body content, as well as a `metaData` property, containing the same metaData that was passed in.
|
||||||
|
|
||||||
The remove function should accept a key and remove the cached value associated with that key, if any.
|
The get function should accept a key and return undefined if no cached value is found, or else an object with a `bodyStream` property, containing a stream of the body content, as well as a `metaData` property, containing the metadata that was stored via the `set(key, bodyStream, bodyMeta)` function.
|
||||||
|
|
||||||
Both functions can be async.
|
The remove function should accept a key and remove the cached value associated with that key, if any. It is also safe for your caching delegate to remove values from the cache arbitrarily if desired (for example if you want to implement a TTL in the caching delegate).
|
||||||
|
|
||||||
It is safe to remove values from the cache arbitrarily (for example if you implement a TTL in the caching delegate).
|
All three functions may be async.
|
||||||
|
|
||||||
Example: you could make and use your own simple memory cache like this:
|
|
||||||
|
|
||||||
```js
|
|
||||||
class MyMemoryCache {
|
|
||||||
set(key, value) {
|
|
||||||
this[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get(key) {
|
|
||||||
return this[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(key) {
|
|
||||||
delete this[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchBuilder = require('node-fetch-cache');
|
|
||||||
fetch = fetchBuilder.withCache(new MyMemoryCache());
|
|
||||||
|
|
||||||
fetch('http://google.com')
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(text => console.log(text));
|
|
||||||
```
|
|
||||||
|
|
||||||
## Bugs / Help / Feature Requests / Contributing
|
## Bugs / Help / Feature Requests / Contributing
|
||||||
|
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
const FPersist = require('fpersist');
|
|
||||||
const KeyTimeout = require('./key_timeout.js');
|
|
||||||
|
|
||||||
module.exports = class FileSystemCache {
|
|
||||||
constructor(options = {}) {
|
|
||||||
this.ttl = options.ttl;
|
|
||||||
this.keyTimeout = new KeyTimeout();
|
|
||||||
|
|
||||||
const cacheDirectory = options.cacheDirectory || '.cache';
|
|
||||||
this.cache = new FPersist(cacheDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
get(key) {
|
|
||||||
return this.cache.getItem(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(key) {
|
|
||||||
this.keyTimeout.clearTimeout(key);
|
|
||||||
return this.cache.deleteItem(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
async set(key, value) {
|
|
||||||
await this.cache.setItem(key, value);
|
|
||||||
|
|
||||||
if (typeof this.ttl === 'number') {
|
|
||||||
this.keyTimeout.updateTimeout(key, this.ttl, () => this.remove(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,26 +0,0 @@
|
|||||||
const KeyTimeout = require('./key_timeout.js');
|
|
||||||
|
|
||||||
module.exports = class MemoryCache {
|
|
||||||
constructor(options = {}) {
|
|
||||||
this.ttl = options.ttl;
|
|
||||||
this.keyTimeout = new KeyTimeout();
|
|
||||||
this.cache = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
get(key) {
|
|
||||||
return this.cache[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(key) {
|
|
||||||
this.keyTimeout.clearTimeout(key);
|
|
||||||
delete this.cache[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
set(key, value) {
|
|
||||||
this.cache[key] = value;
|
|
||||||
|
|
||||||
if (typeof this.ttl === 'number') {
|
|
||||||
this.keyTimeout.updateTimeout(key, this.ttl, () => this.remove(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,33 +0,0 @@
|
|||||||
class Headers {
|
|
||||||
constructor(rawHeaders) {
|
|
||||||
this.rawHeaders = rawHeaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
entries() {
|
|
||||||
return Object.entries(this.rawHeaders)
|
|
||||||
.sort((e1, e2) => e1[0].localeCompare(e2[0]))
|
|
||||||
.map(([key, val]) => [key, val[0]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
keys() {
|
|
||||||
return this.entries().map((e) => e[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
values() {
|
|
||||||
return this.entries().map((e) => e[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
get(name) {
|
|
||||||
return (this.rawHeaders[name.toLowerCase()] || [])[0] || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
has(name) {
|
|
||||||
return !!this.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
raw() {
|
|
||||||
return this.rawHeaders;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Headers;
|
|
@ -1,47 +0,0 @@
|
|||||||
const stream = require('stream');
|
|
||||||
const Headers = require('./headers.js');
|
|
||||||
|
|
||||||
class Response {
|
|
||||||
constructor(raw, ejectSelfFromCache, fromCache) {
|
|
||||||
Object.assign(this, raw);
|
|
||||||
this.ejectSelfFromCache = ejectSelfFromCache;
|
|
||||||
this.headers = new Headers(raw.headers);
|
|
||||||
this.fromCache = fromCache;
|
|
||||||
this.bodyUsed = false;
|
|
||||||
|
|
||||||
if (this.bodyBuffer.type === 'Buffer') {
|
|
||||||
this.bodyBuffer = Buffer.from(this.bodyBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get body() {
|
|
||||||
return stream.Readable.from(this.bodyBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
consumeBody() {
|
|
||||||
if (this.bodyUsed) {
|
|
||||||
throw new Error('Error: body used already');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bodyUsed = true;
|
|
||||||
return this.bodyBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
async text() {
|
|
||||||
return this.consumeBody().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
async json() {
|
|
||||||
return JSON.parse(this.consumeBody().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
async buffer() {
|
|
||||||
return this.consumeBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
ejectFromCache() {
|
|
||||||
return this.ejectSelfFromCache();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Response;
|
|
4
commonjs/wrapper.cjs
Normal file
4
commonjs/wrapper.cjs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
const mod = require('./index.cjs');
|
||||||
|
|
||||||
|
module.exports = mod.default;
|
||||||
|
Object.assign(module.exports, mod);
|
240
package-lock.json
generated
240
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-fetch-cache",
|
"name": "node-fetch-cache",
|
||||||
"version": "2.0.0",
|
"version": "3.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -427,6 +427,22 @@
|
|||||||
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
|
"integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@npmcli/move-file": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==",
|
||||||
|
"requires": {
|
||||||
|
"mkdirp": "^1.0.4",
|
||||||
|
"rimraf": "^3.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/color-name": {
|
"@types/color-name": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||||
@ -461,7 +477,6 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||||
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
|
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"clean-stack": "^2.0.0",
|
"clean-stack": "^2.0.0",
|
||||||
"indent-string": "^4.0.0"
|
"indent-string": "^4.0.0"
|
||||||
@ -587,8 +602,7 @@
|
|||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"binary-extensions": {
|
"binary-extensions": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
@ -600,7 +614,6 @@
|
|||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@ -634,6 +647,45 @@
|
|||||||
"node-releases": "^1.1.71"
|
"node-releases": "^1.1.71"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cacache": {
|
||||||
|
"version": "15.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cacache/-/cacache-15.2.0.tgz",
|
||||||
|
"integrity": "sha512-uKoJSHmnrqXgthDFx/IU6ED/5xd+NNGe+Bb+kLZy7Ku4P+BaiWEUflAKPZ7eAzsYGcsAGASJZsybXp+quEcHTw==",
|
||||||
|
"requires": {
|
||||||
|
"@npmcli/move-file": "^1.0.1",
|
||||||
|
"chownr": "^2.0.0",
|
||||||
|
"fs-minipass": "^2.0.0",
|
||||||
|
"glob": "^7.1.4",
|
||||||
|
"infer-owner": "^1.0.4",
|
||||||
|
"lru-cache": "^6.0.0",
|
||||||
|
"minipass": "^3.1.1",
|
||||||
|
"minipass-collect": "^1.0.2",
|
||||||
|
"minipass-flush": "^1.0.5",
|
||||||
|
"minipass-pipeline": "^1.2.2",
|
||||||
|
"mkdirp": "^1.0.3",
|
||||||
|
"p-map": "^4.0.0",
|
||||||
|
"promise-inflight": "^1.0.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"ssri": "^8.0.1",
|
||||||
|
"tar": "^6.0.2",
|
||||||
|
"unique-filename": "^1.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||||
|
},
|
||||||
|
"p-map": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
|
||||||
|
"requires": {
|
||||||
|
"aggregate-error": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"caching-transform": {
|
"caching-transform": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
|
||||||
@ -697,6 +749,11 @@
|
|||||||
"readdirp": "~3.5.0"
|
"readdirp": "~3.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"chownr": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
|
||||||
|
},
|
||||||
"ci-info": {
|
"ci-info": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
|
||||||
@ -706,8 +763,7 @@
|
|||||||
"clean-stack": {
|
"clean-stack": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||||
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
|
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"cli-cursor": {
|
"cli-cursor": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
@ -805,8 +861,7 @@
|
|||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"confusing-browser-globals": {
|
"confusing-browser-globals": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.9",
|
||||||
@ -1508,22 +1563,24 @@
|
|||||||
"mime-types": "^2.1.12"
|
"mime-types": "^2.1.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fpersist": {
|
|
||||||
"version": "1.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/fpersist/-/fpersist-1.0.5.tgz",
|
|
||||||
"integrity": "sha512-WXY+zZXlOo1dU+wS8rqigz5PFu7WHBDd0vcaaWcnu319bPJi/IeWipOmi1PNaHAUqFVSzp1mLpNkgX6g2uLGbQ=="
|
|
||||||
},
|
|
||||||
"fromentries": {
|
"fromentries": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
|
||||||
"integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
|
"integrity": "sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"fs-minipass": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"fsevents": {
|
"fsevents": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
@ -1566,7 +1623,6 @@
|
|||||||
"version": "7.1.6",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
"inflight": "^1.0.4",
|
"inflight": "^1.0.4",
|
||||||
@ -1810,20 +1866,22 @@
|
|||||||
"imurmurhash": {
|
"imurmurhash": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
||||||
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
|
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"indent-string": {
|
"indent-string": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="
|
||||||
"dev": true
|
},
|
||||||
|
"infer-owner": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A=="
|
||||||
},
|
},
|
||||||
"inflight": {
|
"inflight": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"once": "^1.3.0",
|
"once": "^1.3.0",
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
@ -1832,8 +1890,7 @@
|
|||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"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
|
|
||||||
},
|
},
|
||||||
"inquirer": {
|
"inquirer": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
@ -2266,6 +2323,11 @@
|
|||||||
"path-exists": "^3.0.0"
|
"path-exists": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"locko": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/locko/-/locko-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-ekhPWcejAum9WHN2ClkFA8RAUTDyYDlRRb4dSq1wCEPhIS6IMsdSKoWHl1qineCrlMEMbeD1/o2uautG4QEc7w=="
|
||||||
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
@ -2338,6 +2400,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lru-cache": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||||
|
"requires": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"make-dir": {
|
"make-dir": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
@ -2372,7 +2442,6 @@
|
|||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
@ -2383,6 +2452,47 @@
|
|||||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"minipass": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
|
||||||
|
"requires": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minipass-collect": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minipass-flush": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minipass-pipeline": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minizlib": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^3.0.0",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.5",
|
"version": "0.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||||
@ -2829,7 +2939,6 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@ -2941,8 +3050,7 @@
|
|||||||
"path-is-absolute": {
|
"path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"path-key": {
|
"path-key": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@ -3016,6 +3124,11 @@
|
|||||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"promise-inflight": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
|
||||||
|
},
|
||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
@ -3117,11 +3230,28 @@
|
|||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"glob": "^7.1.3"
|
"glob": "^7.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rollup": {
|
||||||
|
"version": "2.53.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.53.0.tgz",
|
||||||
|
"integrity": "sha512-spgrY78Toh+m0+zaOoeaayJKuzFuWy6o1PdFIBMVwRcuxT0xCOX9A5rChyKe+2ruL4lePKWUMImS4mMW1QAkmQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"run-async": {
|
"run-async": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz",
|
||||||
@ -3300,6 +3430,14 @@
|
|||||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ssri": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==",
|
||||||
|
"requires": {
|
||||||
|
"minipass": "^3.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"string-width": {
|
"string-width": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||||
@ -3439,6 +3577,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tar": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
|
||||||
|
"requires": {
|
||||||
|
"chownr": "^2.0.0",
|
||||||
|
"fs-minipass": "^2.0.0",
|
||||||
|
"minipass": "^3.0.0",
|
||||||
|
"minizlib": "^2.1.1",
|
||||||
|
"mkdirp": "^1.0.3",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"test-exclude": {
|
"test-exclude": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
|
||||||
@ -3516,6 +3674,22 @@
|
|||||||
"is-typedarray": "^1.0.0"
|
"is-typedarray": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"unique-filename": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
|
||||||
|
"requires": {
|
||||||
|
"unique-slug": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unique-slug": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
|
||||||
|
"requires": {
|
||||||
|
"imurmurhash": "^0.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"uri-js": {
|
"uri-js": {
|
||||||
"version": "4.2.2",
|
"version": "4.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||||
@ -3661,8 +3835,7 @@
|
|||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"write": {
|
"write": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
@ -3691,6 +3864,11 @@
|
|||||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
|
},
|
||||||
"yaml": {
|
"yaml": {
|
||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz",
|
||||||
|
23
package.json
23
package.json
@ -1,13 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "node-fetch-cache",
|
"name": "node-fetch-cache",
|
||||||
"version": "2.0.0",
|
"version": "3.0.0",
|
||||||
"description": "node-fetch with a persistent cache.",
|
"description": "node-fetch with a persistent cache.",
|
||||||
"main": "index.js",
|
"main": "src/index.js",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
"import": "./src/index.js",
|
||||||
|
"require": "./commonjs/wrapper.cjs"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha --timeout 10000 --exit",
|
"buildcjs": "rollup src/index.js --file commonjs/index.cjs --format cjs",
|
||||||
|
"test": "npm run buildcjs && mocha --timeout 10000 --exit",
|
||||||
"coverage": "nyc --reporter=lcov --reporter=text npm test",
|
"coverage": "nyc --reporter=lcov --reporter=text npm test",
|
||||||
"lint": "./node_modules/.bin/eslint .",
|
"lint": "./node_modules/.bin/eslint .",
|
||||||
"lintfix": "./node_modules/.bin/eslint . --fix"
|
"lintfix": "./node_modules/.bin/eslint . --fix",
|
||||||
|
"prepublishOnly": "npm test"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -34,11 +41,13 @@
|
|||||||
"husky": "^4.3.0",
|
"husky": "^4.3.0",
|
||||||
"mocha": "^8.2.1",
|
"mocha": "^8.2.1",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^15.1.0",
|
||||||
"rimraf": "^3.0.2"
|
"rimraf": "^3.0.2",
|
||||||
|
"rollup": "^2.53.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fpersist": "^1.0.5",
|
"cacache": "^15.2.0",
|
||||||
"node-fetch": "*"
|
"locko": "0.0.3",
|
||||||
|
"node-fetch": "2.6.1"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
84
src/classes/caching/file_system_cache.js
Normal file
84
src/classes/caching/file_system_cache.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import cacache from 'cacache';
|
||||||
|
import { Readable } from 'stream';
|
||||||
|
import { KeyTimeout } from './key_timeout.js';
|
||||||
|
|
||||||
|
function getBodyAndMetaKeys(key) {
|
||||||
|
return [`${key}body`, `${key}meta`];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileSystemCache {
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.ttl = options.ttl;
|
||||||
|
this.keyTimeout = new KeyTimeout();
|
||||||
|
this.cacheDirectory = options.cacheDirectory || '.cache';
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(key) {
|
||||||
|
const [, metaKey] = getBodyAndMetaKeys(key);
|
||||||
|
|
||||||
|
const metaInfo = await cacache.get.info(this.cacheDirectory, metaKey);
|
||||||
|
|
||||||
|
if (!metaInfo) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metaBuffer = await cacache.get.byDigest(this.cacheDirectory, metaInfo.integrity);
|
||||||
|
const metaData = JSON.parse(metaBuffer);
|
||||||
|
const { bodyStreamIntegrity, empty } = metaData;
|
||||||
|
delete metaData.bodyStreamIntegrity;
|
||||||
|
delete metaData.empty;
|
||||||
|
|
||||||
|
const bodyStream = empty
|
||||||
|
? Readable.from(Buffer.alloc(0))
|
||||||
|
: cacache.get.stream.byDigest(this.cacheDirectory, bodyStreamIntegrity);
|
||||||
|
|
||||||
|
return {
|
||||||
|
bodyStream,
|
||||||
|
metaData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(key) {
|
||||||
|
const [bodyKey, metaKey] = getBodyAndMetaKeys(key);
|
||||||
|
|
||||||
|
this.keyTimeout.clearTimeout(key);
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
cacache.rm.entry(this.cacheDirectory, bodyKey),
|
||||||
|
cacache.rm.entry(this.cacheDirectory, metaKey),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async set(key, bodyStream, metaData) {
|
||||||
|
const [bodyKey, metaKey] = getBodyAndMetaKeys(key);
|
||||||
|
const metaCopy = { ...metaData };
|
||||||
|
|
||||||
|
this.keyTimeout.clearTimeout(key);
|
||||||
|
|
||||||
|
try {
|
||||||
|
metaCopy.bodyStreamIntegrity = await new Promise((fulfill, reject) => {
|
||||||
|
bodyStream.pipe(cacache.put.stream(this.cacheDirectory, bodyKey))
|
||||||
|
.on('integrity', (i) => fulfill(i))
|
||||||
|
.on('error', (e) => {
|
||||||
|
reject(e);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
if (err.code !== 'ENODATA') {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
metaCopy.empty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metaBuffer = Buffer.from(JSON.stringify(metaCopy));
|
||||||
|
await cacache.put(this.cacheDirectory, metaKey, metaBuffer);
|
||||||
|
const cachedData = await this.get(key);
|
||||||
|
|
||||||
|
if (typeof this.ttl === 'number') {
|
||||||
|
this.keyTimeout.updateTimeout(key, this.ttl, () => this.remove(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedData;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
module.exports = class KeyTimeout {
|
export class KeyTimeout {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.timeoutHandleForKey = {};
|
this.timeoutHandleForKey = {};
|
||||||
}
|
}
|
||||||
@ -13,4 +13,4 @@ module.exports = class KeyTimeout {
|
|||||||
callback();
|
callback();
|
||||||
}, durationMs);
|
}, durationMs);
|
||||||
}
|
}
|
||||||
};
|
}
|
47
src/classes/caching/memory_cache.js
Normal file
47
src/classes/caching/memory_cache.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Readable } from 'stream';
|
||||||
|
import { KeyTimeout } from './key_timeout.js';
|
||||||
|
|
||||||
|
function streamToBuffer(stream) {
|
||||||
|
const chunks = [];
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
|
||||||
|
stream.on('error', (err) => reject(err));
|
||||||
|
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MemoryCache {
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.ttl = options.ttl;
|
||||||
|
this.keyTimeout = new KeyTimeout();
|
||||||
|
this.cache = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
const cachedValue = this.cache[key];
|
||||||
|
if (cachedValue) {
|
||||||
|
return {
|
||||||
|
bodyStream: Readable.from(cachedValue.bodyBuffer),
|
||||||
|
metaData: cachedValue.metaData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(key) {
|
||||||
|
this.keyTimeout.clearTimeout(key);
|
||||||
|
delete this.cache[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
async set(key, bodyStream, metaData) {
|
||||||
|
const bodyBuffer = await streamToBuffer(bodyStream);
|
||||||
|
this.cache[key] = { bodyBuffer, metaData };
|
||||||
|
|
||||||
|
if (typeof this.ttl === 'number') {
|
||||||
|
this.keyTimeout.updateTimeout(key, this.ttl, () => this.remove(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.get(key);
|
||||||
|
}
|
||||||
|
}
|
29
src/classes/response.js
Normal file
29
src/classes/response.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { Response } from 'node-fetch';
|
||||||
|
|
||||||
|
const responseInternalSymbol = Object.getOwnPropertySymbols(new Response())[1];
|
||||||
|
|
||||||
|
export class NFCResponse extends Response {
|
||||||
|
constructor(bodyStream, metaData, ejectFromCache, fromCache) {
|
||||||
|
super(bodyStream, metaData);
|
||||||
|
this.ejectFromCache = ejectFromCache;
|
||||||
|
this.fromCache = fromCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
static serializeMetaFromNodeFetchResponse(res) {
|
||||||
|
const metaData = {
|
||||||
|
url: res.url,
|
||||||
|
status: res.status,
|
||||||
|
statusText: res.statusText,
|
||||||
|
headers: res.headers.raw(),
|
||||||
|
size: res.size,
|
||||||
|
timeout: res.timeout,
|
||||||
|
counter: res[responseInternalSymbol].counter,
|
||||||
|
};
|
||||||
|
|
||||||
|
return metaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
ejectFromCache() {
|
||||||
|
return this.ejectSelfFromCache();
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
const fetch = require('node-fetch');
|
import fetch, { Request } from 'node-fetch';
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const { URLSearchParams } = require('url');
|
import { URLSearchParams } from 'url';
|
||||||
const crypto = require('crypto');
|
import crypto from 'crypto';
|
||||||
const Response = require('./classes/response.js');
|
import locko from 'locko';
|
||||||
const MemoryCache = require('./classes/caching/memory_cache.js');
|
import { NFCResponse } from './classes/response.js';
|
||||||
const FileSystemCache = require('./classes/caching/file_system_cache.js');
|
import { MemoryCache } from './classes/caching/memory_cache.js';
|
||||||
|
|
||||||
const CACHE_VERSION = 2;
|
const CACHE_VERSION = 3;
|
||||||
|
|
||||||
function md5(str) {
|
function md5(str) {
|
||||||
return crypto.createHash('md5').update(str).digest('hex');
|
return crypto.createHash('md5').update(str).digest('hex');
|
||||||
@ -47,57 +47,92 @@ function getBodyCacheKeyJson(body) {
|
|||||||
return body.path;
|
return body.path;
|
||||||
} if (body.toString && body.toString() === '[object FormData]') {
|
} if (body.toString && body.toString() === '[object FormData]') {
|
||||||
return getFormDataCacheKey(body);
|
return getFormDataCacheKey(body);
|
||||||
|
} if (body instanceof Buffer) {
|
||||||
|
return body.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Unsupported body type. Supported body types are: string, number, undefined, null, url.URLSearchParams, fs.ReadStream, FormData');
|
throw new Error('Unsupported body type. Supported body types are: string, number, undefined, null, url.URLSearchParams, fs.ReadStream, FormData');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRequestCacheKey(req) {
|
||||||
|
return {
|
||||||
|
cache: req.cache,
|
||||||
|
credentials: req.credentials,
|
||||||
|
destination: req.destination,
|
||||||
|
headers: req.headers,
|
||||||
|
integrity: req.integrity,
|
||||||
|
method: req.method,
|
||||||
|
redirect: req.redirect,
|
||||||
|
referrer: req.referrer,
|
||||||
|
referrerPolicy: req.referrerPolicy,
|
||||||
|
url: req.url,
|
||||||
|
body: getBodyCacheKeyJson(req.body),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getCacheKey(requestArguments) {
|
function getCacheKey(requestArguments) {
|
||||||
const resource = requestArguments[0];
|
const resource = requestArguments[0];
|
||||||
const init = requestArguments[1] || {};
|
const init = requestArguments[1] || {};
|
||||||
|
|
||||||
if (typeof resource !== 'string') {
|
const resourceCacheKeyJson = resource instanceof Request
|
||||||
throw new Error('The first argument must be a string (fetch.Request is not supported).');
|
? getRequestCacheKey(resource)
|
||||||
}
|
: { url: resource };
|
||||||
|
|
||||||
const resourceCacheKeyJson = { url: resource };
|
|
||||||
const initCacheKeyJson = { ...init };
|
const initCacheKeyJson = { ...init };
|
||||||
|
|
||||||
resourceCacheKeyJson.body = getBodyCacheKeyJson(resourceCacheKeyJson.body);
|
resourceCacheKeyJson.body = getBodyCacheKeyJson(resourceCacheKeyJson.body);
|
||||||
initCacheKeyJson.body = getBodyCacheKeyJson(initCacheKeyJson.body);
|
initCacheKeyJson.body = getBodyCacheKeyJson(initCacheKeyJson.body);
|
||||||
|
|
||||||
|
delete initCacheKeyJson.agent;
|
||||||
|
|
||||||
return md5(JSON.stringify([resourceCacheKeyJson, initCacheKeyJson, CACHE_VERSION]));
|
return md5(JSON.stringify([resourceCacheKeyJson, initCacheKeyJson, CACHE_VERSION]));
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createRawResponse(fetchRes) {
|
|
||||||
const buffer = await fetchRes.buffer();
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: fetchRes.status,
|
|
||||||
statusText: fetchRes.statusText,
|
|
||||||
type: fetchRes.type,
|
|
||||||
url: fetchRes.url,
|
|
||||||
ok: fetchRes.ok,
|
|
||||||
headers: fetchRes.headers.raw(),
|
|
||||||
redirected: fetchRes.redirected,
|
|
||||||
bodyBuffer: buffer,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getResponse(cache, requestArguments) {
|
async function getResponse(cache, requestArguments) {
|
||||||
const cacheKey = getCacheKey(requestArguments);
|
const cacheKey = getCacheKey(requestArguments);
|
||||||
const cachedValue = await cache.get(cacheKey);
|
let cachedValue = await cache.get(cacheKey);
|
||||||
|
|
||||||
const ejectSelfFromCache = () => cache.remove(cacheKey);
|
const ejectSelfFromCache = () => cache.remove(cacheKey);
|
||||||
|
|
||||||
if (cachedValue) {
|
if (cachedValue) {
|
||||||
return new Response(cachedValue, ejectSelfFromCache, true);
|
return new NFCResponse(
|
||||||
|
cachedValue.bodyStream,
|
||||||
|
cachedValue.metaData,
|
||||||
|
ejectSelfFromCache,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await locko.lock(cacheKey);
|
||||||
|
try {
|
||||||
|
cachedValue = await cache.get(cacheKey);
|
||||||
|
if (cachedValue) {
|
||||||
|
return new NFCResponse(
|
||||||
|
cachedValue.bodyStream,
|
||||||
|
cachedValue.metaData,
|
||||||
|
ejectSelfFromCache,
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchResponse = await fetch(...requestArguments);
|
const fetchResponse = await fetch(...requestArguments);
|
||||||
const rawResponse = await createRawResponse(fetchResponse);
|
const serializedMeta = NFCResponse.serializeMetaFromNodeFetchResponse(fetchResponse);
|
||||||
await cache.set(cacheKey, rawResponse);
|
|
||||||
return new Response(rawResponse, ejectSelfFromCache, false);
|
const newlyCachedData = await cache.set(
|
||||||
|
cacheKey,
|
||||||
|
fetchResponse.body,
|
||||||
|
serializedMeta,
|
||||||
|
);
|
||||||
|
|
||||||
|
return new NFCResponse(
|
||||||
|
newlyCachedData.bodyStream,
|
||||||
|
newlyCachedData.metaData,
|
||||||
|
ejectSelfFromCache,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
locko.unlock(cacheKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFetchWithCache(cache) {
|
function createFetchWithCache(cache) {
|
||||||
@ -109,7 +144,7 @@ function createFetchWithCache(cache) {
|
|||||||
|
|
||||||
const defaultFetch = createFetchWithCache(new MemoryCache());
|
const defaultFetch = createFetchWithCache(new MemoryCache());
|
||||||
|
|
||||||
module.exports = defaultFetch;
|
export default defaultFetch;
|
||||||
module.exports.fetchBuilder = defaultFetch;
|
export const fetchBuilder = defaultFetch;
|
||||||
module.exports.MemoryCache = MemoryCache;
|
export { MemoryCache } from './classes/caching/memory_cache.js';
|
||||||
module.exports.FileSystemCache = FileSystemCache;
|
export { FileSystemCache } from './classes/caching/file_system_cache.js';
|
18
test/tests.cjs
Normal file
18
test/tests.cjs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const assert = require('assert');
|
||||||
|
const fetch = require('../commonjs/wrapper.cjs');
|
||||||
|
|
||||||
|
const TWO_HUNDRED_URL = 'https://httpbin.org/status/200';
|
||||||
|
|
||||||
|
describe('Commonjs module tests', function() {
|
||||||
|
it('Can make a request', async function() {
|
||||||
|
const res = await fetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.status, 200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Has expected properties', function() {
|
||||||
|
assert(typeof fetch === 'function');
|
||||||
|
assert(fetch.MemoryCache);
|
||||||
|
assert(fetch.FileSystemCache);
|
||||||
|
assert(fetch.fetchBuilder);
|
||||||
|
});
|
||||||
|
});
|
102
test/tests.js
102
test/tests.js
@ -1,11 +1,16 @@
|
|||||||
const fs = require('fs');
|
import { dirname } from 'path';
|
||||||
const FormData = require('form-data');
|
import { fileURLToPath } from 'url';
|
||||||
const assert = require('assert');
|
import fs from 'fs';
|
||||||
const rimraf = require('rimraf');
|
import FormData from 'form-data';
|
||||||
const path = require('path');
|
import assert from 'assert';
|
||||||
const { URLSearchParams } = require('url');
|
import rimraf from 'rimraf';
|
||||||
const standardFetch = require('node-fetch');
|
import path from 'path';
|
||||||
const FetchCache = require('../index.js');
|
import { URLSearchParams } from 'url';
|
||||||
|
import standardFetch from 'node-fetch';
|
||||||
|
import FetchCache, { MemoryCache, FileSystemCache } from '../src/index.js';
|
||||||
|
import { Agent } from 'http';
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
const CACHE_PATH = path.join(__dirname, '..', '.cache');
|
const CACHE_PATH = path.join(__dirname, '..', '.cache');
|
||||||
const expectedPngBuffer = fs.readFileSync(path.join(__dirname, 'expected_png.png'));
|
const expectedPngBuffer = fs.readFileSync(path.join(__dirname, 'expected_png.png'));
|
||||||
@ -59,9 +64,11 @@ async function dualFetch(...args) {
|
|||||||
|
|
||||||
beforeEach(async function() {
|
beforeEach(async function() {
|
||||||
rimraf.sync(CACHE_PATH);
|
rimraf.sync(CACHE_PATH);
|
||||||
cachedFetch = FetchCache.withCache(new FetchCache.MemoryCache());
|
cachedFetch = FetchCache.withCache(new MemoryCache());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let res;
|
||||||
|
|
||||||
describe('Basic property tests', function() {
|
describe('Basic property tests', function() {
|
||||||
it('Has a status property', async function() {
|
it('Has a status property', async function() {
|
||||||
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
|
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
|
||||||
@ -123,22 +130,22 @@ describe('Header tests', function() {
|
|||||||
|
|
||||||
it('Gets correct header keys', async function() {
|
it('Gets correct header keys', async function() {
|
||||||
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
|
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
|
||||||
assert.deepStrictEqual(cachedFetchResponse.headers.keys(), [...standardFetchResponse.headers.keys()]);
|
assert.deepStrictEqual([...cachedFetchResponse.headers.keys()], [...standardFetchResponse.headers.keys()]);
|
||||||
|
|
||||||
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
|
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
assert.deepStrictEqual(cachedFetchResponse.headers.keys(), [...standardFetchResponse.headers.keys()]);
|
assert.deepStrictEqual([...cachedFetchResponse.headers.keys()], [...standardFetchResponse.headers.keys()]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Gets correct header values', async function() {
|
it('Gets correct header values', async function() {
|
||||||
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
|
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
removeDates(cachedFetchResponse.headers.values()),
|
removeDates([...cachedFetchResponse.headers.values()]),
|
||||||
removeDates([...standardFetchResponse.headers.values()]),
|
removeDates([...standardFetchResponse.headers.values()]),
|
||||||
);
|
);
|
||||||
|
|
||||||
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
|
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
removeDates(cachedFetchResponse.headers.values()),
|
removeDates([...cachedFetchResponse.headers.values()]),
|
||||||
removeDates([...standardFetchResponse.headers.values()]),
|
removeDates([...standardFetchResponse.headers.values()]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -146,13 +153,13 @@ describe('Header tests', function() {
|
|||||||
it('Gets correct header entries', async function() {
|
it('Gets correct header entries', async function() {
|
||||||
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
|
let { cachedFetchResponse, standardFetchResponse } = await dualFetch(TWO_HUNDRED_URL);
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
removeDates(cachedFetchResponse.headers.entries()),
|
removeDates([...cachedFetchResponse.headers.entries()]),
|
||||||
removeDates([...standardFetchResponse.headers.entries()]),
|
removeDates([...standardFetchResponse.headers.entries()]),
|
||||||
);
|
);
|
||||||
|
|
||||||
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
|
cachedFetchResponse = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
removeDates(cachedFetchResponse.headers.entries()),
|
removeDates([...cachedFetchResponse.headers.entries()]),
|
||||||
removeDates([...standardFetchResponse.headers.entries()]),
|
removeDates([...standardFetchResponse.headers.entries()]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -252,8 +259,8 @@ describe('Cache tests', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Gives different read streams different cache keys', async function() {
|
it('Gives different read streams different cache keys', async function() {
|
||||||
const s1 = fs.createReadStream(__filename);
|
const s1 = fs.createReadStream(path.join(__dirname, 'expected_png.png'));
|
||||||
const s2 = fs.createReadStream(path.join(__dirname, '..', 'index.js'));
|
const s2 = fs.createReadStream(path.join(__dirname, '..', 'src', 'index.js'));
|
||||||
|
|
||||||
res = await cachedFetch(TWO_HUNDRED_URL, post(s1));
|
res = await cachedFetch(TWO_HUNDRED_URL, post(s1));
|
||||||
assert.strictEqual(res.fromCache, false);
|
assert.strictEqual(res.fromCache, false);
|
||||||
@ -263,7 +270,7 @@ describe('Cache tests', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Gives the same read streams the same cache key', async function() {
|
it('Gives the same read streams the same cache key', async function() {
|
||||||
const s1 = fs.createReadStream(__filename);
|
const s1 = fs.createReadStream(path.join(__dirname, 'expected_png.png'));
|
||||||
|
|
||||||
res = await cachedFetch(TWO_HUNDRED_URL, post(s1));
|
res = await cachedFetch(TWO_HUNDRED_URL, post(s1));
|
||||||
assert.strictEqual(res.fromCache, false);
|
assert.strictEqual(res.fromCache, false);
|
||||||
@ -299,17 +306,24 @@ describe('Cache tests', function() {
|
|||||||
res = await cachedFetch(TWO_HUNDRED_URL, post(data2));
|
res = await cachedFetch(TWO_HUNDRED_URL, post(data2));
|
||||||
assert.strictEqual(res.fromCache, true);
|
assert.strictEqual(res.fromCache, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Does not error with custom agent with circular properties', async function() {
|
||||||
|
const agent = new Agent();
|
||||||
|
agent.agent = agent;
|
||||||
|
|
||||||
|
await cachedFetch('http://httpbin.org/status/200', { agent });
|
||||||
|
})
|
||||||
}).timeout(10000);
|
}).timeout(10000);
|
||||||
|
|
||||||
describe('Data tests', function() {
|
describe('Data tests', function() {
|
||||||
it('Does not support Request objects', async function() {
|
it('Supports request objects', async function() {
|
||||||
try {
|
let request = new standardFetch.Request('https://google.com', { body: 'test', method: 'POST' });
|
||||||
const request = new standardFetch.Request('https://google.com');
|
res = await cachedFetch(request);
|
||||||
await cachedFetch(request);
|
assert.strictEqual(res.fromCache, false);
|
||||||
throw new Error('The above line should have thrown.');
|
|
||||||
} catch (err) {
|
request = new standardFetch.Request('https://google.com', { body: 'test', method: 'POST' });
|
||||||
assert(err.message.includes('The first argument must be a string (fetch.Request is not supported).'));
|
res = await cachedFetch(request);
|
||||||
}
|
assert.strictEqual(res.fromCache, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Refuses to consume body twice', async function() {
|
it('Refuses to consume body twice', async function() {
|
||||||
@ -320,7 +334,7 @@ describe('Data tests', function() {
|
|||||||
await res.text();
|
await res.text();
|
||||||
throw new Error('The above line should have thrown.');
|
throw new Error('The above line should have thrown.');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
assert(err.message.includes('Error: body used already'));
|
assert(err.message.includes('body used already for:'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -390,11 +404,21 @@ describe('Data tests', function() {
|
|||||||
assert(err.message.includes('Unsupported body type'));
|
assert(err.message.includes('Unsupported body type'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Uses cache even if you make multiple requests at the same time', async function() {
|
||||||
|
const [res1, res2] = await Promise.all([
|
||||||
|
cachedFetch('http://httpbin.org/status/200'),
|
||||||
|
cachedFetch('http://httpbin.org/status/200'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// One should be false, the other should be true
|
||||||
|
assert(res1.fromCache !== res2.fromCache);
|
||||||
|
});
|
||||||
}).timeout(10000);
|
}).timeout(10000);
|
||||||
|
|
||||||
describe('Memory cache tests', function() {
|
describe('Memory cache tests', function() {
|
||||||
it('Supports TTL', async function() {
|
it('Supports TTL', async function() {
|
||||||
cachedFetch = FetchCache.withCache(new FetchCache.MemoryCache({ ttl: 100 }));
|
cachedFetch = FetchCache.withCache(new MemoryCache({ ttl: 100 }));
|
||||||
let res = await cachedFetch(TWO_HUNDRED_URL);
|
let res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
assert.strictEqual(res.fromCache, false);
|
assert.strictEqual(res.fromCache, false);
|
||||||
res = await cachedFetch(TWO_HUNDRED_URL);
|
res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
@ -409,7 +433,7 @@ describe('Memory cache tests', function() {
|
|||||||
|
|
||||||
describe('File system cache tests', function() {
|
describe('File system cache tests', function() {
|
||||||
it('Supports TTL', async function() {
|
it('Supports TTL', async function() {
|
||||||
cachedFetch = FetchCache.withCache(new FetchCache.FileSystemCache({ ttl: 100 }));
|
cachedFetch = FetchCache.withCache(new FileSystemCache({ ttl: 100 }));
|
||||||
let res = await cachedFetch(TWO_HUNDRED_URL);
|
let res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
assert.strictEqual(res.fromCache, false);
|
assert.strictEqual(res.fromCache, false);
|
||||||
res = await cachedFetch(TWO_HUNDRED_URL);
|
res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
@ -422,7 +446,7 @@ describe('File system cache tests', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('Can get PNG buffer body', async function() {
|
it('Can get PNG buffer body', async function() {
|
||||||
cachedFetch = FetchCache.withCache(new FetchCache.FileSystemCache());
|
cachedFetch = FetchCache.withCache(new FileSystemCache());
|
||||||
res = await cachedFetch(PNG_BODY_URL);
|
res = await cachedFetch(PNG_BODY_URL);
|
||||||
body = await res.buffer();
|
body = await res.buffer();
|
||||||
assert.strictEqual(expectedPngBuffer.equals(body), true);
|
assert.strictEqual(expectedPngBuffer.equals(body), true);
|
||||||
@ -433,4 +457,22 @@ describe('File system cache tests', function() {
|
|||||||
assert.strictEqual(expectedPngBuffer.equals(body), true);
|
assert.strictEqual(expectedPngBuffer.equals(body), true);
|
||||||
assert.strictEqual(res.fromCache, true);
|
assert.strictEqual(res.fromCache, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Can eject from cache', async function() {
|
||||||
|
cachedFetch = FetchCache.withCache(new FileSystemCache());
|
||||||
|
|
||||||
|
res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.fromCache, false);
|
||||||
|
|
||||||
|
res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.fromCache, true);
|
||||||
|
|
||||||
|
await res.ejectFromCache();
|
||||||
|
|
||||||
|
res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.fromCache, false);
|
||||||
|
|
||||||
|
res = await cachedFetch(TWO_HUNDRED_URL);
|
||||||
|
assert.strictEqual(res.fromCache, true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user