servie-send
Advanced tools
Comparing version 0.0.1 to 1.0.0
/// <reference types="node" /> | ||
import { Request, Response } from 'servie'; | ||
import { Stream } from 'stream'; | ||
import { Request, Response, CreateHeaders } from 'servie'; | ||
import { CreateBody } from 'servie/dist/body/node'; | ||
export interface SendOptions { | ||
statusCode?: number; | ||
headers?: CreateHeaders; | ||
contentType?: string; | ||
contentLength?: number; | ||
mtime?: Date; | ||
replacer?: (key: string, value: any) => string; | ||
space?: string | number; | ||
type?: string; | ||
status?: number; | ||
etag?: string; | ||
length?: number; | ||
skipEtag?: boolean; | ||
jsonSpaces?: string | number; | ||
jsonReplacer?: (key: string, value: any) => string; | ||
} | ||
/** | ||
* Send the payload as a HTTP response. | ||
* Create an empty response. | ||
*/ | ||
export declare function send(req: Request, payload: any, options?: SendOptions): Response; | ||
export declare function sendEmpty(req: Request, options?: SendOptions): Response; | ||
/** | ||
* Send JSON response. | ||
*/ | ||
export declare function sendJson(req: Request, payload: object | string | number, options?: SendOptions): Response; | ||
export declare function sendJson(req: Request, payload: boolean | string | number | object, options?: SendOptions): Response; | ||
/** | ||
* Send the response as a stream. | ||
*/ | ||
export declare function sendStream(req: Request, payload: Stream, options?: SendOptions): Response; | ||
export declare function sendStream(req: Request, payload: CreateBody, options?: SendOptions): Response; | ||
/** | ||
* Send as text response (defaults to `text/plain`). | ||
*/ | ||
export declare function sendText(req: Request, payload: string | Buffer, options?: SendOptions): Response; | ||
export declare function sendText(req: Request, payload: CreateBody, options?: SendOptions): Response; | ||
/** | ||
* Send as html response (`text/html`). | ||
*/ | ||
export declare function sendHtml(req: Request, payload: CreateBody, options?: SendOptions): Response; | ||
/** | ||
* Generate the response for Servie. | ||
*/ | ||
export declare function send(req: Request, payload: CreateBody, options?: SendOptions): Response; | ||
/** | ||
* Create an ETag of the payload body. | ||
*/ | ||
export declare function entityTag(body: string | Buffer): string; |
"use strict"; | ||
var __assign = (this && this.__assign) || Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var crypto_1 = require("crypto"); | ||
var servie_1 = require("servie"); | ||
var crypto_1 = require("crypto"); | ||
var stream_1 = require("stream"); | ||
var node_1 = require("servie/dist/body/node"); | ||
var CACHE_CONTROL_NO_CACHE_REGEXP = /(?:^|,)\s*?no-cache\s*?(?:,|$)/; | ||
var TOKEN_LIST_REGEXP = / *, */; | ||
var ZERO_LENGTH_ENTITY_TAG = createEntityTag(''); | ||
/** | ||
* Send the payload as a HTTP response. | ||
* Create an empty response. | ||
*/ | ||
function send(req, payload, options) { | ||
if (payload === null || payload === undefined) { | ||
return sendText(req, '', options); | ||
} | ||
if (payload instanceof stream_1.Stream) { | ||
return sendStream(req, payload, options); | ||
} | ||
if (Buffer.isBuffer(payload)) { | ||
return sendText(req, payload, options); | ||
} | ||
if (typeof payload === 'object') { | ||
return sendJson(req, payload, options); | ||
} | ||
return sendText(req, String(payload), options); | ||
function sendEmpty(req, options) { | ||
if (options === void 0) { options = {}; } | ||
return send(req, undefined, options); | ||
} | ||
exports.send = send; | ||
exports.sendEmpty = sendEmpty; | ||
/** | ||
@@ -32,5 +30,5 @@ * Send JSON response. | ||
if (options === void 0) { options = {}; } | ||
return sendText(req, JSON.stringify(payload, options.replacer, options.space), { | ||
type: options.type || 'application/json' | ||
}); | ||
var contentType = options.contentType || 'application/json'; | ||
var data = JSON.stringify(payload, options.jsonReplacer, options.jsonSpaces); | ||
return send(req, data, __assign({}, options, { contentType: contentType })); | ||
} | ||
@@ -43,22 +41,4 @@ exports.sendJson = sendJson; | ||
if (options === void 0) { options = {}; } | ||
var headers = {}; | ||
var status = options.status || 200; | ||
var body = req.method === 'HEAD' ? undefined : payload; | ||
if (fresh(req, options.etag, options.mtime)) { | ||
status = 304; | ||
body = undefined; | ||
} | ||
else { | ||
headers['Content-Type'] = options.type || 'application/octet-stream'; | ||
if (options.length) { | ||
headers['Content-Length'] = String(options.length); | ||
} | ||
} | ||
if (options.etag) { | ||
headers['ETag'] = options.etag; | ||
} | ||
if (options.mtime) { | ||
headers['Last-Modified'] = options.mtime.toUTCString(); | ||
} | ||
return new servie_1.Response({ status: status, headers: headers, body: body }); | ||
var contentType = options.contentType || 'application/octet-stream'; | ||
return send(req, payload, __assign({}, options, { contentType: contentType })); | ||
} | ||
@@ -71,33 +51,61 @@ exports.sendStream = sendStream; | ||
if (options === void 0) { options = {}; } | ||
var headers = {}; | ||
var length = typeof payload === 'string' ? Buffer.byteLength(payload) : payload.length; | ||
var etag = options.etag || entityTag(length, payload); | ||
var status = options.status || 200; | ||
var body = req.method === 'HEAD' ? undefined : payload; | ||
if (fresh(req, etag, options.mtime)) { | ||
status = 304; | ||
var contentType = options.contentType || 'text/plain'; | ||
return send(req, payload, __assign({}, options, { contentType: contentType })); | ||
} | ||
exports.sendText = sendText; | ||
/** | ||
* Send as html response (`text/html`). | ||
*/ | ||
function sendHtml(req, payload, options) { | ||
if (options === void 0) { options = {}; } | ||
var contentType = options.contentType || 'text/html'; | ||
return send(req, payload, __assign({}, options, { contentType: contentType })); | ||
} | ||
exports.sendHtml = sendHtml; | ||
/** | ||
* Generate the response for Servie. | ||
*/ | ||
function send(req, payload, options) { | ||
if (options === void 0) { options = {}; } | ||
var headers = servie_1.createHeaders(options.headers); | ||
var statusCode = options.statusCode || 200; | ||
var body = req.method === 'HEAD' ? undefined : node_1.createBody(payload); | ||
if (fresh(req, options.etag, options.mtime)) { | ||
statusCode = 304; | ||
body = undefined; | ||
} | ||
else { | ||
headers['Content-Type'] = options.type || 'text/plain'; | ||
headers['Content-Length'] = String(length); | ||
if (options.contentType) | ||
headers.set('Content-Type', options.contentType); | ||
if (options.contentLength) | ||
headers.set('Content-Length', String(options.contentLength)); | ||
} | ||
headers['ETag'] = etag; | ||
if (options.mtime) { | ||
headers['Last-Modified'] = options.mtime.toUTCString(); | ||
if (options.mtime) | ||
headers.set('Last-Modified', options.mtime.toUTCString()); | ||
if (options.etag) { | ||
headers.set('ETag', options.etag); | ||
} | ||
return new servie_1.Response({ status: status, headers: headers, body: body }); | ||
else if (!options.skipEtag) { | ||
if (typeof payload === 'string' || Buffer.isBuffer(payload)) { | ||
headers.set('ETag', entityTag(payload)); | ||
} | ||
} | ||
return new servie_1.Response({ statusCode: statusCode, headers: headers, body: body }); | ||
} | ||
exports.sendText = sendText; | ||
exports.send = send; | ||
/** | ||
* Create an ETag of the payload body. | ||
*/ | ||
function entityTag(len, body) { | ||
if (len === 0) { | ||
return "\"0-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU\""; | ||
} | ||
var hash = crypto_1.createHash('sha256').update(body).digest('base64').replace(/=+$/, ''); | ||
return "\"" + len.toString(36) + "-" + hash + "\""; | ||
function entityTag(body) { | ||
return body.length === 0 ? ZERO_LENGTH_ENTITY_TAG : createEntityTag(body); | ||
} | ||
exports.entityTag = entityTag; | ||
/** | ||
* Create an entity tag for cache identification. | ||
*/ | ||
function createEntityTag(body) { | ||
var hash = crypto_1.createHash('sha256').update(body).digest('base64'); | ||
return "\"" + hash + "\""; | ||
} | ||
/** | ||
* Check if a request is fresh. | ||
@@ -110,5 +118,4 @@ * | ||
var modifiedSince = req.headers.get('if-modified-since'); | ||
if (!noneMatch && !modifiedSince) { | ||
if (!noneMatch && !modifiedSince) | ||
return false; | ||
} | ||
var cacheControl = req.headers.get('cache-control'); | ||
@@ -119,12 +126,12 @@ if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl)) { | ||
if (noneMatch && etag) { | ||
var isStale = noneMatch.split(TOKEN_LIST_REGEXP).every(function (match) { return match !== etag; }); | ||
if (isStale) { | ||
var isStale = noneMatch.split(TOKEN_LIST_REGEXP).every(function (match) { | ||
return match !== etag; | ||
}); | ||
if (isStale) | ||
return false; | ||
} | ||
} | ||
if (modifiedSince && lastModified) { | ||
var isStale = lastModified.getTime() > Date.parse(modifiedSince); | ||
if (isStale) { | ||
if (isStale) | ||
return false; | ||
} | ||
} | ||
@@ -131,0 +138,0 @@ return true; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [0, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
var _this = this; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -6,37 +42,103 @@ var index_1 = require("./index"); | ||
var stream_1 = require("stream"); | ||
var node_1 = require("servie/dist/body/node"); | ||
describe('servie-send', function () { | ||
it('should send text', function () { | ||
var req = new servie_1.Request({ url: '/' }); | ||
expect(index_1.send(req, 'hello world')).toMatchSnapshot(); | ||
}); | ||
it('should send an empty response', function () { | ||
var req = new servie_1.Request({ url: '/' }); | ||
expect(index_1.send(req, null)).toMatchSnapshot(); | ||
}); | ||
it('should send json', function () { | ||
var req = new servie_1.Request({ url: '/' }); | ||
expect(index_1.send(req, { hello: 'world' })).toMatchSnapshot(); | ||
}); | ||
it('should send stream', function () { | ||
var req = new servie_1.Request({ url: '/' }); | ||
var chunk = 'hello world'; | ||
var stream = new stream_1.Readable({ | ||
read: function () { | ||
this.push(chunk); | ||
chunk = null; | ||
it('should send text', function () { return __awaiter(_this, void 0, void 0, function () { | ||
var req, res, _a; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
req = new servie_1.Request({ url: '/' }); | ||
res = index_1.sendText(req, 'hello world'); | ||
expect(res.statusCode).toEqual(200); | ||
expect(res.getHeaders()).toMatchSnapshot(); | ||
_a = expect; | ||
return [4 /*yield*/, res.body.text()]; | ||
case 1: | ||
_a.apply(void 0, [_b.sent()]).toEqual('hello world'); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
expect(index_1.send(req, stream)).toMatchSnapshot(); | ||
}); | ||
it('should send 304 with matching etag', function () { | ||
var req = new servie_1.Request({ | ||
url: '/', | ||
headers: { | ||
'If-None-Match': '"0-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU"' | ||
}, | ||
body: '' | ||
}); }); | ||
it('should send an empty response', function () { return __awaiter(_this, void 0, void 0, function () { | ||
var req, res, _a; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
req = new servie_1.Request({ url: '/' }); | ||
res = index_1.sendEmpty(req); | ||
expect(res.statusCode).toEqual(200); | ||
expect(res.getHeaders()).toMatchSnapshot(); | ||
_a = expect; | ||
return [4 /*yield*/, res.body.text()]; | ||
case 1: | ||
_a.apply(void 0, [_b.sent()]).toEqual(''); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
expect(index_1.send(req, '')).toMatchSnapshot(); | ||
}); | ||
}); }); | ||
it('should send json', function () { return __awaiter(_this, void 0, void 0, function () { | ||
var req, res, _a; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
req = new servie_1.Request({ url: '/' }); | ||
res = index_1.sendJson(req, { hello: 'world' }); | ||
expect(res.statusCode).toEqual(200); | ||
expect(res.getHeaders()).toMatchSnapshot(); | ||
_a = expect; | ||
return [4 /*yield*/, res.body.text()]; | ||
case 1: | ||
_a.apply(void 0, [_b.sent()]).toEqual('{"hello":"world"}'); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
it('should send stream', function () { return __awaiter(_this, void 0, void 0, function () { | ||
var req, chunk, stream, res, _a; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
req = new servie_1.Request({ url: '/' }); | ||
chunk = 'hello world'; | ||
stream = new stream_1.Readable({ | ||
read: function () { | ||
this.push(chunk); | ||
chunk = null; | ||
} | ||
}); | ||
res = index_1.sendStream(req, stream); | ||
expect(res.statusCode).toEqual(200); | ||
expect(res.getHeaders()).toMatchSnapshot(); | ||
_a = expect; | ||
return [4 /*yield*/, res.body.text()]; | ||
case 1: | ||
_a.apply(void 0, [_b.sent()]).toEqual('hello world'); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
it('should send 304 with matching etag', function () { return __awaiter(_this, void 0, void 0, function () { | ||
var req, res, _a; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
req = new servie_1.Request({ | ||
url: '/', | ||
headers: servie_1.createHeaders({ | ||
'If-None-Match': index_1.entityTag('') | ||
}), | ||
body: node_1.createBody('') | ||
}); | ||
res = index_1.sendText(req, ''); | ||
expect(res.statusCode).toEqual(304); | ||
expect(res.getHeaders()).toMatchSnapshot(); | ||
_a = expect; | ||
return [4 /*yield*/, res.body.text()]; | ||
case 1: | ||
_a.apply(void 0, [_b.sent()]).toEqual(''); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); }); | ||
}); | ||
//# sourceMappingURL=index.spec.js.map |
{ | ||
"name": "servie-send", | ||
"version": "0.0.1", | ||
"version": "1.0.0", | ||
"description": "Generate a HTTP response with client-side cache support", | ||
@@ -11,3 +11,3 @@ "main": "dist/index.js", | ||
"scripts": { | ||
"lint": "tslint \"src/**/*.ts\" --project tsconfig.json --type-check", | ||
"lint": "tslint \"src/**/*.ts\" --project tsconfig.json", | ||
"build": "rm -rf dist/ && tsc", | ||
@@ -41,21 +41,21 @@ "specs": "jest --coverage", | ||
"devDependencies": { | ||
"@types/jest": "^19.2.3", | ||
"@types/node": "^7.0.18", | ||
"jest": "^20.0.0", | ||
"@types/jest": "^22.2.2", | ||
"@types/node": "^8.0.0", | ||
"jest": "^22.4.3", | ||
"rimraf": "^2.5.4", | ||
"servie": "^0.2.5", | ||
"servie": "^2.1.3", | ||
"throwback": "^2.0.0", | ||
"ts-jest": "^20.0.2", | ||
"tslint": "^5.0.0", | ||
"tslint-config-standard": "^5.0.0", | ||
"ts-jest": "^22.4.2", | ||
"tslint": "^5.9.1", | ||
"tslint-config-standard": "^7.0.0", | ||
"typescript": "^2.2.1" | ||
}, | ||
"peerDependencies": { | ||
"servie": "^0.2.0" | ||
"servie": "^2.1.0" | ||
}, | ||
"jest": { | ||
"transform": { | ||
".(ts|tsx)": "ts-jest/preprocessor.js" | ||
".(ts|tsx)": "ts-jest" | ||
}, | ||
"testRegex": "src/.*\\.(?:test|spec)\\.(ts|tsx|js)$", | ||
"testRegex": "src/.*\\.(?:test|spec)\\.(tsx?|jsx?)$", | ||
"moduleFileExtensions": [ | ||
@@ -62,0 +62,0 @@ "ts", |
@@ -7,5 +7,4 @@ # Servie Send | ||
[![Test coverage](https://img.shields.io/coveralls/serviejs/servie-send.svg?style=flat)](https://coveralls.io/r/serviejs/servie-send?branch=master) | ||
[![Greenkeeper badge](https://badges.greenkeeper.io/serviejs/servie-send.svg)](https://greenkeeper.io/) | ||
> Create a HTTP response to send using Servie - a thin layer for creating a `Response` object with correct client-side cache headers. | ||
> Create a HTTP response to send using Servie - a thin layer for creating a `Response` object with cache headers. | ||
@@ -21,6 +20,10 @@ ## Installation | ||
```ts | ||
import { send } from 'servie-send' | ||
import { sendText, sendHtml, sendJson, sendStream, sendEmpty } from 'servie-send' | ||
function handle (req) { | ||
return send(req, 'hello world!') | ||
return sendText(req, 'hello world!') | ||
return sendHtml(req, '<!doctype html>') | ||
return sendJson(req, { json: true }) | ||
return sendStream(req, fs.createReadStream('example.txt')) | ||
return sendEmpty(req) // Nothing in response. | ||
} | ||
@@ -27,0 +30,0 @@ ``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
29025
319
0
37
1