Skip to content

Commit

Permalink
Implementing esmodule syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
avoidwork committed Sep 21, 2022
1 parent a15b164 commit 0417eb6
Show file tree
Hide file tree
Showing 12 changed files with 952 additions and 41 deletions.
115 changes: 115 additions & 0 deletions dist/tiny-etag.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* tiny-etag
*
* @copyright 2022 Jason Mulligan <jason.mulligan@avoidwork.com>
* @license BSD-3-Clause
* @version 3.0.0
*/
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var node_util = require('node:util');
var tinyLru = require('tiny-lru');
var MurmurHash3 = require('murmurhash3js');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

var MurmurHash3__default = /*#__PURE__*/_interopDefaultLegacy(MurmurHash3);

const mmh3 = MurmurHash3__default["default"].x64.hash128;

function clone (arg) {
return JSON.parse(JSON.stringify(arg, null, 0));
}

function keep (arg) {
return arg === "cache-control" || arg === "content-location" || arg === "date" || arg === "etag" || arg === "expires" || arg === "vary";
}

function parse (arg) {
return new node_util.URL(typeof arg === "string" ? arg : `http://${arg.headers.host || `localhost:${arg.socket.server._connectionKey.replace(/.*::/, "")}`}${arg.url}`);
}

class ETag {
constructor (cacheSize, cacheTTL, seed, mimetype) {
this.cache = tinyLru.lru(cacheSize, cacheTTL);
this.mimetype = mimetype;
this.seed = seed;
}

create (arg) {
return `"${mmh3(arg, this.seed)}"`;
}

middleware (req, res, next) {
if (req.method === "GET") {
const uri = (req.parsed || parse(req)).href,
key = this.hash(uri, req.headers.accept),
cached = this.cache.get(key);

res.on("finish", () => {
const headers = res.getHeaders(),
status = res.statusCode;

if ((status === 200 || status === 304) && "etag" in headers && this.valid(headers)) {
this.register(key, {
etag: headers.etag,
headers: headers,
timestamp: cached ? cached.timestamp : Math.floor(new Date().getTime() / 1000)
});
}
});

if (cached !== void 0 && "etag" in cached && "range" in req.headers === false && req.headers["if-none-match"] === cached.etag) {
const headers = clone(cached.headers);

headers.age = Math.floor(new Date().getTime() / 1000) - cached.timestamp;
res.removeHeader("cache-control");
res.send("", 304, headers);
} else {
next();
}
} else {
next();
}
}

hash (arg = "", mimetype = "") {
return this.create(`${arg}_${mimetype || this.mimetype}`);
}

register (key, arg) {
const state = clone(arg);

state.headers = Object.keys(state.headers).filter(i => keep(i.toLowerCase())).reduce((a, v) => {
a[v] = state.headers[v];

return a;
}, {});

this.cache.set(key, state);

return this;
}

unregister (key) {
this.cache.delete(key);
}

valid (headers) {
const header = headers["cache-control"] || "";

return header.length === 0 || (header.includes("no-cache") === false && header.includes("no-store") === false); // eslint-disable-line no-extra-parens
}
}

function etag ({cacheSize = 1e3, cacheTTL = 0, seed = null, mimetype = "text/plain"} = {}) {
const obj = new ETag(cacheSize, cacheTTL, seed !== null ? seed : Math.floor(Math.random() * cacheSize) + 1, mimetype);

obj.middleware = obj.middleware.bind(obj);

return obj;
}

exports.etag = etag;
101 changes: 101 additions & 0 deletions dist/tiny-etag.esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* tiny-etag
*
* @copyright 2022 Jason Mulligan <jason.mulligan@avoidwork.com>
* @license BSD-3-Clause
* @version 3.0.0
*/
import {URL}from'node:util';import {lru}from'tiny-lru';import MurmurHash3 from'murmurhash3js';const mmh3 = MurmurHash3.x64.hash128;

function clone (arg) {
return JSON.parse(JSON.stringify(arg, null, 0));
}

function keep (arg) {
return arg === "cache-control" || arg === "content-location" || arg === "date" || arg === "etag" || arg === "expires" || arg === "vary";
}

function parse (arg) {
return new URL(typeof arg === "string" ? arg : `http://${arg.headers.host || `localhost:${arg.socket.server._connectionKey.replace(/.*::/, "")}`}${arg.url}`);
}

class ETag {
constructor (cacheSize, cacheTTL, seed, mimetype) {
this.cache = lru(cacheSize, cacheTTL);
this.mimetype = mimetype;
this.seed = seed;
}

create (arg) {
return `"${mmh3(arg, this.seed)}"`;
}

middleware (req, res, next) {
if (req.method === "GET") {
const uri = (req.parsed || parse(req)).href,
key = this.hash(uri, req.headers.accept),
cached = this.cache.get(key);

res.on("finish", () => {
const headers = res.getHeaders(),
status = res.statusCode;

if ((status === 200 || status === 304) && "etag" in headers && this.valid(headers)) {
this.register(key, {
etag: headers.etag,
headers: headers,
timestamp: cached ? cached.timestamp : Math.floor(new Date().getTime() / 1000)
});
}
});

if (cached !== void 0 && "etag" in cached && "range" in req.headers === false && req.headers["if-none-match"] === cached.etag) {
const headers = clone(cached.headers);

headers.age = Math.floor(new Date().getTime() / 1000) - cached.timestamp;
res.removeHeader("cache-control");
res.send("", 304, headers);
} else {
next();
}
} else {
next();
}
}

hash (arg = "", mimetype = "") {
return this.create(`${arg}_${mimetype || this.mimetype}`);
}

register (key, arg) {
const state = clone(arg);

state.headers = Object.keys(state.headers).filter(i => keep(i.toLowerCase())).reduce((a, v) => {
a[v] = state.headers[v];

return a;
}, {});

this.cache.set(key, state);

return this;
}

unregister (key) {
this.cache.delete(key);
}

valid (headers) {
const header = headers["cache-control"] || "";

return header.length === 0 || (header.includes("no-cache") === false && header.includes("no-store") === false); // eslint-disable-line no-extra-parens
}
}

function etag ({cacheSize = 1e3, cacheTTL = 0, seed = null, mimetype = "text/plain"} = {}) {
const obj = new ETag(cacheSize, cacheTTL, seed !== null ? seed : Math.floor(Math.random() * cacheSize) + 1, mimetype);

obj.middleware = obj.middleware.bind(obj);

return obj;
}export{etag};
5 changes: 5 additions & 0 deletions dist/tiny-etag.esm.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/tiny-etag.esm.min.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

101 changes: 101 additions & 0 deletions dist/tiny-etag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* tiny-etag
*
* @copyright 2022 Jason Mulligan <jason.mulligan@avoidwork.com>
* @license BSD-3-Clause
* @version 3.0.0
*/
(function(g,f){typeof exports==='object'&&typeof module!=='undefined'?f(exports,require('node:util'),require('tiny-lru'),require('murmurhash3js')):typeof define==='function'&&define.amd?define(['exports','node:util','tiny-lru','murmurhash3js'],f):(g=typeof globalThis!=='undefined'?globalThis:g||self,f(g.etag={},g.node_util,g.tinyLru,g.MurmurHash3));})(this,(function(exports,node_util,tinyLru,MurmurHash3){'use strict';function _interopDefaultLegacy(e){return e&&typeof e==='object'&&'default'in e?e:{'default':e}}var MurmurHash3__default=/*#__PURE__*/_interopDefaultLegacy(MurmurHash3);const mmh3 = MurmurHash3__default["default"].x64.hash128;

function clone (arg) {
return JSON.parse(JSON.stringify(arg, null, 0));
}

function keep (arg) {
return arg === "cache-control" || arg === "content-location" || arg === "date" || arg === "etag" || arg === "expires" || arg === "vary";
}

function parse (arg) {
return new node_util.URL(typeof arg === "string" ? arg : `http://${arg.headers.host || `localhost:${arg.socket.server._connectionKey.replace(/.*::/, "")}`}${arg.url}`);
}

class ETag {
constructor (cacheSize, cacheTTL, seed, mimetype) {
this.cache = tinyLru.lru(cacheSize, cacheTTL);
this.mimetype = mimetype;
this.seed = seed;
}

create (arg) {
return `"${mmh3(arg, this.seed)}"`;
}

middleware (req, res, next) {
if (req.method === "GET") {
const uri = (req.parsed || parse(req)).href,
key = this.hash(uri, req.headers.accept),
cached = this.cache.get(key);

res.on("finish", () => {
const headers = res.getHeaders(),
status = res.statusCode;

if ((status === 200 || status === 304) && "etag" in headers && this.valid(headers)) {
this.register(key, {
etag: headers.etag,
headers: headers,
timestamp: cached ? cached.timestamp : Math.floor(new Date().getTime() / 1000)
});
}
});

if (cached !== void 0 && "etag" in cached && "range" in req.headers === false && req.headers["if-none-match"] === cached.etag) {
const headers = clone(cached.headers);

headers.age = Math.floor(new Date().getTime() / 1000) - cached.timestamp;
res.removeHeader("cache-control");
res.send("", 304, headers);
} else {
next();
}
} else {
next();
}
}

hash (arg = "", mimetype = "") {
return this.create(`${arg}_${mimetype || this.mimetype}`);
}

register (key, arg) {
const state = clone(arg);

state.headers = Object.keys(state.headers).filter(i => keep(i.toLowerCase())).reduce((a, v) => {
a[v] = state.headers[v];

return a;
}, {});

this.cache.set(key, state);