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

servie-send

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

servie-send - npm Package Compare versions

Comparing version 1.1.1 to 2.0.0

25

dist/index.d.ts
/// <reference types="node" />
import { Request, Response, CreateHeaders } from 'servie';
import { CreateBody } from 'servie/dist/body/node';
import { Request, Response, HeadersInit, CreateBody } from "servie/dist/node";
export interface SendOptions {
statusCode?: number;
headers?: CreateHeaders;
status?: number;
headers?: HeadersInit;
contentType?: string;
contentLength?: number;
mtime?: Date;
etag?: string;
skipEtag?: boolean;
jsonSpaces?: string | number;
jsonReplacer?: (key: string, value: any) => string;
etag?: boolean | string;
}

@@ -20,5 +16,12 @@ /**

/**
* JSON response configuration.
*/
export interface JsonOptions extends SendOptions {
jsonSpaces?: string | number;
jsonReplacer?: (key: string, value: any) => string;
}
/**
* Send JSON response.
*/
export declare function sendJson(req: Request, payload: boolean | string | number | object, options?: SendOptions): Response;
export declare function sendJson(req: Request, payload: boolean | string | number | object, options?: JsonOptions): Response;
/**

@@ -41,4 +44,4 @@ * Send the response as a stream.

/**
* Create an entity tag for cache identification.
* Create an ETag from the payload body.
*/
export declare function entityTag(body: string | Buffer): string;
export declare function entityTag(body: string | Buffer): string | undefined;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const crypto_1 = require("crypto");
const servie_1 = require("servie");
const node_1 = require("servie/dist/body/node");
const node_1 = require("servie/dist/node");
const CACHE_CONTROL_NO_CACHE_REGEXP = /(?:^|,)\s*?no-cache\s*?(?:,|$)/;
const TOKEN_LIST_REGEXP = / *, */;
const ZERO_LENGTH_ENTITY_TAG = entityTag('');
const ZERO_LENGTH_ENTITY_TAG = toEntityTag("");
/**

@@ -13,3 +12,3 @@ * Create an empty response.

function sendEmpty(req, options = {}) {
return send(req, undefined, options);
return send(req, null, options);
}

@@ -21,5 +20,13 @@ exports.sendEmpty = sendEmpty;

function sendJson(req, payload, options = {}) {
const contentType = options.contentType || 'application/json';
const contentType = options.contentType || "application/json";
const { status, headers, contentLength, mtime, etag } = options;
const data = JSON.stringify(payload, options.jsonReplacer, options.jsonSpaces);
return send(req, data, Object.assign({}, options, { contentType }));
return send(req, data, {
status,
headers,
contentType,
contentLength,
mtime,
etag
});
}

@@ -31,3 +38,3 @@ exports.sendJson = sendJson;

function sendStream(req, payload, options = {}) {
const contentType = options.contentType || 'application/octet-stream';
const contentType = options.contentType || "application/octet-stream";
return send(req, payload, Object.assign({}, options, { contentType }));

@@ -40,3 +47,3 @@ }

function sendText(req, payload, options = {}) {
const contentType = options.contentType || 'text/plain';
const contentType = options.contentType || "text/plain";
return send(req, payload, Object.assign({}, options, { contentType }));

@@ -49,3 +56,3 @@ }

function sendHtml(req, payload, options = {}) {
const contentType = options.contentType || 'text/html';
const contentType = options.contentType || "text/html";
return send(req, payload, Object.assign({}, options, { contentType }));

@@ -58,9 +65,9 @@ }

function send(req, payload, options = {}) {
let statusCode = options.statusCode || 200;
let body = req.method === 'HEAD' ? undefined : node_1.createBody(payload);
const headers = servie_1.createHeaders(options.headers);
const { mtime, contentType, contentLength, skipEtag } = options;
const etag = options.etag || (skipEtag ? undefined : toEntityTag(payload));
let status = options.status || 200;
let body = req.method === "HEAD" ? undefined : payload;
const headers = new node_1.Headers(options.headers);
const { mtime, contentType, contentLength } = options;
const etag = computeEtag(body, options.etag);
if (fresh(req, etag, mtime)) {
statusCode = 304;
status = 304;
body = undefined;

@@ -70,29 +77,43 @@ }

if (contentType)
headers.set('Content-Type', contentType);
headers.set("Content-Type", contentType);
if (contentLength)
headers.set('Content-Length', String(contentLength));
headers.set("Content-Length", String(contentLength));
}
if (etag)
headers.set('ETag', etag);
headers.set("ETag", etag);
if (mtime)
headers.set('Last-Modified', mtime.toUTCString());
return new servie_1.Response({ statusCode, headers, body });
headers.set("Last-Modified", mtime.toUTCString());
return new node_1.Response(body, { status, headers });
}
exports.send = send;
/**
* Compute etags when requested.
*/
function computeEtag(body, etag) {
if (typeof etag === "string")
return etag;
if (!etag)
return;
if (body === undefined || body === null)
return ZERO_LENGTH_ENTITY_TAG;
if (typeof body === "string" || Buffer.isBuffer(body))
return entityTag(body);
throw new TypeError("Etag can only be computed on a string or buffer");
}
/**
* Create an ETag from the payload body.
*/
function toEntityTag(body) {
if (typeof body === 'string' || Buffer.isBuffer(body)) {
return body.length === 0 ? ZERO_LENGTH_ENTITY_TAG : entityTag(body);
}
function entityTag(body) {
return body.length === 0 ? ZERO_LENGTH_ENTITY_TAG : toEntityTag(body);
}
exports.entityTag = entityTag;
/**
* Create an entity tag for cache identification.
*/
function entityTag(body) {
const hash = crypto_1.createHash('sha256').update(body).digest('base64');
function toEntityTag(body) {
const hash = crypto_1.createHash("sha256")
.update(body)
.digest("base64");
return `"${hash}"`;
}
exports.entityTag = entityTag;
/**

@@ -104,7 +125,7 @@ * Check if a request is fresh.

function fresh(req, etag, mtime) {
const ifNoneMatch = req.headers.get('if-none-match');
const ifModifiedSince = req.headers.get('if-modified-since');
const ifNoneMatch = req.headers.get("if-none-match");
const ifModifiedSince = req.headers.get("if-modified-since");
if (!ifNoneMatch && !ifModifiedSince)
return false;
const cacheControl = req.headers.get('cache-control');
const cacheControl = req.headers.get("cache-control");
if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl)) {

@@ -114,15 +135,15 @@ return false;

if (ifNoneMatch && etag) {
const isStale = ifNoneMatch.split(TOKEN_LIST_REGEXP).every(match => {
return match !== etag;
const isFresh = ifNoneMatch.split(TOKEN_LIST_REGEXP).every(match => {
return match === etag;
});
if (isStale)
return false;
if (isFresh)
return true;
}
if (ifModifiedSince && mtime) {
const isStale = mtime.getTime() > Date.parse(ifModifiedSince);
if (isStale)
return false;
const isFresh = mtime.getTime() <= Date.parse(ifModifiedSince);
if (isFresh)
return true;
}
return true;
return false;
}
//# sourceMappingURL=index.js.map

@@ -12,30 +12,29 @@ "use strict";

const index_1 = require("./index");
const servie_1 = require("servie");
const node_1 = require("servie/dist/node");
const stream_1 = require("stream");
const node_1 = require("servie/dist/body/node");
describe('servie-send', () => {
it('should send text', () => __awaiter(this, void 0, void 0, function* () {
const req = new servie_1.Request({ url: '/' });
const res = index_1.sendText(req, 'hello world');
expect(res.statusCode).toEqual(200);
expect(res.allHeaders).toMatchSnapshot();
expect(yield res.body.text()).toEqual('hello world');
describe("servie-send", () => {
it("should send text", () => __awaiter(this, void 0, void 0, function* () {
const req = new node_1.Request("/");
const res = index_1.sendText(req, "hello world");
expect(res.status).toEqual(200);
expect(res.headers).toMatchSnapshot();
expect(yield res.text()).toEqual("hello world");
}));
it('should send an empty response', () => __awaiter(this, void 0, void 0, function* () {
const req = new servie_1.Request({ url: '/' });
it("should send an empty response", () => __awaiter(this, void 0, void 0, function* () {
const req = new node_1.Request("/");
const res = index_1.sendEmpty(req);
expect(res.statusCode).toEqual(200);
expect(res.allHeaders).toMatchSnapshot();
expect(yield res.body.text()).toEqual('');
expect(res.status).toEqual(200);
expect(res.headers).toMatchSnapshot();
expect(yield res.text()).toEqual("");
}));
it('should send json', () => __awaiter(this, void 0, void 0, function* () {
const req = new servie_1.Request({ url: '/' });
const res = index_1.sendJson(req, { hello: 'world' });
expect(res.statusCode).toEqual(200);
expect(res.allHeaders).toMatchSnapshot();
expect(yield res.body.text()).toEqual('{"hello":"world"}');
it("should send json", () => __awaiter(this, void 0, void 0, function* () {
const req = new node_1.Request("/");
const res = index_1.sendJson(req, { hello: "world" });
expect(res.status).toEqual(200);
expect(res.headers).toMatchSnapshot();
expect(yield res.text()).toEqual('{"hello":"world"}');
}));
it('should send stream', () => __awaiter(this, void 0, void 0, function* () {
const req = new servie_1.Request({ url: '/' });
let chunk = 'hello world';
it("should send stream", () => __awaiter(this, void 0, void 0, function* () {
const req = new node_1.Request("/");
let chunk = "hello world";
const stream = new stream_1.Readable({

@@ -48,33 +47,43 @@ read() {

const res = index_1.sendStream(req, stream);
expect(res.statusCode).toEqual(200);
expect(res.allHeaders).toMatchSnapshot();
expect(yield res.body.text()).toEqual('hello world');
expect(res.status).toEqual(200);
expect(res.headers).toMatchSnapshot();
expect(yield res.text()).toEqual("hello world");
}));
it('should send 304 with matching etag', () => __awaiter(this, void 0, void 0, function* () {
const req = new servie_1.Request({
url: '/',
headers: servie_1.createHeaders({
'If-None-Match': index_1.entityTag('')
}),
body: node_1.createBody('')
it("should always send 200 when not computing etag", () => __awaiter(this, void 0, void 0, function* () {
const req = new node_1.Request("/", {
headers: {
"If-None-Match": index_1.entityTag("")
},
body: ""
});
const res = index_1.sendText(req, '');
expect(res.statusCode).toEqual(304);
expect(res.allHeaders).toMatchSnapshot();
expect(yield res.body.text()).toEqual('');
const res = index_1.sendText(req, "");
expect(res.status).toEqual(200);
expect(res.headers).toMatchSnapshot();
expect(yield res.text()).toEqual("");
}));
it('should send 200 with changed etag', () => __awaiter(this, void 0, void 0, function* () {
const req = new servie_1.Request({
url: '/',
headers: servie_1.createHeaders({
'If-None-Match': index_1.entityTag('content')
}),
body: node_1.createBody('')
it("should send 304 with matching etag", () => __awaiter(this, void 0, void 0, function* () {
const req = new node_1.Request("/", {
headers: {
"If-None-Match": index_1.entityTag("")
},
body: ""
});
const res = index_1.sendText(req, '');
expect(res.statusCode).toEqual(200);
expect(res.allHeaders).toMatchSnapshot();
expect(yield res.body.text()).toEqual('');
const res = index_1.sendText(req, "", { etag: true });
expect(res.status).toEqual(304);
expect(res.headers).toMatchSnapshot();
expect(yield res.text()).toEqual("");
}));
it("should send 200 with changed etag", () => __awaiter(this, void 0, void 0, function* () {
const req = new node_1.Request("/", {
headers: {
"If-None-Match": index_1.entityTag("content")
},
body: ""
});
const res = index_1.sendText(req, "", { etag: true });
expect(res.status).toEqual(200);
expect(res.headers).toMatchSnapshot();
expect(yield res.text()).toEqual("");
}));
});
//# sourceMappingURL=index.spec.js.map
{
"name": "servie-send",
"version": "1.1.1",
"version": "2.0.0",
"description": "Generate a HTTP response with client-side cache support",

@@ -11,7 +11,9 @@ "main": "dist/index.js",

"scripts": {
"prettier": "prettier --write",
"lint": "tslint \"src/**/*.ts\" --project tsconfig.json",
"build": "rm -rf dist/ && tsc",
"format": "npm run prettier -- .travis.yml README.md \"src/**/*.{js,ts}\"",
"build": "rimraf dist && tsc",
"specs": "jest --coverage",
"test": "npm run -s lint && npm run -s build && npm run -s specs",
"prepublish": "npm run build"
"prepare": "npm run build"
},

@@ -40,28 +42,52 @@ "repository": {

"homepage": "https://github.com/serviejs/servie-send",
"devDependencies": {
"@types/jest": "^23.3.2",
"@types/node": "^10.9.4",
"jest": "^23.6.0",
"rimraf": "^2.5.4",
"servie": "^3.0.0",
"throwback": "^3.0.0",
"ts-jest": "^23.1.4",
"tslint": "^5.9.1",
"tslint-config-standard": "^8.0.1",
"typescript": "^3.0.3"
},
"peerDependencies": {
"servie": ">=2.1 <4"
},
"jest": {
"roots": [
"<rootDir>/src/"
],
"transform": {
".(ts|tsx)": "ts-jest"
"\\.tsx?$": "ts-jest"
},
"testRegex": "src/.*\\.(?:test|spec)\\.(tsx?|jsx?)$",
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
"js",
"jsx",
"json",
"node"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,json,css,md}": [
"npm run prettier",
"git add"
]
},
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@types/jest": "^24.0.13",
"@types/node": "^12.0.2",
"husky": "^2.3.0",
"jest": "^24.8.0",
"lint-staged": "^8.1.7",
"prettier": "^1.17.1",
"rimraf": "^2.5.4",
"servie": "^4.0.2",
"throwback": "^4.0.0",
"ts-jest": "^24.0.2",
"tslint": "^5.9.1",
"tslint-config-prettier": "^1.18.0",
"tslint-config-standard": "^8.0.1",
"typescript": "^3.4.5"
},
"peerDependencies": {
"servie": "^4.0.0"
}
}

@@ -19,13 +19,29 @@ # Servie Send

```ts
import { sendText, sendHtml, sendJson, sendStream, sendEmpty } from 'servie-send'
import {
sendText,
sendHtml,
sendJson,
sendStream,
sendEmpty,
entityTag
} from "servie-send";
function handle (req) {
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.
function handle(req) {
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.
}
```
### Options
- `status?` Change the default response status code (200).
- `headers?` Define the headers to use for the response.
- `contentType?` Define content length for the response.
- `contentLength?` Define content length for the response.
- `mtime?` Define the modification `Date` for the response.
- `etag?` Define an ETag for the response (e.g. pre-computed with `entityTag()` or `true` for on-demand).
## TypeScript

@@ -32,0 +48,0 @@

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