UNPKG

hono-sess

Version:

A Simple Session Middleware for Hono

294 lines 11.9 kB
'use strict'; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MemoryStore = exports.Session = exports.Cookie = exports.Store = void 0; const cookie_1 = require("hono/cookie"); const crypto_1 = require("crypto"); const cookie_2 = require("./cookie"); Object.defineProperty(exports, "Cookie", { enumerable: true, get: function () { return cookie_2.Cookie; } }); const memory_1 = require("./memory"); Object.defineProperty(exports, "MemoryStore", { enumerable: true, get: function () { return memory_1.MemoryStore; } }); const session_1 = require("./session"); Object.defineProperty(exports, "Session", { enumerable: true, get: function () { return session_1.Session; } }); const store_1 = require("./store"); Object.defineProperty(exports, "Store", { enumerable: true, get: function () { return store_1.Store; } }); const utils_1 = require("./utils"); let env = process.env.NODE_ENV; __exportStar(require("./types"), exports); const session = ({ cookie = {}, genid = crypto_1.default.randomUUID, name = 'connect.sid', store = new memory_1.MemoryStore(), proxy = false, resave = false, rolling = false, saveUninitialized = true, secret = 'dev-secret', unset = 'keep', }) => { if (typeof genid !== 'function') throw new TypeError('genid option must be a function'); if (resave === undefined) { (0, utils_1.deprecate)('undefined resave option; provide resave option'); resave = true; } if (saveUninitialized === undefined) { (0, utils_1.deprecate)('undefined saveUninitialized option; provide saveUninitialized option'); saveUninitialized = true; } if (unset !== 'destroy' && unset !== 'keep') throw new TypeError('unset option must be "destroy" or "keep"'); const unsetDestroy = unset === 'destroy'; if (Array.isArray(secret) && secret.length === 0) { throw new TypeError('secret option array must contain one or more strings'); } if (secret && !Array.isArray(secret)) secret = [secret]; if (!secret) (0, utils_1.deprecate)('req.secret; provide secret option'); if (env === 'production' && store instanceof memory_1.MemoryStore) console.warn(utils_1.warning); store.generate = function (req) { req.sessionID = genid(); req.session = new session_1.Session(req, null); req.session.cookie = new cookie_2.Cookie(cookie); req.raw.sessionID = req.sessionID; if (cookie.secure === 'auto') { req.session.cookie.secure = (0, utils_1.issecure)(req, proxy); } }; const storeImplementsTouch = typeof store.touch === 'function'; let storeReady = true; store.on('disconnect', function ondisconnect() { storeReady = false; }); store.on('connect', function onconnect() { storeReady = true; }); return async function session(context, next) { const c = context; if (c.req.session) { return await next(); } if (!storeReady) { (0, utils_1.debug)('store is disconnected'); return await next(); } if (c.req.path.indexOf(cookie.path || '/') !== 0) { (0, utils_1.debug)('pathname mismatch'); return await next(); } if (!secret) { console.error('secret option required for sessions'); return await next(); } let secrets = secret; let cookieId = null; let originalHash = null; let originalId = null; let savedHash = null; let touched = false; c.req.sessionStore = store; const signedCookie = (((await (0, cookie_1.getSignedCookie)(c, secrets.join(' '), name)) || undefined)); cookieId = signedCookie; if (cookieId) { c.req.sessionID = cookieId; } const getNext = () => next().then(async (nextResult) => { if (shouldDestroy(c.req)) { (0, utils_1.debug)('destroying'); await new Promise((resolve, reject) => { store.destroy(c.req.sessionID, (err) => { if (err) reject(err); (0, utils_1.debug)('destroyed'); resolve(); }); }); } if (!c.req.session) { (0, utils_1.debug)('no session at post next'); return nextResult; } if (!touched) { c.req.session.touch(); touched = true; } if (shouldSave(c.req)) { await new Promise((resolve, reject) => { c.req.session.save((err) => { if (err) { reject(err); } resolve(); }); }); } else if (storeImplementsTouch && shouldTouch(c.req)) { (0, utils_1.debug)('touching'); await new Promise((resolve, reject) => { store.touch?.(c.req.sessionID, c.req.session, (err) => { if (err) { reject(err); } (0, utils_1.debug)('touched'); resolve(); }); }); } await handleCookies(); return nextResult; }); const handleCookies = async () => { if (!c.req.session) { (0, utils_1.debug)('no session'); return; } if (!shouldSetCookie(c.req)) { (0, utils_1.debug)('should not set cookie'); return; } if (c.req.session.cookie.secure && !(0, utils_1.issecure)(c.req, proxy)) { (0, utils_1.debug)('not secured'); return; } if (!touched) { c.req.session.touch(); touched = true; (0, utils_1.debug)('touched'); } try { (0, utils_1.debug)('setting cookie'); const cookieData = c.req.session.cookie.data; await (0, cookie_1.setSignedCookie)(c, name, c.req.sessionID, secrets.join(' '), (0, utils_1.expressCookieOptionsToHonoCookieOptions)(cookieData, c.req, proxy)); return; } catch (err) { console.error(err); return; } }; function generate() { (0, utils_1.debug)('generating'); store.generate(c.req); originalId = c.req.sessionID; originalHash = (0, utils_1.hash)(c.req.session); wrapmethods(c.req.session); } function inflate(req, session) { (0, utils_1.debug)('inflating'); store.createSession(req, session); originalId = req.sessionID; originalHash = (0, utils_1.hash)(session); if (!resave) { savedHash = originalHash; } wrapmethods(session); } function rewrapmethods(session, callback) { (0, utils_1.debug)('rewrapmethods'); return function () { if (c.req.session !== session) { wrapmethods(c.req.session); } callback.apply(this, arguments); }; } function wrapmethods(session) { const _reload = session.reload; const _save = session.save; function reload(callback) { (0, utils_1.debug)('reloading %s', session.id); _reload.call(session, rewrapmethods(session, callback)); } function save() { (0, utils_1.debug)('saving %s', session.id); savedHash = (0, utils_1.hash)(session); _save.apply(session, arguments); } Object.defineProperty(session, 'reload', { configurable: true, enumerable: false, value: reload, writable: true, }); Object.defineProperty(session, 'save', { configurable: true, enumerable: false, value: save, writable: true, }); } function isModified(session) { return originalId !== session.id || originalHash !== (0, utils_1.hash)(session); } function isSaved(session) { return originalId === session.id && savedHash === (0, utils_1.hash)(session); } function shouldDestroy(req) { return req.sessionID && unsetDestroy && req.session == null; } function shouldSave(req) { if (typeof req.sessionID !== 'string') { (0, utils_1.debug)('session ignored because of bogus req.sessionID %o', req.sessionID); return false; } return !saveUninitialized && !savedHash && cookieId !== req.sessionID ? isModified(req.session) : !isSaved(req.session); } function shouldTouch(req) { if (typeof req.sessionID !== 'string') { (0, utils_1.debug)('session ignored because of bogus req.sessionID %o', req.sessionID); return false; } return cookieId === req.sessionID && !shouldSave(req); } function shouldSetCookie(req) { if (typeof req.sessionID !== 'string') { (0, utils_1.debug)('session ignored because of bogus req.sessionID %o'); return false; } return cookieId !== req.sessionID ? saveUninitialized || isModified(req.session) : rolling || (req.session.cookie.expires != null && isModified(req.session)); } if (!c.req.sessionID) { (0, utils_1.debug)('no SID sent, generating session'); generate(); return getNext(); } (0, utils_1.debug)('fetching %s', c.req.sessionID); return new Promise((resolve) => { store.get(c.req.sessionID, (err, session) => { if (err && err.code !== 'ENOENT') { (0, utils_1.debug)('error %j', err); resolve(getNext()); return; } try { if (err || !session) { (0, utils_1.debug)('no session found'); generate(); } else { (0, utils_1.debug)('session found'); inflate(c.req, session); } } catch (e) { console.error(e); resolve(getNext()); return; } resolve(getNext()); }); }); }; }; exports.default = session; //# sourceMappingURL=index.js.map