-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.ts
121 lines (105 loc) · 4.13 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import type {
CookieStore, CookieListItem, CookieList, CookieInit, CookieStoreGetOptions, CookieStoreDeleteOptions,
} from 'https://ghuc.cc/qwtel/cookie-store-interface/index.d.ts';
export * from 'https://ghuc.cc/qwtel/cookie-store-interface/index.d.ts';
import { setCookie, attrsToSetCookie, parseCookieHeader } from './set-cookie.ts';
/**
* An implementation of the [Cookie Store API](https://wicg.github.io/cookie-store) for request handlers.
*
* It uses the `Cookie` header of a request to populate the store and
* keeps a record of changes that can be exported as a list of `Set-Cookie` headers.
*
* Note that this is not a polyfill! It is intended as a cookie middleware for Cloudflare Workers,
* and perhaps some other uses.
*/
export class RequestCookieStore implements CookieStore {
#origin?: URL;
#map: Map<string, string> = new Map();
#changes: Map<string, string[][]> = new Map();
constructor(request: Request) {
const origin = request.headers.get('origin') || request.url;
const cookie = request.headers.get('cookie');
if (origin) this.#origin = new URL(origin);
this.#map = parseCookieHeader(cookie);
}
get(name?: string): Promise<CookieListItem | null>;
get(options?: CookieStoreGetOptions): Promise<CookieListItem | null>;
get(options?: string | CookieStoreGetOptions): Promise<CookieListItem | null> {
// FIXME
if (typeof options !== 'string') throw Error('Overload not implemented.');
return Promise.resolve(this.#map.has(options)
? { name: options, value: this.#map.get(options)! }
: null);
}
getAll(name?: string): Promise<CookieList>;
getAll(options?: CookieStoreGetOptions): Promise<CookieList>;
getAll(options?: string | CookieStoreGetOptions): Promise<CookieList> {
// FIXME
if (options != null) throw Error('Overload not implemented.');
return Promise.resolve([...this.#map].map(([name, value]) => ({ name, value })))
}
set(name: string, value: string): Promise<void>;
set(options: CookieInit): Promise<void>;
set(options: string | CookieInit, value?: string): Promise<void> {
const result = setCookie(options, value, this.#origin);
if (!result) return Promise.resolve();
const [attributes, expires] = result;
const [[name, val]] = attributes;
this.#changes.set(name, attributes);
if (expires && expires < new Date())
this.#map.delete(name);
else
this.#map.set(name, val);
return Promise.resolve()
}
delete(name: string): Promise<void>;
delete(options: CookieStoreDeleteOptions): Promise<void>;
delete(options: string | CookieStoreDeleteOptions): Promise<void> {
// FIXME
if (typeof options !== 'string') throw Error('Overload not implemented.');
this.set({ name: options, value: '', expires: new Date(0) });
return Promise.resolve();
}
/**
* Exports the recorded changes to this store as a list of `Set-Cookie` headers.
*
* Can be passed as the `headers` field when building a new `Response`:
* ```ts
* new Response(body, { headers: cookieStore.headers })
* ```
*/
get headers(): [string, string][] {
const headers: [string, string][] = [];
for (const attrs of this.#changes.values()) {
headers.push(['Set-Cookie', attrsToSetCookie(attrs)]);
}
return headers;
}
/** Exports the entire cookie store as a `cookie` header string */
toCookieString() {
return [...this.#map].map(x => x.join('=')).join('; ');
}
/** Helper to turn a single `CookieInit` into a `set-cookie` string.
* @deprecated Might remove/change name */
static toSetCookie(cookie: CookieInit): string {
const x = setCookie(cookie);
return x ? attrsToSetCookie(x[0]) : '';
}
addEventListener(
_type: string,
_listener: EventListenerOrEventListenerObject,
_options?: boolean | AddEventListenerOptions
): void {
throw new Error("Method not implemented.")
}
dispatchEvent(_event: Event): boolean {
throw new Error("Method not implemented.")
}
removeEventListener(
_type: string,
_callback: EventListenerOrEventListenerObject,
_options?: boolean | EventListenerOptions
): void {
throw new Error("Method not implemented.")
}
}