hono-sessions 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -74,8 +74,11 @@ const store = new CookieStore()
74
74
 
75
75
  app.use('*', sessionMiddleware({
76
76
  store,
77
- encryptionKey: 'password_at_least_32_characters_long', // Required for CookieStore, recommended for others
77
+ encryptionKey: 'password_at_least_32_characters_long', // Required for CookieStore, recommended for others.
78
+ // You can also supply a function instead of a plain string
79
+ // encryptionKey: () => 'function_that_returns_a_long_string'
78
80
  expireAfterSeconds: 900, // Expire session after 15 minutes of inactivity
81
+ autoExtendExpiration: true, // Extend the session expiration time automatically. Defaults to true
79
82
  cookieOptions: {
80
83
  sameSite: 'Lax', // Recommended for basic CSRF protection in modern browsers
81
84
  path: '/', // Required for this library to work properly
@@ -91,6 +94,14 @@ app.get('/', async (c, next) => {
91
94
  return c.html(`<h1>You have visited this page ${ session.get('counter') } times</h1>`)
92
95
  })
93
96
 
97
+ app.get('/read', (c) => {
98
+ const session = c.get('session')
99
+ session.touch() // Update the session expiration time
100
+ return c.json({
101
+ counter: session.get('counter')
102
+ })
103
+ })
104
+
94
105
  Deno.serve(app.fetch)
95
106
  ```
96
107
 
package/esm/deps.d.ts CHANGED
@@ -3,4 +3,5 @@ export { createMiddleware } from 'hono/factory';
3
3
  export { getCookie, setCookie } from 'hono/cookie';
4
4
  export type { CookieOptions } from 'hono/utils/cookie';
5
5
  export * as Iron from 'iron-webcrypto';
6
+ export { hash } from 'ohash';
6
7
  //# sourceMappingURL=deps.d.ts.map
package/esm/deps.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../src/deps.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../src/deps.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA"}
package/esm/deps.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { createMiddleware } from 'hono/factory';
2
2
  export { getCookie, setCookie } from 'hono/cookie';
3
3
  export * as Iron from 'iron-webcrypto';
4
+ export { hash } from 'ohash';
@@ -1 +1 @@
1
- {"version":3,"file":"Middleware.d.ts","sourceRoot":"","sources":["../../src/src/Middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGtF,OAAO,cAAc,MAAM,qBAAqB,CAAA;AAEhD,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,CAyI5E"}
1
+ {"version":3,"file":"Middleware.d.ts","sourceRoot":"","sources":["../../src/src/Middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGtF,OAAO,cAAc,MAAM,qBAAqB,CAAA;AAEhD,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,CAkK5E"}
@@ -4,10 +4,22 @@ import { Session, encrypt, decrypt } from '../mod.js';
4
4
  /** Function that returns a Hono-compatible session middleware */
5
5
  export function sessionMiddleware(options) {
6
6
  const store = options.store;
7
- const encryptionKey = options.encryptionKey;
7
+ let encryptionKey;
8
8
  const expireAfterSeconds = options.expireAfterSeconds;
9
9
  const cookieOptions = options.cookieOptions;
10
10
  const sessionCookieName = options.sessionCookieName || 'session';
11
+ const autoExtendExpiration = options.autoExtendExpiration ?? true;
12
+ if (options.encryptionKey !== undefined) {
13
+ if (typeof options.encryptionKey === 'function') {
14
+ encryptionKey = options.encryptionKey();
15
+ }
16
+ else {
17
+ encryptionKey = options.encryptionKey;
18
+ }
19
+ }
20
+ else {
21
+ encryptionKey = undefined;
22
+ }
11
23
  if (store instanceof CookieStore) {
12
24
  store.sessionCookieName = sessionCookieName;
13
25
  if (encryptionKey) {
@@ -21,7 +33,7 @@ export function sessionMiddleware(options) {
21
33
  }
22
34
  }
23
35
  const middleware = createMiddleware(async (c, next) => {
24
- const session = new Session;
36
+ const session = new Session(expireAfterSeconds);
25
37
  let sid = '';
26
38
  let session_data;
27
39
  let createNewSession = false;
@@ -34,6 +46,9 @@ export function sessionMiddleware(options) {
34
46
  try {
35
47
  sid = (encryptionKey ? await decrypt(encryptionKey, sessionCookie) : sessionCookie);
36
48
  session_data = await store.getSessionById(sid);
49
+ if (session_data) {
50
+ session_data._id = sid;
51
+ }
37
52
  }
38
53
  catch {
39
54
  createNewSession = true;
@@ -42,7 +57,9 @@ export function sessionMiddleware(options) {
42
57
  if (session_data) {
43
58
  session.setCache(session_data);
44
59
  if (session.sessionValid()) {
45
- session.reupSession(expireAfterSeconds);
60
+ if (autoExtendExpiration) {
61
+ session.reupSession();
62
+ }
46
63
  }
47
64
  else {
48
65
  store instanceof CookieStore ? await store.deleteSession(c) : await store.deleteSession(sid);
@@ -68,16 +85,22 @@ export function sessionMiddleware(options) {
68
85
  }
69
86
  else {
70
87
  sid = globalThis.crypto.randomUUID();
88
+ defaultData._id = sid;
71
89
  await store.createSession(sid, defaultData);
72
90
  }
73
- session.setCache(defaultData);
91
+ session.setCache(defaultData, true);
74
92
  }
75
93
  if (!(store instanceof CookieStore)) {
76
94
  setCookie(c, sessionCookieName, encryptionKey ? await encrypt(encryptionKey, sid) : sid, cookieOptions);
77
95
  }
78
- session.updateAccess();
96
+ if (autoExtendExpiration) {
97
+ session.updateAccess();
98
+ }
79
99
  c.set('session', session);
80
100
  await next();
101
+ if (session.isStale()) {
102
+ session.touch();
103
+ }
81
104
  const shouldDelete = session.getCache()._delete;
82
105
  const shouldRotateSessionKey = c.get("session_key_rotation") === true;
83
106
  const storeIsCookieStore = store instanceof CookieStore;
@@ -106,7 +129,8 @@ export function sessionMiddleware(options) {
106
129
  * or the store is a CookieStore (which does not have its session key rotated)
107
130
  */
108
131
  const shouldPersistSession = !shouldDelete &&
109
- (!shouldRotateSessionKey || storeIsCookieStore);
132
+ (!shouldRotateSessionKey || storeIsCookieStore) &&
133
+ session.isStale();
110
134
  if (shouldPersistSession) {
111
135
  store instanceof CookieStore
112
136
  ? await store.persistSessionData(c, session.getCache())
@@ -3,6 +3,7 @@ interface SessionDataEntry<T> {
3
3
  flash: boolean;
4
4
  }
5
5
  export interface SessionData<T = any> {
6
+ _id?: string;
6
7
  _data: Record<string, SessionDataEntry<T>>;
7
8
  _expire: string | null;
8
9
  _delete: boolean;
@@ -10,13 +11,26 @@ export interface SessionData<T = any> {
10
11
  }
11
12
  export declare class Session<T = any> {
12
13
  private cache;
13
- constructor();
14
- setCache(cache_data: SessionData<T>): void;
14
+ private expiration;
15
+ private hash;
16
+ constructor(expiration?: number);
17
+ setCache(cache_data: SessionData<T>, isNew?: boolean): void;
18
+ isStale(): boolean;
15
19
  getCache(): SessionData<T>;
16
20
  setExpiration(expiration: string): void;
17
- reupSession(expiration: number | null | undefined): void;
21
+ /**
22
+ * Extend expiration
23
+ */
24
+ reupSession(): void;
25
+ /**
26
+ * Extend session expiration and update access time
27
+ */
28
+ touch(): void;
18
29
  deleteSession(): void;
19
30
  sessionValid(): boolean;
31
+ /**
32
+ * Update the last accessed time
33
+ */
20
34
  updateAccess(): void;
21
35
  get<K extends keyof T>(key: K): T[K] | null;
22
36
  set<K extends keyof T>(key: K, value: T[K]): void;
@@ -1 +1 @@
1
- {"version":3,"file":"Session.d.ts","sourceRoot":"","sources":["../../src/src/Session.ts"],"names":[],"mappings":"AAAA,UAAU,gBAAgB,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,qBAAa,OAAO,CAAC,CAAC,GAAG,GAAG;IAE1B,OAAO,CAAC,KAAK,CAAgB;;IAW7B,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;IAInC,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC;IAI1B,aAAa,CAAC,UAAU,EAAE,MAAM;IAIhC,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAMjD,aAAa;IAIb,YAAY,IAAI,OAAO;IAIvB,YAAY;IAIZ,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAe3C,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAS1C,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IAMhC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;CAQ7C"}
1
+ {"version":3,"file":"Session.d.ts","sourceRoot":"","sources":["../../src/src/Session.ts"],"names":[],"mappings":"AAEA,UAAU,gBAAgB,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,qBAAa,OAAO,CAAC,CAAC,GAAG,GAAG;IAE1B,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,IAAI,CAAsB;gBAEtB,UAAU,CAAC,EAAE,MAAM;IAU/B,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,GAAE,OAAe;IAK3D,OAAO,IAAI,OAAO;IAIlB,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC;IAI1B,aAAa,CAAC,UAAU,EAAE,MAAM;IAIhC;;OAEG;IACH,WAAW;IAMX;;OAEG;IACH,KAAK;IAKL,aAAa;IAIb,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,YAAY;IAIZ,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAe3C,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAS1C,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IAMhC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;CAQ7C"}
@@ -1,11 +1,25 @@
1
+ import { hash } from '../deps.js';
1
2
  export class Session {
2
- constructor() {
3
+ constructor(expiration) {
3
4
  Object.defineProperty(this, "cache", {
4
5
  enumerable: true,
5
6
  configurable: true,
6
7
  writable: true,
7
8
  value: void 0
8
9
  });
10
+ Object.defineProperty(this, "expiration", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: void 0
15
+ });
16
+ Object.defineProperty(this, "hash", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: null
21
+ });
22
+ this.expiration = expiration;
9
23
  this.cache = {
10
24
  _data: {},
11
25
  _expire: null,
@@ -13,26 +27,43 @@ export class Session {
13
27
  _accessed: null,
14
28
  };
15
29
  }
16
- setCache(cache_data) {
30
+ setCache(cache_data, isNew = false) {
31
+ this.hash = !isNew ? hash(cache_data) : null;
17
32
  this.cache = cache_data;
18
33
  }
34
+ isStale() {
35
+ return !this.hash || this.hash !== hash(this.cache);
36
+ }
19
37
  getCache() {
20
38
  return this.cache;
21
39
  }
22
40
  setExpiration(expiration) {
23
41
  this.cache._expire = expiration;
24
42
  }
25
- reupSession(expiration) {
26
- if (expiration) {
27
- this.setExpiration(new Date(Date.now() + expiration * 1000).toISOString());
43
+ /**
44
+ * Extend expiration
45
+ */
46
+ reupSession() {
47
+ if (this.expiration) {
48
+ this.setExpiration(new Date(Date.now() + this.expiration * 1000).toISOString());
28
49
  }
29
50
  }
51
+ /**
52
+ * Extend session expiration and update access time
53
+ */
54
+ touch() {
55
+ this.reupSession();
56
+ this.updateAccess();
57
+ }
30
58
  deleteSession() {
31
59
  this.cache._delete = true;
32
60
  }
33
61
  sessionValid() {
34
62
  return this.cache._expire == null || Date.now() < new Date(this.cache._expire).getTime();
35
63
  }
64
+ /**
65
+ * Update the last accessed time
66
+ */
36
67
  updateAccess() {
37
68
  this.cache._accessed = new Date().toISOString();
38
69
  }
@@ -3,9 +3,10 @@ import CookieStore from './store/CookieStore.js';
3
3
  import { CookieOptions } from '../deps.js';
4
4
  export default interface SessionOptions {
5
5
  store: Store | CookieStore;
6
- encryptionKey?: string;
6
+ encryptionKey?: string | (() => string);
7
7
  expireAfterSeconds?: number;
8
8
  cookieOptions?: CookieOptions;
9
9
  sessionCookieName?: string;
10
+ autoExtendExpiration?: boolean;
10
11
  }
11
12
  //# sourceMappingURL=SessionOptions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SessionOptions.d.ts","sourceRoot":"","sources":["../../src/src/SessionOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,kBAAkB,CAAA;AACpC,OAAO,WAAW,MAAM,wBAAwB,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,CAAC,OAAO,WAAW,cAAc;IACrC,KAAK,EAAE,KAAK,GAAG,WAAW,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B"}
1
+ {"version":3,"file":"SessionOptions.d.ts","sourceRoot":"","sources":["../../src/src/SessionOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,kBAAkB,CAAA;AACpC,OAAO,WAAW,MAAM,wBAAwB,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,CAAC,OAAO,WAAW,cAAc;IACrC,KAAK,EAAE,KAAK,GAAG,WAAW,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono-sessions",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "Cookie-based sessions for Hono web framework",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,7 +24,8 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "hono": "^4.0.0",
27
- "iron-webcrypto": "^1.2.1"
27
+ "iron-webcrypto": "^1.2.1",
28
+ "ohash": "^2"
28
29
  },
29
30
  "_generatedBy": "dnt@dev"
30
31
  }
package/script/deps.d.ts CHANGED
@@ -3,4 +3,5 @@ export { createMiddleware } from 'hono/factory';
3
3
  export { getCookie, setCookie } from 'hono/cookie';
4
4
  export type { CookieOptions } from 'hono/utils/cookie';
5
5
  export * as Iron from 'iron-webcrypto';
6
+ export { hash } from 'ohash';
6
7
  //# sourceMappingURL=deps.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../src/deps.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAA"}
1
+ {"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../src/deps.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAA;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,IAAI,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA"}
package/script/deps.js CHANGED
@@ -23,10 +23,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.Iron = exports.setCookie = exports.getCookie = exports.createMiddleware = void 0;
26
+ exports.hash = exports.Iron = exports.setCookie = exports.getCookie = exports.createMiddleware = void 0;
27
27
  var factory_1 = require("hono/factory");
28
28
  Object.defineProperty(exports, "createMiddleware", { enumerable: true, get: function () { return factory_1.createMiddleware; } });
29
29
  var cookie_1 = require("hono/cookie");
30
30
  Object.defineProperty(exports, "getCookie", { enumerable: true, get: function () { return cookie_1.getCookie; } });
31
31
  Object.defineProperty(exports, "setCookie", { enumerable: true, get: function () { return cookie_1.setCookie; } });
32
32
  exports.Iron = __importStar(require("iron-webcrypto"));
33
+ var ohash_1 = require("ohash");
34
+ Object.defineProperty(exports, "hash", { enumerable: true, get: function () { return ohash_1.hash; } });
@@ -1 +1 @@
1
- {"version":3,"file":"Middleware.d.ts","sourceRoot":"","sources":["../../src/src/Middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGtF,OAAO,cAAc,MAAM,qBAAqB,CAAA;AAEhD,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,CAyI5E"}
1
+ {"version":3,"file":"Middleware.d.ts","sourceRoot":"","sources":["../../src/src/Middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGtF,OAAO,cAAc,MAAM,qBAAqB,CAAA;AAEhD,iEAAiE;AACjE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,iBAAiB,CAkK5E"}
@@ -10,10 +10,22 @@ const mod_js_1 = require("../mod.js");
10
10
  /** Function that returns a Hono-compatible session middleware */
11
11
  function sessionMiddleware(options) {
12
12
  const store = options.store;
13
- const encryptionKey = options.encryptionKey;
13
+ let encryptionKey;
14
14
  const expireAfterSeconds = options.expireAfterSeconds;
15
15
  const cookieOptions = options.cookieOptions;
16
16
  const sessionCookieName = options.sessionCookieName || 'session';
17
+ const autoExtendExpiration = options.autoExtendExpiration ?? true;
18
+ if (options.encryptionKey !== undefined) {
19
+ if (typeof options.encryptionKey === 'function') {
20
+ encryptionKey = options.encryptionKey();
21
+ }
22
+ else {
23
+ encryptionKey = options.encryptionKey;
24
+ }
25
+ }
26
+ else {
27
+ encryptionKey = undefined;
28
+ }
17
29
  if (store instanceof CookieStore_js_1.default) {
18
30
  store.sessionCookieName = sessionCookieName;
19
31
  if (encryptionKey) {
@@ -27,7 +39,7 @@ function sessionMiddleware(options) {
27
39
  }
28
40
  }
29
41
  const middleware = (0, deps_js_1.createMiddleware)(async (c, next) => {
30
- const session = new mod_js_1.Session;
42
+ const session = new mod_js_1.Session(expireAfterSeconds);
31
43
  let sid = '';
32
44
  let session_data;
33
45
  let createNewSession = false;
@@ -40,6 +52,9 @@ function sessionMiddleware(options) {
40
52
  try {
41
53
  sid = (encryptionKey ? await (0, mod_js_1.decrypt)(encryptionKey, sessionCookie) : sessionCookie);
42
54
  session_data = await store.getSessionById(sid);
55
+ if (session_data) {
56
+ session_data._id = sid;
57
+ }
43
58
  }
44
59
  catch {
45
60
  createNewSession = true;
@@ -48,7 +63,9 @@ function sessionMiddleware(options) {
48
63
  if (session_data) {
49
64
  session.setCache(session_data);
50
65
  if (session.sessionValid()) {
51
- session.reupSession(expireAfterSeconds);
66
+ if (autoExtendExpiration) {
67
+ session.reupSession();
68
+ }
52
69
  }
53
70
  else {
54
71
  store instanceof CookieStore_js_1.default ? await store.deleteSession(c) : await store.deleteSession(sid);
@@ -74,16 +91,22 @@ function sessionMiddleware(options) {
74
91
  }
75
92
  else {
76
93
  sid = globalThis.crypto.randomUUID();
94
+ defaultData._id = sid;
77
95
  await store.createSession(sid, defaultData);
78
96
  }
79
- session.setCache(defaultData);
97
+ session.setCache(defaultData, true);
80
98
  }
81
99
  if (!(store instanceof CookieStore_js_1.default)) {
82
100
  (0, deps_js_1.setCookie)(c, sessionCookieName, encryptionKey ? await (0, mod_js_1.encrypt)(encryptionKey, sid) : sid, cookieOptions);
83
101
  }
84
- session.updateAccess();
102
+ if (autoExtendExpiration) {
103
+ session.updateAccess();
104
+ }
85
105
  c.set('session', session);
86
106
  await next();
107
+ if (session.isStale()) {
108
+ session.touch();
109
+ }
87
110
  const shouldDelete = session.getCache()._delete;
88
111
  const shouldRotateSessionKey = c.get("session_key_rotation") === true;
89
112
  const storeIsCookieStore = store instanceof CookieStore_js_1.default;
@@ -112,7 +135,8 @@ function sessionMiddleware(options) {
112
135
  * or the store is a CookieStore (which does not have its session key rotated)
113
136
  */
114
137
  const shouldPersistSession = !shouldDelete &&
115
- (!shouldRotateSessionKey || storeIsCookieStore);
138
+ (!shouldRotateSessionKey || storeIsCookieStore) &&
139
+ session.isStale();
116
140
  if (shouldPersistSession) {
117
141
  store instanceof CookieStore_js_1.default
118
142
  ? await store.persistSessionData(c, session.getCache())
@@ -3,6 +3,7 @@ interface SessionDataEntry<T> {
3
3
  flash: boolean;
4
4
  }
5
5
  export interface SessionData<T = any> {
6
+ _id?: string;
6
7
  _data: Record<string, SessionDataEntry<T>>;
7
8
  _expire: string | null;
8
9
  _delete: boolean;
@@ -10,13 +11,26 @@ export interface SessionData<T = any> {
10
11
  }
11
12
  export declare class Session<T = any> {
12
13
  private cache;
13
- constructor();
14
- setCache(cache_data: SessionData<T>): void;
14
+ private expiration;
15
+ private hash;
16
+ constructor(expiration?: number);
17
+ setCache(cache_data: SessionData<T>, isNew?: boolean): void;
18
+ isStale(): boolean;
15
19
  getCache(): SessionData<T>;
16
20
  setExpiration(expiration: string): void;
17
- reupSession(expiration: number | null | undefined): void;
21
+ /**
22
+ * Extend expiration
23
+ */
24
+ reupSession(): void;
25
+ /**
26
+ * Extend session expiration and update access time
27
+ */
28
+ touch(): void;
18
29
  deleteSession(): void;
19
30
  sessionValid(): boolean;
31
+ /**
32
+ * Update the last accessed time
33
+ */
20
34
  updateAccess(): void;
21
35
  get<K extends keyof T>(key: K): T[K] | null;
22
36
  set<K extends keyof T>(key: K, value: T[K]): void;
@@ -1 +1 @@
1
- {"version":3,"file":"Session.d.ts","sourceRoot":"","sources":["../../src/src/Session.ts"],"names":[],"mappings":"AAAA,UAAU,gBAAgB,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,qBAAa,OAAO,CAAC,CAAC,GAAG,GAAG;IAE1B,OAAO,CAAC,KAAK,CAAgB;;IAW7B,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;IAInC,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC;IAI1B,aAAa,CAAC,UAAU,EAAE,MAAM;IAIhC,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAMjD,aAAa;IAIb,YAAY,IAAI,OAAO;IAIvB,YAAY;IAIZ,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAe3C,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAS1C,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IAMhC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;CAQ7C"}
1
+ {"version":3,"file":"Session.d.ts","sourceRoot":"","sources":["../../src/src/Session.ts"],"names":[],"mappings":"AAEA,UAAU,gBAAgB,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,qBAAa,OAAO,CAAC,CAAC,GAAG,GAAG;IAE1B,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,IAAI,CAAsB;gBAEtB,UAAU,CAAC,EAAE,MAAM;IAU/B,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,GAAE,OAAe;IAK3D,OAAO,IAAI,OAAO;IAIlB,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC;IAI1B,aAAa,CAAC,UAAU,EAAE,MAAM;IAIhC;;OAEG;IACH,WAAW;IAMX;;OAEG;IACH,KAAK;IAKL,aAAa;IAIb,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,YAAY;IAIZ,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAe3C,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAS1C,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IAMhC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;CAQ7C"}
@@ -1,14 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Session = void 0;
4
+ const deps_js_1 = require("../deps.js");
4
5
  class Session {
5
- constructor() {
6
+ constructor(expiration) {
6
7
  Object.defineProperty(this, "cache", {
7
8
  enumerable: true,
8
9
  configurable: true,
9
10
  writable: true,
10
11
  value: void 0
11
12
  });
13
+ Object.defineProperty(this, "expiration", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: void 0
18
+ });
19
+ Object.defineProperty(this, "hash", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: null
24
+ });
25
+ this.expiration = expiration;
12
26
  this.cache = {
13
27
  _data: {},
14
28
  _expire: null,
@@ -16,26 +30,43 @@ class Session {
16
30
  _accessed: null,
17
31
  };
18
32
  }
19
- setCache(cache_data) {
33
+ setCache(cache_data, isNew = false) {
34
+ this.hash = !isNew ? (0, deps_js_1.hash)(cache_data) : null;
20
35
  this.cache = cache_data;
21
36
  }
37
+ isStale() {
38
+ return !this.hash || this.hash !== (0, deps_js_1.hash)(this.cache);
39
+ }
22
40
  getCache() {
23
41
  return this.cache;
24
42
  }
25
43
  setExpiration(expiration) {
26
44
  this.cache._expire = expiration;
27
45
  }
28
- reupSession(expiration) {
29
- if (expiration) {
30
- this.setExpiration(new Date(Date.now() + expiration * 1000).toISOString());
46
+ /**
47
+ * Extend expiration
48
+ */
49
+ reupSession() {
50
+ if (this.expiration) {
51
+ this.setExpiration(new Date(Date.now() + this.expiration * 1000).toISOString());
31
52
  }
32
53
  }
54
+ /**
55
+ * Extend session expiration and update access time
56
+ */
57
+ touch() {
58
+ this.reupSession();
59
+ this.updateAccess();
60
+ }
33
61
  deleteSession() {
34
62
  this.cache._delete = true;
35
63
  }
36
64
  sessionValid() {
37
65
  return this.cache._expire == null || Date.now() < new Date(this.cache._expire).getTime();
38
66
  }
67
+ /**
68
+ * Update the last accessed time
69
+ */
39
70
  updateAccess() {
40
71
  this.cache._accessed = new Date().toISOString();
41
72
  }
@@ -3,9 +3,10 @@ import CookieStore from './store/CookieStore.js';
3
3
  import { CookieOptions } from '../deps.js';
4
4
  export default interface SessionOptions {
5
5
  store: Store | CookieStore;
6
- encryptionKey?: string;
6
+ encryptionKey?: string | (() => string);
7
7
  expireAfterSeconds?: number;
8
8
  cookieOptions?: CookieOptions;
9
9
  sessionCookieName?: string;
10
+ autoExtendExpiration?: boolean;
10
11
  }
11
12
  //# sourceMappingURL=SessionOptions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SessionOptions.d.ts","sourceRoot":"","sources":["../../src/src/SessionOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,kBAAkB,CAAA;AACpC,OAAO,WAAW,MAAM,wBAAwB,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,CAAC,OAAO,WAAW,cAAc;IACrC,KAAK,EAAE,KAAK,GAAG,WAAW,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B"}
1
+ {"version":3,"file":"SessionOptions.d.ts","sourceRoot":"","sources":["../../src/src/SessionOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,kBAAkB,CAAA;AACpC,OAAO,WAAW,MAAM,wBAAwB,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,CAAC,OAAO,WAAW,cAAc;IACrC,KAAK,EAAE,KAAK,GAAG,WAAW,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IACxC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B"}