spirit
Version:
extensible web library for building applications & frameworks
176 lines (160 loc) • 5.13 kB
JavaScript
;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _require = require("./response-class"),
Response = _require.Response;
var fs = require("fs");
var path = require("path");
var Promise = require("bluebird");
var stream = require("stream"); // for make_stream
/**
* checks if `resp` is a valid response map
* this test will return true for a Response too
*
* @param {*} resp - object to check
* @return {boolean}
*/
var is_response = function is_response(resp) {
if ((typeof resp === "undefined" ? "undefined" : _typeof(resp)) === "object" && resp !== null && typeof resp.status === "number" && _typeof(resp.headers) === "object" && !Array.isArray(resp.headers)) {
return true;
}
return false;
};
/**
* Returns a new Response with `body` as it's body
* and by default a 200 status code
* Additionally if `body` is a (non-empty) string, it will
* set content type to html and it's content length
*
* If it's already a Response it will recreate it.
* If it's a response map, it is converted into a Response.
* Leaving the status code and headers untouched.
*
* @param {*} body - the body for a response, or a response map
* @return {Response}
*/
var response = function response(body) {
var rmap = void 0;
if (is_response(body)) {
// just convert or recreate but don't touch!
rmap = new Response(body.body);
rmap.status = body.status;
rmap.headers = body.headers;
} else {
rmap = new Response(body);
if (typeof body === "string" && body !== "") {
rmap.type("html");
}
}
return rmap;
};
/**
* Creates a writable stream that can be used with response()
*
* @return {stream.Transform}
*/
var make_stream = function make_stream() {
return new stream.Transform({
transform: function transform(data, encoding, callback) {
this.push(data);
callback();
}
});
};
/**
* Returns a Promise of a Response of the file
* Where the body of the Response is a stream
*
* A Content-Type is guessed from the file extension.
*
* It will set a Content-Length header of the size
* of the file. As well as the Last-Modified header.
* NOTE that it is susceptible to time for both fields,
* so it can be inaccurate. That is, since it is a
* file stream, there is a moment in time (nanoseconds)
* where the file is being modified but after the headers
* are set and before the response is sent.
* For volatile files, it's recommended to:
* - strip the Content-Length header and/or switch
* to chunk transfer
* - not use this and instead create a Response with
* the file data as a Buffer
*
* @param {string} file_path path to file
* @return {Promise} Promise of a Response
*/
var file_response = function file_response(file) {
var resp = response();
if (typeof file !== "string") {
if (typeof file.path !== "string" || typeof file.pipe !== "function") {
throw new TypeError("Expected a file path (string) or a file stream for `file_response`");
}
resp.body = file;
file = file.path;
}
return new Promise(function (resolve, reject) {
fs.stat(file, function (err, fdata) {
if (err || !fdata.isFile()) {
if (!err) err = new TypeError(file + " is not a file.");
return reject(err);
}
resp._file = fdata;
resp._file.filepath = file;
if (!resp.body) {
resp.body = fs.createReadStream(file);
}
resp.type(path.extname(file)).len(fdata.size).set("Last-Modified", fdata.mtime.toUTCString());
resolve(resp);
});
});
};
/**
* returns a Response for a http redirect based
* on status code and url, default status code is 302
*
* moved-permanently 301
* found 302
* see-other 303
* temporary-redirect 307
* permanent-redirect 308
*
* @param {number} status - http status code
* @param {string} url - url to redirect to
* @return {Response}
*/
var redirect = function redirect(status, url) {
if (!url) {
url = status;
status = 302;
}
if (typeof status !== "number" || typeof url !== "string") {
throw TypeError("invalid arguments to `redirect`, need (number, string) or (string). number is a optional argument for a valid redirect status code, string is required for the URL to redirect");
}
return new Response().status_(status).set("Location", url);
};
/**
* returns a 500 Response with `err`
*
* @param {*} err - typically a Error
* @return {Response}
*/
var err_response = function err_response(err) {
if (!err) {
err = "";
}
var body = err.toString();
if (err instanceof Error) {
body += "\n\n" + err.stack;
}
if (!body) {
body = "An error occured, but there was no error message given.";
}
return new Response(body).status_(500);
};
module.exports = {
is_response: is_response,
make_stream: make_stream,
response: response,
file_response: file_response,
redirect: redirect,
err_response: err_response
};