UNPKG

next-session

Version:

Simple promise-based session for Next.js

165 lines (127 loc) 5.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = session; var _cookie = _interopRequireDefault(require("cookie")); var _nanoid = require("nanoid"); var _memoryStore = _interopRequireDefault(require("./memory-store.cjs")); var _symbol = require("./symbol.cjs"); var _utils = require("./utils.cjs"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function session(options = {}) { var _options$touchAfter, _options$autoCommit; const name = options.name || "sid"; const store = options.store || new _memoryStore.default(); const genId = options.genid || _nanoid.nanoid; const encode = options.encode; const decode = options.decode; const touchAfter = (_options$touchAfter = options.touchAfter) !== null && _options$touchAfter !== void 0 ? _options$touchAfter : -1; const autoCommit = (_options$autoCommit = options.autoCommit) !== null && _options$autoCommit !== void 0 ? _options$autoCommit : true; const cookieOpts = options.cookie || {}; function decorateSession(req, res, session, id, _now) { Object.defineProperties(session, { commit: { value: async function commit() { (0, _utils.commitHeader)(res, name, this, encode); await store.set(this.id, this); } }, touch: { value: async function commit() { this.cookie.expires = new Date(_now + this.cookie.maxAge * 1000); this[_symbol.isTouched] = true; } }, destroy: { value: async function destroy() { this[_symbol.isDestroyed] = true; this.cookie.expires = new Date(1); await store.destroy(this.id); if (!autoCommit) (0, _utils.commitHeader)(res, name, this, encode); delete req.session; } }, id: { value: id } }); } return async function sessionHandle(req, res) { var _req$headers; if (req.session) return req.session; const _now = Date.now(); let sessionId = (_req$headers = req.headers) !== null && _req$headers !== void 0 && _req$headers.cookie ? _cookie.default.parse(req.headers.cookie)[name] : null; if (sessionId && decode) { sessionId = decode(sessionId); } const _session = sessionId ? await store.get(sessionId) : null; let session; if (_session) { session = _session; // Some store return cookie.expires as string, convert it to Date if (typeof session.cookie.expires === "string") { session.cookie.expires = new Date(session.cookie.expires); } // Add session methods decorateSession(req, res, session, sessionId, _now); // Extends the expiry of the session if options.touchAfter is sastified if (touchAfter >= 0 && session.cookie.expires) { const lastTouchedTime = session.cookie.expires.getTime() - session.cookie.maxAge * 1000; if (_now - lastTouchedTime >= touchAfter * 1000) { session.touch(); } } } else { var _cookieOpts$httpOnly; sessionId = genId(); session = { [_symbol.isNew]: true, cookie: { path: cookieOpts.path || "/", httpOnly: (_cookieOpts$httpOnly = cookieOpts.httpOnly) !== null && _cookieOpts$httpOnly !== void 0 ? _cookieOpts$httpOnly : true, domain: cookieOpts.domain || undefined, sameSite: cookieOpts.sameSite, secure: cookieOpts.secure || false } }; if (cookieOpts.maxAge) { session.cookie.maxAge = cookieOpts.maxAge; session.cookie.expires = new Date(_now + cookieOpts.maxAge * 1000); } // Add session methods decorateSession(req, res, session, sessionId, _now); } req.session = session; // prevSessStr is used to compare the session later // in autoCommit -- that is, we only save the // session if it has changed. let prevHash; if (autoCommit) { prevHash = (0, _utils.hash)(session); } // autocommit: We commit the header and save the session automatically // by "proxying" res.writeHead and res.end methods. After committing, we // call the original res.writeHead and res.end. if (autoCommit) { const _writeHead = res.writeHead; res.writeHead = function resWriteHeadProxy(...args) { // Commit the header if either: // - session is new and has been populated // - session is flagged to commit header (touched or destroyed) if (session[_symbol.isNew] && Object.keys(session).length > 1 || session[_symbol.isTouched] || session[_symbol.isDestroyed]) { (0, _utils.commitHeader)(res, name, session, encode); } return _writeHead.apply(this, args); }; const _end = res.end; res.end = function resEndProxy(...args) { const done = () => _end.apply(this, args); if (session[_symbol.isDestroyed]) { done(); } else if ((0, _utils.hash)(session) !== prevHash) { store.set(session.id, session).finally(done); } else if (session[_symbol.isTouched] && store.touch) { store.touch(session.id, session).finally(done); } else { done(); } }; } return session; }; }