Skip to content

Commit fdb080b

Browse files
committed
fix: support for express-json-cookies
1 parent 3e52d91 commit fdb080b

7 files changed

Lines changed: 88 additions & 6 deletions

File tree

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@
118118
"import": "./dist/utils/auth.es.js",
119119
"require": "./dist/utils/auth.cjs.js"
120120
},
121+
"./utils/cookies": {
122+
"types": "./dist/utils/cookies.d.ts",
123+
"import": "./dist/utils/cookies.es.js",
124+
"require": "./dist/utils/cookies.cjs.js"
125+
},
121126
"./utils/form": {
122127
"types": "./dist/utils/form.d.ts",
123128
"import": "./dist/utils/form.es.js",

src/server/entry/client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
} from "./common"
2121
import DefaultRoutes from "../DefaultRoutes"
2222

23-
export function client({
23+
export default function client({
2424
App,
2525
routes,
2626
catchAllRoute = DEFAULT_CATCH_ALL,

src/server/entry/server.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
import DefaultRoutes from "../DefaultRoutes"
2424
import packageJson from "../../../package.json"
2525

26-
export async function server({
26+
export default async function server({
2727
App,
2828
routes,
2929
catchAllRoute = DEFAULT_CATCH_ALL,

src/utils/auth.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Cookies from "js-cookie"
1+
import cookies from "../utils/cookies"
22

33
import {
44
getCsrfCookieName,
@@ -8,13 +8,13 @@ import {
88
import { generateSecureRandomString } from "./general"
99

1010
export function logout() {
11-
Cookies.remove(getSessionCookieName())
12-
Cookies.remove(getSessionMetadataCookieName())
11+
cookies.remove(getSessionCookieName())
12+
cookies.remove(getSessionMetadataCookieName())
1313
}
1414

1515
// https://docs.djangoproject.com/en/3.2/ref/csrf/
1616
export function getCsrfCookie() {
17-
return Cookies.get(getCsrfCookieName())
17+
return cookies.get(getCsrfCookieName()) as string | undefined
1818
}
1919

2020
export function makeOAuth2StorageKey(provider: string, key: string) {

src/utils/cookies.test.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
2+
import Cookies from "js-cookie"
3+
4+
import ExpressCookies from "./cookies"
5+
6+
function testCookie(
7+
value: any,
8+
{
9+
name = "name",
10+
path = "/",
11+
setValue,
12+
getValue,
13+
}: {
14+
name?: string
15+
path?: string
16+
setValue?: string
17+
getValue?: any
18+
} = {},
19+
) {
20+
const cookieValue = ExpressCookies.set(name, value)
21+
setValue = setValue ?? String(value)
22+
setValue = Cookies.converter.write(setValue, name)
23+
expect(cookieValue).toBe(`${name}=${setValue}; path=${path}`)
24+
25+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
26+
const actualValue = ExpressCookies.get(name)
27+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
28+
getValue = getValue ?? value
29+
expect(typeof actualValue).toEqual(typeof getValue)
30+
}
31+
32+
test("round-trips an object as a Express JSON cookie", () => {
33+
const value = { key: "value" }
34+
testCookie(value, { setValue: `j:${JSON.stringify(value)}` })
35+
})
36+
37+
test("round-trips an array as a Express JSON cookie", () => {
38+
const value = [1, 2, 3]
39+
testCookie(value, { setValue: `j:${JSON.stringify(value)}` })
40+
})
41+
42+
test("returns plain string cookies as-is", () => {
43+
testCookie("plain-text")
44+
})
45+
46+
test("does not mark JSON primitives as Express JSON cookies", () => {
47+
testCookie(42, { getValue: "42" })
48+
})

src/utils/cookies.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
2+
import Cookies from "js-cookie"
3+
4+
const EXPRESS_JSON_PREFIX = "j:"
5+
6+
/**
7+
* A wrapper around js-cookie that supports Express-style JSON cookies. See:
8+
* https://github.com/js-cookie/js-cookie/blob/main/SERVER_SIDE.md#express
9+
*/
10+
const ExpressCookies = Cookies.withConverter<any>({
11+
write: (value, name) => {
12+
const stringValue =
13+
typeof value === "object" && value !== null
14+
? EXPRESS_JSON_PREFIX + JSON.stringify(value as object)
15+
: String(value)
16+
17+
return Cookies.converter.write(stringValue, name)
18+
},
19+
read: (cookieValue, name) => {
20+
const stringValue = Cookies.converter.read(cookieValue, name)
21+
22+
return stringValue.startsWith(EXPRESS_JSON_PREFIX)
23+
? (JSON.parse(stringValue.slice(EXPRESS_JSON_PREFIX.length)) as object)
24+
: stringValue
25+
},
26+
})
27+
28+
export default ExpressCookies

vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ const viteConfig = defineConfig({
9494
"theme/components/index.ts",
9595
"utils/api.tsx",
9696
"utils/auth.ts",
97+
"utils/cookies.ts",
9798
"utils/form.ts",
9899
"utils/general.ts",
99100
"utils/router.ts",

0 commit comments

Comments
 (0)