├── .editorconfig ├── .gitattributes ├── .gitignore ├── .remarkignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package.json ├── test └── test.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | .idea 4 | node_modules 5 | coverage 6 | .nyc_output 7 | -------------------------------------------------------------------------------- /.remarkignore: -------------------------------------------------------------------------------- 1 | test/snapshots/**/*.md 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '8' 4 | - '10' 5 | - '12' 6 | script: 7 | npm run test-coverage 8 | after_success: 9 | npm run coverage 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nick Baugh (http://niftylettuce.com/) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # koa-better-flash 2 | 3 | [![build status](https://semaphoreci.com/api/v1/niftylettuce/koa-better-flash/branches/master/shields_badge.svg)](https://semaphoreci.com/niftylettuce/koa-better-flash) 4 | [![code coverage](https://img.shields.io/codecov/c/github/niftylettuce/koa-better-flash.svg)](https://codecov.io/gh/niftylettuce/koa-better-flash) 5 | [![code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo) 6 | [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 7 | [![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://github.com/lassjs/lass) 8 | [![license](https://img.shields.io/github/license/niftylettuce/koa-better-flash.svg)](<>) 9 | 10 | > Flash message middleware for Koa and Passport 11 | 12 | 13 | ## Table of Contents 14 | 15 | * [Install](#install) 16 | * [Usage](#usage) 17 | * [Contributors](#contributors) 18 | * [License](#license) 19 | 20 | 21 | ## Install 22 | 23 | [npm][]: 24 | 25 | ```sh 26 | npm install koa-better-flash 27 | ``` 28 | 29 | [yarn][]: 30 | 31 | ```sh 32 | yarn add koa-better-flash 33 | ``` 34 | 35 | 36 | ## Usage 37 | 38 | ```js 39 | const Koa = require('koa'); 40 | const Router = require('koa-router'); 41 | const session = require('koa-generic-session'); 42 | const flash = require('koa-better-flash'); 43 | 44 | const app = new Koa(); 45 | const router = new Router(); 46 | 47 | router.post('/', (ctx, next) => { 48 | // you can also pass an array of messages: 49 | // ctx.flash('info', [ 'hi', 'hello', 'good morning' ]); 50 | ctx.flash('info', 'hello world'); 51 | ctx.status = 200; 52 | }); 53 | 54 | router.get('/', ctx => { 55 | // to get all messages by type simply call `ctx.flash()` 56 | ctx.body = ctx.flash('info'); 57 | // outputs: [ 'hello world '] 58 | }); 59 | 60 | app.keys = [ 'keys' ]; 61 | app.use(session()); 62 | app.use(flash()); 63 | app.use(router.routes()); 64 | app.listen(); 65 | ``` 66 | 67 | 68 | ## Contributors 69 | 70 | | Name | Website | 71 | | -------------- | -------------------------- | 72 | | **Nick Baugh** | | 73 | 74 | 75 | ## License 76 | 77 | [MIT](LICENSE) © [Nick Baugh](http://niftylettuce.com/) 78 | 79 | 80 | ## 81 | 82 | [npm]: https://www.npmjs.com/ 83 | 84 | [yarn]: https://yarnpkg.com/ 85 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { format } = require('util'); 2 | 3 | module.exports = function() { 4 | return function(ctx, next) { 5 | if (ctx.flash) return next(); 6 | ctx.flash = flash.bind(ctx); 7 | return next(); 8 | }; 9 | }; 10 | 11 | function flash(type, msg, ...args) { 12 | if (typeof this.session !== 'object') 13 | throw new Error('ctx.flash() requires sessions'); 14 | this.session.flash = this.session.flash || {}; 15 | const msgs = this.session.flash; 16 | if (type && msg) { 17 | if (args.length > 0) { 18 | msg = format(...[msg, ...args]); 19 | } else if (Array.isArray(msg)) { 20 | for (const element of msg) { 21 | msgs[type] = msgs[type] || []; 22 | msgs[type].push(element); 23 | } 24 | 25 | return [...new Set(msgs[type])]; 26 | } 27 | 28 | msgs[type] = msgs[type] || []; 29 | msgs[type].push(msg); 30 | return [...new Set(msgs[type])]; 31 | } 32 | 33 | if (type) { 34 | const arr = msgs[type]; 35 | delete msgs[type]; 36 | return [...new Set(arr)] || []; 37 | } 38 | 39 | this.session.flash = {}; 40 | return msgs; 41 | } 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-better-flash", 3 | "description": "Flash message middleware for Koa and Passport", 4 | "version": "0.0.4", 5 | "author": "Nick Baugh (http://niftylettuce.com/)", 6 | "bugs": { 7 | "url": "https://github.com/niftylettuce/koa-better-flash/issues", 8 | "email": "niftylettuce@gmail.com" 9 | }, 10 | "commitlint": { 11 | "extends": [ 12 | "@commitlint/config-conventional" 13 | ] 14 | }, 15 | "contributors": [ 16 | "Nick Baugh (http://niftylettuce.com/)" 17 | ], 18 | "dependencies": {}, 19 | "devDependencies": { 20 | "@commitlint/cli": "latest", 21 | "@commitlint/config-conventional": "latest", 22 | "@koa/router": "^8.0.3", 23 | "ava": "latest", 24 | "codecov": "latest", 25 | "cross-env": "latest", 26 | "eslint": "latest", 27 | "eslint-config-xo-lass": "latest", 28 | "fixpack": "latest", 29 | "husky": "latest", 30 | "koa": "^2.11.0", 31 | "koa-generic-session": "^2.0.1", 32 | "lint-staged": "latest", 33 | "nyc": "latest", 34 | "remark-cli": "latest", 35 | "remark-preset-github": "latest", 36 | "supertest": "^4.0.2", 37 | "xo": "latest" 38 | }, 39 | "engines": { 40 | "node": ">=8.x" 41 | }, 42 | "homepage": "https://github.com/niftylettuce/koa-better-flash", 43 | "husky": { 44 | "hooks": { 45 | "pre-commit": "lint-staged && npm test", 46 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 47 | } 48 | }, 49 | "keywords": [ 50 | "connect", 51 | "cookie", 52 | "cookies", 53 | "express", 54 | "flash", 55 | "info", 56 | "koa-better-flash", 57 | "kue", 58 | "lass", 59 | "messaging", 60 | "node", 61 | "queue", 62 | "redis", 63 | "sessions" 64 | ], 65 | "license": "MIT", 66 | "lint-staged": { 67 | "*.js": [ 68 | "xo --fix", 69 | "git add" 70 | ], 71 | "*.md": [ 72 | "remark . -qfo", 73 | "git add" 74 | ], 75 | "package.json": [ 76 | "fixpack", 77 | "git add" 78 | ] 79 | }, 80 | "main": "index.js", 81 | "prettier": { 82 | "singleQuote": true, 83 | "bracketSpacing": true, 84 | "trailingComma": "none" 85 | }, 86 | "remarkConfig": { 87 | "plugins": [ 88 | "preset-github" 89 | ] 90 | }, 91 | "repository": { 92 | "type": "git", 93 | "url": "https://github.com/niftylettuce/koa-better-flash" 94 | }, 95 | "scripts": { 96 | "ava": "cross-env NODE_ENV=test ava", 97 | "coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov", 98 | "lint": "xo && remark . -qfo", 99 | "nyc": "cross-env NODE_ENV=test nyc ava", 100 | "test": "npm run lint && npm run ava", 101 | "test-coverage": "npm run lint && npm run nyc" 102 | }, 103 | "xo": { 104 | "prettier": true, 105 | "space": true, 106 | "extends": [ 107 | "xo-lass" 108 | ] 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const test = require('ava'); 2 | const request = require('supertest'); 3 | const Koa = require('koa'); 4 | const session = require('koa-generic-session'); 5 | const Router = require('@koa/router'); 6 | 7 | const flash = require('..'); 8 | 9 | test.beforeEach(t => { 10 | const app = new Koa(); 11 | const router = new Router(); 12 | router.post('/', ctx => { 13 | if (ctx.query.name) ctx.flash('info', 'hello %s world', ctx.query.name); 14 | else if (ctx.query.multiple) ctx.flash('info', ['hi', 'world', 'hello']); 15 | else ctx.flash('info', 'hello world'); 16 | ctx.status = 200; 17 | }); 18 | router.get('/', ctx => { 19 | ctx.body = ctx.flash('info'); 20 | }); 21 | app.keys = ['keys']; 22 | app.use(session()); 23 | app.use(flash()); 24 | app.use(router.routes()); 25 | Object.assign(t.context, { app, flash }); 26 | }); 27 | 28 | test('binds a middleware function', t => { 29 | t.true(t.context.app.middleware.length === 3); 30 | }); 31 | 32 | test('koa errors without sessions', async t => { 33 | const app = new Koa(); 34 | app.context.onerror = function(err) { 35 | if (!err) return; 36 | this.status = 500; 37 | this.body = err.message; 38 | this.length = Buffer.byteLength(this.body); 39 | this.res.end(this.body); 40 | }; 41 | 42 | const router = new Router(); 43 | router.get('/', ctx => { 44 | ctx.flash(); 45 | }); 46 | app.use(t.context.flash()); 47 | app.use(router.routes()); 48 | const res = await request(app.listen()).get('/'); 49 | t.is(res.status, 500); 50 | t.is(res.text, 'ctx.flash() requires sessions'); 51 | }); 52 | 53 | test('flash is not set twice', t => { 54 | const ctx = { flash: 1 }; 55 | t.context.flash()(ctx, () => {}); 56 | t.is(ctx.flash, 1); 57 | }); 58 | 59 | test('koa has empty flash session object after called', async t => { 60 | const app = new Koa(); 61 | const router = new Router(); 62 | router.get('/', ctx => { 63 | ctx.flash('info', 'hello world'); 64 | ctx.flash(); 65 | t.deepEqual(ctx.session.flash, {}); 66 | ctx.status = 200; 67 | }); 68 | app.keys = ['keys']; 69 | app.use(session()); 70 | app.use(t.context.flash()); 71 | app.use(router.routes()); 72 | const res = await request(app.listen()).get('/'); 73 | t.is(res.status, 200); 74 | }); 75 | 76 | test('flash message', async t => { 77 | const { app } = t.context; 78 | const agent = request.agent(app.listen()); 79 | await agent.post('/'); 80 | const res = await agent.get('/').set('Accept', 'application/json'); 81 | t.deepEqual(res.body, ['hello world']); 82 | }); 83 | 84 | test('flash message without duplicaes', async t => { 85 | const { app } = t.context; 86 | const agent = request.agent(app.listen()); 87 | await agent.post('/'); 88 | await agent.post('/'); 89 | await agent.post('/'); 90 | await agent.post('/'); 91 | const res = await agent.get('/').set('Accept', 'application/json'); 92 | t.deepEqual(res.body, ['hello world']); 93 | }); 94 | 95 | test('flash message with format', async t => { 96 | const { app } = t.context; 97 | const agent = request.agent(app.listen()); 98 | await agent.post('/?name=koa'); 99 | const res = await agent.get('/').set('Accept', 'application/json'); 100 | t.deepEqual(res.body, ['hello koa world']); 101 | }); 102 | 103 | test('flash an array of messages', async t => { 104 | const { app } = t.context; 105 | const agent = request.agent(app.listen()); 106 | await agent.post('/?multiple=true'); 107 | const res = await agent.get('/').set('Accept', 'application/json'); 108 | t.deepEqual(res.body, ['hi', 'world', 'hello']); 109 | }); 110 | 111 | test('return all messages', async t => { 112 | const { app } = t.context; 113 | const res = await request(app.listen()) 114 | .get('/') 115 | .set('Accept', 'application/json'); 116 | t.deepEqual(res.body, []); 117 | }); 118 | --------------------------------------------------------------------------------