Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

koa-cash

Package Overview
Dependencies
Maintainers
5
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

koa-cash - npm Package Compare versions

Comparing version 3.0.1-0 to 3.0.1

.editorconfig

148

index.js

@@ -1,10 +0,12 @@

'use strict'
const { gzip } = require('zlib');
const { promisify } = require('util');
const compressible = require('compressible')
const toArray = require('stream-to-array')
const isJSON = require('koa-is-json')
const Bluebird = require('bluebird')
const bytes = require('bytes')
const bytes = require('bytes');
const compressible = require('compressible');
const getStream = require('get-stream');
const isJSON = require('koa-is-json');
const isStream = require('is-stream');
const safeStringify = require('fast-safe-stringify');
const compress = Bluebird.promisify(require('zlib').gzip)
const compress = promisify(gzip);

@@ -15,63 +17,73 @@ // methods we cache

GET: true
}
};
module.exports = function (options) {
options = options || {}
module.exports = function(options) {
options = options || {};
const hash = options.hash || function (ctx) { return ctx.request.url }
let threshold = options.threshold || '1kb'
if (typeof threshold === 'string') threshold = bytes(threshold)
const get = options.get
const set = options.set
if (!get) throw new Error('.get not defined')
if (!set) throw new Error('.set not defined')
const hash =
options.hash ||
function(ctx) {
return ctx.request.url;
};
const stringify = options.stringify || safeStringify || JSON.stringify;
let threshold = options.threshold || '1kb';
if (typeof threshold === 'string') threshold = bytes(threshold);
const { get } = options;
const { set } = options;
if (!get) throw new Error('.get not defined');
if (!set) throw new Error('.set not defined');
// ctx.cashed(maxAge) => boolean
const cashed = async function cashed (ctx, maxAge) {
async function cashed(maxAge) {
// uncacheable request method
if (!methods[ctx.request.method]) return false
if (!methods[this.request.method]) return false;
const key = ctx.cashKey = hash(ctx)
const obj = await get(key, maxAge || options.maxAge || 0)
const body = obj && obj.body
this.cashKey = hash(this);
const key = this.cashKey;
const obj = await get(key, maxAge || options.maxAge || 0);
const body = obj && obj.body;
if (!body) {
// tell the upstream middleware to cache this response
ctx.cash = { maxAge }
return false
this.cash = { maxAge };
return false;
}
// serve from cache
ctx.response.type = obj.type
if (obj.lastModified) ctx.response.lastModified = obj.lastModified
if (obj.etag) ctx.response.etag = obj.etag
if (ctx.request.fresh) {
ctx.response.status = 304
return true
this.response.type = obj.type;
if (obj.lastModified) this.response.lastModified = obj.lastModified;
if (obj.etag) this.response.etag = obj.etag;
if (this.request.fresh) {
this.response.status = 304;
return true;
}
if (obj.gzip && ctx.request.acceptsEncodings('gzip', 'identity') === 'gzip') {
ctx.response.body = new Buffer(obj.gzip)
ctx.response.set('Content-Encoding', 'gzip')
if (
obj.gzip &&
this.request.acceptsEncodings('gzip', 'identity') === 'gzip'
) {
this.response.body = Buffer.from(obj.gzip);
this.response.set('Content-Encoding', 'gzip');
} else {
ctx.response.body = obj.body
this.response.body = obj.body;
// tell any compress middleware to not bother compressing this
ctx.response.set('Content-Encoding', 'identity')
this.response.set('Content-Encoding', 'identity');
}
return true
return true;
}
// the actual middleware
return async function cash (ctx, next) {
ctx.vary('Accept-Encoding')
ctx.cashed = function (maxAge) {
return cashed(ctx, maxAge)
}
// eslint-disable-next-line complexity
async function middleware(ctx, next) {
ctx.vary('Accept-Encoding');
ctx.cashed = cashed.bind(ctx);
await next()
await next();
// check for HTTP caching just in case
if (!ctx.cash) {
if (ctx.request.fresh) ctx.response.status = 304
return
if (ctx.request.fresh) ctx.response.status = 304;
return;
}

@@ -82,13 +94,15 @@

// only cache GET/HEAD 200s
if (ctx.response.status !== 200) return
if (!methods[ctx.request.method]) return
let body = ctx.response.body
if (!body) return
if (ctx.response.status !== 200) return;
if (!methods[ctx.request.method]) return;
let { body } = ctx.response;
if (!body) return;
// stringify JSON bodies
if (isJSON(body)) body = ctx.response.body = JSON.stringify(body)
// buffer streams
if (typeof body.pipe === 'function') {
// note: non-binary streams are NOT supported!
body = ctx.response.body = Buffer.concat(await toArray(body))
if (isJSON(body)) {
ctx.response.body = stringify(body);
body = ctx.response.body;
} else if (isStream(body)) {
// buffer streams
ctx.response.body = await getStream(body);
body = ctx.response.body;
}

@@ -98,3 +112,3 @@

if ((ctx.response.get('Content-Encoding') || 'identity') !== 'identity') {
throw new Error('Place koa-cache below any compression middleware.')
throw new Error('Place koa-cache below any compression middleware.');
}

@@ -107,19 +121,25 @@

etag: ctx.response.get('etag') || null
}
};
const fresh = ctx.request.fresh
if (fresh) ctx.response.status = 304
const { fresh } = ctx.request;
if (fresh) ctx.response.status = 304;
if (compressible(obj.type) && ctx.response.length >= threshold) {
obj.gzip = await compress(body)
if (!fresh && ctx.request.acceptsEncodings('gzip', 'identity') === 'gzip') {
ctx.response.body = obj.gzip
ctx.response.set('Content-Encoding', 'gzip')
obj.gzip = await compress(body);
if (
!fresh &&
ctx.request.acceptsEncodings('gzip', 'identity') === 'gzip'
) {
ctx.response.body = obj.gzip;
ctx.response.set('Content-Encoding', 'gzip');
}
}
if (!ctx.response.get('Content-Encoding')) ctx.response.set('Content-Encoding', 'identity')
if (!ctx.response.get('Content-Encoding'))
ctx.response.set('Content-Encoding', 'identity');
await set(ctx.cashKey, obj, ctx.cash.maxAge || options.maxAge || 0)
await set(ctx.cashKey, obj, ctx.cash.maxAge || options.maxAge || 0);
}
}
return middleware;
};
{
"name": "koa-cash",
"description": "HTTP response caching for Koa",
"version": "3.0.1-0",
"publishConfig": {
"tag": "next"
"description": "HTTP response caching for Koa. HTTP response caching for Koa. Supports Redis, in-memory store, and more!",
"version": "3.0.1",
"author": "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)",
"ava": {
"verbose": true,
"serial": true,
"failFast": true
},
"bugs": {
"url": "https://github.com/koajs/cash/issues",
"email": "me@jongleberry.com"
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"contributors": [
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)",
"Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com)"
],
"dependencies": {
"bluebird": "^3.1.1",
"bytes": "^2.1.0",
"compressible": "^2.0.0",
"koa-is-json": "^1.0.0",
"stream-to-array": "^2.0.0"
"bytes": "^3.1.0",
"compressible": "^2.0.18",
"fast-safe-stringify": "^2.0.7",
"get-stream": "^5.1.0",
"is-stream": "^2.0.0",
"koa-is-json": "^1.0.0"
},
"devDependencies": {
"istanbul": "^1.1.0-alpha.1",
"koa": "^2.0.0",
"lru-cache": "^4.0.0",
"mocha": "^3.1.2",
"standard": "^8.6.0",
"supertest": "^1.1.0"
"@commitlint/cli": "latest",
"@commitlint/config-conventional": "latest",
"ava": "latest",
"codecov": "latest",
"cross-env": "latest",
"eslint": "6.x",
"eslint-config-xo-lass": "latest",
"fixpack": "latest",
"husky": "latest",
"into-stream": "^5.1.1",
"koa": "^2.12.0",
"lint-staged": "latest",
"lru-cache": "4.x",
"nyc": "latest",
"remark-cli": "latest",
"remark-preset-github": "latest",
"supertest": "1.x",
"xo": "0.25"
},
"engines": {
"node": ">= 7.6.0"
"node": ">=8.3"
},
"scripts": {
"lint": "standard index.js test/**/*.js",
"test": "NODE_ENV=test mocha",
"test-cov": "NODE_ENV=test node ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha",
"test-travis": "npm run lint && NODE_ENV=test node ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha --report lcovonly"
"homepage": "https://github.com/koajs/cash",
"husky": {
"hooks": {
"pre-commit": "lint-staged && npm test",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"author": {
"name": "Jonathan Ong",
"email": "me@jongleberry.com",
"url": "http://jongleberry.com",
"twitter": "https://twitter.com/jongleberry"
},
"repository": "koajs/cash",
"keywords": [
"alternative",
"amazon",
"aws",
"cache",
"caching",
"cdn",
"cloudfront",
"content",
"database",
"db",
"delivery",
"handler",
"hosting",
"http",
"in-memory",
"ioredis",
"key",
"koa",
"memory",
"middleware",
"network",
"provider",
"redis",
"response",
"responses",
"s3",
"sentinel",
"serve",
"server",
"service",
"session",
"sessions",
"space",
"spaces",
"static",
"storage",
"value"
],
"license": "MIT",
"main": "index.js",
"keywords": [
"koa",
"cache",
"middleware"
]
"nyc": {
"check-coverage": true,
"lines": 100,
"functions": 100,
"branches": 100,
"reporter": [
"lcov",
"html",
"text"
]
},
"prettier": {
"singleQuote": true,
"bracketSpacing": true,
"trailingComma": "none"
},
"remarkConfig": {
"plugins": [
"preset-github"
]
},
"repository": {
"type": "git",
"url": "https://github.com/koajs/cash"
},
"scripts": {
"ava": "cross-env NODE_ENV=test ava",
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
"lint": "xo && remark . -qfo",
"nyc": "cross-env NODE_ENV=test nyc ava",
"test": "yarn run lint && yarn run ava",
"test-coverage": "yarn run lint && yarn run nyc"
},
"xo": {
"prettier": true,
"space": true,
"extends": [
"xo-lass"
]
}
}

@@ -1,36 +0,78 @@

# Koa Cash
# koa-cash
[![NPM version][npm-image]][npm-url]
[![Build status][travis-image]][travis-url]
[![Test coverage][coveralls-image]][coveralls-url]
[![Dependency Status][david-image]][david-url]
[![License][license-image]][license-url]
[![Downloads][downloads-image]][downloads-url]
[![build status](https://img.shields.io/travis/com/koajs/cash.svg)](https://travis-ci.com/koajs/cash)
[![code coverage](https://img.shields.io/codecov/c/github/koajs/cash.svg)](https://codecov.io/gh/koajs/cash)
[![code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://lass.js.org)
[![license](https://img.shields.io/github/license/koajs/cash.svg)](LICENSE)
[![npm downloads](https://img.shields.io/npm/dt/koa-cash.svg)](https://npm.im/koa-cash)
HTTP response caching for Koa.
> HTTP response caching for Koa. Supports Redis, in-memory store, and more!
## Table of Contents
* [Features](#features)
* [Install](#install)
* [Usage](#usage)
* [API](#api)
* [app.use(koaCash(options))](#appusekoacashoptions)
* [const cached = await ctx.cashed(\[maxAge\])](#const-cached--await-ctxcashedmaxage)
* [Notes](#notes)
* [Usage](#usage-1)
* [Contributors](#contributors)
* [License](#license)
## Features
Caches the response based on any arbitrary store you'd like.
- Handles JSON and stream bodies
- Handles gzip compression negotiation
- Handles 304 responses
* Handles JSON and stream bodies
* Handles gzip compression negotiation
* Handles 304 responses
:tada: **Pairs great with [@ladjs/koa-cache-responses](https://github.com/ladjs/koa-cache-responses)** :tada:
## Install
[npm][]:
```sh
npm install koa-cash
```
[yarn][]:
```sh
yarn add koa-cash
```
## Usage
```js
app.use(require('koa-cash')({
// some options
}))
const koaCash = require('koa-cash');
app.use(async (ctx, next) => {
// ...
app.use(koaCash())
app.use(async ctx => {
// this response is already cashed if `true` is returned,
// so this middleware will automatically serve this response from cache
if (await ctx.cashed()) return
if (await ctx.cashed()) return;
// set the response body here,
// and the upstream middleware will automatically cache it
ctx.response.body = 'hello world!'
})
ctx.body = 'hello world!';
});
```
## API
### app.use(require('koa-cash')(options))
### app.use(koaCash(options))

@@ -53,46 +95,45 @@ Options are:

function hash(ctx) {
return ctx.request.url
return ctx.response.url; // same as ctx.url
}
```
`ctx` is the Koa context. By default, it caches based on the URL.
`ctx` is the Koa context and is also passed as an argument. By default, it caches based on the URL.
#### `get()`
Get a value from a store. Can be a regular function or an `async` function,
which returns the cache's value, if any.
Get a value from a store. Must return a Promise, which returns the cache's value, if any.
```js
async function get(key, maxAge) {
return <cached-value>
function get(key, maxAge) {
return Promise;
}
```
Note that all the `maxAge` stuff must be handled by you.
This module makes no opinion about it.
Note that all the `maxAge` stuff must be handled by you. This module makes no opinion about it.
#### `set()`
Set a value to a store. Can be a regular function or an `async` function.
Set a value to a store. Must return a Promise.
```js
async function set(key, value, maxAge) {
...
function set(key, value, maxAge) {
return Promise;
}
```
Note: `maxAge` is set by `.cash={ maxAge }`.
If it's not set, then `maxAge` will be `0`, which you should then ignore.
Note: `maxAge` is set by `.cash = { maxAge }`. If it's not set, then `maxAge` will be `0`, which you should then ignore.
#### Example
Using a library like [lru-cache](https://github.com/isaacs/node-lru-cache),
though this would not quite work since it doesn't allow per-key expiration times.
Using a library like [lru-cache](https://github.com/isaacs/node-lru-cache), though this would not quite work since it doesn't allow per-key expiration times.
```js
var cache = require('lru-cache')({
const koaCash = require('koa-cash');
const LRU = require('lru-cache');
const cache = new LRU({
maxAge: 30000 // global max age
})
app.use(require('koa-cash')({
app.use(koaCash({
get (key, maxAge) {

@@ -107,35 +148,40 @@ return cache.get(key)

### var cached = await ctx.cashed([maxAge])
See [@ladjs/koa-cache-responses](https://github.com/ladjs/koa-cache-responses) test folder more examples (e.g. Redis with `ioredis`).
This is how you enable a route to be cached.
If you don't call `await ctx.cashed()`,
then this route will not be cached nor will it attempt to serve the request from the cache.
### const cached = await ctx.cashed(\[maxAge])
This is how you enable a route to be cached. If you don't call `await ctx.cashed()`, then this route will not be cached nor will it attempt to serve the request from the cache.
`maxAge` is the max age passed to `get()`.
If `cached` is `true`,
then the current request has been served from cache and __you should early `return`__.
Otherwise, continue setting `ctx.response.body=` and this will cache the response.
If `cached` is `true`, then the current request has been served from cache and **you should early `return`**. Otherwise, continue setting `ctx.body=` and this will cache the response.
## Notes
- Only `GET` and `HEAD` requests are cached.
- Only `200` responses are cached.
Don't set `304` status codes on these routes - this middleware will handle it for you
- The underlying store should be able to handle `Date` objects as well as `Buffer` objects.
Otherwise, you may have to serialize/deserialize yourself.
* Only `GET` and `HEAD` requests are cached.
* Only `200` responses are cached. Don't set `304` status codes on these routes - this middleware will handle it for you
* The underlying store should be able to handle `Date` objects as well as `Buffer` objects. Otherwise, you may have to serialize/deserialize yourself.
[npm-image]: https://img.shields.io/npm/v/koa-cash.svg?style=flat-square
[npm-url]: https://npmjs.org/package/koa-cash
[github-tag]: http://img.shields.io/github/tag/koajs/cash.svg?style=flat-square
[github-url]: https://github.com/koajs/cash/tags
[travis-image]: https://img.shields.io/travis/koajs/cash.svg?style=flat-square
[travis-url]: https://travis-ci.org/koajs/cash
[coveralls-image]: https://img.shields.io/coveralls/koajs/cash.svg?style=flat-square
[coveralls-url]: https://coveralls.io/r/koajs/cash?branch=master
[david-image]: http://img.shields.io/david/koajs/cash.svg?style=flat-square
[david-url]: https://david-dm.org/koajs/cash
[license-image]: http://img.shields.io/npm/l/koa-cash.svg?style=flat-square
[license-url]: LICENSE
[downloads-image]: http://img.shields.io/npm/dm/koa-cash.svg?style=flat-square
[downloads-url]: https://npmjs.org/package/koa-cash
## Usage
## Contributors
| Name | Website |
| ---------------- | ------------------------- |
| **Jonathan Ong** | <http://jongleberry.com> |
| **Nick Baugh** | <http://niftylettuce.com> |
## License
[MIT](LICENSE) © [Jonathan Ong](http://jongleberry.com)
##
[npm]: https://www.npmjs.com/
[yarn]: https://yarnpkg.com/

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc