@@ -11,6 +11,111 @@ import { CancelablePromise } from './CancelablePromise';
11
11
import type { OnCancel } from './CancelablePromise';
12
12
import type { OpenAPIConfig } from './OpenAPI';
13
13
14
+ interface Cookie {
15
+ name: string;
16
+ value: string;
17
+ expires?: Date;
18
+ path?: string;
19
+ sameSite?: "Lax" | "None" | "Strict";
20
+ secure?: boolean;
21
+ }
22
+
23
+ class CookieJar {
24
+ constructor(private jar = new Map<string , Cookie[]>()) {}
25
+
26
+ setCookie(url: URL, cookieStr: string) {
27
+ const key = url.origin.toLowerCase();
28
+ if (!this.jar.has(key)) {
29
+ this.jar.set(key, []);
30
+ }
31
+
32
+ const cookie = CookieJar.parse(cookieStr);
33
+ this.jar.set(key, [...(this.jar.get(key)?.filter((c) => c.name !== cookie.name) || []), cookie]);
34
+ }
35
+
36
+ getCookies(url: URL): Cookie[] {
37
+ const key = url.origin.toLowerCase();
38
+ if (!this.jar.get(key)) {
39
+ return [];
40
+ }
41
+
42
+ // Filter out expired cookies
43
+ return this.jar.get(key)?.filter((cookie) => !cookie.expires || cookie.expires > new Date()) || [];
44
+ }
45
+
46
+ static parse(str: string): Cookie {
47
+ if (typeof str !== "string") {
48
+ throw new Error("argument str must be a string");
49
+ }
50
+
51
+ const parts = str.split(";").map((part) => part.trim());
52
+
53
+ let cookie: Cookie;
54
+
55
+ if (parts.length > 0) {
56
+ const [name, value] = parts[0].split("=");
57
+ if (!name || !value) {
58
+ throw new Error("Invalid cookie");
59
+ }
60
+
61
+ cookie = {
62
+ name,
63
+ value,
64
+ };
65
+ } else {
66
+ throw new Error("Invalid cookie");
67
+ }
68
+
69
+ parts.slice(1).forEach((part) => {
70
+ const [name, value] = part.split("=");
71
+ if (!name.trim()) {
72
+ throw new Error("Invalid cookie");
73
+ }
74
+
75
+ const nameLow = name.toLowerCase();
76
+ // eslint-disable-next-line quotes
77
+ const val = value?.charAt(0) === "'" || value?.charAt(0) === '"' ? value?.slice(1, -1) : value;
78
+ if (nameLow === "expires") {
79
+ cookie.expires = new Date(val);
80
+ }
81
+ if (nameLow === "path") {
82
+ cookie.path = val;
83
+ }
84
+ if (nameLow === "samesite") {
85
+ if (val !== "Lax" && val !== "None" && val !== "Strict") {
86
+ throw new Error("Invalid cookie SameSite value");
87
+ }
88
+ cookie.sameSite = val;
89
+ }
90
+ if (nameLow === "secure") {
91
+ cookie.secure = true;
92
+ }
93
+ });
94
+
95
+ return cookie;
96
+ }
97
+ }
98
+
99
+ const jar = new CookieJar();
100
+
101
+ axios.interceptors.response.use((response) => {
102
+ if (Array.isArray(response.headers["set-cookie"])) {
103
+ response.headers["set-cookie"].forEach((c) => {
104
+ jar.setCookie(new URL(response.config.url), c);
105
+ });
106
+ }
107
+ return response;
108
+ });
109
+
110
+ axios.interceptors.request.use(function (config) {
111
+ const cookies = jar.getCookies(new URL(config.url));
112
+
113
+ if (cookies?.length > 0) {
114
+ config.headers.cookie = cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join("; ");
115
+ }
116
+ return config;
117
+ });
118
+
14
119
{{> functions/isDefined }}
15
120
16
121
0 commit comments