@jsenv/server 15.0.2 → 15.0.4

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.
Files changed (53) hide show
  1. package/README.md +23 -23
  2. package/package.json +4 -4
  3. package/src/content_negotiation/pick_accepted_content.js +13 -13
  4. package/src/content_negotiation/pick_content_encoding.js +25 -25
  5. package/src/content_negotiation/pick_content_language.js +35 -34
  6. package/src/content_negotiation/pick_content_type.js +29 -29
  7. package/src/fetch_filesystem.js +110 -107
  8. package/src/from_fetch_response.js +6 -6
  9. package/src/interfacing_with_node/body.js +22 -22
  10. package/src/interfacing_with_node/observable.js +80 -80
  11. package/src/interfacing_with_node/observable_from_node_stream.js +35 -35
  12. package/src/interfacing_with_node/request_factory.js +26 -26
  13. package/src/interfacing_with_node/write_node_response.js +82 -82
  14. package/src/internal/colorizeResponseStatus.js +26 -26
  15. package/src/internal/convertFileSystemErrorToResponseProperties.js +10 -10
  16. package/src/internal/dns_resolution.js +7 -7
  17. package/src/internal/etag.js +11 -11
  18. package/src/internal/filesystem.js +19 -19
  19. package/src/internal/headersFromObject.js +8 -8
  20. package/src/internal/headersFromString.js +11 -11
  21. package/src/internal/headersToObject.js +5 -5
  22. package/src/internal/headersToString.js +7 -7
  23. package/src/internal/headers_composition.js +9 -9
  24. package/src/internal/hostname_parser.js +9 -9
  25. package/src/internal/listen.js +47 -47
  26. package/src/internal/listenClientError.js +10 -10
  27. package/src/internal/listenEvent.js +5 -5
  28. package/src/internal/listenRequest.js +8 -8
  29. package/src/internal/listenServerConnectionError.js +19 -19
  30. package/src/internal/multiple-header.js +50 -50
  31. package/src/internal/normalizeHeaderName.js +4 -4
  32. package/src/internal/normalizeHeaderValue.js +2 -2
  33. package/src/internal/object_composition.js +41 -39
  34. package/src/internal/response_composition.js +6 -6
  35. package/src/internal/server-polyglot.js +42 -42
  36. package/src/internal/server_ips.js +20 -20
  37. package/src/internal/trackServerPendingConnections.js +23 -23
  38. package/src/internal/trackServerPendingRequests.js +28 -28
  39. package/src/main.js +18 -18
  40. package/src/readRequestBody.js +15 -15
  41. package/src/serve_directory.js +16 -16
  42. package/src/server_timing/timing_header.js +24 -24
  43. package/src/server_timing/timing_measure.js +14 -14
  44. package/src/service_composition/path_to_regexp.js +173 -168
  45. package/src/service_composition/routing.js +12 -12
  46. package/src/service_controller.js +77 -77
  47. package/src/services/cors/jsenv_service_cors.js +29 -29
  48. package/src/services/error_handler/jsenv_service_error_handler.js +18 -18
  49. package/src/services/request_aliases/jsenv_service_request_aliases.js +20 -20
  50. package/src/services/response_acceptance_check/jsenv_service_response_acceptance_check.js +16 -16
  51. package/src/sse/sse_room.js +117 -115
  52. package/src/start_server.js +328 -328
  53. package/src/stopReasons.js +9 -9
@@ -1,22 +1,22 @@
1
- import http from "node:http"
2
- import { Http2ServerResponse } from "node:http2"
3
- import { raceCallbacks } from "@jsenv/abort"
1
+ import http from "node:http";
2
+ import { Http2ServerResponse } from "node:http2";
3
+ import { raceCallbacks } from "@jsenv/abort";
4
4
 
5
- import { normalizeBodyMethods } from "./body.js"
5
+ import { normalizeBodyMethods } from "./body.js";
6
6
 
7
7
  export const writeNodeResponse = async (
8
8
  responseStream,
9
9
  { status, statusText, headers, body, bodyEncoding },
10
10
  { signal, ignoreBody, onAbort, onError, onHeadersSent, onEnd } = {},
11
11
  ) => {
12
- body = await body
13
- const bodyMethods = normalizeBodyMethods(body)
12
+ body = await body;
13
+ const bodyMethods = normalizeBodyMethods(body);
14
14
 
15
15
  if (signal.aborted) {
16
- bodyMethods.destroy()
17
- responseStream.destroy()
18
- onAbort()
19
- return
16
+ bodyMethods.destroy();
17
+ responseStream.destroy();
18
+ onAbort();
19
+ return;
20
20
  }
21
21
 
22
22
  writeHead(responseStream, {
@@ -24,31 +24,31 @@ export const writeNodeResponse = async (
24
24
  statusText,
25
25
  headers,
26
26
  onHeadersSent,
27
- })
27
+ });
28
28
 
29
29
  if (!body) {
30
- onEnd()
31
- responseStream.end()
32
- return
30
+ onEnd();
31
+ responseStream.end();
32
+ return;
33
33
  }
34
34
 
35
35
  if (ignoreBody) {
36
- onEnd()
37
- bodyMethods.destroy()
38
- responseStream.end()
39
- return
36
+ onEnd();
37
+ bodyMethods.destroy();
38
+ responseStream.end();
39
+ return;
40
40
  }
41
41
 
42
42
  if (bodyEncoding) {
43
- responseStream.setEncoding(bodyEncoding)
43
+ responseStream.setEncoding(bodyEncoding);
44
44
  }
45
45
 
46
46
  await new Promise((resolve) => {
47
- const observable = bodyMethods.asObservable()
47
+ const observable = bodyMethods.asObservable();
48
48
  const subscription = observable.subscribe({
49
49
  next: (data) => {
50
50
  try {
51
- responseStream.write(data)
51
+ responseStream.write(data);
52
52
  } catch (e) {
53
53
  // Something inside Node.js sometimes puts stream
54
54
  // in a state where .write() throw despites nodeResponse.destroyed
@@ -59,59 +59,59 @@ export const writeNodeResponse = async (
59
59
  // For the record it's "hard" to reproduce but can be by running
60
60
  // a lot of tests against a browser in the context of @jsenv/core testing
61
61
  if (e.code === "ERR_HTTP2_INVALID_STREAM") {
62
- return
62
+ return;
63
63
  }
64
- responseStream.emit("error", e)
64
+ responseStream.emit("error", e);
65
65
  }
66
66
  },
67
67
  error: (value) => {
68
- responseStream.emit("error", value)
68
+ responseStream.emit("error", value);
69
69
  },
70
70
  complete: () => {
71
- responseStream.end()
71
+ responseStream.end();
72
72
  },
73
- })
73
+ });
74
74
 
75
75
  raceCallbacks(
76
76
  {
77
77
  abort: (cb) => {
78
- signal.addEventListener("abort", cb)
78
+ signal.addEventListener("abort", cb);
79
79
  return () => {
80
- signal.removeEventListener("abort", cb)
81
- }
80
+ signal.removeEventListener("abort", cb);
81
+ };
82
82
  },
83
83
  error: (cb) => {
84
- responseStream.on("error", cb)
84
+ responseStream.on("error", cb);
85
85
  return () => {
86
- responseStream.removeListener("error", cb)
87
- }
86
+ responseStream.removeListener("error", cb);
87
+ };
88
88
  },
89
89
  close: (cb) => {
90
- responseStream.on("close", cb)
90
+ responseStream.on("close", cb);
91
91
  return () => {
92
- responseStream.removeListener("close", cb)
93
- }
92
+ responseStream.removeListener("close", cb);
93
+ };
94
94
  },
95
95
  finish: (cb) => {
96
- responseStream.on("finish", cb)
96
+ responseStream.on("finish", cb);
97
97
  return () => {
98
- responseStream.removeListener("finish", cb)
99
- }
98
+ responseStream.removeListener("finish", cb);
99
+ };
100
100
  },
101
101
  },
102
102
  (winner) => {
103
103
  const raceEffects = {
104
104
  abort: () => {
105
- subscription.unsubscribe()
106
- responseStream.destroy()
107
- onAbort()
108
- resolve()
105
+ subscription.unsubscribe();
106
+ responseStream.destroy();
107
+ onAbort();
108
+ resolve();
109
109
  },
110
110
  error: (error) => {
111
- subscription.unsubscribe()
112
- responseStream.destroy()
113
- onError(error)
114
- resolve()
111
+ subscription.unsubscribe();
112
+ responseStream.destroy();
113
+ onError(error);
114
+ resolve();
115
115
  },
116
116
  close: () => {
117
117
  // close body in case nodeResponse is prematurely closed
@@ -119,46 +119,46 @@ export const writeNodeResponse = async (
119
119
  // it may happen in case of server sent event
120
120
  // where body is kept open to write to client
121
121
  // and the browser is reloaded or closed for instance
122
- subscription.unsubscribe()
123
- responseStream.destroy()
124
- onAbort()
125
- resolve()
122
+ subscription.unsubscribe();
123
+ responseStream.destroy();
124
+ onAbort();
125
+ resolve();
126
126
  },
127
127
  finish: () => {
128
- onEnd()
129
- resolve()
128
+ onEnd();
129
+ resolve();
130
130
  },
131
- }
132
- raceEffects[winner.name](winner.data)
131
+ };
132
+ raceEffects[winner.name](winner.data);
133
133
  },
134
- )
135
- })
136
- }
134
+ );
135
+ });
136
+ };
137
137
 
138
138
  const writeHead = (
139
139
  responseStream,
140
140
  { status, statusText, headers, onHeadersSent },
141
141
  ) => {
142
142
  const responseIsHttp2ServerResponse =
143
- responseStream instanceof Http2ServerResponse
143
+ responseStream instanceof Http2ServerResponse;
144
144
  const responseIsServerHttp2Stream =
145
- responseStream.constructor.name === "ServerHttp2Stream"
145
+ responseStream.constructor.name === "ServerHttp2Stream";
146
146
  let nodeHeaders = headersToNodeHeaders(headers, {
147
147
  // https://github.com/nodejs/node/blob/79296dc2d02c0b9872bbfcbb89148ea036a546d0/lib/internal/http2/compat.js#L112
148
148
  ignoreConnectionHeader:
149
149
  responseIsHttp2ServerResponse || responseIsServerHttp2Stream,
150
- })
150
+ });
151
151
  if (statusText === undefined) {
152
- statusText = statusTextFromStatus(status)
152
+ statusText = statusTextFromStatus(status);
153
153
  }
154
154
  if (responseIsServerHttp2Stream) {
155
155
  nodeHeaders = {
156
156
  ...nodeHeaders,
157
157
  ":status": status,
158
- }
159
- responseStream.respond(nodeHeaders)
160
- onHeadersSent({ nodeHeaders, status, statusText })
161
- return
158
+ };
159
+ responseStream.respond(nodeHeaders);
160
+ onHeadersSent({ nodeHeaders, status, statusText });
161
+ return;
162
162
  }
163
163
  // nodejs strange signature for writeHead force this
164
164
  // https://nodejs.org/api/http.html#http_response_writehead_statuscode_statusmessage_headers
@@ -166,13 +166,13 @@ const writeHead = (
166
166
  // https://github.com/nodejs/node/blob/79296dc2d02c0b9872bbfcbb89148ea036a546d0/lib/internal/http2/compat.js#L97
167
167
  responseIsHttp2ServerResponse
168
168
  ) {
169
- responseStream.writeHead(status, nodeHeaders)
170
- onHeadersSent({ nodeHeaders, status, statusText })
171
- return
169
+ responseStream.writeHead(status, nodeHeaders);
170
+ onHeadersSent({ nodeHeaders, status, statusText });
171
+ return;
172
172
  }
173
173
 
174
174
  try {
175
- responseStream.writeHead(status, statusText, nodeHeaders)
175
+ responseStream.writeHead(status, statusText, nodeHeaders);
176
176
  } catch (e) {
177
177
  if (
178
178
  e.code === "ERR_INVALID_CHAR" &&
@@ -180,29 +180,29 @@ const writeHead = (
180
180
  ) {
181
181
  throw new Error(`Invalid character in statusMessage
182
182
  --- status message ---
183
- ${statusText}`)
183
+ ${statusText}`);
184
184
  }
185
- throw e
185
+ throw e;
186
186
  }
187
- onHeadersSent({ nodeHeaders, status, statusText })
188
- }
187
+ onHeadersSent({ nodeHeaders, status, statusText });
188
+ };
189
189
 
190
190
  const statusTextFromStatus = (status) =>
191
- http.STATUS_CODES[status] || "not specified"
191
+ http.STATUS_CODES[status] || "not specified";
192
192
 
193
193
  const headersToNodeHeaders = (headers, { ignoreConnectionHeader }) => {
194
- const nodeHeaders = {}
194
+ const nodeHeaders = {};
195
195
 
196
196
  Object.keys(headers).forEach((name) => {
197
- if (name === "connection" && ignoreConnectionHeader) return
198
- const nodeHeaderName = name in mapping ? mapping[name] : name
199
- nodeHeaders[nodeHeaderName] = headers[name]
200
- })
197
+ if (name === "connection" && ignoreConnectionHeader) return;
198
+ const nodeHeaderName = name in mapping ? mapping[name] : name;
199
+ nodeHeaders[nodeHeaderName] = headers[name];
200
+ });
201
201
 
202
- return nodeHeaders
203
- }
202
+ return nodeHeaders;
203
+ };
204
204
 
205
205
  const mapping = {
206
206
  // "content-type": "Content-Type",
207
207
  // "last-modified": "Last-Modified",
208
- }
208
+ };
@@ -1,40 +1,40 @@
1
1
  // https://github.com/Marak/colors.js/blob/b63ef88e521b42920a9e908848de340b31e68c9d/lib/styles.js#L29
2
2
 
3
- const close = "\x1b[0m"
4
- const red = "\x1b[31m"
5
- const green = "\x1b[32m"
6
- const yellow = "\x1b[33m"
3
+ const close = "\x1b[0m";
4
+ const red = "\x1b[31m";
5
+ const green = "\x1b[32m";
6
+ const yellow = "\x1b[33m";
7
7
  // const blue = "\x1b[34m"
8
- const magenta = "\x1b[35m"
9
- const cyan = "\x1b[36m"
8
+ const magenta = "\x1b[35m";
9
+ const cyan = "\x1b[36m";
10
10
  // const white = "\x1b[37m"
11
11
 
12
12
  export const colorizeResponseStatus = (status) => {
13
- const statusType = statusToType(status)
14
- if (statusType === "information") return `${cyan}${status}${close}`
15
- if (statusType === "success") return `${green}${status}${close}`
16
- if (statusType === "redirection") return `${magenta}${status}${close}`
17
- if (statusType === "client_error") return `${yellow}${status}${close}`
18
- if (statusType === "server_error") return `${red}${status}${close}`
19
- return status
20
- }
13
+ const statusType = statusToType(status);
14
+ if (statusType === "information") return `${cyan}${status}${close}`;
15
+ if (statusType === "success") return `${green}${status}${close}`;
16
+ if (statusType === "redirection") return `${magenta}${status}${close}`;
17
+ if (statusType === "client_error") return `${yellow}${status}${close}`;
18
+ if (statusType === "server_error") return `${red}${status}${close}`;
19
+ return status;
20
+ };
21
21
 
22
22
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
23
23
  export const statusToType = (status) => {
24
- if (statusIsInformation(status)) return "information"
25
- if (statusIsSuccess(status)) return "success"
26
- if (statusIsRedirection(status)) return "redirection"
27
- if (statusIsClientError(status)) return "client_error"
28
- if (statusIsServerError(status)) return "server_error"
29
- return "unknown"
30
- }
24
+ if (statusIsInformation(status)) return "information";
25
+ if (statusIsSuccess(status)) return "success";
26
+ if (statusIsRedirection(status)) return "redirection";
27
+ if (statusIsClientError(status)) return "client_error";
28
+ if (statusIsServerError(status)) return "server_error";
29
+ return "unknown";
30
+ };
31
31
 
32
- const statusIsInformation = (status) => status >= 100 && status < 200
32
+ const statusIsInformation = (status) => status >= 100 && status < 200;
33
33
 
34
- const statusIsSuccess = (status) => status >= 200 && status < 300
34
+ const statusIsSuccess = (status) => status >= 200 && status < 300;
35
35
 
36
- const statusIsRedirection = (status) => status >= 300 && status < 400
36
+ const statusIsRedirection = (status) => status >= 300 && status < 400;
37
37
 
38
- const statusIsClientError = (status) => status >= 400 && status < 500
38
+ const statusIsClientError = (status) => status >= 400 && status < 500;
39
39
 
40
- const statusIsServerError = (status) => status >= 500 && status < 600
40
+ const statusIsServerError = (status) => status >= 500 && status < 600;
@@ -4,19 +4,19 @@ export const convertFileSystemErrorToResponseProperties = (error) => {
4
4
  return {
5
5
  status: 403,
6
6
  statusText: `EACCES: No permission to read file at ${error.path}`,
7
- }
7
+ };
8
8
  }
9
9
  if (isErrorWithCode(error, "EPERM")) {
10
10
  return {
11
11
  status: 403,
12
12
  statusText: `EPERM: No permission to read file at ${error.path}`,
13
- }
13
+ };
14
14
  }
15
15
  if (isErrorWithCode(error, "ENOENT")) {
16
16
  return {
17
17
  status: 404,
18
18
  statusText: `ENOENT: File not found at ${error.path}`,
19
- }
19
+ };
20
20
  }
21
21
  // file access may be temporarily blocked
22
22
  // (by an antivirus scanning it because recently modified for instance)
@@ -27,7 +27,7 @@ export const convertFileSystemErrorToResponseProperties = (error) => {
27
27
  headers: {
28
28
  "retry-after": 0.01, // retry in 10ms
29
29
  },
30
- }
30
+ };
31
31
  }
32
32
  // emfile means there is too many files currently opened
33
33
  if (isErrorWithCode(error, "EMFILE")) {
@@ -37,17 +37,17 @@ export const convertFileSystemErrorToResponseProperties = (error) => {
37
37
  headers: {
38
38
  "retry-after": 0.1, // retry in 100ms
39
39
  },
40
- }
40
+ };
41
41
  }
42
42
  if (isErrorWithCode(error, "EISDIR")) {
43
43
  return {
44
44
  status: 500,
45
45
  statusText: `EISDIR: Unexpected directory operation at ${error.path}`,
46
- }
46
+ };
47
47
  }
48
- return null
49
- }
48
+ return null;
49
+ };
50
50
 
51
51
  const isErrorWithCode = (error, code) => {
52
- return typeof error === "object" && error.code === code
53
- }
52
+ return typeof error === "object" && error.code === code;
53
+ };
@@ -1,4 +1,4 @@
1
- import { lookup } from "node:dns"
1
+ import { lookup } from "node:dns";
2
2
 
3
3
  export const applyDnsResolution = async (
4
4
  hostname,
@@ -7,11 +7,11 @@ export const applyDnsResolution = async (
7
7
  const dnsResolution = await new Promise((resolve, reject) => {
8
8
  lookup(hostname, { verbatim }, (error, address, family) => {
9
9
  if (error) {
10
- reject(error)
10
+ reject(error);
11
11
  } else {
12
- resolve({ address, family })
12
+ resolve({ address, family });
13
13
  }
14
- })
15
- })
16
- return dnsResolution
17
- }
14
+ });
15
+ });
16
+ return dnsResolution;
17
+ };
@@ -1,22 +1,22 @@
1
- import { createHash } from "node:crypto"
1
+ import { createHash } from "node:crypto";
2
2
 
3
- const ETAG_FOR_EMPTY_CONTENT = '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"'
3
+ const ETAG_FOR_EMPTY_CONTENT = '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
4
4
 
5
5
  export const bufferToEtag = (buffer) => {
6
6
  if (!Buffer.isBuffer(buffer)) {
7
- throw new TypeError(`buffer expected, got ${buffer}`)
7
+ throw new TypeError(`buffer expected, got ${buffer}`);
8
8
  }
9
9
 
10
10
  if (buffer.length === 0) {
11
- return ETAG_FOR_EMPTY_CONTENT
11
+ return ETAG_FOR_EMPTY_CONTENT;
12
12
  }
13
13
 
14
- const hash = createHash("sha1")
15
- hash.update(buffer, "utf8")
14
+ const hash = createHash("sha1");
15
+ hash.update(buffer, "utf8");
16
16
 
17
- const hashBase64String = hash.digest("base64")
18
- const hashBase64StringSubset = hashBase64String.slice(0, 27)
19
- const length = buffer.length
17
+ const hashBase64String = hash.digest("base64");
18
+ const hashBase64StringSubset = hashBase64String.slice(0, 27);
19
+ const length = buffer.length;
20
20
 
21
- return `"${length.toString(16)}-${hashBase64StringSubset}"`
22
- }
21
+ return `"${length.toString(16)}-${hashBase64StringSubset}"`;
22
+ };
@@ -1,43 +1,43 @@
1
- import { pathToFileURL, fileURLToPath } from "node:url"
1
+ import { pathToFileURL, fileURLToPath } from "node:url";
2
2
 
3
3
  export const isFileSystemPath = (value) => {
4
4
  if (typeof value !== "string") {
5
5
  throw new TypeError(
6
6
  `isFileSystemPath first arg must be a string, got ${value}`,
7
- )
7
+ );
8
8
  }
9
9
 
10
10
  if (value[0] === "/") {
11
- return true
11
+ return true;
12
12
  }
13
13
 
14
- return startsWithWindowsDriveLetter(value)
15
- }
14
+ return startsWithWindowsDriveLetter(value);
15
+ };
16
16
 
17
17
  const startsWithWindowsDriveLetter = (string) => {
18
- const firstChar = string[0]
19
- if (!/[a-zA-Z]/.test(firstChar)) return false
18
+ const firstChar = string[0];
19
+ if (!/[a-zA-Z]/.test(firstChar)) return false;
20
20
 
21
- const secondChar = string[1]
22
- if (secondChar !== ":") return false
21
+ const secondChar = string[1];
22
+ if (secondChar !== ":") return false;
23
23
 
24
- return true
25
- }
24
+ return true;
25
+ };
26
26
 
27
27
  export const fileSystemPathToUrl = (value) => {
28
28
  if (!isFileSystemPath(value)) {
29
- throw new Error(`received an invalid value for fileSystemPath: ${value}`)
29
+ throw new Error(`received an invalid value for fileSystemPath: ${value}`);
30
30
  }
31
- return String(pathToFileURL(value))
32
- }
31
+ return String(pathToFileURL(value));
32
+ };
33
33
 
34
34
  export const urlToFileSystemPath = (url) => {
35
- let urlString = String(url)
35
+ let urlString = String(url);
36
36
  if (urlString[urlString.length - 1] === "/") {
37
37
  // remove trailing / so that nodejs path becomes predictable otherwise it logs
38
38
  // the trailing slash on linux but does not on windows
39
- urlString = urlString.slice(0, -1)
39
+ urlString = urlString.slice(0, -1);
40
40
  }
41
- const fileSystemPath = fileURLToPath(urlString)
42
- return fileSystemPath
43
- }
41
+ const fileSystemPath = fileURLToPath(urlString);
42
+ return fileSystemPath;
43
+ };
@@ -3,21 +3,21 @@ https://developer.mozilla.org/en-US/docs/Web/API/Headers
3
3
  https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
4
4
  */
5
5
 
6
- import { normalizeHeaderName } from "./normalizeHeaderName.js"
7
- import { normalizeHeaderValue } from "./normalizeHeaderValue.js"
6
+ import { normalizeHeaderName } from "./normalizeHeaderName.js";
7
+ import { normalizeHeaderValue } from "./normalizeHeaderValue.js";
8
8
 
9
9
  export const headersFromObject = (headersObject) => {
10
- const headers = {}
10
+ const headers = {};
11
11
 
12
12
  Object.keys(headersObject).forEach((headerName) => {
13
13
  if (headerName[0] === ":") {
14
14
  // exclude http2 headers
15
- return
15
+ return;
16
16
  }
17
17
  headers[normalizeHeaderName(headerName)] = normalizeHeaderValue(
18
18
  headersObject[headerName],
19
- )
20
- })
19
+ );
20
+ });
21
21
 
22
- return headers
23
- }
22
+ return headers;
23
+ };
@@ -1,21 +1,21 @@
1
- import { normalizeHeaderName } from "./normalizeHeaderName.js"
2
- import { normalizeHeaderValue } from "./normalizeHeaderValue.js"
1
+ import { normalizeHeaderName } from "./normalizeHeaderName.js";
2
+ import { normalizeHeaderValue } from "./normalizeHeaderValue.js";
3
3
 
4
4
  // https://gist.github.com/mmazer/5404301
5
5
  export const headersFromString = (headerString) => {
6
- const headers = {}
6
+ const headers = {};
7
7
 
8
8
  if (headerString) {
9
- const pairs = headerString.split("\r\n")
9
+ const pairs = headerString.split("\r\n");
10
10
  pairs.forEach((pair) => {
11
- const index = pair.indexOf(": ")
11
+ const index = pair.indexOf(": ");
12
12
  if (index > 0) {
13
- const key = pair.slice(0, index)
14
- const value = pair.slice(index + 2)
15
- headers[normalizeHeaderName(key)] = normalizeHeaderValue(value)
13
+ const key = pair.slice(0, index);
14
+ const value = pair.slice(index + 2);
15
+ headers[normalizeHeaderName(key)] = normalizeHeaderValue(value);
16
16
  }
17
- })
17
+ });
18
18
  }
19
19
 
20
- return headers
21
- }
20
+ return headers;
21
+ };
@@ -1,7 +1,7 @@
1
1
  export const headersToObject = (headers) => {
2
- const headersObject = {}
2
+ const headersObject = {};
3
3
  headers.forEach((value, name) => {
4
- headersObject[name] = value
5
- })
6
- return headersObject
7
- }
4
+ headersObject[name] = value;
5
+ });
6
+ return headersObject;
7
+ };
@@ -1,16 +1,16 @@
1
1
  export const headersToString = (headers, { convertName = (name) => name }) => {
2
2
  const headersString = headersToArray(headers).map(({ name, value }) => {
3
- return `${convertName(name)}: ${value}`
4
- })
3
+ return `${convertName(name)}: ${value}`;
4
+ });
5
5
 
6
- return headersString.join("\r\n")
7
- }
6
+ return headersString.join("\r\n");
7
+ };
8
8
 
9
9
  const headersToArray = (headers) => {
10
10
  return Object.keys(headers).map((name) => {
11
11
  return {
12
12
  name,
13
13
  value: headers[name],
14
- }
15
- })
16
- }
14
+ };
15
+ });
16
+ };
@@ -1,21 +1,21 @@
1
- import { composeTwoObjects } from "./object_composition.js"
1
+ import { composeTwoObjects } from "./object_composition.js";
2
2
 
3
3
  export const composeTwoHeaders = (firstHeaders, secondHeaders) => {
4
4
  return composeTwoObjects(firstHeaders, secondHeaders, {
5
5
  keysComposition: HEADER_NAMES_COMPOSITION,
6
6
  forceLowerCase: true,
7
- })
8
- }
7
+ });
8
+ };
9
9
 
10
10
  const composeHeaderValues = (value, nextValue) => {
11
- const headerValues = value.split(", ")
11
+ const headerValues = value.split(", ");
12
12
  nextValue.split(", ").forEach((value) => {
13
13
  if (!headerValues.includes(value)) {
14
- headerValues.push(value)
14
+ headerValues.push(value);
15
15
  }
16
- })
17
- return headerValues.join(", ")
18
- }
16
+ });
17
+ return headerValues.join(", ");
18
+ };
19
19
 
20
20
  const HEADER_NAMES_COMPOSITION = {
21
21
  "accept": composeHeaderValues,
@@ -29,4 +29,4 @@ const HEADER_NAMES_COMPOSITION = {
29
29
  "server-timing": composeHeaderValues,
30
30
  // 'content-type', // https://github.com/ninenines/cowboy/issues/1230
31
31
  "vary": composeHeaderValues,
32
- }
32
+ };