next-session
Version:
Simple promise-based session for Next.js
165 lines (127 loc) • 5.29 kB
JavaScript
;
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;
};
}