@whatwg-node/server-plugin-cookies 0.0.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.
@@ -0,0 +1,7 @@
1
+ import { Event } from '@whatwg-node/events';
2
+ import { CookieChangeEventInit, CookieList } from './types';
3
+ export declare class CookieChangeEvent extends Event {
4
+ changed: CookieList;
5
+ deleted: CookieList;
6
+ constructor(type: string, eventInitDict?: CookieChangeEventInit);
7
+ }
@@ -0,0 +1,12 @@
1
+ import { CookieChangeEvent } from './CookieChangeEvent';
2
+ import { Cookie, CookieListItem, CookieStoreDeleteOptions, CookieStoreGetOptions } from './types';
3
+ export declare class CookieStore extends EventTarget {
4
+ cookieString: string;
5
+ onchange?: (event: CookieChangeEvent) => void;
6
+ get [Symbol.toStringTag](): 'CookieStore';
7
+ constructor(cookieString: string);
8
+ get(init?: CookieStoreGetOptions['name'] | CookieStoreGetOptions): Promise<Cookie | undefined>;
9
+ set(init: CookieListItem | string, possibleValue?: string): Promise<void>;
10
+ getAll(init?: CookieStoreGetOptions['name'] | CookieStoreGetOptions): Promise<Cookie[]>;
11
+ delete(init: CookieStoreDeleteOptions['name'] | CookieStoreDeleteOptions): Promise<void>;
12
+ }
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './useCookies';
package/index.js ADDED
@@ -0,0 +1,218 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ const events = require('@whatwg-node/events');
6
+
7
+ class CookieChangeEvent extends events.Event {
8
+ constructor(type, eventInitDict = { changed: [], deleted: [] }) {
9
+ super(type, eventInitDict);
10
+ this.changed = eventInitDict.changed || [];
11
+ this.deleted = eventInitDict.deleted || [];
12
+ }
13
+ }
14
+
15
+ const decode = decodeURIComponent;
16
+ const pairSplitRegExp = /; */;
17
+ // Try decoding a string using a decoding function.
18
+ function tryDecode(str, decode) {
19
+ try {
20
+ return typeof decode === 'boolean' ? decodeURIComponent(str) : decode(str);
21
+ }
22
+ catch (e) {
23
+ return str;
24
+ }
25
+ }
26
+ /**
27
+ * Parse a cookie header.
28
+ *
29
+ * Parse the given cookie header string into an object
30
+ * The object has the various cookies as keys(names) => values
31
+ */
32
+ function parse(str, options = {}) {
33
+ if (typeof str !== 'string') {
34
+ throw new TypeError('argument str must be a string');
35
+ }
36
+ const obj = [];
37
+ const opt = options || {};
38
+ const pairs = str.split(pairSplitRegExp);
39
+ const dec = opt.decode || decode;
40
+ for (let i = 0; i < pairs.length; i++) {
41
+ const pair = pairs[i];
42
+ let eqIdx = pair.indexOf('=');
43
+ // skip things that don't look like key=value
44
+ if (eqIdx < 0) {
45
+ continue;
46
+ }
47
+ const key = pair.substr(0, eqIdx).trim();
48
+ let val = pair.substr(++eqIdx, pair.length).trim();
49
+ // quoted values
50
+ if (val[0] === '"') {
51
+ val = val.slice(1, -1);
52
+ }
53
+ // only assign once
54
+ if (obj[key] == null) {
55
+ obj.push({
56
+ name: key,
57
+ value: tryDecode(val, dec),
58
+ });
59
+ }
60
+ }
61
+ return obj;
62
+ }
63
+
64
+ class CookieStore extends EventTarget {
65
+ get [Symbol.toStringTag]() {
66
+ return 'CookieStore';
67
+ }
68
+ constructor(cookieString) {
69
+ super();
70
+ this.cookieString = cookieString;
71
+ }
72
+ async get(init) {
73
+ if (init == null) {
74
+ throw new TypeError('CookieStoreGetOptions must not be empty');
75
+ }
76
+ else if (init instanceof Object && !Object.keys(init).length) {
77
+ throw new TypeError('CookieStoreGetOptions must not be empty');
78
+ }
79
+ return (await this.getAll(init))[0];
80
+ }
81
+ async set(init, possibleValue) {
82
+ var _a, _b, _c;
83
+ const item = {
84
+ name: '',
85
+ value: '',
86
+ path: '/',
87
+ secure: false,
88
+ sameSite: 'strict',
89
+ expires: null,
90
+ domain: null,
91
+ };
92
+ if (typeof init === 'string') {
93
+ item.name = init;
94
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
95
+ item.value = possibleValue;
96
+ }
97
+ else {
98
+ Object.assign(item, init);
99
+ if (item.path && !item.path.startsWith('/')) {
100
+ throw new TypeError('Cookie path must start with "/"');
101
+ }
102
+ if ((_a = item.domain) === null || _a === void 0 ? void 0 : _a.startsWith('.')) {
103
+ throw new TypeError('Cookie domain cannot start with "."');
104
+ }
105
+ if (((_b = item.name) === null || _b === void 0 ? void 0 : _b.startsWith('__Host')) && item.domain) {
106
+ throw new TypeError('Cookie domain must not be specified for host cookies');
107
+ }
108
+ if (((_c = item.name) === null || _c === void 0 ? void 0 : _c.startsWith('__Host')) && item.path !== '/') {
109
+ throw new TypeError('Cookie path must not be specified for host cookies');
110
+ }
111
+ if (item.path && item.path.endsWith('/')) {
112
+ item.path = item.path.slice(0, -1);
113
+ }
114
+ if (item.path === '') {
115
+ item.path = '/';
116
+ }
117
+ }
118
+ if (item.name === '' && item.value && item.value.includes('=')) {
119
+ throw new TypeError("Cookie value cannot contain '=' if the name is empty");
120
+ }
121
+ if (item.name && item.name.startsWith('__Host')) {
122
+ item.secure = true;
123
+ }
124
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
125
+ let cookieString = `${item.name}=${encodeURIComponent(item.value)}`;
126
+ if (item.domain) {
127
+ cookieString += '; Domain=' + item.domain;
128
+ }
129
+ if (item.path) {
130
+ cookieString += '; Path=' + item.path;
131
+ }
132
+ if (typeof item.expires === 'number') {
133
+ cookieString += '; Expires=' + new Date(item.expires).toUTCString();
134
+ }
135
+ else if (item.expires instanceof Date) {
136
+ cookieString += '; Expires=' + item.expires.toUTCString();
137
+ }
138
+ if ((item.name && item.name.startsWith('__Secure')) || item.secure) {
139
+ item.sameSite = 'lax';
140
+ cookieString += '; Secure';
141
+ }
142
+ switch (item.sameSite) {
143
+ case 'lax':
144
+ cookieString += '; SameSite=Lax';
145
+ break;
146
+ case 'strict':
147
+ cookieString += '; SameSite=Strict';
148
+ break;
149
+ case 'none':
150
+ cookieString += '; SameSite=None';
151
+ break;
152
+ }
153
+ const previousCookie = await this.get(item);
154
+ this.cookieString = cookieString;
155
+ if (this.onchange) {
156
+ const changed = [];
157
+ const deleted = [];
158
+ if (previousCookie && !(await this.get(item))) {
159
+ deleted.push({ ...item, value: undefined });
160
+ }
161
+ else {
162
+ changed.push(item);
163
+ }
164
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
165
+ // @ts-ignore
166
+ const event = new CookieChangeEvent('change', { changed, deleted });
167
+ this.onchange(event);
168
+ }
169
+ }
170
+ async getAll(init) {
171
+ const cookies = parse(this.cookieString);
172
+ if (init == null || Object.keys(init).length === 0) {
173
+ return cookies;
174
+ }
175
+ let name;
176
+ if (typeof init === 'string') {
177
+ name = init;
178
+ }
179
+ else {
180
+ name = init.name;
181
+ }
182
+ return cookies.filter(cookie => cookie.name === name);
183
+ }
184
+ async delete(init) {
185
+ const item = {
186
+ name: '',
187
+ value: '',
188
+ path: '/',
189
+ secure: false,
190
+ sameSite: 'strict',
191
+ expires: null,
192
+ domain: null,
193
+ };
194
+ if (typeof init === 'string') {
195
+ item.name = init;
196
+ }
197
+ else {
198
+ Object.assign(item, init);
199
+ }
200
+ item.expires = 0;
201
+ await this.set(item);
202
+ }
203
+ }
204
+
205
+ function useCookies() {
206
+ return {
207
+ onRequest({ request }) {
208
+ request.cookieStore = new CookieStore(request.headers.get('cookie') || '');
209
+ },
210
+ onResponse({ request, response }) {
211
+ if (request.cookieStore) {
212
+ response.headers.set('set-cookie', request.cookieStore.cookieString);
213
+ }
214
+ },
215
+ };
216
+ }
217
+
218
+ exports.useCookies = useCookies;
package/index.mjs ADDED
@@ -0,0 +1,214 @@
1
+ import { Event } from '@whatwg-node/events';
2
+
3
+ class CookieChangeEvent extends Event {
4
+ constructor(type, eventInitDict = { changed: [], deleted: [] }) {
5
+ super(type, eventInitDict);
6
+ this.changed = eventInitDict.changed || [];
7
+ this.deleted = eventInitDict.deleted || [];
8
+ }
9
+ }
10
+
11
+ const decode = decodeURIComponent;
12
+ const pairSplitRegExp = /; */;
13
+ // Try decoding a string using a decoding function.
14
+ function tryDecode(str, decode) {
15
+ try {
16
+ return typeof decode === 'boolean' ? decodeURIComponent(str) : decode(str);
17
+ }
18
+ catch (e) {
19
+ return str;
20
+ }
21
+ }
22
+ /**
23
+ * Parse a cookie header.
24
+ *
25
+ * Parse the given cookie header string into an object
26
+ * The object has the various cookies as keys(names) => values
27
+ */
28
+ function parse(str, options = {}) {
29
+ if (typeof str !== 'string') {
30
+ throw new TypeError('argument str must be a string');
31
+ }
32
+ const obj = [];
33
+ const opt = options || {};
34
+ const pairs = str.split(pairSplitRegExp);
35
+ const dec = opt.decode || decode;
36
+ for (let i = 0; i < pairs.length; i++) {
37
+ const pair = pairs[i];
38
+ let eqIdx = pair.indexOf('=');
39
+ // skip things that don't look like key=value
40
+ if (eqIdx < 0) {
41
+ continue;
42
+ }
43
+ const key = pair.substr(0, eqIdx).trim();
44
+ let val = pair.substr(++eqIdx, pair.length).trim();
45
+ // quoted values
46
+ if (val[0] === '"') {
47
+ val = val.slice(1, -1);
48
+ }
49
+ // only assign once
50
+ if (obj[key] == null) {
51
+ obj.push({
52
+ name: key,
53
+ value: tryDecode(val, dec),
54
+ });
55
+ }
56
+ }
57
+ return obj;
58
+ }
59
+
60
+ class CookieStore extends EventTarget {
61
+ get [Symbol.toStringTag]() {
62
+ return 'CookieStore';
63
+ }
64
+ constructor(cookieString) {
65
+ super();
66
+ this.cookieString = cookieString;
67
+ }
68
+ async get(init) {
69
+ if (init == null) {
70
+ throw new TypeError('CookieStoreGetOptions must not be empty');
71
+ }
72
+ else if (init instanceof Object && !Object.keys(init).length) {
73
+ throw new TypeError('CookieStoreGetOptions must not be empty');
74
+ }
75
+ return (await this.getAll(init))[0];
76
+ }
77
+ async set(init, possibleValue) {
78
+ var _a, _b, _c;
79
+ const item = {
80
+ name: '',
81
+ value: '',
82
+ path: '/',
83
+ secure: false,
84
+ sameSite: 'strict',
85
+ expires: null,
86
+ domain: null,
87
+ };
88
+ if (typeof init === 'string') {
89
+ item.name = init;
90
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
91
+ item.value = possibleValue;
92
+ }
93
+ else {
94
+ Object.assign(item, init);
95
+ if (item.path && !item.path.startsWith('/')) {
96
+ throw new TypeError('Cookie path must start with "/"');
97
+ }
98
+ if ((_a = item.domain) === null || _a === void 0 ? void 0 : _a.startsWith('.')) {
99
+ throw new TypeError('Cookie domain cannot start with "."');
100
+ }
101
+ if (((_b = item.name) === null || _b === void 0 ? void 0 : _b.startsWith('__Host')) && item.domain) {
102
+ throw new TypeError('Cookie domain must not be specified for host cookies');
103
+ }
104
+ if (((_c = item.name) === null || _c === void 0 ? void 0 : _c.startsWith('__Host')) && item.path !== '/') {
105
+ throw new TypeError('Cookie path must not be specified for host cookies');
106
+ }
107
+ if (item.path && item.path.endsWith('/')) {
108
+ item.path = item.path.slice(0, -1);
109
+ }
110
+ if (item.path === '') {
111
+ item.path = '/';
112
+ }
113
+ }
114
+ if (item.name === '' && item.value && item.value.includes('=')) {
115
+ throw new TypeError("Cookie value cannot contain '=' if the name is empty");
116
+ }
117
+ if (item.name && item.name.startsWith('__Host')) {
118
+ item.secure = true;
119
+ }
120
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
121
+ let cookieString = `${item.name}=${encodeURIComponent(item.value)}`;
122
+ if (item.domain) {
123
+ cookieString += '; Domain=' + item.domain;
124
+ }
125
+ if (item.path) {
126
+ cookieString += '; Path=' + item.path;
127
+ }
128
+ if (typeof item.expires === 'number') {
129
+ cookieString += '; Expires=' + new Date(item.expires).toUTCString();
130
+ }
131
+ else if (item.expires instanceof Date) {
132
+ cookieString += '; Expires=' + item.expires.toUTCString();
133
+ }
134
+ if ((item.name && item.name.startsWith('__Secure')) || item.secure) {
135
+ item.sameSite = 'lax';
136
+ cookieString += '; Secure';
137
+ }
138
+ switch (item.sameSite) {
139
+ case 'lax':
140
+ cookieString += '; SameSite=Lax';
141
+ break;
142
+ case 'strict':
143
+ cookieString += '; SameSite=Strict';
144
+ break;
145
+ case 'none':
146
+ cookieString += '; SameSite=None';
147
+ break;
148
+ }
149
+ const previousCookie = await this.get(item);
150
+ this.cookieString = cookieString;
151
+ if (this.onchange) {
152
+ const changed = [];
153
+ const deleted = [];
154
+ if (previousCookie && !(await this.get(item))) {
155
+ deleted.push({ ...item, value: undefined });
156
+ }
157
+ else {
158
+ changed.push(item);
159
+ }
160
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
161
+ // @ts-ignore
162
+ const event = new CookieChangeEvent('change', { changed, deleted });
163
+ this.onchange(event);
164
+ }
165
+ }
166
+ async getAll(init) {
167
+ const cookies = parse(this.cookieString);
168
+ if (init == null || Object.keys(init).length === 0) {
169
+ return cookies;
170
+ }
171
+ let name;
172
+ if (typeof init === 'string') {
173
+ name = init;
174
+ }
175
+ else {
176
+ name = init.name;
177
+ }
178
+ return cookies.filter(cookie => cookie.name === name);
179
+ }
180
+ async delete(init) {
181
+ const item = {
182
+ name: '',
183
+ value: '',
184
+ path: '/',
185
+ secure: false,
186
+ sameSite: 'strict',
187
+ expires: null,
188
+ domain: null,
189
+ };
190
+ if (typeof init === 'string') {
191
+ item.name = init;
192
+ }
193
+ else {
194
+ Object.assign(item, init);
195
+ }
196
+ item.expires = 0;
197
+ await this.set(item);
198
+ }
199
+ }
200
+
201
+ function useCookies() {
202
+ return {
203
+ onRequest({ request }) {
204
+ request.cookieStore = new CookieStore(request.headers.get('cookie') || '');
205
+ },
206
+ onResponse({ request, response }) {
207
+ if (request.cookieStore) {
208
+ response.headers.set('set-cookie', request.cookieStore.cookieString);
209
+ }
210
+ },
211
+ };
212
+ }
213
+
214
+ export { useCookies };
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@whatwg-node/server-plugin-cookies",
3
+ "version": "0.0.0",
4
+ "description": "Cookies Plugin",
5
+ "sideEffects": false,
6
+ "peerDependencies": {
7
+ "@whatwg-node/server": "^0.7.1"
8
+ },
9
+ "dependencies": {
10
+ "@whatwg-node/events": "^0.0.2",
11
+ "tslib": "^2.3.1"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "ardatan/whatwg-node",
16
+ "directory": "packages/server-plugin-cookies"
17
+ },
18
+ "author": "Arda TANRIKULU <ardatanrikulu@gmail.com>",
19
+ "license": "MIT",
20
+ "main": "index.js",
21
+ "module": "index.mjs",
22
+ "typings": "index.d.ts",
23
+ "typescript": {
24
+ "definition": "index.d.ts"
25
+ },
26
+ "exports": {
27
+ ".": {
28
+ "require": "./index.js",
29
+ "import": "./index.mjs"
30
+ },
31
+ "./*": {
32
+ "require": "./*.js",
33
+ "import": "./*.mjs"
34
+ },
35
+ "./package.json": "./package.json"
36
+ }
37
+ }
package/parse.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { Cookie } from './types';
2
+ export interface ParseOptions {
3
+ decode?: boolean;
4
+ }
5
+ /**
6
+ * Parse a cookie header.
7
+ *
8
+ * Parse the given cookie header string into an object
9
+ * The object has the various cookies as keys(names) => values
10
+ */
11
+ export declare function parse(str: string, options?: ParseOptions): Cookie[];
package/types.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ export interface Cookie {
2
+ domain?: string;
3
+ expires?: number;
4
+ name: string;
5
+ path?: string;
6
+ secure?: boolean;
7
+ sameSite?: CookieSameSite;
8
+ value: string;
9
+ }
10
+ export interface CookieStoreDeleteOptions {
11
+ name: string;
12
+ domain?: string;
13
+ path?: string;
14
+ }
15
+ export interface CookieStoreGetOptions {
16
+ name?: string;
17
+ url?: string;
18
+ }
19
+ export type CookieSameSite = 'strict' | 'lax' | 'none';
20
+ export interface CookieListItem {
21
+ name?: string;
22
+ value?: string;
23
+ domain: string | null;
24
+ path?: string;
25
+ expires: Date | number | null;
26
+ secure?: boolean;
27
+ sameSite?: CookieSameSite;
28
+ }
29
+ export type CookieList = CookieListItem[];
30
+ export interface CookieChangeEventInit extends EventInit {
31
+ changed: CookieList;
32
+ deleted: CookieList;
33
+ }
@@ -0,0 +1,8 @@
1
+ import { ServerAdapterPlugin } from '@whatwg-node/server';
2
+ import { CookieStore } from './CookieStore';
3
+ declare global {
4
+ interface Request {
5
+ cookieStore?: CookieStore;
6
+ }
7
+ }
8
+ export declare function useCookies<TServerContext>(): ServerAdapterPlugin<TServerContext>;