Repository: dabroek/node-cache-manager-redis-store
Branch: master
Commit: 04d04cb5e5a4
Files: 13
Total size: 63.9 KB
Directory structure:
gitextract_lte6y37z/
├── .babelrc
├── .github/
│ └── workflows/
│ ├── run-coverage.yml
│ └── run-tests.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── dist/
│ ├── index.d.ts
│ └── index.js
├── index.js
├── package.json
├── rollup.config.js
└── test/
└── index.test.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"presets": [
[
"@babel/env",
{
"modules": false
}
]
],
"env": {
"test": {
"presets": [
"@babel/env"
],
"plugins": [
"@babel/plugin-transform-runtime"
]
}
}
}
================================================
FILE: .github/workflows/run-coverage.yml
================================================
# This workflow will do a clean installation of node dependencies and run code coverage across different versions of node
name: Run Code Coverage
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: 18.x
- name: Start Redis ${{ matrix.redis-version }}
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: 6
- name: Install dependencies
run: npm install
- name: Run the tests
run: npm test -- --coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
================================================
FILE: .github/workflows/run-tests.yml
================================================
# This workflow will do a clean installation of node dependencies and run tests across different versions of node
name: Run tests
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x]
redis-version: [4, 5, 6]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Start Redis ${{ matrix.redis-version }}
uses: supercharge/redis-github-action@1.4.0
with:
redis-version: ${{ matrix.redis-version }}
- name: Install dependencies
run: npm install
- name: Run the tests
run: npm run test
================================================
FILE: .gitignore
================================================
node_modules/
coverage/
.idea/
*.log
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## v3.0.1 - 17 Oct, 2022
Fixed bug where the `redisStore.del` would no longer accept an options object, which broke the multiCaching interface.
## v3.0.0 - 15 Oct, 2022
Upgraded to redis@^4
### Breaking Changes
- The store needs to be instantiated before passing it to cache-manager and can no longer be instantiated with the factory method
- Dropped support for Node.js < 16.18
## v2.0.0 - 13 Feb, 2020
Updates all outdated dependencies. Updating Jest from v20 to v25 revealed that not all tests that asserted a promise rejecting were succeeding as expected. This resulted in the breaking change mentioned below.
### Breaking Changes
- The `set` method now actually checks `isCacheableValue` before setting a value
- Dropped support for Node.js < 8.3
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 Matthijs Dabroek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
[](https://badge.fury.io/js/cache-manager-redis-store)
[](https://github.com/dabroek/node-cache-manager-redis-store/issues)
[](https://codecov.io/github/dabroek/node-cache-manager-redis-store)
Redis store for node cache manager
==================================
Redis cache store for [node-cache-manager](https://github.com/BryanDonovan/node-cache-manager).
How is this package different from `node-cache-manager-redis`?
----------------------------------------------------------------------------------
This is a **completely different version** than the earlier [node-cache-manager-redis](https://github.com/dial-once/node-cache-manager-redis). This package does not use `redis-pool` which is unnecessary and not actively maintained.
This package aims to provide **the most simple wrapper possible** by just passing the configuration to the underlying `node_redis` package.
Installation
------------
```sh
npm install cache-manager-redis-store --save
```
or
```sh
yarn add cache-manager-redis-store
```
Usage Examples
--------------
See examples below on how to implement the Redis cache store.
### Single store
```js
var cacheManager = require('cache-manager');
var redisStore = require('cache-manager-redis-store').redisStore;
var config = {
socket: {
host: 'localhost', // default value
port: 6379, // default value
},
password: 'XXXXX',
db: 0,
ttl: 600
};
var redisCache = cacheManager.caching({
store: await redisStore(config),
});
// listen for redis connection error event
var redisClient = redisCache.store.getClient();
redisClient.on('error', (error) => {
// handle error here
console.log(error);
});
var ttl = 5;
await redisCache.set('foo', 'bar', { ttl: ttl });
// You can use either a Promise...
var result = await redisCache.get('foo');
console.log(result);
// ...or a callback
redisCache.get('foo', (err, result) => {
if (err) {
// handle error here
}
console.log(result);
});
// >> 'bar'
console.log(await redisCache.del('foo'));
// >> 1
function getUser(id, cb) {
setTimeout(() => {
console.log("Returning user from slow database.");
cb(null, { id: id, name: 'Bob' });
}, 100);
}
var userId = 123;
var key = `user_${userId}`;
// Note: ttl is optional in wrap()
redisCache.wrap(key, (cb) => {
getUser(userId, cb);
}, { ttl: ttl }, (err, user) => {
console.log(user);
// Second time fetches user from redisCache
redisCache
.wrap(key, () => getUser(userId))
.then(console.log)
.catch(err => {
// handle error
});
});
```
### Multi-store
```js
var cacheManager = require('cache-manager');
var redisStore = require('cache-manager-redis-store').redisStore;
var redisCache = cacheManager.caching({ store: await redisStore({ ...config, db: 0, ttl: 600 }) });
var memoryCache = cacheManager.caching({ store: 'memory', max: 100, ttl: 60 });
var multiCache = cacheManager.multiCaching([memoryCache, redisCache]);
var userId2 = 456;
var key2 = `user_${userId2}`;
// Set value in all caches
await multiCache.set('foo2', 'bar2', { ttl: ttl });
// Fetches from highest priority cache that has the key
var result = await multiCache.get('foo2');
console.log(result);
// >> 'bar2'
// Delete from all caches
await multiCache.del('foo2');
// Note: ttl is optional in wrap
multiCache.wrap(key2, (cb) => {
getUser(userId2, cb);
}, (err, user) => {
console.log(user);
// Second time fetches user from memoryCache, since it's highest priority.
// If the data expires in the memory cache, the next fetch would pull it from
// the 'someOtherCache', and set the data in memory again.
multiCache.wrap(key2, (cb) => {
getUser(userId2, cb);
}, (err, user) => {
console.log(user);
});
});
```
Contribution
------------
Want to help improve this package? We take [pull requests](https://github.com/dabroek/node-cache-manager-redis-store/pulls).
License
-------
The `node-cache-manager-redis-store` is licensed under the MIT license.
================================================
FILE: dist/index.d.ts
================================================
import type { Store, StoreConfig } from "cache-manager";
import type { RedisClientType, RedisClientOptions } from "redis";
interface RedisStore extends Store {
name: string;
getClient: () => RedisClientType;
isCacheableValue: any;
set: (key: any, value: any, options: any, cb: any) => Promise<any>;
get: (key: any, options: any, cb: any) => Promise<any>;
del: (...args: any[]) => Promise<any>;
mset: (...args: any[]) => Promise<any>;
mget: (...args: any[]) => Promise<any>;
mdel: (...args: any[]) => Promise<any>;
reset: (cb: any) => Promise<any>;
keys: (pattern: string, cb: any) => Promise<any>;
ttl: (key: any, cb: any) => Promise<any>;
}
export function redisStore(config: RedisClientOptions & StoreConfig): Promise<RedisStore>;
================================================
FILE: dist/index.js
================================================
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var node_util = require('node:util');
var redis = require('redis');
function _regeneratorRuntime() {
_regeneratorRuntime = function () {
return exports;
};
var exports = {},
Op = Object.prototype,
hasOwn = Op.hasOwnProperty,
$Symbol = "function" == typeof Symbol ? Symbol : {},
iteratorSymbol = $Symbol.iterator || "@@iterator",
asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator",
toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
function define(obj, key, value) {
return Object.defineProperty(obj, key, {
value: value,
enumerable: !0,
configurable: !0,
writable: !0
}), obj[key];
}
try {
define({}, "");
} catch (err) {
define = function (obj, key, value) {
return obj[key] = value;
};
}
function wrap(innerFn, outerFn, self, tryLocsList) {
var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator,
generator = Object.create(protoGenerator.prototype),
context = new Context(tryLocsList || []);
return generator._invoke = function (innerFn, self, context) {
var state = "suspendedStart";
return function (method, arg) {
if ("executing" === state) throw new Error("Generator is already running");
if ("completed" === state) {
if ("throw" === method) throw arg;
return doneResult();
}
for (context.method = method, context.arg = arg;;) {
var delegate = context.delegate;
if (delegate) {
var delegateResult = maybeInvokeDelegate(delegate, context);
if (delegateResult) {
if (delegateResult === ContinueSentinel) continue;
return delegateResult;
}
}
if ("next" === context.method) context.sent = context._sent = context.arg;else if ("throw" === context.method) {
if ("suspendedStart" === state) throw state = "completed", context.arg;
context.dispatchException(context.arg);
} else "return" === context.method && context.abrupt("return", context.arg);
state = "executing";
var record = tryCatch(innerFn, self, context);
if ("normal" === record.type) {
if (state = context.done ? "completed" : "suspendedYield", record.arg === ContinueSentinel) continue;
return {
value: record.arg,
done: context.done
};
}
"throw" === record.type && (state = "completed", context.method = "throw", context.arg = record.arg);
}
};
}(innerFn, self, context), generator;
}
function tryCatch(fn, obj, arg) {
try {
return {
type: "normal",
arg: fn.call(obj, arg)
};
} catch (err) {
return {
type: "throw",
arg: err
};
}
}
exports.wrap = wrap;
var ContinueSentinel = {};
function Generator() {}
function GeneratorFunction() {}
function GeneratorFunctionPrototype() {}
var IteratorPrototype = {};
define(IteratorPrototype, iteratorSymbol, function () {
return this;
});
var getProto = Object.getPrototypeOf,
NativeIteratorPrototype = getProto && getProto(getProto(values([])));
NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol) && (IteratorPrototype = NativeIteratorPrototype);
var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype);
function defineIteratorMethods(prototype) {
["next", "throw", "return"].forEach(function (method) {
define(prototype, method, function (arg) {
return this._invoke(method, arg);
});
});
}
function AsyncIterator(generator, PromiseImpl) {
function invoke(method, arg, resolve, reject) {
var record = tryCatch(generator[method], generator, arg);
if ("throw" !== record.type) {
var result = record.arg,
value = result.value;
return value && "object" == typeof value && hasOwn.call(value, "__await") ? PromiseImpl.resolve(value.__await).then(function (value) {
invoke("next", value, resolve, reject);
}, function (err) {
invoke("throw", err, resolve, reject);
}) : PromiseImpl.resolve(value).then(function (unwrapped) {
result.value = unwrapped, resolve(result);
}, function (error) {
return invoke("throw", error, resolve, reject);
});
}
reject(record.arg);
}
var previousPromise;
this._invoke = function (method, arg) {
function callInvokeWithMethodAndArg() {
return new PromiseImpl(function (resolve, reject) {
invoke(method, arg, resolve, reject);
});
}
return previousPromise = previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg();
};
}
function maybeInvokeDelegate(delegate, context) {
var method = delegate.iterator[context.method];
if (undefined === method) {
if (context.delegate = null, "throw" === context.method) {
if (delegate.iterator.return && (context.method = "return", context.arg = undefined, maybeInvokeDelegate(delegate, context), "throw" === context.method)) return ContinueSentinel;
context.method = "throw", context.arg = new TypeError("The iterator does not provide a 'throw' method");
}
return ContinueSentinel;
}
var record = tryCatch(method, delegate.iterator, context.arg);
if ("throw" === record.type) return context.method = "throw", context.arg = record.arg, context.delegate = null, ContinueSentinel;
var info = record.arg;
return info ? info.done ? (context[delegate.resultName] = info.value, context.next = delegate.nextLoc, "return" !== context.method && (context.method = "next", context.arg = undefined), context.delegate = null, ContinueSentinel) : info : (context.method = "throw", context.arg = new TypeError("iterator result is not an object"), context.delegate = null, ContinueSentinel);
}
function pushTryEntry(locs) {
var entry = {
tryLoc: locs[0]
};
1 in locs && (entry.catchLoc = locs[1]), 2 in locs && (entry.finallyLoc = locs[2], entry.afterLoc = locs[3]), this.tryEntries.push(entry);
}
function resetTryEntry(entry) {
var record = entry.completion || {};
record.type = "normal", delete record.arg, entry.completion = record;
}
function Context(tryLocsList) {
this.tryEntries = [{
tryLoc: "root"
}], tryLocsList.forEach(pushTryEntry, this), this.reset(!0);
}
function values(iterable) {
if (iterable) {
var iteratorMethod = iterable[iteratorSymbol];
if (iteratorMethod) return iteratorMethod.call(iterable);
if ("function" == typeof iterable.next) return iterable;
if (!isNaN(iterable.length)) {
var i = -1,
next = function next() {
for (; ++i < iterable.length;) if (hasOwn.call(iterable, i)) return next.value = iterable[i], next.done = !1, next;
return next.value = undefined, next.done = !0, next;
};
return next.next = next;
}
}
return {
next: doneResult
};
}
function doneResult() {
return {
value: undefined,
done: !0
};
}
return GeneratorFunction.prototype = GeneratorFunctionPrototype, define(Gp, "constructor", GeneratorFunctionPrototype), define(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, toStringTagSymbol, "GeneratorFunction"), exports.isGeneratorFunction = function (genFun) {
var ctor = "function" == typeof genFun && genFun.constructor;
return !!ctor && (ctor === GeneratorFunction || "GeneratorFunction" === (ctor.displayName || ctor.name));
}, exports.mark = function (genFun) {
return Object.setPrototypeOf ? Object.setPrototypeOf(genFun, GeneratorFunctionPrototype) : (genFun.__proto__ = GeneratorFunctionPrototype, define(genFun, toStringTagSymbol, "GeneratorFunction")), genFun.prototype = Object.create(Gp), genFun;
}, exports.awrap = function (arg) {
return {
__await: arg
};
}, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, asyncIteratorSymbol, function () {
return this;
}), exports.AsyncIterator = AsyncIterator, exports.async = function (innerFn, outerFn, self, tryLocsList, PromiseImpl) {
void 0 === PromiseImpl && (PromiseImpl = Promise);
var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList), PromiseImpl);
return exports.isGeneratorFunction(outerFn) ? iter : iter.next().then(function (result) {
return result.done ? result.value : iter.next();
});
}, defineIteratorMethods(Gp), define(Gp, toStringTagSymbol, "Generator"), define(Gp, iteratorSymbol, function () {
return this;
}), define(Gp, "toString", function () {
return "[object Generator]";
}), exports.keys = function (object) {
var keys = [];
for (var key in object) keys.push(key);
return keys.reverse(), function next() {
for (; keys.length;) {
var key = keys.pop();
if (key in object) return next.value = key, next.done = !1, next;
}
return next.done = !0, next;
};
}, exports.values = values, Context.prototype = {
constructor: Context,
reset: function (skipTempReset) {
if (this.prev = 0, this.next = 0, this.sent = this._sent = undefined, this.done = !1, this.delegate = null, this.method = "next", this.arg = undefined, this.tryEntries.forEach(resetTryEntry), !skipTempReset) for (var name in this) "t" === name.charAt(0) && hasOwn.call(this, name) && !isNaN(+name.slice(1)) && (this[name] = undefined);
},
stop: function () {
this.done = !0;
var rootRecord = this.tryEntries[0].completion;
if ("throw" === rootRecord.type) throw rootRecord.arg;
return this.rval;
},
dispatchException: function (exception) {
if (this.done) throw exception;
var context = this;
function handle(loc, caught) {
return record.type = "throw", record.arg = exception, context.next = loc, caught && (context.method = "next", context.arg = undefined), !!caught;
}
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i],
record = entry.completion;
if ("root" === entry.tryLoc) return handle("end");
if (entry.tryLoc <= this.prev) {
var hasCatch = hasOwn.call(entry, "catchLoc"),
hasFinally = hasOwn.call(entry, "finallyLoc");
if (hasCatch && hasFinally) {
if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0);
if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc);
} else if (hasCatch) {
if (this.prev < entry.catchLoc) return handle(entry.catchLoc, !0);
} else {
if (!hasFinally) throw new Error("try statement without catch or finally");
if (this.prev < entry.finallyLoc) return handle(entry.finallyLoc);
}
}
}
},
abrupt: function (type, arg) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) {
var finallyEntry = entry;
break;
}
}
finallyEntry && ("break" === type || "continue" === type) && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc && (finallyEntry = null);
var record = finallyEntry ? finallyEntry.completion : {};
return record.type = type, record.arg = arg, finallyEntry ? (this.method = "next", this.next = finallyEntry.finallyLoc, ContinueSentinel) : this.complete(record);
},
complete: function (record, afterLoc) {
if ("throw" === record.type) throw record.arg;
return "break" === record.type || "continue" === record.type ? this.next = record.arg : "return" === record.type ? (this.rval = this.arg = record.arg, this.method = "return", this.next = "end") : "normal" === record.type && afterLoc && (this.next = afterLoc), ContinueSentinel;
},
finish: function (finallyLoc) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.finallyLoc === finallyLoc) return this.complete(entry.completion, entry.afterLoc), resetTryEntry(entry), ContinueSentinel;
}
},
catch: function (tryLoc) {
for (var i = this.tryEntries.length - 1; i >= 0; --i) {
var entry = this.tryEntries[i];
if (entry.tryLoc === tryLoc) {
var record = entry.completion;
if ("throw" === record.type) {
var thrown = record.arg;
resetTryEntry(entry);
}
return thrown;
}
}
throw new Error("illegal catch attempt");
},
delegateYield: function (iterable, resultName, nextLoc) {
return this.delegate = {
iterator: values(iterable),
resultName: resultName,
nextLoc: nextLoc
}, "next" === this.method && (this.arg = undefined), ContinueSentinel;
}
}, exports;
}
function _typeof(obj) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
}, _typeof(obj);
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _asyncToGenerator(fn) {
return function () {
var self = this,
args = arguments;
return new Promise(function (resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArrayLimit(arr, i) {
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
if (_i == null) return;
var _arr = [];
var _n = true;
var _d = false;
var _s, _e;
try {
for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _createForOfIteratorHelper(o, allowArrayLike) {
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
if (!it) {
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
if (it) o = it;
var i = 0;
var F = function () {};
return {
s: F,
n: function () {
if (i >= o.length) return {
done: true
};
return {
done: false,
value: o[i++]
};
},
e: function (e) {
throw e;
},
f: F
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var normalCompletion = true,
didErr = false,
err;
return {
s: function () {
it = it.call(o);
},
n: function () {
var step = it.next();
normalCompletion = step.done;
return step;
},
e: function (e) {
didErr = true;
err = e;
},
f: function () {
try {
if (!normalCompletion && it.return != null) it.return();
} finally {
if (didErr) throw err;
}
}
};
}
function redisStore(_x) {
return _redisStore.apply(this, arguments);
}
function _redisStore() {
_redisStore = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee10(config) {
var redisCache;
return _regeneratorRuntime().wrap(function _callee10$(_context10) {
while (1) {
switch (_context10.prev = _context10.next) {
case 0:
redisCache = redis.createClient(config);
_context10.next = 3;
return redisCache.connect();
case 3:
return _context10.abrupt("return", buildRedisStoreWithConfig(redisCache, config));
case 4:
case "end":
return _context10.stop();
}
}
}, _callee10);
}));
return _redisStore.apply(this, arguments);
}
var buildRedisStoreWithConfig = function buildRedisStoreWithConfig(redisCache, config) {
var isCacheableValue = config.isCacheableValue || function (value) {
return value !== undefined && value !== null;
};
var _set = /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(key, value, options) {
var ttl;
return _regeneratorRuntime().wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (isCacheableValue(value)) {
_context.next = 2;
break;
}
throw new Error("\"".concat(value, "\" is not a cacheable value"));
case 2:
ttl = options !== null && options !== void 0 && options.ttl || (options === null || options === void 0 ? void 0 : options.ttl) === 0 ? options.ttl : config.ttl;
if (!ttl) {
_context.next = 7;
break;
}
return _context.abrupt("return", redisCache.setEx(key, ttl, encodeValue(value)));
case 7:
return _context.abrupt("return", redisCache.set(key, encodeValue(value)));
case 8:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function set(_x2, _x3, _x4) {
return _ref.apply(this, arguments);
};
}();
var _get = /*#__PURE__*/function () {
var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(key, options) {
var val;
return _regeneratorRuntime().wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
_context2.next = 2;
return redisCache.get(key);
case 2:
val = _context2.sent;
if (!(val === null)) {
_context2.next = 5;
break;
}
return _context2.abrupt("return", null);
case 5:
return _context2.abrupt("return", options.parse !== false ? decodeValue(val) : val);
case 6:
case "end":
return _context2.stop();
}
}
}, _callee2);
}));
return function get(_x5, _x6) {
return _ref2.apply(this, arguments);
};
}();
var _del = /*#__PURE__*/function () {
var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(args) {
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
if (isObject(args.at(-1))) {
args.pop();
}
return _context3.abrupt("return", redisCache.del(args));
case 3:
case "end":
return _context3.stop();
}
}
}, _callee3);
}));
return function del(_x7) {
return _ref3.apply(this, arguments);
};
}();
var _mset = /*#__PURE__*/function () {
var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4(args) {
var options, ttl, items, multi, _iterator, _step, kv, _kv, key, value;
return _regeneratorRuntime().wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
options = {};
if (isObject(args.at(-1))) {
options = args.pop();
}
ttl = options.ttl || options.ttl === 0 ? options.ttl : config.ttl; // Zips even and odd array items into tuples
items = args.map(function (key, index) {
if (index % 2 !== 0) return null;
var value = args[index + 1];
if (!isCacheableValue(value)) {
throw new Error("\"".concat(value, "\" is not a cacheable value"));
}
return [key, encodeValue(value)];
}).filter(function (key) {
return key !== null;
});
if (!ttl) {
_context4.next = 11;
break;
}
multi = redisCache.multi();
_iterator = _createForOfIteratorHelper(items);
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
kv = _step.value;
_kv = _slicedToArray(kv, 2), key = _kv[0], value = _kv[1];
multi.setEx(key, ttl, value);
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return _context4.abrupt("return", multi.exec());
case 11:
return _context4.abrupt("return", redisCache.mSet(items));
case 12:
case "end":
return _context4.stop();
}
}
}, _callee4);
}));
return function mset(_x8) {
return _ref4.apply(this, arguments);
};
}();
var _mget = /*#__PURE__*/function () {
var _ref5 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
var options,
_len,
args,
_key,
_args5 = arguments;
return _regeneratorRuntime().wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
options = {};
for (_len = _args5.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = _args5[_key];
}
if (isObject(args.at(-1))) {
options = args.pop();
}
return _context5.abrupt("return", redisCache.mGet(args).then(function (res) {
return res.map(function (val) {
if (val === null) {
return null;
}
return options.parse !== false ? decodeValue(val) : val;
});
}));
case 4:
case "end":
return _context5.stop();
}
}
}, _callee5);
}));
return function mget() {
return _ref5.apply(this, arguments);
};
}();
var _mdel = /*#__PURE__*/function () {
var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6() {
var _len2,
args,
_key2,
_args6 = arguments;
return _regeneratorRuntime().wrap(function _callee6$(_context6) {
while (1) {
switch (_context6.prev = _context6.next) {
case 0:
for (_len2 = _args6.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = _args6[_key2];
}
if (isObject(args.at(-1))) {
args.pop();
}
if (Array.isArray(args)) {
args = args.flat();
}
return _context6.abrupt("return", redisCache.del(args));
case 5:
case "end":
return _context6.stop();
}
}
}, _callee6);
}));
return function mdel() {
return _ref6.apply(this, arguments);
};
}();
var _reset = /*#__PURE__*/function () {
var _ref7 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7() {
return _regeneratorRuntime().wrap(function _callee7$(_context7) {
while (1) {
switch (_context7.prev = _context7.next) {
case 0:
return _context7.abrupt("return", redisCache.flushDb());
case 1:
case "end":
return _context7.stop();
}
}
}, _callee7);
}));
return function reset() {
return _ref7.apply(this, arguments);
};
}();
var _keys = /*#__PURE__*/function () {
var _ref8 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee8(pattern) {
return _regeneratorRuntime().wrap(function _callee8$(_context8) {
while (1) {
switch (_context8.prev = _context8.next) {
case 0:
return _context8.abrupt("return", redisCache.keys(pattern));
case 1:
case "end":
return _context8.stop();
}
}
}, _callee8);
}));
return function keys(_x9) {
return _ref8.apply(this, arguments);
};
}();
var _ttl = /*#__PURE__*/function () {
var _ref9 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee9(key) {
return _regeneratorRuntime().wrap(function _callee9$(_context9) {
while (1) {
switch (_context9.prev = _context9.next) {
case 0:
return _context9.abrupt("return", redisCache.ttl(key));
case 1:
case "end":
return _context9.stop();
}
}
}, _callee9);
}));
return function ttl(_x10) {
return _ref9.apply(this, arguments);
};
}();
return {
name: 'redis',
getClient: function getClient() {
return redisCache;
},
isCacheableValue: isCacheableValue,
set: function set(key, value, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
options = options || {};
if (typeof cb === 'function') {
node_util.callbackify(_set)(key, value, options, cb);
} else {
return _set(key, value, options);
}
},
get: function get(key, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}
options = options || {};
if (typeof cb === 'function') {
node_util.callbackify(_get)(key, options, cb);
} else {
return _get(key, options);
}
},
del: function del() {
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
}
if (typeof args.at(-1) === 'function') {
var cb = args.pop();
node_util.callbackify(_del)(args, cb);
} else {
return _del(args);
}
},
mset: function mset() {
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
args[_key4] = arguments[_key4];
}
if (typeof args.at(-1) === 'function') {
var cb = args.pop();
node_util.callbackify(_mset)(args, cb);
} else {
return _mset(args);
}
},
mget: function mget() {
for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
args[_key5] = arguments[_key5];
}
if (typeof args.at(-1) === 'function') {
var cb = args.pop();
node_util.callbackify(function () {
return _mget.apply(void 0, args);
})(cb);
} else {
return _mget.apply(void 0, args);
}
},
mdel: function mdel() {
for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
args[_key6] = arguments[_key6];
}
if (typeof args.at(-1) === 'function') {
var cb = args.pop();
node_util.callbackify(function () {
return _mdel.apply(void 0, args);
})(cb);
} else {
return _mdel.apply(void 0, args);
}
},
reset: function reset(cb) {
if (typeof cb === 'function') {
node_util.callbackify(_reset)(cb);
} else {
return _reset();
}
},
keys: function keys() {
var pattern = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '*';
var cb = arguments.length > 1 ? arguments[1] : undefined;
if (typeof cb === 'function') {
node_util.callbackify(function () {
return _keys(pattern);
})(cb);
} else {
return _keys(pattern);
}
},
ttl: function ttl(key, cb) {
if (typeof cb === 'function') {
node_util.callbackify(function () {
return _ttl(key);
})(cb);
} else {
return _ttl(key);
}
}
};
};
function encodeValue(value) {
return JSON.stringify(value) || '"undefined"';
}
function decodeValue(val) {
return JSON.parse(val);
}
function isObject(object) {
return _typeof(object) === 'object' && !Array.isArray(object) && object !== null;
}
exports.redisStore = redisStore;
================================================
FILE: index.js
================================================
import {callbackify} from 'node:util';
import {createClient} from 'redis';
export async function redisStore(config) {
const redisCache = createClient(config);
await redisCache.connect();
return buildRedisStoreWithConfig(redisCache, config);
}
const buildRedisStoreWithConfig = (redisCache, config) => {
const isCacheableValue =
config.isCacheableValue || (value => value !== undefined && value !== null);
const set = async (key, value, options) => {
if (!isCacheableValue(value)) {
throw new Error(`"${value}" is not a cacheable value`);
}
const ttl = (options?.ttl || options?.ttl === 0) ? options.ttl : config.ttl;
if (ttl) {
return redisCache.setEx(key, ttl, encodeValue(value));
} else {
return redisCache.set(key, encodeValue(value));
}
};
const get = async (key, options) => {
const val = await redisCache.get(key);
if (val === null) {
return null;
}
return options.parse !== false ? decodeValue(val) : val;
};
const del = async (args) => {
let options = {};
if (isObject(args.at(-1))) {
options = args.pop();
}
return redisCache.del(args);
};
const mset = async (args) => {
let options = {};
if (isObject(args.at(-1))) {
options = args.pop();
}
const ttl = (options.ttl || options.ttl === 0) ? options.ttl : config.ttl;
// Zips even and odd array items into tuples
const items = args
.map((key, index) => {
if (index % 2 !== 0) return null;
const value = args[index + 1];
if (!isCacheableValue(value)) {
throw new Error(`"${value}" is not a cacheable value`);
}
return [key, encodeValue(value)];
})
.filter((key) => key !== null);
if (ttl) {
const multi = redisCache.multi();
for (const kv of items) {
const [key, value] = kv;
multi.setEx(key, ttl, value);
}
return multi.exec();
} else {
return redisCache.mSet(items);
}
};
const mget = async (...args) => {
let options = {};
if (isObject(args.at(-1))) {
options = args.pop();
}
return redisCache
.mGet(args)
.then((res) =>
res.map((val) => {
if (val === null) {
return null;
}
return options.parse !== false ? decodeValue(val) : val;
}),
);
};
const mdel = async (...args) => {
let options = {};
if (isObject(args.at(-1))) {
options = args.pop();
}
if (Array.isArray(args)) {
args = args.flat();
}
return redisCache.del(args);
};
const reset = async () => {
return redisCache.flushDb();
};
const keys = async (pattern) => {
return redisCache.keys(pattern);
};
const ttl = async (key) => {
return redisCache.ttl(key);
};
return {
name: 'redis',
getClient: () => redisCache,
isCacheableValue,
set: (key, value, options, cb) => {
if (typeof options === 'function') {
cb = options;
options = {};
}
options = options || {};
if (typeof cb === 'function') {
callbackify(set)(key, value, options, cb);
} else {
return set(key, value, options);
}
},
get: (key, options, cb) => {
if (typeof options === 'function') {
cb = options;
options = {};
}
options = options || {};
if (typeof cb === 'function') {
callbackify(get)(key, options, cb);
} else {
return get(key, options);
}
},
del: (...args) => {
if (typeof args.at(-1) === 'function') {
const cb = args.pop();
callbackify(del)(args, cb);
} else {
return del(args);
}
},
mset: (...args) => {
if (typeof args.at(-1) === 'function') {
const cb = args.pop();
callbackify(mset)(args, cb);
} else {
return mset(args);
}
},
mget: (...args) => {
if (typeof args.at(-1) === 'function') {
const cb = args.pop();
callbackify(() => mget(...args))(cb);
} else {
return mget(...args);
}
},
mdel: (...args) => {
if (typeof args.at(-1) === 'function') {
const cb = args.pop();
callbackify(() => mdel(...args))(cb);
} else {
return mdel(...args);
}
},
reset: (cb) => {
if (typeof cb === 'function') {
callbackify(reset)(cb);
} else {
return reset();
}
},
keys: (pattern = '*', cb) => {
if (typeof cb === 'function') {
callbackify(() => keys(pattern))(cb);
} else {
return keys(pattern);
}
},
ttl: (key, cb) => {
if (typeof cb === 'function') {
callbackify(() => ttl(key))(cb);
} else {
return ttl(key);
}
},
};
};
function encodeValue(value) {
return JSON.stringify(value) || '"undefined"';
}
function decodeValue(val) {
return JSON.parse(val);
}
function isObject(object) {
return typeof object === 'object'
&& !Array.isArray(object)
&& object !== null;
}
================================================
FILE: package.json
================================================
{
"name": "cache-manager-redis-store",
"author": "Matthijs Dabroek <dabroek@gmail.com>",
"description": "Redis store for node-cache-manager",
"version": "3.0.1",
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/dabroek/node-cache-manager-redis-store.git"
},
"scripts": {
"prepare": "npm prune",
"test": "jest --forceExit",
"test-cov": "jest --forceExit --coverage",
"build": "rollup -c"
},
"dependencies": {
"redis": "^4.3.1"
},
"devDependencies": {
"@babel/cli": "^7.19.3",
"@babel/core": "^7.19.3",
"@babel/plugin-transform-runtime": "^7.19.1",
"@babel/preset-env": "^7.19.4",
"@babel/runtime": "^7.19.4",
"@rollup/plugin-babel": "^6.0.0",
"@types/cache-manager": "^4.0.2",
"babel-jest": "^29.2.0",
"cache-manager": "^4.1.0",
"jest": "^29.2.0",
"rollup": "^2.79.1"
},
"engines": {
"node": ">= 16.18.0"
}
}
================================================
FILE: rollup.config.js
================================================
import babel from '@rollup/plugin-babel';
export default {
input: 'index.js',
output: {
format: 'cjs',
file: 'dist/index.js'
},
plugins: [
babel({
exclude: 'node_modules/**'
}),
],
};
================================================
FILE: test/index.test.js
================================================
import cacheManager from 'cache-manager';
import {redisStore} from '../index';
let redisCache;
let customRedisCache;
const config = {
socket: {
host: '127.0.0.1',
port: 6379
},
password: undefined,
db: 0,
ttl: 5,
};
beforeEach(async () => {
redisCache = cacheManager.caching({
store: await redisStore(config),
});
await redisCache.reset();
const customConfig = {
...config,
isCacheableValue: (val) => {
if (val === undefined) { // allow undefined
return true;
} else if (val === 'FooBarString') { // disallow FooBarString
return false;
}
return redisCache.store.isCacheableValue(val);
}
};
customRedisCache = cacheManager.caching({
store: await redisStore(customConfig),
});
await customRedisCache.reset();
});
describe('initialization', () => {
it('should create a store with the options that were provided', async () => {
const redisPwdCache = cacheManager.caching({
store: await redisStore(config),
...config
});
expect(redisPwdCache.store.getClient().options.socket.host).toEqual(config.socket.host);
expect(redisPwdCache.store.getClient().options.socket.port).toEqual(config.socket.port);
});
});
describe('set', () => {
it('should return a promise', () => {
expect(redisCache.set('foo', 'bar')).toBeInstanceOf(Promise);
});
it('can be used with a callback', (done) => {
redisCache.set('foo', 'bar', function (err, res) {
expect(err).toBeNull();
expect(res).toEqual("OK");
done();
});
});
it('should store a value without ttl', async () => {
await redisCache.set('foo', 'bar');
await expect(redisCache.get('foo')).resolves.toEqual('bar');
await expect(redisCache.ttl('foo')).resolves.toEqual(config.ttl);
});
it('should store a value with a specific ttl', async () => {
await redisCache.set('foo', 'bar', {ttl: 5});
await expect(redisCache.ttl('foo')).resolves.toEqual(5);
});
it('should store a value with a infinite ttl', async () => {
await redisCache.set('foo', 'bar', {ttl: 0});
await expect(redisCache.ttl('foo')).resolves.toEqual(-1);
});
it('should not be able to store a null value (not cacheable)', async () => {
await expect(redisCache.set('foo', null)).rejects.toThrowError('"null" is not a cacheable value');
});
it('should not store an invalid value', async () => {
await expect(redisCache.set('foo1', undefined)).rejects.toThrowError('"undefined" is not a cacheable value');
});
it('should store an undefined value if permitted by isCacheableValue', async () => {
expect(customRedisCache.store.isCacheableValue(undefined)).toBe(true);
await customRedisCache.set('foo3', undefined);
});
it('should not store a value disallowed by isCacheableValue', async () => {
expect(customRedisCache.store.isCacheableValue('FooBarString')).toBe(false);
await expect(customRedisCache.set('foobar', 'FooBarString')).rejects.toThrowError('"FooBarString" is not a cacheable value');
});
it('should return an error if there is an error acquiring a connection', async () => {
await redisCache.store.getClient().disconnect();
await expect(redisCache.set('foo', 'bar')).rejects.toBeDefined();
});
});
describe('get', () => {
it('should return a promise', async () => {
expect(redisCache.get('foo')).toBeInstanceOf(Promise);
});
it('can be used with a callback', (done) => {
redisCache.set('foo', 'bar')
.then(() => {
redisCache.get('foo', function (err, res) {
expect(err).toBeNull();
expect(res).toEqual('bar');
done();
});
})
.catch(err => done(err));
});
it('should retrieve a value for a given key', async () => {
const value = 'bar';
await redisCache.set('foo', value);
await expect(redisCache.get('foo')).resolves.toEqual(value);
});
it('should retrieve a value for a given key if options provided', async () => {
const value = 'bar';
await redisCache.set('foo', value);
await expect(redisCache.get('foo', {})).resolves.toEqual(value);
});
it('should retrieve a value for a given key unparsed', async () => {
const value = 'bar';
await redisCache.set('foo', value);
await expect(redisCache.get('foo', {parse:false})).resolves.toEqual('\"bar\"');
});
it('should return null when the key is invalid', async () => {
await expect(redisCache.get('invalidKey')).resolves.toEqual(null);
});
it('should return an error if there is an error acquiring a connection', async () => {
await redisCache.store.getClient().disconnect();
await expect(redisCache.get('foo')).rejects.toThrowError('The client is closed');
});
});
describe('del', () => {
it('should return a promise', async () => {
expect(redisCache.del('foo')).toBeInstanceOf(Promise);
});
it('can be used with a callback', (done) => {
redisCache.set('foo', 'bar')
.then(() => {
redisCache.del('foo', function (err, res) {
expect(err).toBeNull();
expect(res).toEqual(1);
done();
});
})
.catch(err => done(err));
});
it('should delete a value for a given key', async () => {
await redisCache.set('foo', 'bar');
await expect(redisCache.get('foo')).resolves.toEqual('bar');
await redisCache.del('foo');
await expect(redisCache.get('foo')).resolves.toEqual(null);
});
it('should delete a value for a given key if options provided', async () => {
await redisCache.set('foo', 'bar');
await expect(redisCache.get('foo')).resolves.toEqual('bar');
await redisCache.del('foo', {});
await expect(redisCache.get('foo')).resolves.toEqual(null);
});
it('should return an error if there is an error acquiring a connection', async () => {
await redisCache.store.getClient().disconnect();
await expect(redisCache.del('foo')).rejects.toThrowError('The client is closed');
});
});
describe('mset', () => {
it('should return a promise', () => {
expect(redisCache.mset('foo', 'bar')).toBeInstanceOf(Promise);
});
it('can be used with a callback', (done) => {
redisCache.mset('foo', 'bar', 'baz', 'qux', function (err, res) {
expect(err).toBeNull();
expect(res).toEqual(["OK", "OK"]);
redisCache.mget('foo', 'baz')
.then((res) => {
expect(res).toEqual(['bar', 'qux']);
done();
})
.catch(err => done(err));
});
});
it('should store a value without ttl', async () => {
await expect(redisCache.mset('foo', 'bar', 'foo2', 'bar2')).resolves.toEqual(["OK", "OK"]);
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual(['bar', 'bar2']);
});
it('should store a value with a specific ttl', async () => {
await redisCache.mset('foo', 'bar', 'foo2', 'bar2', {ttl: 60});
await expect(redisCache.ttl('foo')).resolves.toEqual(60);
await expect(redisCache.ttl('foo2')).resolves.toEqual(60);
});
it('should store a value with a infinite ttl', async () => {
await redisCache.mset('foo', 'bar', {ttl: 0});
await expect(redisCache.ttl('foo')).resolves.toEqual(-1);
});
it('should not be able to store a null value (not cacheable)', async () => {
await expect(redisCache.mset('foo2', null)).rejects.toThrowError('"null" is not a cacheable value');
});
it('should store a value without callback', async () => {
await expect(redisCache.mset('foo', 'baz', 'foo2', 'baz2')).resolves.toEqual(["OK", "OK"]);
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual(['baz', 'baz2']);
});
it('should not store an invalid value', async () => {
await expect(redisCache.mset('foo1', undefined)).rejects.toThrowError('"undefined" is not a cacheable value');
});
it('should store an undefined value if permitted by isCacheableValue', async () => {
expect(customRedisCache.store.isCacheableValue(undefined)).toBe(true);
await customRedisCache.mset('foo3', undefined, 'foo4', undefined);
await expect(customRedisCache.mget('foo3', 'foo4')).resolves.toEqual(['undefined', 'undefined']);
});
it('should not store a value disallowed by isCacheableValue', async () => {
expect(customRedisCache.store.isCacheableValue('FooBarString')).toBe(false);
await expect(customRedisCache.mset('foobar', 'FooBarString')).rejects.toThrowError('"FooBarString" is not a cacheable value');
});
it('should return an error if there is an error acquiring a connection', async () => {
await redisCache.store.getClient().disconnect();
/**
* Waiting for a pull request to be merged in order to uncomment the following assertion. The multi.exec() is
* currently not rejecting the promise on client disconnect.
*
* @see https://github.com/redis/node-redis/pull/2293
*/
// await expect(redisCache.mset('foo', 'bar')).rejects.toThrowError('The client is closed');
});
});
describe('mget', () => {
it('should return a promise', () => {
expect(redisCache.mget('foo', 'foo2')).toBeInstanceOf(Promise);
});
it('can be used with a callback', (done) => {
redisCache.mset('foo', 'bar', 'baz', 'qux')
.then((res) => {
expect(res).toEqual(["OK", "OK"]);
redisCache.mget('foo', 'baz', function (err, res) {
expect(err).toBeNull();
expect(res).toEqual(['bar', 'qux']);
done();
});
})
.catch(err => done(err));
});
it('should retrieve a value for a given key', async () => {
const value = 'bar';
const value2 = 'bar2';
await redisCache.mset('foo', value, 'foo2', value2);
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual([value, value2]);
});
it('should retrieve a value for a given key if options provided', async () => {
const value = 'bar';
await redisCache.mset('foo', value);
await expect(redisCache.mget('foo', {someConfig: true})).resolves.toEqual([value]);
});
it('should retrieve a value for a given key unparsed', async () => {
const value = 'bar';
await redisCache.mset('foo', value);
await expect(redisCache.mget('foo', {parse: false})).resolves.toEqual(['\"bar\"']);
});
it('should return null when the key is invalid', async () => {
await expect(redisCache.mget('invalidKey', 'otherInvalidKey')).resolves.toEqual([null, null]);
});
it('should return an error if there is an error acquiring a connection', async () => {
await redisCache.store.getClient().disconnect();
await expect(redisCache.mget('foo')).rejects.toThrowError('The client is closed');
});
});
describe('mdel', () => {
it('should return a promise', async () => {
expect(redisCache.store.mdel('foo', 'bar')).toBeInstanceOf(Promise);
});
it('can be used with a callback', (done) => {
redisCache.mset('foo', 'bar', 'baz', 'qux')
.then((res) => {
expect(res).toEqual(["OK", "OK"]);
redisCache.store.mdel('foo', 'baz', function (err, res) {
expect(err).toBeNull();
expect(res).toEqual(2);
redisCache.mget('foo', 'baz')
.then((res) => {
expect(res).toEqual([null, null]);
done();
})
.catch(err => done(err))
});
})
.catch(err => done(err));
});
it('should delete a unlimited number of keys', async () => {
await redisCache.mset('foo', 'bar', 'foo2', 'bar2');
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual(['bar', 'bar2']);
await redisCache.store.mdel('foo', 'foo2');
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual([null, null]);
});
it('should delete a unlimited number of keys if options provided', async () => {
await redisCache.mset('foo', 'bar', 'foo2', 'bar2');
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual(['bar', 'bar2']);
await redisCache.store.mdel('foo', 'foo2', {});
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual([null, null]);
});
it('should delete an array of keys', async () => {
await redisCache.mset('foo', 'bar', 'foo2', 'bar2');
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual(['bar', 'bar2']);
await redisCache.store.mdel(['foo', 'foo2']);
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual([null, null]);
});
it('should delete an array of keys if options provided', async () => {
await redisCache.mset('foo', 'bar', 'foo2', 'bar2');
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual(['bar', 'bar2']);
await redisCache.store.mdel(['foo', 'foo2'], {});
await expect(redisCache.mget('foo', 'foo2')).resolves.toEqual([null, null]);
});
it('should return an error if there is an error acquiring a connection', async () => {
await redisCache.store.getClient().disconnect();
await expect(redisCache.store.mdel('foo')).rejects.toThrowError('The client is closed');
});
});
describe('reset', () => {
it('should return a promise', async () => {
expect(redisCache.reset()).toBeInstanceOf(Promise);
});
it('can be used with a callback', (done) => {
redisCache.mset('foo', 'bar', 'baz', 'qux')
.then((res) => {
expect(res).toEqual(["OK", "OK"]);
redisCache.reset(function (err, res) {
expect(err).toBeNull();
expect(res).toEqual("OK");
redisCache.mget('foo', 'baz')
.then((res) => {
expect(res).toEqual([null, null]);
done();
})
.catch(err => done(err))
});
})
.catch(err => done(err));
});
it('should flush underlying db', async () => {
await redisCache.set('foo', 'bar');
await redisCache.set('baz', 'qux');
await redisCache.reset();
await expect(redisCache.mget('foo', 'baz')).resolves.toEqual([null, null]);
});
it('should return an error if there is an error acquiring a connection', async () => {
await redisCache.store.getClient().disconnect();
await expect(redisCache.reset()).rejects.toThrowError('The client is closed');
});
});
describe('keys', () => {
it('should return a promise', async () => {
expect(redisCache.keys('foo')).toBeInstanceOf(Promise);
});
it('can be used with a callback', (done) => {
redisCache.mset('foo', 'bar', 'baz', 'qux')
.then((res) => {
expect(res).toEqual(["OK", "OK"]);
redisCache.keys('*', function (err, res) {
expect(err).toBeNull();
expect(res).toEqual(expect.arrayContaining(['foo', 'baz']));
done();
});
})
.catch(err => done(err));
});
it('should return an array of keys for the given pattern', async () => {
await redisCache.set('foo', 'bar');
await expect(redisCache.keys('foo')).resolves.toContainEqual('foo');
});
it('should return an array of all keys if called without a pattern', async () => {
await redisCache.mset('foo', 'bar', 'foo2', 'bar2', 'foo3', 'bar3');
await expect(redisCache.keys()).resolves.toHaveLength(3);
await expect(redisCache.keys()).resolves.toEqual(expect.arrayContaining(['foo', 'foo2', 'foo3']));
});
it('should return an error if there is an error acquiring a connection', async () => {
await redisCache.store.getClient().disconnect();
await expect(redisCache.keys('foo')).rejects.toThrowError('The client is closed');
});
});
describe('ttl', () => {
it('should return a promise', async () => {
expect(redisCache.ttl('foo')).toBeInstanceOf(Promise);
});
it('can be used with a callback', (done) => {
redisCache.set('foo', 'bar')
.then((res) => {
expect(res).toEqual("OK");
redisCache.ttl('foo', function (err, res) {
expect(err).toBeNull();
expect(res).toEqual(config.ttl);
done();
});
})
.catch(err => done(err));
});
it('should retrieve ttl for a given key', async () => {
await redisCache.set('foo', 'bar');
await expect(redisCache.ttl('foo')).resolves.toEqual(config.ttl);
});
it('should retrieve ttl for an invalid key', async () => {
await expect(redisCache.ttl('invalidKey')).resolves.toEqual(-2);
});
it('should return an error if there is an error acquiring a connection', async () => {
await redisCache.set('foo', 'bar');
await redisCache.store.getClient().disconnect();
await expect(redisCache.ttl('foo')).rejects.toThrowError('The client is closed');
});
});
describe('isCacheableValue', () => {
it('should return true when the value is not undefined', () => {
expect(redisCache.store.isCacheableValue(0)).toBe(true);
expect(redisCache.store.isCacheableValue(100)).toBe(true);
expect(redisCache.store.isCacheableValue('')).toBe(true);
expect(redisCache.store.isCacheableValue('test')).toBe(true);
});
it('should return false when the value is undefined', () => {
expect(redisCache.store.isCacheableValue(undefined)).toBe(false);
});
it('should return false when the value is null', () => {
expect(redisCache.store.isCacheableValue(null)).toBe(false);
});
});
describe('redis error event', () => {
it('should return an error when the redis server is unavailable', (done) => {
redisCache.store.getClient().on('error', (err) => {
expect(err).not.toEqual(null);
done();
});
redisCache.store.getClient().emit('error', 'Something unexpected');
});
});
describe('overridable isCacheableValue function', () => {
let redisCache2;
beforeEach(async () => {
redisCache2 = cacheManager.caching({
store: await redisStore({
...config,
isCacheableValue: () => {
return 'I was overridden';
}
}),
password: config.password
});
});
it('should return its return value instead of the built-in function', () => {
expect(redisCache2.store.isCacheableValue(0)).toEqual('I was overridden');
});
});
describe('wrap function', () => {
// Simulate retrieving a user from a database
function getUser(id, cb) {
setTimeout(() => {
cb(null, {id: id});
}, 100);
}
// Simulate retrieving a user from a database with Promise
function getUserPromise(id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({id: id});
}, 100);
});
}
it('should be able to cache objects', (done) => {
const userId = 123;
// First call to wrap should run the code
redisCache.wrap('wrap-user', (cb) => {
getUser(userId, cb);
}, (err, user) => {
expect(user.id).toEqual(userId);
// Second call to wrap should retrieve from cache
redisCache.wrap('wrap-user', (cb) => {
getUser(userId + 1, cb);
}, (err, user) => {
expect(user.id).toEqual(userId);
done();
});
});
});
it('should work with promises', async () => {
const userId = 123;
// First call to wrap should run the code
return redisCache
.wrap(
'wrap-promise',
() => getUserPromise(userId),
)
.then((user) => {
expect(user.id).toEqual(userId);
// Second call to wrap should retrieve from cache
return redisCache.wrap(
'wrap-promise',
() => getUserPromise(userId + 1),
)
.then((user) => expect(user.id).toEqual(userId));
});
});
});
gitextract_lte6y37z/
├── .babelrc
├── .github/
│ └── workflows/
│ ├── run-coverage.yml
│ └── run-tests.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── dist/
│ ├── index.d.ts
│ └── index.js
├── index.js
├── package.json
├── rollup.config.js
└── test/
└── index.test.js
SYMBOL INDEX (23 symbols across 4 files)
FILE: dist/index.d.ts
type RedisStore (line 4) | interface RedisStore extends Store {
FILE: dist/index.js
function _regeneratorRuntime (line 8) | function _regeneratorRuntime() {
function _typeof (line 299) | function _typeof(obj) {
function asyncGeneratorStep (line 308) | function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, ar...
function _asyncToGenerator (line 322) | function _asyncToGenerator(fn) {
function _slicedToArray (line 338) | function _slicedToArray(arr, i) {
function _arrayWithHoles (line 341) | function _arrayWithHoles(arr) {
function _iterableToArrayLimit (line 344) | function _iterableToArrayLimit(arr, i) {
function _unsupportedIterableToArray (line 368) | function _unsupportedIterableToArray(o, minLen) {
function _arrayLikeToArray (line 376) | function _arrayLikeToArray(arr, len) {
function _nonIterableRest (line 381) | function _nonIterableRest() {
function _createForOfIteratorHelper (line 384) | function _createForOfIteratorHelper(o, allowArrayLike) {
function redisStore (line 436) | function redisStore(_x) {
function _redisStore (line 439) | function _redisStore() {
function encodeValue (line 828) | function encodeValue(value) {
function decodeValue (line 831) | function decodeValue(val) {
function isObject (line 834) | function isObject(object) {
FILE: index.js
function redisStore (line 4) | async function redisStore(config) {
function encodeValue (line 195) | function encodeValue(value) {
function decodeValue (line 199) | function decodeValue(val) {
function isObject (line 203) | function isObject(object) {
FILE: test/index.test.js
function getUser (line 517) | function getUser(id, cb) {
function getUserPromise (line 524) | function getUserPromise(id) {
Condensed preview — 13 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (68K chars).
[
{
"path": ".babelrc",
"chars": 250,
"preview": "{\n \"presets\": [\n [\n \"@babel/env\",\n {\n \"modules\": false\n }\n ]\n ],\n \"env\": {\n \"test\": {\n"
},
{
"path": ".github/workflows/run-coverage.yml",
"chars": 786,
"preview": "# This workflow will do a clean installation of node dependencies and run code coverage across different versions of nod"
},
{
"path": ".github/workflows/run-tests.yml",
"chars": 795,
"preview": "# This workflow will do a clean installation of node dependencies and run tests across different versions of node\n\nname:"
},
{
"path": ".gitignore",
"chars": 36,
"preview": "node_modules/\ncoverage/\n.idea/\n*.log"
},
{
"path": "CHANGELOG.md",
"chars": 774,
"preview": "# Changelog\n\n## v3.0.1 - 17 Oct, 2022\n\nFixed bug where the `redisStore.del` would no longer accept an options object, wh"
},
{
"path": "LICENSE",
"chars": 1073,
"preview": "MIT License\n\nCopyright (c) 2017 Matthijs Dabroek\n\nPermission is hereby granted, free of charge, to any person obtaining "
},
{
"path": "README.md",
"chars": 4246,
"preview": "[](https://badge.fury.io/js/cache-manager-redis-st"
},
{
"path": "dist/index.d.ts",
"chars": 783,
"preview": "import type { Store, StoreConfig } from \"cache-manager\";\nimport type { RedisClientType, RedisClientOptions } from \"redis"
},
{
"path": "dist/index.js",
"chars": 30938,
"preview": "'use strict';\n\nObject.defineProperty(exports, '__esModule', { value: true });\n\nvar node_util = require('node:util');\nvar"
},
{
"path": "index.js",
"chars": 5102,
"preview": "import {callbackify} from 'node:util';\nimport {createClient} from 'redis';\n\nexport async function redisStore(config) {\n "
},
{
"path": "package.json",
"chars": 1027,
"preview": "{\n \"name\": \"cache-manager-redis-store\",\n \"author\": \"Matthijs Dabroek <dabroek@gmail.com>\",\n \"description\": \"Redis sto"
},
{
"path": "rollup.config.js",
"chars": 217,
"preview": "import babel from '@rollup/plugin-babel';\n\nexport default {\n input: 'index.js',\n output: {\n format: 'cjs',\n file"
},
{
"path": "test/index.test.js",
"chars": 19362,
"preview": "import cacheManager from 'cache-manager';\nimport {redisStore} from '../index';\n\nlet redisCache;\nlet customRedisCache;\n\nc"
}
]
About this extraction
This page contains the full source code of the dabroek/node-cache-manager-redis-store GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 13 files (63.9 KB), approximately 16.9k tokens, and a symbol index with 23 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.