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

quinn-respond

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

quinn-respond - npm Package Compare versions

Comparing version 1.0.1 to 2.0.0

.travis.yml

153

dist/respond.js
'use strict';
var STATUS_CODES = require('http').STATUS_CODES;
var QuinnResponse = require('./response');
var mod$0 = require('./body');var JSONBody = mod$0.JSONBody;var BufferBody = mod$0.BufferBody;
function respond(props) {
if (props === undefined) {
return;
} else if (props instanceof QuinnResponse) {
return props;
} else if (Buffer.isBuffer(props)) {
return new QuinnResponse({ body: props });
} else if (typeof props === 'number') {
return new QuinnResponse({
statusCode: props,
body: STATUS_CODES[props]
});
} else if (typeof props === 'object' && props !== null){
return new QuinnResponse(props);
function respond(options) {
options = options || {};
if (options instanceof QuinnResponse)
return options;
var headers, statusCode, body;
if (typeof options === 'string' || Buffer.isBuffer(options)) {
body = options;
} else {
return new QuinnResponse({ body: props });
headers = options.headers;
statusCode = options.statusCode;
body = options.body;
}
}
module.exports = respond;
var res = new QuinnResponse(statusCode || 200, headers || {});
module.exports.JSONBody = JSONBody;module.exports.BufferBody = BufferBody;
if (body !== undefined) {
if (!Buffer.isBuffer(body)) {
body = new Buffer(String(body), 'utf8');
res.header('Content-Length', body.length);
}
res.write(body);
res.end();
}
function json(data) {
if (data === undefined)
return;
return res;
}
module.exports = respond;
function text(body) {
return respond({
body: new JSONBody(data),
body: body,
headers: {
'Content-Type': 'application/json; charset=utf-8'
'Content-Type': 'text/plain; charset=utf-8'
}
});
} module.exports.json = json;
} module.exports.text = text;
respond.text = text;
function text(data) {
if (data === undefined)
return;
function html(body) {
return respond({
body: body,
headers: {
'Content-Type': 'text/html; charset=utf-8'
}
});
} module.exports.html = html;
respond.html = html;
function json(data) {
var body;
if (data !== undefined) {
body = JSON.stringify(data);
}
return respond({
body: new BufferBody(data),
body: body,
headers: {
'Content-Type': 'text/plain; charset=utf8'
'Content-Type': 'application/json; charset=utf-8'
}
});
} module.exports.text = text;
/**
* Helpers: status code
*
* This is not complete but should cover the most common response codes.
* If someone needs more, writing .status(418) shouldn't be too much to ask.
*/
/* 20x */
function ok(props) {
return respond(props).status(200);
} module.exports.ok = ok;
function created(props) {
return respond(props).status(201);
} module.exports.created = created;
function accepted(props) {
return respond(props).status(202);
} module.exports.accepted = accepted;
/* 30x */
function movedPermanently(props) {
return respond(props).status(301);
} module.exports.movedPermanently = movedPermanently;
function found(props) {
return respond(props).status(302);
} module.exports.found = found;
function redirect(location, code) {
return respond('')
.header('Location', location)
.status(code || 302);
} module.exports.redirect = redirect;
/* 40x */
function badRequest(props) {
return respond(props).status(400);
} module.exports.badRequest = badRequest;
function unauthorized(props) {
return respond(props).status(401);
} module.exports.unauthorized = unauthorized;
function forbidden(props) {
return respond(props).status(403);
} module.exports.forbidden = forbidden;
function notFound(props) {
return respond(props).status(404);
} module.exports.notFound = notFound;
/* 50x */
function internalServerError(props) {
return respond(props).status(500);
} module.exports.internalServerError = internalServerError;
function notImplemented(props) {
return respond(props).status(501);
} module.exports.notImplemented = notImplemented;
function badGateway(props) {
return respond(props).status(502);
} module.exports.badGateway = badGateway;
function serviceUnavailable(props) {
return respond(props).status(503);
} module.exports.serviceUnavailable = serviceUnavailable;
function gatewayTimeout(props) {
return respond(props).status(504);
} module.exports.gatewayTimeout = gatewayTimeout;
} module.exports.json = json;
respond.json = json;

@@ -0,119 +1,91 @@

/**
* We need to disallow munging private properties since _* are part of our
* interface with ReadableStream
*
* @preventMunge
*/
'use strict';
var caseless = require('caseless');
var mod$0 = require('bluebird');var all = mod$0.all;var resolve = mod$0.resolve;var join = mod$0.join;
var mod$1 = require('lodash');var zipObject = mod$1.zipObject;var each = mod$1.each;var isObject = mod$1.isObject;
var toStream = require('./body').toStream;
var PassThrough = require('readable-stream').PassThrough;
function toArray(value) {
return Array.isArray(value) ? value : [value];
}
for(var PassThrough____Key in PassThrough){if(PassThrough.hasOwnProperty(PassThrough____Key)){QuinnResponse[PassThrough____Key]=PassThrough[PassThrough____Key];}}var ____SuperProtoOfPassThrough=PassThrough===null?null:PassThrough.prototype;QuinnResponse.prototype=Object.create(____SuperProtoOfPassThrough);QuinnResponse.prototype.constructor=QuinnResponse;QuinnResponse.__superConstructor__=PassThrough;
function QuinnResponse(statusCode, headers) {
PassThrough.call(this);
function concatHeaders(old, newValue) {
if (old === undefined) {
return toArray(newValue);
}
return toArray(old).concat(toArray(newValue));
}
this.statusCode = statusCode;
var c = caseless.httpify(this, headers || {});
this.headers = c;
this.getHeader = function(name) {
return c.get(name);
};
function resolvedHeaders(headers) {
var headerKeys = Object.keys(headers);
var values = headerKeys.map( function(key) {return headers[key];} );
return all(values).then( function(resolvedValues)
{return zipObject(headerKeys, resolvedValues);}
);
}
this.on('pipe', this._onIncomingPipe.bind(this));
function QuinnResponse(props, isResolved) {
this.statusCode = props.statusCode || 200;
this.headers = caseless(props.headers || {});
this.body = props.body || null; // null === empty body
this.$QuinnResponse0 = !!isResolved;
this._cachedError = null;
}
QuinnResponse.prototype.resolved=function() {
if (this.$QuinnResponse0) return resolve(this);
return this.resolvedBody().then( function(body) {
if (!this.hasHeader('Content-Type'))
this.header('Content-Type', 'text/plain; charset=utf-8');
QuinnResponse.prototype._onIncomingPipe=function(src) {
if (src.path) {
// path can be used to automatically set content-type,
// worth preserving.
this.path = src.path;
}
src.on('error', this.error.bind(this));
};
if (typeof body.getByteSize === 'function') {
this.header('Content-Length', body.getByteSize());
} else {
// TODO: Handle content-length for other kinds of streams..?
}
QuinnResponse.prototype.error=function(err) {
if (this._readableState.pipesCount > 0) {
this.emit('error', err);
} else {
this._cachedError = err;
}
};
if (isObject(body.headers)) {
// TODO: Assume this is an attempt to pipe through in incoming response
// Important: exclude Host, Content-Length, and other dangerous headers
}
QuinnResponse.prototype._forwardMeta=function(dest) {
dest.statusCode = this.statusCode;
if (typeof body.path === 'string') {
// TODO: Try guessing Content-Type based on this property
// Idea by @mikeal:
// https://github.com/mikeal/response/blob/0bb9b978cd120d69c9369faf385b11c974ab35a5/index.js#L22
}
var headers = this.headers.dict;
var keys = Object.keys(headers);
var name, idx;
return resolvedHeaders(this.headers.dict).then(
function(headers) {return new QuinnResponse({
statusCode: this.statusCode,
headers: headers,
body: body
}, true);}.bind(this)
);
}.bind(this));
for (idx = 0; idx < keys.length; ++idx) {
name = keys[idx];
dest.setHeader(name, headers[name]);
}
};
QuinnResponse.prototype.resolvedBody=function() {
if (this.$QuinnResponse0) return resolve(this.body);
return resolve(this.body).then(toStream);
QuinnResponse.prototype.wrappedPipe=function(dest, options) {
// this -> dest -> wrapped
var wrapped = new QuinnResponse(this.statusCode, this.headers.dict);
wrapped._onIncomingPipe(this);
return PassThrough.prototype.pipe.call(this, dest, options)
.pipe(wrapped, options);
};
QuinnResponse.prototype.status=function(code) {
return this.statusCode = code, this;
};
QuinnResponse.prototype.pipe=function(dest, options) {
if (this._cachedError !== null) {
setImmediate(this.emit.bind(this, 'error', this._cachedError));
this._cachedError = null;
}
QuinnResponse.prototype.toJSON=function() {
return this.resolvedBody().then(function(body) {return body.toJSON();});
};
QuinnResponse.prototype.toBuffer=function() {
return this.resolvedBody().then(function(body) {return body.toBuffer();});
};
QuinnResponse.prototype.pipe=function(res) {
if (!this.$QuinnResponse0) {
this.resolved().then( function(r) {return r.pipe(res);} );
} else {
if (res.setHeader) {
res.statusCode = this.statusCode;
each(this.headers.dict, function(header, name) {
res.setHeader(name, header);
});
if (dest) {
if (typeof dest.setHeader === 'function') {
this._forwardMeta(dest);
} else if (typeof dest.pipe === 'function') {
// rewrap so the util methods are properly preserved
return this.wrappedPipe(dest, options);
}
this.body.pipe(res);
}
return res;
};
QuinnResponse.prototype.hasHeader=function(name) {
return this.headers.has(name);
return PassThrough.prototype.pipe.call(this, dest, options);
};
QuinnResponse.prototype.getHeader=function(name) {
return this.headers.get(name);
QuinnResponse.prototype.status=function(statusCode) {
this.statusCode = statusCode;
return this;
};
QuinnResponse.prototype.header=function(name, value) {
if (value === undefined)
return this.headers.del(name), this;
else
return this.headers.set(name, value), this;
};
QuinnResponse.prototype.addHeader=function(name, value) {
var oldValue = this.headers.get(name);
this.headers.set(name, join(oldValue, value, concatHeaders));
QuinnResponse.prototype.header=function() {
this.setHeader.apply(this, arguments);
return this;

@@ -120,0 +92,0 @@ };

{
"name": "quinn-respond",
"version": "1.0.1",
"version": "2.0.0",
"description": "Response generation for quinn",

@@ -20,9 +20,10 @@ "main": "dist/respond.js",

"dependencies": {
"bluebird": "^2.1.2",
"caseless": "^0.3.0",
"lodash": "^2.4.1",
"readable-stream": "^1.0.27-1",
"resolve-deep": "^1.0.1"
"readable-stream": "^1.0.27-1"
},
"devDependencies": {
"assertive": "^1.4.0",
"bluebird": "^2.1.2",
"concat-stream": "^1.4.6",
"jshint": "^2.5.2",
"mocha": "^1.20.1",

@@ -29,0 +30,0 @@ "quinnc": "^1.0.0",

# quinn: respond
## API
### `respond({ statusCode, headers, body })`
Create a new `QuinnResponse`.
The body can be a string or a buffer.
If no body is provided, it defaults to a through stream.
### QuinnResponse
#### `.pipe(res)`
Forward response to node http response.
If you pipe to something that isn't an HTTP response,
this will only forward the response body and return a wrapped result.
This enables things like:
```js
function handle(req, res) {
fs.createReadStream('README.md')
.pipe(respond.text())
.pipe(toUpperCase()) // pipe the body through a transform stream
.pipe(res); // headers etc. are preserved
}
```
The minimum requirement for something to be seen as an HTTP response
is the presence of a `setHeader` method.
### Helpers
#### `respond.text(body)`
Create a `text/plain` response with the given body.
The body can be a string or a buffer.
If no body is provided, it defaults to a through stream.
#### `respond.html(body)`
Create `text/html` response with the given body.
The body can be a string or a buffer.
If no body is provided, it defaults to a through stream.
#### `respond.json(data)`
Serialize the data and create an `application/json` response.
If no data is provided, it defaults to a through stream.
'use strict';
import {STATUS_CODES} from 'http';
import QuinnResponse from './response';
import {JSONBody, BufferBody} from './body';
function respond(props) {
if (props === undefined) {
return;
} else if (props instanceof QuinnResponse) {
return props;
} else if (Buffer.isBuffer(props)) {
return new QuinnResponse({ body: props });
} else if (typeof props === 'number') {
return new QuinnResponse({
statusCode: props,
body: STATUS_CODES[props]
});
} else if (typeof props === 'object' && props !== null){
return new QuinnResponse(props);
function respond(options) {
options = options || {};
if (options instanceof QuinnResponse)
return options;
var headers, statusCode, body;
if (typeof options === 'string' || Buffer.isBuffer(options)) {
body = options;
} else {
return new QuinnResponse({ body: props });
headers = options.headers;
statusCode = options.statusCode;
body = options.body;
}
}
export default respond;
var res = new QuinnResponse(statusCode || 200, headers || {});
export {JSONBody, BufferBody};
if (body !== undefined) {
if (!Buffer.isBuffer(body)) {
body = new Buffer(String(body), 'utf8');
res.header('Content-Length', body.length);
}
res.write(body);
res.end();
}
export function json(data) {
if (data === undefined)
return;
return res;
}
export default respond;
export function text(body) {
return respond({
body: new JSONBody(data),
body: body,
headers: {
'Content-Type': 'application/json; charset=utf-8'
'Content-Type': 'text/plain; charset=utf-8'
}
});
}
respond.text = text;
export function text(data) {
if (data === undefined)
return;
export function html(body) {
return respond({
body: new BufferBody(data),
body: body,
headers: {
'Content-Type': 'text/plain; charset=utf8'
'Content-Type': 'text/html; charset=utf-8'
}
});
}
respond.html = html;
/**
* Helpers: status code
*
* This is not complete but should cover the most common response codes.
* If someone needs more, writing .status(418) shouldn't be too much to ask.
*/
/* 20x */
export function ok(props) {
return respond(props).status(200);
export function json(data) {
var body;
if (data !== undefined) {
body = JSON.stringify(data);
}
return respond({
body: body,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
});
}
export function created(props) {
return respond(props).status(201);
}
export function accepted(props) {
return respond(props).status(202);
}
/* 30x */
export function movedPermanently(props) {
return respond(props).status(301);
}
export function found(props) {
return respond(props).status(302);
}
export function redirect(location, code) {
return respond('')
.header('Location', location)
.status(code || 302);
}
/* 40x */
export function badRequest(props) {
return respond(props).status(400);
}
export function unauthorized(props) {
return respond(props).status(401);
}
export function forbidden(props) {
return respond(props).status(403);
}
export function notFound(props) {
return respond(props).status(404);
}
/* 50x */
export function internalServerError(props) {
return respond(props).status(500);
}
export function notImplemented(props) {
return respond(props).status(501);
}
export function badGateway(props) {
return respond(props).status(502);
}
export function serviceUnavailable(props) {
return respond(props).status(503);
}
export function gatewayTimeout(props) {
return respond(props).status(504);
}
respond.json = json;

@@ -0,119 +1,91 @@

/**
* We need to disallow munging private properties since _* are part of our
* interface with ReadableStream
*
* @preventMunge
*/
'use strict';
import caseless from 'caseless';
import {all, resolve, join} from 'bluebird';
import {zipObject, each, isObject} from 'lodash';
import {toStream} from './body';
import {PassThrough} from 'readable-stream';
function toArray(value) {
return Array.isArray(value) ? value : [value];
}
class QuinnResponse extends PassThrough {
constructor(statusCode, headers) {
PassThrough.call(this);
function concatHeaders(old, newValue) {
if (old === undefined) {
return toArray(newValue);
}
return toArray(old).concat(toArray(newValue));
}
this.statusCode = statusCode;
var c = caseless.httpify(this, headers || {});
this.headers = c;
this.getHeader = function(name) {
return c.get(name);
};
function resolvedHeaders(headers) {
var headerKeys = Object.keys(headers);
var values = headerKeys.map( key => headers[key] );
return all(values).then( resolvedValues =>
zipObject(headerKeys, resolvedValues)
);
}
this.on('pipe', this._onIncomingPipe.bind(this));
class QuinnResponse {
constructor(props, isResolved) {
this.statusCode = props.statusCode || 200;
this.headers = caseless(props.headers || {});
this.body = props.body || null; // null === empty body
this._cachedError = null;
}
this._isResolved = !!isResolved;
_onIncomingPipe(src) {
if (src.path) {
// path can be used to automatically set content-type,
// worth preserving.
this.path = src.path;
}
src.on('error', this.error.bind(this));
}
resolved() {
if (this._isResolved) return resolve(this);
return this.resolvedBody().then( body => {
if (!this.hasHeader('Content-Type'))
this.header('Content-Type', 'text/plain; charset=utf-8');
error(err) {
if (this._readableState.pipesCount > 0) {
this.emit('error', err);
} else {
this._cachedError = err;
}
}
if (typeof body.getByteSize === 'function') {
this.header('Content-Length', body.getByteSize());
} else {
// TODO: Handle content-length for other kinds of streams..?
}
_forwardMeta(dest) {
dest.statusCode = this.statusCode;
if (isObject(body.headers)) {
// TODO: Assume this is an attempt to pipe through in incoming response
// Important: exclude Host, Content-Length, and other dangerous headers
}
var headers = this.headers.dict;
var keys = Object.keys(headers);
var name, idx;
if (typeof body.path === 'string') {
// TODO: Try guessing Content-Type based on this property
// Idea by @mikeal:
// https://github.com/mikeal/response/blob/0bb9b978cd120d69c9369faf385b11c974ab35a5/index.js#L22
}
return resolvedHeaders(this.headers.dict).then(
headers => new QuinnResponse({
statusCode: this.statusCode,
headers: headers,
body: body
}, true)
);
});
for (idx = 0; idx < keys.length; ++idx) {
name = keys[idx];
dest.setHeader(name, headers[name]);
}
}
resolvedBody() {
if (this._isResolved) return resolve(this.body);
return resolve(this.body).then(toStream);
wrappedPipe(dest, options) {
// this -> dest -> wrapped
var wrapped = new QuinnResponse(this.statusCode, this.headers.dict);
wrapped._onIncomingPipe(this);
return PassThrough.prototype.pipe.call(this, dest, options)
.pipe(wrapped, options);
}
status(code) {
return this.statusCode = code, this;
}
pipe(dest, options) {
if (this._cachedError !== null) {
setImmediate(this.emit.bind(this, 'error', this._cachedError));
this._cachedError = null;
}
toJSON() {
return this.resolvedBody().then(body => body.toJSON());
}
toBuffer() {
return this.resolvedBody().then(body => body.toBuffer());
}
pipe(res) {
if (!this._isResolved) {
this.resolved().then( r => r.pipe(res) );
} else {
if (res.setHeader) {
res.statusCode = this.statusCode;
each(this.headers.dict, (header, name) => {
res.setHeader(name, header);
});
if (dest) {
if (typeof dest.setHeader === 'function') {
this._forwardMeta(dest);
} else if (typeof dest.pipe === 'function') {
// rewrap so the util methods are properly preserved
return this.wrappedPipe(dest, options);
}
this.body.pipe(res);
}
return res;
}
hasHeader(name) {
return this.headers.has(name);
return PassThrough.prototype.pipe.call(this, dest, options);
}
getHeader(name) {
return this.headers.get(name);
status(statusCode) {
this.statusCode = statusCode;
return this;
}
header(name, value) {
if (value === undefined)
return this.headers.del(name), this;
else
return this.headers.set(name, value), this;
}
addHeader(name, value) {
var oldValue = this.headers.get(name);
this.headers.set(name, join(oldValue, value, concatHeaders));
header() {
this.setHeader.apply(this, arguments);
return this;

@@ -120,0 +92,0 @@ }

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