@remix-run/session-middleware 0.1.3 → 0.2.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
@@ -1,20 +1,27 @@
1
1
  # session-middleware
2
2
 
3
- Middleware for managing sessions with [`@remix-run/fetch-router`](https://github.com/remix-run/remix/tree/main/packages/fetch-router) via securely signed cookies.
3
+ Session middleware for Remix using signed cookies. It loads session state from incoming requests, stores it in request context using `Session`, and persists updates automatically.
4
+
5
+ ## Features
6
+
7
+ - **Session Lifecycle Handling** - Reads and saves session state per request
8
+ - **Context Integration** - Exposes session APIs directly on request context
9
+ - **Secure Cookie Support** - Designed for signed session cookies
4
10
 
5
11
  ## Installation
6
12
 
7
13
  ```sh
8
- npm install @remix-run/session-middleware
14
+ npm i remix
9
15
  ```
10
16
 
11
17
  ## Usage
12
18
 
13
19
  ```ts
14
- import { createRouter } from '@remix-run/fetch-router'
15
- import { createCookie } from '@remix-run/cookie'
16
- import { createCookieSessionStorage } from '@remix-run/session/cookie-storage'
17
- import { session } from '@remix-run/session-middleware'
20
+ import { createRouter } from 'remix/fetch-router'
21
+ import { createCookie } from 'remix/cookie'
22
+ import { Session } from 'remix/session'
23
+ import { createCookieSessionStorage } from 'remix/session/cookie-storage'
24
+ import { session } from 'remix/session-middleware'
18
25
 
19
26
  let sessionCookie = createCookie('__session', {
20
27
  secrets: ['s3cr3t'], // session cookies must be signed!
@@ -30,15 +37,16 @@ let router = createRouter({
30
37
  })
31
38
 
32
39
  router.get('/', (context) => {
33
- context.session.set('count', Number(context.session.get('count') ?? 0) + 1)
34
- return new Response(`Count: ${context.session.get('count')}`)
40
+ let session = context.get(Session)
41
+ session.set('count', Number(session.get('count') ?? 0) + 1)
42
+ return new Response(`Count: ${session.get('count')}`)
35
43
  })
36
44
  ```
37
45
 
38
46
  The middleware:
39
47
 
40
48
  - Reads the session from the cookie on incoming requests
41
- - Makes it available as `context.session`
49
+ - Makes it available as `context.get(Session)`
42
50
  - Automatically saves session changes and sets the cookie on responses
43
51
 
44
52
  Note: The session cookie must be signed for security. This prevents tampering with the session data on the client.
@@ -48,9 +56,11 @@ Note: The session cookie must be signed for security. This prevents tampering wi
48
56
  A basic login/logout flow could look like this:
49
57
 
50
58
  ```ts
51
- import * as res from '@remix-run/fetch-router/response-helpers'
59
+ import * as res from 'remix/fetch-router/response-helpers'
60
+ import { Session } from 'remix/session'
52
61
 
53
- router.get('/login', ({ session }) => {
62
+ router.get('/login', ({ get }) => {
63
+ let session = get(Session)
54
64
  let error = session.get('error')
55
65
  return res.html(`
56
66
  <html>
@@ -67,7 +77,9 @@ router.get('/login', ({ session }) => {
67
77
  `)
68
78
  })
69
79
 
70
- router.post('/login', ({ session, formData }) => {
80
+ router.post('/login', ({ get }) => {
81
+ let session = get(Session)
82
+ let formData = get(FormData)
71
83
  let username = formData.get('username')
72
84
  let password = formData.get('password')
73
85
 
@@ -83,7 +95,8 @@ router.post('/login', ({ session, formData }) => {
83
95
  return res.redirect('/dashboard')
84
96
  })
85
97
 
86
- router.post('/logout', ({ session }) => {
98
+ router.post('/logout', ({ get }) => {
99
+ let session = get(Session)
87
100
  session.destroy()
88
101
  return res.redirect('/')
89
102
  })
@@ -1,12 +1,14 @@
1
1
  import type { Cookie } from '@remix-run/cookie';
2
2
  import type { Middleware } from '@remix-run/fetch-router';
3
- import type { SessionStorage } from '@remix-run/session';
3
+ import { Session, type SessionStorage } from '@remix-run/session';
4
+ type SetSessionContextTransform = readonly [readonly [typeof Session, Session]];
4
5
  /**
5
- * Middleware that manages `context.session` based on the session cookie.
6
+ * Middleware that manages request session state on request context.
6
7
  *
7
8
  * @param sessionCookie The session cookie to use
8
9
  * @param sessionStorage The storage backend for session data
9
10
  * @returns The session middleware
10
11
  */
11
- export declare function session(sessionCookie: Cookie, sessionStorage: SessionStorage): Middleware;
12
+ export declare function session(sessionCookie: Cookie, sessionStorage: SessionStorage): Middleware<any, any, SetSessionContextTransform>;
13
+ export {};
12
14
  //# sourceMappingURL=session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,cAAc,GAAG,UAAU,CA4BzF"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/lib/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEjE,KAAK,0BAA0B,GAAG,SAAS,CAAC,SAAS,CAAC,OAAO,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;AAE/E;;;;;;GAMG;AACH,wBAAgB,OAAO,CACrB,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,cAAc,GAC7B,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,0BAA0B,CAAC,CA8BlD"}
@@ -1,5 +1,6 @@
1
+ import { Session } from '@remix-run/session';
1
2
  /**
2
- * Middleware that manages `context.session` based on the session cookie.
3
+ * Middleware that manages request session state on request context.
3
4
  *
4
5
  * @param sessionCookie The session cookie to use
5
6
  * @param sessionStorage The storage backend for session data
@@ -10,18 +11,20 @@ export function session(sessionCookie, sessionStorage) {
10
11
  throw new Error('Session cookie must be signed');
11
12
  }
12
13
  return async (context, next) => {
13
- if (context.sessionStarted) {
14
+ if (context.has(Session)) {
14
15
  throw new Error('Existing session found, refusing to overwrite');
15
16
  }
16
17
  let cookieValue = await sessionCookie.parse(context.headers.get('Cookie'));
17
18
  let session = await sessionStorage.read(cookieValue);
18
- context.session = session;
19
+ context.set(Session, session);
19
20
  let response = await next();
20
- if (session !== context.session) {
21
+ if (session !== context.get(Session)) {
21
22
  throw new Error('Cannot save session that was initialized by another middleware/handler');
22
23
  }
23
24
  let setCookieValue = await sessionStorage.save(session);
24
25
  if (setCookieValue != null) {
26
+ // make sure the response is mutable
27
+ response = new Response(response.body, response);
25
28
  response.headers.append('Set-Cookie', await sessionCookie.serialize(setCookieValue));
26
29
  }
27
30
  return response;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remix-run/session-middleware",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Middleware for managing sessions with cookie-based storage",
5
5
  "author": "Michael Jackson <mjijackson@gmail.com>",
6
6
  "license": "MIT",
@@ -28,15 +28,15 @@
28
28
  "devDependencies": {
29
29
  "@types/node": "^24.6.0",
30
30
  "@typescript/native-preview": "7.0.0-dev.20251125.1",
31
- "@remix-run/cookie": "0.5.1",
32
- "@remix-run/headers": "0.19.0",
33
31
  "@remix-run/session": "0.4.1",
34
- "@remix-run/fetch-router": "0.16.0"
32
+ "@remix-run/headers": "0.19.0",
33
+ "@remix-run/cookie": "0.5.1",
34
+ "@remix-run/fetch-router": "0.18.0"
35
35
  },
36
36
  "dependencies": {
37
37
  "@remix-run/cookie": "^0.5.1",
38
- "@remix-run/session": "^0.4.1",
39
- "@remix-run/fetch-router": "^0.16.0"
38
+ "@remix-run/fetch-router": "^0.18.0",
39
+ "@remix-run/session": "^0.4.1"
40
40
  },
41
41
  "keywords": [
42
42
  "fetch",
@@ -49,7 +49,7 @@
49
49
  "scripts": {
50
50
  "build": "tsgo -p tsconfig.build.json",
51
51
  "clean": "git clean -fdX",
52
- "test": "node --disable-warning=ExperimentalWarning --test",
52
+ "test": "node --test",
53
53
  "typecheck": "tsgo --noEmit"
54
54
  }
55
55
  }
@@ -1,37 +1,44 @@
1
1
  import type { Cookie } from '@remix-run/cookie'
2
2
  import type { Middleware } from '@remix-run/fetch-router'
3
- import type { SessionStorage } from '@remix-run/session'
3
+ import { Session, type SessionStorage } from '@remix-run/session'
4
+
5
+ type SetSessionContextTransform = readonly [readonly [typeof Session, Session]]
4
6
 
5
7
  /**
6
- * Middleware that manages `context.session` based on the session cookie.
8
+ * Middleware that manages request session state on request context.
7
9
  *
8
10
  * @param sessionCookie The session cookie to use
9
11
  * @param sessionStorage The storage backend for session data
10
12
  * @returns The session middleware
11
13
  */
12
- export function session(sessionCookie: Cookie, sessionStorage: SessionStorage): Middleware {
14
+ export function session(
15
+ sessionCookie: Cookie,
16
+ sessionStorage: SessionStorage,
17
+ ): Middleware<any, any, SetSessionContextTransform> {
13
18
  if (!sessionCookie.signed) {
14
19
  throw new Error('Session cookie must be signed')
15
20
  }
16
21
 
17
22
  return async (context, next) => {
18
- if (context.sessionStarted) {
23
+ if (context.has(Session)) {
19
24
  throw new Error('Existing session found, refusing to overwrite')
20
25
  }
21
26
 
22
27
  let cookieValue = await sessionCookie.parse(context.headers.get('Cookie'))
23
28
  let session = await sessionStorage.read(cookieValue)
24
29
 
25
- context.session = session
30
+ context.set(Session, session)
26
31
 
27
32
  let response = await next()
28
33
 
29
- if (session !== context.session) {
34
+ if (session !== context.get(Session)) {
30
35
  throw new Error('Cannot save session that was initialized by another middleware/handler')
31
36
  }
32
37
 
33
38
  let setCookieValue = await sessionStorage.save(session)
34
39
  if (setCookieValue != null) {
40
+ // make sure the response is mutable
41
+ response = new Response(response.body, response)
35
42
  response.headers.append('Set-Cookie', await sessionCookie.serialize(setCookieValue))
36
43
  }
37
44